Skip to content

Connecting an MCP client

Loom serves its operations as MCP tools at <loom>/mcp. A streamable-HTTP client connects with a URL and a bearer token (none on a single-user server; Cursor and VS Code have install links for that case: Cursor, VS Code); a stdio-only client goes through the mcp-remote bridge. A tool call runs the same auth, rate limit, audit and idempotency path as POST /v1/<op>. The protocol is in the MCP reference.

Plugin or plain MCP

You run Use You get
Claude Code the Claude Code plugin tools, capture hooks, code shunt, skills, the code-scope header
Codex the Codex plugin the same, with Codex wiring
Claude Desktop, Cursor, VS Code, Cline, Windsurf, Zed loom install --client <name> (below) the tools; capture only through a transcript template (Cursor)
an orchestrator running several agents one MCP mount per agent (frameworks.md) shared memory across the agents

A plain MCP entry gives the tools only: no session capture, primer or code shunt. Loom still records an implicit run per MCP session and names the agent after the client's clientInfo.name (Cursor becomes cursor). To teach the agent when to call the tools, add the agent instructions block to a coding client's rule file, with ?toolset=coding on the entry's URL (--toolset coding with loom install, the default only for Claude Code, Codex and Cursor).

Wire a client with loom install

loom install merges the server entry into a client's config file, or, for Claude Code and Codex, runs their mcp add command:

loom install --client cursor --server-url https://loom.example.com --token "$LOOM_API_KEY"
Flag Default Meaning
--client every supported client whose config directory exists one or more of claude-desktop, claude-code, codex, cursor, vscode, cline, windsurf, zed
--server-url LOOM_BASE_URL, then ~/.loom/config.json, else http://localhost:7777 the Loom base URL; the entry points at <url>/mcp
--token LOOM_API_KEY, then ~/.loom/config.json, then ~/.loom/token the JWT the entry authenticates with; required
--name loom the server key in the config file, and the name claude mcp add / codex mcp add registers
--toolset empty: coding for Claude Code, Codex and Cursor, else the server default (LOOM_MCP_TOOLSET) appended as ?toolset=; a toolset name or a + union of curated rosters such as coding+tracker; anything else is rejected before writing
--dry-run off print what would be written or run, touching nothing
--force off Claude Code only: add the user-scope entry even when the loom plugin is installed

Restart the client afterwards. Exit codes: 2 no token, 5 no client detected, 1 any client failed (the others still run). See the CLI reference.

Get a token from the dashboard's Settings → Tokens tab (<server>/settings), which also prints the per-client blocks below, or with loom mint-token --subject you --org <org> --namespaces <ns> --roles writer. A client that only reads can use a viewer token; see tokens and grants.

What it writes per client

Client Config file (Linux; macOS and Windows use the platform equivalent) Section Entry shape
claude-desktop ~/.config/Claude/claude_desktop_config.json mcpServers npx mcp-remote bridge
cursor ~/.cursor/mcp.json mcpServers url + headers
vscode ~/.config/Code/User/mcp.json (a separate file, so settings.json is left alone) servers url + headers + type: "http"
cline ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json mcpServers npx mcp-remote bridge
windsurf ~/.codeium/windsurf/mcp_config.json mcpServers url + headers
zed ~/.config/zed/mcp.json (a sibling of settings.json) context_servers npx mcp-remote bridge
claude-code none: runs claude mcp add … -s user see Claude Code
codex none: runs codex mcp add see Codex

The file is replaced atomically with mode 0600; every other key is kept. A file that is not a valid JSON object is refused, not overwritten.

The two entry shapes, the first as the Cursor command above writes it:

{
  "url": "https://loom.example.com/mcp?toolset=coding",
  "headers": { "Authorization": "Bearer <JWT>" }
}
{
  "command": "npx",
  "args": [
    "-y",
    "mcp-remote",
    "https://loom.example.com/mcp",
    "--allow-http",
    "--header",
    "Authorization: Bearer <JWT>"
  ]
}

