# Ovok MCP tools

Canonical page: https://docs.ovok.com/working-with-ai-agents/mcp-tools

Every tool acts on the one project the connection was approved for, as the admin who approved it. Each calls the service behind the matching API route, so it follows that route's rules and answers its errors. The server tells the agent to read before it changes: `get_project` and `get_settings` first.

Paged tools take `page` (from `0`) and `count` (`1`–`100`, default `20`).

## Project and settings [#project-and-settings]

| Tool | Changes data | What it does | Inputs |
| --- | --- | --- | --- |
| `get_project` | No | Reads the project's ID, name and tenant code. | — |
| `get_settings` | No | Reads every on/off setting, with its saved value (`overrides`, `null` when unset) and the value Ovok applies (`effectiveSettings`), and the text settings (`values`): app URLs and default access policies, with `warnings`. | — |
| `set_setting` | Yes | Turns one on/off setting on or off, and answers every setting after the change, with `warnings`. | `key`, `enabled` |
| `set_setting_value` | Yes | Sets one text setting, or clears it with `null`. App URLs are `https` URLs or app deep links; access policy settings are `AccessPolicy/<id>` of a policy in this project. Answers every setting, with `warnings`. | `key`, `value` |

The keys are the ones on [Settings and features](/settings-and-features). Some switches need another setting to work. `warnings` lists each switch that is on, saved or by default, but has no effect, as `{ setting, message }`; the message names the setting it needs. See [Defaults and dependencies](/settings-and-features#defaults-and-dependencies).

## Members and apps [#members-and-apps]

| Tool | Changes data | What it does | Inputs |
| --- | --- | --- | --- |
| `list_members` | No | Lists the project's members, with their profile, access policy and admin flag. | `page`, `count` |
| `invite_member` | Yes, sends email | Invites a practitioner by email. A new address gets an account and an email to set its password; `admin: true` makes them a project admin. | `email`, `firstName`, `lastName`, `admin`, optional `access` |
| `list_apps` | No | Lists the project's client applications (OAuth clients), without their secrets. | `page`, `count`, optional `search`, `type` |
| `create_app` | Yes | Creates a client application and shows its secret once: `default` for an app signing users in, `admin` for a server acting as a project admin, `social-login` with its Google or Apple sign-ins. | `type`, `name`, `description`, optional `socialLogins` |

See [Invitations](/invitations) and [Authentication](/authentication) for what these create.

## Signals [#signals]

| Tool | Changes data | What it does | Inputs |
| --- | --- | --- | --- |
| `get_signals` | No | Reads whether [Signals](/signals) is on for the project, which tenant it uses, and its counts of enrolled patients, alerts and observations. | — |

## Requests and usage [#requests-and-usage]

Use these to debug an app: find the requests that failed, then read one in full.

| Tool | Changes data | What it does | Inputs |
| --- | --- | --- | --- |
| `get_requests` | No | Lists the project's API requests on this environment, newest first: method, route, status, duration and `traceId`. Where request traces are sampled, it is a sample. The Console's own calls are left out. | `page`, `count`, optional `search`, `window` |
| `get_failed_requests` | No | As `get_requests`, for requests answered `4xx` or `5xx`, or marked as an error. | `page`, `count`, optional `search`, `window` |
| `get_request_details` | No | Reads one request in full, by its `traceId`: every step it ran (calls to the FHIR store, Redis and other services) in start order, with durations and errors. Looks back 7 days. | `traceId` |
| `get_usage_details` | No | Reads the project's plan and this month's usage against its allowances (API requests, monitored patients, devices and sub-tenants), with its invoices. For a parent project; a sub-tenant answers `403 not_parent_project`. | — |

`search` matches part of the method and route, in any case. `window` is `1h`, `24h` (the default) or `7d`.

## FHIR data [#fhir-data]

These read and change the project's FHIR R4 data as the admin who approved the connection: their project, its linked projects and their access policy decide what each call reaches, as on the [FHIR API](/fhir-basics). Use them for data, such as patients, observations and devices; use the tools above for project setup.

| Tool | Changes data | What it does | Inputs |
| --- | --- | --- | --- |
| `search_fhir` | No | Searches one resource type with FHIR search parameters, such as `name=Smith&_count=10`. Answers `resources`, `hasMore`, and `total` when you ask for `_total=accurate`. Search by `_id` to read one resource. | `resourceType`, optional `query` |
| `create_fhir_resource` | Yes | Creates one resource and answers it with its ID. | `resource` |
| `update_fhir_resource` | Yes | **Replaces** one resource: fields left out are deleted. Send the whole resource back with its `meta.versionId`, and the update answers `412` if someone changed it since. | `resource` (with `id`) |
| `delete_fhir_resource` | Yes | Deletes one resource. It cannot be restored through Ovok, and resources that reference it are kept. | `resourceType`, `id` |

- `query` is a query string without the leading `?`. A page holds at most 100 resources (`_count`); page on with `_offset`.
- Each tool's description carries its warning, and the update and delete tools are marked destructive, so a client can ask you before it runs them.
- `Project`, `ProjectMembership`, `User`, `UserSecurityRequest`, `Login`, `ClientApplication`, `Bot`, `JsonWebKey` and `DomainConfiguration` are refused with `422`. Use `create_app`, `invite_member` and the settings tools for project administration.
- Writes to patients, devices and observations reach Ovok's apps and Signals as any write through the API does.

## CMS content [#cms-content]

These manage the project's [CMS](/cms) content, as the Console does. They are offered only where the CMS is configured.

| Tool | Changes data | What it does | Inputs |
| --- | --- | --- | --- |
| `get_cms_schema` | No | Lists the collections the project can write (legal pages, posts, translations, content types and items, media, release notes) with their fields, and the locales. Read it before writing a document. | — |
| `list_cms_documents` | No | Lists the project's documents in one collection. | `collection`, `page`, `count`, optional `where`, `sort`, `locale`, `draft`, `depth` |
| `get_cms_document` | No | Reads one document by ID. | `collection`, `id`, optional `locale`, `draft`, `depth` |
| `create_cms_document` | Yes | Creates a document, its localized fields in `locale`. Answers a summary, not the document. | `collection`, `data`, optional `locale`, `draft`, `returnDocument` |
| `update_cms_document` | Yes | Changes the fields given in `data`; the others are kept. Answers a summary, not the document. | `collection`, `id`, `data`, optional `locale`, `draft`, `returnDocument` |
| `delete_cms_document` | Yes | Deletes one document. It cannot be undone. | `collection`, `id` |
| `set_cms_translations` | Yes | Sets strings of one `translations` group in one locale, by key, and keeps every other key and locale. Creates the group if it does not exist. | `slug`, `locale`, optional `strings`, `remove`, `title`, `publish` |
| `list_cms_api_keys` | No | Lists the project's [CMS API keys](/cms#api-keys): `id`, `name`, `prefix`, `createdAt` and `isDefault`. Never a key itself. | — |
| `create_cms_api_key` | Yes | Creates a CMS API key and shows it once, in `key`. | `name` |
| `revoke_cms_api_key` | Yes | Revokes a CMS API key. Apps sending it are refused at once. | `id` |

- `collection` is a slug that `get_cms_schema` lists, such as `legal-pages`. Any other answers `404 unknown_collection`.
- `where` filters as the Payload REST API does, such as `{ "slug": { "equals": "terms-and-conditions" } }`. `sort` is a field name, with `-` first for descending.
- `locale` is a language tag such as `de`; `all` reads every locale.
- `draft` reads or saves the draft, in collections that have drafts.
- `depth` (`0`–`3`, default `0`) fills in related documents; `0` answers their IDs.
- The project's tenant is set on new documents for you.
- Publishing a legal page asks users to accept it again.
- Media files are uploaded in the Console, not through MCP.

### Write answers [#write-answers]

`create_cms_document` and `update_cms_document` answer a summary of the saved document, not the document itself:

```json
{
  "id": 12,
  "slug": "welcome",
  "status": "draft",
  "updatedAt": "2026-10-18T09:00:00.000Z",
  "locale": "de",
  "note": "Saved. The document is not repeated here: read it with get_cms_document, or pass returnDocument: true."
}
```

`locale` is `null` when the call named none, which writes the default locale. A rich-text body can be tens of kilobytes, so the full document can exceed an agent's tool-answer limit even when the write succeeded. Pass `returnDocument: true` to get the whole document, or read it with `get_cms_document`.

### Translations [#translations]

`set_cms_translations` changes a [`translations` group](/cms/use-ovok-cms-with-i18n) key by key. Use it instead of `update_cms_document`, which replaces the group's whole `strings` list: rows left out are deleted, and a row sent without its `id` loses its text in the other locales.

```json
{
  "slug": "sign-in",
  "locale": "de",
  "strings": { "title": "Anmelden", "errors.required": "Pflichtfeld" }
}
```

It answers counts of keys, such as `{ "slug": "sign-in", "locale": "de", "created": 0, "updated": 1, "removed": 0, "unchanged": 1, "status": "published" }`.

- **One locale a call.** `strings` sets each key's text in `locale`. Every other key, and every other locale of the keys you set, is kept. An empty text clears the key in that locale, so apps fall back to English.
- **New keys start in English.** A key the group does not have yet can be created only in a call with `locale` `en`, the default locale. A call in another locale that names a new key writes nothing and answers `422 default_locale_first` with the keys. Write them in `en`, then call again for each other locale.
- **New groups.** If no group has the `slug`, a call with `locale` `en` and a `title` creates it. Otherwise it answers `422 group_not_found`.
- **Removing keys.** `remove` lists keys to delete from the group in every locale. It cannot be undone. A key cannot be in both `strings` and `remove`.
- **Publishing.** `publish: true` publishes the group. `publish: false` makes it a draft, which takes it off every app at once. Without `publish`, a new group is a draft and an existing group keeps its status. Apps read published groups only.
- **Keys** are letters, digits, `_`, `-` and `.`, as in the Console; a dot nests, as in `errors.required`. `__proto__`, `constructor` and `prototype` are refused. The `slug` is letters, digits, `_` and `-`, without dots. A call takes at most 1,000 keys.
- **Concurrent edits.** The write applies only if the group is unchanged since the tool read it. If it changed, nothing is written and the call answers `409 translations_changed` with `retryable: true`. Call the tool again. A save that lands within milliseconds of the write can still be overwritten, so avoid editing the same group in the Console while an agent writes it.
- **Refused groups.** If the CMS refuses the group, for example a row with no key, nothing is written and the call answers `422 cms_refused` with the CMS's `errors`.
- **Duplicate groups.** If the project has two groups with the same `slug`, the call answers `409 duplicate_group`. Delete the extra one with `delete_cms_document`.

### CMS API keys [#cms-api-keys]

`create_cms_api_key` answers the new key in `key`, once. Ovok stores only its hash, so it cannot be shown again: give it to the person who asked, to keep in a secret store or a server environment variable. A project holds at most 20 keys. `revoke_cms_api_key` takes an `id` from `list_cms_api_keys`, including `default`, and takes effect at once: apps that send the key are refused. See [API keys](/cms#api-keys).

## Errors [#errors]

A refused call answers the matching route's status and body, such as `403`, `404` or `409`. Invalid inputs answer `422` with the failing fields. A `5xx` answers without internal details. Calls over a limit answer `429`; see [Limits and lifetime](/working-with-ai-agents/mcp-server#limits-and-lifetime).
