# Connect an agent with MCP

Use the shared product operations through authenticated, scoped tools.

BytesMap exposes its registered product operations through a stateless Streamable
HTTP endpoint at `/api/mcp`. Tools use the real API routes and the shared platform
authorization layer. There is no separate agent implementation of query, chart,
or dashboard logic.

## Connect

Open **Settings → Agents & MCP** for this instance's endpoint and
supported authentication method. Hosted instances support OAuth-capable clients.
BytesMap advertises its authorization server through the MCP challenge and
protected-resource metadata. The client opens **Connect to BytesMap**, where you
sign in, choose a workspace, and approve full access or selected resources. The
same permission editor is used for people, API keys, and assistant conversations.
**Settings → Agents & MCP** lets you edit access or disconnect.

Packaged BytesMap Desktop accepts local clients with a scoped, expiring API key.
The CLI's desktop transport discovers and authenticates the current instance, so
clients do not need to save its changing localhost port. Choose **Selected resources**, include
**MCP connection → View**, and set an expiry. Full-workspace keys and keys without
an expiry are rejected. The private host capability is never an agent credential.
Desktop OAuth and automatic editing of other applications' client settings are
not provided.

### Choose your client

Settings includes client-specific recipes with copy controls for Claude Code, Codex,
Cursor, VS Code, Claude Desktop, Windsurf, Zed, and other MCP clients. Select your
client and either **Sign in with BytesMap** or **API key**. The guide uses the
instance's advertised endpoint; it does not guess from the browser address.

For a hosted OAuth connection, replace the example with your actual endpoint:

```sh
# Claude Code: use /mcp after adding the server to finish authorization.
claude mcp add --scope user --transport http bytesmap https://api.example.com/api/mcp

# Codex: add the endpoint, then complete the browser sign-in.
codex mcp add bytesmap --url https://api.example.com/api/mcp
codex mcp login bytesmap
```

| Client                      | Where to connect                                                                                              |
| --------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Claude Code                 | User-scoped `claude mcp add`; authorize through `/mcp`.                                                       |
| Codex CLI and IDE extension | `codex mcp add` and `codex mcp login`; shared user configuration.                                             |
| Cursor                      | Personal `~/.cursor/mcp.json`, under `mcpServers`.                                                            |
| VS Code                     | Run **MCP: Add Server** from the Command Palette and select HTTP or local stdio.                              |
| Claude Desktop              | Add a remote custom connector in its connector settings, or use `claude_desktop_config.json` for local stdio. |
| Windsurf                    | `~/.codeium/windsurf/mcp_config.json`, with `serverUrl` for HTTP.                                             |
| Zed                         | `context_servers` in settings, with a native `url` for HTTP.                                                  |

Claude's remote connectors must reach the API from the provider's infrastructure;
a private localhost address is not a hosted connector. Use local stdio for BytesMap
Desktop. Client availability and authentication features can vary by client version.
The in-app guide links to each client's official setup instructions.

For Codex API-key authentication, keep the credential in an environment variable
available to the Codex process:

```toml
[mcp_servers.bytesmap]
url = "https://api.example.com/api/mcp"
bearer_token_env_var = "BYTESMAP_API_KEY"
```

Do not put real keys in project configuration or source control. The guide supplies
placeholders only. Connected OAuth clients appear in **Agents & MCP**, where you
can edit resource access or disconnect. API-key clients are managed under **API keys**.

### Desktop clients using stdio

Keep BytesMap Desktop running. Save the selected-resource key using stdin or your
secret manager; do not put it in command arguments:

```sh
bytesmap login --desktop --name desktop-agent --token-stdin
```

Configure a client that supports MCP stdio:

```json
{
  "mcpServers": {
    "bytesmap": {
      "command": "bytesmap",
      "args": ["--context", "desktop-agent", "mcp", "--desktop"]
    }
  }
}
```

Alternatively provide the scoped key through `BYTESMAP_API_TOKEN` and run
`bytesmap mcp --desktop`. Remove `BYTESMAP_API_URL` for this transport. Remote and
native contexts' saved credentials are never reused for desktop discovery.
`bytesmap logout desktop-agent` removes the saved connection; revoke its key in
Settings to disconnect all clients using that key.

