Skip to content

Cadenza Mcp

Jung Hyun, Nam edited this page May 28, 2026 · 2 revisions

Cadenza.Mcp

A Model Context Protocol server in a single .cs file. Expose typed Tool, Resource, and Prompt primitives to Claude Desktop, Cursor, VS Code, or any MCP-aware client — no SDK glue, no project tree.

#:sdk Cadenza.Mcp@1.0.15
Base SDK Microsoft.NET.Sdk
Transport stdio (default) — clients spawn the process
Underlying library ModelContextProtocol
AOT-clean Yes

Minimal example

#!/usr/bin/env dotnet run
#:sdk Cadenza.Mcp@1.0.15

Tool("read_file", "Read a UTF-8 text file from disk",
    (string path) => ReadText(path));

Tool("list_files", "List files matching a glob pattern (e.g., **/*.cs)",
    (string pattern) => Glob(pattern).ToArray());

await Run();

Cadenza infers the JSON schema for the tool's input from the handler's parameter types and serializes the return value back to the client. Parameter XML comments become argument descriptions in the schema.

Tier 1 surface

Tool(name, description, handler)       // expose a callable tool
Resource(uri, name, handler)           // expose a readable resource at a fixed URI
Prompt(name, description, handler)     // expose a parameterized prompt template
await Run();                           // start the MCP server on stdio

Plus the shared modules — Fs, Sh, Http, Env, Json. WriteLine is deliberately not exposed — stdout is reserved for the JSON-RPC framing. Use Log.Info / Log.Warn / Log.Error / Log.Debug (they write to stderr).

Tools

The handler can be sync or async, with any combination of typed parameters.

Tool("get_weather", "Get current weather for a city",
    async (string city) =>
    {
        var key = Env.Get("OPENWEATHER_API_KEY")
            ?? throw new InvalidOperationException("OPENWEATHER_API_KEY missing");
        return await Http.GetText(
            $"https://api.openweathermap.org/data/2.5/weather?q={city}&appid={key}");
    });

Multiple parameters become a JSON object input:

Tool("search", "Search code in the repo",
    (string pattern, string filetype, int maxResults) =>
        Glob($"**/*.{filetype}")
            .Where(f => ReadText(f).Contains(pattern))
            .Take(maxResults)
            .ToArray());

Resources

Expose a known file or computed value at a fixed URI:

Resource("readme://current", "Project README",
    () => ReadText("README.md"));

Resource("config://defaults", "Default config",
    () => Json.Stringify(new { theme = "dark", verbose = true }, Ctx.Default));

Clients can list resources and read them on demand. The handler can be sync or async.

Prompts

Prompts are parameterized templates the client can invoke (e.g. Claude's "Slash commands"):

Prompt("review_code", "Review the given code for issues",
    (string code) => $@"Please review this code for bugs, security, and style:

{code}");

Prompt("explain_diff", "Explain a git diff in plain English",
    (string diff, string audience) =>
        $"Explain this diff to a {audience}:\n\n{diff}");

Logging

Never call WriteLine from an MCP stdio server — it corrupts the JSON-RPC stream. Use Log.* instead; Cadenza routes those to stderr:

Log.Info("Cadenza.Mcp server starting on stdio");
Log.Warn($"Tool {name} took {ms} ms (>1s budget)");
Log.Error("Upstream API failed", ex);

Registering with a client

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json (macOS) / %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "files": {
      "command": "dotnet",
      "args": ["run", "/absolute/path/to/mcp-files.cs"]
    }
  }
}

Cursor

Settings → MCP:

{
  "mcpServers": {
    "files": {
      "command": "dotnet",
      "args": ["run", "/absolute/path/to/mcp-files.cs"]
    }
  }
}

VS Code

The C# extension's MCP integration accepts the same JSON shape. Drop the snippet into .vscode/mcp.json (workspace) or the global MCP config.

From a container

Ship the MCP server as an OCI image and have the client launch it via docker run -i (-i is required so stdin/stdout pipe through):

{
  "mcpServers": {
    "files": {
      "command": "docker",
      "args": ["run", "-i", "--rm", "my-org/files:1.0"]
    }
  }
}

See Deployment Container.

When to use it

  • Wrapping a database, internal API, or filesystem area as MCP tools
  • Prototyping MCP servers before promoting to a full project
  • Hobby integrations where one .cs is the whole product

When not to use it

  • You need an OpenAI-compatible chat endpoint instead of MCP → use Cadenza Agent
  • You're serving HTTP responses to humans → use Cadenza Web

Samples

  • mcp-files.csTool only, minimal
  • mcp-extended.csTool + Resource + Prompt together

See Samples for the full annotated list.

See also

Clone this wiki locally