# Add MCP providers

> `ati provider add-mcp` for stdio and HTTP transports, `${key}` env injection, MCP `tools/list` discovery, and namespaced tool calls (`provider:tool`).

- Repository: Parcha-ai/ati
- GitHub: https://github.com/Parcha-ai/ati
- Human docs: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa
- Complete Markdown: https://grok-wiki.com/public/docs/parcha-ai-ati-a9d4398f11fa/llms-full.txt

## Source Files

- `src/cli/provider.rs`
- `src/core/mcp_client.rs`
- `manifests/github-mcp.toml`
- `manifests/deepwiki-mcp.toml`
- `tests/mcp_live_test.rs`
- `src/proxy/server.rs`

---

---
title: "Add MCP providers"
description: "`ati provider add-mcp` for stdio and HTTP transports, `${key}` env injection, MCP `tools/list` discovery, and namespaced tool calls (`provider:tool`)."
---

MCP providers are declared with `handler = "mcp"` in `~/.ati/manifests/<name>.toml` (or via `ati provider add-mcp`), connect through `core/mcp_client.rs` over **stdio** (subprocess) or **Streamable HTTP**, discover tools with MCP `tools/list`, and expose them to agents as `provider:tool` names that `ati run` dispatches via `tools/call` after stripping the provider prefix.

## When to use MCP providers

| Use MCP when | Prefer HTTP/OpenAPI when |
|---|---|
| The upstream ships an official MCP server (`npx`, remote HTTP endpoint) | You have a REST/OpenAPI spec and want `import-openapi` |
| Tools change with the server version and should not be hand-authored | You need curated `[[tools]]`, response `extract`, or fixed schemas |
| Auth is env-based (stdio) or header-based (HTTP) on the MCP wire | Auth maps cleanly to query/header/bearer on REST |

MCP manifests do **not** include `[[tools]]`. Tool definitions come from the server at runtime.

## Add a permanent manifest

`ati provider add-mcp <name>` writes `~/.ati/manifests/<name>.toml` with `handler = "mcp"`. The command validates transport-specific fields and auth before saving; it refuses to overwrite an existing manifest.

<Steps>
<Step title="Generate the manifest">

<CodeGroup>
```bash title="HTTP (remote MCP)"
ati provider add-mcp deepwiki \
  --transport http \
  --url https://mcp.deepwiki.com/mcp \
  --description "DeepWiki documentation MCP" \
  --category documentation
```

```bash title="Stdio (subprocess MCP)"
ati provider add-mcp github \
  --transport stdio \
  --command npx \
  --args -y \
  --args @modelcontextprotocol/server-github \
  --env GITHUB_PERSONAL_ACCESS_TOKEN=${github_token} \
  --category developer-tools
```

```bash title="HTTP with bearer auth"
ati provider add-mcp parallel \
  --transport http \
  --url https://search-mcp.parallel.ai/mcp \
  --auth bearer \
  --auth-key parallel_api_key
```
</CodeGroup>

</Step>
<Step title="Store secrets in the keyring">

For `--env KEY=${key_name}` or `--auth-key`, add values before the first call:

```bash
ati key set github_token ghp_...
ati key set parallel_api_key lin_api_...
```

`add-mcp` logs a reminder when an `auth_key` is set. Stdio `${...}` placeholders in `mcp_env` are **not** auto-hinted the way CLI `@{...}` credential files are.

</Step>
<Step title="Verify discovery and run a tool">

```bash
ati provider list          # TYPE column shows mcp/http or mcp/stdio, TOOLS = auto
ati tool list              # after discovery: github:search_repositories, deepwiki:ask_question, ...
ati tool info github:search_repositories
ati run github:search_repositories --query "ati agent tools"
```

</Step>
</Steps>

### CLI flags

| Flag | Required | Purpose |
|---|---|---|
| `--transport` | Yes | `stdio` or `http` |
| `--url` | HTTP only | `mcp_url` for Streamable HTTP |
| `--command` | Stdio only | Executable (e.g. `npx`, `uvx`) |
| `--args` | No | Repeatable argv entries → `mcp_args` |
| `--env` | No | Repeatable `KEY=VALUE` → `[provider.mcp_env]`; use `VALUE=${keyring_key}` |
| `--auth` | No | `none` (default), `bearer`, or `header` |
| `--auth-key` | If bearer/header | Keyring name for HTTP auth header |
| `--auth-header` | Header auth | Custom header name (default `Authorization` at runtime) |
| `--description`, `--category` | No | Metadata for listings |

