Appendix: MCP server implementation examples
Appendix: MCP Server Implementation Examples
Parent article: How to Create MCP Servers for GitHub Copilot
Each implementation follows the same architecture (out-of-process, stdio transport, JSON-RPC) but uses language-specific patterns:
| Language | Runtime | Style | Best for |
|---|---|---|---|
| TypeScript | Node.js | Functional | Web integrations, npm ecosystem |
| C# (.NET) | .NET 8.0+ | Attribute-based | Enterprise apps, existing .NET codebases |
| Python | Python 3.10+ | Decorator-based (FastMCP) | AI/ML, quick prototyping |
π§ Implementation: TypeScript
TypeScript/Node.js is the most common language for MCP servers, with excellent SDK support.
Setup
# Create new project
mkdir my-mcp-server && cd my-mcp-server
npm init -y
# Install dependencies
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node
# Initialize TypeScript
npx tsc --initBasic server structure
// src/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
// Create server instance
const server = new Server(
{
name: "my-mcp-server",
version: "1.0.0",
},
{
capabilities: {
tools: {},
},
}
);
// Define available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "greet",
description: "Generates a greeting message for the given name",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Name of the person to greet",
},
},
required: ["name"],
},
},
],
};
});
// Handle tool calls
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name === "greet") {
const personName = args?.name as string;
return {
content: [
{
type: "text",
text: `Hello, ${personName}! Welcome to MCP.`,
},
],
};
}
throw new Error(`Unknown tool: ${name}`);
});
// Start server with stdio transport
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP server running on stdio");
}
main().catch(console.error);Build and run
// package.json
{
"name": "my-mcp-server",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
}
}// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
},
"include": ["src/**/*"]
}π§ Implementation: C# (.NET)
C# provides strong typing and excellent performance for MCP servers. The official SDK is maintained in collaboration with Microsoft.
Setup
# Create new console project
dotnet new console -n MyMcpServer
cd MyMcpServer
# Add MCP SDK (official package)
dotnet add package ModelContextProtocol --prerelease
dotnet add package Microsoft.Extensions.HostingBasic server structure (attribute-based)
The recommended approach uses attributes and dependency injection:
// Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
// Configure logging to stderr (required for stdio transport)
builder.Logging.AddConsole(options =>
{
options.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly(); // Auto-discovers tools in assembly
await builder.Build().RunAsync();
// Define tools using attributes
[McpServerToolType]
public static class GreetingTools
{
[McpServerTool, Description("Generates a greeting message for the given name")]
public static string Greet(
[Description("Name of the person to greet")] string name)
=> $"Hello, {name}! Welcome to MCP.";
[McpServerTool, Description("Generates a farewell message")]
public static string Farewell(
[Description("Name of the person")] string name,
[Description("Optional custom message")] string? message = null)
=> message ?? $"Goodbye, {name}! See you soon.";
}Advanced: dependency injection and server access
Tools can access services and the MCP server instance:
[McpServerToolType]
public class DataTools
{
[McpServerTool, Description("Query the database and summarize results")]
public static async Task<string> QueryDatabase(
McpServer server, // Injected: access to MCP server
HttpClient httpClient, // Injected: from DI container
[Description("The query to execute")] string query,
CancellationToken cancellationToken)
{
// Use httpClient from dependency injection
var data = await httpClient.GetStringAsync($"/api/query?q={query}", cancellationToken);
// Use server to make sampling requests back to client
var summary = await server.AsSamplingChatClient()
.GetResponseAsync($"Summarize: {data}", cancellationToken);
return summary;
}
}Alternative: low-level handler approach
For fine-grained control over the protocol:
using ModelContextProtocol;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using System.Text.Json;
McpServerOptions options = new()
{
ServerInfo = new Implementation { Name = "MyServer", Version = "1.0.0" },
Handlers = new McpServerHandlers
{
ListToolsHandler = (request, ct) =>
ValueTask.FromResult(new ListToolsResult
{
Tools = [
new Tool
{
Name = "greet",
Description = "Generates a greeting message",
InputSchema = JsonSerializer.Deserialize<JsonElement>("""
{
"type": "object",
"properties": {
"name": { "type": "string", "description": "Name to greet" }
},
"required": ["name"]
}
"""),
}
]
}),
CallToolHandler = (request, ct) =>
{
if (request.Params?.Name == "greet")
{
var name = request.Params.Arguments?["name"]?.ToString() ?? "World";
return ValueTask.FromResult(new CallToolResult
{
Content = [new TextContentBlock { Text = $"Hello, {name}!", Type = "text" }]
});
}
throw new McpProtocolException($"Unknown tool: '{request.Params?.Name}'", McpErrorCode.InvalidRequest);
}
}
};
await using var server = McpServer.Create(new StdioServerTransport("MyServer"), options);
await server.RunAsync();π§ Implementation: Python
Python offers quick prototyping and access to rich AI/ML libraries. The SDK includes FastMCP, a high-level API that minimizes boilerplate.
Setup
# Using uv (recommended)
uv init mcp-server-demo
cd mcp-server-demo
uv add "mcp[cli]"
# Or with pip
pip install "mcp[cli]"Basic server structure (FastMCP β recommended)
FastMCP provides a decorator-based API for minimal boilerplate:
# server.py
from mcp.server.fastmcp import FastMCP
# Create server instance
mcp = FastMCP("my-mcp-server")
@mcp.tool()
def greet(name: str) -> str:
"""Generates a greeting message for the given name.
Args:
name: Name of the person to greet
"""
return f"Hello, {name}! Welcome to MCP."
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers together."""
return a + b
@mcp.resource("config://settings")
def get_settings() -> str:
"""Get application settings."""
return '{"theme": "dark", "language": "en"}'
@mcp.prompt()
def summarize(content: str) -> str:
"""Generate a summarization prompt."""
return f"Please summarize the following content:\n\n{content}"
if __name__ == "__main__":
mcp.run() # Uses stdio transport by defaultRun with:
python server.py
# Or with uv
uv run server.pyAdvanced: context access and progress reporting
Tools can access the MCP context for logging, progress, and client interaction:
from mcp.server.fastmcp import FastMCP, Context
from mcp.server.session import ServerSession
mcp = FastMCP("progress-demo")
@mcp.tool()
async def long_task(
task_name: str,
ctx: Context[ServerSession, None],
steps: int = 5
) -> str:
"""Execute a long-running task with progress updates."""
await ctx.info(f"Starting: {task_name}")
for i in range(steps):
progress = (i + 1) / steps
await ctx.report_progress(
progress=progress,
total=1.0,
message=f"Step {i + 1}/{steps}"
)
await ctx.debug(f"Completed step {i + 1}")
return f"Task '{task_name}' completed"Alternative: low-level server
For full control over the protocol:
import asyncio
from mcp.server.lowlevel import Server
from mcp.server.stdio import stdio_server
import mcp.types as types
server = Server("my-mcp-server")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="greet",
description="Generates a greeting message",
inputSchema={
"type": "object",
"properties": {
"name": {"type": "string", "description": "Name to greet"}
},
"required": ["name"]
}
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
if name == "greet":
person_name = arguments.get("name", "World")
return [types.TextContent(type="text", text=f"Hello, {person_name}!")]
raise ValueError(f"Unknown tool: {name}")
async def main():
async with stdio_server() as (read, write):
await server.run(read, write)
if __name__ == "__main__":
asyncio.run(main())Running with HTTP transport
For remote deployments, use Streamable HTTP:
if __name__ == "__main__":
# Streamable HTTP (recommended for production)
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)