Opair

The coding harness that eschews autonomy

MCP reference

This reference is for people who write or maintain MCP integrations for Opair. If you just want to connect GitHub, GitLab or Atlassian, see the MCP guide instead.

How servers are configured

One JSON file per server, in the MCP directory (<user config dir>/opair/mcp, overridable with OPAIR_MCP_DIR). Every .json file in that directory is a server. Unknown fields are rejected. Files are re-read when they change on disk; no restart needed.

Server names are lowercase letters, digits, hyphens and underscores, starting with a letter. The name becomes part of tool names and permission strings, so it must be safe to embed in both.

Example, the shipped GitHub config:

{
  "name": "github",
  "transport": "http",
  "url": "https://api.githubcopilot.com/mcp/",
  "auth": "oauth",
  "authFlow": "device",
  "clientId": "Ov23lifmXOhmKxDGdvq1",
  "scopes": ["offline_access", "repo", "read:org"],
  "readOnly": true
}

(scopes list abbreviated; see mcp/github.json for the full one)

Config fields

Field Applies to Meaning
name all Server name (see rules above). Required.
transport all "http" or "stdio". Required.
url http Server URL. Required for http.
command stdio Command to run. Required for stdio.
args stdio Command arguments.
env stdio Environment variables for the command. $VAR values are expanded.
headers http HTTP headers sent with every request. $VAR values are expanded.
auth http Set to "oauth" for stored-token sign-in.
authFlow http "device" for the device flow. Empty means browser code flow.
authIssuer http Issuer hint for the authorization server, for servers that don't advertise one themselves.
authAudience http audience parameter sent during sign-in.
clientId http Pre-registered OAuth client ID.
clientSecret http Pre-registered client secret. $VAR values are expanded.
redirectPort http Port for the local redirect during sign-in. Empty: the OS picks a free one.
scopes http OAuth scopes to request.
readOnly all Withhold every tool that does not declare readOnlyHint: true (see Read-only servers).
permissions all Per-tool permission overrides (see Permissions).

Validation rules

Invalid configs fail at load. The server is unavailable; other servers are unaffected.

Authentication

Two ways for a server to authenticate:

OAuth flows

Two OAuth flows are supported:

Client registration

Servers with a registration_endpoint are registered dynamically at sign-in. Nothing to configure.

Servers without one need clientId (and often clientSecret) in the config. Pre-registered credentials always win over dynamic registration.

Sign-in

The user runs /mcp auth {server} or opair mcp auth {server}. Both do the same thing. The flow never runs at startup; startup only connects from stored tokens.

Token storage

Tokens live in mcp-tokens.json, a sibling of the MCP directory, written with 0600 permissions. It sits outside the MCP directory because every .json file in there is treated as a server.

Tokens are keyed by authorization server issuer: credentials from one server are never sent to another.

Access tokens refresh automatically on 401. If a refresh fails with invalid_grant, the stored tokens are deleted and the user must sign in again. A new scope also needs a fresh sign-in; the client does not step up scopes on its own.

Permissions

MCP tools live in their own permission space, mcp:<server> (e.g. mcp:github), separate from the local-disk reads/writes categories.

Read-only servers

A server configured with readOnly: true only offers tools that declare readOnlyHint: true. Everything else is withheld.

Two limits to be clear about. First, readOnly is a guardrail on the harness, not a trust boundary against the server: the hint is the server's own claim, and the OAuth token keeps whatever scopes it was granted. Second, readOnly does not narrow scopes; when a server does offer read-only scopes, list them explicitly in scopes.

Tool naming

Tools surface to the agent as mcp__<server>__<tool>, e.g. mcp__github__create_issue. The double underscore is the only separator that every supported LLM provider accepts in tool names.

Diagnostics

mcpprobe performs a handshake against a server URL, lists its tools and can call one. Useful when an integration misbehaves.