{
  "schema": "https://ai-atoms.com/schemas/skill-v1.json",
  "type": "skill",
  "id": "skill/mcp-csharp-publish",
  "version": "1.0.1",
  "name": "mcp-csharp-publish",
  "description": "Publish and deploy MCP servers to their target platforms. stdio servers are distributed as NuGet tool packages. HTTP servers are containerized and deployed to Azure or other container hosts. Both can optionally be listed in the official MCP Registry.",
  "system_prompt_fragment": "# C# MCP Server Publishing\n\nPublish and deploy MCP servers to their target platforms. stdio servers are distributed as NuGet tool packages. HTTP servers are containerized and deployed to Azure or other container hosts. Both can optionally be listed in the official MCP Registry.\n\n## When to Use\n\n- Packaging a stdio MCP server for NuGet distribution\n- Creating a Docker container for an HTTP MCP server\n- Deploying to Azure Container Apps or App Service\n- Publishing to the official MCP Registry for discoverability\n- Setting up `server.json` metadata for the MCP Registry\n\n## Stop Signals\n\n- **Server not tested yet?** → Use `mcp-csharp-test` first\n- **Server not working locally?** → Use `mcp-csharp-debug`\n- **No server project yet?** → Use `mcp-csharp-create`\n- **Publishing a non-MCP NuGet package?** → Use `nuget-trusted-publishing` instead\n\n## Inputs\n\n| Input | Required | Description |\n|-------|----------|-------------|\n| Transport type | Yes | `stdio` → NuGet path, `http` → Docker/Azure path |\n| Target destination | Yes | NuGet.org, Docker registry, Azure Container Apps, Azure App Service, MCP Registry |\n| Project path | Yes | Path to the `.csproj` file |\n| Package ID / server name | Required for publishing | NuGet `PackageId` or MCP Registry name |\n\n## Workflow\n\n### Step 1: Choose the publishing path\n\n| Transport | Primary Destination | Users Run With |\n|-----------|-------------------|----------------|\n| **stdio** | NuGet.org | `dnx YourPackage@version` |\n| **HTTP** | Docker → Azure | Container URL |\n\nBoth paths can optionally publish to the MCP Registry for discoverability.\n\n### Step 2a: NuGet publishing (stdio servers)\n\n1. **Configure `.csproj`** with package properties:\n```xml\n<PropertyGroup>\n  <PackAsTool>true</PackAsTool>\n  <ToolCommandName>mymcpserver</ToolCommandName>\n  <PackageId>YourUsername.MyMcpServer</PackageId>\n  <Version>1.0.0</Version>\n  <Authors>Your Name</Authors>\n  <Description>MCP server for interacting with MyService</Description>\n  <PackageLicenseExpression>MIT</PackageLicenseExpression>\n  <PackageTags>mcp;modelcontextprotocol;ai;llm</PackageTags>\n  <PackageReadmeFile>README.md</PackageReadmeFile>\n</PropertyGroup>\n\n<ItemGroup>\n  <None Include=\"README.md\" Pack=\"true\" PackagePath=\"\\\" />\n</ItemGroup>\n```\n\n2. **Build and pack:**\n```bash\ndotnet build -c Release\ndotnet pack -c Release\n```\n\n3. **Test locally before publishing:**\n```bash\ndotnet tool install --global --add-source bin/Release/ YourUsername.MyMcpServer\nmymcpserver --help          # verify it runs\ndotnet tool uninstall --global YourUsername.MyMcpServer\n```\n\n4. **Push to NuGet.org:**\n```bash\ndotnet nuget push bin/Release/*.nupkg \\\n  --api-key YOUR_NUGET_API_KEY \\\n  --source https://api.nuget.org/v3/index.json\n```\n\n5. **Verify** — users configure in `mcp.json`:\n```json\n{\n  \"servers\": {\n    \"MyMcpServer\": {\n      \"type\": \"stdio\",\n      \"command\": \"dnx\",\n      \"args\": [\"YourUsername.MyMcpServer@1.0.0\", \"--yes\"]\n    }\n  }\n}\n```\n\n**For detailed NuGet packaging and trusted publishing setup**, see [references/nuget-packaging.md](references/nuget-packaging.md).\n\n### Step 2b: Docker containerization (HTTP servers)\n\n1. **Create Dockerfile:**\n```dockerfile\nFROM mcr.microsoft.com/dotnet/sdk:10.0 AS build\nWORKDIR /src\nCOPY *.csproj ./\nRUN dotnet restore\nCOPY . ./\nRUN dotnet publish -c Release -o /app\n\nFROM mcr.microsoft.com/dotnet/aspnet:10.0\nWORKDIR /app\nCOPY --from=build /app .\n\n# Non-root user for security\nRUN adduser --disabled-password --gecos '' appuser\nUSER appuser\n\nENV ASPNETCORE_URLS=http://+:8080\nEXPOSE 8080\nHEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \\\n  CMD curl -f http://localhost:8080/health || exit 1\nENTRYPOINT [\"dotnet\", \"MyMcpServer.dll\"]\n```\n\n2. **Build and test locally:**\n```bash\ndocker build -t mymcpserver:latest .\ndocker run -d -p 3001:8080 -e API_KEY=test-key --name mymcpserver mymcpserver:latest\ncurl http://localhost:3001/health\n```\n\n3. **Push to container registry:**\n```bash\n# Docker Hub\ndocker tag mymcpserver:latest <yourusername>/<mymcpserver>:1.0.0\ndocker push <yourusername>/<mymcpserver>:1.0.0\n\n# Azure Container Registry\naz acr login --name yourregistry\ndocker tag mymcpserver:latest <yourregistry>.azurecr.io/<mymcpserver>:1.0.0\ndocker push <yourregistry>.azurecr.io/<mymcpserver>:1.0.0\n```\n\n### Step 3: Deploy to Azure (HTTP servers)\n\n**Azure Container Apps** (recommended — serverless with auto-scaling):\n```bash\naz containerapp create \\\n  --name mymcpserver \\\n  --resource-group mygroup \\\n  --environment myenvironment \\\n  --image <yourregistry>.azurecr.io/<mymcpserver>:1.0.0 \\\n  --target-port 8080 \\\n  --ingress external \\\n  --min-replicas 0 \\\n  --max-replicas 10 \\\n  --secrets api-key=my-actual-api-key \\\n  --env-vars API_KEY=secretref:api-key\n```\n\n**Azure App Service** (traditional web hosting):\n```bash\naz webapp create \\\n  --name mymcpserver \\\n  --resource-group mygroup \\\n  --plan myplan \\\n  --deployment-container-image-name <yourregistry>.azurecr.io/<mymcpserver>:1.0.0\n```\n\n**For detailed Azure deployment**, see [references/docker-azure.md](references/docker-azure.md).\n\n### Step 4: Publish to MCP Registry (optional)\n\nList your server in the official MCP Registry for discoverability.\n\n1. **Install `mcp-publisher`:**\n```bash\n# macOS/Linux\nbrew install mcp-publisher\n\n# Or download from https://github.com/modelcontextprotocol/registry/releases\n```\n\n2. **Create `.mcp/server.json`** (or run `mcp-publisher init` to generate interactively):\n```json\n{\n  \"$schema\": \"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json\",\n  \"name\": \"io.github.username/servername\",\n  \"description\": \"Your server description\",\n  \"version\": \"1.0.0\",\n  \"packages\": [{\n    \"registryType\": \"nuget\",\n    \"registryBaseUrl\": \"https://api.nuget.org\",\n    \"identifier\": \"YourUsername.MyMcpServer\",\n    \"version\": \"1.0.0\",\n    \"transport\": { \"type\": \"stdio\" }\n  }],\n  \"repository\": {\n    \"url\": \"https://github.com/username/repo\",\n    \"source\": \"github\"\n  }\n}\n```\n\n> **Version consistency (critical):** The root `version`, `packages[].version`, and `<Version>` in `.csproj` **must all match**. A mismatch causes registry validation failures or users downloading the wrong version.\n\n3. **Authenticate and publish:**\n```bash\nmcp-publisher login github      # name must be io.github.<username>/... for GitHub auth\nmcp-publisher publish\n```\n\n4. **Verify:**\n```bash\ncurl \"https://registry.modelcontextprotocol.io/v0.1/servers?search=io.github.<username>/<servername>\"\n```\n\n**For Registry details** (namespace conventions, environment variables, CI/CD automation), see [references/mcp-registry.md](references/mcp-registry.md).\n\n### Step 5: Security checklist\n\n- [ ] No hardcoded secrets — use environment variables or Key Vault\n- [ ] HTTPS enabled for HTTP transport in production\n- [ ] Health check endpoint implemented\n- [ ] Input validation on all tool parameters\n- [ ] Rate limiting considered for HTTP servers\n\n## Validation\n\n- [ ] **NuGet:** Package installs and runs via `dnx PackageId@version`\n- [ ] **Docker:** Container starts and health check passes\n- [ ] **Azure:** Server is reachable and tools respond\n- [ ] **MCP Registry:** Server appears at `registry.modelcontextprotocol.io`\n- [ ] MCP client can connect and call tools on the deployed server\n\n## Common Pitfalls\n\n| Pitfall | Solution |\n|---------|----------|\n| NuGet package doesn't run as a tool | Missing `<PackAsTool>true</PackAsTool>` in `.csproj` |\n| Version mismatch between `.csproj` and `server.json` | Keep `<Version>`, `server.json` root `version`, and `packages[].version` in sync |\n| Docker container exits immediately | Check entrypoint DLL name matches project output. Run `docker logs mymcpserver` for errors |\n| Azure Container App returns 502 | Target port mismatch. Ensure `--target-port` matches `ASPNETCORE_URLS` port in the container |\n| MCP Registry rejects publish | Name must follow namespace convention: `io.github.<username>/<name>` for GitHub auth |\n| API keys leaked in Docker image | Use multi-stage builds. Never `COPY` `.env` files. Pass secrets via `--env-vars` at runtime |\n\n## Related Skills\n\n- `mcp-csharp-create` — Create a new MCP server project\n- `mcp-csharp-debug` — Running and interactive debugging\n- `mcp-csharp-test` — Automated tests and evaluations\n\n## Reference Files\n\n- [references/nuget-packaging.md](references/nuget-packaging.md) — Complete NuGet `.csproj` configuration, `server.json` for MCP, NuGet.org push, testing with `dnx`, version management. **Load when:** publishing a stdio server to NuGet.\n- [references/docker-azure.md](references/docker-azure.md) — Production Dockerfile patterns, ACR setup, Azure Container Apps full configuration, App Service with Key Vault, secrets management. **Load when:** deploying an HTTP server to Docker or Azure.\n- [references/mcp-registry.md](references/mcp-registry.md) — `mcp-publisher` CLI installation, `server.json` schema, namespace conventions (GitHub vs DNS auth), CI/CD automation. **Load when:** publishing to the official MCP Registry.\n\n## More Info\n\n- [NuGet publishing](https://learn.microsoft.com/nuget/nuget-org/publish-a-package) — NuGet.org publishing guide\n- [Azure Container Apps](https://learn.microsoft.com/azure/container-apps/) — Serverless container hosting\n- [MCP Registry](https://registry.modelcontextprotocol.io) — Official MCP server registry",
  "applicable_domains": [
    "code",
    "dotnet",
    "engineering"
  ],
  "invocation": [
    "/mcp-csharp-publish"
  ],
  "tags": [
    "dotnet-ai",
    "dotnet",
    "csharp",
    "microsoft"
  ],
  "authored_by": "anthropics",
  "source_url": "https://github.com/dotnet/skills/blob/main/plugins/dotnet-ai/skills/mcp-csharp-publish/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-publish/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."
  }
}