Skip to main content

MCP Config Reference

This page is the compact reference companion to the main MCP docs.

For conceptual guidance, see:

Root config shape

mcp_servers:
<server_name>:
command: "..." # stdio servers
args: []
env: {}

# OR
url: "..." # HTTP servers
headers: {}

# Optional HTTP/SSE TLS settings:
ssl_verify: true # bool or path to a CA bundle (PEM)
client_cert: "/path/to/cert.pem" # mTLS client certificate (see below)
# client_key: "/path/to/key.pem" # optional, when key lives in a separate file

enabled: true
timeout: 120
connect_timeout: 60
supports_parallel_tool_calls: false
tools:
include: []
exclude: []
resources: true
prompts: true

Server keys

KeyTypeApplies toMeaning
commandstringstdioExecutable to launch
argsliststdioArguments for the subprocess
envmappingstdioEnvironment passed to the subprocess
urlstringHTTPRemote MCP endpoint
headersmappingHTTPHeaders for remote server requests
ssl_verifybool or stringHTTPTLS verification. true (default) uses system CAs, false disables verification (insecure), or a string path to a custom CA bundle (PEM)
client_certstring or listHTTPmTLS client certificate. String = path to a PEM file containing cert + key. List [cert, key] = separate files. List [cert, key, password] = encrypted key
client_keystringHTTPPath to the client private key, when client_cert is a string and the key is in a separate file
enabledboolbothSkip the server entirely when false
timeoutnumberbothTool call timeout in seconds (default: 300)
connect_timeoutnumberbothInitial connection timeout in seconds (default: 60)
protocolstringbothProtocol-era negotiation: auto (default — legacy initialize handshake first, falling back to the 2026-07-28 server/discover stateless probe when the server rejects the handshake as modern-only), stateless (probe server/discover first; one legacy retry), or legacy (handshake only, no fallback)
supports_parallel_tool_callsboolbothAllow tools from this server to run concurrently
skip_preflightboolHTTPBypass the fail-fast content-type probe for valid Streamable HTTP endpoints whose HEAD/GET answers a non-MCP content type (default: false)
transportstringHTTPSet to sse to use the SSE transport instead of Streamable HTTP
keepalive_intervalnumberbothLiveness ping cadence in seconds (default: 180, floored at 5s). Set below the server's session TTL for servers that GC idle sessions quickly
idle_timeout_secondsnumberstdioOptional stdio server recycle after idle time (0 disables). May also live under a lifecycle: mapping
max_lifetime_secondsnumberstdioOptional stdio server recycle after age (0 disables). May also live under a lifecycle: mapping
toolsmappingbothFiltering and utility-tool policy
authstringHTTPAuthentication method. Set to oauth to enable OAuth 2.1 with PKCE
samplingmappingbothServer-initiated LLM request policy (see MCP guide)
elicitationmappingbothServer-initiated user-input requests. enabled (default true) and timeout in seconds (default 300). Form-mode requests route through the approval surface; URL-mode is declined (see MCP guide)
truststringbothTrust tier: full (default) or untrusted. On an untrusted server, every write-capable tool call (any tool without a readOnlyHint: true annotation) requires user approval through the standard approval surface before it runs. readOnlyHint is a server-supplied hint — a lying server can at most skip approval for tools it claims are read-only, never gain extra access — so mark any server you don't fully control as untrusted. Unrecognized values are treated as untrusted (fail-closed)

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:

mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "${env:GITHUB_TOKEN}" # same as "${GITHUB_TOKEN}"

Values resolve from the active profile's secret scope (falling back to the process environment), so put the secret in ~/.noora/.env. An unset variable keeps its literal placeholder.

Context variables

Beyond env vars, the Cursor-style context variables are interpolated too (names are case-sensitive):

