Skip to content

MCP servers

Since v0.6, letscode speaks the Model Context Protocol via the letscode-mcp plugin. Configure an MCP server in TOML and restart letscode; the server's tools then show up in /tools and the model can call them, same shape as any other letscode tool.

MCP is deliberately not in core; it's a plugin, on the same footing as letscode-memory. See the plugin page for the internals; this page is for using it.

Install

The letscode-mcp plugin ships in the same repo but is not installed by default. Install it into the same environment as letscode:

uv tool install letscode --with letscode-mcp
# or, from a locally built wheel:
uv pip install --python <letscode-env> letscode_mcp-*.whl
uv pip install -e plugins/letscode-mcp/

letscode discovers the plugin via its letscode entry point at startup; no further setup.

Configure

Add one or more [mcp.servers.<name>] tables to your ~/.letscode/config.toml (or ./.letscode/config.toml for a per-project override):

[mcp.servers.filesystem]
command = "uvx"
args    = ["mcp-server-filesystem", "/path/to/project"]

[mcp.servers.github]
command = "npx"
args    = ["-y", "@modelcontextprotocol/server-github"]
env     = { GITHUB_TOKEN = "$GITHUB_TOKEN" }   # $VAR / ${VAR} expand from the calling env
prefix  = "gh"                                  # optional; default = "mcp_<server>_"
timeout = 10                                    # optional; seconds for handshake

Per-server keys:

key required meaning
command yes Executable to spawn (resolved on PATH).
args no Command-line args (default []).
env no Extra env vars. Values support $VAR / ${VAR} expansion from the calling process's environment (empty string when unset; configs stay portable).
prefix no Tool-name prefix override. Default mcp_<server>_.
timeout no Seconds for initialize + list_tools at startup (default 10).

Project configs merge per-key over the user config: a project-local [mcp.servers.github] overlays the same-named user entry rather than replacing it. Disable a server by removing the block or uninstalling the plugin. There is no enabled = false knob (one less thing to surprise you with).

Tool naming

MCP servers commonly expose generic names like read_file, write_file, search which would collide with letscode built-ins and with each other. Discovered tools are always namespaced:

  • Default: mcp_<server>_<tool>. A server keyed filesystem exposing read_file registers as mcp_filesystem_read_file.
  • Override per server with prefix = "...": prefix = "fs" gives fs_read_file. You own the tradeoff between safety and token cost (shorter names = fewer tokens the model spends on tool names).
  • Within one server, tool names are assumed unique (the MCP server guarantees that); across servers, the prefix disambiguates.

Tool descriptions pass through verbatim from the MCP server's Tool.description, so the model sees exactly what the server author wrote.

Failure modes

The v0.6 rule: failures degrade, they never crash the agent loop. Every one of these surfaces as a one-line stderr warning at registration or a ToolResult(is_error=True) at call time.

When Failure You see
register command not on PATH Warning naming the server + command; that server's tools don't register; other servers continue.
register Startup exceeds timeout Warning; server skipped.
register A tool's inputSchema is malformed Warning naming the server + tool; that tool is skipped; other tools from the same server still register.
call Server crashed since startup ToolResult(is_error=True) with an explanatory message. No auto-restart in v0.6. Restart letscode to recover.
call MCP server returns isError: true ToolResult(is_error=True) carrying the server's error text.

Trust model

The letscode-mcp plugin inherits the same trust model as the built-in bash tool: a configured MCP server runs as a subprocess with your full shell permissions. Only configure servers you trust. letscode-mcp deliberately does not sandbox them; if you need isolation, run letscode itself in a container.

What's not in v0.6

These absences are intentional:

  • Resources (resources/list / resources/read): unblock: a concrete consumer + a design pass on how to surface them (tool? skill? slash command?).
  • Prompts (prompts/list / prompts/get): same unblock as resources.
  • HTTP/SSE transport: stdio covers every official MCP server today; add HTTP if a hosted-MCP consumer shows up.
  • Auto-restart on server crash: the model gets errors; you restart letscode.
  • Per-tool filtering: expose only some of a server's tools. Unblock: a server with 40 tools you don't all want visible to the model.

See also