> ## Documentation Index
> Fetch the complete documentation index at: https://modem.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP Tool Reference

> Parameters, results, and examples for every tool on the Modem MCP Server

This page is the complete reference for the tools the Modem MCP Server exposes at `https://mcp.modem.dev/mcp`. For client setup and an overview of when to use each tool, see the [Modem MCP Server page](/docs/api/modem-mcp-server).

The server exposes up to 14 tools, grouped by what they do:

| Group      | Tools                                                                                                                                                                     | Scope required |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
| Search     | `search_modem`                                                                                                                                                            | `data:read`    |
| Agent runs | `modem_agent_invoke`, `modem_agent_get_run`, `modem_agent_send_message`, `modem_agent_cancel_run`                                                                         | `agent:invoke` |
| Write      | `update_topic`, `bulk_update_topics`, `merge_topics`, `create_companies`, `update_companies`, `merge_companies`, `add_people_to_company`, `update_people`, `merge_people` | `agent:invoke` |

Your client only sees the tools covered by the scopes you approved during authorization. A token with `data:read` alone sees `search_modem` and nothing else.

## How authorization works

The Modem MCP Server uses standard MCP OAuth. Your client discovers and completes the flow on its own; there is no API key.

<Steps>
  <Step title="Discovery">
    An unauthenticated request to `https://mcp.modem.dev/mcp` returns `401` with a `WWW-Authenticate` challenge pointing at the server's
    protected-resource metadata (`/.well-known/oauth-protected-resource/mcp`, also served at the root well-known location). The metadata
    names Modem's authorization server.
  </Step>

  <Step title="Client registration">
    Clients that support OAuth dynamic client registration register themselves automatically. No manual client ID setup is needed.
  </Step>

  <Step title="Browser consent">
    The client opens a browser window using the OAuth 2.0 authorization code flow with PKCE. On the Modem consent screen you sign in,
    pick the Modem organization if your account belongs to more than one, and approve the requested permissions (`agent:invoke` and/or
    `data:read`).
  </Step>

  <Step title="Token issue">
    The client receives an access token bound to your user, the selected organization, and the approved scopes. Clients that request
    `offline_access` also receive a refresh token, so you do not re-authorize every session.
  </Step>
</Steps>

The organization is resolved from the token claims on every call. Tool arguments never carry an organization ID, and a client cannot target a different organization than the one approved at consent.

## search\_modem

Answer a natural-language question about your Modem data and get matching rows back. Read-only. Does not run the full Modem Agent and does not consume agent credits.

Requires the `data:read` scope.

### Parameters

| Parameter  | Type   | Required                    | Description                                                                                                                                           |
| ---------- | ------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `question` | string | Yes, unless `cursor` is set | Natural-language data question, 1 to 2,000 characters.                                                                                                |
| `limit`    | number | No                          | Page size, 1 to 100. When set, the result is paged server-side and a `cursor` comes back when more rows exist.                                        |
| `cursor`   | string | No                          | Opaque continuation token from a previous response. Fetches the next page of the same result set without re-running the search. No `question` needed. |
| `columns`  | array  | No                          | Requested row shape, 1 to 24 column specs. The returned rows are validated against it and the verdict is reported under `schema` on the result.       |

Each entry in `columns` is an object:

| Field         | Type    | Required | Description                                                                                       |
| ------------- | ------- | -------- | ------------------------------------------------------------------------------------------------- |
| `name`        | string  | Yes      | Column name the returned rows should carry, 1 to 64 characters.                                   |
| `type`        | string  | Yes      | One of `string`, `number`, `boolean`, `timestamp`, `json`. `number` also accepts numeric strings. |
| `description` | string  | No       | What the column should contain, up to 200 characters.                                             |
| `required`    | boolean | No       | Whether every row must carry this column. Defaults to `true`.                                     |

### Example

```json theme={null}
{
    "question": "Which topics have the most feedback from VIP companies in the last 30 days?",
    "limit": 25,
    "columns": [
        { "name": "topic_id", "type": "string" },
        { "name": "title", "type": "string" },
        { "name": "feedback_count", "type": "number" }
    ]
}
```

### Result

The result carries a short natural-language `answer` plus the structured fields below:

