Org-level MCP server declaration channel
An adopting organization declares the MCP servers it wants wired into its
agents through a single org-owned file at the repository root:
mcp-servers.org.json. This is the HOW realization of
spec 0091; the WHY and the CLI-first
forge baseline it builds on are recorded in
ADR-0015 and
spec 0090.
The channel is a declaration artifact: it maps each server's name to how
the server is reached and authorized (transport, endpoint, headers or
environment). It does not hold server implementation code — a server the
organization itself writes and hosts belongs in artifacts/community/mcp-servers/
instead (see layers.md).
Setup translates the manifest into each CLI's native MCP configuration and folds it in after the operator-preservation merge of spec 0089, so the resolved precedence is:
framework-reserved > org > operator-pre-existing
The file is org-owned and excluded from upstream synchronization
(.crewrig/core-paths.txt), exactly like AGENTS.org.md: upstream never
modifies, restores, or aborts on it. Upstream ships it empty — a fresh adopter
gets no org MCP server until it populates the channel itself.
Where declarations go
Setup reads only the .mcpServers object. Declare your servers there.
{
"mcpServers": {
"atlassian": {
"transport": "http",
"url": "https://mcp.atlassian.example/mcp",
"headers": { "Authorization": "Bearer ${ATLASSIAN_TOKEN}" }
},
"acme-internal": {
"transport": "stdio",
"command": "acme-mcp",
"args": ["--serve"],
"env": { "ACME_REGION": "eu-west-1" }
}
}
}
Footgun — do not confuse
_examplewith.mcpServers. The shipped file carries an inert_exampleblock (keyed directly by server name) purely as documentation. Setup never reads_example. A server declared under_example(or at any key other than.mcpServers) is silently not delivered. Copy it into.mcpServersto activate it.
Schema
Each entry under .mcpServers is keyed by the server name and carries a
transport plus its transport-specific fields:
| Field | Applies to | Meaning |
|---|---|---|
transport |
all | stdio (default when omitted), http, or sse. |
command |
stdio |
Executable to launch the server. |
args |
stdio |
Argument array (optional). |
env |
stdio |
Environment variables map (optional). |
cwd |
stdio |
Working directory (optional, spec 0185). |
timeout |
all | Timeout threshold (optional, spec 0185). |
url |
http / sse |
Endpoint the CLI connects to. |
headers |
http / sse |
Request headers map, e.g. an Authorization bearer (optional). |
Servers are delivered as declared — the framework does not validate, health-check, deduplicate, or normalize them (spec 0091 Out of scope).
Precedence and collision warnings
- Reserved names win for the framework (R10). The names
mempalaceandsequentialthinkingare framework-managed (MCP_RESERVED_NAMESinscripts/lib/common.sh). An org declaration under a reserved name is not applied; setup prints a non-silent warning. - Org wins over an operator's hand-added server (R11). If an org-declared name collides, under a non-reserved name, with a server an operator added by hand, the org declaration wins and setup prints a non-silent warning naming the server (the operator's prior entry stays in the timestamped backup).
- Everything else survives (R6). Any operator server the org channel does not name is preserved unchanged.
Per-CLI delivery
| CLI | stdio | http / sse |
|---|---|---|
| Claude Code | ✅ claude mcp add --scope user … -- cmd args |
✅ claude mcp add --transport http … --header … |
| Gemini CLI | ✅ native {command,args,env} in ~/.gemini/settings.json |
✅ native {type,url,headers} |
| Copilot CLI | ✅ native {type:"stdio",command,args,env} in ~/.copilot/mcp-config.json |
✅ native {type,url,headers} |
| Antigravity CLI | ✅ native {command,args,env} in ~/.gemini/config/mcp_config.json |
✅ native {serverUrl,headers} |
Antigravity remote-entry key. The neutral url is delivered to all four
CLIs, but the native remote key differs per CLI. Antigravity uses serverUrl
(not url) and carries no transport type field — one shape covers both http
and Streamable-HTTP/SSE — while Gemini/Copilot use url and Claude uses
--transport http. This shape is grounded against the official
Antigravity MCP docs
(file ~/.gemini/config/mcp_config.json; remote entry {serverUrl, headers}),
which supersede the stale "format not publicly documented" note in spec 0054 —
so both stdio and remote org servers now reach all four CLIs. See
cli-matrix.md row 7h.
Delivery happens at setup time. After editing mcp-servers.org.json, re-run
the setup script for each CLI you use (scripts/setup-<cli>-interactive.sh);
setup is idempotent. Because the manifest is excluded from sync, your
declarations survive both a repeated setup run and an upstream synchronization.
Re-adding a forge MCP server
Under the CLI-first forge baseline (spec 0090 / ADR-0015) no forge MCP ships in
any default — forge operations route through gh / glab / tea. An
organization that nonetheless wants a GitHub, GitLab, or Gitea MCP server
re-adds it through this channel, not by editing an upstream-owned default.
Declare it under .mcpServers like any other server. For example, a GitHub MCP
reached over stdio:
{
"mcpServers": {
"github": {
"transport": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"],
"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}" }
}
}
}
or a remote forge MCP reached over http with a bearer token:
{
"mcpServers": {
"github": {
"transport": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${GITHUB_PERSONAL_ACCESS_TOKEN}" }
}
}
}
Substitute the command/URL and headers your forge MCP actually requires.
Credentials
Securing credentials is explicitly out of scope (spec 0091): the framework delivers the declaration verbatim and the organization owns whatever secrets it adds. To avoid committing a plaintext secret into a tracked file:
- Use a
${VAR}reference (as in the examples above) and export the real value in the operator's environment; the CLI resolves it at runtime. - Prefer each CLI's own credential store where available — e.g.
claude mcp loginfor OAuth servers, or-e KEY=$VARso the value is read from the environment rather than inlined.
The shipped mcp-servers.org.json carries no operational server and no
credential; its placeholder header is a ${VAR} reference, never a literal
secret.