> ## Documentation Index
> Fetch the complete documentation index at: https://modem.ai/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 16 tools, grouped by what they do:

| Group | Tools | Scope required |
| - | - | - |
| Skills | `modem_skills` | `data:read` or `agent:invoke` |
| Search | `search_modem` | `data:read` |
| Docs | `modem_docs` | `data:read` or `agent:invoke` |
| 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 `modem_skills`, `modem_docs`, and `search_modem`; a token with `agent:invoke` alone sees `modem_skills`, `modem_docs`, the agent-run tools, and the write tools.

## 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.

## modem\_skills

Skills that teach an MCP client how to use this server's tools. Call it when you are unsure which tool to use. Read-only.

Requires either the `data:read` or the `agent:invoke` scope, so every token can read the skills for the tools it holds.

This is not the Skills page in Modem (`Settings → Skills`), and it is not a general skill reader. It cannot load SKILL.md files, harness skills, or organization-authored skills.

### Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `name` | string | No | A skill from this server's catalog, for example `search-modem`. Omit (or pass an empty string) to list the catalog. |

### Result

The result is markdown text.

* With no `name`, the result is the catalog, a list of skill names and descriptions.
* With a known `name`, the result is that skill's body.
* With an unknown `name`, the result is `No skill named …` followed by the catalog. The call still succeeds.

The catalog has three skills:

| Name | Covers |
| - | - |
| `search-modem` | Asking `search_modem` a question, paging results, and telling a complete result from a partial one. |
| `agent-runs` | Starting a run with `modem_agent_invoke`, polling `modem_agent_get_run`, follow-ups, and cancelling. |
| `write-tools` | Finding IDs first, confirming merges, and reading partial results from the write tools. |

### Example

List the catalog:

```json theme={null}
{}
```

Load the `agent-runs` skill:

```json theme={null}
{ "name": "agent-runs" }
```

## 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. |
| `context` | string | No | What the user is trying to accomplish, up to 1,000 characters. It does not change the results, so put every filter in `question`. |
| `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). See the note below the table. |
| `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. |

`complete`, `cursor`, `partial`, and `warnings` are only returned when the call sends `limit`, `columns`, or `cursor`. To page through results, send `limit`.

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

## modem\_docs

Search or read the Modem docs to answer how Modem works: setup, integrations, this MCP server and its scopes, and the API. Read-only. Does not consume agent credits. For questions about your organization's data, use `search_modem`.

Requires either the `data:read` or the `agent:invoke` scope.

### Parameters

Send exactly one of the two.

| Parameter | Type | Required | Description |
| - | - | - | - |
| `query` | string | Yes, unless `page` is set | What to look up, 1 to 500 characters. Returns the matching doc sections. |
| `page` | string | Yes, unless `query` is set | A page path as a search result prints it, for example `api/modem-mcp-server`. Returns the whole page. |

### Result

The result is markdown text, plus these structured fields:

| Field | Type | Description |
| - | - | - |
| `mode` | string | `search` or `page`. |
| `results` | array | For a search: the matching sections, each with `title`, `page`, `url`, and `content` (markdown). |
| `markdown` | string | For a page: the page's full markdown. |
| `truncated` | boolean | True when the result was cut to fit the size cap. |

### Example

```json theme={null}
{ "query": "Which scope does search_modem need?" }
```

## 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. The update and merge tools are annotated as destructive, so most clients ask you to confirm each call. `create_companies` and `add_people_to_company` only add data and are not.

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

An invalid or expired token is rejected with HTTP 401 before any tool runs; re-authorize your client. Otherwise, a failed tool call returns a standard MCP tool error (`isError: true`) with a human-readable message and no machine-readable code.

| Situation | Meaning |
| - | - |
| Unauthorized | The token's user no longer has access. |
| Forbidden | The token lacks the required scope, your role doesn't permit the change, or the organization has reached its monthly agent usage limit. |
| Not found | The target record, conversation, or run does not exist in the authorized organization, or no docs page has that path. |
| Invalid arguments | The arguments failed validation. |
| Conflict | An `idempotency_key` was reused with a different prompt, or the conversation is busy. |
| Rate limited | Too many calls to one tool, or, for the agent tools, the organization's daily agent cost limit. Wait and retry with backoff. |
| Internal error | Something went wrong on Modem's side. Retry, and contact support if it persists. |

## 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="Skills for MCP Clients" icon="https://mintcdn.com/modem-844d7a4a/Wr2r4IRr97lNQiQb/icons/code.svg?fit=max&auto=format&n=Wr2r4IRr97lNQiQb&q=85&s=6c9b5cbb0e89aebc55e9fd769945fc07" href="/docs/api/mcp-skills" width="24" height="24" data-path="icons/code.svg">
    The skills the server gives your MCP client, and how the client finds them.
  </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>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.