# MCP servers

> OpenEng speaks the Model Context Protocol (MCP), the open standard for giving AI tools. Connect an MCP server (GitHub, databases, browsers, internal tools, …) and its tools become available to your agents, gated by each tool's risk class. As of September 25, 2026, OpenEng AI isn't live yet.

OpenEng speaks the **Model Context Protocol (MCP)** — the open standard for
giving AI tools. Connect any MCP server (GitHub, databases, browsers,
internal tools, …) and its tools become available to your agents.

> [!WARNING] Status on September 25, 2026: OpenEng AI isn't live yet
> The Console and Web Console interfaces load, but the OpenEng AI API routes and the Broker aren't
> deployed. Nothing can be saved or published, and no AI engine can register or connect. Pro,
> Enterprise and pay-as-you-go launch in Q4 2026 and can't be purchased yet. This page describes
> OpenEng AI as it works once those services are live ([status](ai.md)).

*Figure: Connect → MCP servers — definitions edit in a slide-over (name, transport, ${ENV} credential rows, tags) and publish with your config; rows carry Versions, Edit and Delete actions.*

## Define servers centrally (Console)

**Connect → MCP servers** authors definitions in a slide-over and publishes
them with your config, so every machine that serves the config receives the
same definitions. Rows carry trailing **Edit** / **Delete** actions (delete
behind a confirmation) plus **Versions**, the item's version history, and
validation errors in the slide-over clear the moment you fix the field. The
form emits exactly what the engine parses:

- **Name** — required; the engine drops unnamed entries.
- **Transport** — **HTTP** takes an **Endpoint URL**; **stdio** takes a
  **Command** and its **Arguments**, launched on the machine by the engine. A
  definition never carries both.
- **Credential** — **one or more** `${ENV_VAR}` placeholder rows (never raw
  values), resolved on each machine from its environment or from the value
  stored for the config on the Web Console's **Placeholder values** screen
  (the environment wins); if a variable is unset on a machine, that server is
  disabled there with a message naming it. The
  first row is the `apiKey` the engine consumes; extra rows publish as numbered
  siblings (`apiKey2`, `apiKey3`, …) that older engines safely ignore.
- **Tags** — for filtering the list in the Console; the engine doesn't read
  them.

The list shows each server's transport, endpoint or command, credential
placeholder and tags. There is no test button and no tool listing: the
Console stores configuration only, and the engine discovers a server's tools
when it connects.

> **Warning:** The https law applies here too — a credentialed server on
> plain `http://` to a non-loopback host is **skipped by the engine**. The
> editor pre-flags it; use `https://`.

SSE transport is deliberately not offered: the engine speaks stdio and HTTP —
the Console doesn't author dead transports.

