Code Mode: turning MCP tool calls into a TypeScript execution step
🔌 Plug-and-play library to enable agents to call MCP and UTCP tools via code execution.
At a glance
- What is it?
- The @utcp/code-mode library gives an agent one tool that runs TypeScript against registered MCP, HTTP, file and CLI providers. It is a good fit for coding agents with shell access and a poor fit for MCP-only clients, where the separate CLI path does not apply.
- Who is it for?
- Adopt Code Mode if your agent already writes and runs code, and you want one execution tool instead of a large function-calling schema. Do not adopt it if your client only speaks MCP and cannot spawn a shell, because the recommended path is the utcp CLI and the MCP server is described as the fallback for those clients.
- Can I use it commercially?
- Yes, with conditions. MPL-2.0 is a weak copyleft licence: you can use it inside commercial and closed-source software, but if you distribute changes to its own files, you must publish those changes under the same licence.
- Is it still maintained?
- Yes. The repository last received commits 21 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem Code Mode is aimed at
Function calling forces a model to emit JSON that matches a schema, one call at a time. The README frames the cost of that pattern directly: an agent that needs a pull request, its comments and its reviews makes three separate round trips, and the model reprocesses context between each one. The repository's own example chains those three GitHub calls inside a single code block and returns a filtered object, which the README describes as replacing fifteen or more traditional tool calls.
Code Mode is built for agent authors who control the execution loop and can run code on the model's behalf. It is not an end-user product. If you are assembling an agent that talks to many MCP servers, or to REST APIs described by OpenAPI specs, and you are tired of shipping hundreds of tool definitions in every prompt, this library is the layer being offered. The README also cites Apple, Cloudflare and Anthropic as arguing that code execution is a more efficient route than dumping function information and extracting JSON, so the project is positioning itself inside an argument that predates it rather than inventing the premise.
One tool instead of a schema wall
The mechanism is a substitution. Rather than exposing every provider function to the model, the library exposes a single tool that accepts TypeScript source. Inside that source, the registered providers appear as namespaced objects. The README's GitHub example calls github.get_pull_request, github.get_pull_request_comments and github.get_pull_request_reviews as ordinary async functions, then does the filtering and counting in the same block and returns a plain object.
Two supporting mechanisms make that usable. The first is progressive discovery: client.searchTools('github pull request') is documented as returning a small set of relevant tools rather than the full catalogue, so the agent can look before it writes. The second is interface generation. The README shows auto-generated TypeScript declarations for a tool's input, with doc comments carried through from the source schema, which is what lets a model write a correct call without the schema being pasted into the prompt.
Execution returns more than a value. The documented shape is { result, logs }, so console output from inside the block is captured and surfaced alongside the return value. That matters for debugging a chain that fails halfway: you get the intermediate prints, not just the final error.
Providers are registered, not bundled
The library does not ship a fixed tool set. You register providers, and the README lists four call template types: mcp for Model Context Protocol servers, http for REST APIs with auto-discovery, file for local JSON or YAML configurations, and cli for command-line execution. In the three-line example, registration is a single call: await client.registerManual({ name: 'github', /* MCP config */ }). The manual is named, and that name becomes the namespace the generated code calls into.
This is the part of the design worth reading carefully. The README states that tools are only accessible through registered UTCP or MCP servers, which is the security boundary as described: the sandbox has no ambient network or filesystem access beyond what the registered providers expose. Whether that holds depends on the sandbox implementation, which the README characterises only as Node.js isolates. That phrase is doing a lot of work and the material does not name the isolation primitive, so treat the boundary as a property to verify rather than a guarantee to assume.
Getting it running: the library path
Installation is a single package: npm install @utcp/code-mode. The README's first example is three steps in TypeScript. Create the client with const client = await CodeModeUtcpClient.create(). Register a provider with await client.registerManual({ name: 'github', /* MCP config */ }). Execute with const { result } = await client.callToolChain(`/* TypeScript */`).
The same pattern appears with a fuller body, where the chain destructures { result, logs } and the code inside uses the provider namespace. Discovery is a separate call, client.searchTools with a natural-language string. There is no configuration file required for this path and no environment variables are mentioned for it.
Version history matters here. v1.0.5 is described as the first public release, dated 2025-11-15, and v1.0.6 added Python support alongside TypeScript. If you are reading older examples, check which language they target, because the release notes treat Python as a recent addition rather than something present from the start.
The utcp CLI is the path the maintainers prefer
For agents that can run shell commands, the README recommends the utcp CLI over the MCP server, and the reasoning is stated plainly: no MCP server, no client config, no environment variables. The agent writes a .utcp_config.json itself and drives everything from the shell. Claude Code, Cursor, Codex and Claude Cowork are named as examples of this kind of agent.
The entry point is a guide the agent reads: npx -y @utcp/code-mode-cli prompt. From there, discovery is npx -y @utcp/code-mode-cli search "<task>", which the README says returns tools plus TypeScript interfaces. Execution is npx -y @utcp/code-mode-cli run with a heredoc containing the chain, and the example calls openlibrary.read_search_json_search_json_get and maps over r.docs. Authentication is handled by npx -y @utcp/code-mode-cli login <manual>, described as interactive OAuth that writes a token to .env, with Notion given as the example.
This is the most distinctive part of the project. The README says you can hand the agent a UTCP call template, an OpenAPI spec, or a plain-English description of an API, and it will write the configuration and discover tools from it. The CLI and the MCP server are documented as wrapping the same @utcp/code-mode engine, so the choice between them is about client capability, not about features.
The MCP-only case, and where the design strains
The README is explicit that the MCP server exists for MCP-only clients such as Claude Desktop, and that the CLI is preferred whenever the agent has shell access. That is an unusual admission and it should shape your decision. If your client cannot spawn a process, you are on the secondary path, and the self-configuring workflow that the CLI advertises does not apply to you.
The larger limitation is the sandbox. The README claims secure VM sandboxing with Node.js isolates, timeout protection with configurable execution limits, and zero external dependencies. None of the specifics are in the material supplied: no isolation API is named, no default timeout value is given, and no failure mode for a killed execution is described. For a component whose entire job is running model-generated code, that is the gap that matters most. A timeout that kills a chain mid-write is a different problem from one that kills it before any provider call, and the README does not say which you get.
The benchmark table is also worth reading with care. It cites an independent Python benchmark study reporting 67%, 75% and 88% improvements across simple, medium and complex scenarios, plus a $9,536/year cost figure at 1,000 scenarios per day. The study is external and Python-based, and the table counts iterations rather than wall-clock time, so the numbers describe round-trip reduction, not latency. Treat the cost figure as the study's estimate under its own assumptions, not as a property of this library.
How it compares to the function-calling status quo
The alternative is what the README calls traditional tool calling: define each tool as a JSON schema, let the model select one, execute it, append the result, and repeat. That approach is well supported across model providers and needs no execution environment on your side. Its cost is context. Every tool definition occupies prompt space, and every step reprocesses the accumulated history.
The difference in approach is where the orchestration lives. In function calling, the model orchestrates by choosing tools one at a time and your code mediates each hop. In Code Mode, the model orchestrates by writing a program, and the library mediates one hop. That moves the failure surface: you no longer debug a sequence of tool selections, you debug a script, which is easier when the script is short and harder when it is long, because a single execution can contain many provider calls with no checkpoint between them.
A second alternative for shell-capable agents is simply giving the model a shell and letting it call APIs with curl. The utcp CLI is closer to that than it first appears, but it adds the config file, the discovery step and the generated interfaces, which is what turns an ad-hoc curl into something a model can call correctly on the first attempt.
Licence, maintenance and what to check first
The project is licensed MPL-2.0, a file-level copyleft licence. Modifications to files covered by the licence must be made available under the same terms; combining the library with proprietary code in separate files is generally permitted, but the boundary is a legal question and this is not legal advice. If you fork a file and change it, read the licence text rather than relying on that summary. The repository is not archived and the last push recorded is 2026-08-25, with v1.0.6 in November 2025 adding Python support. That is a short release history: two public releases, the first only days before the second.
Upgrade cost is hard to estimate from the material. The library has no external dependencies per the README, which removes one class of breakage, but the generated TypeScript interfaces are derived from provider schemas, so a provider that changes its API changes what your stored chains can call. The CLI writes a .utcp_config.json and a .env token file, both of which live in your repository and both of which you own.
What to verify before adopting, in order: run the CLI prompt guide against one real API and confirm the generated interfaces match what you expect; force a timeout and observe how the failure is reported, since the README does not describe it; and check whether the isolation primitive behind the Node.js sandbox claim is documented anywhere in the repository, because that claim is the one carrying the most weight and the least detail.
Editorial conclusion
Adopt Code Mode if your agent already writes and runs code, and you want one execution tool instead of a large function-calling schema. Do not adopt it if your client only speaks MCP and cannot spawn a shell, because the recommended path is the utcp CLI and the MCP server is described as the fallback for those clients. Before committing, verify three things in your own environment: which sandbox the Node.js VM actually is, what the timeout default is and how it reports a kill, and whether the generated TypeScript interfaces match the tools you registered. Those are the points the README leaves open.
Community notes