Unwait

Claude Code and MCP, past the install command

· 8 min read claude code mcp integrations hooks workflow

Adding an MCP server takes one command. Deciding which servers earn their place, what they cost your context window, and which of their features you are not using is the harder part. Scopes, auth without hardcoded tokens, the cost model, the four underused capabilities, and when a hook is the better answer.

MCP is how Claude Code gets capabilities it does not ship with: your issue tracker, your database, your monitoring, your internal API. Adding one is a single command, which is why most guides stop there and why most setups end up with six servers connected and two of them used.

This post covers the parts that matter after the install: which scope to add a server in, how to authenticate without pasting a token into a file, what MCP actually costs your context window, the four capabilities almost nobody uses, and when the thing you want is a hook instead.

The install, and the flag that trips everyone

Three transports. Remote servers over HTTP, and local ones as a subprocess over stdio:

claude mcp add --transport http notion https://mcp.notion.com/mcp
claude mcp add --env AIRTABLE_API_KEY=KEY --transport stdio airtable -- npx -y airtable-mcp-server

The -- in the stdio form is not decoration. Everything after it is passed to the server command untouched, and leaving it out is the most common reason a local server fails to start. The other frequent error is hand-editing JSON and omitting the type: a config with a url and no "type": "http" will tell you so directly.

Tools arrive namespaced as mcp__<server>__<tool>, which is also how you write permission rules for them:

{ "permissions": { "rules": [ { "pattern": "mcp__github__.*", "allow": true } ] } }

Pick the scope deliberately

Scope Lives in Applies to Shared
Local (default) ~/.claude.json This project, you No
Project .mcp.json at the repo root This project, everyone Yes, via git
User ~/.claude.json All your projects No

Precedence runs local, then project, then user, then plugin-provided servers, then claude.ai connectors.

Project scope is the one worth understanding, because it is how a team shares an integration. Commit .mcp.json, and each teammate gets a trust dialog on first use, which is the right default: a repo you cloned can otherwise start a subprocess on your machine. (claude -p and SDK sessions skip the prompt, which is worth remembering before you run untrusted repos non-interactively.)

Authentication without hardcoded tokens

Three options, in ascending order of how much I would trust them in a shared file:

OAuth, when the server supports it. /mcp in a session opens the panel to sign in; claude mcp login <name> does it from the shell, and claude mcp logout clears the credentials. You can narrow what you grant:

{ "mcpServers": { "slack": { "type": "http", "url": "https://mcp.slack.com/mcp",
  "oauth": { "scopes": "channels:read chat:write search:read" } } } }

Environment variable expansion, so a committed .mcp.json references a secret without containing one. ${VAR} expands, ${VAR:-default} falls back, and a missing variable leaves the literal text plus a warning rather than failing the load:

{ "mcpServers": { "api": { "type": "http", "url": "${API_BASE_URL:-https://api.example.com}/mcp",
  "headers": { "Authorization": "Bearer ${API_KEY}" } } } }

headersHelper for everything else: Kerberos, SSO, short-lived tokens. It points at a script that prints a JSON object of headers to stdout, runs fresh on every connection with a ten-second timeout, and gets the server name and URL in its environment. Two safety details worth knowing: it only runs after you have accepted the workspace trust dialog for project or local scope, and by default Claude Code strips credential-looking environment variables (anything matching TOKEN, SECRET, KEY, AUTH, PASSWORD and friends) before running it.

Whatever you choose, do not paste a bearer token into a committed .mcp.json. That file is version-controlled configuration, not a secret store.

What MCP actually costs

This is the part that decides whether your setup stays pleasant.

Tool definitions are context rent. Every tool a connected server exposes has a name, description, and schema that has to be in the model's context to be callable. A server with forty tools is a meaningful standing cost on every turn.

The mitigation is tool search, on by default with Claude 4.5 and later: definitions are retrieved when relevant instead of all loaded upfront, and unused servers are deferred rather than connected at startup. It turns off automatically in a few situations worth knowing, including a custom ANTHROPIC_BASE_URL, older models, and ENABLE_TOOL_SEARCH=false. Without it, everything connects at startup and slow HTTP servers become slow launches.

