Cursor’s Marketplace turned Model Context Protocol servers from a copy-paste config chore into a one-click install, but the install button only gets you halfway. If you want your team to open a project and immediately have a working MCP server (no README, no manual JSON edits, no “did you restart Cursor yet?” Slack threads), you need to package that server inside a real VS Code-compatible extension and register it through Cursor’s extension API. That’s a different skill than writing an MCP server itself, and most tutorials stop at the server. This one doesn’t.
By the end of this guide you’ll have a working extension that registers an MCP server programmatically with vscode.cursor.mcp.registerServer(), falls back gracefully to a .cursor/mcp.json file for plain VS Code users, and is ready to package, test locally, and publish through Cursor’s Marketplace or a private team marketplace. We’ll build it against the current stable toolchain: VS Code 1.139 (released September 23, 2026) and Cursor’s documented extension API, which is the officially supported path Cursor recommends for extension authors who don’t want to force users into manual config editing.
Don't miss new tech stories on Google
Add Tech Insider once in the Google app and our stories appear in your news suggestions.
What You’ll Build and Why It Matters
The end product is a small, real extension: a Node-based local MCP server (spawned via npx), a VS Code/Cursor extension shell that registers it automatically on activation, and a packaged .vsix you can install locally, share with a team, or submit to a marketplace. Along the way you’ll also wire up a remote MCP server variant with authentication headers, so the same codebase supports both the “runs on my machine” case and the “calls our internal API” case.
Why bother with the extension API instead of just shipping a mcp.json file in a repo? Three reasons. First, project-level .cursor/mcp.json only activates for people who clone that specific repo – it doesn’t travel with a user across projects. Second, editing global ~/.cursor/mcp.json by hand is exactly the kind of manual step that breaks onboarding docs within a month. Third, Cursor’s own documentation frames registerServer() as built specifically so an extension can avoid requiring users to edit configuration files manually, which is the whole point if you’re distributing this to more than a handful of teammates.
Where MCP Fits Inside Cursor’s Architecture
Model Context Protocol defines a client-server relationship between an AI agent (the client, running inside Cursor) and a small process that exposes capabilities (the server, which is what you’re building). A server can expose three kinds of primitives: tools the agent can invoke, resources it can read, and prompts it can reuse. Most internal-tooling extensions, including the one in this guide, lean almost entirely on tools, since that’s the primitive that maps cleanly onto “call an internal API and return a result.”
Two transport types matter for extension authors. A stdio transport spawns a local process and communicates over standard input and output, which is what this guide’s local server uses. An HTTP/SSE-based remote transport instead points at a URL, which is what Step 5 covers for internal APIs that already run as a hosted service. Cursor’s registration API accepts either shape through the same ExtMCPServerConfig object, so an extension author doesn’t need two different code paths beyond the branching shown later in this guide.
This matters for extension design because it determines what actually ships inside your .vsix. A stdio-based server means your compiled JavaScript travels inside the packaged extension and runs on the end user’s machine, with all the packaging and path-resolution concerns covered in Steps 7 and 8. A remote server means the extension only ships a thin registration call and a URL, and the actual logic lives on infrastructure your team already operates and patches independently of the extension’s release cycle. Choosing between the two early avoids a rewrite later, particularly if your internal API requires infrastructure (database credentials, VPN access, service accounts) that shouldn’t travel to every developer’s laptop as a bundled local process.
Prerequisites and Exact Versions
Version drift is the single biggest cause of “it worked in the docs but not on my machine” with MCP tooling right now, so pin these before you start:
| Tool | Version Used in This Guide | Check Command | Notes |
|---|---|---|---|
| Cursor | Latest stable (check in-app) | Cursor menu → About Cursor | Cursor does not publish a fixed version cadence; always check About before publishing |
| VS Code | 1.139 (Sept 23, 2026 stable) | code --version | Needed for extension host compatibility testing |
| Node.js | 20.x LTS or newer | node -v | Required by yo code generator and the MCP SDK |
| @vscode/vsce | Latest via npm | vsce --version | Packaging and publishing CLI |
| Yeoman + generator-code | Latest via npm | yo --version | Scaffolds the extension skeleton |
| Cursor CLI (optional) | Latest | agent --version | Cursor’s CLI changelog documents agent update for in-place upgrades |
You’ll also want a GitHub or GitLab account if you plan to distribute the extension through a team marketplace, and an Azure DevOps personal access token if you’re publishing to the public VS Code Marketplace as well as Cursor’s own Marketplace. The two are separate publishing surfaces, covered later in this guide.
Step 1: Scaffold the Extension
Install the Yeoman generator and scaffold a TypeScript extension. This is the same generator Microsoft ships for standard VS Code extensions, since Cursor’s extension host is a fork of VS Code’s and accepts the same manifest format.
npm install -g yo generator-code
yo code
# When prompted, choose:
# ? What type of extension do you want to create? New Extension (TypeScript)
# ? What's the name of your extension? cursor-mcp-bridge
# ? What's the identifier of your extension? cursor-mcp-bridge
# ? Bundle the source code with webpack? Yes
# ? Initialize a git repository? Yes
# ? Which package manager to use? npm
Once scaffolding finishes, cd into the folder and open it in Cursor (or VS Code) to confirm the default “Hello World” command activates. That confirms your build toolchain is healthy before you add MCP-specific code.
Step 2: Add the MCP Server Package
Your extension needs an actual MCP server to register. This guide uses a minimal local server built on the standard MCP TypeScript SDK, the same SDK most published MCP servers use. Install it as a dependency of the extension package (not a devDependency, since the server code ships inside the packaged extension).
npm install @modelcontextprotocol/sdk zod
npm install --save-dev @types/node
Create a new file src/mcp-server.ts. This defines a small server exposing one example tool. Swap the tool logic for whatever your team actually needs, such as querying an internal API, running a linter, or hitting a ticketing system:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "cursor-mcp-bridge",
version: "0.1.0",
});
server.tool(
"lookup-service-status",
"Returns the current status of an internal service by name",
{ serviceName: z.string() },
async ({ serviceName }) => {
// Replace with a real internal API call.
const status = serviceName.toLowerCase().includes("prod")
? "degraded"
: "healthy";
return {
content: [
{
type: "text",
text: `Service "${serviceName}" is currently ${status}.`,
},
],
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Add a build script so this compiles to a standalone entry point your extension can spawn with npx or a direct node call:
{
"scripts": {
"build:server": "tsc src/mcp-server.ts --outDir dist/server --module NodeNext --target ES2022"
}
}
Step 3: Register the Server With Cursor’s Extension API
This is the step most tutorials skip. Instead of telling the user to edit mcp.json, your extension’s activate() function calls vscode.cursor.mcp.registerServer() directly, using an ExtMCPServerConfig object as documented in Cursor’s extension API reference. Open src/extension.ts and replace the default activation logic:
import * as vscode from "vscode";
import * as path from "path";
export function activate(context: vscode.ExtensionContext) {
const serverEntry = context.asAbsolutePath(
path.join("dist", "server", "mcp-server.js")
);
// Cursor-specific API - no-op in plain VS Code, so guard it.
const cursorApi = (vscode as any).cursor;
if (cursorApi?.mcp?.registerServer) {
cursorApi.mcp.registerServer({
name: "cursor-mcp-bridge",
command: "node",
args: [serverEntry],
env: {},
});
vscode.window.showInformationMessage(
"cursor-mcp-bridge: MCP server registered."
);
} else {
vscode.window.showWarningMessage(
"cursor-mcp-bridge: Cursor MCP API not detected. Falling back to manual .cursor/mcp.json instructions."
);
}
context.subscriptions.push(
vscode.commands.registerCommand("cursor-mcp-bridge.showStatus", () => {
vscode.window.showInformationMessage("cursor-mcp-bridge is active.");
})
);
}
export function deactivate() {}
Note the defensive cursorApi?.mcp?.registerServer check. Because this extension format is also valid in plain VS Code, where the cursor namespace doesn’t exist, that guard is what keeps the extension from throwing on activation for non-Cursor users. It’s also the reason this same package can serve both audiences from one build.
Step 4: Ship a Config-File Fallback for Plain VS Code and Older Cursor Builds
Not every teammate will be on a Cursor build with the registration API available, and plain VS Code users have no cursor namespace at all. Ship a project-level .cursor/mcp.json alongside your extension so the server still works if someone configures it manually. Cursor merges project-level and global MCP configs, with project-level entries taking priority when names collide, so this file is safe to commit even for teams that also use the registration API.
{
"mcpServers": {
"cursor-mcp-bridge": {
"command": "node",
"args": ["./dist/server/mcp-server.js"]
}
}
}
Place this at .cursor/mcp.json in the root of any repo where you want the fallback available without the extension installed at all. This is useful for CI environments or contractors who aren’t running your extension.
Step 5: Support a Remote MCP Server With Auth Headers
Local stdio servers cover linting and file-system tools, but most internal-API integrations need a remote server behind authentication. Cursor’s mcp.json schema supports a url field plus a headers object for exactly this case. Extend your extension’s registration call to branch between local and remote based on a workspace setting:
const config = vscode.workspace.getConfiguration("cursorMcpBridge");
const remoteUrl = config.get<string>("remoteServerUrl");
const apiToken = process.env.CURSOR_MCP_BRIDGE_TOKEN;
if (remoteUrl && apiToken) {
cursorApi?.mcp?.registerServer({
name: "cursor-mcp-bridge-remote",
url: remoteUrl,
headers: {
Authorization: `Bearer ${apiToken}`,
},
});
} else if (cursorApi?.mcp?.registerServer) {
cursorApi.mcp.registerServer({
name: "cursor-mcp-bridge",
command: "node",
args: [serverEntry],
});
}
Never hardcode the token. Pull it from an environment variable or, better, from VS Code’s SecretStorage API if the extension will be distributed outside a trusted internal team. Committing a bearer token into a workspace setting is the fastest way to leak API credentials through a shared repo.
Step 6: Test the Extension Locally in Cursor
Before packaging anything, run the extension in the Extension Development Host to confirm registration actually fires and the tool responds.
- Open the project folder in Cursor.
- Run
npm run build:serverthennpm run compile(or your generator’s default build task). - Press F5 (or run “Debug: Start Debugging”) to launch the Extension Development Host.
- In the new Cursor window, open Customize → MCPs and confirm
cursor-mcp-bridgeappears as a registered server. - Open the agent chat panel and ask it to call the
lookup-service-statustool with a test service name. - Check the Output panel (select your extension’s channel) for registration logs and any errors.
If the tool doesn’t show up in the agent’s available tools list, the most common cause is a stale build. Rerun build:server after any change to mcp-server.ts, since the compiled JS in dist/ is what actually gets spawned, not the TypeScript source.
Debugging the Server Directly With the MCP Inspector
If a tool call fails inside Cursor’s agent chat, it’s often faster to bypass the extension host entirely and talk to the server directly. The MCP ecosystem’s standard debugging tool is the Inspector, a small local web UI that connects to your server over the same transport Cursor would use and lets you call tools manually with arbitrary arguments:
npx @modelcontextprotocol/inspector node dist/server/mcp-server.js
This opens a browser tab where you can see the server’s advertised tools, inspect the exact JSON schema Cursor’s agent would receive, and fire a test call with sample input without touching Cursor at all. It’s the fastest way to tell whether a bug lives in your server logic (it will reproduce in the Inspector) or in the extension’s registration and activation code (the Inspector will work fine while Cursor still shows nothing). Run this check before filing a bug against Cursor itself; a server that fails in the Inspector is never a Cursor problem.
For the remote-server variant from Step 5, point the Inspector at the URL directly instead of a spawn command, and pass the same bearer token you’d use in production. This confirms your authentication headers are formatted correctly before you spend time debugging registration code in the extension itself, since a 401 at this stage is almost always a header formatting issue rather than an extension bug.
Step 7: Package the Extension With vsce
Once local testing passes, package the extension into a distributable .vsix file using Microsoft’s official packaging CLI:
npm install -g @vscode/vsce
vsce package
This produces cursor-mcp-bridge-0.1.0.vsix in your project root. Before packaging, double-check your .vscodeignore file excludes node_modules dev dependencies and any .env files. A bloated or credential-leaking .vsix is a common review-rejection reason on both marketplaces.
Step 8: Install and Verify the Packaged Build
Install the packaged file directly to confirm it behaves identically to the dev-host version, since packaging can occasionally strip files that a .vscodeignore misconfiguration excludes by accident:
code --install-extension cursor-mcp-bridge-0.1.0.vsix
# Or in Cursor: Extensions panel -> "..." menu -> Install from VSIX
Reload the window, reopen Customize → MCPs, and confirm the server registers exactly as it did in the development host. If it doesn’t, the packaging step likely dropped your compiled dist/server directory. Check the “files included” output that vsce package prints during the build.
Step 9: Publish to the Cursor Marketplace
Cursor’s Marketplace is the install surface most of your users will actually see. It’s what powers the one-click “Add to Cursor” flow from the Customize sidebar. Publishing there today means submitting your packaged extension through Cursor’s plugin submission flow, accessible from the Marketplace section of Cursor’s developer docs, which reviews the manifest, permissions, and any MCP server declarations before listing it publicly.
Keep your package.json manifest fields accurate, since these populate the listing directly:
{
"name": "cursor-mcp-bridge",
"displayName": "Cursor MCP Bridge",
"description": "Registers an internal-tools MCP server automatically on activation.",
"version": "0.1.0",
"publisher": "your-publisher-id",
"engines": { "vscode": "^1.90.0" },
"categories": ["Other"],
"activationEvents": ["onStartupFinished"]
}
For a broader distribution strategy that also covers the open-source VS Code Marketplace alternative, see our earlier walkthrough on publishing VS Code extensions to Open VSX. The packaging step overlaps almost entirely with what you just did here.
Step 10: Distribute Privately Through a Team Marketplace
If this extension is internal-only, skip public listing entirely. Cursor supports team-managed distribution through Dashboard → Plugins & MCPs, where an admin can create a Team Marketplace from scratch or import one from a private repository. Team marketplaces support access control and automatic refreshing, so once you push a new version to the linked repo, teammates get the update without re-downloading a .vsix manually.
- Go to Dashboard → Plugins & MCPs in Cursor.
- Select Team Marketplaces → Create from Repository.
- Point it at the repo containing your packaged extension and manifest.
- Set access controls (org-wide, specific teams, or invite-only).
- Enable automatic refresh so version bumps propagate without manual reinstalls.
Step 11: Set Up Versioning and a Release Workflow
Once more than one person depends on this extension, ad hoc vsce package runs stop being reliable. A minimal CI workflow, even a single GitHub Actions job, removes the “which version did I actually ship” problem:
name: Package Extension
on:
push:
tags: ["v*"]
jobs:
package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
- run: npm ci
- run: npm run build:server
- run: npx @vscode/vsce package
- uses: actions/upload-artifact@v4
with:
name: vsix
path: "*.vsix"
Tag a release, let the workflow build the .vsix, and attach that artifact to your team marketplace’s linked repo or the public listing. This also gives you a reproducible build to hand to a security reviewer if your org requires that before internal tools touch production APIs.
Real-World Use Cases for a Registered MCP Extension
The example tool in this guide is deliberately generic so the code samples stay portable, but the pattern applies directly to the internal tooling most engineering teams already maintain in some half-finished form. A few concrete examples of what teams actually register through this exact flow:
- Deployment and incident status lookups. A tool that queries your deployment platform or status page API and returns the current rollout state for a service, so an agent debugging a production issue doesn’t need a human to paste in a dashboard screenshot.
- Internal documentation search. A tool wrapping your internal wiki or docs search API, letting the agent pull runbook steps or architecture decisions directly into a coding session instead of the developer tab-switching to find them.
- Ticketing system integration. A tool that creates or updates a ticket in Jira, Linear, or a similar system directly from the agent chat, useful for teams that want AI-assisted work to leave the same audit trail as human-initiated work.
- Database schema introspection. A read-only tool that returns table structures or recent migration history from a shared database, so an agent writing a query doesn’t have to guess at column names from stale training data.
- Internal API contract validation. A tool that checks a proposed API change against your team’s internal schema registry before the agent suggests code that would break a downstream consumer.
Each of these follows the same shape as the example server built earlier: a narrow, well-scoped tool with a clear input schema and a text response the agent can reason about. The temptation with internal tooling is to expose an entire internal API surface through one tool with a generic “query” parameter. Resist it. A separate, narrowly scoped tool per capability is easier to review for security, easier for the agent to select correctly among several options, and easier to version independently as your internal APIs change.
Common Pitfalls When Building Cursor MCP Extensions
A handful of mistakes account for most of the failed installs and silent no-shows in the tool list. Watch for these specifically:
- Registering before the server binary exists. Calling
registerServer()beforebuild:serverhas produced a compiled JS file means Cursor tries to spawn a file that isn’t there yet. This shows up as a silent registration failure with no obvious error in the UI. - Skipping the cursorApi guard. Extensions that assume the
cursornamespace always exists will throw a TypeError on activation in plain VS Code, breaking the extension for any non-Cursor user who installs it from the public VS Code Marketplace. - Hardcoding absolute file paths. Using a developer machine’s absolute path instead of
context.asAbsolutePath()means the server spawn command breaks the moment anyone else installs the extension. - Committing secrets into mcp.json or workspace settings. Auth headers and tokens belong in environment variables or SecretStorage, never in a file that gets committed to a shared repo.
- Forgetting project-vs-global config precedence. If a project-level
.cursor/mcp.jsondefines a server with the same name as one registered globally, the project-level one wins, which is a common source of confusing “which config is even running” debugging sessions. - Not testing the packaged VSIX, only the dev host. A
.vscodeignoremisconfiguration can silently strip the compiled server directory from the package while the dev host, which reads uncompiled source directly, works fine. - Overly broad activationEvents. Using a wildcard activation event slows down every workspace load for every user, even ones who never touch the feature the extension provides.
- Assuming remote servers don’t need error handling. A remote MCP server with no timeout or retry logic on the auth call can hang an agent session if the internal API is briefly unavailable.
Expected Output at Each Stage
Here’s what a working setup should look like at each checkpoint, so you can catch a broken step before it compounds:
| Stage | Expected Output |
|---|---|
| After npm run build:server | dist/server/mcp-server.js exists and runs standalone with node dist/server/mcp-server.js (it will idle waiting for stdio input, which is correct) |
| After F5 in dev host | Output panel logs “cursor-mcp-bridge: MCP server registered.” and no red error text |
| Customize → MCPs panel | cursor-mcp-bridge listed with a green/active status indicator |
| Agent chat test call | Response text: Service “checkout-prod” is currently degraded (or your test string) |
| After vsce package | Terminal prints a file list ending in “Packaged: cursor-mcp-bridge-0.1.0.vsix (NN files)” |
| After VSIX install + reload | Extension appears in Extensions panel as installed, MCP server still shows active in Customize → MCPs |
Troubleshooting Guide
Work through these in order if the server isn’t registering or the tool isn’t reachable from the agent:
- Server doesn’t appear in Customize → MCPs at all. Confirm the extension actually activated by checking the Output panel dropdown for your extension’s channel. If it’s missing entirely, your activationEvents may not be firing; try onStartupFinished for testing.
- “Cursor MCP API not detected” warning appears even in Cursor. You’re likely on an older Cursor build predating the extension API’s rollout. Update Cursor via the in-app updater and retest.
- Server registers but tool calls time out. Check that the spawned Node process isn’t crashing immediately. Run the compiled server manually with
node dist/server/mcp-server.jsand watch stderr for a stack trace. - Works in dev host, fails after VSIX install. Compare the file list printed by vsce package against your expected dist contents. A missing dist/server directory is the most common cause.
- Remote server returns 401 on every call. The bearer token likely isn’t reaching the process. Environment variables set in your shell profile don’t always propagate to Cursor if Cursor was launched from a GUI icon instead of a terminal. Launch Cursor from the same terminal session where the variable is exported to confirm.
- Two servers with the same name conflict. Remember project-level mcp.json overrides a global registration with the same server name. Rename one of them if you need both active simultaneously for testing.
- Team Marketplace doesn’t show the latest version. Confirm automatic refresh is enabled in Marketplace Settings, and that your release tag actually triggered the packaging workflow. Check the Actions tab for a failed run.
- Extension installs but the command palette entry is missing. Check that your command is registered in both package.json’s contributes.commands array and in context.subscriptions.push() inside activate(). A mismatch between the two is a common copy-paste error.
Advanced Tips for Production Use
Once the basic flow works, a few refinements make the extension safer and more pleasant to maintain:
- Use VS Code’s SecretStorage API instead of environment variables for any token that a non-technical teammate might need to configure through a settings UI rather than a shell export.
- Add a status bar item that reflects live MCP server health, rather than relying on users to open Customize → MCPs to check.
- Version your MCP tool schemas separately from the extension version, since agents may cache tool definitions across sessions and a schema change without a version bump can cause stale-tool confusion.
- If your team runs both Cursor and plain VS Code, publish two listings from one codebase: the Cursor Marketplace listing with MCP registration active, and a VS Code Marketplace listing where the same extension degrades gracefully to just the fallback config file.
- Log tool invocations server-side, not just client-side, if the MCP server touches anything with compliance implications. Agent-initiated calls should be as auditable as human-initiated ones.
Comparing Distribution Paths
Which distribution method fits depends on audience size and how much control you need over updates:
| Method | Best For | Update Mechanism | Review Required |
|---|---|---|---|
| Project .cursor/mcp.json only | Solo devs, quick prototypes | Manual, per-repo | None |
| Extension with local registerServer() | Small teams sharing one repo | Reinstall VSIX per update | None (internal) |
| Cursor Team Marketplace | Org-wide internal tools | Automatic refresh from linked repo | Internal admin approval |
| Cursor public Marketplace | Tools meant for external users | New version submission | Cursor plugin review |
| VS Code public Marketplace + Open VSX | Cross-editor distribution | Publisher-managed via vsce | Microsoft/Eclipse Foundation review |
The Complete Working Project Structure
For reference, here’s the full file layout of the extension you just built, so you can check your project against it:
cursor-mcp-bridge/
├── .cursor/
│ └── mcp.json
├── .vscodeignore
├── package.json
├── tsconfig.json
├── src/
│ ├── extension.ts
│ └── mcp-server.ts
├── dist/
│ ├── extension.js
│ └── server/
│ └── mcp-server.js
└── .github/
└── workflows/
└── package.yml
Every file here has a defined job: extension.ts handles registration and activation, mcp-server.ts is the actual tool implementation, .cursor/mcp.json is the manual fallback, and the GitHub Actions workflow keeps packaging reproducible once more than one person contributes.
How This Fits Into a Broader Cursor Workflow
An MCP-registering extension rarely lives in isolation. Teams that build one typically pair it with Cursor Skills for repeatable agent workflows, since a skill can call the MCP tool you just registered as part of a larger multi-step task. If your extension needs to run inside a consistent, reproducible environment across a distributed team, our guide on setting up VS Code Dev Containers for AI agents covers pairing MCP servers with containerized dev environments so the Node version and dependency tree stay identical across every machine that installs your extension.
If you’re evaluating whether Cursor’s extension and MCP model is the right fit compared to other AI-native editors, our breakdown of Google Antigravity vs Kiro vs Windsurf covers how competing editors handle the same class of tool-registration problem, and whether their extension APIs offer comparable programmatic control. For teams building more autonomous agent workflows on top of these registered tools, our computer-use AI agent setup guide walks through the layer above this one, where an agent needs to chain multiple registered MCP tools together to complete a task end to end.
Security Considerations Before You Ship
Because an MCP server extension effectively gives an AI agent programmatic access to whatever the server exposes, treat the review bar the same as you would for any service account with API access. Scope the tool’s permissions narrowly, read-only where possible, avoid granting broad filesystem or shell access unless the use case genuinely requires it, and rotate any bearer tokens on a schedule rather than treating them as permanent. If the extension will be installed by people outside your immediate team, a lightweight internal security review before publishing to a Team Marketplace catches most of the exposure risk before it reaches production.
Pay particular attention to prompt injection risk if any tool response includes text pulled from an external or user-controlled source, such as a ticket description or a wiki page. A malicious string embedded in that content could attempt to manipulate the agent into calling other registered tools in unintended ways. Sanitize or clearly delimit any untrusted text before returning it as tool output, and avoid registering a tool that both reads untrusted external content and has write access to a sensitive system in the same extension, since that combination is the one most likely to be exploited if it’s ever abused.
It’s also worth logging which user session triggered a given tool call, not just what the call did. If a registered MCP tool ever touches production data, the incident review process afterward needs to distinguish between a human directly invoking an action versus an agent doing so autonomously on a developer’s behalf, and blanket logs that only capture the tool’s output make that distinction impossible to reconstruct later.
Frequently Asked Questions
Does vscode.cursor.mcp.registerServer() work in plain VS Code?
No. The cursor namespace only exists inside Cursor’s forked extension host. In plain VS Code, vscode.cursor is undefined, which is why the guard clause in Step 3 is required to avoid a runtime error for VS Code users who install the same extension.
Can I register more than one MCP server from a single extension?
Yes. Call registerServer() multiple times with different name values during activation. There’s no documented limit on servers per extension, though each one adds its own process overhead if it’s a local stdio server.
What’s the difference between the Cursor Marketplace and the VS Code Marketplace?
They’re separate listing surfaces with separate review processes. The Cursor Marketplace is Cursor’s own catalog, accessible from Customize in-app, and supports Cursor-specific capabilities like MCP server registration and Agent Plugins. The VS Code Marketplace is Microsoft’s general extension catalog and doesn’t understand Cursor-specific APIs, though a well-guarded extension can still install and run there with reduced functionality.
Do I need a paid Cursor plan to publish to the Marketplace?
Publishing an extension to the Marketplace is a developer action separate from Cursor’s usage-based subscription tiers. Check Cursor’s current publisher terms in-app before submitting, since marketplace policies can change independently of pricing plans.
How do project-level and global MCP configs interact if I have both?
Cursor merges them. If both a project-level .cursor/mcp.json and a global ~/.cursor/mcp.json define a server with the same name, the project-level definition takes priority for that workspace.
Why did my tool disappear from the agent’s available tools after a Cursor update?
Extension APIs in actively developed editors can change between releases. If a tool that previously registered successfully stops appearing after an update, check Cursor’s extension API documentation for signature changes to ExtMCPServerConfig before assuming your code is broken.
Is it safe to bundle npx-based server commands in a distributed extension?
It works, but it means every user needs Node and npm/npx available on their PATH, and the first run will trigger an npx download if the package isn’t cached locally. For internal tools with a controlled environment, bundling the compiled server directly inside the extension, as this guide does, avoids that dependency and startup delay entirely.
Can this same extension support both local and remote servers for different users simultaneously?
Yes, as shown in Step 5. Branching on a workspace setting or environment variable lets the same packaged extension serve a local stdio server to some users and a remote authenticated server to others, without maintaining two separate builds.
Should I write automated tests for the MCP server logic?
Treat the server’s tool handlers like any other backend code that touches an internal API: unit-test the handler function directly (mocking the underlying API call), and reserve the MCP Inspector for manual, protocol-level verification rather than as a substitute for automated coverage. A handler that silently returns malformed content on an edge case is much easier to catch in a fast unit test than by noticing an agent behaving oddly three tool calls later.





