# MCP server

> Connect any MCP client to Cymo. Agents read rooms, run scenarios and manage webhooks with one tool per API operation.

## Remote MCP server

Cymo hosts the MCP server at:

`Streamable HTTP`

```
https://mcp.cymatics.tech/mcp
```

Connect any MCP client that supports remote servers over Streamable HTTP. There's nothing to install or run. The server holds no data: each tool calls the API with your key, so the key's scopes, rate limits and metering apply, and a test key keeps every call on the simulator.

## Authentication

Send a Cymo API key from the dashboard as a bearer token: `Authorization: Bearer cym_test_…`. A request without one gets `401 missing_api_key`. Use a `cym_test_` key while you build and a `cym_live_` key for your paired Cymos.

## Set up your client

Replace `cym_test_…` with your key.

**Claude Code**

`terminal`

```sh
claude mcp add --transport http cymo https://mcp.cymatics.tech/mcp \
  --header "Authorization: Bearer cym_test_…"
```

**Cursor**

`~/.cursor/mcp.json`

```json
{
  "mcpServers": {
    "cymo": {
      "url": "https://mcp.cymatics.tech/mcp",
      "headers": {
        "Authorization": "Bearer cym_test_…"
      }
    }
  }
}
```

**VS Code**

`settings.json`

```json
{
  "mcp": {
    "servers": {
      "cymo": {
        "type": "http",
        "url": "https://mcp.cymatics.tech/mcp",
        "headers": {
          "Authorization": "Bearer cym_test_…"
        }
      }
    }
  }
}
```

**Codex**

`terminal`

```sh
export CYMO_API_KEY=cym_test_…
codex mcp add cymo --url https://mcp.cymatics.tech/mcp \
  --bearer-token-env-var CYMO_API_KEY
```

**Windsurf**

`~/.codeium/windsurf/mcp_config.json`

```json
{
  "mcpServers": {
    "cymo": {
      "serverUrl": "https://mcp.cymatics.tech/mcp",
      "headers": {
        "Authorization": "Bearer cym_test_…"
      }
    }
  }
}
```

**Gemini CLI**

`~/.gemini/settings.json`

```json
{
  "mcpServers": {
    "cymo": {
      "httpUrl": "https://mcp.cymatics.tech/mcp",
      "headers": {
        "Authorization": "Bearer cym_test_…"
      }
    }
  }
}
```

**Zed**

`settings.json`

```json
{
  "context_servers": {
    "cymo": {
      "url": "https://mcp.cymatics.tech/mcp",
      "headers": {
        "Authorization": "Bearer cym_test_…"
      }
    }
  }
}
```

