> ## Documentation Index
> Fetch the complete documentation index at: https://chatcli.edilsonfreitas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP (Model Context Protocol)

> Integrate external tools via Model Context Protocol with support for stdio, SSE and Streamable HTTP transports, plus OAuth 2.1 authorization for remote servers.

**MCP (Model Context Protocol)** is an open protocol for tool interoperability with AI models. ChatCLI integrates with MCP servers, allowing the AI to access external tools such as file systems, web searches, databases, and any compatible service.

***

## Overview

```text theme={"system"}
ChatCLI <-> MCP Manager <-> MCP Server (stdio | sse | http)
                              |
                         External tools
                         (filesystem, search, DB, ...)
```

<Steps>
  <Step title="Load configuration">
    The MCP Manager loads the MCP server configuration.
  </Step>

  <Step title="Start servers">
    Starts the configured servers (stdio, SSE or Streamable HTTP).
  </Step>

  <Step title="Discover tools">
    Discovers the available tools on each server.
  </Step>

  <Step title="Expose to the model">
    Exposes the tools to the AI model as `ToolDefinition`.
  </Step>

  <Step title="Route calls">
    Routes tool calls to the correct server.
  </Step>
</Steps>

***

## Configuration

### The `chatcli mcp` subcommand (recommended)

Manage servers from the shell — same developer experience as `claude mcp add`, writing to `~/.chatcli/mcp_servers.json` atomically (hand-maintained extension keys survive rewrites; re-adding a name replaces it):

```bash theme={"system"}
# stdio server (everything after -- is the server's command line)
chatcli mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem ~/Documents
chatcli mcp add github --env GITHUB_TOKEN=ghp_xxx -- npx -y @modelcontextprotocol/server-github

# remote servers (SSE / streamable HTTP)
chatcli mcp add --transport http notion https://mcp.notion.com/mcp
chatcli mcp add --transport sse asana https://mcp.asana.com/sse

# authenticated remote servers — raw header (Claude Code compatible)…
chatcli mcp add --transport sse private https://api.company.com/sse --header "X-API-Key: your-key"

# …or typed auth with the secret kept in the environment (expanded per request)
chatcli mcp add --transport http secure https://api.example.com/mcp --bearer '${MCP_TOKEN}'
chatcli mcp add --transport sse corp https://mcp.corp.com/sse --basic 'user:${LDAP_PASS}'
chatcli mcp add --transport sse api https://mcp.x.com/sse --auth-header 'X-API-Key: ${KEY}'

chatcli mcp list            # table of configured servers
chatcli mcp get <name>      # full JSON of one server
chatcli mcp remove <name>   # delete
```

Other flags for `add`: `--cwd DIR` (stdio working directory), `--description TEXT`, `--disabled` (add without enabling).

### Configuration File

Create `~/.chatcli/mcp_servers.json`:

```json theme={"system"}
{
  "mcpServers": [
    {
      "name": "filesystem",
      "transport": "stdio",
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-filesystem", "/workspace"],
      "enabled": true
    },
    {
      "name": "web-search",
      "transport": "sse",
      "url": "http://localhost:8080/sse",
      "enabled": true,
      "overrides": ["@webfetch", "@websearch"]
    },
    {
      "name": "database",
      "transport": "stdio",
      "command": "/usr/local/bin/mcp-postgres",
      "args": ["--connection-string", "postgresql://localhost/mydb"],
      "env": {
        "PGPASSWORD": "secret"
      },
      "enabled": true
    }
  ]
}
```

### Extended server capabilities

Beyond the minimum (`name`, `command`, `args`, `env`, `transport`, `enabled`, `overrides`), each server entry accepts an opt-in set of fields covering the same cases Claude Code, Cline, Cursor, and the AWS EKS MCP server support — tool auto-approval, `enabledTools`/`disabledTools` filtering, per-server timeouts, working directory, custom HTTP headers, and HTTP authentication (`bearer`/`basic`/`header`, shared by `sse` and `http`), plus metadata (`description`, `tags`, `category`) shown in `/mcp status`.

