# MCP server

> Give Claude Code, Cursor, or VS Code read-only access to a project's logs, metrics, checks, alerts, and infrastructure.

Canonical: https://docs.getlumin.dev/mcp/

Lumin serves a remote [MCP](https://modelcontextprotocol.io) server at `https://api.getlumin.dev/v1/mcp`. A coding agent connects to it with a read-only API key and can then read one project's telemetry with ten tools. Nothing runs on your machine, and the server never writes.

## Connect in one minute

1. Open **Settings → API keys → New key** in the project you want the agent to read.
2. Pick the kind **Coding agent (read-only)**. The key starts with `lmn_read_`.
3. Copy the snippet for your client from the key dialog, or use one of these:

**Claude Code**

```sh
claude mcp add --transport http lumin https://api.getlumin.dev/v1/mcp \
  --header "Authorization: Bearer lmn_read_..."
```

**Cursor** (`~/.cursor/mcp.json` or `.cursor/mcp.json`)

```json
{
  "mcpServers": {
    "lumin": {
      "url": "https://api.getlumin.dev/v1/mcp",
      "headers": { "Authorization": "Bearer ${env:LUMIN_READ_KEY}" }
    }
  }
}
```

**VS Code** (`.vscode/mcp.json`)

```json
{
  "inputs": [
    { "type": "promptString", "id": "lumin-key", "description": "Lumin read-only API key", "password": true }
  ],
  "servers": {
    "lumin": {
      "type": "http",
      "url": "https://api.getlumin.dev/v1/mcp",
      "headers": { "Authorization": "Bearer ${input:lumin-key}" }
    }
  }
}
```

**Codex CLI** (`~/.codex/config.toml`)

```toml
[mcp_servers.lumin]
url = "https://api.getlumin.dev/v1/mcp"
bearer_token_env_var = "LUMIN_READ_KEY"
```

Windsurf, Gemini CLI, Zed, JetBrains AI Assistant, and OpenCode accept the same URL with the same `Authorization` header. claude.ai connectors and ChatGPT need OAuth, which Lumin does not offer yet.

Then ask the agent a question about production. Three to try:

- "What errored in the last hour, and which service?"
- "Is any uptime check down? Since when?"
- "The checkout endpoint got slow after this morning's deploy. What happened?"

Claude Code also gets the `/mcp__lumin__investigate` prompt, which walks the tools in the order that finds a cause fastest.

## The tools

Every tool reads the project bound to the key. No tool takes a project argument, and no tool writes.

| Tool | What it returns |
|---|---|
| `get_project_overview` | The project, its environments, the services seen in the last hour, service health, the 24 h check summary, alert rules and open incidents, and host and container counts. Call it first. |
| `search_logs` | One page of log rows, newest first, filtered by window, level, service, query text, or trace id. Default 50 rows, max 200, window up to 30 days. |
| `get_log_context` | The rows written right before and after one row id. |
| `get_log_volume` | Rows per level over time, plus the loudest services. Use it to find when a problem started. |
| `list_metrics` | Metric names and kinds emitted in the last 30 days. |
| `query_metric` | One metric aggregated into time series, with `group_by`, label filters, and a min, max, avg, last summary per series. At most 20 series and 300 points per series. |
| `get_checks` | Uptime checks with status, uptime percentage, latest latency, and the periods each check was down. Window up to 90 days. |
| `get_alerts` | Alert rules with their condition as text, and incidents with open ones first. |
| `get_infrastructure` | The latest host and container metrics from `lumin-agent`. |
| `search_events` | Browser and mobile SDK events, errors by default, or page views, tracked actions, and identify calls. |

Time arguments accept a relative age (`15m`, `2h`, `7d`), the word `now`, an RFC3339 time, or unix seconds or milliseconds. Every tool takes an optional `environment` argument. The default is the environment bound to the key. Pass `all` to span every environment.

Log search uses the same grammar as the Logs page: bare words match the message and combine with AND, `-word` excludes, `"quoted phrase"` matches exactly, `level:error`, `service:api`, and `trace_id:abc` filter columns, and `attr.key:value` matches a JSON attribute.

## Limits

- The key reads one project. Mint one key per project you want the agent to see.
- 120 calls per minute per key. The server answers `429` with a `Retry-After` header above that.
- Four tool calls run at the same time per instance. A fifth waits up to five seconds and then returns a tool error.
- Results are capped: log messages at 2 KiB, attributes at 2 KiB, error stacks at 4 KiB. A clipped row carries `truncated: true`.

## Security

- **Read-only.** The server exposes no write tool, and the ten tools are annotated `readOnlyHint` so clients can show them as safe. A `lmn_read_` key is refused by every ingest endpoint, and an ingest key is refused by the MCP server.
- **Project-scoped.** The project comes from the key, never from an argument. A key cannot name another project.
- **Revoke to cut access.** Revocation takes effect on the next call. Rotate a key that landed in a shared config or in shell history.
- **Log content is data, not instructions.** Log lines, attributes, event properties, and label values are written by your services and by whoever talks to them. Every result that carries such text says so in a `notice` field, and control characters are stripped. Your agent's own guardrails decide what it does with that text. Read [the agent security model](https://docs.getlumin.dev/agent/security/#log-content-is-your-responsibility) for the same rule on the ingest side.
- **Instance kill switch.** The operator can turn the server off for the whole instance. It then answers `403` on every call.

## Server card

The server publishes a card for agents that discover MCP servers by domain. Upstream has not settled on one path, so the card answers at all three that clients probe:

| Path | Origin |
| --- | --- |
| `/v1/mcp/server-card` | Next to the streamable endpoint |
| `/.well-known/mcp/server-card.json` | SEP-2127 |
| `/.well-known/mcp.json` | SEP-1649, still probed |

Every path returns the same document, with `Access-Control-Allow-Origin: *` so a browser client can read it. `api.getlumin.dev` and `getlumin.dev` both serve it.