<Warning>
`--url` is mandatory for `http`; `--command` is mandatory for `stdio`. Unknown `--transport` or `--auth` values fail at CLI validation time.
</Warning>

## Manifest schema (MCP)

Generated and hand-edited MCP manifests share the same `[provider]` shape:

```toml
[provider]
name = "github"
description = "GitHub via official MCP server"
handler = "mcp"
mcp_transport = "stdio"    # or "http"
mcp_command = "npx"        # stdio only
mcp_args = ["-y", "@modelcontextprotocol/server-github"]
# mcp_url = "https://mcp.example.com/mcp"   # http only
auth_type = "none"         # none | bearer | header (HTTP)
category = "developer-tools"

[provider.mcp_env]
GITHUB_PERSONAL_ACCESS_TOKEN = "${github_token}"
```

Stock examples in the repo:

- **Stdio + env injection:** `manifests/github-mcp.toml` — `npx` launches `@modelcontextprotocol/server-github`, token via `${github_token}`.
- **HTTP, no auth:** `manifests/deepwiki-mcp.toml` — `mcp_url = "https://mcp.deepwiki.com/mcp"`.

Optional HTTP-only field `mcp_url_env` lets a proxy honor sandbox `X-Ati-Upstream-Url` after allowlist validation; it requires `mcp_transport = "http"`.

## Transports

```mermaid
flowchart TB
  subgraph cli["ati run / ati proxy"]
    R[ManifestRegistry]
    C[McpClient]
  end
  subgraph stdio["stdio transport"]
    SP[Subprocess mcp_command + mcp_args]
    ND[Newline-delimited JSON-RPC on stdin/stdout]
  end
  subgraph http["http transport"]
    POST[POST Streamable HTTP]
    SSE[JSON or text/event-stream response]
    SID[Mcp-Session-Id]
  end
  R --> C
  C -->|mcp_transport = stdio| SP --> ND
  C -->|mcp_transport = http| POST --> SSE
  POST --> SID
```

### Stdio

- Spawns `mcp_command` with `mcp_args`, clears the environment, then injects `PATH`, `HOME`, and resolved `[provider.mcp_env]`.
- JSON-RPC messages are **one JSON object per line** on stdin/stdout.
- On disconnect, stdin is closed and the child is killed to avoid orphans.
- In **proxy mode**, the proxy host runs the subprocess with real credentials; the sandbox client never sees tokens in `mcp_env`.

### HTTP (Streamable HTTP)

- `POST` to `mcp_url` with `Accept: application/json, text/event-stream`.
- Responses may be single JSON bodies or SSE streams (`data:` lines parsed into JSON-RPC).
- `Mcp-Session-Id` from responses is stored and sent on later requests; session teardown uses `DELETE` when a session id exists.
- `auth_type = bearer` → `Authorization: Bearer <keyring>`; `header` uses `auth_header_name` (default `Authorization`).
- `${key}` placeholders in `mcp_url` are resolved from the keyring the same way as env values.

Protocol revision used at initialize: **`2025-03-26`**. The client sends `initialize`, requires a `tools` capability, then `notifications/initialized`.

## Tool discovery and naming

Discovery flow:

1. **Proxy startup:** `discover_all_mcp_tools` connects to each MCP provider (30s timeout per provider, up to 10 concurrent), calls `tools/list` (with pagination/cursor), registers tools, then disconnects.
2. **Lazy CLI:** If `ati run <tool>` misses the static index but the prefix matches an MCP provider name, ATI connects once, lists tools, registers them, then resolves the call.
3. **`ati provider load --mcp`:** Writes a TTL cache under `~/.ati/cache/providers/` and optionally probes with `tools/list` (JSON output includes tool names when the probe succeeds).

Registration prefixes every MCP tool name:

```
<provider_name>:<mcp_tool_name>
```

Examples: `github:search_repositories`, `deepwiki:ask_question`. Scope entries are auto-set to `tool:<prefixed_name>`.

`tools/list` results are cached inside `McpClient` for the lifetime of the connection. Pagination follows `nextCursor` (cap 10_000 tools).

## Executing `provider:tool` calls

```mermaid
sequenceDiagram
  participant Agent as Agent / ati run
  participant ATI as ATI dispatch
  participant MCP as MCP server
  Agent->>ATI: ati run github:search_repositories --query ati
  ATI->>ATI: Scope check tool:github:search_repositories
  ATI->>MCP: connect → initialize
  ATI->>MCP: tools/call name=search_repositories
  Note over ATI,MCP: Prefix github: stripped before tools/call
  MCP-->>ATI: content[] (text/json/image)
  ATI-->>Agent: formatted text/json output
  ATI->>MCP: disconnect
```

