{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/mcp-csharp-create",
  "version": "1.0.1",
  "name": "mcp-csharp-create",
  "description": "Create Model Context Protocol servers using the official C# SDK (ModelContextProtocol NuGet package) and the dotnet new mcpserver project template. Servers expose tools, prompts, and resources that LLMs can discover and invoke via the MCP protocol.",
  "system_prompt_fragment": "# C# MCP Server Creation\n\nCreate Model Context Protocol servers using the official C# SDK (`ModelContextProtocol` NuGet package) and the `dotnet new mcpserver` project template. Servers expose tools, prompts, and resources that LLMs can discover and invoke via the MCP protocol.\n\n## When to Use\n\n- Starting a new MCP server project from scratch\n- Adding tools, prompts, or resources to an existing MCP server\n- Choosing between stdio (`--transport local`) and HTTP (`--transport remote`) transport\n- Setting up ASP.NET Core hosting for an HTTP MCP server\n- Wrapping an external API or service as MCP tools\n\n## Stop Signals\n\n- **Server already exists and needs debugging?** → Use `mcp-csharp-debug`\n- **Need tests or evaluations?** → Use `mcp-csharp-test`\n- **Ready to publish?** → Use `mcp-csharp-publish`\n- **Building an MCP client, not a server** → This skill is server-side only\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Transport type | Yes | `stdio` (local/CLI) or `http` (remote/web). Ask user if not specified — default to stdio |\n| Project name | Yes | PascalCase name for the project (e.g., `WeatherMcpServer`) |\n| .NET SDK version | Recommended | .NET 10.0+ required. Check with `dotnet --version` |\n| Service/API to wrap | Recommended | External API or service the tools will interact with |\n\n## Workflow\n\n> **Commit strategy:** Commit after completing each step so scaffolding and implementation are separately reviewable.\n\n### Step 1: Verify prerequisites\n\n1. Confirm .NET 10+ SDK: `dotnet --version` (install from https://dotnet.microsoft.com if < 10.0)\n\n2. Check if the MCP server template is already installed:\n   ```bash\n   dotnet new list mcpserver\n   ```\n   If \"No templates found\" → install: `dotnet new install Microsoft.McpServer.ProjectTemplates`\n\n### Step 2: Choose transport\n\n| Choose **stdio** if… | Choose **HTTP** if… |\n|----------------------|---------------------|\n| Local CLI tool or IDE plugin | Cloud/web service deployment |\n| Single user at a time | Multiple simultaneous clients |\n| Running as subprocess (VS Code, GitHub Copilot) | Cross-network access needed |\n| Simpler setup, no network config | Containerized deployment (Docker/Azure) |\n\n**Default:** stdio — simpler, works for most local development. Users can add HTTP later.\n\n### Step 3: Scaffold the project\n\n**stdio server:**\n```bash\ndotnet new mcpserver -n <ProjectName>\n```\nIf the template times out or is unavailable, use `dotnet new console -n <ProjectName>` and add `dotnet add package ModelContextProtocol`.\n\n**HTTP server:**\n```bash\ndotnet new web -n <ProjectName>\ncd <ProjectName>\ndotnet add package ModelContextProtocol.AspNetCore\n```\nThis is the recommended approach — faster and more reliable than the template. The template also supports HTTP via `dotnet new mcpserver -n <ProjectName> --transport remote`, but `dotnet new web` gives you more control over the project structure.\n\n**Template flags reference:** `--transport local` (stdio, default), `--transport remote` (ASP.NET Core HTTP), `--aot`, `--self-contained`.\n\n### Step 4: Implement tools\n\nTools are the primary way MCP servers expose functionality. Add a class with `[McpServerToolType]` and methods with `[McpServerTool]`:\n\n```csharp\nusing ModelContextProtocol.Server;\nusing System.ComponentModel;\n\n[McpServerToolType]\npublic static class MyTools\n{\n    [McpServerTool, Description(\"Brief description of what the tool does.\")]\n    public static async Task<string> DoSomething(\n        [Description(\"What this parameter controls\")] string input,\n        CancellationToken cancellationToken = default)\n    {\n        // Implementation\n        return $\"Result: {input}\";\n    }\n}\n```\n\n**Critical rules:**\n- Every tool method **must** have a `[Description]` attribute — LLMs use this to decide when to call the tool\n- Every parameter **must** have a `[Description]` attribute\n- Accept `CancellationToken` in all async tools\n- Use `[McpServerTool(Name = \"custom_name\")]` only if the default method name is unclear\n\n**DI injection patterns** — the SDK supports two styles:\n\n1. **Method parameter injection (static class):** DI services appear as method parameters. The SDK resolves them automatically — they do not appear in the tool schema.\n\n2. **Constructor injection (non-static class):** Use when tools need shared state or multiple services:\n```csharp\n[McpServerToolType]\npublic class ApiTools(HttpClient httpClient, ILogger<ApiTools> logger)\n{\n    [McpServerTool, Description(\"Fetch a resource by ID.\")]\n    public async Task<string> FetchResource(\n        [Description(\"Resource identifier\")] string id,\n        CancellationToken cancellationToken = default)\n    {\n        logger.LogInformation(\"Fetching {Id}\", id);\n        return await httpClient.GetStringAsync($\"/api/{id}\", cancellationToken);\n    }\n}\n```\nRegister services in Program.cs:\n```csharp\nvar builder = Host.CreateApplicationBuilder(args);\nbuilder.Logging.AddConsole(options =>\n    options.LogToStandardErrorThreshold = LogLevel.Trace);\n\nbuilder.Services.AddHttpClient();        // registers IHttpClientFactory + HttpClient\n// ILogger<T> is registered by default — no extra setup needed.\n\nbuilder.Services.AddMcpServer()\n    .WithStdioServerTransport()\n    .WithToolsFromAssembly();            // discovers non-static [McpServerToolType] classes\n\nawait builder.Build().RunAsync();\n```\n\n**For the full attribute reference, return types, DI injection, and builder API patterns**, see [references/api-patterns.md](references/api-patterns.md).\n\n### Step 5: Add prompts and resources (optional)\n\n**Prompts** — reusable LLM interaction templates:\n```csharp\n[McpServerPromptType]\npublic static class MyPrompts\n{\n    [McpServerPrompt, Description(\"Summarize content into one sentence.\")]\n    public static ChatMessage Summarize(\n        [Description(\"Content to summarize\")] string content) =>\n        new(ChatRole.User, $\"Summarize this into one sentence: {content}\");\n}\n```\n\n**Resources** — data the LLM can read:\n```csharp\n[McpServerResourceType]\npublic static class MyResources\n{\n    [McpServerResource(UriTemplate = \"config://app\", Name = \"App Config\",\n        MimeType = \"application/json\"), Description(\"Application configuration\")]\n    public static string GetConfig() => JsonSerializer.Serialize(AppConfig.Current);\n}\n```\n\n### Step 6: Configure Program.cs\n\n**stdio transport:**\n```csharp\nusing Microsoft.Extensions.DependencyInjection;\nusing Microsoft.Extensions.Hosting;\nusing Microsoft.Extensions.Logging;\nusing ModelContextProtocol.Server;\n\nvar builder = Host.CreateApplicationBuilder(args);\nbuilder.Logging.AddConsole(options =>\n    options.LogToStandardErrorThreshold = LogLevel.Trace); // CRITICAL: stderr only\n\nbuilder.Services.AddMcpServer()\n    .WithStdioServerTransport()\n    .WithToolsFromAssembly();\n\nawait builder.Build().RunAsync();\n```\n\n**HTTP transport:**\n```csharp\nusing ModelContextProtocol.Server;\n\nvar builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddMcpServer()\n    .WithHttpTransport()\n    .WithToolsFromAssembly();\n\n// Register services your tools need via DI\n// builder.Services.AddHttpClient();\n// builder.Services.AddSingleton<IMyService, MyService>();\n\nvar app = builder.Build();\napp.MapMcp();                     // exposes MCP endpoint at /mcp (Streamable HTTP)\napp.MapGet(\"/health\", () => \"ok\"); // health check for container orchestrators\napp.Run();\n```\n\n**Key HTTP details:** `MapMcp()` defaults to `/mcp` path. For containers, set `ASPNETCORE_URLS=http://+:8080` and `EXPOSE 8080`. The MCP HTTP protocol uses Streamable HTTP — no special client config needed beyond the URL.\n\n**For transport configuration details** (stateless mode, auth, path prefix, `HttpContextAccessor`), see [references/transport-config.md](references/transport-config.md).\n\n### Step 7: Verify the server starts\n\n```bash\ncd <ProjectName>\ndotnet build\ndotnet run\n```\n\nFor stdio: the process starts and waits for JSON-RPC input on stdin.\nFor HTTP: the server listens on the configured port.\n\n## Validation\n\n- [ ] Project builds with no errors (`dotnet build`)\n- [ ] All tool classes have `[McpServerToolType]` attribute\n- [ ] All tool methods have `[McpServerTool]` and `[Description]` attributes\n- [ ] All parameters have `[Description]` attributes\n- [ ] stdio: logging directed to stderr, not stdout\n- [ ] HTTP: `app.MapMcp()` is called in Program.cs\n- [ ] Server starts successfully with `dotnet run`\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| stdio server outputs garbage or hangs | Logging to stdout corrupts JSON-RPC protocol. Set `LogToStandardErrorThreshold = LogLevel.Trace` |\n| Tool not discovered by LLM clients | Missing `[McpServerToolType]` on the class or `[McpServerTool]` on the method. Verify `.WithToolsFromAssembly()` in Program.cs |\n| LLM doesn't understand when to use a tool | Add clear `[Description]` attributes on both the method and all parameters |\n| `WithToolsFromAssembly()` fails in AOT | Reflection-based discovery is incompatible with Native AOT. Use `.WithTools<MyTools>()` instead |\n| Parameters not appearing in tool schema | `CancellationToken`, `IMcpServer`, and DI services are injected automatically — they do not appear in the schema. Only parameters with `[Description]` are exposed |\n| HTTP server returns 404 | `app.MapMcp()` must be called. Check the request path matches the configured route |\n\n## Related Skills\n\n- `mcp-csharp-debug` — Run, debug, and test with MCP Inspector\n- `mcp-csharp-test` — Unit tests, integration tests, evaluations\n- `mcp-csharp-publish` — NuGet, Docker, Azure deployment\n\n## Reference Files\n\n- [references/api-patterns.md](references/api-patterns.md) — Complete attribute reference, return types, DI injection, builder API, dynamic tools, experimental APIs. **Load when:** implementing tools, prompts, or resources beyond the basic patterns shown above.\n- [references/transport-config.md](references/transport-config.md) — Detailed transport configuration: stateless HTTP mode, OAuth/auth, custom path prefix, `HttpContextAccessor`, OpenTelemetry observability. **Load when:** configuring advanced transport options or authentication.\n\n## More Info\n\n- [C# MCP SDK](https://github.com/modelcontextprotocol/csharp-sdk) — Official SDK repository\n- [Build an MCP server (.NET)](https://learn.microsoft.com/dotnet/ai/quickstarts/build-mcp-server) — Microsoft quickstart\n- [MCP Specification](https://modelcontextprotocol.io/specification/) — Protocol specification",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/mcp-csharp-create"
  ],
  "tags": [
    "dotnet-ai",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-ai/skills/mcp-csharp-create/SKILL.md",
  "lifecycle": "stable",
  "category": "dotnet",
  "provenance": {
    "source": "dotnet/skills",
    "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-ai/skills/mcp-csharp-create/SKILL.md",
    "author": "Microsoft / .NET Foundation",
    "license": "MIT",
    "notes": "Imported by scripts/import-anthropic-skills.py. Description taken from the fragment's first paragraph: the original SKILL.md description was a mangled YAML indicator and the upstream file no longer exists at its recorded path."
  }
}