Claude's custom connectors (Settings > Connectors) sign in with OAuth and can't send an API key, so in Claude Desktop use the [local server](#local). Any other client that can send a header on a remote server works with the same URL and header.

## Read-only mode

Add `?read_only=1` to the URL to list only tools that read, for an agent that should look but never change anything. The key's scopes still apply on top.

`URL`

```
https://mcp.cymatics.tech/mcp?read_only=1
```

## Local MCP server

The hosted server runs the same code as `@cymos/mcp`. Run it yourself over stdio with your key in `CYMO_API_KEY`; add `--read-only` after the package name to list only read tools.

**Claude Desktop**

`claude_desktop_config.json`

```json
{
  "mcpServers": {
    "cymo": {
      "command": "npx",
      "args": [
        "-y",
        "@cymos/mcp"
      ],
      "env": {
        "CYMO_API_KEY": "cym_test_…"
      }
    }
  }
}
```

**Claude Code**

`terminal`

```sh
claude mcp add cymo -e CYMO_API_KEY=cym_test_… \
  -- npx -y @cymos/mcp
```

**Cursor**

`~/.cursor/mcp.json`

```json
{
  "mcpServers": {
    "cymo": {
      "command": "npx",
      "args": [
        "-y",
        "@cymos/mcp"
      ],
      "env": {
        "CYMO_API_KEY": "cym_test_…"
      }
    }
  }
}
```

**VS Code**

`settings.json`

```json
{
  "mcp": {
    "servers": {
      "cymo": {
        "type": "stdio",
        "command": "npx",
        "args": [
          "-y",
          "@cymos/mcp"
        ],
        "env": {
          "CYMO_API_KEY": "cym_test_…"
        }
      }
    }
  }
}
```

**Codex**

`terminal`

```sh
codex mcp add cymo --env CYMO_API_KEY=cym_test_… \
  -- npx -y @cymos/mcp
```

With [cymatics mock start](https://docs.cymatics.tech/cli#mock), the whole loop runs on your machine:

`terminal`

```sh
$ cymatics mock start --port 8787

# then run the MCP server against it
$ CYMO_API_URL=http://127.0.0.1:8787 CYMO_API_KEY=cym_test_local \
    npx -y @cymos/mcp
```

## Local HTTP server

The package also serves Streamable HTTP at `http://127.0.0.1:8788/mcp`, with each client sending its own key as a bearer token, as with the hosted server.

`terminal`

```sh
$ npm install @cymos/mcp
$ PORT=8788 node node_modules/@cymos/mcp/dist/http.js
```

`~/.cursor/mcp.json`

```json
{
  "mcpServers": {
    "cymo": {
      "url": "http://127.0.0.1:8788/mcp",
      "headers": {
        "Authorization": "Bearer cym_test_…"
      }
    }
  }
}
```

- `CYMO_API_KEY` · stdio, required — The key every tool call is made with.
- `CYMO_API_URL` · default https://api.cymatics.tech — The API the tools call, such as a local simulator.
- `CYMO_MCP_READ_ONLY` · 1 or true — List only tools that read, for every client. The --read-only flag does the same over stdio.
- `PORT, HOST` · HTTP, default 8788, 127.0.0.1 — Where the HTTP server listens.

## MCP tools

The server has one tool per API operation, named after it and generated from the same contract, with the operation's input schema and read, write and destructive hints. Each [API reference](https://docs.cymatics.tech/api) page names its tool. Tools for operations with a request body take it as `input`. Read-only mode hides the tools marked write.

- Project  
[get\_project](https://docs.cymatics.tech/api/get-project)
- Capabilities  
[list\_capabilities](https://docs.cymatics.tech/api/list-capabilities)
- Models  
[list\_models](https://docs.cymatics.tech/api/list-models) [get\_model](https://docs.cymatics.tech/api/get-model)
- Sensing profiles  
[list\_sensing\_profiles](https://docs.cymatics.tech/api/list-sensing-profiles)
- Retention profiles  
[list\_retention\_profiles](https://docs.cymatics.tech/api/list-retention-profiles)
- Coordinate frames  
[list\_coordinate\_frames](https://docs.cymatics.tech/api/list-coordinate-frames)
- Skeleton definitions  
[list\_skeleton\_definitions](https://docs.cymatics.tech/api/list-skeleton-definitions)
- Rooms  
[list\_rooms](https://docs.cymatics.tech/api/list-rooms) [create\_room write](https://docs.cymatics.tech/api/create-room) [get\_room](https://docs.cymatics.tech/api/get-room) [update\_room write](https://docs.cymatics.tech/api/update-room) [archive\_room write](https://docs.cymatics.tech/api/archive-room) [get\_zones](https://docs.cymatics.tech/api/get-zones) [replace\_zones write](https://docs.cymatics.tech/api/replace-zones) [get\_calibration](https://docs.cymatics.tech/api/get-calibration) [replace\_calibration write](https://docs.cymatics.tech/api/replace-calibration) [get\_processing\_config](https://docs.cymatics.tech/api/get-processing-config) [replace\_processing\_config write](https://docs.cymatics.tech/api/replace-processing-config) [stream\_room](https://docs.cymatics.tech/api/stream-room)
- Devices  
[list\_devices](https://docs.cymatics.tech/api/list-devices) [get\_device](https://docs.cymatics.tech/api/get-device) [update\_device write](https://docs.cymatics.tech/api/update-device) [get\_device\_health](https://docs.cymatics.tech/api/get-device-health) [get\_sensing\_config](https://docs.cymatics.tech/api/get-sensing-config) [replace\_sensing\_config write](https://docs.cymatics.tech/api/replace-sensing-config) [list\_device\_observations](https://docs.cymatics.tech/api/list-device-observations)
- Scenes  
[list\_scenes](https://docs.cymatics.tech/api/list-scenes) [create\_scene write](https://docs.cymatics.tech/api/create-scene) [get\_scene](https://docs.cymatics.tech/api/get-scene) [update\_scene write](https://docs.cymatics.tech/api/update-scene) [delete\_scene write](https://docs.cymatics.tech/api/delete-scene)
- Vitals  
[get\_vitals](https://docs.cymatics.tech/api/get-vitals) [list\_vitals\_history](https://docs.cymatics.tech/api/list-vitals-history)
- Presence  
[get\_presence](https://docs.cymatics.tech/api/get-presence)
- Movement  
[get\_movement](https://docs.cymatics.tech/api/get-movement)
- Stress  
[get\_stress](https://docs.cymatics.tech/api/get-stress)
- Skeleton  
[get\_skeleton](https://docs.cymatics.tech/api/get-skeleton)
- Classifications  
[get\_classifications](https://docs.cymatics.tech/api/get-classifications)
- Results  
[list\_results](https://docs.cymatics.tech/api/list-results)
- People  
[list\_person\_results](https://docs.cymatics.tech/api/list-person-results) [stream\_person](https://docs.cymatics.tech/api/stream-person)
- Sleep  
[list\_sleep\_sessions](https://docs.cymatics.tech/api/list-sleep-sessions) [get\_sleep\_summary](https://docs.cymatics.tech/api/get-sleep-summary)
- Activity  
[get\_activity\_summary](https://docs.cymatics.tech/api/get-activity-summary)
- Analyses  
[list\_analyses](https://docs.cymatics.tech/api/list-analyses) [get\_analysis](https://docs.cymatics.tech/api/get-analysis)
- Events  
[list\_events](https://docs.cymatics.tech/api/list-events)
- Webhooks  
[list\_webhooks](https://docs.cymatics.tech/api/list-webhooks) [create\_webhook write](https://docs.cymatics.tech/api/create-webhook) [get\_webhook](https://docs.cymatics.tech/api/get-webhook) [update\_webhook write](https://docs.cymatics.tech/api/update-webhook) [delete\_webhook write](https://docs.cymatics.tech/api/delete-webhook) [test\_webhook write](https://docs.cymatics.tech/api/test-webhook) [list\_webhook\_deliveries](https://docs.cymatics.tech/api/list-webhook-deliveries)
- Scenarios  
[list\_scenarios](https://docs.cymatics.tech/api/list-scenarios) [create\_scenario\_run write](https://docs.cymatics.tech/api/create-scenario-run)
- Scenario runs  
[get\_scenario\_run](https://docs.cymatics.tech/api/get-scenario-run) [control\_scenario\_run write](https://docs.cymatics.tech/api/control-scenario-run)
- Raw data  
[create\_raw\_data\_export write](https://docs.cymatics.tech/api/create-raw-data-export) [get\_raw\_data\_export](https://docs.cymatics.tech/api/get-raw-data-export)
- Usage  
[get\_usage](https://docs.cymatics.tech/api/get-usage)
- Request logs  
[list\_request\_logs](https://docs.cymatics.tech/api/list-request-logs)

## Streaming windows

Stream tools return a bounded window instead of an open stream. They watch for `duration_ms` (default 10 s, up to 60 s) or until `max_samples` messages (default 20, up to 200), then return them with `truncated` set when more may have followed. Raw radar bytes have no tool; use the raw-data export tools instead.

`MCP tool`

```
stream_room({ room_id: "room_bedroom", duration_ms: 30000, max_samples: 50 })

{
  "data": {
    "object": "stream_window",
    "operation_id": "stream-room",
    "sample_count": 50,
    "truncated": true,
    "ended_by": "max_samples",
    "messages": [{ "id": "…", "kind": "snapshot", "data": { … } }, …]
  },
  "meta": { "livemode": false, … }
}
```

## Example prompts

- Run the bed\_exit scenario and tell me when the person left bed. · create\_scenario\_run, list\_events
- Is anyone in room\_bedroom right now? What are their heart rate and breathing? · get\_room, get\_vitals
- Watch room\_bedroom for 30 seconds and summarise what changed. · stream\_room
- How did the person in room\_bedroom sleep last night? · get\_sleep\_summary
- Send bed.exited and fall.detected events to my staging webhook. · create\_webhook, test\_webhook
- Which of my requests failed today, and why? · list\_request\_logs