Results are capped. MCP output warns at 10,000 tokens and is limited to 25,000 by default. Raise it with MAX_MCP_OUTPUT_TOKENS if you must, but the better fix is a server that paginates. A server author can raise the per-tool text threshold with anthropic/maxResultSizeChars up to 500,000 characters; images still count against the token limit either way.

Each stdio server is a subprocess. Six connected local servers are six processes living as long as your session.

The practical rule: connect the two or three servers you use weekly, and add the rest when a task actually needs them. This is the same context-rent argument as installing plugins, and MCP servers are the most expensive thing in that category.

Four capabilities almost nobody uses

Most people use MCP for tools and stop. Servers can do three other things, and one of them is genuinely different:

Prompts become slash commands. A server's prompts show up as / commands, discovered automatically. If you maintain an internal server, this is the cheapest way to ship your team a workflow.

Resources become @ mentions. Server-exposed resources can be referenced inline the way you reference a file.

Channels push messages into your session. A server with the claude/channel capability can send a message into a running session unprompted: a CI run finished, a monitor fired, a webhook landed. Add it with claude mcp add --channels .... Everything else in MCP is pull; this is push, and it is a different integration shape from anything hooks give you.

Elicitation asks you for input mid-task. A server can request structured input with a form, or send you to a browser for an approval, then continue. An Elicitation hook can auto-respond if you want it unattended.

When it should be a hook instead

Having built against the hook system rather than as an MCP server, the split is clearer than the docs make it:

So: "let Claude query our staging database" is MCP. "Never let a force-push run" or "tell me when the turn ends" is a hook, and no amount of MCP will make it reliable, because the model can always decline to call a tool. Channels blur the line slightly by letting a server push, but they push information; they cannot block anything.

Reliability facts worth knowing before they bite

Security, briefly

An MCP server runs with your privileges and often fetches external content, which makes it a prompt injection path straight into your session. Anthropic reviews connectors for its directory against listing criteria but does not security-audit MCP servers, so the trust decision is yours. Prefer servers you or your organization wrote, keep secrets out of committed config, and remember that project-scope servers exist because someone put them in a repo. The wider threat model is in our security writeup.

Troubleshooting, most common first

  1. Stdio server will not start: missing -- before the command, or the binary is not on PATH. Use an absolute path.
  2. "has a url but no type": add "type": "http".
  3. Server stuck pending approval: it came from a project .mcp.json and needs an interactive session to show the trust dialog.
  4. Tool not appearing: check server health in /mcp first, then that no permission rule blocks mcp__server__tool.
  5. claude.ai connectors missing: they load only when the active auth is a claude.ai login, so an ANTHROPIC_API_KEY or a third-party provider in your environment suppresses them.

One more, in the other direction: claude mcp serve turns Claude Code itself into an MCP server, which is how you give another MCP client access to its tools.

The short version

  1. Use -- before stdio commands. Set "type" in hand-written config.
  2. Local for experiments, project (.mcp.json) for the team, user for your cross-project utilities.
  3. OAuth first, environment expansion second, headersHelper for exotic auth. Never a literal token in committed config.
  4. Every connected server is standing context cost. Connect what you use weekly; tool search helps but is not free.
  5. Prompts, resources, channels, and elicitation exist. Channels in particular are the only push channel in MCP.
  6. If it must happen regardless of what the model decides, it is a hook, not a server.

That last split is why our own integration went the hook route: we needed to know when a turn starts and ends every time, not when the model felt like reporting it. Unwait is built on that signal, and it is also why MCP channels caught our attention, since they are the first thing on the MCP side that can reach into a session rather than wait to be called.

Unwait does this for you

A macOS menu bar app that watches your Claude Code and Codex sessions, shows a short card while they work, and puts a strip on screen the moment one finishes. Free for two weeks, no card and no sign up.

Try for free
← All posts