The desktop publishes only its per-launch public identity and loopback origin in
the current user's private `~/.bytesmap/desktop.json`. Before each request, the
CLI verifies a fresh Ed25519 challenge and sends the key on that **same TCP
connection**. The connector cannot silently create another connection after the
proof. A stale port, changed key, redirect or lost connection fails closed; no
tool call is automatically replayed. Reopening the app publishes its new identity.
The private record's integrity depends on the operating system user account;
this is not a sandbox against other code already running as that same user.
Requests are bounded to 4 MB, replies to 8 MB and transport time to two minutes,
with at most 16 outstanding stdio operations. Cancellation remains available
when that limit is reached.

Direct URL/header clients can still use the exact current endpoint shown in
Settings. Prefer the discovered stdio transport for a persistent desktop setup;
an old localhost URL alone does not authenticate the process now listening there.

### Hosted and directly configured HTTP clients

For clients using an API key:

1. Open **Settings → API keys** in the workspace you want the client to use.
2. Create a key with **MCP connection → View**, plus the resource permissions it
   needs. An authoring key can have source read access and query/chart/dashboard
   edit access without permission to execute queries.
3. Copy the key when it is shown. Configure the client with the API's `/api/mcp`
   URL and an `Authorization: Bearer <key>` header.

For clients supporting URL/header configuration:

```json
{
  "mcpServers": {
    "bytesmap": {
      "url": "http://localhost:4077/api/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}
```

Use the actual HTTPS API URL for remote hosting. Prefer the client's secret or
environment-variable support rather than committing credentials in a config file.
The Settings page also shows the endpoint and a configuration example.

