# Connect an AI agent with MCP

> Give Claude Code, Cursor, Codex, or another MCP client governed access to Anectico evidence.

Canonical page: https://anectico.com/docs/agents/connect-mcp/


Anectico exposes a remote Model Context Protocol endpoint. An AI agent can investigate customers,
Issues, traces, logs, replay, incidents, and LLM cost without copying telemetry into prompts by hand.

Start with read-only access. For the whole loop end to end — connect, discover, investigate, act —
see [Run an agent without the dashboard](/docs/agents/zero-dashboard-quickstart); for what a client
must support, see [MCP client compatibility](/docs/reference/mcp-compatibility).

## Before the first OAuth connection

An OAuth-capable MCP host can discover Anectico's sign-in service, register its client, and run the
browser authorization flow without storing an API key. That flow authenticates an existing
Anectico member; it does not create an organization or membership from the access token.

If this is the person's first Anectico access, an organization owner must invite or provision them
first. An owner can do that without the dashboard by running
`anectico invitations create person@example.com --role ROLE_MEMBER`. The person then completes the
normal Anectico sign-in (or runs `anectico login`) once and accepts the invitation before
reconnecting the MCP host. See [Manage an organization and projects](/docs/manage/workspace-and-projects#invite-and-administer-members)
for the available roles and invitation lifecycle.

When an operation rechecks access, Anectico repeats OAuth token validation and resolves your
current membership and delegated permissions. These checks use your MCP authorization; they do
not require a separate dashboard login session. Losing a required permission or presenting an
invalid token prevents the operation from continuing with its earlier access.

A valid OAuth token that cannot be matched to an existing membership receives the same generic
`403` for every cause. This is intentional: the endpoint does not reveal whether an email,
identity, or organization exists. Give support the attempt time and the signed-in address; do not send
the token. An operator can distinguish a missing account, an external identity that still needs
linking, a conflicting link, and a missing organization membership from the protected audit trail.

## Set it up in one command

`anectico agent bootstrap` does the whole setup: it creates a narrow, project-scoped, expiring key,
stores the secret somewhere that is not your terminal history, writes your agent's MCP
configuration, and then verifies the new key against the endpoint.

```bash
anectico agent bootstrap --project <project-id> --host claude
```

`--host` accepts `claude`, `codex`, `cursor`, `gemini`, or `none`. For Claude Code and Cursor it also
writes the MCP server entry (`.mcp.json` and `.cursor/mcp.json` respectively), leaving every other
entry in those files untouched.

By default the key is created with the `investigate` scope bundle and expires in 30 days. Use
`--scope-profile` to pick a different bundle, `--scope` to add individual scopes, `--expires-in` to
change the lifetime, and `--name` to label it.

The `investigate` bundle is read-only, but it is not shallow: alongside logs, traces, metrics and
Issues it grants read access to person profile properties, agent run transcripts, and session replay
content — the customer detail that makes an investigation possible. If an agent should not see that,
name the scopes you want individually with `--scope` instead of taking the bundle.

It also grants the project inventory read, so an agent can discover the project ids that most other
reads take as an argument. Naming scopes individually instead means including `projects:read`, or
the agent has no way to look one up.

The key is bound to your active project (`--project`, `ANECTICO_PROJECT`, or the profile default).
If no project resolves, the command stops rather than creating a key that can read every project;
pass `--org-wide` when that is genuinely what you want.

### Where the secret goes

The secret is never printed to standard output unless you ask for it, so it does not end up in an AI
agent's transcript.

| `--secret-dest` | Result |
| --- | --- |
| `keychain` (default) | Stored in your operating system's keychain. On a machine without one, a file with owner-only permissions under the CLI configuration directory. |
| `env-file` | Written to `--env-file <path>` as `ANECTICO_API_KEY=…` with owner-only permissions. An existing file is never replaced without `--force`. |
| `stdout` | Printed as the only thing on standard output, so it can be captured: `KEY=$(anectico agent bootstrap --secret-dest stdout)`. |

The generated MCP configuration refers to the `ANECTICO_API_KEY` environment variable rather than
embedding the key, so the file stays safe to commit. Export that variable in the environment your
agent runs in. If your client cannot expand environment variables, re-run with `--embed-secret` to
write the key itself into the configuration file — it is then created with owner-only permissions
and must not be committed.

### One step is yours: putting the key in the environment

With the default `keychain` destination the secret is in your keychain and the configuration reads
`ANECTICO_API_KEY`, which nothing sets yet. Until you connect the two, the agent cannot authenticate:

```bash
eval "$(anectico agent key env --export)"
```

`anectico agent key env` prints `ANECTICO_API_KEY=…` for the key stored under the current profile,
as the only thing on standard output, so it can be evaluated by a shell or appended to the
environment file your agent host reads. It prints a secret on purpose — do not run it where the
output is recorded. Add `--profile` to select a different profile's stored key.

The bootstrap report names this step explicitly: it appears in `steps` as `bridge` with the status
`manual`, so a successful run never implies the agent is ready when the credential has not reached
it yet.

If `ANECTICO_API_KEY` is **already** set when you run the bootstrap, the summary warns you: the
agent will keep using that existing credential — which is usually an older, broader key — instead of
the narrow one just created, until you replace the variable. Neither key is printed; the two are
identified by a short fingerprint so you can tell them apart.

### What it verifies

The last step connects with the **new** key and reports how many tools it can see. A key that
authenticates but sees zero tools is reported as a failure: `mcp:read` on its own is served an empty
tool list, because every tool also requires the scope of the domain it reads (`errors:read`,
`logs:read`, `traces:read`, `persons:read`, and so on; the
[MCP tool reference](/docs/reference/mcp-tools) lists each tool's scopes).

Add `--json` for a single machine-readable object on standard output, with the key id, scopes,
expiry, where the secret went, and the verification result. It never contains the secret unless the
destination is `stdout`. The command exits non-zero if any step failed, and a key that was already
created is never revoked for you — the report names its id so you can decide.

## Create a key by hand

If you would rather do it yourself, create an API key in **Settings → Access** with `mcp:read`
**plus the domain read scopes the agent's job needs**, or from the CLI:

```bash
anectico apikey create --scope-profile investigate --expires-in 7d
```

Give the key a name tied to the person, agent, or automation that will use it.

Do not reuse an application telemetry key. Store the MCP key in the client’s local secret or
credential configuration and never commit it to a repository.

## Endpoint

Configure a remote HTTP MCP server with:

```text
URL:           https://app.anectico.com/mcp
Authorization: Bearer an_...
```

The connection is scoped to the organization represented by the API key.

### Narrowing one connection with a profile

Append `?profile=investigate`, `?profile=incident`, `?profile=agent-observability` or
`?profile=admin` to the URL to focus what the client gets. A profile can only ever subtract from
what your key's scopes already permit — and what it subtracts applies to the whole connection, so a
tool it excludes is neither listed nor callable. Your key's authority is unchanged, and connecting
without the parameter gives you the full surface again. Use it to keep an agent's tool list short
and its attention on one job; mint a narrower key when you want narrower authority. A malformed
parameter is refused rather than ignored — an unknown name, an empty value, a whitespace-only value
and the same parameter supplied twice each answer `400` with the list of valid names. See the
[MCP tool reference](/docs/reference/mcp-tools) for what each profile leaves visible.

Where they are served, `search` and `fetch` are the two tools to start from: one query across people,
issues, logs, accounts and Anectico's own documentation, and one reference resolved to the object it
names. Neither survives a profile that admits nothing for it to route to, and they disappear
independently:

| Profile | `search` | `fetch` |
| --- | --- | --- |
| none | yes | yes — all seven reference kinds your scopes allow |
| `investigate` | yes | yes, but not `incident` references — `get_incident` is native to the `incident` profile and `investigate` does not inherit it |
| `incident` | yes | yes |
| `agent-observability` | **no** — it admits none of the search sources | yes, but `agent_run` references only |
| `admin` | yes — the documentation source only | yes — `doc` references only |

A refusal names the profile and tells you to connect without one, so this is diagnosable rather than
mysterious.

### Correlating and tracing your agent's calls

The server identifies itself in `serverInfo` — in the connection result for older clients, and in
the `_meta` of successful responses for clients on the 2026-07-28 protocol. **Read that field rather
than assuming a value**: the version it reports depends on how the build that answered you was
produced, and a deployment that carries no release identifier reports the placeholder
`0.0.0-unknown` rather than inventing one. Do not hard-code either the placeholder or a real version
into a client's compatibility check.

If your agent is instrumented with OpenTelemetry, its calls can join the trace Anectico records for
them. Send W3C trace context either as ordinary HTTP headers (`traceparent`, `tracestate`) or, for
clients on the 2026-07-28 protocol that put per-request metadata in `_meta`, as `_meta` fields —
`com.anectico/traceparent` and `com.anectico/tracestate`, or the bare header names. Headers win when
both are present.

W3C `baggage` is filtered before your call reaches a tool. Two keys are accepted, both purely for
correlating your own agent's work with what Anectico records:

| Baggage key | Meaning |
| --- | --- |
| `anectico.agent.session_id` | The agent session making the calls. |
| `anectico.agent.task_id` | The task or job the calls belong to. |

Members with any other key are dropped. An accepted member keeps its key and value only — any
properties attached to it are stripped — and is dropped entirely if the resulting `key=value` pair
exceeds 256 bytes. From the tool call onward, the two keys above are the only baggage that exists.

Be precise about what that does and does not promise. The filter applies to your tool call and to
everything Anectico does to answer it — not to the moment your request arrives. Baggage you send may
still appear in Anectico's own diagnostic traces of the request itself, including on requests that
are rejected before any tool runs. Treat baggage as ordinary request metadata: send only what you
would put in any other header. In particular, it is not a way to tell Anectico who your end user is
— identity comes from the credential and from your instrumented application, never from a header on
an agent's request.

Tool-list and other list responses carry cache hints (`ttlMs`, `cacheScope`). Anectico always sends
`ttlMs: 0` and `cacheScope: "private"`: the list of tools depends on the scopes of the key that
asked for it, so it must not be cached — not by a shared proxy, and not across a change of
credential or a change of permissions. Re-list when you need it.

## Claude Code

`anectico agent bootstrap --host claude` writes this configuration for you. To add it yourself:

```bash
claude mcp add --transport http anectico https://app.anectico.com/mcp \
  --header "Authorization: Bearer an_..."
```

Run `claude mcp list` in a new session to confirm the server is connected. Protect the local client
configuration because it contains the header value.

## Other MCP clients

Create a remote HTTP server named `anectico`, use the endpoint above, and set the bearer header through
the client’s credential UI or local configuration. The client must support Streamable HTTP.

## Verify with a safe question

Ask the agent:

> Search for the most recently active customers and explain which Anectico tools you used. Do not change anything.

Then ask a bounded production question such as:

> What happened to buyer@acme.example in the last hour? Cite the underlying Anectico evidence.

Tool results include deep links back to the dashboard so you can verify the answer.

### Ask for the documentation

The endpoint also serves this documentation, so an agent can look something up instead of guessing
it. Ask:

> Using Anectico's own documentation, what scope does a key need to read session replay content?

`search_docs` finds the page and `get_doc` reads it, both through `execute_read_action`, and the
`search`/`fetch` pair covers the same ground for a client that only knows those two. Each search hit
has a stable `ref` that `fetch` accepts. `get_doc` takes an optional H2 through H6 `heading`, which
is how to read one endpoint or command group out of a long reference page without pulling the whole
thing through the agent's context. An uncapped page body is byte-identical to the body published
at [anectico.com/docs](https://anectico.com/docs); repository frontmatter is omitted, and every hit
carries the public URL so you can check what the agent read.

Both need `docs:read`, which every role holds and which discloses nothing about your organization. A
key minted without it simply reports the documentation source as unauthorized. The CLI's
`investigate` and `incident` bundles include it; `agent-observability` and `admin` do not. The URL
profile `agent-observability` excludes docs even when the key holds the scope.

`get_doc` caps its body at 48 KiB. If `truncated` is true, use the returned `outline` to request an
H2–H6 section or a smaller nested section; omitted content is not summarized. Supply a page `slug`
to `get_doc`, not the `ref` accepted by `fetch`. See
[Machine-readable documentation](/docs/reference/machine-readable-docs) for the public Markdown,
manifest, `llms.txt`, and complete-corpus retrieval surfaces.

## Add writes only when required

Read access is sufficient for investigation. If an agent must acknowledge alerts, triage Issues, or
manage another resource, create a separate key with `mcp:write` and only the required domain scopes.
[Scope recipes for agent jobs](/docs/agents/scope-recipes) gives the minimum set per job, and
[How agent access is enforced](/docs/agents/security-model) explains what a scope grants, how
confirmation works, and what "destructive" means on this surface.

When an agent is retired — or a laptop is lost — follow
[Revoke an agent's access](/docs/agents/revoke-agent-access), which is also exact about what a
revocation does not reach.


Mutation receipts encode `targets[].before_version` and `targets[].after_version` as exact
unsigned decimal strings, or `null` when unavailable. Keep them as strings when preparing a
subsequent expected revision; converting through a JavaScript number can round large versions.
