Skip to main content
This page is the compact reference companion to the main MCP docs. For conceptual guidance, see:

Root config shape

Server keys

Environment variable references

String values anywhere in a server entry (env, headers, args, url, …) may reference environment variables with ${VAR} or the Cursor-style SecretRef form ${env:VAR} — both resolve to the same variable, so MCP snippets copied from Cursor / Claude configs work unchanged:
Values resolve from the active profile’s secret scope (falling back to the process environment), so put the secret in ~/.mibyan/.env. An unset variable keeps its literal placeholder — except in a remote server’s url or headers: sending a literal ${VAR} there is a guaranteed 401 that reads as a bad credential, so the connect fails closed instead, with the variable named in the server’s status (MCP server 'name': ${VAR} in url/headers is not set in this profile's .env or secret source). Fix the profile’s .env (or its secret source); the next connect attempt renders the reference afresh.

Context variables

Beyond env vars, the Cursor-style context variables are interpolated too (names are case-sensitive):
Any other ${...} reference falls through to the env-var lookup above.

tools policy keys

Filtering semantics

include

If include is set, only those server-native MCP tools are registered.

exclude

If exclude is set and include is not, every server-native MCP tool except those names is registered.

Precedence

If both are set, include wins.
Result:
  • create_issue is still allowed
  • delete_issue is ignored because include takes precedence

Utility-tool policy

Mibyan may register these utility wrappers per MCP server: Resources:
  • list_resources
  • read_resource
Prompts:
  • list_prompts
  • get_prompt

Disable resources

Disable prompts

Capability-aware registration

Even when resources: true or prompts: true, Mibyan only registers those utility tools if the MCP session actually exposes the corresponding capability. So this is normal:
  • you enable prompts
  • but no prompt utilities appear
  • because the server does not support prompts

enabled: false

Behavior:
  • no connection attempt
  • no discovery
  • no tool registration
  • config remains in place for later reuse

Empty result behavior

If filtering removes all server-native tools and no utility tools are registered, Mibyan does not create an empty MCP runtime toolset for that server.

Example configs

Safe GitHub allowlist

Stripe blacklist

Resource-only docs server

TLS client certificate (mTLS)

For HTTP/SSE servers that require a client certificate, set client_cert (and optionally client_key):
Notes:
  • Paths support ~ expansion. Missing files fail fast at connect time with a server-scoped error message.
  • ssl_verify: false disables server certificate verification entirely. Don’t use this with real services.
  • Works on both Streamable HTTP and SSE transports.

Reloading config

After changing MCP config, reload servers with:

Tool naming

Server-native MCP tools become:
Examples:
  • mcp__github__create_issue
  • mcp__filesystem__read_file
  • mcp__my_api__query_data
Utility tools follow the same prefixing pattern:
  • mcp__<server>__list_resources
  • mcp__<server>__read_resource
  • mcp__<server>__list_prompts
  • mcp__<server>__get_prompt
The double-underscore delimiter (mcp__…__…) matches the convention used by Claude Code, Codex, and OpenCode, and disambiguates the server/tool boundary even when either component contains underscores.

Name sanitization

Any character that is not a letter, digit, or underscore (hyphens, dots, spaces, etc.) in both server names and tool names is replaced with an underscore before registration. This ensures tool names are valid identifiers for LLM function-calling APIs. For example, a server named my-api exposing a tool called list-items.v2 becomes:
Keep this in mind when writing include / exclude filters — use the original MCP tool name (with hyphens/dots), not the sanitized version.

OAuth 2.1 authentication

For HTTP servers that require OAuth, set auth: oauth on the server entry:
Behavior:
  • Mibyan uses the MCP SDK’s OAuth 2.1 PKCE flow (metadata discovery, client identification, token exchange, and refresh)
  • On first connect, a browser window opens for authorization
  • Tokens are persisted to ~/.mibyan/mcp-tokens/<server>.json (a named profile uses ~/.mibyan/profiles/<name>/mcp-tokens/) and reused across sessions
  • Token refresh is automatic; re-authorization only happens when refresh fails
  • Only applies to HTTP/StreamableHTTP transport (url-based servers)
  • Under a multiplexed gateway, an OAuth connection is never shared across profiles: each profile authenticates with its own token and opens its own connection, even when the mcp_servers entries are identical

Device-code login (RFC 8628)

For an authorization server advertising device_authorization_endpoint, explicitly choose device authorization from a terminal on the machine running Mibyan:
Open the printed verification URL on any device and enter the displayed user code. Mibyan polls for approval, respects authorization_pending and slow_down, and stops on denial or expiry. No browser is launched and no callback listener is needed. oauth.timeout bounds the approval wait (default 300 seconds), also limited by the code’s lifetime. When the server’s protected-resource metadata lists several authorization servers, device login scans them in order and uses the first one whose metadata issuer matches its advertised URL and that offers the device_code grant (a browser-only server listed first is skipped); the only accepted difference is the path-scoped shape described under “OAuth-authenticated HTTP servers” in the MCP feature guide (a server advertised as https://host/path whose document at /.well-known/oauth-authorization-server/path names https://host). Set oauth.flow: device on the server to make mibyan mcp login and mibyan mcp reauth (including reauth --all) use device authorization. login --flow browser overrides that setting for one login; browser PKCE remains the default. Unsupported metadata produces an actionable error rather than silently falling back to a different flow. Device login requests the device and refresh grants during dynamic registration, or uses your configured oauth.client_id, oauth.client_secret, and oauth.token_endpoint_auth_method. The registered client must permit device authorization. The browser CIMD document is not used. oauth.scope is sent on the device authorization request; oauth.user_agent also applies to token polling. Tokens, registration, and issuer metadata stay in the active profile’s MCP token store and the existing runtime refresh path reuses them after a restart. Failed device grants do not replace previously saved credentials. Initial device login is terminal-only: dashboard/browser callbacks and background reconnects do not start device authorization. Run the login command against the same profile and host as the gateway. An expired/rejected device grant without a usable refresh token requires another explicit login.

Client identification: CIMD and DCR

Mibyan identifies itself to authorization servers with a Client ID Metadata Document (CIMD), the mechanism the MCP 2026-07-28 spec adopted in place of Dynamic Client Registration. The document is published at https://nousresearch.github.io/hermes-agent/docs/oauth/client-metadata.json, and that URL is the client_id — the authorization server fetches it to learn Mibyan’ name, logo, and permitted redirect URIs. Nothing is registered per install, and nothing is user-specific. The final choice belongs to the authorization server: the SDK sends the document URL as the client_id only when the server advertises client_id_metadata_document_supported: true in its metadata, and otherwise registers via DCR exactly as before. DCR is deprecated in the MCP spec but still what almost every deployed server uses today.

Callback ports

The document declares a fixed set of loopback redirect URIs, and the spec requires the redirect URI in an authorization request to be an exact string match against one of them — so a CIMD flow cannot use the random high port Mibyan normally picks. Mibyan therefore pins the callback to one of ports 27890–27894. That pin has to be chosen before the server’s capabilities are known, because the redirect URI is fixed at the start of the flow while the server’s metadata only arrives partway through. So Mibyan pins the port for any flow that could end up using CIMD, and reverts to a random port for the rest:
  • A server Mibyan has connected to before, whose cached metadata does not advertise CIMD, keeps the random port it has always used.
  • A server Mibyan has never reached gets a pinned port on that first login, since guessing is the only way CIMD can ever be used.
  • Anything that would move the callback elsewhere reverts too: a pre-registered oauth.client_id, an oauth.client_secret, a custom oauth.client_name or oauth.token_endpoint_auth_method, an oauth.redirect_uri or oauth.redirect_port override, a dashboard- or desktop-driven login, an existing client registration on disk, or all five ports being held by other processes.
Each pinned port is bound as soon as it is chosen and held until the browser redirect arrives, so two concurrent logins — a second profile, or another server in the same process — cannot land on the same listener.

When a server rejects the document

If a server fetches the document and refuses it at the token endpoint (invalid_client), Mibyan logs the rejection, records it under ~/.mibyan/mcp-tokens/<server>.cimd-off, and uses DCR for that server from then on. A server that cannot fetch or validate the document at all aborts at the authorization endpoint instead, before any redirect happens. There is no signal Mibyan can observe there, so the browser shows an invalid-client error and the login times out after five minutes. The timeout message names the document and points at cimd: false. Running mibyan mcp login <server> clears the recorded rejection, so a corrected document gets another chance.

Optional per-server keys

client_metadata_url must be an HTTPS URL with a path (no bare origin, no fragment, no userinfo, no ./.. segments) that returns 200 and Content-Type: application/json with no redirect — authorization servers are forbidden from following redirects when fetching it. Mibyan still pins its callback to the same 27890–27894 range, so a self-hosted document must declare all ten loopback URIs (http://127.0.0.1:<port>/callback and http://localhost:<port>/callback for each port), and its client_id must be its own URL. user_agent replaces the default User-Agent on token-endpoint requests only (authorization-code exchange, refresh and the device-flow token poll). Without it Mibyan sends Mibyan-Agent/<version> — never a header-less request, which WAF-fronted authorization servers answer with 403. It never applies to MCP traffic, and no other token-request headers are configurable. Empty or null values are ignored. A failed exchange reports the status plus a short, redacted excerpt of the error body (a WAF’s “Request blocked” page vs the issuer’s invalid_grant), so the cause is visible without packet captures. mibyan mcp login clears the stale grant and client registration before re-authorizing but keeps the cached authorization-server metadata, so the announced authorize URL stays the discovered one even when the metadata document cannot be re-fetched. OAuth discovery and dynamic-client-registration requests (the /.well-known/... metadata documents and the registration_endpoint POST) always carry User-Agent: Mibyan-Agent/<version>. The MCP SDK builds those requests without any client default headers, and WAF-fronted authorization servers answer a header-less request with 403 — the metadata document then looks unreadable, registration falls back to a guessed /register on the MCP host, and the login fails with Registration failed: 404. When every metadata fetch does fail, the error now leads with those statuses (Could not read authorization-server metadata (403 from https://…/.well-known/oauth-authorization-server; …)) before the registration fallback’s own error. MCP vendors and docs can offer a one-click “Add to Mibyan” button that opens the Mibyan desktop app with a pre-filled server config, mirroring Cursor’s cursor://anysphere.cursor-deeplink/mcp/install scheme:
  • name — the server name. Must match ^[A-Za-z0-9._-]{1,64}$.
  • config — the server config object as base64url-encoded JSON (standard base64 is also accepted). The decoded JSON must be an object with either a string url field (http:///https:// only) or a string command field, and may carry any of the server keys documented above. Payloads over 32KB are rejected.
Example (JavaScript):
Opening the link never installs anything by itself: the desktop app shows a confirmation dialog with the server name and the full pretty-printed config (with an extra caution for command-based servers, which run a local process), and the user must explicitly confirm. Existing server names are never overwritten — the user is asked to rename or cancel.