Keys are fixed to their workspace and intersect with the owner's current access.
Expiry, revocation, demotion, or removal applies to later tool calls. Full-access
keys still cannot mint API or event-ingestion keys, or manage account security,
memberships or invitations. Existing event-key metadata and revocation have the
separate dataset administration boundary described below.
See [identity and access](https://bytesmap.com/docs/workspace/access.md).

## Tools and execution

`tools/list` is the authoritative list for the current caller. It comes from
the `mcp` metadata beside each route/contract and uses the route's request schema.
Current capabilities include:

| Area               | Tools                                                                                                           |
| ------------------ | --------------------------------------------------------------------------------------------------------------- |
| Workspace          | `workspace_list`                                                                                                |
| Sources            | `source_list`, `source_kinds_list`, `source_schema_read`, source health/profile and key-value preview tools     |
| Query authoring    | `query_list`, `query_read`, `query_create`, `query_update`, `query_delete`                                      |
| Visual nodes       | `flow_catalog`, `flow_validate`: actual node templates, ports, controls, operators and the saved graph protocol |
| Views              | `view_catalog`, `view_validate`, `view_list`, `view_get`, `view_create`, `view_update`, `view_remove`           |
| Chart authoring    | `chart_list`, `chart_read`, `chart_create`, `chart_update`, `chart_delete`, `chart_suggest`                     |
| Dashboards         | `dashboard_list`, `dashboard_read`, `dashboard_create`, `dashboard_update`, overview/home tools                 |
| Dashboard folders  | `dashboard_folder_create`, `dashboard_folder_rename`, `dashboard_folder_remove`, `dashboard_move`               |
| Explicit execution | `source_sql_run`, `query_run`, `flow_preview`, `view_data`; `view_submit` explicitly runs a configured workflow |
| Planning           | `query_cost`, `view_cost`, `dashboard_cost`                                                                     |
| Events             | Dataset and destination setup, typed fields, `events_search`, `events_inspect`, `events_groups_read`            |
| Activity           | `audit_list`, when the key has workspace audit read access                                                      |

This is a capability overview. The [operation reference](https://bytesmap.com/docs/reference.md) lists
every public tool with its exact input schema, permission and effect annotation.
Views are the public component-authoring family; internal panel transport aliases
are not a second set of discoverable tools. Read `view_catalog` before authoring
and use `view_validate` with real sample columns before publication.

Event ingestion is an effect: `events_ingest` appends using the caller's existing
dataset ingest permission. It does not create a bearer key. The user creates
ingestion credentials in Events; tools can list redacted credential metadata and,
with dataset administration access, revoke an existing credential.

The permission layer filters capabilities; individual calls also check the exact
resource ids. A client may have access to one source without access to another.
Source connection/discovery tools are opt-in per route. Desktop tools may inspect
and scan an authorized saved server; public MCP does not expose host-only routes.
There is no raw SSH or operating-system command tool.

Read a definition before editing it. `query_update` accepts the same saved query
protocol as the app, including flow graphs and native statements; the existing
flow/query validators apply. `chart_update` changes rendering settings without
executing the query. `dashboard_update` changes title, description, layout, or
parameters through the same handler as the dashboard UI.

Saving does not run a query. Run/preview requires explicit execution permissions,
including every source a flow references. Query-plan estimation does not execute
the data query, but it contacts the database planner; its permission checks still
apply. Schema/profile tools may also contact the database.

Dashboard promotion uses three separate tools. `dashboard_promotion_review`
reads saved metadata and prior catalog evidence; it returns blockers and a comparison
fingerprint without contacting source databases. `source_promotion_schema_capture`
explicitly reads bounded database catalogs and stores compatibility evidence; it
never reads application rows or accepts caller-supplied schema. After reviewing
explicit destination mappings, `dashboard_promotion_apply` creates new unpublished
drafts atomically. It does not copy credentials, publish workflows, run effects or
activate schedules. Native SQL, HTTP effect remapping and unresolved dependencies
remain blocked. A reviewable result is draft-copy readiness, not a production safety
certificate. Reuse the exact request key and body after an uncertain copy response;
a stale-review conflict requires a fresh review before a new copy intent.

Tool output can contain real database values when execution or inspection is
authorized. Results retain their `estimated`, `sampled`, and `truncated` metadata;
clients should preserve those distinctions. There is no claim that attaching an
agent keeps every returned value away from that agent's model provider.

## Transport and authorization

```text
MCP request → authenticate key/session → permission-filtered tool
            → app.fetch(internal Request + verified context)
            → route validation + current resource permissions → handler
            → shared platform operation → audit record
```

The verified internal context is attached in a process-local WeakMap. It is not a
JSON argument or a forwarded identity header. The inner request rechecks current
membership/key restrictions without authenticating or counting the credential a
second time. API and MCP share errors and response shapes.

The server supports `initialize`, `ping`, `tools/list`, and `tools/call` with
protocol version `2025-06-18`. Notifications receive `202` without a JSON-RPC
response. There is no server event stream or persistent MCP session; authenticated
GET/DELETE probes receive `405` with `Allow: POST`. Tool failures use
`isError: true`; protocol failures use JSON-RPC errors. The server does not offer
resources, prompts, or subscriptions.

## OAuth and live access

The maintained Better Auth OAuth provider owns registration, S256 PKCE, signed
consent requests, authorization codes, rotating refresh tokens, and signing keys.
The configured API origin is the issuer; caller-supplied Host/Forwarded headers
cannot change it. Set `BYTESMAP_ADVERTISED_ORIGIN` to the external API origin when
hosting behind a proxy. Access tokens are audience-bound to that origin's
`/api/mcp` and cannot be replayed against ordinary REST or account endpoints.

OpenShip's grant-holder pattern ties each user/client connection to one workspace.
JWTs contain a binding reference, not a frozen copy of permissions. Every tool call
resolves current membership and intersects its current grants with the connection's
selected grants. Disconnect removes tokens, consent, and the binding; reconnect
creates a new binding so old JWTs cannot regain access. Revoking a JWT through the
OAuth revocation endpoint also closes that connection. Revoking only a refresh
token prevents future refreshes; use Disconnect to revoke all existing access.
Userinfo and introspection also honor live binding and membership state.

An explicitly local development host can still use a framework session for OAuth
consent. A one-use host assertion creates its HttpOnly cookie only after the normal
kernel-peer and pinned-user checks. No identity header or browser-supplied user ID
can bootstrap this session. This development path does not expose OAuth to external
clients through the packaged desktop's private host boundary.

## Desktop Mindwire

The desktop assistant uses Mindwire's SDK and daemon natively inside the existing
control plane. Its private MCP bridge is generated from the same operation catalog
and calls the same shared platform services with a delegated user context. Native
agent capabilities cannot bypass that bridge. See [the assistant guide](https://github.com/oblien/bytesmap/blob/main/docs/assistant.md)
for sign-in, approvals, supported harnesses, and verification.