The second is for stdio-only clients: mcp-remote bridges stdio to HTTP, and --allow-http lets it reach a local plain-HTTP server. Both shapes store the token in the config file; treat it as a secret.

Claude Code without the plugin

Prefer the plugin. When it is installed, loom install --client claude-code adds no entry (it would list every tool twice) unless you pass --force.

Without the plugin, loom install --client claude-code runs, or prints when claude is not on PATH:

claude mcp add --transport http loom "https://loom.example.com/mcp?toolset=coding" \
  -H 'Authorization: Bearer ${LOOM_API_KEY}' -s user

The header names the variable, not the token; Claude Code expands ${LOOM_API_KEY} at launch. loom install writes LOOM_BASE_URL and LOOM_API_KEY into the env block of ~/.claude/settings.json (the plugin's hooks read them there too) and prints the plugin install commands. For one repository, put the entry in <repo>/.mcp.json:

{
  "mcpServers": {
    "loom": {
      "type": "http",
      "url": "https://loom.example.com/mcp?toolset=coding",
      "headers": { "Authorization": "Bearer ${LOOM_API_KEY}" }
    }
  }
}

Without the plugin's skills, add the agent instructions block to the repository's CLAUDE.md, or to its AGENTS.md with @AGENTS.md in CLAUDE.md.

Codex without the plugin

loom install --client codex runs, or prints when codex is not on PATH:

codex mcp add loom --url "https://loom.example.com/mcp?toolset=coding" --bearer-token-env-var LOOM_API_KEY

Codex reads the token from the environment, so export LOOM_API_KEY in your shell profile. The command also prints the equivalent TOML block, writes url and token to ~/.loom/config.json for the plugin's hooks, and prints the plugin install commands. With the plugin installed, read codex.md first: an entry under the same name replaces the plugin's. Without the plugin, add the agent instructions block to AGENTS.md.

Choosing a toolset

A toolset decides which tools tools/list shows, not what is callable. Every op not hidden from MCP stays callable by name or through loom_op. Under all, an experimental op that neither the core nor the atomic roster lists is left out of the listing; code tools are listed only with LOOM_MCP_CODE_TOOLS=true. Fewer listed tools means less context spent on every turn.

The server default is LOOM_MCP_TOOLSET (core). Coding agents use ?toolset=coding. Curated rosters combine with , or + (?toolset=coding,tracker). The rosters are tabled in the MCP reference.

Two more URL parameters shape a connection:

  • ?schema_profile=compact sends the first paragraph of each description and the argument tree, for a smaller listing; standard (default) and full (adds outputSchema) are the others.
  • A second registration on another toolset adds a roster without changing the first: register loom-tracker at ?toolset=tracker beside loom at ?toolset=coding, for example with loom install --client cursor --name loom-tracker --toolset tracker.

Other editors and agents

loom install covers the clients in its table; VS Code also has install links. Each client below reaches <loom>/mcp with its own config. In the entries, URL is http://localhost:7777/mcp for a single-user server on the same machine (no header needed) or https://loom.example.com/mcp with a token; add ?toolset=coding for a coding agent that uses the code index (choosing a toolset). Restart or reload the client afterwards.

Client Where Entry Checked
JetBrains AI Assistant (IntelliJ IDEA, PyCharm, WebStorm, GoLand) Settings → Tools → AI Assistant → Model Context Protocol (MCP) → Add {"mcpServers": {"loom": {"url": "URL"}}} (single-user server only: no header field or sign-in is documented)
JetBrains Junie ~/.junie/mcp/mcp.json, or .junie/mcp/mcp.json in the project {"mcpServers": {"loom": {"url": "URL", "headers": {…}}}}
Visual Studio (Copilot agent mode) %USERPROFILE%\.mcp.json, or .mcp.json in the solution {"servers": {"loom": {"type": "http", "url": "URL", "headers": {…}}}} (Visual Studio also reads .vscode/mcp.json)
Kiro ~/.kiro/settings/mcp.json, or .kiro/settings/mcp.json {"mcpServers": {"loom": {"url": "URL", "headers": {…}}}}
Gemini CLI ~/.gemini/settings.json, or .gemini/settings.json {"mcpServers": {"loom": {"httpUrl": "URL", "headers": {…}}}}, or gemini mcp add --transport http --header "Authorization: Bearer <JWT>" loom URL (drop --header for a single-user server); or the extension, below; Gemini lists a server as disabled in a folder it does not trust connected (gemini mcp add, and the extension)
GitHub Copilot CLI ~/.copilot/mcp-config.json {"mcpServers": {"loom": {"type": "http", "url": "URL", "headers": {…}, "tools": ["*"]}}} accepted
Neovim with mcphub.nvim (also serves avante.nvim and CodeCompanion) ~/.config/mcphub/servers.json {"mcpServers": {"loom": {"url": "URL", "headers": {…}}}}
Emacs with mcp.el and gptel mcp-hub-servers in your init file, then gptel-mcp-connect (setq mcp-hub-servers '(("loom" . (:url "URL")))) (single-user server only: no header field is documented)
OpenCode opencode.json in the project (read inside a git repository), or ~/.config/opencode/opencode.json opencode mcp add loom --url URL --header "Authorization=Bearer <JWT>", which writes {"mcp": {"servers": {"loom": {"type": "remote", "url": "URL", "headers": {…}}}}} connected
Kilo Code (extension and CLI) kilo.jsonc in the project, or ~/.config/kilo/kilo.jsonc {"mcp": {"loom": {"type": "remote", "url": "URL", "headers": {…}}}} connected (CLI)
Cline CLI ~/.cline/data/settings/cline_mcp_settings.json cline mcp add loom URL --transport http --header "Authorization: Bearer <JWT>" --yes (the Cline extension: loom install) accepted
Google Antigravity ~/.gemini/config/mcp_config.json, or .agents/mcp_config.json in the workspace {"mcpServers": {"loom": {"serverUrl": "URL", "headers": {…}}}} (the key is serverUrl)
Devin Desktop and Devin CLI .devin/mcp_config.local.json in the project (git-ignored, for the token), or ~/.config/devin/mcp_config.json {"mcpServers": {"loom": {"url": "URL", "transport": "http", "headers": {…}}}} (loom install --client windsurf writes the older ~/.codeium/windsurf/ path)
Qwen Code ~/.qwen/settings.json, or .qwen/settings.json qwen mcp add --transport http loom URL --header "Authorization: Bearer <JWT>"
Kimi Code CLI ~/.kimi-code/mcp.json, or .kimi-code/mcp.json {"mcpServers": {"loom": {"url": "URL", "headers": {…}}}}
Grok Build ~/.grok/config.toml, or .grok/config.toml; it also reads a project .mcp.json grok mcp add --transport http loom URL --header "Authorization: Bearer <JWT>"
Qoder CLI: ~/.qoder/settings.json, .qoder/settings.json, or a project .mcp.json; IDE: Settings → MCP → My Servers → Add (its docs show no header field: single-user server) {"mcpServers": {"loom": {"type": "http", "url": "URL", "headers": {…}}}}
MiniMax Code ~/.minimax/mcp.json, or the MCP Servers tab {"mcpServers": {"loom": {"type": "streamable-http", "url": "URL", "headers": {…}}}}
goose ~/.config/goose/config.yaml, under extensions loom: {type: streamable_http, name: loom, enabled: true, uri: URL, headers: {Authorization: "Bearer <JWT>"}}
Amp ~/.config/amp/settings.json, or .amp/settings.json, under amp.mcpServers {"loom": {"url": "URL", "headers": {…}}} (amp mcp add documents no header flag: use the file for a token)
Warp ~/.warp/.mcp.json {"mcpServers": {"loom": {"url": "URL", "headers": {…}}}}
Factory droid ~/.factory/mcp.json droid mcp add loom URL --type http --header "Authorization: Bearer <JWT>" --no-oauth
Augment (Auggie CLI) ~/.augment/settings.json (--project writes .augment/settings.json) auggie mcp add loom --transport http --url URL --header "Authorization: Bearer <JWT>"
Hermes Agent ~/.hermes/config.yaml, under mcp_servers hermes mcp add loom --url URL --auth header, which asks for the token; or loom: {url: URL, headers: {Authorization: "Bearer <JWT>"}} in the file
Zed context_servers in settings.json {"context_servers": {"loom": {"url": "URL", "headers": {…}}}} (loom install writes the mcp-remote bridge instead)
OpenHands Customize → MCP Servers transport SHTTP, the URL, Authentication Bearer token with the token in the API key field
n8n an MCP Client Tool node endpoint URL, transport HTTP Streamable, Bearer authentication with the token
smolagents (Python) code MCPClient({"url": URL, "transport": "streamable-http"}) (single-user server: its docs show no header field)

{…} is {"Authorization": "Bearer <JWT>"}; leave it out for a single-user server. In Checked, connected means the client's own status command reported the server connected (a local Loom with a token, no model call; OpenCode 2.3.0, Kilo CLI 7.8.27, Gemini CLI 0.68.0), and accepted that the client wrote or accepted the entry but has no command that checks the connection (Cline CLI 3.0.74, GitHub Copilot CLI 1.0.110); a blank one was not run.

Gemini CLI also installs Loom as an extension, which asks for the MCP URL and, for a team server, a token (leave it empty for a single-user server):

gemini extensions install https://github.com/ClickHouse/loom

Gemini installs an extension from the repository's latest release; --ref <branch> installs another ref. The extension also installs Loom's two agent skills, loom and loom-code. gemini extensions config clickhouse-loom changes the two values later; a scripted install passes --skip-settings and writes LOOM_MCP_URL and LOOM_API_KEY to the extension's .env file (~/.gemini/extensions/clickhouse-loom/.env). gemini mcp list shows the loom server as connected once the URL and token are right. On a single-user server, an empty token still sends Authorization: Bearer, which the server accepts with a warning in its log for each request; gemini mcp add without --header (above) avoids it.

Claude Desktop's custom connectors (Settings → Connectors) reach the server from the provider's cloud, so they need a Loom on a public HTTPS address; on the same machine, loom install --client claude-desktop writes the mcp-remote bridge entry.

A hosted agent or app builder (Replit Agent, Lovable, v0, the cloud editions of n8n, Dify, Flowise or Langflow, and Zapier or Make) and another chat app's custom connector (Grok, Perplexity, Le Chat) reach Loom only at a public HTTPS address; a self-hosted builder reaches it on the same network. ChatGPT's custom MCP servers connect from its cloud and send no custom header, so they need a public HTTPS address and the browser sign-in.

Clients that sign in by browser

Needs LOOM_OAUTH_ENABLED=true. Loom then acts as an OAuth 2.1 authorization server: a client that supports MCP OAuth connects with the bare URL and a browser sign-in, and gets a Loom JWT for the signed-in user. Details: MCP reference, configuration.

Checking the connection

  • The client lists loom_* tools after a restart. A 401 in the client means the bearer is missing or invalid: the server answers with WWW-Authenticate: Bearer, so most clients report "needs authentication".
  • After the first tool call the client appears on the admin drawer's Agents page (agents.connected), named after its clientInfo.name.
  • loom_activate returns the session primer, the quickest end-to-end check that the token resolves to the namespace you expect.
  • loom install --dry-run shows exactly what would be written, for comparing with a config file that behaves unexpectedly.