|
| 1 | +--- |
| 2 | +title: Connect an MCP Client |
| 3 | +description: Point Claude Code, Claude Desktop, or any MCP client at your running app — OAuth or API key — and verify the agent can see and operate it. |
| 4 | +--- |
| 5 | + |
| 6 | +# Connect an MCP Client |
| 7 | + |
| 8 | +Every ObjectStack deployment is already an MCP server. The runtime serves the |
| 9 | +[Model Context Protocol](https://modelcontextprotocol.io) at **`/api/v1/mcp`** |
| 10 | +— on by default, no plugin to install, no configuration step. Your objects and |
| 11 | +exposed actions become typed tools the moment you define them; this page is |
| 12 | +about the only thing left to do: **connecting a client and proving it works**. |
| 13 | + |
| 14 | +<Callout type="info"> |
| 15 | +To turn the surface off, set `OS_MCP_SERVER_ENABLED=false` — the endpoint then |
| 16 | +returns 404 and the **Setup → Connect an Agent** page disappears with it. See |
| 17 | +[environment variables](/docs/deployment/environment-variables#mcp-server). |
| 18 | +</Callout> |
| 19 | + |
| 20 | +## Claude Code (one command) |
| 21 | + |
| 22 | +Interactive clients use OAuth — each deployment is its own OAuth 2.1 |
| 23 | +authorization server, so there are no admin-minted credentials to pass around. |
| 24 | +The first tool call opens a browser login and you connect **as yourself**: |
| 25 | + |
| 26 | +```bash |
| 27 | +# local dev server |
| 28 | +claude mcp add --transport http my-app http://localhost:3000/api/v1/mcp |
| 29 | + |
| 30 | +# a deployed instance |
| 31 | +claude mcp add --transport http my-app https://your-deployment.example.com/api/v1/mcp |
| 32 | +``` |
| 33 | + |
| 34 | +For headless use (CI, containers) skip OAuth and attach an |
| 35 | +[API key](#headless-api-keys) instead: |
| 36 | + |
| 37 | +```bash |
| 38 | +claude mcp add --transport http my-app https://your-deployment.example.com/api/v1/mcp \ |
| 39 | + --header "x-api-key: osk_..." |
| 40 | +``` |
| 41 | + |
| 42 | +## Claude Desktop and claude.ai |
| 43 | + |
| 44 | +**Settings → Connectors → Add custom connector**, then paste the MCP URL |
| 45 | +(`https://your-deployment.example.com/api/v1/mcp`). The first use walks through |
| 46 | +the same browser login. |
| 47 | + |
| 48 | +## Any MCP client (`.mcp.json`) |
| 49 | + |
| 50 | +Clients that read an `mcpServers` map connect the same way. With an API key: |
| 51 | + |
| 52 | +```json |
| 53 | +{ |
| 54 | + "mcpServers": { |
| 55 | + "objectstack": { |
| 56 | + "type": "http", |
| 57 | + "url": "https://your-deployment.example.com/api/v1/mcp", |
| 58 | + "headers": { "x-api-key": "osk_..." } |
| 59 | + } |
| 60 | + } |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +## Headless: API keys |
| 65 | + |
| 66 | +Mint a key from **Setup → Connect an Agent** in the Console (which also shows |
| 67 | +copy-paste-ready connect snippets per client), or over REST: |
| 68 | + |
| 69 | +```bash |
| 70 | +curl -b cookies.txt -X POST https://your-deployment.example.com/api/v1/keys |
| 71 | +# → { "key": "osk_..." } — shown once; store it in your secret manager |
| 72 | +``` |
| 73 | + |
| 74 | +Send it on every request in any of three equivalent forms: |
| 75 | + |
| 76 | +| Header | Example | |
| 77 | +|:---|:---| |
| 78 | +| `x-api-key` | `x-api-key: osk_...` | |
| 79 | +| `Authorization: ApiKey` | `Authorization: ApiKey osk_...` | |
| 80 | +| `Authorization: Bearer` | `Authorization: Bearer osk_...` (recognized by the `osk_` prefix) | |
| 81 | + |
| 82 | +<Callout type="warn"> |
| 83 | +OAuth requires TLS — plain-HTTP deployments (except `localhost`) fall back to |
| 84 | +**API-key-only**: the browser-login track is disabled rather than allowed to |
| 85 | +run insecurely. |
| 86 | +</Callout> |
| 87 | + |
| 88 | +## What the agent gets |
| 89 | + |
| 90 | +Ten data and action tools, generated from your metadata: |
| 91 | + |
| 92 | +| Tool | What it does | |
| 93 | +|:---|:---| |
| 94 | +| `list_objects` / `describe_object` | Discover which objects exist and their fields | |
| 95 | +| `query_records` / `get_record` | Read data (list queries are capped at 50 rows per page by default) | |
| 96 | +| `aggregate_records` | Grouped aggregation (registered when the active driver supports it) | |
| 97 | +| `create_record` / `update_record` / `delete_record` | Write data | |
| 98 | +| `list_actions` / `run_action` | Discover and invoke your business actions by name | |
| 99 | + |
| 100 | +Two exposure rules to know: |
| 101 | + |
| 102 | +- **Objects are exposed automatically** — except `sys_*` system objects, which |
| 103 | + are blocked fail-closed. |
| 104 | +- **Actions require the author's opt-in**: `ai: { exposed: true }` plus an |
| 105 | + `ai.description` of at least 40 characters, and the action must be callable |
| 106 | + without a UI (`script` with a body or registered handler, or `flow`). |
| 107 | + See [Actions as Tools](/docs/ai/actions-as-tools) and |
| 108 | + [Actions](/docs/ui/actions). |
| 109 | + |
| 110 | +## The security model |
| 111 | + |
| 112 | +- **Every call runs as the caller.** The MCP bridge resolves the same |
| 113 | + `ExecutionContext` as a REST request, so RBAC, row-level security, and |
| 114 | + field-level security apply to the agent exactly as they do to a person in |
| 115 | + the Console. Sparse results or denied writes usually mean governance is |
| 116 | + *working*, not that the connection is broken. |
| 117 | +- **OAuth scopes narrow the toolset.** Tokens carry `data:read`, |
| 118 | + `data:write`, and `actions:execute` scopes — tools outside the granted |
| 119 | + scopes are not even registered for that session. API-key and session |
| 120 | + callers get the full set, still permission-checked per call. |
| 121 | +- **Action bodies run as trusted app code** once invoked (the `ai.exposed` |
| 122 | + gate and `requiredPermissions` are checked at *invoke* time). Treat writing |
| 123 | + an action as a code-review-worthy act — that's the real security boundary. |
| 124 | +- An action can also declare `ai.requiresConfirmation`; destructive-looking |
| 125 | + actions (`confirmText`, danger variants) default to requiring it. |
| 126 | + |
| 127 | +## Verify the connection |
| 128 | + |
| 129 | +Ask the agent something only the live schema can answer: |
| 130 | + |
| 131 | +> "What objects does this app have, and what fields does the main one carry?" |
| 132 | +
|
| 133 | +You should see `list_objects` and `describe_object` fire. The natural working |
| 134 | +pattern for an agent is `list_objects` → `describe_object` → `query_records` → |
| 135 | +`run_action` — if all four work, the connection is fully operational. |
| 136 | + |
| 137 | +Agents work noticeably better with the app's **skill file**: download it from |
| 138 | +`GET /api/v1/mcp/skill`, or install the |
| 139 | +[official Claude plugin](https://github.com/objectstack-ai/claude-plugin) |
| 140 | +(`claude plugin marketplace add objectstack-ai/claude-plugin`) which bundles the |
| 141 | +skill and a guided `/objectstack:connect` command. |
| 142 | + |
| 143 | +## Troubleshooting |
| 144 | + |
| 145 | +| Symptom | Cause → fix | |
| 146 | +|:---|:---| |
| 147 | +| `404` on `/api/v1/mcp` | The surface is disabled — unset `OS_MCP_SERVER_ENABLED` (default is on) | |
| 148 | +| `501 Not Implemented` | The MCP plugin isn't part of this build — check your stack's plugins | |
| 149 | +| `401` on every call | Anonymous or invalid credentials. Interactive clients: complete the browser login (the `WWW-Authenticate` header advertises the OAuth metadata). Headless: check the `osk_` key and header spelling | |
| 150 | +| `403 insufficient_scope` | The OAuth token lacks the scope for that tool family (e.g. writes without `data:write`) — reconnect and grant the scope | |
| 151 | +| An action is missing from `list_actions` | `ai.exposed` is not `true`, `ai.description` is shorter than 40 characters, the type isn't headless-callable (`url` / `modal` / `form` never appear), it targets a `sys_*` object, or the caller fails its `requiredPermissions` | |
| 152 | +| Reads return few rows / writes denied | Working as designed — the caller's permissions and RLS apply. Verify with the same user in the Console | |
| 153 | + |
| 154 | +## Related |
| 155 | + |
| 156 | +- [Actions as Tools](/docs/ai/actions-as-tools) — the `run_action` bridge and its governance |
| 157 | +- [Actions](/docs/ui/actions) — defining the actions you expose |
| 158 | +- [Your app as an MCP server](/docs/api#your-app-as-an-mcp-server) — the API-level view |
| 159 | +- [AI Agents](/docs/ai/agents) — building agents *inside* your app (the other direction) |
0 commit comments