Skip to content

MCP Server

Repl.Mcp lets your app expose its command graph as an MCP server. It is not a standalone MCP server by itself: the app you build with Repl is the server that agent hosts launch. Every Map(...) becomes a tool; return values, resources, and prompts are all first-class.

using Repl.Mcp;
app.UseMcpServer();

Start with:

Terminal window
myapp mcp serve

Configure your agent host to launch your app:

{
"mcpServers": {
"myapp": {
"command": "myapp",
"args": ["mcp", "serve"]
}
}
}

Every Map(...) becomes an MCP tool automatically. The tool name is the route, description comes from [Description] or .WithDescription(...).

app.Map("contacts list", static (IContactStore store) => store.All())
.WithDescription("List all contacts");
app.Map("contacts add {name} {email:email}", static (string name, string email, IContactStore store) =>
{
store.Add(name, email);
return Results.Success($"Added {name}.");
})
.WithDescription("Add a new contact");

Expose read-only data as MCP resources:

app.Map("contacts export", static (IContactStore store) => store.All())
.AsResource()
.WithDescription("All contacts as a JSON resource");

Register reusable prompt templates:

app.Map("prompts summarize {contact_id:int}", static (int contactId, IContactStore store) =>
{
var contact = store.Get(contactId);
return new McpPromptResult
{
Messages = [new McpMessage(McpRole.User, $"Summarize the following contact: {contact}")]
};
})
.AsPrompt()
.WithDescription("Generate a summary prompt for a contact");

Return HTML from a command and register it as an MCP App resource — the client renders it as an interactive panel:

using System.Net; // WebUtility.HtmlEncode
app.Map("contacts dashboard", static (IContactStore store) =>
{
var contacts = store.All().ToList();
return $$"""
<html>
<body>
<h1>Contacts ({{contacts.Count}})</h1>
<ul>{{string.Join("", contacts.Select(c =>
$"<li>{WebUtility.HtmlEncode(c.Name)}</li>"))}}</ul>
</body>
</html>
""";
})
.AsMcpAppResource()
.WithDescription("Open the contacts dashboard");

Always HTML-encode user-controlled values. Raw interpolation of contact data into HTML creates stored XSS — an attacker who controls the data can execute scripts in the MCP client’s WebView.

Tell the agent how it should handle your tools:

app.Map("contacts delete {id:int}", static (int id, IContactStore store) =>
{
store.Delete(id);
return Results.Success("Contact deleted.");
})
.Destructive()
.WithDescription("Delete a contact permanently");
AnnotationMeaning
.Destructive()Agent should warn before calling
.ReadOnly()No side effects — call freely, parallelize
.Idempotent()Safe to retry on transient failure
.OpenWorld()Reaches external systems (email, HTTP, etc.)
.LongRunning()Enable call-now/poll-later pattern

Some commands are useful for humans in REPL but should be hidden from agents:

app.Map("debug reset", static (IContactStore store) => store.Clear())
.AutomationHidden();

.AutomationHidden() hides the command from MCP and other programmatic surfaces while keeping it accessible in the interactive REPL and --help.

Expose the client’s file system roots to your tools:

app.Map("analyze {path}", static async (string path, IMcpClientRoots roots) =>
{
if (!roots.IsAllowed(path))
return Results.Error("outside_root", $"Path '{path}' is not within an allowed root.");
return await AnalyzeFile(path);
});

For local samples, prefer absolute project paths in host configs. Build the sample once before launching it from an MCP host:

Terminal window
dotnet build samples/08-mcp-server/McpServerSample.csproj

Then configure the host to run without rebuilding. Cursor, Cline, Claude-style clients, and many generic MCP clients use the mcpServers shape:

{
"mcpServers": {
"repl-contacts-sample": {
"command": "dotnet",
"args": [
"run",
"--no-build",
"--project",
"/absolute/path/to/repl/samples/08-mcp-server/McpServerSample.csproj",
"--",
"mcp",
"serve"
]
}
}
}

VS Code/Copilot uses top-level servers in .vscode/mcp.json:

{
"servers": {
"repl-contacts-sample": {
"type": "stdio",
"command": "dotnet",
"args": [
"run",
"--no-build",
"--project",
"/absolute/path/to/repl/samples/08-mcp-server/McpServerSample.csproj",
"--",
"mcp",
"serve"
]
}
}
}

Rebuild-on-launch dotnet run host configs are fragile because cold builds can exceed startup timeouts or emit build output before the MCP JSON-RPC stream starts. Use .cursor/mcp.json for Cursor, Claude Code’s MCP registration/settings flow, Cline’s MCP settings, or the VS Code-specific shape above. Review commands before enabling them: local MCP servers run code on the user’s machine.

  • MCP Concepts — tool annotations, resources, prompt templates, client roots, and the full MCP protocol integration model.
  • For Coding Agents — decision rules, implementation guidance, and copy/paste agent instructions.
  • Agent-Native Design — designing commands for AI agents: naming, descriptions, idempotency, and output contracts.
  • Best Practices — output correctness, logging vs output separation, and security considerations for exposed commands.