MCP endpoint (POST only)
- Sandbox: available
- Staging: available
- Production: unknown
curl -X DELETE 'https://api.sandbox.ovok.com/mcp'MCP endpoint POST
Ovok's MCP server, for Claude or any MCP client: it sets up the project it is connected to, as one of the project's admins. **Auth:** an access token from Ovok's MCP OAuth server, never a Medplum token. Clients find it the standard way: 1. A `GET`, `POST` or `DELETE` without a token answers 401 with `WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp"`. 2. That document names the authorization server, `<this host>/mcp`. Its metadata is at `/.well-known/oauth-authorization-server/mcp`. 3. The client registers itself at `/mcp/register`, sends the user to `/mcp/authorize` (PKCE S256), and exchanges the code at `/mcp/token`. The answer to the client carries `iss`, this authorization server's issuer (RFC 9207), on allow and on deny. Sign-in and consent happen on the Ovok console. Only an admin of the project can connect a client, and never to the Super Admin project. ## Behaviour - Streamable HTTP, stateless: send every JSON-RPC message with `POST`, one per request. A batch answers 400. There are no sessions: `GET` and `DELETE` answer 405 to a caller with a valid token, and 401 without one. - `server/discover` (MCP 2026-07-28) answers the protocol versions this server speaks, its `tools` capability and its name. None of those versions is 2026-07-28 yet: start with `initialize`. - `/mcp`, its OAuth routes and both MCP metadata documents answer with `Strict-Transport-Security: max-age=31536000; includeSubDomains`. - Every request checks the token, then that its account is still an admin of the project. An account that is no longer an admin gets 403 at once, whatever the token's expiry. When that can't be read, the answer is 503: try again. - Tools act on the project the connection was granted for: - `get_project`; - `get_settings`, `set_setting`, `set_setting_value`; - `list_members`, `invite_member`; - `list_apps` (without the apps' secrets), `create_app`; - `get_signals`. - Each tool runs the same service as the matching API route, with the same rules. A refused call answers a tool error carrying the route's status and error body. - Limits: - Each admin may send 300 messages a minute. Over that, the answer is 429 with `Retry-After`. - Each tool allows 60 calls a minute per admin; `create_app` and `invite_member` allow 10. Over that, the tool error has status 429 and `retryAfterSec`. - Tool errors carry the route's status and body for a refusal (4xx). A failure (5xx) is logged and answered without its detail. - MCP calls are not API usage: like the console's own calls, they are left out of statistics and the plan's monthly request cap. - Access tokens last an hour. Refresh tokens last 30 days, and each one works once: a refresh answers a new pair. A repeat within 30 seconds, such as parallel refreshes, gets a pair of its own. A refresh token used again later ends the connection, and so does revoking either token. A connection ends 90 days after its consent, however often it refreshes: connect again. ## Example Add it to Claude Code, which signs in through the browser on first use: ```bash claude mcp add --transport http ovok https://api.sandbox.ovok.com/mcp ```
Get legal consent GET
Returns the signed-in practitioner's legal consent: which terms they accepted, when, and whether the acceptance is still active. Use it on app load to decide whether to ask for the terms again. **Auth:** Bearer token for a practitioner session (the profile must be a `Practitioner`). The caller's access policy must grant `Consent:read` and `Consent:search` (a project admin who holds no access policy skips this check). **Scope:** The caller's practitioner profile in the caller's project (from the token). ## Behaviour - `legal` is `null` when the practitioner has never accepted the terms in this project. - `status` is `active` or `revoked`. Publishing new terms revokes every acceptance on an older version with `revokedReason: "tos-updated"`; withdrawing revokes it with `revokedReason: "withdrawn"`. Both reasons are `null` while the consent is active. - `revokedAt` is when the revoke happened, or for `tos-updated` when the new terms were published. - `termsUrl` is the terms page the practitioner accepted. It is `null` unless it is an `http(s)` URL or an app-relative path. - `termsUpdatedAt` is when the accepted terms were last updated, `null` when that is not known. - The published terms version is the one of the root project of your project tree: a child project follows its parent's terms. - Answers are cached for a rolling day. Any consent write by the practitioner and any publish of new terms refreshes them right away. ## Example ```bash curl -X GET 'https://api.sandbox.ovok.com/v1/me/consent/legal' \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE3NjcyNjg4MDAsImV4cCI6MTc2NzI3MjQwMH0.Ql3B_ztDvctlyZ2z0vp4sl3ExXRQGkzmxdmTAqxNzZk' ``` ```json { "legal": { "consentId": "c3499c27-2973-0a7f-807e-fb8676a92dcb", "status": "revoked", "termsUrl": "https://app.example.com/legal/terms-and-conditions", "termsUpdatedAt": "2026-09-01T08:00:00.000Z", "acceptedAt": "2026-09-02T07:30:00.000Z", "revokedAt": "2026-10-01T09:00:00.000Z", "revokedReason": "tos-updated" } } ```