# EONYX and MCP: connecting external tools


**Model Context Protocol (MCP)** is an open standard.
Through it an agent reaches external systems: databases, APIs, trackers, anything.
EONYX ships an MCP client built on the official Go SDK, `modelcontextprotocol/go-sdk`.
Servers are described in `eonyx.json`, and their tools appear to the model next to the built-in ones.

<!--more-->

## 🔌 Setup

### Configuration

MCP servers live in the `mcp` array.
There is no "type" field.
The transport follows from the fields: a `url` means HTTP, a `command` means stdio.

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

A local process, talking over stdin and stdout:

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

`env` is layered on top of the current environment for the spawned process.

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

A remote server over HTTP/SSE:

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

`headers` go with every request.
Values may reference environment variables such as `$TRACKER_TOKEN`, so the token stays out of 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" >}}
EONYX dials every server at startup.
**A failing server is skipped**, not fatal.
A `warning: mcp: …` goes to stderr and the launch continues.
The client introduces itself to the server as `eonyx`, with the build version.
{{< /admonition >}}

### Permissions

MCP tools are not always-allowed.
The first call raises an **Allow once / Allow always / Deny** dialog, like any writing tool.
To stop a trusted tool from asking every time:

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

{{< admonition type="warning" title="Security" >}}
Keep tokens out of the config text, because the project config is committed.
HTTP `headers` expand `$ENV` references for you.
A stdio server's `env` values are literal, but the process inherits your environment — so keep the secret in a variable instead.

Add **specific** tools to `allowed_tools`, never the whole set.
An MCP server acts on your behalf.

Remember that tool descriptions and results land in the model's context.
Do not connect a server you do not trust.
{{< /admonition >}}

### When MCP is not needed

One common case needs no MCP at all.
EONYX has built-in read-only reporting tools for [PostgreSQL](https://www.postgresql.org).
Just set the connection string:

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

Leave it empty and the tools are never registered.
That is the off switch.

## 🧠 What the model gets

### Tools

Every tool is registered as **`<server>_<tool>`**.
On the `github` server, `search_issues` becomes `github_search_issues`.
The argument schema comes from the server untouched.
The result reaches 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
```

Working servers register their tools.
A failing one is cut off with a warning.
EONYX starts either way.

### Resources and prompts

EONYX probes the server's capabilities during registration.
If they are declared, these tools appear too:

| 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 never flood the context.
Instead of the content the model sees the marker `(binary resource <uri>, N bytes)`.

### Managing servers on the fly

Active servers show up in the TUI header, in the `» mcp` segment.
**Ctrl+K** opens the capabilities dialog with Skills and MCP tabs.
Any server can be switched off and on inside the session.
Its tools then disappear from the model and stop being called.
No restart, no config edit.
The `✓` and `✗` 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 >}}

