Threads AI

MCP

Connect any Model Context Protocol server in one line, with its credentials kept on your side.

mcp() turns an MCP server's tools into agent tools. Pass it in tools next to your own tools.

import { mcp } from "@threads/mcp";

const docs = mcp({ name: "docs", url: "https://example.com/mcp" });

const assistant = agent({ model, tools: [docs, github, files] });

Python needs the mcp extra (threads[mcp]).

Each server tool shows up as mcp__<server>__<tool>, for example mcp__docs__search. If the server offers resources, the agent also gets mcp__<server>__read_resource.

Remote servers

Give a url. Headers can hold a secret(), which is read on your host when the connection opens and never reaches the log, the model or the sandbox.

const github = mcp({
  name: "github",
  url: "https://api.githubcopilot.com/mcp/",
  headers: { Authorization: secret("GITHUB_MCP_TOKEN") },
  tools: { allow: ["get_issue", "list_pull_requests"] },
  effect: "read_only",
});

threads speaks Streamable HTTP and falls back to the older SSE transport when a server doesn't support it.

Local servers

Give a command and args to start a stdio server as a child process on your host. It starts with only PATH and HOME from your environment plus the env you pass, so your other environment variables don't leak into it.

const files = mcp({
  name: "files",
  command: "npx",
  args: ["-y", "@modelcontextprotocol/server-filesystem", "./data"],
  tools: { deny: ["write_file"] },
});

Running a stdio server inside the sandbox (runs: "sandbox") is not supported yet and is a setup error. Servers run on the host.

Options

namestringrequired

Lowercase letters, digits and underscores. Used in every tool name.

urlstring

A remote server. Give exactly one of url or command.

headersRecord<string, string | Secret>

HTTP headers for a remote server.

commandstring

A local stdio server to start.

argsstring[]

Arguments for command.

envRecord<string, string | Secret>

Extra environment variables for command.

tools{ allow?: string[], deny?: string[] }

Keep only these tools, or drop these, by the server's own tool names. Applied before the agent sees the list.

effectstringdefault unguarded

What the server's tools do to the outside world, as for your own tools. read_only lets them run without approval in the default mode. The default, unguarded, means an interrupted call parks instead of being retried.

deferbooleandefault false

The model sees only the server's tool names, listed in tool_search's description, until it loads them with tool_search; their descriptions and schemas are sent after the load. Use it for servers with many tools; each load rewrites the provider's prompt cache once. See load them on demand.

effect: "idempotent" needs dedupWindowMs (dedup_window_ms in Python): each call then carries its effect key in _meta as threads/effect_key, for the server to dedup on.

When servers connect

Each check() and each run opens its own connection to every server, in both languages. The tool list is fetched then, filtered, and fixed for the life of the thread. The connection closes when the check() or the run ends, however it ends, which also stops a stdio server. There is nothing for you to close. A server that can't be reached is a setup error, mcp_unreachable, naming the server: check() returns it and a run throws it as ConfigError. The agent never runs with tools silently missing.

Edit on GitHub

On this page