# EONYX and MCP: connecting external tools


**Model Context Protocol (MCP)** is an open standard through which an agent gains access to external systems: databases, APIs, issue trackers and any other services. EONYX has a built-in MCP client based on the official Go SDK (`modelcontextprotocol/go-sdk`) — servers are described right in `eonyx.json` and their tools show up for the model alongside the built-in ones.

<!--more-->

## Setup

Connecting an MCP server takes two steps: describe it in the config and grant access to its tools. And for one common case MCP is not needed at all — we will cover that too.

### Configuration

MCP servers are described in the `mcp` array of the config. There is no separate "type" field — the transport is inferred from the fields: if there is a `url` it is HTTP, if there is a `command` it is stdio.

{{< tabs defaultTab="0" >}}
{{< tab title="stdio server" markdown="true" >}}

A local process, communication over stdin/stdout:

```json
"mcp": [
  {
    "name": "github",
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env": { "GITHUB_TOKEN": "ghp_..." }
  }
]
```

`env` is added to the environment of the spawned process on top of the current one.

{{< /tab >}}
{{< tab title="HTTP server" markdown="true" >}}

A remote server, HTTP/SSE transport:

```json
"mcp": [
  {
    "name": "tracker",
    "url": "https://mcp.example.com/sse",
    "headers": { "Authorization": "Bearer $TRACKER_TOKEN" }
  }
]
```

`headers` are added to every request; values can reference environment variables (`$TRACKER_TOKEN`) — the token is not stored in the file.

{{< /tab >}}
{{< /tabs >}}

Server fields:

| Field     | Purpose                                                        |
| --------- | -------------------------------------------------------------- |
| `name`    | server name — becomes the prefix of all its tools               |
| `command` | executable of the stdio server                                  |
| `args`    | command arguments                                               |
| `env`     | environment variables for the server process                    |
| `url`     | endpoint of the HTTP server (presence of url switches transport) |
| `headers` | headers for every HTTP request, support `$ENV`                  |

{{< admonition type="note" title="Connection is best-effort" >}}
On startup EONYX tries to connect to all servers; **a failing server is skipped** rather than breaking the launch — a `warning: mcp: …` goes to stderr. The client identifies itself to the server as `eonyx` with the build version.
{{< /admonition >}}

### Permissions

MCP tools are not on the always-allowed list — on the first call EONYX shows an **Allow once / Allow always / Deny** dialog, just like for any writing tool. So that a trusted tool does not ask every time:

```json
"permissions": {
  "allowed_tools": ["github_search_issues", "tracker_get_task"]
}
```

{{< admonition type="warning" title="Security" >}}
- Do not keep tokens in the config text (the project config is committed to the repository). In the `headers` of an HTTP server, values expand `$ENV` references. For a stdio server the `env` values are substituted literally — but the process inherits your environment, so it is enough to keep the secret in an environment variable instead of the config.
- Add **specific** tools to `allowed_tools`, not everything at once: an MCP server performs actions on your behalf.
- Remember that descriptions and results of MCP tools end up in the model's context — do not connect servers you do not trust.
{{< /admonition >}}

### When MCP is not needed

For one common case MCP is not required: EONYX has built-in read-only reporting tools for [PostgreSQL](https://www.postgresql.org). It is enough to set the connection string:

```json
"report": { "database_url": "$DATABASE_URL" }
```

Leave it empty and the tools are not registered at all; that is the off switch.

## What the model gets

Once connected, the server's tools line up alongside the built-in ones. Here is exactly what the model receives.

### Tools

Every tool of a server is registered under the name **`<server>_<tool>`**: on the `github` server the `search_issues` tool becomes `github_search_issues`. The argument schema is taken from the server as is, and the result is returned to the model as text.

**The path of a single call**

```mermaid
sequenceDiagram
    participant M as Model
    participant P as EONYX
    participant S as MCP server
    participant E as External system
    M->>P: github_search_issues call
    P->>S: request over stdio / HTTP
    S->>E: call to the API or database
    E-->>S: data
    S-->>P: result
    P-->>M: result as text
```

**Connecting servers at startup**

```mermaid
sequenceDiagram
    participant P as EONYX
    participant A as github server
    participant B as tracker server
    P->>A: connect at startup
    A-->>P: tools registered
    P->>B: connect at startup
    B--xP: server unavailable
    Note over P,B: warning: mcp — skipped
    Note over P: startup continues without it
```

Connecting is best-effort: working servers register their tools, a failing one is cut off with a warning, and EONYX starts in any case.

### Resources and prompts

If the server declares the corresponding capabilities (EONYX checks this during registration), these appear as well:

| Tool                      | Purpose                                     |
| ------------------------- | ------------------------------------------- |
| `<server>_list_resources` | list the server's resources                 |
| `<server>_read_resource`  | read a resource by `uri`                    |
| `<server>_list_prompts`   | list the prompts                            |
| `<server>_get_prompt`     | get a prompt by `name` (+ `arguments`)      |

Binary resources are not dumped into the context: instead of the content the model sees the marker `(binary resource <uri>, N bytes)`.

### Managing servers on the fly

Active servers are visible in the TUI header — the `» mcp` segment. **Ctrl+K** opens the capabilities dialog (Skills / MCP tabs): any server can be turned off and on right in the session — its tools are hidden from the model and stop being called, with no restart and no config edits. The `✓`/`✗` marks show the state.

## Summary

{{< admonition type="abstract" title="Cheat sheet" >}}
- **Configuration**
  - the `mcp` array in `eonyx.json`
  - `command`+`args`+`env` for stdio, `url`+`headers` for HTTP
- **Naming**
  - `<server>_<tool>` for every tool
  - plus resource and prompt tools if the server supports them
- **Fault tolerance**
  - a failing server is skipped with a warning
  - EONYX always starts
- **Management**
  - `Ctrl+K` turns servers on and off on the fly
  - the header shows the active ones
- **Permissions**
  - MCP tools go through the common gate
  - add trusted ones to `allowed_tools` by name
{{< /admonition >}}