| Field       | Type    | Description                                                                                                                          |
| ----------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `rows`      | array   | Rows from the search's final query.                                                                                                  |
| `rowCount`  | number  | Number of rows in `rows`.                                                                                                            |
| `answer`    | string  | Short natural-language summary of the result.                                                                                        |
| `complete`  | boolean | True when `rows` is the complete result set (no truncation, no further pages).                                                       |
| `cursor`    | string  | Present when more rows are available. Pass it back as the `cursor` parameter to fetch the next page.                                 |
| `schema`    | object  | When `columns` was supplied: `{ valid, errors }` reporting whether the rows satisfied the requested shape.                           |
| `truncated` | boolean | True when the rows were cut to stay within the result-size cap.                                                                      |
| `partial`   | boolean | True when the search could not finish exploring; rows and answer may be intermediate.                                                |
| `warnings`  | array   | Machine-readable degradation codes: `step_budget_exhausted`, `rows_truncated`, `page_execution_failed`, `answer_narration_stripped`. |
| `traceId`   | string  | Trace ID for this call. Quote it when contacting Modem support.                                                                      |

Topic `priority` values in returned rows are named levels encoded as integers: `very_low` (-100), `low` (-50), `default` (0), `high` (50), `very_high` (100).

## Agent run tools

The four `modem_agent_*` tools run the full Modem Agent asynchronously. Starting a run returns immediately with IDs; the run continues in Modem even if your client disconnects. Agent runs consume credits. All four require the `agent:invoke` scope.

### modem\_agent\_invoke

Start a new durable agent conversation.

| Parameter         | Type   | Required | Description                                                          |
| ----------------- | ------ | -------- | -------------------------------------------------------------------- |
| `prompt`          | string | Yes      | Natural-language prompt for the Modem Agent, 1 to 50,000 characters. |
| `idempotency_key` | string | Yes      | Stable retry key. Reuse only for the exact same prompt.              |

```json theme={null}
{
    "prompt": "Which customers requested SSO this quarter, and do any related Linear issues already exist?",
    "idempotency_key": "sso-requests-2026-09-02"
}
```

Returns `conversation_id`, `run_id`, and `status: "working"`.

### modem\_agent\_get\_run

Get the current status and, once complete, the result of a run.

| Parameter         | Type          | Required | Description                                                            |
| ----------------- | ------------- | -------- | ---------------------------------------------------------------------- |
| `conversation_id` | string (UUID) | Yes      | Conversation ID returned by `modem_agent_invoke`.                      |
| `run_id`          | string        | Yes      | Run ID returned by `modem_agent_invoke` or `modem_agent_send_message`. |

Returns:

| Field         | Type   | Description                                                 |
| ------------- | ------ | ----------------------------------------------------------- |
| `status`      | string | One of `working`, `completed`, `failed`, `cancelled`.       |
| `result`      | object | Present when completed: `{ text }` with the agent's answer. |
| `error`       | object | Present when failed: `{ code, message, retryable }`.        |
| `accepted_at` | string | When the run was accepted.                                  |
| `updated_at`  | string | When the run state last changed.                            |

Poll this tool until the status is terminal. The tool is read-only and safe to call repeatedly.

### modem\_agent\_send\_message

Start a follow-up turn in an existing MCP conversation. Wait for the current run to finish first; only one turn can run in a conversation at a time.

| Parameter         | Type          | Required | Description                                                  |
| ----------------- | ------------- | -------- | ------------------------------------------------------------ |
| `conversation_id` | string (UUID) | Yes      | Existing MCP conversation ID.                                |
| `prompt`          | string        | Yes      | Follow-up prompt, 1 to 50,000 characters.                    |
| `idempotency_key` | string        | Yes      | Stable retry key for this follow-up. Use a new key per turn. |

Returns `conversation_id`, `run_id`, and `status: "working"` for the new turn.

### modem\_agent\_cancel\_run

Cooperatively cancel one exact run. Actions the agent already completed are not reversed.

| Parameter         | Type          | Required | Description       |
| ----------------- | ------------- | -------- | ----------------- |
| `conversation_id` | string (UUID) | Yes      | Conversation ID.  |
| `run_id`          | string        | Yes      | Run ID to cancel. |

Returns `status: "cancelled"`, or `status: "already_terminal"` if the run had already finished.

## Write tools

Write tools apply changes directly, without going through the agent. They run as **you**: each call is authorized against your own role in the organization you approved during consent, exactly like the Modem dashboard. All write tools require the `agent:invoke` scope and are annotated as destructive, so most clients ask you to confirm each call.

ID formats: topic IDs are UUIDs; company and person IDs are numeric strings. Use `search_modem` to find IDs before calling a write tool.

### update\_topic

Update a single topic. At least one optional field must be provided.