A config uses the servers you pick in the config builder's **MCP servers**
step ([Configs](configs.md#components)). Picking one pins a frozen copy to that
config; those copies are listed under **Pinned to configs** at the bottom of
this page, never mixed into your own list.

> [!NOTE] What the current engine connects
> At start, the engine connects the servers declared in the `.mcp.json` files
> described below. Servers you define in the Console reach each machine with
> the published config, and the engine resolves their `${ENV}` credentials
> there, but the current engine build doesn't connect them yet. Until it does,
> declare a server you need to call in `.mcp.json`.

## Or configure per machine (`.mcp.json`)

Local files work, in two tiers that merge:

- `~/.openeng/.mcp.json` (in `$OPENENG_HOME` when that is set) — your
  personal servers, every project
- `<project>/.mcp.json` in the directory you start the engine from —
  per-project servers (override personal ones with the same name; commit this
  so your team shares the setup)

```json
{
  "mcpServers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"]
    },
    "internal-tools": {
      "url": "https://mcp.internal.example.com"
    }
  }
}
```

Each entry is either `command` (plus optional `args`) for a stdio server or
`url` for an HTTP server; an entry with neither is ignored, and a file that
isn't valid JSON is skipped. The files carry no credential fields.

**Project servers need a trusted directory.** A project `.mcp.json` travels
with the directory, so a checked-out repository could otherwise start
processes on your machine. The engine connects a project file's servers
(stdio and HTTP alike) only when the directory, or one above it, is in the
engine's trusted list (`~/.openeng/trust.json`, see
[the engine's files](engine.md#files)); otherwise it skips them and prints a
`SECURITY` notice naming them. Your personal file's servers always connect.

An HTTP server without a credential gets the OAuth access token stored for it
under `~/.openeng/mcp-tokens/`, if there is one. The engine has no sign-in flow
of its own to obtain such a token.

## Use them

Connected servers' **tools** join the agent's toolbox automatically — just
ask; the agent picks the right tool. Each tool is addressed as
`server::tool`; a server tool that shares its bare name with one of the
engine's own tools is reachable only by that full name, so a server can never
take over a built-in tool.

Only tools are used today: the engine can read a server's prompts and
resources, but no OpenEng surface offers them yet.

## Permissions

Every MCP tool call passes the engine's gateway, and each tool has a risk
class: **read**, **write** or **destructive**. The class comes from the
server's own hint, which can raise the engine's classification but never
lower it, or else from the tool's name (names with delete, drop, deploy,
publish, push and the like count as destructive).

| Class | Default |
|---|---|
| Read | Runs |
| Write | Asks for your approval first |
| Destructive | Always asks for approval, unless you name the tool in `OPENENG_MCP_AUTO_ALLOW`; `OPENENG_MCP_ALLOW_WRITE` never covers it |

Three environment variables on the machine adjust this:

| Variable | Effect |
|---|---|
| `OPENENG_MCP_DENY` | Comma-separated tool names that are always blocked (this wins over everything) |
| `OPENENG_MCP_AUTO_ALLOW` | Comma-separated tool names that run without asking, whatever their class, destructive included |
| `OPENENG_MCP_ALLOW_WRITE=1` | Write-class tools run without asking (destructive ones still ask) |

On top of that, a [Human-in-the-Loop](human-in-the-loop.md) rule can pause a
run before any call to a given MCP server, and the platform's
[gateway floor](govern.md#gateway-guardrails) blocks secrets from leaving in
requests to MCP servers. A misbehaving or unreachable server is skipped with a
warning; the rest of your setup keeps working.

## OpenEng as an MCP server

The `openeng` engine doesn't act as an MCP server for other hosts. Its
commands are `serve`, `pull`, `headless` and `daemon`
([the engine](engine.md#commands)); the `openeng mcp-server` command belonged
to the retired OpenEng CLI.

## MCP or a cell?

| Aspect | MCP server | Cell |
|---|---|---|
| Runs | on/near your machine (or any HTTP host) | inside **your cloud account** |
| Best for | dev tools, APIs, databases, browsers | operating cloud infrastructure with IAM-scoped access |
| Auth | per-server (`${ENV}` credential or a stored OAuth token) | your cloud's API-key gateway + IAM role |
| Read/write gating | by each tool's risk class (by default, write and destructive tools ask first) | **enforced by type** (write cells run only in execution runs) |

Rule of thumb: tools about *software you use* → MCP; tools about
*infrastructure you run* → [cells](cells.md).

## Plan & tier notes

Authoring MCP definitions in the Console is open on **every plan**. Running
them needs the `openeng` engine, and the engine registers only with an active
**Pro** plan; OpenEng AI's Pro, Enterprise and pay-as-you-go plans launch in
Q4 2026 and can't be purchased yet ([Plans & billing](account.md#ai-pricing)).

## Troubleshooting

**A server doesn't show up.** In the current engine build, only servers from
`.mcp.json` files connect (see the note above). Check the JSON in
`~/.openeng/.mcp.json` and the project file. A project file's servers need a
trusted directory; look for the `SECURITY` notice at start. A server that fails
to start is skipped with a warning; the others still load. The engine reads
the files when it starts, so restart it after editing them.

**`mcp "<name>" disabled — set <VAR> to enable it`.** A Console-defined
server's `${ENV_VAR}` isn't set in the environment the engine runs in. Export
it and restart the engine.

**A credentialed server is silently absent on one machine.** If its URL is
plain `http://` to a remote host, the engine refuses it by design — switch to
`https://`.

**A tool keeps asking for approval.** It is classed write or destructive. If
you trust it, add it to `OPENENG_MCP_AUTO_ALLOW`. That also covers destructive
tools, so list them deliberately. Or set `OPENENG_MCP_ALLOW_WRITE=1` for all
write-class tools; destructive tools still ask.
