How to Create an MCP Server in VS Code
Build, configure, debug, and distribute an MCP server in VS Code with stdio, HTTP, workspace settings, and extension providers.
VS Code is both an MCP client and a development environment. You can create a standalone MCP server in any language that supports standard input and output, then register it in VS Code. Or you can distribute server definitions from a VS Code extension by contributing an MCP server definition provider.
Choose the delivery route before writing code:
| Route | Best for | Configuration |
|---|---|---|
| Standalone server | A reusable local or remote process configured by a user or workspace | .vscode/mcp.json, portable .mcp.json, or user profile settings |
| Extension provider | A server distributed and configured through your extension | package.json contribution plus vscode.lm.registerMcpServerDefinitionProvider |
The official VS Code documentation covers MCP server setup and management and the extension provider API.
1. Decide how VS Code will run your server
Standalone process
A standalone server is an executable process that speaks MCP over a supported transport. Local servers commonly use stdio. Remote services can use Streamable HTTP; legacy SSE is also supported for compatibility. The process can run on the developer’s machine or on a remote host, depending on the transport and configuration.
Use this route when users should be able to add, update, or replace the server without installing your extension.
Extension-managed server
An extension can contribute an MCP server definition provider. The extension declares a provider ID and label in package.json, then registers a matching provider in extension code. The provider returns server definitions and can resolve a definition when VS Code starts it, including flows that require user interaction such as authentication.
Use this route when your extension owns distribution, setup, credentials, or connection discovery.
2. Create a standalone server project
VS Code does not require one programming language. The server only needs an MCP SDK or implementation that can communicate over the selected transport. The official guide points to SDKs for TypeScript, Python, Java, Kotlin, and C#. Select the SDK documentation for your language and pin the version in your project.
A practical project layout is:
my-mcp-server/
├── src/
│ └── server.<your-language>
├── .env.example
├── README.md
└── package-or-build-file
Keep the server’s protocol traffic on stdout. Write diagnostics to stderr or to a file. Any log line written to stdout can corrupt a stdio MCP session.
Capabilities to implement
Implement only the capabilities your use case needs:
- Tools: callable operations such as querying a database or taking a screenshot.
- Prompts: reusable prompt templates.
- Resources: readable data exposed through URIs.
- Elicitation: requests for additional user input.
- Sampling: requests for model assistance where supported.
- Authentication: OAuth or another documented credential flow.
- Instructions: server guidance that helps the client use capabilities safely.
- Roots: workspace boundaries supplied by the client.
- MCP Apps: interactive app experiences where supported.
A basic server normally starts with one or two tools. Add resources, prompts, authentication, or sampling only when the client and your use case require them.
3. Register the server in VS Code
Workspace configuration: .vscode/mcp.json
Create .vscode/mcp.json in the workspace. VS Code uses a top-level servers object:
{
"servers": {
"my-local-server": {
"type": "stdio",
"command": "<command-that-starts-your-server>",
"args": ["<argument>"],
"env": {
"EXAMPLE_API_KEY": "${input:exampleApiKey}"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "exampleApiKey",
"description": "API key for the MCP server",
"password": true
}
]
}
Replace the command and arguments with the launcher produced by your language’s official SDK. Input variables keep secrets out of checked-in configuration.
Portable workspace configuration: .mcp.json
Use the portable format when the same workspace configuration must be consumed by compatible MCP tools. It uses mcpServers instead of servers:
{
"mcpServers": {
"my-local-server": {
"type": "stdio",
"command": "<command-that-starts-your-server>",
"args": ["<argument>"],
"env": {
"EXAMPLE_API_KEY": "${input:exampleApiKey}"
}
}
}
}
User profile configuration
Add a server to the user-level MCP configuration when it should be available across workspaces. This is useful for personal utilities, but workspace configuration is easier to share with a team.
Guided setup
Run MCP: Add Server from the Command Palette to have VS Code create a server entry. You can then inspect and edit the generated configuration.
4. Configure Streamable HTTP or legacy SSE
For a remote server, use the HTTP transport supported by your client and server. Keep credentials in environment variables or the authentication mechanism documented by the service.
{
"servers": {
"remote-server": {
"type": "http",
"url": "https://example.invalid/mcp",
"headers": {
"Authorization": "Bearer ${input:remoteToken}"
}
}
},
"inputs": [
{
"type": "promptString",
"id": "remoteToken",
"description": "Remote MCP token",
"password": true
}
]
}
Use the exact transport and field names accepted by the VS Code version and server implementation you support. SSE remains a legacy option; prefer Streamable HTTP for new remote deployments when both sides support it.
5. Provide a server from a VS Code extension
An extension provider has two required pieces: a manifest contribution and a registered provider.
Manifest contribution
{
"contributes": {
"mcpServerDefinitionProviders": [
{
"id": "example.mcpProvider",
"label": "Example MCP Server"
}
]
}
}
The provider ID in the manifest must match the ID passed to the registration API.
Provider registration
Register the provider during extension activation with vscode.lm.registerMcpServerDefinitionProvider. Return definitions that describe how VS Code should start or connect to the server. Resolve a definition when startup requires authentication, account selection, or another user interaction.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const provider = {
provideMcpServerDefinitions: async () => {
// Return the server definitions your extension can provide.
return [];
},
resolveMcpServerDefinition: async (definition: unknown) => {
// Resolve credentials or interactive setup here when required.
return definition;
}
};
context.subscriptions.push(
vscode.lm.registerMcpServerDefinitionProvider(
'example.mcpProvider',
provider
)
);
}
Use the current VS Code MCP extension API reference for the concrete definition classes and return types in your target release. The provider route is appropriate when the extension controls installation, authentication, or server discovery.
6. Start, inspect, and debug the server
- Open the workspace containing the MCP configuration.
- Use the MCP view or Command Palette commands to start the server.
- Confirm that the server appears as running and that its tools are listed.
- Open the server output to inspect initialization and tool errors.
- During development, use the
devconfiguration’s watch patterns and restart support where available.
VS Code documents commands for starting, stopping, restarting, listing, and showing server output. For stdio servers, the documentation also describes Node.js and Python debugging workflows. Set breakpoints in the server process and keep protocol output separate from debugger logs.
7. Test a tool safely
Start with a deterministic read-only tool. Verify:
- The server initializes without writing unexpected stdout output.
- The tool name and input schema are visible to the client.
- Invalid arguments produce a structured error.
- Long-running work reports failure or timeout clearly.
- Secrets are redacted from logs and tool results.
- File and network access is limited to the intended scope.
Test both a normal call and failure cases before adding destructive tools. If a tool changes files, sends requests, or executes commands, require explicit inputs and validate them on the server.
8. Security and trust boundaries
VS Code warns that local MCP servers can run arbitrary code on the machine. Review the publisher, source, command, arguments, environment variables, and downloaded dependencies before starting a server.
- Do not hardcode API keys in
.vscode/mcp.jsonor.mcp.json. - Use input variables, environment files, or the server’s supported authentication flow.
- Commit only configuration that is safe for every workspace contributor.
- Validate URLs, file paths, and command arguments inside the server.
- Run with the least filesystem and network access needed.
- Remember that workspace MCP configuration follows Workspace Trust. In Restricted Mode, workspace MCP configuration is blocked.
VS Code’s setup documentation describes sandboxing controls where available, but sandboxing is currently unavailable on Windows. Do not assume a sandbox exists on every platform. Where a controlled sandbox is enabled, understand its file-write and network-domain restrictions and its tool approval behavior.
9. Performance, reliability, and cost
Performance
- Keep initialization fast; defer expensive connections until the first tool call.
- Reuse HTTP clients and database pools instead of reconnecting for every request.
- Return concise results and provide pagination for large resources.
- Use bounded concurrency so one model request cannot exhaust local resources.
- Cache stable metadata, but define invalidation rules.
Reliability
- Set timeouts for every network and subprocess operation.
- Return actionable errors with a safe message for the model and detailed diagnostics on stderr.
- Retry only idempotent operations, with backoff and a maximum attempt count.
- Handle client restarts and reconnects without duplicating side effects.
- Make destructive operations explicit and repeat-safe where possible.
Cost
Local stdio execution has no MCP network charge, but it consumes the developer’s CPU, memory, storage, and any downstream API quota. Remote servers add hosting, bandwidth, and provider costs. Track downstream usage per tool and expose limits before a tool can trigger expensive work.
10. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Server never appears | Invalid JSON, wrong file location, or incorrect top-level key | Validate the file, use servers in .vscode/mcp.json and mcpServers in .mcp.json, then restart VS Code. |
| Initialization fails immediately | Launcher command or arguments are wrong | Run the exact command outside VS Code and confirm the executable is on PATH. |
| Protocol parse errors | Logs or banners were written to stdout | Send diagnostics to stderr and reserve stdout for MCP protocol messages. |
| Tools are missing | Capability registration failed or the server returned an incomplete list | Inspect initialization output and verify tool schemas with a minimal read-only tool. |
| Authentication prompts repeatedly | Credentials are not persisted or the provider does not resolve definitions correctly | Use the documented input or OAuth flow and implement provider resolution for interactive setup. |
| Workspace configuration is ignored | Workspace Trust is restricted | Trust the workspace or configure the server at user level where appropriate. |
| Remote connection times out | Incorrect URL, blocked network, proxy, or server timeout | Check the endpoint from the same machine, configure proxy access, and add bounded server-side timeouts. |
| Works on one OS only | Shell syntax, path separators, or unavailable sandbox behavior | Use platform-aware launchers and test on each supported operating system. |
11. Or skip the browser setup
If your MCP tool needs website screenshots, ScreenshotNeo provides a screenshot API and MCP server. You can call it directly instead of maintaining browser installation, page cleanup, and capture code. The API documentation is at screenshotneo.com/docs.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://stripe.com \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://stripe.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', image);
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and whether the request was billed. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
FAQ
Can I use an MCP server without a VS Code extension?
Yes. Configure a standalone process in workspace, portable, or user-level MCP settings.
Which transport should a new server use?
Use stdio for a local process and Streamable HTTP for a remote service when supported. SSE is a legacy option.
Where should secrets live?
Use input variables, environment files, or the server’s authentication flow. Do not commit keys to workspace configuration.
Why does VS Code block my workspace server?
Workspace MCP configuration follows Workspace Trust. Restricted workspaces block it until the workspace is trusted.
Should every server implement prompts, resources, and sampling?
No. Implement the smallest capability set that solves the task, then add capabilities as the client and workflow require them.


