Model Context Protocol Free code examples in C#, Python, Java and JavaScript
The open standard for giving any model access to your tools and data.
Using Claude Code? Free subagents, skills, hooks and MCP server templates are in the Claude Kit.
WHAT THIS PAGE COVERS
Writing one MCP server and using it from several different clients.
2026-07-28
mcp
Model ids change. If a sample returns a 404 on the model, that is the first thing to check against the official docs.
The .NET SDK is still prerelease. Microsoft.Extensions.Hosting comes with it because an MCP server is a host with a transport rather than a bare console app.
dotnet new console -o WarehouseMcp
cd WarehouseMcp
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.Hosting
CreateEmptyApplicationBuilder, NOT CreateDefaultBuilder. The default builder adds console logging, which writes to stdout — and stdout is the protocol stream. This one line is the difference between a working server and a parse error.
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol;
using System.Net.Http.Headers;
// Empty, not Default: the default builder logs to stdout and stdout
// belongs to the transport.
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
// Speaks MCP revision 2026-07-28.
builder.Services.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
builder.Services.AddSingleton(_ =>
{
var client = new HttpClient { BaseAddress = new Uri("https://inventory.internal") };
client.DefaultRequestHeaders.UserAgent.Add(
new ProductInfoHeaderValue("warehouse-mcp", "1.0"));
return client;
});
var app = builder.Build();
await app.RunAsync();
[McpServerToolType] marks the class and [McpServerTool] marks each method. Parameters are injected from DI or bound from the call, so HttpClient arrives without being a tool argument.
using ModelContextProtocol.Server;
using System.ComponentModel;
using System.Text.Json;
namespace WarehouseMcp.Tools;
[McpServerToolType]
public static class StockTools
{
[McpServerTool, Description("Get the current stock level for a product code.")]
public static async Task<string> GetStock(
HttpClient client, // injected from DI
[Description("The product code, e.g. X-17")] string code)
{
using var response = await client.GetAsync($"/stock/{code}");
response.EnsureSuccessStatusCode();
using var json = await JsonDocument.ParseAsync(
await response.Content.ReadAsStreamAsync());
var quantity = json.RootElement.GetProperty("quantity").GetInt32();
return $"{code}: {quantity} units";
}
}
Every log line goes to stderr. Configuring a console logger the usual way writes to stdout and corrupts the stream — the symptom is a client that connects and then reports malformed JSON.
using Microsoft.Extensions.Logging;
builder.Logging.AddConsole(options =>
{
// The one setting that matters in a stdio MCP server.
options.LogToStandardErrorThreshold = LogLevel.Trace;
});
// Register with a client by pointing it at the built executable:
// {
// "mcpServers": {
// "warehouse": {
// "command": "dotnet",
// "args": ["run", "--project", "/abs/path/to/WarehouseMcp"]
// }
// }
// }
The [cli] extra brings the inspector and the dev runner, which is how you test a server without wiring it into a client first.
uv add "mcp[cli]"
# or: pip install "mcp[cli]"
THE CLASS IS MCPServer FROM mcp.server. FastMCP from mcp.server.fastmcp is the older name and is what most tutorials still import. Type hints become the schema; the docstring becomes the description the model reads.
from typing import Any
import httpx
from mcp.server import MCPServer
# Speaks MCP revision 2026-07-28.
mcp = MCPServer("warehouse")
@mcp.tool()
async def get_stock(code: str) -> str:
"""Get the current stock level for a single product code.
Args:
code: The product code, e.g. X-17
"""
async with httpx.AsyncClient() as client:
response = await client.get(
f"https://inventory.internal/stock/{code}", timeout=30.0)
response.raise_for_status()
return f"{code}: {response.json()['quantity']} units"
if __name__ == "__main__":
mcp.run(transport="stdio")
STDOUT IS THE TRANSPORT. One print() corrupts the JSON-RPC stream and the client reports a parse error that points at nothing. Log to stderr, always.
import logging
import sys
# stderr. Never stdout — that channel belongs to the protocol.
logging.basicConfig(
level=logging.INFO,
stream=sys.stderr,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger("warehouse")
@mcp.tool()
async def reorder(code: str, quantity: int) -> str:
"""Place a reorder for a product."""
log.info("reorder requested: %s x%d", code, quantity)
return f"Ordered {quantity} of {code}."
The inspector speaks the protocol so you can call tools by hand. Doing this first turns 'the client cannot see my server' into a question with an answer.
# Interactive: list the tools, call one, read the raw JSON-RPC.
uv run mcp dev server.py
# Then register it with a client. For Claude Desktop, in
# claude_desktop_config.json:
# {
# "mcpServers": {
# "warehouse": {
# "command": "uv",
# "args": ["--directory", "/abs/path/to/server", "run", "server.py"]
# }
# }
# }
Java's supported path is the Spring AI MCP server starter — the protocol docs point at it rather than at a standalone SDK. WebFlux and stdio starters exist; stdio is the one a desktop client launches.
# Gradle
implementation("org.springframework.ai:spring-ai-starter-mcp-server:2.0.1")
# application.properties
# spring.main.web-application-type=none
# spring.main.banner-mode=off
# logging.pattern.console=
THOSE THREE PROPERTIES ARE NOT COSMETIC. The Spring banner and the console log both write to stdout, and stdout is the JSON-RPC transport — leave them on and the client cannot parse a single frame.
# src/main/resources/application.properties
# The starter speaks MCP revision 2026-07-28.
# No web server: this process talks over stdio.
spring.main.web-application-type=none
# The banner goes to stdout and would corrupt the first frame.
spring.main.banner-mode=off
# Empty pattern disables console logging entirely. Log to a file.
logging.pattern.console=
logging.file.name=./warehouse-mcp.log
@Tool and @ToolParam carry the descriptions the model reads. The starter discovers them and publishes the schema, so there is no registration code to write.
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;
@Service
public class StockTools {
private final InventoryClient inventory;
StockTools(InventoryClient inventory) {
this.inventory = inventory;
}
@Tool(description = "Get the current stock level for a single product code.")
public String getStock(
@ToolParam(description = "The product code, e.g. X-17") String code) {
return code + ": " + inventory.quantityOf(code) + " units";
}
}
A ToolCallbackProvider bean is all the wiring there is. The starter turns it into the server's tool list, which is why the tool class above has no MCP types in it at all.
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class WarehouseMcpApplication {
public static void main(String[] args) {
SpringApplication.run(WarehouseMcpApplication.class, args);
}
@Bean
ToolCallbackProvider stockTools(StockTools tools) {
return MethodToolCallbackProvider.builder()
.toolObjects(tools)
.build();
}
}
THE PACKAGE IS @modelcontextprotocol/server. The older @modelcontextprotocol/sdk name is what most examples still install, and the imports below will not resolve against it.
npm install @modelcontextprotocol/server zod
npm install -D @types/node typescript
registerTool takes the name, a descriptor with a zod inputSchema, and the handler. The schema is what the model sees, so describe() on each field is prompt text.
import { McpServer } from "@modelcontextprotocol/server";
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
import { z } from "zod";
// Speaks MCP revision 2026-07-28.
const server = new McpServer({ name: "warehouse", version: "1.0.0" });
server.registerTool(
"get_stock",
{
description: "Get the current stock level for a single product code.",
inputSchema: z.object({
code: z.string().describe("The product code, e.g. X-17"),
}),
},
async ({ code }) => {
const response = await fetch(`https://inventory.internal/stock/${code}`);
const { quantity } = await response.json();
return { content: [{ type: "text", text: `${code}: ${quantity} units` }] };
},
);
CONSOLE.LOG IS FATAL HERE. stdout carries the JSON-RPC frames, so every log line must go to stderr — console.error is the right call even for information.
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
// stderr. console.log would corrupt the protocol stream.
console.error("warehouse MCP server running on stdio");
}
main().catch((error) => {
console.error("fatal:", error);
process.exit(1);
});
isError tells the client this is a failed call rather than a successful one that happens to say 'error'. Throwing instead kills the tool call with no message the model can act on.
server.registerTool(
"reorder",
{
description: "Place a reorder. Confirm with the user before calling.",
inputSchema: z.object({
code: z.string(),
quantity: z.number().int().positive(),
}),
},
async ({ code, quantity }) => {
if (quantity > 1000) {
return {
isError: true,
content: [{
type: "text",
text: "Orders above 1000 units need a human approver.",
}],
};
}
return { content: [{ type: "text", text: `Ordered ${quantity} of ${code}.` }] };
},
);
QUESTIONS
Which languages are the Model Context Protocol examples in?
C#, Python, Java and JavaScript. Every sample runs as pasted and reads its credentials from an environment variable — no key is ever written into the code.
Which package do I install for Model Context Protocol?
The samples use mcp. The install step on each language tab gives the exact command.
Which Model Context Protocol model do these examples use?
2026-07-28, checked against the official documentation on 26 September 2026. Model ids change often; if a call returns a model-not-found error, check the current list in the official docs.
Where is the official Model Context Protocol documentation?
https://modelcontextprotocol.io/ — it is linked at the bottom of this page, and it is the authority whenever it disagrees with a sample here.
ALSO IN FRAMEWORKS & SDKS
The authority
Official Model Context Protocol documentation
Model names, pricing and rate limits change faster than any page here can. When this page and the official docs disagree, the official docs are right.
https://modelcontextprotocol.io/