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

# MCP Servers

> Extend your agent with external tool servers using the Model Context Protocol.

Idun Engine uses the [Model Context Protocol (MCP)](https://modelcontextprotocol.io) to extend agent capabilities with external tools. You declare MCP servers in your `config.yaml` (or through the admin panel at `/admin/mcp/`), and the engine discovers every advertised tool at boot.

<Tip>
  The [langgraph-tool-local template](/templates) shows how to mix local tools with MCP tools in a single agent.
</Tip>

## How it works

1. **Register MCP servers** in the admin panel or YAML config with transport settings and connection details
2. **The engine discovers tools** from each configured MCP server at startup
3. **Agents invoke tools** during conversations as needed, with results passed back for response generation

The standalone runs a single agent per process, so every MCP server registered in the config (or admin panel) is available to that agent.

## Transport types

MCP servers connect to the engine through one of four transport protocols:

| Transport         | Use case                                     | Required fields   |
| ----------------- | -------------------------------------------- | ----------------- |
| `stdio`           | Local processes, Docker containers           | `command`, `args` |
| `sse`             | Remote servers with Server-Sent Events       | `url`             |
| `streamable_http` | Remote servers with HTTP streaming (default) | `url`             |
| `websocket`       | Persistent bidirectional connections         | `url`             |

<Note>
  The transport is decided by the MCP server, not the client. Most hosted servers expose exactly one. For example, `https://mcp.data.gouv.fr/mcp` is `streamable_http` only, with no `sse` or `stdio` fallback. Check the server's documentation before picking a transport; mismatched values fail at the handshake, not the YAML schema.
</Note>

## Configuration example

Define MCP servers in your `config.yaml` or through the admin panel:

<Tabs>
  <Tab title="Config file">
    ```yaml config.yaml theme={"theme":{"light":"github-light","dark":"github-dark"}}
    mcp_servers:
      - name: fetch
        transport: stdio
        command: docker
        args: ["run", "-i", "--rm", "mcp/fetch"]

      - name: filesystem
        transport: stdio
        command: npx
        args: ["-y", "@modelcontextprotocol/server-filesystem", "/data"]
        env:
          ALLOWED_DIRECTORY: /data

      - name: custom-api
        transport: streamable_http
        url: https://mcp.example.com/api
        headers:
          Authorization: "Bearer ${MCP_API_TOKEN}"
    ```

    Tools are discovered at engine boot and made available to your agent via `get_langchain_tools()` / `get_adk_tools()`.
  </Tab>

  <Tab title="Admin UI">
    <Steps>
      <Step title="Open the MCP admin page">
        Navigate to `/admin/mcp/` in the running standalone. The catalog at the top groups MCP servers by transport: Streamable HTTP, SSE, WebSocket, and stdio. Existing servers are listed below with their transport, endpoint, and status.

        <Frame>
          <img alt="MCP admin page" src="https://mintcdn.com/idunlabs/AMDWQnad_7024acM/images/ui/mcp-overview.png?fit=max&auto=format&n=AMDWQnad_7024acM&q=85&s=424589763e8f16546003999aca733102" width="1708" height="1007" data-path="images/ui/mcp-overview.png" />
        </Frame>
      </Step>

      <Step title="Add an MCP server">
        Click the transport card you want and fill in the form: a unique name, the URL (or command and args for stdio), optional headers. The **Enabled** toggle lets you keep a server configured but skipped at startup.
      </Step>

      <Step title="Save and verify">
        Save; the reload pipeline re-instantiates the engine with the new MCP registry. Click the wrench icon next to a server to probe it and list the tools it advertises. The probe doubles as a connection check.

        <Frame>
          <img alt="Tools discovered for the atlassian MCP server" src="https://mintcdn.com/idunlabs/AMDWQnad_7024acM/images/ui/mcp-tools-discovery.png?fit=max&auto=format&n=AMDWQnad_7024acM&q=85&s=27cc658f48db692b5dae069152163103" width="1708" height="1007" data-path="images/ui/mcp-tools-discovery.png" />
        </Frame>
      </Step>
    </Steps>
  </Tab>
</Tabs>

## Integration approaches

<Cards>
  <Card title="Docker MCP toolkit" icon="docker" href="/mcp-servers/docker-toolkit">
    Pre-built MCP servers packaged as Docker containers. Pull, configure, and use without writing server code.
  </Card>

  <Card title="Custom MCP servers" icon="code" href="/mcp-servers/docker-toolkit#advanced-configuration">
    Host your own MCP servers for custom business logic, proprietary data sources, or internal APIs.
  </Card>
</Cards>

## Framework integration

The engine provides helper functions to load MCP tools into your agent code:

<CodeGroup>
  ```python ADK theme={"theme":{"light":"github-light","dark":"github-dark"}}
  from idun_agent_engine.mcp import get_adk_tools

  idun_tools = get_adk_tools()
  ```

  ```python LangGraph theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import asyncio
  from idun_agent_engine.mcp import get_langchain_tools

  async def _load_tools():
      return await get_langchain_tools()

  mcp_tools = asyncio.run(_load_tools())
  ```
</CodeGroup>

These functions discover all MCP servers attached to your agent and make their tools available. You do not need to configure individual tools.

## Probing a server programmatically

The wrench icon in the MCP admin page is backed by an admin REST endpoint, so the same probe runs from a script or a CI smoke check. `POST /admin/api/v1/mcp-servers/{mcp_id}/tools` connects to the registered server, lists every advertised tool, and returns a `StandaloneConnectionCheck`:

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl -sX POST "http://localhost:8000/admin/api/v1/mcp-servers/$MCP_ID/tools" \
  --cookie "$IDUN_SESSION_COOKIE"
```

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "ok": true,
  "details": {
    "name": "data_gouv",
    "transport": "streamable_http",
    "toolCount": 9,
    "tools": ["get_dataset_info", "search_datasets", "..."]
  },
  "error": null
}
```

On failure (unreachable URL, MCP handshake mismatch, malformed config, registry init error) the same shape returns with `ok: false`, the upstream message in `error`, and any partial detail still attached. The probe runs under a 5-second timeout (`services/connection_checks.py`) and converts every exception into the `ok: false` payload, so it is safe in a polling loop. The HTTP status stays 200 on every outcome except the auth check failing or the row id not existing.

The route is admin-authenticated: under `IDUN_ADMIN_AUTH_MODE=password` you need the session cookie minted by `POST /admin/api/v1/auth/login`. Use this in CI to fail fast when a production MCP server has gone away, or in a release-time check before flipping traffic.

<Note>
  `get_langchain_tools()` is async, so it must be awaited inside an `async` function. If you need to bind tools at module load, run it with `asyncio.run(...)` as shown above. A synchronous helper (`get_langchain_tools_sync`) is also available; see the [engine MCP reference](/mcp-servers/overview) for details.
</Note>

## Next steps

<Card title="Docker MCP toolkit" icon="server" horizontal href="/mcp-servers/docker-toolkit">
  Pre-built MCP servers packaged as Docker containers. Pull, configure, and use without writing server code.
</Card>

<Card title="Configuration reference" icon="file-text" horizontal href="/configuration">
  Full schema for `config.yaml` including the `mcp_servers` block.
</Card>