VariableResolves to
${userHome}The current user's home directory
${workspaceFolder}The session workspace root (the session's terminal cwd when known, else the process cwd)
${workspaceFolderBasename}The basename of ${workspaceFolder}
${pathSeparator} / ${/}The OS path separator (os.sep)
mcp_servers:
filesystem:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-filesystem", "${workspaceFolder}"]
env:
CACHE_DIR: "${userHome}${/}.cache${/}mcp"

Any other ${...} reference falls through to the env-var lookup above.

tools policy keys

KeyTypeMeaning
includestring or listWhitelist server-native MCP tools. Entries may be exact names or fnmatch-style globs (*_radar_*, get_zones_*)
excludestring or listBlacklist server-native MCP tools. Same exact-name / glob semantics as include
resourcesbool-likeEnable/disable list_resources + read_resource
promptsbool-likeEnable/disable list_prompts + get_prompt

Filtering semantics

include

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

tools:
include: [create_issue, list_issues]

exclude

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

tools:
exclude: [delete_customer]

Precedence

If both are set, include wins.

tools:
include: [create_issue]
exclude: [create_issue, delete_issue]

Result:

  • create_issue is still allowed
  • delete_issue is ignored because include takes precedence

Utility-tool policy

Noora may register these utility wrappers per MCP server:

Resources:

  • list_resources
  • read_resource

Prompts:

  • list_prompts
  • get_prompt

Disable resources

tools:
resources: false

Disable prompts

tools:
prompts: false

Capability-aware registration

Even when resources: true or prompts: true, Noora 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

mcp_servers:
legacy:
url: "https://mcp.legacy.internal"
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, Noora does not create an empty MCP runtime toolset for that server.

Example configs

Safe GitHub allowlist

mcp_servers:
github:
command: "npx"
args: ["-y", "@modelcontextprotocol/server-github"]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: "***"
tools:
include: [list_issues, create_issue, update_issue, search_code]
resources: false
prompts: false

Stripe blacklist

mcp_servers:
stripe:
url: "https://mcp.stripe.com"
headers:
Authorization: "Bearer ***"
tools:
exclude: [delete_customer, refund_payment]

Resource-only docs server

mcp_servers:
docs:
url: "https://mcp.docs.example.com"
tools:
include: []
resources: true
prompts: false

TLS client certificate (mTLS)

For HTTP/SSE servers that require a client certificate, set client_cert (and optionally client_key):

mcp_servers:
# Combined cert + key in a single PEM file
internal_api:
url: "https://mcp.internal.example.com/mcp"
client_cert: "~/secrets/mcp-client.pem"

# Separate cert and key files
partner_api:
url: "https://mcp.partner.example.com/mcp"
client_cert: "~/secrets/client.crt"
client_key: "~/secrets/client.key"

# Encrypted key with a passphrase (3-element list form)
bank_api:
url: "https://mcp.bank.example.com/mcp"
client_cert: ["~/secrets/client.crt", "~/secrets/client.key", "my-passphrase"]

# Custom CA bundle (private CA / self-signed server)
lab_api:
url: "https://mcp.lab.local/mcp"
ssl_verify: "~/secrets/lab-ca.pem"
client_cert: "~/secrets/lab-client.pem"

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:

/reload-mcp

Tool naming

Server-native MCP tools become:

mcp__<server>__<tool>

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:

mcp__my_api__list_items_v2

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:

mcp_servers:
protected_api:
url: "https://mcp.example.com/mcp"
auth: oauth

Behavior:

  • Noora uses the MCP SDK's OAuth 2.1 PKCE flow (metadata discovery, dynamic client registration, token exchange, and refresh)
  • On first connect, a browser window opens for authorization
  • Tokens are persisted to ~/.noora/mcp-tokens/<server>.json and reused across sessions
  • Token refresh is automatic; re-authorization only happens when refresh fails
  • Only applies to HTTP/StreamableHTTP transport (url-based servers)

MCP vendors and docs can offer a one-click "Add to Noora" button that hands the desktop app a pre-filled server config. The desktop app is not currently published, so the scheme is documented but unhandled. It mirrors Cursor's cursor://anysphere.cursor-deeplink/mcp/install scheme:

noora://mcp/install?name=NAME&config=BASE64
  • 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):

const config = { url: 'https://mcp.example.com/mcp' }
const link = `noora://mcp/install?name=example&config=${btoa(JSON.stringify(config))
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')}`

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.