Dispatch (`mcp_client::execute_with_gen`):

1. Connect (resolve `${key}` in env and URL; optional dynamic `auth_generator`).
2. Strip `provider:` prefix — `github:read_file` → `read_file`.
3. `tools/call` with the bare MCP tool name and normalized args.
4. Map `McpToolResult` to JSON (single text item parsed as JSON when possible).
5. Disconnect.

Proxy JSON-RPC (`POST /mcp`) exposes `tools/list` and `tools/call` over the **already-discovered** registry, with JWT scope checks on each tool. MCP tool names in `tools/call` use the full `provider:tool` form.

## Ephemeral load vs permanent add

| Command | Persistence | Discovery probe |
|---|---|---|
| `ati provider add-mcp` | `~/.ati/manifests/<name>.toml` | No (manifest only) |
| `ati provider load --mcp ...` | Cache JSON with TTL (default 1h) | Yes — connect, `tools/list`, disconnect |
| `ati provider load --mcp ... --save` | Delegates to `add-mcp` | N/A |

Example ephemeral HTTP load:

```bash
ati provider load --mcp --name serpapi \
  --transport http --url https://mcp.serpapi.com/mcp \
  --output json
```

JSON may include `status`, `tools_count`, `tools`, `probe`, and `setup_commands` when keyring refs are missing.

## Credential patterns

| Pattern | Where | Resolution |
|---|---|---|
| `${key_name}` | `[provider.mcp_env]`, `mcp_url` | Keyring lookup; missing key leaves placeholder unchanged in strings |
| `--auth bearer` + `--auth-key` | HTTP `auth_type` | `Authorization: Bearer <keyring>` |
| `--auth header` + `--auth-key` + `--auth-header` | HTTP custom header | Header name + keyring value |
| `auth_generator` | Advanced manifests | Dynamic token/env injection (`ATI_AUTH_TOKEN`, `extra_env`) |

<Note>
MCP env resolution supports `${key}` (including inline `prefix/${key}/suffix`). The `@{key}` credential-file syntax applies to **CLI** providers, not MCP `mcp_env`.
</Note>

## Proxy vs local execution

| Mode | Who runs MCP | Credentials |
|---|---|---|
| Local (default) | `ati` on the agent machine | Keyring decrypted via session key |
| Proxy (`ATI_PROXY_URL`) | Proxy host | Keyring on proxy; sandbox sends JWT only |

In proxy mode, stdio MCP servers start on the proxy with resolved secrets. HTTP MCP calls originate from the proxy with bearer/header auth from the proxy keyring.

## Troubleshooting

| Symptom | Likely cause | What to check |
|---|---|---|
| `Unknown tool: 'github:foo'` | Discovery not run or typo | `ati tool list`; ensure provider prefix matches manifest `name` |
| `Failed to spawn MCP server` | Bad `--command` / missing `npx` | PATH; run command manually |
| `MCP server did not return tools capability` | Non-compliant server | Server must answer `initialize` with `capabilities.tools` |
| `MCP tool discovery timed out (30s)` | Slow stdio cold start | Retry; reduce providers probed at proxy boot |
| HTTP 401/403 | Missing bearer/header key | `ati key set <auth_key_name> ...` |
| Env var empty in subprocess | Keyring miss | `ati key set` for each `${...}` in `mcp_env` |
| Suggestions list on typo | Wrong suffix after `provider:` | Error lists tools sharing the same provider prefix |

Live integration tests (`tests/mcp_live_test.rs`, `tests/mcp_cmd_test.rs`) cover HTTP/stdio manifest generation, env injection, and real GitHub/Linear servers — run with `cargo test --test mcp_live_test -- --ignored` when API keys and `npx` are available.

## Related pages

<CardGroup>
<Card title="Providers and handlers" href="/providers-and-handlers">
Handler types, dispatch paths, and how MCP fits beside HTTP, OpenAPI, and CLI.
</Card>
<Card title="Manifest reference" href="/manifest-reference">
Full `[provider]` TOML fields including MCP, auth, and validation errors.
</Card>
<Card title="Configure JWT and keys" href="/configure-jwt-and-keys">
Keyring setup, `ati key set`, and scoped tokens for proxy mode.
</Card>
<Card title="Deploy proxy server" href="/deploy-proxy-server">
Run `ati proxy`, startup MCP discovery, and `/mcp` JSON-RPC for sandboxes.
</Card>
<Card title="Build, test, and troubleshooting" href="/build-test-and-troubleshooting">
`cargo test`, live MCP tests, and E2E scripts.
</Card>
</CardGroup>
