Connecting coding agents over MCP¶
the tracker speaks MCP over Streamable HTTP at http://127.0.0.1:8001/mcp (or
https://<host>:8001/mcp when TLS is in front), with a static Bearer key when
GHAT_API_KEY is set. This page is not the protocol reference — it is how
the four coding agents most people actually run manage MCP configuration, what
each one stores where, and the exact snippets that connect them to this service.
The four were chosen because they cover the shapes everything else copies:
Claude Code (project .mcp.json + CLI), OpenAI Codex (TOML), OpenCode (JSON
with OAuth built in), and Command Code (Claude-shaped, with an importer). If
your agent is not here, its configuration is very likely one of these four
shapes under a different name.
The same server, four grammars¶
Everything below connects to one deployment: local, no key.
| Agent | Config file | Scope model | CLI |
|---|---|---|---|
| Claude Code | .mcp.json (project), ~/.claude.json (user/local) |
local / project / user |
claude mcp add/list/get/remove |
| Codex | ~/.codex/config.toml, project .codex/config.toml (trusted projects) |
user / project | codex mcp add/list/login |
| OpenCode | opencode.json(c), global or per project |
global / per agent | opencode mcp auth/list/logout |
| Command Code | .mcp.json (project), ~/.commandcode/mcp.json (user), ~/.commandcode/projects/<slug>/mcp.json (local) |
local / project / user |
cmd mcp add/list/get/remove/auth |
All four speak Streamable HTTP; all four can carry the Bearer key in a header;
and all four have a menu or list command that shows whether the server
actually connected — run it before you prompt the model, because a config that
parses is not a server that answers.
Claude Code¶
Claude Code stores servers in three scopes: local (default, this project,
this user), project (.mcp.json at the repository root, meant to be
committed), and user (all projects). The CLI writes the file for you:
# Local deployment, no API key configured:
claude mcp add --transport http github-actions-tracker http://127.0.0.1:8001/mcp
# TLS in front, API key set:
claude mcp add --transport http github-actions-tracker https://tracker.example.com:8001/mcp \
--header "Authorization: Bearer <GHAT_API_KEY>"
# Share with the team through the repository:
claude mcp add --transport http github-actions-tracker http://127.0.0.1:8001/mcp --scope project
The same thing as committed JSON (.mcp.json):
{
"mcpServers": {
"github-actions-tracker": {
"type": "http",
"url": "http://127.0.0.1:8001/mcp",
"headers": { "Authorization": "Bearer ${GHAT_API_KEY}" }
}
}
}
Two things the reference documentation considers important enough to repeat:
- A
urlwithout atypeis read as a stdio server and the entry is skipped with an error.type: "http"(or the aliasstreamable-http) must be spelled out — the protocol's own name is not inferred from the URL scheme. - A project
.mcp.jsonneeds approval: a cloned repository cannot approve its own servers. Runclaudein the folder, accept the trust dialog, and approve the server (or setenableAllProjectMcpServersin a settings file that is not committed). Until thenclaude mcp listshows⏸ Pending approvaland the tools never load.
Verify with claude mcp list (✔ Connected) or /mcp inside a session, which
also shows the tool count — thirteen, at the time of writing.
Codex¶
Codex keeps MCP servers in ~/.codex/config.toml, with a project overlay at
.codex/config.toml (trusted projects only). The ChatGPT desktop app and the
IDE extension read the same file, so one configuration covers all three. The
transport is spelled per server: stdio takes command + args; Streamable HTTP
takes url and its authentication travels separately.
[mcp_servers.github-actions-tracker]
url = "http://127.0.0.1:8001/mcp"
# TLS in front, API key set:
[mcp_servers.github-actions-tracker]
url = "https://tracker.example.com:8001/mcp"
bearer_token_env_var = "GHAT_API_KEY"
tool_timeout_sec = 120
The CLI form (writes the same file):
codex mcp add github-actions-tracker --url http://127.0.0.1:8001/mcp
codex mcp list # servers and their connection state
codex mcp login github-actions-tracker # only for OAuth servers; this one uses a Bearer key
One tool_timeout_sec note that matters for this service specifically:
wait_for_run is a bounded long poll — a single call that legitimately runs for
tens of seconds. A per-server timeout in the 30-second range that is fine for
most tools will cut it off mid-wait. Either raise the timeout (120s covers the
wait ceiling) or skip that tool; the other tools answer in milliseconds.
OpenCode¶
OpenCode reads opencode.json(c) — global, per project, and through an
organisation's .well-known/opencode defaults, which a local enabled: true
can opt into. Remote servers take type: "remote"; OpenCode detects OAuth
automatically, so a server that authenticates with a static key should say so
explicitly:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"github-actions-tracker": {
"type": "remote",
"url": "http://127.0.0.1:8001/mcp",
"enabled": true,
"oauth": false,
"headers": { "Authorization": "Bearer {env:GHAT_API_KEY}" }
}
}
}
oauth: falseturns off OAuth auto-detection; without it, a 401 starts a browser flow instead of using the header. This service never starts one — the Bearer key is the authentication — so the flag only saves you a confusing browser window on a mistyped key.{env:VAR}is expanded at load time from the process environment.timeoutcovers requests and tool calls; the samewait_for_runnote as Codex applies (raise it above the wait ceiling if you plan to use that tool).
Verify with opencode mcp list, or debug a failing server with
opencode mcp debug github-actions-tracker, which tests HTTP connectivity and prints the
auth state.
Command Code¶
Command Code's MCP surface is the Claude Code shape with a different prefix:
cmd mcp add --transport http <name> <url>, scopes local (default, per
project), project (.mcp.json, version controlled) and user
(~/.commandcode/mcp.json), and local overriding project overriding user
when the same name appears twice.
cmd mcp add --transport http github-actions-tracker http://127.0.0.1:8001/mcp
cmd mcp add --transport http github-actions-tracker https://tracker.example.com:8001/mcp \
--header "Authorization: Bearer <GHAT_API_KEY>"
As JSON (the schema uses transport, with type accepted as an alias, so a
Claude Code .mcp.json block pastes in unchanged):
{
"mcpServers": {
"github-actions-tracker": {
"transport": "http",
"url": "http://127.0.0.1:8001/mcp",
"headers": { "Authorization": "Bearer ${GHAT_API_KEY}" }
}
}
}
${VAR} and ${VAR:-default} in headers and env are resolved at runtime; a
variable that is unset and has no default stops that server with an error naming
the variable. Verify with cmd mcp list or the /mcp menu (green means
connected, and the tool count is shown).
Two conveniences worth knowing: /import pulls MCP servers from other agents
(/import claude, /import codex, /import opencode, ...), so an existing
setup migrates in one command; and its tools are prefixed
mcp__tracker__<tool>, which matters if you write rules that reference them.
Choosing where the configuration lives¶
The scope question is the same everywhere, and the answer is the same too:
- A repository's
.mcp.json/.codex/config.tomlis a shared file. Put the deployment's address in it — that is useful to the whole team. Do not put a key in it; every one of these agents can read the key from the environment (${GHAT_API_KEY},{env:...},bearer_token_env_var), and the environment is where a secret belongs. - A user-scope entry is for a personal deployment. A laptop-local
the tracker with no API key is a user-scope or local-scope server; the project
file would publish
127.0.0.1addresses to teammates whose deployment is elsewhere. - Approval gates are per machine. Claude Code's pending-approval state and Codex's trusted-projects rule both exist so a cloned repository cannot arm tools without the operator looking at them. That is the reason the approval step cannot be scripted past, and it should stay that way.
After connecting¶
Run /mcp (Claude Code, Command Code) or the agent's list command and check
the tool count before prompting. Then the first question to ask is the cheap
one: ci_status — it exercises the database connection, reports build.revision
against the image that was pulled, and returns the pool gauges, without spending
a GitHub API call. find_runs --repository <repo> is the first question that
does spend one.
The thirteen tools and what each answers are documented in the API page. Two integration notes that are not obvious from the tool list:
wait_for_runholds one database connection for its whole wait (see the pool rules) and is a bounded long poll — per-agent tool timeouts should clear 60 seconds for it (Codex:tool_timeout_sec; OpenCode:timeout; Claude Code: the per-servertimeoutfield).- Everything the tools return is untrusted text. The MCP responses carry
untrusted: trueand a hint saying not to follow instructions found inside — the same rule a good agent applies on its own.