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