Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .cursor-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "agent-switchboard",
"owner": {
"name": "Agent Switchboard",
"email": "barnir@agentmail.to"
},
"metadata": {
"description": "The Agent Switchboard plugin for Cursor: search a curated directory of AI agents, MCP servers and agentic tools from the agent.",
"version": "1.0.0"
},
"plugins": [
{
"name": "agent-switchboard",
"source": "./plugins/agent-switchboard",
"description": "Search a curated directory of AI agents, MCP servers and agentic tools, compare them, find alternatives, and build one MCP config for several."
}
]
}
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ Every release of [Agent Switchboard](https://agentswitchboard.dev), newest first

Versions follow MAJOR.MINOR.PATCH: a minor version adds features, a patch fixes or adjusts. Versions before 2.2.0 were assigned afterwards, reconstructed from the git history. "Directory" lines summarize catalog changes since the previous release; the full log of every listing added, updated or removed is on [/changelog](https://agentswitchboard.dev/changelog), and batch by batch on [/drops](https://agentswitchboard.dev/drops). From 2.2.0 on, each minor release opens with its highlights, what you can do now with a link to try it, which [/whats-new](https://agentswitchboard.dev/whats-new) also shows.

## [2.8.2] - 2026-11-03 - Cursor plugin

### Added
- A Cursor plugin for the Cursor Marketplace: [plugins/agent-switchboard](https://github.com/assafbar2/agentswitchboard.dev/tree/main/plugins/agent-switchboard) adds the Agent Switchboard MCP server to Cursor in one install, with no account or key. Until the marketplace lists it, add the server by URL as [/connect](https://agentswitchboard.dev/connect) shows. ([#322](https://github.com/assafbar2/agentswitchboard.dev/pull/322))

## [2.8.1] - 2026-11-02 - Code and data licenses

### Changed
Expand Down
11 changes: 10 additions & 1 deletion DISTRIBUTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ or a placement-dependent order reaches either surface.
| Claude Connectors Directory | ◻ Ready to submit | — | Assaf submits at claude.ai/directory/manage (paid Claude plan). Custom connector by URL works today; /connect has a prefilled install link |
| ChatGPT plugin directory | ◻ Ready to submit | — | Assaf uploads a plugin ZIP at platform.openai.com (verified identity) and sets `OPENAI_APPS_CHALLENGE` in Vercel for domain verification. Custom MCP plugin by URL works today |
| GitHub MCP Registry (github.com/mcp, VS Code) | ◻ Not listed | — | Curated from the official registry by manual onboarding: request in github/github-mcp-server discussions or partnerships@github.com |
| Cursor Marketplace | ◻ Not listed | — | Public repo with a Cursor plugin manifest, submitted at cursor.com/marketplace/publish (manual review) |
| Cursor Marketplace | ◻ Ready to submit | [plugins/agent-switchboard](plugins/agent-switchboard) | Assaf applies at cursor.com/marketplace/publish (Cursor sign-in) with this repo's URL; Cursor reviews it by hand |
| Smithery | ◻ Not listed | — | Publish the hosted URL at smithery.ai/new (Smithery account) |
| Microsoft 365 Copilot connectors | ◻ Not listed | — | Partner Center, verified publisher; every tool needs a title and `readOnlyHint` (already set) |

Expand All @@ -50,5 +50,14 @@ curl -s -X POST http://localhost:3100/api/mcp \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"check","version":"1.0"}}}'
```

**Cursor Marketplace**: the plugin lives in [plugins/agent-switchboard](plugins/agent-switchboard)
(Cursor Plugin format: `.cursor-plugin/plugin.json`, `mcp.json`, `assets/logo.svg`, `README.md`),
registered by [.cursor-plugin/marketplace.json](.cursor-plugin/marketplace.json) at the repo root.
Cursor reviews every update before it ships, so bump `version` in the plugin's `plugin.json` when
its files change. [tests/cursor-plugin.test.ts](tests/cursor-plugin.test.ts) checks it against
Cursor's submission checklist and keeps its README's tool list, MCP URL and logo in step with the
server. To try it in Cursor, copy the folder to `~/.cursor/plugins/local/agent-switchboard`,
run Developer: Reload Window, and check Customize for the `agent-switchboard` MCP server.

**awesome-mcp-servers**: the listing line lives in their README (Search & Data Extraction
section) with a Glama score badge. Edit via PR only.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "agentswitchboard.dev",
"version": "2.8.1",
"version": "2.8.2",
"private": true,
"scripts": {
"dev": "next dev",
Expand Down
15 changes: 15 additions & 0 deletions plugins/agent-switchboard/.cursor-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "agent-switchboard",
"displayName": "Agent Switchboard",
"version": "1.0.0",
"description": "Search a curated directory of AI agents, MCP servers and agentic tools, compare them, find alternatives, and build one MCP config for several.",
"author": {
"name": "Agent Switchboard",
"email": "barnir@agentmail.to"
},
"homepage": "https://agentswitchboard.dev",
"repository": "https://github.com/assafbar2/agentswitchboard.dev",
"license": "MIT",
"keywords": ["mcp", "mcp-servers", "ai-agents", "directory", "search", "developer-tools"],
"logo": "assets/logo.svg"
}
47 changes: 47 additions & 0 deletions plugins/agent-switchboard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Agent Switchboard for Cursor

Search [Agent Switchboard](https://agentswitchboard.dev), a curated directory of AI agents, MCP
servers and agentic tools, from Cursor's agent. Ask which tool fits a job, read a listing's setup
steps, compare listings, find alternatives, and get one MCP config for several tools.

The plugin adds one MCP server, `https://agentswitchboard.dev/api/mcp` (Streamable HTTP). It needs
no account, API key or configuration. In approval prompts it appears as
`plugin-agent-switchboard-agent-switchboard`.

## Tools

| Tool | What it does |
| --- | --- |
| `search_agents` | Find listings for a job: by query, task, category, access method (MCP, API, CLI, SDK and more), sign-in type, or official and verified status |
| `get_agent` | One listing with its description, access methods, and MCP address or install command |
| `list_categories` | The directory's categories, with how many listings each has |
| `compare_agents` | Two or three listings side by side |
| `alternatives_to` | Listings that could stand in for one, with what each has in common |
| `connect_bundle` | One MCP client config for up to 10 listings, and how to connect the rest |
| `submit_listing` | Suggests a missing product by filing a public GitHub issue for human review. It publishes nothing. It is marked destructive, so clients ask before calling it |

Every tool except `submit_listing` is read-only.

## Try it

- "Find an MCP server that can query a Postgres database."
- "How do I connect the Playwright MCP server, and is it official?"
- "Compare GitHub Copilot, Cursor and Windsurf."
- "What are good alternatives to Pinecone?"
- "Give me one MCP config with Playwright MCP, GitHub MCP and Notion MCP."

## Results and privacy

Results are organic: they rank by relevance or A→Z, never by paid placement. The server receives
only the arguments of each tool call, never your conversation or code. See the
[privacy policy](https://agentswitchboard.dev/privacy).

## License

The plugin is MIT, like the rest of the repository's code. The catalog data its tools return is
CC BY-NC 4.0; see [LICENSE-DATA](https://github.com/assafbar2/agentswitchboard.dev/blob/main/LICENSE-DATA).

## Help

Setup for other assistants and troubleshooting: [agentswitchboard.dev/connect](https://agentswitchboard.dev/connect#troubleshooting).
Support: [agentswitchboard.dev/contact](https://agentswitchboard.dev/contact).
16 changes: 16 additions & 0 deletions plugins/agent-switchboard/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions plugins/agent-switchboard/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"mcpServers": {
"agent-switchboard": {
"url": "https://agentswitchboard.dev/api/mcp"
}
}
}
81 changes: 81 additions & 0 deletions tests/cursor-plugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* The Cursor Marketplace plugin (.cursor-plugin/marketplace.json and
* plugins/agent-switchboard/) against Cursor's submission checklist
* (cursor.com/docs/reference/plugins), and in step with the live server:
* the MCP URL in server.json, the site icon, and every tool /api/mcp lists.
*/
import { describe, expect, it } from 'vitest';
import * as fs from 'fs';
import * as path from 'path';
import { POST as mcpPost } from '@/app/api/[transport]/route';

const ROOT = path.resolve(__dirname, '..');
const readJson = (file: string) => JSON.parse(fs.readFileSync(path.join(ROOT, file), 'utf8'));

const PLUGIN_NAME = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
const marketplace = readJson('.cursor-plugin/marketplace.json');
const entry = marketplace.plugins[0];
const pluginDir = path.join(ROOT, entry.source);
const manifest = readJson(path.join(entry.source, '.cursor-plugin/plugin.json'));
const mcpConfig = readJson(path.join(entry.source, 'mcp.json'));
const serverJson = readJson('server.json');

function isSafeRelative(p: string) {
const normalized = path.posix.normalize(p);
return !path.isAbsolute(p) && normalized !== '..' && !normalized.startsWith('../');
}

async function listedTools(): Promise<string[]> {
const res = await mcpPost(
new Request('http://localhost/api/mcp', {
method: 'POST',
headers: { 'content-type': 'application/json', accept: 'application/json, text/event-stream' },
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list', params: {} }),
})
);
const text = await res.text();
const data = text.split(/\r?\n/).filter((l) => l.startsWith('data:')).pop();
const { result } = JSON.parse(data ? data.slice(5) : text);
return result.tools.map((t: { name: string }) => t.name);
}

describe('Cursor Marketplace plugin', () => {
it('has one marketplace entry that resolves to a plugin folder with a manifest', () => {
expect(marketplace.name).toMatch(PLUGIN_NAME);
expect(marketplace.owner.name).toBeTruthy();
expect(marketplace.plugins).toHaveLength(1);
expect(isSafeRelative(entry.source)).toBe(true);
expect(fs.existsSync(path.join(pluginDir, '.cursor-plugin/plugin.json'))).toBe(true);
expect(entry.name).toBe(manifest.name);
});

it('has a kebab-case name, a description, a semver version and a committed relative logo', () => {
expect(manifest.name).toMatch(PLUGIN_NAME);
expect(manifest.description.length).toBeGreaterThan(40);
expect(manifest.version).toMatch(/^\d+\.\d+\.\d+$/);
expect(isSafeRelative(manifest.logo)).toBe(true);
expect(fs.readFileSync(path.join(pluginDir, manifest.logo), 'utf8')).toBe(
fs.readFileSync(path.join(ROOT, 'app/icon.svg'), 'utf8')
);
});

it('adds only the public MCP server, with no variables or secrets', () => {
const servers = Object.values(mcpConfig.mcpServers) as Record<string, unknown>[];
expect(servers).toEqual([{ url: serverJson.remotes[0].url }]);
expect(manifest.variables).toBeUndefined();
});

it('ships no rules, skills, agents, commands or hooks that discovery would pick up', () => {
for (const dir of ['rules', 'skills', 'agents', 'commands', 'hooks']) {
expect(fs.existsSync(path.join(pluginDir, dir)), dir).toBe(false);
}
expect(fs.existsSync(path.join(pluginDir, 'SKILL.md'))).toBe(false);
});

it('documents every tool the MCP server lists in its README', async () => {
const readme = fs.readFileSync(path.join(pluginDir, 'README.md'), 'utf8');
const tools = await listedTools();
expect(tools.length).toBeGreaterThan(0);
for (const tool of tools) expect(readme, tool).toContain(`\`${tool}\``);
});
});
Loading