MCP servers
Declaring MCP servers in ivar.json, how sync spells them for each provider, and why a hall's servers can hold their own accounts.
A hall declares its MCP servers once, in ivar.json, and ivar sync
materialises them for every provider configured in the hall. The definitions are
hall-scoped: a harness finds them by walking up from the view directory, so
every session in the hall sees the same servers.
Declare them in ivar.json
A complete v4 hall manifest declaring all three providers (claude-code,
opencode, and omp) and both HTTP endpoints (linear and figma) alongside
a local tool:
{
"$schema": "https://ivar.run/schema/4.json",
"name": "acme",
"providers": {
"available": ["claude-code", "opencode", "omp"],
"default": "claude-code"
},
"mcp": [
{
"name": "linear",
"type": "http",
"url": "https://mcp.linear.app/mcp"
},
{
"name": "figma",
"type": "http",
"url": "https://mcp.figma.com/mcp"
},
{
"name": "local-tools",
"type": "local",
"command": "npx",
"args": ["-y", "@acme/tools-mcp"],
"env": {
"TOOLS_ENDPOINT": "ACME_TOOLS_ENDPOINT"
}
}
],
"repos": [],
"integration": {
"via": "local",
"strategy": "squash"
},
"version": 4
}Configuration types
ivar.json strictly enforces canonical http and local definitions:
{
"name": "linear",
"type": "http",
"url": "https://mcp.linear.app/mcp"
}{
"name": "local-tools",
"type": "local",
"command": "npx",
"args": ["-y", "@acme/tools-mcp"],
"env": {
"TOOLS_ENDPOINT": "ACME_TOOLS_ENDPOINT"
}
}| Field | Description | Applies to |
|---|---|---|
name | The canonical unqualified name of the server in the hall (e.g. figma, linear). | http, local |
type | Canonical transport: http for remote servers or local for spawned processes. Provider spellings like remote, stdio, and sse are rejected. | http, local |
url | Remote endpoint HTTP/HTTPS URL. | http |
command | Executable or binary spawned by the harness. | local |
args | Optional argument array passed to the command. | local |
env | Optional mapping of environment variables injected into the process (KEY: ENV_VAR_NAME reference). | local |
oauth | Optional persisted OAuth client and endpoint metadata generated/updated by ivar mcp auth. | http |
The manifest carries no secrets
ivar.json is committed. env holds references — variable names resolved
at runtime — never plain secrets. OAuth credentials live in provider credential
stores or .ivar/secrets/mcp.env.
Provider translation table
ivar sync translates canonical manifest definitions into provider-native
configurations:
Canonical (ivar.json) | Claude Code (.mcp.json) | OpenCode (opencode.json) | OMP (mcp.json) |
|---|---|---|---|
type: "http" | type: "http" | type: "remote" | type: "http" |
type: "local" | type: "stdio" | type: "local" | type: "stdio" |
command, args | command, args | command: ["cmd", ...args] | command, args |
url | url | url | url |
env | env | environment | env |
| Server name | <hall>-<name> | <hall>-<name> | <hall>-<name> |
Naming and authentication commands
The name stored in ivar.json is canonical and unqualified (figma, linear).
At every provider boundary, ivar qualifies the name with the hall name:
<hall>-<server> (for example, acme-figma or acme-linear).
Use ivar mcp auth with the canonical name:
ivar mcp auth figma --provider claude-code # authenticate specific provider
ivar mcp auth linear --provider opencode
ivar mcp auth figma --provider omp
ivar mcp auth figma --all-providers # authenticate all configured providers
# Inspect credential status
ivar mcp status # all servers across all providers
ivar mcp status figma # specific server
ivar mcp status --provider claude-code # specific provider
ivar mcp status --live # query provider harnesses live
ivar mcp status --json # emit JSON status matrix
Direct provider commands take the **hall-qualified** name only where the provider CLI natively exposes MCP login commands:
```sh
claude mcp login acme-figma # Claude Code native login
opencode mcp auth acme-linear # OpenCode native authOMP exposes no MCP login command; authentication for OMP always runs through ivar mcp auth and persists credentials keyed by profile and endpoint via omp auth-broker.
Provider support matrix
| Service | Provider | OAuth Owner | Preregistration | Credential Owner | Config Path |
|---|---|---|---|---|---|
| Linear | Claude Code | Claude Code (claude mcp login) | Not needed (public client) | Claude Code keychain | .mcp.json |
| Linear | OpenCode | OpenCode (opencode mcp auth) | Not needed (public client) | OpenCode (mcp-auth.json) | opencode.json |
| Linear | OMP | Ivar (internal OAuth PKCE) | Dynamic registration (/register) | omp auth-broker | mcp.json |
| Figma | Claude Code | Claude Code (claude mcp login) | Not needed (Claude Code allowlisted) | Claude Code keychain | .mcp.json |
| Figma | OpenCode | Ivar (internal OAuth PKCE) | Yes (client_name: "Codex") | OpenCode (mcp-auth.json) | opencode.json |
| Figma | OMP | Ivar (internal OAuth PKCE) | Yes (client_name: "Codex") | omp auth-broker | mcp.json |
Claude Code
Claude Code reads .mcp.json at the hall root. ivar sync materialises
hall-qualified names. When launching sessions, ivar
supplies process-scoped settings --settings '{"enabledMcpjsonServers":["acme-linear","acme-figma",...]}'
so declared servers are enabled without modifying global state. Claude Code owns
its OAuth lifecycle directly via claude mcp login.
{
"mcpServers": {
"acme-linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp"
},
"acme-figma": {
"type": "http",
"url": "https://mcp.figma.com/mcp"
},
"acme-local-tools": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@acme/tools-mcp"
],
"env": {
"TOOLS_ENDPOINT": "ACME_TOOLS_ENDPOINT"
}
}
}
}OpenCode
OpenCode reads opencode.json at the hall root. For standard services like
Linear, OpenCode manages OAuth directly via opencode mcp auth acme-linear. For
Figma, Ivar performs prerequisite registration and OAuth token acquisition,
storing tokens in OpenCode's credential store (mcp-auth.json).
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"acme-linear": {
"type": "remote",
"url": "https://mcp.linear.app/mcp"
},
"acme-figma": {
"type": "remote",
"url": "https://mcp.figma.com/mcp",
"oauth": {
"clientId": "<generated_client_id>",
"clientSecret": "{env:IVAR_MCP_ACME_FIGMA_SECRET}",
"redirectUri": "http://127.0.0.1:19876/callback"
}
},
"acme-local-tools": {
"type": "local",
"command": [
"npx",
"-y",
"@acme/tools-mcp"
],
"environment": {
"TOOLS_ENDPOINT": "{env:ACME_TOOLS_ENDPOINT}"
}
}
}
}OMP
OMP reads mcp.json at the hall root (or the provider-resolved MCP configuration path).
Because OMP has no built-in mcp login CLI command, ivar mcp auth always runs
Ivar's internal OAuth PKCE engine for OMP, then imports the resulting tokens
keyed by profile and MCP endpoint directly via omp auth-broker.
When persisted OAuth metadata contains a token_url, ivar sync materialises
the corresponding auth block in mcp.json.
{
"mcpServers": {
"acme-linear": {
"type": "http",
"url": "https://mcp.linear.app/mcp",
"auth": {
"type": "oauth",
"clientId": "<generated_client_id>",
"tokenUrl": "<discovered-token-endpoint>"
}
},
"acme-figma": {
"type": "http",
"url": "https://mcp.figma.com/mcp",
"auth": {
"type": "oauth",
"clientId": "<generated_client_id>",
"clientSecret": "${IVAR_MCP_ACME_FIGMA_SECRET}",
"tokenUrl": "<discovered-token-endpoint>"
}
},
"acme-local-tools": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@acme/tools-mcp"
],
"env": {
"TOOLS_ENDPOINT": "ACME_TOOLS_ENDPOINT"
}
}
}
}OAuth metadata and secrets
When ivar mcp auth discovers endpoints or performs dynamic client
registration, it persists client and endpoint metadata back to ivar.json under
the server entry:
{
"name": "figma",
"type": "http",
"url": "https://mcp.figma.com/mcp",
"oauth": {
"client_id": "<generated_client_id>",
"client_secret_env": "IVAR_MCP_ACME_FIGMA_SECRET",
"token_url": "<discovered-token-endpoint>",
"resource": "<discovered-resource>"
}
}| Field | Purpose |
|---|---|
client_id | Registered OAuth client identifier. |
client_secret_env | Name of the environment variable holding the client secret (for confidential clients). |
token_url | Discovered OAuth token endpoint URL. When present, enables materialised provider auth blocks (such as OMP). |
resource | Optional RFC 8707 / RFC 9728 protected resource indicator URI. |
Secrets remain outside version control
Ivar writes only non-sensitive metadata (client_id, token_url, client_secret_env) to ivar.json. Confidential client secrets are stored in .ivar/secrets/mcp.env or the provider's credential vault, never committed in plain text.
Service nuances: Linear vs. Figma
Linear
Linear implements standard RFC 8414 OAuth 2.0 authorization server metadata and RFC 7591 dynamic client registration for public clients (token_endpoint_auth_method: "none"). No pre-shared client credentials or allowlist workarounds are required. Claude Code and OpenCode authenticate natively, while OMP utilizes Ivar's generic PKCE registration and token installation.
Figma
Figma restricts Dynamic Client Registration (DCR) to allowlisted client_name values (such as "Claude Code" and "Codex"). Generic or unrecognized client names receive 403 Forbidden. Furthermore, Figma requires a client secret during token exchange despite advertising public authentication.
To support OpenCode and OMP:
ivar mcp authregisters a client using an allowlisted client name (Codex).- Ivar persists the registration metadata in
ivar.jsonand saves the secret reference to.ivar/secrets/mcp.env. - Ivar runs the browser OAuth PKCE exchange and imports credentials directly to the respective harness stores.
External DCR policy caveat
Figma's dynamic client registration policy is server-controlled and external. Changes to upstream allowlists or registration rules may require updating client identifiers or registration parameters.