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:
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 keyedfilesystemexposingread_fileregisters asmcp_filesystem_read_file. - Override per server with
prefix = "...":prefix = "fs"givesfs_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¶
letscode-mcpplugin internals: how the async-to-sync bridge and Pydantic schema synthesis work.notes/21-mcp-integration.md: the design spec (T84).