mcp.json
Configure, authorize, inspect, edit, and repair local and remote MCP servers across CompozyOS scopes.
mcp.json is an optional JSON sidecar for MCP server declarations. CompozyOS accepts it at global,
workspace, agent, and skill scopes.
Quick Reference
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
mcpServers | object map | empty | Server-name keys with server objects. | Camel-case top-level MCP server map. |
mcp_servers | object map | empty | Server-name keys with server objects. | Snake-case top-level MCP server map. |
<server>.transport | string | inferred | stdio or http. | MCP transport. url without transport is HTTP. |
<server>.command | string | required for stdio | Non-empty after trimming. | Local MCP server command. |
<server>.url | string URL | required for remote | Absolute URL. | Remote MCP endpoint for http. |
<server>.auth | object | empty | OAuth 2.1 PKCE config. | Remote auth metadata and client settings. |
<server>.args | string array | empty | Strings on stdio only. | Command arguments for stdio servers. |
<server>.env | string map | empty | String keys and values on stdio. | Literal environment values for stdio servers. |
Unknown JSON fields fail parsing. Trailing JSON values after the first document also fail parsing.
Missing mcp.json files are treated as absent.
There is no implemented mcp.json tool-mapping schema today. The current parser accepts MCP server
definitions only.
Supported Locations
| Location | Scope | Applies when |
|---|---|---|
$COMPOZY_HOME/mcp.json | Global top-level MCP | General runtime config loads. Defaults to ~/.compozy/mcp.json. |
<workspace>/.compozy/mcp.json | Workspace top-level MCP | A workspace root is resolved for the command or session. |
<agent-dir>/mcp.json | Agent-local MCP | Next to AGENT.md. |
<skill-dir>/mcp.json | Skill-local MCP | Next to SKILL.md. |
Complete Example
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/work"],
"env": {
"LOG_LEVEL": "info"
}
},
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"secret_env": {
"GITHUB_TOKEN": "vault:mcp/global/github/env/GITHUB_TOKEN"
}
},
"remote-docs": {
"transport": "http",
"url": "https://mcp.example.com/mcp",
"auth": {
"registration": "auto",
"scopes": ["mcp.read", "mcp.write"]
}
}
}
}The snake-case form is also accepted:
{
"mcp_servers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
}
}
}Top-Level Keys
| Key | Type | Default | Valid values | Description |
|---|---|---|---|---|
mcpServers | object map | empty | Keys are server names; values are server objects. | Primary JSON form. |
mcp_servers | object map | empty | Keys are server names; values are server objects. | Alternate JSON form. Loaded after mcpServers inside the same file. |
If both top-level keys are present in the same file and both define the same server name,
mcp_servers replaces the whole server object from mcpServers.
Server Object
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
| Map key | string | required | Non-empty after trimming. Duplicate trimmed names fail. | Server name. |
transport | string | inferred | stdio or http. | Local command servers default to stdio; URL servers default to http. |
command | string | required for stdio | Non-empty after trimming. | Executable command. Only valid with stdio transport. |
url | string URL | required for remote | Absolute URL. | Remote MCP endpoint. Required for http; invalid for stdio. |
auth | object | empty | OAuth config listed below. | Remote MCP auth configuration. Only valid for remote transports. |
args | string array | empty | Strings on stdio only. | Process arguments for stdio servers. |
env | string map | empty | String keys and values on stdio only. | Literal environment values. CompozyOS does not expand $VAR, ~, or shell syntax. Management reads expose keys only. |
secret_env | string map | empty | env:NAME or vault:mcp/** refs on stdio only. | Secret bindings resolved at launch. Management reads expose configured keys only. |
catalog_entry | string | empty | CompozyOS-managed catalog entry ID. | Provenance stamped by catalog install. Generic settings replacement clears it. |
catalog_version | string | empty | CompozyOS-managed catalog version. | Provenance stamped with catalog_entry. Do not author these fields manually. |
Both env and secret_env reject process-injection keys such as NODE_OPTIONS, PYTHONPATH,
PYTHONHOME, LD_PRELOAD, and DYLD_*. Literal env also rejects secret-like names; place those
bindings in secret_env with an env:NAME or vault:mcp/** ref.
Management read and write projection
GET /api/settings/mcp-servers does not return literal environment values or binding refs. Each
stdio item exposes env_keys and secret_env_keys; OAuth config exposes
client_secret_configured. To keep an unchanged value during an exact-target PUT, include its
name in preserve_env. Use preserve_secrets.secret_env and
preserve_secrets.oauth_client_secret for existing secret bindings. A rename or target change
requires a new value or an explicit Vault binding because preservation never crosses keys, scopes,
or source files.
Remote OAuth Auth Object
Remote MCP servers can use OAuth 2.1 authorization code with PKCE. The config contains only
metadata and client settings; access tokens, refresh tokens, authorization codes, PKCE verifiers,
and client-secret values are stored or handled by the auth subsystem and are never written to
mcp.json.
CompozyOS-managed MCP secret refs live in the Vault under the mcp namespace. Catalog and settings
writes create scope-qualified refs:
- global:
vault:mcp/global/<server>/env/<KEY>andvault:mcp/global/<server>/oauth/client-secret - workspace:
vault:mcp/ws/<workspace-segment>/<server>/env/<KEY>andvault:mcp/ws/<workspace-segment>/<server>/oauth/client-secret
The workspace segment is the workspace ID when it already fits the Vault grammar; otherwise CompozyOS
encodes it deterministically. A catalog install can also bind any existing, present vault:mcp/**
ref without taking ownership of that ref.
When a later install replaces an owned canonical ref, CompozyOS deletes it only after the new binding is persisted and only when the replacement no longer references it. Shared and retained refs remain untouched. If cleanup fails and CompozyOS can restore every deleted secret, it restores the prior MCP definition and returns an error. If secret restoration is partial, or the prior definition cannot be restored, the replacement remains committed and the mutation returns a warning describing the residual cleanup state.
| Field | Type | Default | Valid values | Description |
|---|---|---|---|---|
registration | string | auto | auto, pre_registered. | auto resolves protected-resource metadata, CIMD, then one DCR fallback. |
issuer_url | string URL | conditional | Absolute http or https URL. | Required with pre_registered; must match the discovered issuer. |
client_id | string | conditional | Non-empty. | Required with pre_registered. |
client_secret_ref | string | empty | vault:mcp/**. | Optional client secret for pre_registered only. |
scopes | string array | empty | Unique non-empty strings. | Requested OAuth scopes. |
For registration = "auto", CompozyOS resolves protected-resource metadata, uses the provider's
client metadata document support when available, and otherwise makes one Dynamic Client Registration
attempt. A pre-registered entry must provide both issuer_url and client_id.
Client metadata identity
By default, CompozyOS identifies itself to compatible authorization servers with the public client
metadata document at https://compozy.com/.well-known/mcp-client.json. It lists the default loopback
callback http://127.0.0.1:2123/api/mcp/oauth/callback.
For a self-hosted daemon, a non-default port, or any custom callback, host your own client metadata
document that lists the exact callback URI and set both global restart-required options in
config.toml:
[mcp.oauth]
client_metadata_url = "https://compozy.example/.well-known/mcp-client.json"
redirect_uri = "https://compozy.example/api/mcp/oauth/callback"CompozyOS skips CIMD when the active redirect URI is not listed in the document and can use the one available Dynamic Client Registration fallback instead. A provider that supports neither path fails authorization with a typed diagnostic; do not work around that by adding a secret to the catalog.
Authorize A Remote Server
OAuth sessions and PKCE verifiers live in the daemon, so CLI, HTTP, and UDS clients complete the same flow. Start authorization with a copyable URL:
compozy mcp auth login linearFor a remote operator or a daemon with a non-loopback HTTP bind, use manual completion and paste the full redirect URL:
compozy mcp auth login linear --manual--timeout bounds the whole authorization attempt, including manual input and exchange. The active
daemon PKCE session expiry can shorten that deadline; it never extends it.
Target a workspace sidecar explicitly with both selectors:
compozy mcp auth login linear --scope workspace --workspace <workspace-id>
compozy mcp auth status linear --scope workspace --workspace <workspace-id> -o jsonThe equivalent daemon routes are:
POST /api/settings/mcp-servers/{name}/auth/beginPOST /api/settings/mcp-servers/{name}/auth/exchangePOST /api/settings/mcp-servers/{name}/auth/logoutGET /api/settings/mcp-servers/{name}/auth/status
Each route requires scope; workspace targets also require workspace_id. The routes are available
over HTTP and UDS. Begin requires a JSON body with mode: "automatic" or mode: "manual"; manual
mode creates a fresh paste-based PKCE session instead of reusing an automatic callback session.
The named status route reads only the target's redacted auth state; it does not start a runtime probe.
GET /api/mcp/oauth/callback is HTTP-only. Automatic begin uses the configured
mcp.oauth.redirect_uri exactly and requires its host to be loopback; it does not derive a callback
from the daemon listener. Set redirect_uri explicitly when the daemon uses a non-default loopback
port. An unavailable callback runtime returns a documented 503 HTML response. Exchange accepts a
JSON body containing the full redirect_url only, and success requires redacted status with both
authenticated and token_present.
Each pending session and issued token is bound to the exact scoped server definition. Editing or deleting the server invalidates pending completion. If the transport, remote URL, or OAuth settings change, CompozyOS keeps the previous token record but does not report it as authenticated, refresh it, revoke it against the replacement provider, or send its bearer to that provider. Authorize the new definition again. Tokens created before definition binding was introduced also require one new login.
Install From The Curated Catalog
Use the catalog install command when an MCP entry comes from Marketplace:
compozy mcp install github --scope workspace --workspace <workspace-id> \
--vault-ref github_personal_access_token=vault:mcp/shared/github-token -o jsonFor workspace scope, pass both --scope workspace and --workspace <workspace-id>. The daemon
loads the catalog entry, locks its command, URL, and auth fields, checks required values and bound
Vault refs, writes the MCP sidecar through the serialized config-apply lifecycle, and stamps catalog
provenance. Inspect the response's apply object for the record ID, lifecycle, active generation,
and required repair action. The install does not probe the MCP server, so a successful config apply
does not claim server readiness. OAuth entries return next_step: "authorize"; other entries return
next_step: "none".
Direct API callers must always include the nullable values property: send null for an input-free
entry. Omitting values violates the request contract and returns 400 before installation.
Use --set id=value for a non-secret catalog input, --secret id to enter one through stdin or a
hidden prompt, or --vault-ref id=vault:mcp/... for an existing secret. Neither secret path puts
secret material in shell arguments or history. Management reads return configured field names, never
bound refs or secret values.
Structured JSON output returns the complete install response: the committed mcp_server, config
apply outcome, next_step, and any top-level diagnostics. In particular,
mcp_install_event_persist_failed means the server and config apply remain committed while the
Marketplace install event was not persisted. Inspect warnings before deciding whether operator
cleanup is required.
Inspect Runtime Status
The MCP Installed scope at /marketplace/mcps and
GET /api/settings/mcp-servers keep configuration, authorization, runtime, and probe state
separate. A configured server is not automatically ready.
| Signal | Payload fields | Interpretation |
|---|---|---|
| Config | One scoped settings entry | The definition exists at the selected scope. |
| Auth | auth_status.status, token_present, diagnostic | OAuth is unconfigured, needs login, authenticated, expired, or invalid. |
| Runtime | runtime_status.state, reason, diagnostic | The daemon reports ready or a concrete config, auth, permission, or availability state. |
| Probe | runtime_status.probe, tool_count | A probe was skipped, failed, or succeeded with a reported tool count. |
Use the redacted CLI and native-tool surfaces for agent diagnostics:
compozy mcp auth status -o json
compozy mcp auth status linear --scope workspace --workspace <workspace-id> -o jsonInside a CompozyOS session, compozy__mcp_status and compozy__mcp_auth_status expose the corresponding
redacted status. They do not return OAuth codes, tokens, PKCE verifiers, or secret values.
The web repair action appears only for OAuth-capable HTTP servers that need their first login,
have expired or invalid auth, or report auth_refresh_failed. Authorization success still requires
both authenticated and token_present=true; runtime and probe state remain independent.
Edit A Configured Server
Open /marketplace/mcps to inspect global definitions together with definitions from
the active workspace, then open the matching Marketplace detail. MCP configuration supports:
- stdio
command,args, literalenv, and boundsecret_envrefs; - remote
httptransport and absoluteurl; - OAuth automatic registration or a pre-registered issuer, client ID, optional client-secret ref, and scopes.
The structured mutation is PUT /api/settings/mcp-servers/{name} over HTTP and UDS. Supply scope
and, for workspace definitions, workspace_id according to the generated
Settings API reference. A generic settings edit clears
catalog_entry and catalog_version because the resulting definition is operator-managed.
Deleting or replacing one scoped definition does not mutate a same-name definition in another workspace or at global scope. OAuth tokens and CompozyOS-owned Vault refs use scope-qualified identities for the same reason.
Repair Authorization
Use Authorize for needs_login and Reauthorize for expired, invalid, or refresh-failed OAuth state.
The daemon preserves an existing token when begin, exchange, refresh, or operator completion fails.
# Automatic loopback completion.
compozy mcp auth login linear --scope global
# Paste the full redirect URL when operating away from the daemon host.
compozy mcp auth login linear --scope global --manual
# Remove the scoped credential when that is the intended repair.
compozy mcp auth logout linear --scope global -o jsonDo not interpret a successful config write, browser open, or redirect as credential confirmation.
Re-read status and require authenticated plus token_present=true.
Merge And Override Rules
mcp.json uses whole-object replacement. TOML uses field-level merging.
| Situation | Behavior |
|---|---|
Same-name TOML [[mcp_servers]] overlay | Fields merge by name. Non-empty transport, command, URL, auth fields, args, and env keys overlay the existing server. |
Same-name mcp.json sidecar overlay | The sidecar replaces the entire existing server object. |
Same-name inline AGENT.md and agent-local mcp.json | Agent-local sidecar replaces the whole inline server object. |
Same-name SKILL.md metadata and skill-local mcp.json | Skill-local sidecar replaces the whole frontmatter server object. |
| Duplicate names inside one JSON document after trimming | Loading fails. |
Runtime Precedence
Top-level configuration loads in this order:
- Built-in defaults.
$COMPOZY_HOME/config.toml.$COMPOZY_HOME/mcp.json.<workspace>/.compozy/config.toml.<workspace>/.compozy/mcp.json.
Daemon boot uses home config for boot-time daemon settings. MCP sidecars are applied by the general config loader used for session and runtime resolution.
Session startup then resolves MCP servers in this order:
- Top-level config MCP servers.
- Provider MCP servers.
- Agent MCP servers.
- Active skill MCP servers.
Provider and agent MCP servers use field-level merging when they come from TOML or YAML. Agent and skill sidecars replace same-name inline servers as whole objects.
Validation Errors
| Error case | Result |
|---|---|
| Unknown top-level key | Parse error. |
| Unknown server field | Parse error. |
| Trailing JSON value | Parse error. |
| Empty server name after trimming | Validation error. |
| Duplicate trimmed server name | Validation error. |
Missing stdio command | Validation error. |
command on remote transport | Validation error. |
args on remote transport | Validation error. |
env on remote transport | Validation error. |
Forbidden key in env/secret_env | Validation error. |
Missing remote url | Validation error. |
url on stdio transport | Validation error. |
auth on stdio transport | Validation error. |
| Incomplete OAuth metadata | Validation error. |
Non-string values in args or env | JSON decoding error. |
| Missing file | Ignored. |
Examples By Scope
Global MCP Server
~/.compozy/
config.toml
mcp.jsonUse this for tools every workspace may use.
Workspace MCP Server
my-project/
.compozy/
config.toml
mcp.jsonUse this for tools that only make sense inside one project.
Agent MCP Server
~/.compozy/agents/reviewer/
AGENT.md
mcp.jsonUse this when the tool belongs to one agent.
Skill MCP Server
~/.compozy/skills/docs-search/
SKILL.md
mcp.jsonUse this when the tool belongs to one skill. Marketplace skill MCP servers are blocked unless the
skill is explicitly allowlisted in [skills].allowed_marketplace_mcp. Skill-local mcp.json
symlinks must resolve inside the skill directory; CompozyOS rejects sidecars that escape the approved
skill root.
Related Pages
- config.toml documents top-level and provider MCP fields.
- Vault documents
env:versusvault:refs and redacted metadata management. - AGENT.md documents agent-local MCP declarations.
- SKILL.md documents skill-local MCP declarations.
- Marketplace covers discovery, guided install, updates, and trust.