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

# Connecting an Agent

> Give any AI agent read access to your traces with a server key and llms.txt

Connecting an agent to Mirador means giving it two things: the API contract (via [`llms.txt`](https://api.mirador.org/llms.txt)) and credentials (a [server key](/api/authentication)). This page shows the setup for the common shapes of agent.

<Warning>
  A server key grants read access to **every trace in its project**. Only hand
  it to agents you run and trust, keep it in an environment variable rather
  than pasted into a prompt, and create a dedicated key per agent so you can
  revoke one without breaking the others.
</Warning>

## Claude Code

The fastest way to try this interactively. Export your key, then tell Claude Code where the API brief lives:

```bash theme={null}
export MIRADOR_SERVER_KEY=mir_srv_...
claude
```

```text theme={null}
Fetch https://api.mirador.org/llms.txt and use the Mirador REST API it
describes, authenticating with the key in $MIRADOR_SERVER_KEY.

Explain why traces in the past 24 hours have been having more errors
than usual, and tell me which traces I should look at first.
```

Claude Code fetches the brief, composes the right `curl` requests, and walks the [investigation loop](/ai/investigating-with-ai) on its own — comparing counts across windows, slicing by tags and attributes, and pulling event timelines for the traces that matter.

<Tip>
  Doing this often? Add the instructions to your project's `CLAUDE.md` (or a
  custom slash command) so every session already knows how to reach Mirador:

  ```markdown theme={null}
  ## Mirador
  Trace observability lives in Mirador. To investigate traces, fetch
  https://api.mirador.org/llms.txt and query the API it describes with
  the server key in $MIRADOR_SERVER_KEY. The API is read-only.
  ```
</Tip>

## A custom agent (Claude API)

For a programmatic agent — an incident bot, a scheduled report, a Slack assistant — put the brief in the system prompt and give the model one HTTP tool. The brief is small enough to include wholesale, and because it requires no key you can fetch it at startup so it's always current:

```ts theme={null}
import Anthropic from "@anthropic-ai/sdk";

const brief = await (await fetch("https://api.mirador.org/llms.txt")).text();
const anthropic = new Anthropic();

const response = await anthropic.messages.create({
  model: "claude-sonnet-5",
  max_tokens: 4096,
  system: [
    "You are an observability assistant. You investigate traces via the",
    "Mirador REST API using the mirador_api tool. The API is read-only.",
    "Ground every conclusion in data you actually fetched, and cite the",
    "trace_ids behind your findings.",
    "",
    "--- Mirador API brief (llms.txt) ---",
    brief,
  ].join("\n"),
  messages: [{
    role: "user",
    content: "Explain why traces in the past 24 hours have more errors.",
  }],
  tools: [{
    name: "mirador_api",
    description: "GET a Mirador REST API path (e.g. /v1/traces?filter=...). Returns the JSON response.",
    input_schema: {
      type: "object",
      properties: {
        path: { type: "string", description: "Path + query string, starting with /v1/" },
      },
      required: ["path"],
    },
  }],
});
```

Your tool handler is a few lines — attach the key server-side so it never enters the model's context:

```ts theme={null}
async function handleMiradorApi(input: { path: string }) {
  const res = await fetch(`https://api.mirador.org${input.path}`, {
    headers: { Authorization: `Bearer ${process.env.MIRADOR_SERVER_KEY}` },
  });
  return await res.text(); // return the body either way; errors are typed JSON
}
```

Run the standard tool-use loop (call → execute → return result → repeat) until the model produces its answer. Error responses use a [typed envelope](/api/endpoints#errors) — pass them back to the model verbatim; the brief teaches it to correct an `INVALID_FILTER` and retry.

<Note>
  Constrain the tool to `GET` and to the `https://api.mirador.org` origin, as
  above. The API itself is read-only, but a narrow tool keeps the agent from
  being repurposed to call anything else with your credentials.
</Note>

## Triggered investigations (webhooks + AI)

Combine [automations](/automations/overview) with an agent to investigate *before a human arrives*: when a rule fires, your webhook consumer fetches the matched trace and asks the agent to analyze it, then posts the summary to your incident channel or ticket.

This is the [trace export recipe](/api/trace-export) with one extra step — instead of attaching raw JSON to the ticket, hand the exported trace to the agent:

```ts theme={null}
const trace = await exportTrace(traceEntity.id); // summary + full event timeline

const analysis = await analyzeWithAgent(
  `This trace matched the rule "${payload.rule_name}" ` +
  `(${payload.match_reason}). Explain what went wrong, when, and what to ` +
  `check first. Trace JSON follows:\n${JSON.stringify(trace)}`
);

await createTicket({
  title: `[Mirador] ${payload.rule_name} — ${trace.name}`,
  body: analysis, // AI summary up top; link the trace for the full data
});
```

For a single already-exported trace the agent doesn't even need API access — the JSON *is* the evidence. Give it API access as well (previous section) and it can widen the investigation: "is this trace an isolated failure or part of a pattern?"

## Prompting tips

Whatever the agent, the same briefing habits produce grounded answers:

* **Name the time window.** "The past 24 hours" beats "recently" — the agent turns it into concrete `since`/`until` bounds and a matching baseline window.
* **Ask for evidence.** "Cite the trace\_ids behind each finding" keeps conclusions tied to real traces you can open in the dashboard.
* **Point at metadata first.** "Discover the project's tags and attributes before filtering" stops the agent from inventing field names.
* **Let counts lead.** Encourage comparing `pagination.total` across slices before fetching event timelines — it's the difference between a handful of requests and hundreds.

## Next Steps

<CardGroup cols={2}>
  <Card title="Worked investigation" icon="magnifying-glass-chart" href="/ai/investigating-with-ai">
    See the full error-spike investigation, request by request
  </Card>

  <Card title="Endpoints" icon="list" href="/api/endpoints">
    The filter grammar and response shapes agents rely on
  </Card>
</CardGroup>
