Appendix: MCP server implementation examples

tech
prompt-engineering
github-copilot
mcp
Appendix with complete MCP server implementation examples in TypeScript, C# (.NET), and Python β€” project setup, tool definitions, resource handlers, error handling, and registration.
Author

Dario Airoldi

Published

March 7, 2026

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 --init

Basic 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.Hosting

Basic 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]"

Advanced: 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)