McpServerConfig
Shape of a server entry in the MCP registry. Identical between declarative (mcp.json, desktop settings.json) and programmatic (Registry.addServer) creation paths.
Source: @electric-ax/agents (re-exported from @electric-ax/agents-mcp)
type McpServerConfig = McpHttpServerConfig | McpStdioServerConfigThe variant is selected by the transport field.
McpHttpServerConfig
Streamable HTTP transport per the current MCP spec.
interface McpHttpServerConfig {
name: string
transport: "http"
url: string
auth: McpAuthConfig
timeoutMs?: number
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Stable identifier. Used as the keychain account, the IPC verb argument, and the mcp__<name>__<tool> tool prefix. |
transport | "http" | Yes | Discriminator selecting the HTTP variant. |
url | string | Yes | The MCP server's HTTPS endpoint. Streamable transport with SSE inside. |
auth | McpAuthConfig | Yes | One of the auth modes below. Use { mode: "none" } for unauthenticated servers. |
timeoutMs | number | No | Per-call timeout override. Default 30000 (30 seconds). |
McpStdioServerConfig
Locally-spawned subprocess speaking the MCP stdio protocol.
interface McpStdioServerConfig {
name: string
transport: "stdio"
command: string
args?: string[]
env?: Record<string, string>
auth?: McpAuthConfig
timeoutMs?: number
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Stable identifier (see HTTP variant). |
transport | "stdio" | Yes | Discriminator selecting the stdio variant. |
command | string | Yes | Executable name or absolute path. Resolved against PATH. |
args | string[] | No | CLI arguments. ${workspaceRoot} is the only built-in expansion. |
env | Record<string, string> | No | Environment variables for the subprocess. Inherits the parent's PATH. |
auth | McpAuthConfig | No | Typically { mode: "none" }. Defaults to none when omitted. |
timeoutMs | number | No | Per-call timeout override. Default 30000. |
The subprocess is spawned lazily on the first tool call, kept alive across calls, and restarted on crash. One process per server, multiplexed via JSON-RPC id.
McpAuthConfig
Discriminated union covering the four supported credential modes.
type McpAuthConfig =
| { mode: "none" }
| ApiKeyAuth
| ClientCredentialsAuth
| AuthorizationCodeAuthnone
{ mode: "none" }No authentication. Required for stdio servers that don't need credentials; rare for HTTP servers.
apiKey
interface ApiKeyAuth {
mode: "apiKey"
key: string
headerName?: string // default "Authorization"
valuePrefix?: string // e.g. "Bearer "
}| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Raw secret. Inline at the call site (e.g. process.env.X_API_KEY). |
headerName | string | No | HTTP header name. Defaults to Authorization. |
valuePrefix | string | No | Prepended to the key. Use "Bearer " for bearer tokens; empty for raw keys. |
clientCredentials
OAuth 2.1 client-credentials grant. Unattended; no user interaction.
interface ClientCredentialsAuth {
mode: "clientCredentials"
tokenUrl: string
clientId: string
clientSecret: string
scopes?: string[]
audience?: string
resource?: string
}| Field | Type | Required | Description |
|---|---|---|---|
tokenUrl | string | Yes | OAuth token endpoint. |
clientId | string | Yes | Inline at the call site (e.g. process.env.X_CLIENT_ID). |
clientSecret | string | Yes | Inline at the call site (e.g. process.env.X_CLIENT_SECRET). |
scopes | string[] | No | Requested OAuth scopes. |
audience | string | No | Auth0/OIDC audience claim, when the auth server requires it. |
resource | string | No | RFC 8707 resource indicator. |
The runtime exchanges the credentials for a short-lived access token, retries the exchange on 401, and never surfaces user-facing errors during steady state.
authorizationCode
OAuth 2.1 authorization-code grant with PKCE. Requires a one-time browser flow per user.
interface AuthorizationCodeAuth {
mode: "authorizationCode"
scopes?: string[]
resource?: string
redirectUri?: string
client?: OAuthClientInfo
tokens?: OAuthTokens
onTokensChanged?: (tokens: OAuthTokens) => void | Promise<void>
onClientRegistered?: (client: OAuthClientInfo) => void | Promise<void>
oauthProviderRef?: string
}| Field | Type | Required | Description |
|---|---|---|---|
scopes | string[] | No | Requested OAuth scopes. |
resource | string | No | RFC 8707 resource indicator. |
redirectUri | string | No | Override the default ${publicUrl}/oauth/callback/<server> sentinel. Most embedders don't need this. |
client | OAuthClientInfo | No | Pre-registered OAuth client (client_id, optional client_secret). When present, RFC 7591 Dynamic Client Registration is skipped. |
tokens | OAuthTokens | No | Pre-existing tokens to seed the in-process cache. The OAuth flow is skipped on boot; refresh-token rotation still happens transparently. |
onTokensChanged | (tokens) => void | Promise<void> | No | Fires after initial auth and on every refresh. Wire to a persistence layer if tokens should survive process restarts. |
onClientRegistered | (client) => void | Promise<void> | No | Fires once after RFC 7591 DCR completes. Pair with client on the next boot to skip DCR. |
oauthProviderRef | string | No | Reference into a per-process map of pre-built OAuthClientProvider instances. Escape hatch for mTLS, OIDC quirks, etc. |
OAuthTokens
interface OAuthTokens {
accessToken: string
refreshToken?: string
expiresAt?: number // Unix seconds
}OAuthClientInfo
interface OAuthClientInfo {
clientId: string
clientSecret?: string
// RFC 7591 metadata (issued_at, expires_at, redirect_uris, …)
// — preserved verbatim from the DCR response.
[key: string]: unknown
}Validation
mcp.json and programmatic configs are validated when first applied:
transportmust be"http"or"stdio".auth.modemust be one ofnone/apiKey/clientCredentials/authorizationCode.mcp.jsonrejects forbidden reference keys (clientIdRef,clientSecretRef,valueRef, …) — secrets must be passed inline at the call site, not declared as references.- Inline
${VAR}placeholders inmcp.jsonare expanded againstprocess.envbefore validation.
Invalid entries surface as error-state entries in Registry.list() with a structured McpToolError. The rest of the registry continues to operate.
Persistence helpers
Two opt-in helpers from @electric-ax/agents-mcp produce auth-config slices that satisfy the persistence callback contract:
import { keychainPersistence, filePersistence } from "@electric-ax/agents-mcp"
const tokens = await keychainPersistence({ server: "honeycomb" })
// → { tokens?, client?, onTokensChanged, onClientRegistered }
await registry.addServer({
name: "honeycomb",
transport: "http",
url: "https://mcp.honeycomb.io/mcp",
auth: { mode: "authorizationCode", scopes: ["mcp:read"], ...tokens },
})| Helper | Backing store | Notes |
|---|---|---|
keychainPersistence({ server }) | macOS security, Linux secret-tool. Throws on Windows. | Service electric-agents, accounts tokens:<server> / client:<server>. |
filePersistence({ path, server }) | Mode-0600 JSON file. Refuses to read files with looser permissions. | Use for CI / containers without an OS keychain. |
See also
- MCP servers usage guide — programmatic, file-based, and desktop-settings paths end-to-end.
McpRegistry— the API that consumes this config (addServer/applyConfig/ lifecycle).BuiltinAgentsServer— host options that affect MCP, includingextraMcpServersandopenAuthorizeUrl.