Any key outside the typed schema is preserved verbatim across save/load (the `Extensions` catch-all), so a config copied from another client works without losing vendor-specific annotations.

Full reference with semantics, examples and troubleshooting: [MCP Servers Configuration](/reference/mcp-config).

### Environment Variables

```bash theme={"system"}
CHATCLI_MCP_ENABLED=true
CHATCLI_MCP_CONFIG=~/.chatcli/mcp_servers.json
```

### Via Server Flag

```bash theme={"system"}
chatcli server --mcp-config ~/.chatcli/mcp_servers.json
```

***

## Transports

<Tabs>
  <Tab title="stdio">
    The `stdio` transport starts a local process and communicates via stdin/stdout using JSON-RPC 2.0:

    ```json theme={"system"}
    {
      "name": "filesystem",
      "transport": "stdio",
      "command": "npx",
      "args": ["-y", "@anthropic/mcp-server-filesystem", "/workspace"]
    }
    ```

    **Use case**: Local servers distributed as npm packages, standalone binaries, or scripts.
  </Tab>

  <Tab title="SSE (HTTP+SSE)">
    The `sse` transport connects to a remote HTTP server that exposes **two endpoints** — `/sse` for the stream and `/messages` for the JSON-RPC POSTs, per the original MCP spec model:

    ```json theme={"system"}
    {
      "name": "remote-tools",
      "transport": "sse",
      "url": "http://mcp-server:8080/sse"
    }
    ```

    The GET `/sse` listener is **supervised**: connection drops (proxy idle timeout, server restart) trigger reconnect with full-jitter exponential backoff (500 ms → 30 s cap), pending RPCs survive the reconnect, and push notifications on the same stream feed [MCP Channels](/features/mcp-channels).

    **Use case**: remote servers built against the HTTP+SSE model (anything predating the 2025-03-26 spec or keeping that shape for compatibility).
  </Tab>

  <Tab title="http (Streamable HTTP)">
    The `http` transport connects to a remote server that exposes **a single endpoint** per the MCP **2025-03-26** spec revision (Streamable HTTP). Adopted by the official SDK and by hosts like Cloudflare Workers MCP, FastMCP, mcp.deepwiki.com, and managed MCP gateways:

    ```json theme={"system"}
    {
      "name": "remote-tools",
      "transport": "http",
      "url": "https://mcp-server.example.com/mcp/",
      "auth": {
        "type": "bearer",
        "token": "${MCP_TOKEN}"
      }
    }
    ```

    Each call is a single POST; the response can arrive as a one-shot JSON, as an SSE upgrade on the same response (carrying the result plus any interleaved `notifications/*`), or as `202 Accepted` for client→server notifications. If the server emits an `Mcp-Session-Id` on `initialize`, the transport captures it and echoes it on every subsequent call.

    Server-initiated push notifications are read by a **separate GET listener** (opt-in per spec). ChatCLI starts the listener right after `initialize`; servers that don't implement push respond 405/404/501 on the GET and the listener stops cleanly, no retries. When the listener is active, push messages land in `/channel list` and can fire trigger rules — see [MCP Channels](/features/mcp-channels).

    **Use case**: servers built against the current MCP spec. When in doubt between `sse` and `http`, hit the endpoint with `curl` — whatever answers a GET with `event: endpoint` is `sse`; whatever answers a POST directly with JSON or an SSE upgrade is `http`.

    <Note>The trailing slash in `url` is significant. Several Streamable HTTP servers only answer on `/mcp/` and 307-redirect `/mcp`. Full details and troubleshooting: [MCP Servers Configuration — http (Streamable HTTP)](/reference/mcp-config#http-streamable-http).</Note>
  </Tab>
</Tabs>

***

## OAuth 2.1 authorization (remote servers)

Remote (`http`/`sse`) MCP servers increasingly gate access behind **OAuth 2.1** rather than a static token — the **AWS MCP server** is the motivating case. ChatCLI supports this end-to-end, and it needs **no client ID, secret, or endpoints in your config**: everything is discovered at runtime.

When a server answers a request with `401` and an OAuth challenge, ChatCLI:

<Steps>
  <Step title="Discovers the authorization server">
    Reads the `WWW-Authenticate` header, fetches the Protected Resource Metadata (RFC 9728), then the Authorization Server Metadata (RFC 8414) to learn the authorization, token, and registration endpoints.
  </Step>

  <Step title="Registers a client dynamically">
    Registers a public PKCE client via Dynamic Client Registration (RFC 7591) — so you never paste a `client_id`.
  </Step>

  <Step title="Runs the browser flow">
    Opens your browser for the authorization-code + PKCE flow through a localhost callback, binding the token to the server via the resource indicator (RFC 8707).
  </Step>

  <Step title="Stores and refreshes the token">
    Persists the access/refresh tokens in the **encrypted auth store** (AES-256-GCM, the same vault as `/auth login`) and refreshes them transparently thereafter.
  </Step>
</Steps>

### Authorizing a server

Once a remote server is configured, authorize it in either of two ways:

<CodeGroup>
  ```bash You (the command) theme={"system"}
  # opens a browser, stores the token, reconnects the server
  /mcp login aws-mcp

  # forget the stored credential (and stop the server)
  /mcp logout aws-mcp
  ```

  ```text The agent (automatically) theme={"system"}
  # When a tool call needs authorization, the error tells the model to
  # authorize, and it invokes the built-in @mcp-login tool itself:
  MCP tool error: MCP server "aws-mcp" requires OAuth authorization.
  Call @mcp-login with {"server":"aws-mcp"} to authorize, then retry.
  ```
</CodeGroup>

`/mcp status` shows a server waiting on authorization with a `🔒 authorization required` state and a login hint. After a successful login the server reconnects and its `mcp_*` tools become available.

<Note>
  No config changes are required for an OAuth server — just add it as a normal `http`/`sse` server (`chatcli mcp add --transport http aws-mcp https://…/mcp`). The first tool call surfaces the authorization prompt. To pin the loopback callback port (for strict redirect-URI allowlists), set `CHATCLI_MCP_OAUTH_PORT` (default `8765`; falls back to an ephemeral port if busy).
</Note>

The static `bearer`/`basic`/`header` auth documented above still applies for servers that accept a pre-minted token; OAuth is used only when a server actively challenges for it.

***

## Tool Naming

MCP tools are automatically prefixed with `mcp_` and receive a description from the source server:

```text theme={"system"}
Original name: read_file
Name in ChatCLI: mcp_read_file
Description: [MCP:filesystem] Reads a file from the filesystem
```

<Info>The `mcp_` prefix prevents collisions with ChatCLI's native tools.</Info>

***

## Built-in Overrides (Shadow + Fallback)

When an MCP server provides a tool that **replaces** a native plugin (e.g., `@webfetch`, `@websearch`), use the `overrides` field to declare which built-ins it replaces. ChatCLI **automatically hides** the listed built-ins while the MCP server is connected. If the server **disconnects**, the built-ins are **immediately restored** — the user never loses the capability.

### Why use overrides?

Without `overrides`, the LLM sees two tools that do the same thing (e.g., `@webfetch` and `mcp_web_fetch`) and picks randomly. With `overrides`, the behavior is **deterministic**: the LLM only sees the MCP tool, with no ambiguity.

### Configuration

Add `overrides` to the MCP server configuration:

<Tabs>
  <Tab title="CLI (mcp_servers.json)">
    ```json theme={"system"}
    {
      "mcpServers": [
        {
          "name": "web-tools",
          "transport": "sse",
          "url": "http://mcp-web:8080/sse",
          "enabled": true,
          "overrides": ["@webfetch", "@websearch"]
        }
      ]
    }
    ```
  </Tab>

  <Tab title="Helm (values.yaml)">
    ```yaml theme={"system"}
    mcp:
      enabled: true
      servers:
        - name: web-tools
          transport: sse
          url: "http://mcp-web:8080/sse"
          enabled: true
          overrides: ["@webfetch", "@websearch"]
    ```
  </Tab>

  <Tab title="Operator (Instance CRD)">
    ```yaml theme={"system"}
    apiVersion: platform.chatcli.dev/v1alpha1
    kind: Instance
    metadata:
      name: my-chatcli
    spec:
      mcp:
        enabled: true
        servers:
          - name: web-tools
            transport: sse
            url: "http://mcp-web:8080/sse"
            enabled: true
            overrides: ["@webfetch", "@websearch"]
    ```
  </Tab>
</Tabs>

### How it works

```text theme={"system"}
MCP server "web-tools" CONNECTED + overrides: ["@webfetch", "@websearch"]
  LLM sees:       mcp_web_fetch, mcp_web_search, @coder, ...
  LLM does NOT see: @webfetch, @websearch  (hidden)

MCP server "web-tools" DISCONNECTED (crash, network, etc.)
  LLM sees:       @webfetch, @websearch, @coder, ...  (automatically restored)
  Log:            "MCP server 'web-tools' disconnected, restoring built-in overrides"
```

<Note>
  Shadow sync happens **on every conversation turn**, not just at startup. This means if the MCP server crashes mid-conversation, the built-ins return **on the very next message** — no restart needed.
</Note>

### Important rules

| Rule                    | Detail                                                                                                                                                  |
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Exact names**         | Use the full built-in name including `@` (e.g., `"@webfetch"`, not `"webfetch"`)                                                                        |
| **Multiple servers**    | Different servers can declare different overrides. If two servers override the same built-in, only **one** needs to be connected to maintain the shadow |
| **Disabled server**     | `"enabled": false` on the server = overrides **do not** apply (server never connects)                                                                   |
| **Partial override**    | You can override just one built-in (e.g., only `@websearch`) and keep the others visible                                                                |
| **No effect on @coder** | `@coder` cannot be overridden via MCP — its subcommands are native tools of /coder mode                                                                 |

<Warning>
  The `overrides` field does not uninstall or modify the built-in plugin. It only **hides it from the LLM prompt**. The plugin remains available internally and returns automatically when needed. This is **shadow, not delete**.
</Warning>

***

## Deploy via Helm

<Tabs>
  <Tab title="Inline MCP Servers">
    ```yaml theme={"system"}
    # values.yaml
    mcp:
      enabled: true
      servers:
        - name: filesystem
          transport: stdio
          command: npx
          args: ["-y", "@anthropic/mcp-server-filesystem", "/workspace"]
          enabled: true
        - name: web-tools
          transport: sse
          url: "http://mcp-web:8080/sse"
          enabled: true
          overrides: ["@webfetch", "@websearch"]  # hides built-ins when connected
    ```
  </Tab>

  <Tab title="Existing ConfigMap">
    ```yaml theme={"system"}
    mcp:
      enabled: true
      existingConfigMap: "my-mcp-config"
    ```
  </Tab>
</Tabs>

The Helm chart automatically creates a ConfigMap with the `mcp_servers.json` and mounts it at `/etc/chatcli/mcp/`.

***

## Deploy via Operator (Instance CRD)

When using the ChatCLI Operator, configure MCP directly in the `Instance` resource:

```yaml theme={"system"}
apiVersion: platform.chatcli.dev/v1alpha1
kind: Instance
metadata:
  name: my-chatcli
spec:
  provider: CLAUDEAI
  model: claude-sonnet-4-20250514
  mcp:
    enabled: true
    servers:
      - name: filesystem
        transport: stdio
        command: npx
        args: ["-y", "@anthropic/mcp-server-filesystem", "/workspace"]
        enabled: true
      - name: github
        transport: stdio
        command: npx
        args: ["-y", "@anthropic/mcp-server-github"]
        env:
          GITHUB_TOKEN: "ghp_xxxxx"
        enabled: true
      - name: remote-search
        transport: sse
        url: "http://mcp-search:8080/sse"
        enabled: true
        overrides: ["@webfetch", "@websearch"]  # hides built-ins when connected
```

Or reference an existing ConfigMap:

```yaml theme={"system"}
spec:
  mcp:
    enabled: true
    existingConfigMap: "my-mcp-servers"
```

The controller automatically:

1. Generates a ConfigMap `<instance>-mcp` with `mcp_servers.json`
2. Mounts at `/etc/chatcli/mcp/` (read-only)
3. Passes `--mcp-config /etc/chatcli/mcp/mcp_servers.json` to the container

<Info>The `MCPSpec` CRD mirrors the `mcp_servers.json` format exactly. Each `MCPServerSpec` field maps to a JSON field — including `overrides` for built-in shadowing.</Info>

***

## Checking Status

The MCP Manager exposes the status of each server:

```go theme={"system"}
statuses := mcpMgr.GetServerStatus()
for _, s := range statuses {
    fmt.Printf("Server: %s, Connected: %v, Tools: %d\n",
        s.Name, s.Connected, s.ToolCount)
}
```

To check if a tool is MCP:

```go theme={"system"}
if mcpMgr.IsMCPTool("mcp_read_file") {
    result, err := mcpMgr.ExecuteTool(ctx, "read_file", args)
}
```

***

## Popular MCP Servers

| Server                             | Description        | Installation                                    |
| :--------------------------------- | :----------------- | :---------------------------------------------- |
| `@anthropic/mcp-server-filesystem` | File system access | `npx -y @anthropic/mcp-server-filesystem /path` |
| `@anthropic/mcp-server-github`     | GitHub integration | `npx -y @anthropic/mcp-server-github`           |
| `@anthropic/mcp-server-postgres`   | PostgreSQL queries | `npx -y @anthropic/mcp-server-postgres`         |
| `@anthropic/mcp-server-slack`      | Slack integration  | `npx -y @anthropic/mcp-server-slack`            |

<Tip>Check [modelcontextprotocol.io](https://modelcontextprotocol.io) for the complete list of available MCP servers.</Tip>

***

## MCP in Client Mode (TTY)

<Info>
  MCP now works directly in interactive mode (TTY), not just server mode.
  ChatCLI auto-detects `~/.chatcli/mcp_servers.json` and initializes servers automatically on startup.
</Info>

MCP tools are exposed across all **3 modes**:

| Mode      | How it works                                                     |
| :-------- | :--------------------------------------------------------------- |
| **Chat**  | Tools listed in system prompt as informational context           |
| **Agent** | Tools invocable via `<tool_call name="mcp_<tool>" args='...' />` |
| **Coder** | Same mechanism as Agent mode                                     |

### Deferred Schemas (Token Savings)

To save tokens in the system prompt, ChatCLI uses **deferred schemas**:

1. Only **name + description** are sent in the prompt (lightweight)
2. Before first use, the model queries the catalog with `@tools describe` and gets the full block: **owning server**, description, parameter JSON Schema and invocation form
3. The model invokes with the correct arguments

```text theme={"system"}
Prompt: "mcp_read_file: [MCP:filesystem] Reads a file from the filesystem"

Model: <tool_call name="@tools" args='{"cmd":"describe","args":{"name":"mcp_read_file"}}' />
→ returns server, description and full JSON Schema

Model invokes with args → real execution
```

`@tools list` also indexes MCP tools alongside the builtins, **grouped by owning server** — every entry carries the `[MCP:server]` tag, so the model always knows which server a tool comes from.

As a fallback, if the model invokes an MCP tool **without its required parameters**, the full schema is returned as feedback for correction.

**Index descriptions are clamped**: each tool's description in the system-prompt index is reduced to its first line, capped at 160 characters. Enterprise MCP servers ship multi-paragraph descriptions — with 100+ tools connected, unclamped descriptions alone add tens of KB to **every request**, which is exactly the payload floor that trips corporate proxy/WAF body caps (see [Context Recovery](/features/context-recovery)). The full description and schema remain one `@tools describe` away.

<Tip>This can save hundreds of tokens per turn when many MCP tools are connected — and with verbose enterprise servers, tens of KB per request.</Tip>

### Dynamic tool discovery

Some MCP servers have a **dynamic tool list** (the `tools.listChanged` capability): they connect exposing a minimal catalog and grow it at runtime. HTTP Toolkit is the canonical case — it exposes only `start` on connect, and after `start` runs it registers the real tools and emits a `notifications/tools/list_changed` notification.

ChatCLI handles this end-to-end:

1. The notification fires a **debounced, per-server refresh** (bursts coalesce into a single `tools/list` call). The refresh runs asynchronously — a synchronous round-trip from the transport's reader would deadlock the response pump.
2. The registry is **reconciled**: new tools added, changed schemas replaced, vanished tools dropped — touching only tools owned by that server.
3. At the next turn boundary the agent is told what changed, so it can use the new tools immediately:

```text theme={"system"}
🔌 MCP tool catalog updated (http-toolkit: +12/-0) — new tools are available to the agent
```

The model receives the added/removed tool names and the reminder to `@tools describe` before first use. Tool dispatch, `@tools list/describe` and the completer all read the live registry, so the change is visible everywhere at once. The `tools/list_changed` event is also recorded in the [channel inbox](/features/mcp-channels) for auditability — but as a handled protocol event it does **not** inflate the unread counter.

Controlled by `CHATCLI_MCP_DYNAMIC_TOOLS` (default on); surfaced under `/config integrations`.

***

## `/mcp` Command

Manage MCP servers directly from the interactive terminal. Every subcommand that takes a server name has **autocomplete** — press `Tab` after `/mcp <subcommand>` to list the configured servers.

| Subcommand                     | Argument | Description                                                                             |
| :----------------------------- | :------- | :-------------------------------------------------------------------------------------- |
| `/mcp` or `/mcp status [name]` | Optional | Status of all servers (no name) or a specific one                                       |
| `/mcp tools [name]`            | Optional | All tools (no name) or only those of `<name>`                                           |
| `/mcp start <name>`            | Required | Start a stopped server (or one that failed to start)                                    |
| `/mcp stop <name>`             | Required | Stop a server without removing it from config — `start` revives it later                |
| `/mcp restart [name]`          | Optional | Restart one server (with name) or **all** (no name)                                     |
| `/mcp reload`                  | —        | Re-read `mcp_servers.json` and reconcile (same logic as hot-reload, on demand)          |
| `/mcp logs <name>`             | Required | Last 200 stderr lines from the server (in-memory ring buffer)                           |
| `/mcp login <name>`            | Required | Run the OAuth authorization flow for a remote server and reconnect it (opens a browser) |
| `/mcp logout <name>`           | Required | Forget a server's stored OAuth credential and stop it                                   |

### Example: status and logs

```text theme={"system"}
> /mcp status

╭── 🔌 MCP SERVERS
│  ● filesystem    connected     3 tools  (2m 15s)
│  ● web-search    connected     2 tools  (2m 15s)
│  ○ database      disconnected
│    ↳ start MCP server "mcp-postgres": exec: not found
│
│  Total: 3 servers, 5 tools available
╰──────────────────────────────────────────────────────

> /mcp logs filesystem

╭── 📜 MCP LOGS — filesystem
│  Server starting on /workspace
│  Loaded 12 file watchers
│  Tool list: read_file, write_file, list_dir
╰──────────────────────────────────────────────────────
```

### Start/stop semantics

| Scenario                                           | Behavior                                                                          |
| :------------------------------------------------- | :-------------------------------------------------------------------------------- |
| `/mcp stop foo` on a running server                | Kills the process, drops its tools; **keeps the entry** in the manager            |
| `/mcp start foo` after `stop`                      | Revives using the in-memory config; tools come back                               |
| Hot-reload (JSON edit) while `foo` is user-stopped | `Reload` **respects** the manual stop; restarts only if `foo`'s config changed    |
| `/mcp restart` (no name)                           | Stop all + start all with a **new `mcpCtx`** — equivalent to a full reset         |
| `/mcp restart foo`                                 | Stop + start of `foo` only, preserving the global ctx and the other servers       |
| Server that failed at startup (LastError set)      | `/mcp start foo` clears `LastError` and retries — useful after fixing command/env |

<Note>
  Difference between `/mcp restart` and `/mcp reload`:

  * **`/mcp restart`** forces a restart **even if the config didn't change** (useful for "reload credentials" after editing shell env).
  * **`/mcp reload`** is a diff-and-apply against `mcp_servers.json` — nothing that hasn't changed gets restarted.
</Note>

### Per-server log buffer

Each stdio server keeps a **200-line ring buffer** fed from the child process's stderr. That means:

* `/mcp logs <name>` shows the last 200 lines **without enabling `--debug`**;
* Chatty servers don't grow memory unboundedly — old lines drop off the front;
* Captures like `npm 404`, panic stacks and startup messages stay available even after startup ends.

<Tip>
  Useful combo for diagnosing connection issues: `/mcp status` (state), then `/mcp logs <name>` (the why). No need to restart with `CHATCLI_LOG_LEVEL=debug`.
</Tip>

***

## Push notifications — MCP Channels

Beyond tools (request/response), ChatCLI accepts **push notifications** from any MCP server across all three transports. Notifications are JSON-RPC messages **without `id`** (the spec form for "the server wants to tell me something, it isn't asking for anything back"). ChatCLI captures, persists, and automatically injects them into the next conversations, and — optionally — fires the agent via trigger rules.

### The essentials

* **Works on `stdio`, `sse`, and `http`** — each transport has its own listener:
  * `sse`: the GET `/sse` that already carries tool responses receives notifications on the same stream.
  * `http`: a separate GET on the endpoint (opt-in by the client, optional by the spec) pulls notifications. Servers that don't support it respond with 405/404/501 and the listener stops cleanly, no retries.
  * `stdio`: JSON-RPC lines on stdout without `id` are treated as notifications.
* **Durable persistence** at `~/.chatcli/mcp/channels.jsonl` (up to 10 MiB, rotates, replays on boot).
* **Auto-injection** of the 5 most recent in the system prompt of `chat`/`agent`/`coder` (uncached so cache isn't trashed).
* **Per-server filter**: `channels` field in `mcp_servers.json` accepts a literal allow-list.
* **Optional rules engine** in `~/.chatcli/mcp/triggers.json` with 3 modes: `notify` (discreet banner, default), `confirm` (manual yes/no), `auto` (runs the agent — requires tool whitelist).
* **Slash commands**: `/channel list`, `/channel ack`, `/channel pause`, `/channel rules`, `/channel confirm <id>`, `/channel run <seq>`, `/channel inject`.

### Minimal example — server with channels

```json theme={"system"}
{
  "name": "prom-alerts",
  "transport": "sse",
  "url": "https://prom-alerts.internal/sse",
  "enabled": true,
  "channels": ["alerts/critical", "alerts/error"]
}
```

With that, alerts land in `/channel list`, show up in the next prompt banner, and are auto-injected into the agent context.

### Example — trigger that investigates CI failures

`~/.chatcli/mcp/triggers.json`:

```json theme={"system"}
{
  "rules": [
    {
      "name": "ci-investigator",
      "server": "ci-monitor",
      "channel": "ci-pipeline",
      "contentRegex": "(?i)fail|broken",
      "mode": "confirm",
      "prompt": "Investigate this CI failure: {{content}}",
      "rateLimit": "5m"
    }
  ]
}
```

Every CI failure asks the user with a prompt in the banner; `/channel confirm <id>` accepts and the agent starts investigating.

<Note>
  Full channels, rules, and troubleshooting docs: [MCP Channels](/features/mcp-channels). Complete `channels` schema and `triggers.json` reference: [MCP Config](/reference/mcp-config#channel-subscriptions-channels).
</Note>

***

## Next steps

<CardGroup cols={2}>
  <Card title="MCP Channels" icon="message" href="/features/mcp-channels">
    Push messages across all three transports (`stdio`, `sse`, `http`) with durable persistence, per-server filter, and a rules engine (notify/confirm/auto).
  </Card>

  <Card title="Agentic plugins" icon="puzzle-piece" href="/features/agentic-plugins">
    ChatCLI's plugin system — compared with MCP.
  </Card>

  <Card title="Native Tool Use" icon="wrench" href="/features/native-tool-use">
    Native Anthropic/OpenAI APIs that power MCP under the hood.
  </Card>

  <Card title="MCP Config" icon="gear" href="/reference/mcp-config">
    Full `mcp_servers.json` format and examples.
  </Card>
</CardGroup>
