# MCP tool reference

> All 20 Ody MCP tools: what each does, its arguments, required scope, annotations and test-mode behaviour.

Source: https://dev.ody.co/mcp/tools

Every tool requires one [scope](https://dev.ody.co/get-started/scopes). `tools/list` only returns the tools the key can call. Results come back as JSON text in `content[0].text`, and the same data is also in `structuredContent.result`.

**Annotation key:** **R** read-only · **W** writes to the workspace · **$** spends money or reaches the outside world (`openWorldHint`) · **D** destructive · **I** idempotent (safe to repeat).

## At a glance

| Tool | Scope | Hints | Test key |
|---|---|---|---|
| [`workspace_summary`](#workspace_summary) | `conversations:read` | R | Same |
| [`list_contacts`](#list_contacts) | `contacts:read` | R | Same |
| [`create_contact`](#create_contact) | `contacts:write` | W | Same (real write) |
| [`update_contact`](#update_contact) | `contacts:write` | W · I | Same (real write) |
| [`delete_contact`](#delete_contact) | `contacts:write` | W · D · I | Same (real delete) |
| [`list_conversations`](#list_conversations) | `conversations:read` | R | Same |
| [`get_conversation`](#get_conversation) | `conversations:read` | R | Same |
| [`set_conversation_status`](#set_conversation_status) | `conversations:write` | W · I | Same (real write) |
| [`search_messages`](#search_messages) | `conversations:read` | R | Same |
| [`wait_for_message`](#wait_for_message) | `conversations:read` | R | Same |
| [`send_message`](#send_message) | `messages:send` | W · $ | Simulated |
| [`list_numbers`](#list_numbers) | `numbers:read` | R | Same |
| [`search_numbers`](#search_numbers) | `numbers:manage` | R · $ | Same (live inventory) |
| [`buy_number`](#buy_number) | `numbers:manage` | W · $ | Simulated |
| [`get_messaging_status`](#get_messaging_status) | `numbers:manage` | R | Same |
| [`submit_10dlc_registration`](#submit_10dlc_registration) | `numbers:manage` | W · $ · I | Mock filing |
| [`place_call`](#place_call) | `calls:place` | W · $ | Simulated |
| [`get_agent`](#get_agent) | `agent:manage` | R | Same |
| [`configure_agent`](#configure_agent) | `agent:manage` | W · $ · I | Saves; publish simulated |
| [`connect_agent_to_number`](#connect_agent_to_number) | `agent:manage` | W · $ · I | Real routing change |

> **Test keys still write data:**
A test key simulates anything that **sends, spends or dials**. Contact, conversation-status and routing changes are real writes to the workspace. Use a dedicated test workspace if you don't want them in your live data.

## Orientation

### workspace_summary

A snapshot of the workspace: contact count, open and done conversation counts, unread total, active numbers, and message and call volume over the last 7 days. Good as an assistant's first call.

**Arguments:** none.

## Contacts

### list_contacts

Lists or searches contacts.

- `q` *string* — Full-text search over names, company, phones and emails.

### create_contact

Creates a contact. Provide at least one of `firstName`, `lastName`, `company`, `phone`, `email`.

- `firstName`, `lastName`, `company` *string*
- `phone` *string* — E.164.
- `email` *string*

### update_contact

Updates a contact. Only the fields you provide change. `phone` and `email` **replace** the contact's phone and email lists.

- `contactId` *uuid* **required**
- `firstName`, `lastName`, `company`, `phone`, `email` *string*

### delete_contact

Permanently deletes a contact. Its conversations remain. This can't be undone.

- `contactId` *uuid* **required**

## Conversations

### list_conversations

Lists recent conversations, most recent activity first.

- `status` *"open" | "done"*

### get_conversation

The full thread: messages, calls, voicemails, transcripts and AI summaries.

- `conversationId` *uuid* **required**

### set_conversation_status

Marks a conversation open or done.

- `conversationId` *uuid* **required**
- `status` *"open" | "done"* **required**

### search_messages

Case-insensitive search over message bodies, voicemail transcripts and call notes, newest first. Each match includes its `conversationId`.

- `q` *string* **required**
- `limit` *integer*

### wait_for_message

Long-polls for a **new inbound message** in a conversation, for up to 25 seconds. It returns `{ activities, lastActivityId }` as soon as something arrives, or `{ activities: [], timedOut: true, lastActivityId }`. Call it again with `afterActivityId: lastActivityId` to keep waiting without missing anything.

- `conversationId` *uuid* **required**
- `afterActivityId` *uuid* — Only return activities newer than this. Defaults to the newest activity at call time.
- `timeoutSeconds` *integer* — 1–25, default 20.

## Texting

### send_message

Sends an SMS. Provide `body`, plus either `conversationId` or `to` (E.164). With `to`, the conversation is created from the workspace's number. Live texting needs [10DLC](https://dev.ody.co/api/messaging).

- `body` *string* **required**
- `conversationId` *uuid*
- `to` *string* — E.164.

## Numbers and 10DLC

### list_numbers

The workspace's phone numbers, with routing (`voiceMode`).

### search_numbers

Searches carrier inventory, with Ody's monthly price per result: local numbers are $5/mo (the first is included in the plan), toll-free $10/mo (calling only).

- `type` *"local" | "toll_free"* — Default `local`.
- `country` *string* — ISO 3166 alpha-2, default `US`.
- `startsWith` *string* — Area-code prefix (local only).
- `contains` *string*, `locality` *string*, `state` *string*
- `limit` *integer*

### buy_number

Buys a number. **Spends real money**: it's billed to the workspace's plan like an in-app purchase. The same plan, identity and App Store gates as the [REST endpoint](https://dev.ody.co/api/numbers#buy-a-number) apply.

- `phoneNumber` *string* **required** — E.164, from `search_numbers`.
- `type` *"local" | "toll_free"* — A hint; Ody works out the real type from the number.

### get_messaging_status

10DLC status: `step`, `canText` and any failure reasons. See [Messaging (10DLC)](https://dev.ody.co/api/messaging#the-messaging-status-object).

### submit_10dlc_registration

Files the brand and campaign. **Submitting is a purchase.** On a free trial it ends the trial and charges the plan plus the $20 per-number setup fee right away, so the assistant must tell the user and pass `acknowledge_trial_end: true`. Fields match [`POST /messaging/registration`](https://dev.ody.co/api/messaging#submit-a-registration).

- `business` *object* **required**
- `campaign` *object*
- `acknowledge_trial_end` *boolean* — Required on a trial, after the user has been told.

## Calls and agent

### place_call

The published AI agent calls `to` and holds the conversation. Billed in AI credits like an inbound AI call. See [Place an AI call](https://dev.ody.co/api/calls#place-an-ai-call).

- `to` *string* **required** — E.164.
- `from` *string* — One of the workspace's numbers.

### get_agent

The agent's configuration (name, greeting, tone, voice, jobs, hours, publish status) plus this cycle's credit usage.

### configure_agent

Creates or updates the agent. Only the fields you provide change. Pass `publish: true` to push the configuration live in the same call.

- `name`, `greeting`, `tone`, `voice`, `language` *string*
- `timezone` *string* — IANA, for example `America/Chicago`.
- `inHoursAction`, `offHoursAction` *"agent" | "voicemail" | "app" | "forward"*
- `enabled` *boolean*
- `jobs` *array of {name, instructions}*
- `publish` *boolean*

### connect_agent_to_number

Routes a number's inbound calls to the agent. The agent must exist; publish it for live answering.

- `phoneNumber` *string* **required** — One of the workspace's numbers.

## Errors

A tool that fails returns a normal result with `isError: true` and a readable message, so the model can explain or recover:

```json
{
  "content": [{ "type": "text", "text": "Unauthorized: this API key does not have the 'messages:send' scope required by send_message. Ask the workspace admin for a key with that scope." }],
  "isError": true
}
```

Protocol-level problems (unknown tool, missing name) come back as JSON-RPC errors instead. See [Protocol details](https://dev.ody.co/mcp/protocol#errors).
