# Add remote MCP servers to Last9 AI

> Register a remote MCP server for Last9 AI, test connectivity, and activate it for chat and investigations.

Source: https://last9.io/docs/ai/remote-mcp-servers/

Connect Last9 AI to a remote MCP server your organization runs (for example an internal incident, payments, or ticketing MCP). After registration, tools from that server are available on the next activated chat turn. To make the assistant prefer those tools during investigations, bind them on a [knowledge topic](/docs/ai/knowledge/).

This page covers **organization MCP management** for Last9 AI. For connecting a coding agent to Last9's hosted MCP, see [Last9 MCP](/docs/ai/mcp/).

## Prerequisites

- Last9 AI enabled for your organization. See [Enable Last9 AI](/docs/ai/#enable-last9-ai).
- An **Admin** user (writes and MCP diagnostics need an administrator).
- An API refresh token with **write** scope to create or update, and **delete** scope to remove servers. See [Getting started with API](/docs/getting-started-with-api/).
- Your remote MCP endpoint reachable from Last9 over HTTPS, with any auth headers you will store on the server record.
- Your organization slug (from the dashboard URL: `app.last9.io/v2/organizations/<org_slug>/...`).

:::note[Base path]
All examples use:

`https://app.last9.io/api/v4/organizations/<org_slug>/ai`

Replace `<org_slug>` and send `X-LAST9-API-TOKEN: Bearer <access_token>` on every request. Mint the access token from your refresh token with `POST /api/v4/oauth/access_token` as described in [Getting started with API](/docs/getting-started-with-api/#tokens).
:::

## Register a remote MCP server

Worked example name: `payments`. Substitute your server name, URL, and credentials.

1. **Mint an access token**

   Exchange a refresh token that includes **write** (and **delete** if you need cleanup):

   ```bash
   curl -sS -X POST 'https://app.last9.io/api/v4/oauth/access_token' \
     -H 'Content-Type: application/json' \
     -d '{"refresh_token":"<refresh_token>"}'
   ```

   Export the access token:

   ```bash
   export LAST9_TOKEN='<access_token>'
   export LAST9_ORG='<org_slug>'
   export LAST9_AI="https://app.last9.io/api/v4/organizations/${LAST9_ORG}/ai"
   ```

2. **Probe the remote MCP without saving**

   Confirm connectivity and tool discovery before you persist configuration. A failed diagnostic does not create a server.

   ```bash
   curl -sS -X POST "${LAST9_AI}/mcp-servers/test" \
     -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}" \
     -H 'Content-Type: application/json' \
     -d '{
       "name": "payments",
       "transport": "streamablehttp",
       "url": "https://mcp.example.com/mcp",
       "headers": {"Authorization": "Bearer <mcp_credential>"}
     }'
   ```

   Use `transport: "sse"` only if your server speaks SSE instead of Streamable HTTP. Fix URL or credentials until the diagnostic succeeds. Do not save an unreachable server — a bad saved server can block new chat turns until you fix or delete it.

   You can also probe a saved server with `POST ${LAST9_AI}/mcp-servers/payments/test` (no body required for the named route when the server already exists).

3. **Register the MCP server**

   ```bash
   curl -sS -X POST "${LAST9_AI}/mcp-servers" \
     -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}" \
     -H 'Content-Type: application/json' \
     -d '{
       "name": "payments",
       "transport": "streamablehttp",
       "url": "https://mcp.example.com/mcp",
       "headers": {"Authorization": "Bearer <mcp_credential>"}
     }'
   ```

   Expected: `201` with `activation.status: "pending"`. Responses list header **names** and whether auth is configured — never secret values. Header values are stored as literal strings (they are not expanded from environment variables).

   Server `name` must match `[a-z][a-z0-9_-]{0,99}` (lowercase, start with a letter). The name `test` is reserved because `POST /mcp-servers/test` is the diagnostic route. The URL must be `https://`.

4. **List and read servers**

   ```bash
   curl -sS "${LAST9_AI}/mcp-servers" \
     -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}"

   curl -sS -D - "${LAST9_AI}/mcp-servers/payments" \
     -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}" -o /tmp/mcp-payments.json
   ```

   Keep the quoted `ETag` from the GET response for later PATCH or DELETE. Deployment-managed servers (such as Last9's own MCP) appear in the list but cannot be mutated or tested through these routes.

5. **Activate with a chat turn**

   There is no separate activate endpoint. After create or PATCH, `activation.status` is `pending`. Open [AI Assistant](https://app.last9.io/ai-assistant) and send a turn, then check:

   ```bash
   curl -sS "${LAST9_AI}/config/status" \
     -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}"
   ```

   Expect `active`. If activation fails (bad MCP URL or auth), new turns stay blocked until you fix or delete the server.

## How tools appear after activation

| Call shape                                                | Typical approval                                 |
| --------------------------------------------------------- | ------------------------------------------------ |
| `mcp__{server}__{tool}` for an API-managed server         | `approve` (human must confirm)                   |
| `{topic_id}__{tool}` from a knowledge topic `mcp` binding | As configured on the topic (`auto` or `approve`) |
| Deployment-managed Last9 tools (`mcp__last9__…`)          | Per Last9 product policy                         |

Creating an MCP server alone does **not** make the model prefer those tools. Register the server here, then bind tools on a [knowledge topic](/docs/ai/knowledge/) with `use: ["incident_triage"]` and put the procedure in the topic overview. Prefer topic-prefixed tools in runbooks so `auto` approval applies where you configured it.

## Update or remove

Read the server first and keep the quoted `ETag`. Send it as `If-Match` on PATCH or DELETE. Missing `If-Match` returns `428`; a stale value returns `412`.

```bash
curl -sS -X PATCH "${LAST9_AI}/mcp-servers/payments" \
  -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}" \
  -H 'Content-Type: application/json' \
  -H 'If-Match: "<etag_from_get>"' \
  -d '{"url":"https://mcp.example.com/mcp/v2"}'
```

Omit `headers` on PATCH to keep existing credentials; send `"headers": {}` to clear them.

Delete (needs **delete** scope on the token):

```bash
curl -sS -X DELETE "${LAST9_AI}/mcp-servers/payments" \
  -H "X-LAST9-API-TOKEN: Bearer ${LAST9_TOKEN}" \
  -H 'If-Match: "<etag>"'
```

## Checklist

1. Admin has AI enabled and an API refresh token with write and delete scopes.
2. `POST /mcp-servers/test` succeeds against the real HTTPS MCP URL and credentials.
3. `POST /mcp-servers` returns `201` and the server appears in `GET /mcp-servers`.
4. After one AI Assistant turn, `GET /config/status` is `active`.
5. A test chat can request `mcp__payments__…` (expects approval) or a topic-bound `{topic}__…` tool.
6. Unreachable test servers are deleted so they cannot leave config stuck pending or failed.
7. For investigations, continue with [knowledge topics](/docs/ai/knowledge/) to bind tools and upload runbooks.

## Related

- [Knowledge topics](/docs/ai/knowledge/) — runbooks, `incident_triage`, and MCP tool bindings
- [AI Assistant](/docs/ai/ai-assistant/) — run a verification chat after activation
- [Last9 MCP](/docs/ai/mcp/) — hosted Last9 tools for coding agents
- [Getting started with API](/docs/getting-started-with-api/) — refresh and access tokens

---

## Troubleshooting

| Symptom                            | What to check                                                                                                                                                                       |
| ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `403` on DELETE                    | Token has write but not delete scope. Create a refresh token that includes delete, or ask an Admin.                                                                                 |
| `400` on create                    | Invalid `name` (must be lowercase; cannot be `test`), bad transport, or non-HTTPS URL. Correct the field and send the request again.                                                |
| `412` / `428` on PATCH             | Send a fresh `If-Match` from the latest GET.                                                                                                                                        |
| Diagnostic `mcp_connection_failed` | URL, TLS, or `headers` auth. Correct the URL or credentials, then run the test again. Do not leave a failed server saved.                                                           |
| Config stuck / new turns rejected  | `GET /config/status` — fix or delete the bad MCP, then take another chat turn.                                                                                                      |
| Model never calls your MCP         | Server registered but no [knowledge](/docs/ai/knowledge/) binding or overview that names the tools. Bind the tools on a topic with `incident_triage` and name them in the overview. |
| Auto approval does not apply       | You called `mcp__{server}__{tool}` instead of `{topic_id}__{tool}`. Bind the tool on a knowledge topic.                                                                             |

Please get in touch with us on [Discord](https://discord.com/invite/Q3p2EEucx9) or [Email](mailto:support@last9.io) if you have any questions.