| Parameter        | Type    | Required | Description                                                                                                   |
| ---------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------- |
| `topicId`        | string  | Yes      | Topic ID (UUID).                                                                                              |
| `priority`       | string  | No       | One of `very_low`, `low`, `default`, `high`, `very_high`.                                                     |
| `lifecycleState` | string  | No       | One of `open`, `in_progress`, `completed`, `dismissed`. The `unknown` state is AI-assessed and cannot be set. |
| `issueType`      | string  | No       | One of `bug_report`, `feature_request`, `complaint`, `praise`, `discussion`, or `null` to clear.              |
| `keywords`       | array   | No       | Replaces the full keyword list. Up to 20 keywords, each 1 to 50 characters.                                   |
| `archived`       | boolean | No       | `true` to archive, `false` to unarchive.                                                                      |
| `archivedReason` | string  | No       | Optional reason, up to 4,000 characters. Only used when `archived` is `true`.                                 |

```json theme={null}
{
    "topicId": "3f9d2a10-8c4e-4b7a-9f21-6e5d0c1b2a34",
    "priority": "high",
    "lifecycleState": "in_progress"
}
```

### bulk\_update\_topics

Update up to 100 topics at once. At least one optional field must be provided; it applies to every topic in the list.

| Parameter        | Type    | Required | Description                                                |
| ---------------- | ------- | -------- | ---------------------------------------------------------- |
| `topicIds`       | array   | Yes      | 1 to 100 topic IDs.                                        |
| `priority`       | string  | No       | Set all topics to this priority.                           |
| `archived`       | boolean | No       | `true` to archive, `false` to unarchive all listed topics. |
| `archivedReason` | string  | No       | Optional reason, up to 4,000 characters.                   |

Returns `updatedCount` and `updatedTopicIds`.

### merge\_topics

Merge up to 50 source topics into a target topic. Source topics are archived and marked as merged; their group and event associations move to the target. Rejects if any source topic is already archived.

| Parameter        | Type   | Required | Description                                              |
| ---------------- | ------ | -------- | -------------------------------------------------------- |
| `sourceTopicIds` | array  | Yes      | 1 to 50 topic IDs to merge. Must not include the target. |
| `targetTopicId`  | string | Yes      | The topic that is kept and receives the merged content.  |

Returns `mergedCount` and `targetTopicId`.

### create\_companies

Create up to 50 companies. Batch-first with partial success: each company is created independently and the result reports success or an error per item.

| Parameter   | Type  | Required | Description                                                                  |
| ----------- | ----- | -------- | ---------------------------------------------------------------------------- |
| `companies` | array | Yes      | 1 to 50 items, each `{ name, domain }`. Both required, up to 255 characters. |

```json theme={null}
{
    "companies": [
        { "name": "Acme", "domain": "acme.com" },
        { "name": "Globex", "domain": "globex.io" }
    ]
}
```

Returns a `results` array with `{ name, domain, success, data?, error? }` per item.

### update\_companies

Update up to 50 existing companies. Partial success per item.

| Parameter   | Type  | Required | Description           |
| ----------- | ----- | -------- | --------------------- |
| `companies` | array | Yes      | 1 to 50 update items. |

Each item:

| Field           | Type    | Required | Description                                                                                                        |
| --------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `companyId`     | string  | Yes      | Company ID (numeric string).                                                                                       |
| `name`          | string  | No       | New company name.                                                                                                  |
| `logoUrl`       | string  | No       | Logo URL, or `null` to clear.                                                                                      |
| `isVip`         | boolean | No       | Mark or unmark the company as VIP.                                                                                 |
| `primaryDomain` | string  | No       | Primary domain, or `null` to clear.                                                                                |
| `connections`   | object  | No       | External links: `email`, `phone`, `linkedin`, `github`, `twitter`. Only provided fields change; `null` clears one. |

### merge\_companies

Merge one or more source companies into a target company. All domains, person associations, and Slack workspaces move to the target.

| Parameter          | Type   | Required | Description                                       |
| ------------------ | ------ | -------- | ------------------------------------------------- |
| `targetCompanyId`  | string | Yes      | The company that is kept.                         |
| `sourceCompanyIds` | array  | Yes      | One or more company IDs to merge into the target. |

Returns `mergedCount`. Sources are merged one at a time; if a later source fails after earlier ones succeeded, the call still returns success with a `mergedCount` lower than the number of `sourceCompanyIds` requested. Check `mergedCount` against the input length to detect partial completion.

### add\_people\_to\_company

Associate one or more people with a company.

| Parameter   | Type   | Required | Description                    |
| ----------- | ------ | -------- | ------------------------------ |
| `companyId` | string | Yes      | Company ID.                    |
| `personIds` | array  | Yes      | One or more person IDs to add. |

Returns `addedCount` and `skippedCount` (people already associated are skipped).

### update\_people

Update up to 50 existing people. Partial success per item.

| Parameter | Type  | Required | Description           |
| --------- | ----- | -------- | --------------------- |
| `people`  | array | Yes      | 1 to 50 update items. |

Each item:

