Skip to content

MCP

MCP Server Reference

Every key accepted in an mcpServers entry. See MCP Integration for concepts and workflow.

File shape

json
{
  "mcpServers": {
    "<server-name>": { }
  }
}

The server name becomes part of every tool's namespaced name (mcp__<server-name>__<tool>), so keep it short and stable. Renaming a server renames every tool it provides, which the model sees as a different tool set.

Transport selection

ConditionTransport
command is setstdio
type is "http" or "streamable-http"Streamable HTTP
url is set and command is notStreamable HTTP

All keys

KeyTypeDefaultApplies toPurpose
typestringinferredboth"http" or "streamable-http" to force HTTP. Usually unnecessary
commandstringstdioThe executable to run
argsstring[][]stdioArguments passed to command
envobject{}stdioEnvironment variables for the subprocess
urlstringHTTPThe server endpoint
headersobject{}HTTPStatic request headers. Values support ${VAR} expansion
oauthobjectHTTPEnables the OAuth 2.1 flow. See below
toolsstring[]allbothAllowlist of tool names to register
readOnlybooleanfalsebothTreat every tool from this server as safe-tier
timeoutMsnumber30000bothPer-request timeout. 0 uses the default
requiredbooleanfalsebothFail startup if this server does not connect

stdio servers

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"],
      "env": { "LOG_LEVEL": "warn" }
    }
  }
}

The subprocess is started at agent startup and shut down when the agent exits. It inherits nothing from your environment except what you list in env.

HTTP servers

json
{
  "mcpServers": {
    "historian": {
      "type": "http",
      "url": "https://historian.internal/mcp",
      "headers": {
        "Authorization": "Bearer ${HISTORIAN_TOKEN}",
        "X-Site": "plant-3"
      },
      "timeoutMs": 120000
    }
  }
}

Environment expansion

${VAR} in a header value expands from the environment. An unset variable expands to empty with a warning. A missing token should produce a 401 you can read, not a startup crash that hides which header was wrong.

OAuth

json
{
  "mcpServers": {
    "tickets": {
      "url": "https://tickets.example.com/mcp",
      "oauth": {
        "scopes": "tickets.read tickets.write"
      }
    }
  }
}
KeyTypePurpose
scopesstringSpace-separated scopes to request
client_idstringA pre-registered client id. Omit to use dynamic registration (RFC 7591)
client_secretstringOnly for a confidential client. Omit for the public-client flow

An empty "oauth": {} object is valid and enables the flow with discovery-supplied defaults.

Standards implemented

RFC / specRole
OAuth 2.1Authorisation-code flow, public client
PKCE (S256)Mandatory. plain is not accepted
RFC 9728Protected Resource Metadata discovery
RFC 8414Authorization Server Metadata discovery
RFC 8707Resource indicator, binding the token to that server
RFC 7591Dynamic client registration, when no client_id is given

Not implemented: the CIMD registration variant, device-code flow, token introspection. None is needed for the standard remote-server flow.

Token storage

Path~/.config/oasis-agent/mcp-tokens.json
OverrideOASIS_MCP_TOKEN_STORE, or XDG_CONFIG_HOME
RefreshAutomatic, one minute before actual expiry

Run the browser flow once:

bash
oasis-agent --mcp-config <path> --mcp-login

It authenticates every configured HTTP server, reports, and exits. It never runs a turn.

Curation

tools: the allowlist

json
{ "tools": ["query_series", "list_tags"] }

Only the named tools are registered. Everything else the server offers is ignored. This is the primary lever: a server that exposes forty tools costs forty tool schemas in your prompt prefix, and the model has to read all of them on every turn.

readOnly

json
{ "readOnly": true }

Every tool from the server is treated as safe-tier and never prompts.

This is a claim you are making about the server

The agent has no way to verify it. Set it only on a server you know cannot change anything.

Without readOnly, MCP tools are treated as mutating and prompt according to the permission mode.

required

json
{ "required": true }
A server that fails to connect
false (default)Skipped, with a message. The run continues
trueStartup fails

Use true when the run is meaningless without that server's tools, and to protect the prompt cache from a flaky server silently reshaping the schema.

Startup output

Each server reports as it loads:

EventMeaning
Server startingConnection attempt begun
Server readyConnected. Reports tool count and read-only status
Server errorFailed to connect. Skipped unless required
Tool renamedA namespaced name collided and was disambiguated with a hash suffix

A tool renamed message is worth reading. The model will see the renamed tool, not the obvious one, and a collision usually means two config entries that wanted to be the same server.

Tool naming rules

text
mcp__<sanitised-server>__<sanitised-tool>
RuleBehaviour
Maximum length64 characters, truncated if longer
SanitisationCharacters outside the allowed set are replaced, so "my server" and "my:server" can collide
CollisionResolved with a short hash suffix and reported at startup

Complete example

json
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]
    },
    "historian": {
      "type": "http",
      "url": "https://historian.internal/mcp",
      "headers": { "Authorization": "Bearer ${HISTORIAN_TOKEN}" },
      "tools": ["query_series", "list_tags"],
      "readOnly": true,
      "timeoutMs": 120000,
      "required": true
    },
    "tickets": {
      "url": "https://tickets.example.com/mcp",
      "oauth": { "scopes": "tickets.read tickets.write" },
      "tools": ["create_ticket", "search_tickets"]
    }
  }
}

software-defined automation