| Field          | Type    | Required | Description                                                                                                                   |
| -------------- | ------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `personId`     | string  | Yes      | Person ID (numeric string).                                                                                                   |
| `displayName`  | string  | No       | New display name.                                                                                                             |
| `avatarUrl`    | string  | No       | Avatar URL, or `null` to clear.                                                                                               |
| `description`  | string  | No       | Free-text description, or `null` to clear.                                                                                    |
| `isVip`        | boolean | No       | Mark or unmark the person as VIP.                                                                                             |
| `primaryEmail` | string  | No       | Primary email address, or `null` to clear.                                                                                    |
| `connections`  | object  | No       | External links: `linkedin`, `github`, `twitter`, `website`, `phone`, `title`. Only provided fields change; `null` clears one. |

### merge\_people

Merge one or more source people into a target person. All identities, emails, and company associations move to the target.

| Parameter         | Type   | Required | Description                                      |
| ----------------- | ------ | -------- | ------------------------------------------------ |
| `targetPersonId`  | string | Yes      | The person that is kept.                         |
| `sourcePersonIds` | array  | Yes      | One or more person IDs to merge into the target. |

Returns `mergedCount`. Sources are merged one at a time; if a later source fails after earlier ones succeeded, the call still returns success with a `mergedCount` lower than the number of `sourcePersonIds` requested. Check `mergedCount` against the input length to detect partial completion.

## Common workflows

### Triage topics

1. Call `search_modem` to find the topics to act on, for example "topics about the old billing page".
2. Take the topic IDs from the returned rows.
3. Call `bulk_update_topics` to archive or re-prioritize them, or `update_topic` for a single change.

### Deduplicate companies or people

1. Call `search_modem` for the duplicates, for example "companies whose name contains Acme".
2. Pick the record to keep as the target.
3. Call `merge_companies` or `merge_people` with the target and the source IDs. Merges are not reversible, so confirm the target first.

### Cross-tool work through the agent

The MCP write tools only change Modem records. For work that reaches your connected tools, such as creating a Linear issue from a topic or posting to Slack, use an agent run:

1. Call `search_modem` to gather the context, for example the highest-priority bug reports this week.
2. Call `modem_agent_invoke` with a prompt that includes that context and the action, for example "Create a Linear issue summarizing this feedback".
3. Poll `modem_agent_get_run` until the run completes.

## Errors

Tool calls fail with standard MCP tool errors carrying one of these codes:

| Code                    | Meaning                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------- |
| `UNAUTHORIZED`          | The token is invalid, or the token's user no longer has access.                                       |
| `FORBIDDEN`             | The token is missing the required scope, or your role in the organization does not permit the change. |
| `NOT_FOUND`             | The target record, conversation, or run does not exist in the authorized organization.                |
| `BAD_REQUEST`           | The arguments failed validation.                                                                      |
| `CONFLICT`              | An `idempotency_key` was reused with a different prompt, or the conversation is busy.                 |
| `TOO_MANY_REQUESTS`     | Rate limited. The error carries a `retryAfter` hint in seconds; back off before retrying.             |
| `INTERNAL_SERVER_ERROR` | Something went wrong on Modem's side. Retry, and quote the `traceId` if you contact support.          |

## Limits

| Limit                          | Value                                                     |
| ------------------------------ | --------------------------------------------------------- |
| Tool calls                     | 20 per minute, per organization, per tool                 |
| Agent runs                     | Additionally subject to your organization's agent credits |
| `prompt` length                | 50,000 characters                                         |
| `question` length              | 2,000 characters                                          |
| `search_modem` page size       | 100 rows                                                  |
| `search_modem` columns         | 24 requested columns                                      |
| Topics per bulk update         | 100                                                       |
| Source topics per merge        | 50                                                        |
| Companies per create or update | 50                                                        |
| People per update              | 50                                                        |

## Related

<CardGroup cols={2}>
  <Card title="Modem MCP Server" icon="https://mintcdn.com/modem-844d7a4a/Wr2r4IRr97lNQiQb/icons/link.svg?fit=max&auto=format&n=Wr2r4IRr97lNQiQb&q=85&s=e774d33588635d44c6c935b78ed55f13" href="/docs/api/modem-mcp-server" width="24" height="24" data-path="icons/link.svg">
    Client setup, authorization, and troubleshooting.
  </Card>

  <Card title="The Modem Agent" icon="https://mintcdn.com/modem-844d7a4a/Wr2r4IRr97lNQiQb/icons/bot-bolt.svg?fit=max&auto=format&n=Wr2r4IRr97lNQiQb&q=85&s=dda761a2f7042ebc27c668cc644df76c" href="/docs/features/agent" width="22" height="22" data-path="icons/bot-bolt.svg">
    What the Modem Agent can query and do.
  </Card>
</CardGroup>
