Carbon Voice
The Carbon Voice MCP server lets AI assistants interact with the Carbon Voice voice-messaging platform—managing messages, conversations, folders, users, action items, AI actions, notifications, and share links.
Messages: list, retrieve, create voice memos (text-to-speech or audio URL), send conversation/direct messages, attach links, move messages to folders, summarize conversations.
Users & workspaces: get current user, search users by email/phone/name, get user profiles, list workspaces.
Conversations: list, get details, get participants, filter by type and users.
Folders: list root folders, create/rename/delete/move folders, fetch folder contents, organize messages into folders.
AI Actions: list available prompts, run AI actions on messages or shared links, retrieve past AI responses, summarize conversations via AI.
Action items: list, create, update, delete, set status, and AI-suggest action items from messages.
Search & notifications: find message IDs by notified/mentioned/label/creator filters, search by heard status, list inbox notifications with unread counts.
Share links: create and look up shareable message links, run AI actions on shared content.
Response narrowing: most read tools accept
response_fieldsto shrink payloads and save context tokens.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Carbon Voiceshow my recent voice memos"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Carbon Voice MCP Server
A Model Context Protocol (MCP) server implementation for integrating with Carbon Voice's API, providing AI assistants with comprehensive tools for voice messaging, conversations, and workspace management.
Carbon Voice: https://getcarbon.app
API: https://api.carbonvoice.app/docs
Features
Message Management: Create, list, and retrieve voice messages, conversation messages, and direct messages
User Operations: Search and retrieve user information
Conversation Management: Access and manage conversations and their participants
Folder Operations: Create, organize, move, and manage folders and their contents
Workspace Administration: Get workspace information
AI Actions: Run AI prompts and retrieve AI-generated responses
Action Items: Create, assign, and track action items, including AI extraction from messages
Search & Notifications: Find messages by notified or heard state, and read the inbox with its unread count
Message Share Links: Create and look up shareable links to messages
Attachment Support: Add link attachments to messages
Response Narrowing: An optional
response_fieldsprojection on most read tools, to keep unwanted payload out of the agent's context
Related MCP server: Bolna MCP Server
Security & Compliance
This server fully complies with MCP Security Best Practices:
OAuth 2.1 Authentication: Secure authorization flow with proper token handling
HTTPS Enforcement: All remote endpoints served over HTTPS
Session Security: Cryptographically secure session management
Input Validation: Comprehensive validation of all user inputs
Rate Limiting: Built-in protection against abuse
For security concerns, please contact: devsupport@phononx.com
Prerequisites
For Stdio Transport (Local Installation)
Required:
Carbon Voice API Key - Contact the Carbon Voice development team to request your API key:
📧 Contact: devsupport@phononx.com
📧 Subject: "Request API key for MCP Server"
npx Installation - You must have
npxinstalled on your system. npx comes bundled with Node.js (version 14.8.0 or later). If you don't have Node.js installed, you can download it from nodejs.org.To verify your installation, run:
npx --version
For HTTP Transport (Remote)
Required:
Nothing! - No additional prerequisites are required. The HTTP transport version runs entirely in the cloud and uses OAuth2 authentication, so you don't need an API key or npx installed.
Configuration
Quick Overview
Client | HTTP Transport (Remote) | Stdio Transport (Local) |
Cursor | ✅ Recommended | ✅ Available |
Claude Desktop | ✅ Recommended | ✅ Available |
HTTP Transport is recommended for easier setup and enhanced security.
For Cursor
HTTP Transport (Remote)
Open Cursor
Go to Cursor Settings > Features > Model Context Protocol
Add a new MCP server configuration:
{
"mcpServers": {
"Carbon Voice": {
"url": "https://mcp.carbonvoice.app"
}
}
}Save and restart Cursor
The first time you use it, Cursor will guide you through the OAuth2 authentication process.
Stdio Transport (Local Installation)
If you prefer to run the MCP server locally with API key authentication:
Open Cursor
Go to Cursor Settings > Features > Model Context Protocol
Add a new MCP server configuration:
{
"mcpServers": {
"Carbon Voice": {
"command": "npx",
"env": {
"CARBON_VOICE_API_KEY": "your_api_key_here"
},
"args": ["-y", "@carbonvoice/cv-mcp-server"]
}
}
}Replace
"your_api_key_here"with your actual Carbon Voice API keySave and restart Cursor
For Claude Desktop
HTTP Transport (Remote)
Setting up Carbon Voice in Claude Desktop is straightforward! Here's how to do it:
Open Claude Desktop and navigate to Search and Tools
Go to Manage Connectors and click "Add custom connector"
Fill in the connector details:
Name: Give it a friendly name like "Carbon Voice"
Remote MCP Server URL: Enter
https://mcp.carbonvoice.app
Save your connector
Click Connect:
The first time you use it, Claude will guide you through the OAuth2 authentication process. You'll just need to sign in with your Carbon Voice account and grant permissions. After that, you're all set!
Stdio Transport (Local Installation)
If you prefer to run the MCP server locally with API key authentication:
Open your Claude Desktop configuration file:
macOS:
~/Library/Application Support/Claude/claude_desktop_config.jsonWindows:
%APPDATA%\Claude\claude_desktop_config.json
Add the Carbon Voice MCP server configuration:
{
"mcpServers": {
"Carbon-Voice": {
"command": "npx",
"env": {
"CARBON_VOICE_API_KEY": "your_api_key_here"
},
"args": ["-y", "@carbonvoice/cv-mcp-server"]
}
}
}Replace
"your_api_key_here"with your actual Carbon Voice API keySave the file and restart Claude Desktop
Audio fetch controls (all transports)
These govern create_voicememo_message's audio_url — the one place the server
fetches a URL a caller supplied. They apply to every transport, stdio and
HTTP alike; they are not part of the stdio-only set below.
AUDIO_FETCH_ALLOWED_HOSTS
Comma-separated hostname allowlist. When set, only these hosts (and their
subdomains) may be fetched. Set this in production — it is the strongest
control against the server being used as an SSRF proxy, and the only one that
also narrows the DNS-rebinding window described in src/utils/fetch-audio-file.ts.
When unset, any public host is allowed over https, while private, loopback,
link-local and site-local address space is still refused.
Entries may be hostnames or IP literals; an IPv6 literal works written bare or bracketed.
AUDIO_FETCH_ALLOWED_HOSTS=cdn.example.com,uploads.example.comNaming a host here is also what permits plain http for it. With no allowlist,
only https URLs are accepted.
AUDIO_FETCH_MAX_BYTES
Maximum size of a fetched audio file, in bytes. Defaults to 26214400 (25 MB).
Enforced against both content-length and the bytes actually received, while
streaming — an oversized body is cancelled rather than buffered.
AUDIO_FETCH_TIMEOUT_MS
Timeout for the whole audio_url fetch, in milliseconds. Defaults to 30000.
Bounds DNS resolution as well as the request itself.
AUDIO_FETCH_MAX_CONCURRENT
How many audio_url fetches may be in flight across the whole process.
Defaults to 4; callers beyond it are refused immediately rather than queued.
AUDIO_FETCH_MAX_BYTES caps a single fetch, this caps their sum. The tool-call
queue serializes per session, so without a process-wide budget one caller
using several sessions could hold gigabytes of transient memory — each in-flight
fetch keeps its chunks, the concatenated buffer, and the resulting file alive
until the upstream upload finishes.
Environment Variables (Only available for Stdio Version)
When using the stdio version of the MCP server, you can configure additional environment variables:
LOG_LEVEL
Controls the verbosity of logging output. Available options:
info(default) - Standard logging informationdebug- Most verbose logging, shows detailed request/response datawarn- Only warning and error messageserror- Only error messages
Example:
{
"mcpServers": {
"Carbon-Voice": {
"command": "npx",
"env": {
"CARBON_VOICE_API_KEY": "your_api_key_here",
"LOG_LEVEL": "debug"
},
"args": ["-y", "@carbonvoice/cv-mcp-server"]
}
}
}LOG_DIR
Specifies the directory where log files will be stored. Defaults to: /tmp/cv-mcp-server/logs
The server will create two log files in this directory:
combined.log- Contains all log messageserror.log- Contains only error messages
Example:
{
"mcpServers": {
"Carbon-Voice": {
"command": "npx",
"env": {
"CARBON_VOICE_API_KEY": "your_api_key_here",
"LOG_DIR": "/Users/USER_NAME/Documents/cv-mcp-server/logs"
},
"args": ["-y", "@carbonvoice/cv-mcp-server"]
}
}
}Complete Example with Both Variables:
{
"mcpServers": {
"Carbon-Voice": {
"command": "npx",
"env": {
"CARBON_VOICE_API_KEY": "your_api_key_here",
"LOG_LEVEL": "debug",
"LOG_DIR": "/Users/USER_NAME/Documents/cv-mcp-server/logs"
},
"args": ["-y", "@carbonvoice/cv-mcp-server"]
}
}
}Available Tools
Messages
list_messages- List messages, filtered by date (max 183-day span), conversation, folder, workspace, creator, or languageget_message- Retrieve a specific message by IDget_recent_messages- Get the 10 most recent messages with full contextcreate_conversation_message- Send a message to a conversationcreate_direct_message- Send direct messages to users or groupscreate_voicememo_message- Create a voice memo from text (spoken via TTS) or from audio at a URLadd_attachments_to_message- Add link attachments to existing messagessummarize_conversation- Summarise a conversation with an AI Action (needs aprompt_idfromlist_ai_actions)
Voice memo audio. Pass
audio_url(a public https URL) to upload existing audio; the server fetches it and forwards the bytes. The upstreamaudio_filemultipart param is not exposed over MCP, because a JSON-RPC client cannot construct aFile. Fetches are constrained: https only (plain http needs the host inAUDIO_FETCH_ALLOWED_HOSTS), private/loopback/ link-local/site-local addresses refused, URLs embedding credentials refused, redirects re-validated per hop, plus a size cap and timeout — seeAUDIO_FETCH_*under Audio fetch controls.
Users
get_current_user- Who you are acting as, plus your workspace IDsget_user- Retrieve user information by IDsearch_user- Find a user by phone number or emailsearch_users- Search multiple users by various identifiers
Conversations
list_conversations- Get all conversations from the last 6 months, with optional filtering byuser_ids/match(applied by the API), plustypesandname(case-insensitive substring), which this server applies to the response. Aname-filtered response also carriesunfiltered_count— how many conversations the name was matched against, after the other filters — so an empty result can be told apart from a name that simply did not matchget_conversation- Retrieve conversation details by IDget_conversation_users- Get all users in a conversation
Folders
get_root_folders- List root folders for a workspacecreate_folder- Create new foldersget_folder- Retrieve folder informationget_folder_with_messages- Get folder with its messagesupdate_folder_name- Rename foldersdelete_folder- Delete folders (⚠️ destructive operation)move_folder- Move folders between locationsmove_message_to_folder- Organize messages into folders
Workspace
get_workspaces_basic_info- Get basic workspace information
AI Actions
list_ai_actions- List available AI prompts/actionsrun_ai_action- Execute AI actions on messagesrun_ai_action_for_shared_link- Run AI actions on shared contentget_ai_action_responses- Retrieve AI-generated responses
Search & Notifications
search_message_ids- Find message IDs by notified state, mentions, labels, creator, conversation, or workspace (cursor-paginated, IDs only)search_messages_by_heard_status- Find unheard/heard messages, with per-conversation unheard countslist_inbox_notifications- List inbox notifications (including thementionscategory) with a total unread count
These three call the full Carbon Voice API rather than the simplified surface, since notified state, listened state, and notification records have no simplified-API equivalent.
Action Items
list_my_action_items- Your action items across every conversation and folder: those assigned to you, plus unassigned ones you created. Checkassigned_tobefore treating an item as someone's personal commitmentlist_action_items- List action items in one conversation, folder, or homeget_action_item- Get a single action item by IDcreate_action_item- Create an action itemupdate_action_item- Update title, notes, assignee, or due dateset_action_item_status- Move an item betweensuggested,todo, anddonedelete_action_item- Permanently delete an action itemsuggest_action_items_from_message- Extract action items from ONE message and return them immediately (no polling)suggest_action_items_from_messages- Extract candidate action items from SEVERAL messages using AI. Enqueued and answered202, so poll a listing tool withstatus: "suggested"for the results. Reasons over the whole set at once, so it can catch commitments that span messages
Message Share Links
create_message_share_link- Create a shareable link to a message (returns the URL)get_message_share_link- Look up an existing share link, including its access settings
Narrowing Responses (response_fields)
Most read tools accept an optional response_fields array — a dot-path allowlist
that shrinks the response before it reaches the agent's context. Paths traverse
arrays element-wise, and pagination fields (total, has_next_page, has_more,
next_cursor, …) are always kept so the "is there more?" signal survives. So is
list_conversations's unfiltered_count, for the same reason: a projection that
stripped it would leave an empty result looking like a conversation that does not
exist.
{ "response_fields": ["total", "has_next_page", "results.id", "results.transcript"] }Omitting it returns the full payload unchanged, so existing integrations are
unaffected. Measured on recorded fixtures (npm run measure:payloads):
Tool | Full | Narrowed |
| 8,447 bytes | 2,050 bytes (75.7% smaller) |
| 21,947 bytes | 4,629 bytes (78.9% smaller) |
Each tool's description suggests a sensible starting set for the common case.
Usage Examples
Getting Started
After configuration, you can interact with Carbon Voice through your AI assistant. Here are some example requests:
"Show me my recent messages"
"Create a voice memo about today's meeting"
"Search for user john@example.com"
"Show me my workspace information"
"List my conversations from this week"Working with Folders
"Create a folder called 'Project Updates'"
"Move message ID 12345 to the Project Updates folder"
"Show me all messages in the Marketing folder"AI Actions
"Run a summary AI action on message ID 67890"
"List all available AI prompts"
"Get AI responses for conversation ID 123"Error Handling
The server includes comprehensive error handling and logging. Errors are returned in a structured format that includes:
Error messages
HTTP status codes
Request context
Debugging information
Local Testing
cp .env.sample .env # credentials only needed for real tool calls
npm run buildPointing a real client at your local build
To use your branch in Claude Desktop / Cursor / Claude Code instead of the published package, build it and point the client at the built entrypoint by absolute path.
npm run build # produces dist/transports/stdio/stdio.js
pwd # note the absolute pathClaude Code (CLI) — easiest, and scoped to one project:
claude mcp add carbon-voice-dev \
--env CARBON_VOICE_PAT=cv_pat_your_token_here \
-- node /absolute/path/to/cv-mcp-server/dist/transports/stdio/stdio.jsClaude Desktop — ~/Library/Application Support/Claude/claude_desktop_config.json
(macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows).
Cursor — Settings → Features → Model Context Protocol. Same JSON:
{
"mcpServers": {
"carbon-voice-dev": {
"command": "node",
"args": ["/absolute/path/to/cv-mcp-server/dist/transports/stdio/stdio.js"],
"env": {
"CARBON_VOICE_PAT": "cv_pat_your_token_here"
}
}
}
}Restart the client after editing. Name it carbon-voice-dev so it can sit
alongside the published Carbon Voice entry and you can compare the two.
Either credential works in the
envblock —CARBON_VOICE_PATis shown because it expires and is self-service;CARBON_VOICE_API_KEYbehaves the same way. Both grant full access. See the credential comparison further down.
The
envblock is mandatory —.envis NOT read here.env-cmdonly wraps the npm scripts, andscripts/mcp-client.mjsparses.envitself; the server reads plainprocess.env. An MCP client spawns the process with a minimal environment, so a key that only exists in.envwill not be seen.
A missing credential looks like success. Both
CARBON_VOICE_PATandCARBON_VOICE_API_KEYare optional in the config schema andtools/listnever calls the API, so the server connects and shows all 42 tools with no key at all. The failure surfaces only on the first tool call. "It connected" does not mean auth works — make a real call to confirm.
After any code change: npm run build, then restart the client. Clients cache
the tool list per connection, so a reconnect is what picks up new or renamed
tools.
Debugging. Logs default to the file transport at
/tmp/cv-mcp-server/logs/ (combined.log, error.log) — the place to look
when a client reports a server that won't start:
tail -f /tmp/cv-mcp-server/logs/combined.logAdd "LOG_LEVEL": "debug" to the env block for request/response tracing.
Logs go to stderr and files, never stdout, so they cannot corrupt the JSON-RPC
stream.
Driving the server from the terminal
scripts/mcp-client.mjs is a minimal stdio MCP client — the MCP Inspector is a
browser UI, which is no help in a terminal or CI.
npm run mcp:list # every tool with its wire cost
npm run mcp:schema -- get_message # description + input JSON Schema an agent sees
npm run mcp:size # tools/list payload budget
npm run mcp:call -- get_current_user '{"response_fields":["user.user_guid"]}'MCP_DEBUG=1 shows server logs (they go to stderr, so they never corrupt the
JSON-RPC stream on stdout).
list, schema and size need no credentials — the server builds its tool
list without calling out. So does anything the tool's own schema rejects, which
never reaches a handler:
npm run mcp:call -- create_voicememo_message '{"audio_url":"not-a-url"}'
# -> JSON-RPC -32602, "Invalid url" on path audio_urlThe SSRF guard is a different matter. audio_url is only fetched after the
caller is authenticated against cv-api, so a URL like
http://169.254.169.254/ returns 401 before the guard is consulted unless
credentials are configured — with them, it returns INVALID_AUDIO_URL. That
ordering is deliberate: an unauthenticated caller should not be able to make
this server fetch anything at all.
Calls that reach the API need stdio credentials: CARBON_VOICE_PAT
(preferred) or CARBON_VOICE_API_KEY.
Smoke-testing against a real account
cp .env.sample .env # set CARBON_VOICE_PAT (preferred) or CARBON_VOICE_API_KEY
npm run build
npm run mcp:smokeWalks ~19 read-only steps against your live account, chaining IDs the way the
tool descriptions tell an agent to — workspace id, then conversation id, then
message id — so a broken prerequisite shows up as a failed step instead of an
agent quietly guessing. Add --verbose to dump each payload.
Read-only is a property of the script, which calls no create, update or delete
tool — not of the credential. Whatever you put in .env can write; see the
credential comparison above.
Every read is called twice: bare, and with the response_fields set its own
description recommends. The report shows the byte delta per tool and in total,
so the projection claim is measured on your data rather than on a fixture.
It cannot change your account — no tool that creates, updates, moves or deletes is invoked. Write paths are listed at the end with copy-paste commands to run deliberately, one at a time.
Exit code is non-zero if any step fails. When every step fails it prints a diagnosis, because that pattern is nearly always configuration rather than code:
Note | Cause |
| no valid |
| key is valid, but workspace access is refused on SSO grounds |
| no route to the API from this machine |
| an HTTP proxy is intercepting — axios needs a CONNECT tunnel, check |
Two credential options for stdio, and a PAT is the better one. Set
CARBON_VOICE_PATinstead ofCARBON_VOICE_API_KEYwhere you can — it is sent asAuthorization: Bearer cv_pat_..., which is what cv-api'sPatTokenStrategyreads:
API key
PAT
Access
full user identity
full user identity
Expiry
long-lived
max 2 years, revocable
Getting one
email devsupport@phononx.com
self-service:
POST /patsA PAT is not a least-privilege credential. It carries
cv:read/cv:writescopes and is issued with both by default, but cv-api enforces them in exactly one place — the app subscribe/unsubscribe endpoints (user-app.service.ts). Nothing in/simplified/*reads them, so acv:readPAT can send messages and delete folders like any other credential. Do not hand one to an untrusted client expecting read-only access. The PAT is better because you can expire and revoke it, not because it is narrower.When both are set the PAT wins and
x-api-keyis suppressed entirely. That matters: cv-api triesapi-keybeforepat-tokenin its strategy chain, so sending both would authenticate the request as the long-lived key rather than the PAT you configured — losing its expiry, its revocability and its identity in the audit trail.Neither is used by the HTTP transport, which authenticates with OAuth.
CARBON_VOICE_API_KEYis a personal API key, not an OAuth credential. The two transports authenticate differently, andsetCarbonVoiceAuthHeader(src/auth/auth.service.ts) sends one or the other, never both:
API key (stdio)
OAuth (HTTP)
Header
x-api-key: <key>
Authorization: Bearer <access_token>Identity
one user, fixed when the key is issued
whoever authorizes your app
Credential
one long-lived token
client_id+client_secret→ access tokenAn OAuth access token will not work here: cv-api's
ApiKeyStrategylooks the value up as aTYPE_API_KEYtoken, and an access token is not in that table. (The reverse does work — an API key is accepted in either header, via a documented backward-compatibility fallback.)The key carries your identity with no scoping, so treat it like a password:
.env(gitignored) or a client'senvblock, never a commit.
MCP Inspector
npm run mcp:inspector:stdio # browser UI against the stdio server
npm run dev:http # then: npm run mcp:inspector:httpHTTP transport
npm run dev:http # stateful (what production runs)
npm run dev:http:stateless # fresh server per requestUnauthenticated endpoints for a quick check:
curl localhost:3005/health # includes upstream API reachability
curl localhost:3005/info
curl localhost:3005/.well-known/oauth-protected-resourcePOST / (the MCP endpoint) needs a bearer token carrying the mcp:read and
mcp:write scopes — without one it returns 401, with the wrong scopes
insufficient_scope.
You do not need an OAuth round trip to test the protocol layer. The dev CLI
mints a local token and targets a running HTTP server with --http:
npm run dev:http # in one terminal
npm run mcp:list -- --http # defaults to localhost:3005
npm run mcp:size -- --http http://localhost:3005/
npm run mcp:schema -- --http get_message
npm run mcp:call -- --http get_current_user '{}'This works because createOAuthTokenVerifier (src/auth/auth.service.ts) uses
jwt.decode, not jwt.verify — it requires a decodable JWT with sub,
client_id and the two scopes, and the SDK middleware enforces exp. The
signature is not checked, so the signing secret is irrelevant.
What that covers, and what it does not. Enough for the transport, session
handling, tools/list, schema shape and error envelopes. Not enough for
calls that touch data: the token is forwarded verbatim to cv-api as
Authorization: Bearer <token>, and cv-api does validate it, so a minted token
gets a 401 there. For real data over HTTP, pass a genuine access token:
npm run mcp:call -- --http --token <access_token> get_current_user '{}'For tool-level work against real data, stdio with an API key is simpler — no OAuth flow at all.
Not verifying the signature locally is not an auth bypass: cv-api is the authority, session ids are random UUIDs rather than derived from token claims, and rate limiting is IP-based. A forged token buys only a forged
sub/client_idin this server's logs and session context — worth knowing if you rely on those for attribution.
Tests
npm run test:unit # fast, no network
npm run test:e2e # HTTP transport
npm run test:coverage
npm run measure:payloads # response projection + tools/list budgetDevelopment
This section is for developers who want to contribute, implement new features, or fix issues.
Development Commands
Building and Development
npm run build # Build the project
npm run auto:build # Watch mode with auto-rebuild (recommended for development)
npm run lint:fix # Fix linting issuesAPI Generation
npm run generate:api # Generate TypeScript types from Carbon Voice APIRunning the Server
npm run dev:http # Start HTTP server in development mode with hot reload
npm run start:http # Start HTTP server in production modeTesting with MCP Inspector
Setup: Copy .env.sample to .env and configure your development environment variables.
npm run mcp:inspector:stdio # Test stdio transport with MCP Inspector
npm run mcp:inspector:http # Test HTTP transport with MCP InspectorFor stdio transport testing:
Open the generated URL with token (e.g.,
http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=46bfbd8938955be26da7f2089a8cccb7be57ed570e65d8d2d68e95561ed9b79e)Set Transport Type:
STDIOSet Command:
nodeClick Connect
Should see Connected info.
For HTTP transport testing:
Open the generated URL with token
Set Transport Type:
Streamable HTTPSet URL:
http://localhost:3005Click Auth, then Quick Oauth Flow.
Will be redirected to Carbon Voice Auth Page. After Login, Bearer token should be auto added to Authorization Request headers.
Click Connect
Should see Connected info.
Version Management
Note: Only code merged to main branch with a different version from the current one will create a new Git tag and trigger a new npm package release. The CI/CD pipeline automatically checks if the version in package.json has changed before deploying and publishing.
Version Commands
npm run version:patch # Bump patch version (1.0.0 → 1.0.1)
npm run version:minor # Bump minor version (1.0.0 → 1.1.0)
npm run version:major # Bump major version (1.0.0 → 2.0.0)Release Commands
npm run release:patch # Build, test, version patch, and merge to main
npm run release:minor # Build, test, version minor, and merge to main
npm run release:major # Build, test, version major, and merge to main
npm run deploy:release # Build, test, and merge to main (no version bump)Development Workflow Examples
Commit to Develop
# 1. Make your changes and test locally
npm run build
npm run lint:fix
# 2. Commit and push to develop
git add .
git commit -m "feat: add new message filtering feature"
git push origin developRelease Bug Fix
# 1. Test your changes
npm run build
npm run mcp:inspector:http
# 2. Release patch version
npm run release:patchRelease New Feature
# 1. Test your changes
npm run build
npm run mcp:inspector:stdio
npm run mcp:inspector:http
# 2. Release minor version
npm run release:minorDevelopment Tips
Use
auto:buildduring development for automatic rebuilding when files changeTest both transports with MCP Inspector before releasing
Run
generate:apiwhen Carbon Voice API changesUse semantic versioning: patch for fixes, minor for features, major for breaking changes
Always test with both stdio and HTTP transports before releasing
MCP Compliance
This server is fully compliant with the Model Context Protocol specification and follows all security best practices outlined in the official documentation. The implementation supports both stdio and HTTP transports as defined in the MCP specification.
Support
Issues: GitHub Issues
API Key Requests: devsupport@phononx.com
Carbon Voice Platform: https://getcarbon.app
API Documentation: https://api.carbonvoice.app/docs
License
ISC License - See LICENSE file for details.
Note: This MCP server requires a valid Carbon Voice API key to function with stdio transport. For HTTP transport, OAuth2 authentication is handled automatically through the web interface. Please ensure you have the appropriate credentials before attempting to use the server.
Available Tools
42 toolsadd_attachments_to_messageA
Attach one or more link URLs to an existing message.
USE WHEN: Adding external links to a message that already exists.
USE INSTEAD: create_message_share_link to share a Carbon Voice message outward — that produces a link, this consumes them.
FIRST: id comes from list_messages (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"msg-abc","links":["https://example.com/spec"]}
RETURNS: {...} confirmation with the resulting attachments.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| links | Yes | Array of links to be attached to the message |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the tool as not read-only and not destructive; the description adds that it consumes supplied links and returns a confirmation with resulting attachments. It does not go into duplicate handling or URL validation, but it gives enough behavioral context for normal use.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with the one-sentence purpose and then uses compact labeled sections for condition, alternative, prerequisite, example, and return. No filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter mutation with no output schema, the description covers the call prerequisites, example payload, and a return expectation. It leaves the return shape vague as {...}, but this is adequate for a confirmation-style response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema describes links but leaves id as a bare string; the description fills that gap by pointing to list_messages results[].id. The example also anchors both parameters with concrete values.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence names a specific action and resource: attach one or more link URLs to an existing message. The USE INSTEAD note explicitly separates it from create_message_share_link by contrasting producing a link vs consuming them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
USE WHEN states the intended trigger, USE INSTEAD names the sibling not to choose and why, and FIRST gives the prerequisite call to list_messages for the id. This removes ambiguity about when and how to invoke the tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_action_itemA
Create an action item, optionally attached to a conversation or folder.
USE WHEN: Recording a task. Only title is required. Attach it by passing both container_type and container_id, and link it to what prompted it with source_message_id.
USE INSTEAD: suggest_action_items_from_messages to have tasks extracted from message content automatically instead of writing each one yourself.
EXAMPLE: {"title":"Send the pricing deck","assigned_to":"user-abc"}
RETURNS: {id, title, status, notes_text?, assigned_to?, due_date?, container_id?, container_type?, creator_id, ...}. New items start at status todo.
ERROR BAD_REQUEST: assigned_to is not a valid user ID, or container_id does not match container_type. — Resolve people with search_users (never pass a name) and containers with list_conversations or get_root_folders.
| Name | Required | Description | Default |
|---|---|---|---|
| title | Yes | What needs doing. The only required field. | |
| due_date | No | Due date as an ISO 8601 timestamp. | |
| notes_text | No | Free-text detail. | |
| assigned_to | No | Assignee user ID, from `search_users`. Not a name. | |
| container_id | No | ||
| workspace_id | No | Workspace scope, from `get_workspaces_basic_info`. | |
| container_type | No | ||
| source_message_id | No | Message that prompted this item, from `list_messages`. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only carry readOnlyHint=false and destructiveHint=false, so the description carries the behavioral burden and delivers. It discloses the return shape, that new items start at status `todo`, the BAD_REQUEST error conditions, and the rule that assigned_to must be a valid user ID resolved via search_users, never a name. This is precisely the context an agent needs beyond static annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is organized into scannable labeled sections (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, ERROR) with the core statement front-loaded. Each sentence earns its place — the example, return shape, and error semantics are all necessary because no output schema exists. The length is justified for an 8-parameter mutation tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no output schema, the description fully explains return values (RETURNS shape), initial status, failure modes, and prerequisite lookup routes (search_users, list_conversations, get_root_folders). workspace_id is the only parameter not explicitly addressed, but the schema documents it. An agent has everything needed to invoke this correctly on the first attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 75%, so the schema already documents six of eight parameters. The description adds cross-parameter constraints the schema cannot express: pass both container_type and container_id together, use source_message_id to 'link it to what prompted it', and resolve assigned_to via search_users. The compact example clarifies the minimal valid payload. Not a 5 because container_type enum values and workspace_id get no dedicated description, though the error section partially offsets the container gap.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource — 'Create an action item' — and adds the optional attachment scope ('conversation or folder'). The USE INSTEAD clause names suggest_action_items_from_messages, the sibling most likely to be confused with this tool, so an agent can discriminate without opening the schema. This exceeds the baseline clarity of tools that rely on the name alone.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states WHEN to use it ('Recording a task') and WHEN NOT to ('USE INSTEAD: suggest_action_items_from_messages... instead of writing each one yourself'). It names the alternative tool, the condition that selects it, and even gives an example payload. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_conversation_messageA
Post a message into an existing conversation, or reply in a thread.
USE WHEN: You have a conversation_id. Pass parent_id (a message ID) to reply as a thread. Either transcript or links is required — the schema marks neither individually, so both param descriptions say so.
USE INSTEAD: create_direct_message to reach people who are not already in a conversation. create_voicememo_message for a standalone memo.
FIRST: id comes from list_conversations (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"conv-abc","transcript":"Agreed, shipping Friday."}
RETURNS: {message: {id, link, transcript?, status, type, conversation_id, created_at, ...}}.
ERROR BAD_REQUEST: Neither transcript nor links was provided. — Pass at least one of them.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| links | No | Link URLs to attach. Either `transcript` or `links` is required. | |
| transcript | No | Text to speak via text-to-speech. Either `transcript` or `links` is required. | |
| from_message_id | No | Message ID to be used as a base for the new message. (Optional only when from_message_type is NewMessage) | |
| from_message_type | No | From Message type | NewMessage |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=false and destructiveHint=false, so the agent knows this is a non-read, non-destructive operation. The description adds valuable behavioral context: the either/or requirement for transcript/links, the threading behavior via parent_id, the expected return shape, and the specific BAD_REQUEST error condition. It doesn't fully disclose all side effects (e.g., notifications, TTS behavior), but the provided context goes well beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded. The core action is in the first sentence, followed by clearly labeled USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, and ERROR sections. Every sentence earns its place, and the formatting makes it easy for an agent to scan for the key decision points.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 5 parameters, no output schema, and moderate complexity, the description covers the essential context: how to get the id, the threading mechanism, the either/or requirement, the return shape, and the primary error case. The from_message_id/from_message_type parameters are not explained in the description, but the schema provides some context. The description is complete enough for an agent to call the tool correctly in the common cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80%, and the description adds meaning beyond the schema. It clarifies that neither transcript nor links is individually required but at least one must be provided, which the schema descriptions also state but the description reinforces. It explains that parent_id is a message ID for threading, and that id comes from list_conversations. The from_message_id/from_message_type parameters are not explained in the description, but the schema provides some context for them. The description compensates well for the schema's ambiguity around the either/or requirement.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Post a message into an existing conversation, or reply in a thread.' It clearly distinguishes the tool from siblings by naming create_direct_message and create_voicememo_message as alternatives. The scope is precise: requires an existing conversation_id, and supports either a top-level post or a threaded reply via parent_id.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit USE WHEN, USE INSTEAD, and FIRST guidance. It tells the agent when to use this tool (has a conversation_id), when to use alternatives (create_direct_message for people not in a conversation, create_voicememo_message for standalone memos), and how to obtain the required id (from list_conversations). It also clarifies the parent_id threading behavior and the either/or requirement for transcript/links.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_direct_messageA
Send a direct message to one or more people, by user ID or email.
USE WHEN: Reaching people outside an existing conversation. Address it with to.user_ids or to.emails. Requires transcript or links.
USE INSTEAD: create_conversation_message when a conversation already exists — a DM starts a separate thread rather than joining it.
FIRST: to.user_ids comes from search_users (field id) — call it first if you don't have one.
EXAMPLE: {"to":{"user_ids":["user-abc"]},"transcript":"Quick question about the deck."}
RETURNS: {message: {id, link, transcript?, status, conversation_id, created_at, ...}}.
ERROR BAD_REQUEST: A user ID is invalid, or neither transcript nor links was provided. — Resolve people with search_users — never pass a display name as a user ID — and include a transcript or links.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | Recipients: `user_ids` (from `search_users`) and/or `emails`. At least one required. | |
| links | No | Array of links to be attached to the message | |
| transcript | No | The Message transcript will be used to generate audio using text-to-speech | |
| workspace_id | No | The workspace ID to send the message to | personal |
| from_message_id | No | Message ID to be used as a base for the new message. (Optional only when from_message_type is NewMessage) | |
| from_message_type | No | From Message type | NewMessage |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the minimal annotations (readOnlyHint=false, destructiveHint=false) by disclosing the requirement for transcript or links, the error condition (BAD_REQUEST for invalid user ID or missing transcript/links), and the fact that a DM starts a separate thread. It also mentions the return shape and example, offering substantial behavioral context without contradicting annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with clear sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, ERROR) that are all informative and directly relevant. Every sentence adds value, and it is front-loaded with the core action. No fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (6 parameters, nested object, enum, and no output schema), the description covers all essential aspects: usage context, prerequisites, example, return format, and error handling. An agent can confidently call this tool correctly based on the description alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by clarifying that 'to.user_ids' comes from search_users and warning against passing display names as user IDs. It also highlights the transcript/links requirement, which is not immediately obvious from the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Send a direct message to one or more people, by user ID or email.' It clearly distinguishes from the sibling tool by noting that a DM starts a separate thread rather than joining an existing conversation, making it unmistakable which tool to use.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use this tool ('Reaching people outside an existing conversation') and when to use an alternative ('USE INSTEAD: create_conversation_message when a conversation already exists'). It also provides a prerequisite step (calling search_users to obtain user_ids), which is valuable guidance for the agent.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_folderA
Create a folder in a workspace, optionally nested under another.
USE WHEN: Organising memos. name, type and workspace_id are all required; add parent_folder_id to nest.
USE INSTEAD: move_folder to relocate a folder that already exists.
FIRST: workspace_id comes from get_workspaces_basic_info (field id) — call it first if you don't have one.
EXAMPLE: {"name":"Q4 planning","type":"voicememo","workspace_id":"ws-abc"}
RETURNS: The created folder, same shape as get_folder.
| Name | Required | Description | Default |
|---|---|---|---|
| name | Yes | Name of the Folder | |
| type | Yes | Folder type | |
| workspace_id | Yes | Workspace ID | |
| parent_folder_id | No | Parent Folder ID |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=false and destructiveHint=false, so the mutation is implied. The description adds value by specifying that it returns the created folder 'same shape as get_folder,' which is beyond the annotations. It also clarifies the nesting behavior via parent_folder_id. This provides useful behavioral context beyond what annotations declare.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet structured with clear section markers (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS). Each sentence serves a purpose: purpose, usage context, alternatives, prerequisite, example, and return shape. No redundancy or fluff, and the most critical information (purpose) is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a create operation with 4 parameters and no output schema, the description covers all necessary aspects: what it does, when to use it, what to use instead, how to obtain a required parameter, an example payload, and the return format. There are no missing pieces an agent would need to correctly invoke the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so all parameters have descriptions. The description adds an example with a sample value, emphasizes required fields, and clarifies parent_folder_id for nesting. This goes beyond the schema's dry descriptions, providing concrete usage context. The example directly maps to the parameter structure.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description explicitly states 'Create a folder in a workspace, optionally nested under another.' This is a specific verb and resource, and the mention of optional nesting distinguishes it from sibling tools like move_folder and delete_folder. It clearly defines what the tool does without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance: 'USE WHEN: Organising memos' and 'USE INSTEAD: move_folder to relocate a folder that already exists.' It also instructs to call get_workspaces_basic_info first to obtain workspace_id. This clearly tells the agent when to use this tool and when to use an alternative, leaving no inference needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
create_voicememo_messageA
Create a voice memo, either from text (spoken via text-to-speech) or from an audio file at a URL.
USE WHEN: Pass transcript (2-5000 chars) to have Carbon Voice speak the text, or an https audio_url to upload audio, which wins over transcript. File it with folder_id, whose type must match, or a workspace_id.
USE INSTEAD: create_conversation_message to post into an existing conversation, or create_direct_message to send to specific people. A voice memo is standalone and lives in a folder or workspace.
EXAMPLE: {"transcript":"Reminder to review the pricing deck before Friday."}
RETURNS: {message: {id, link, transcript?, audio_url?, duration_ms, status, type, created_at, ...}}. status is often processing at first.
ERROR INVALID_AUDIO_URL: audio_url is not https, unreachable, too large, timed out, embeds credentials, or resolves to a private address. — The message gives the reason. Use a public https URL, or pass transcript instead.
ERROR BAD_REQUEST: None of transcript, audio_url or links was provided, or the transcript is outside 2-5000 characters. — Provide one of the three, and keep the transcript within the length limits.
| Name | Required | Description | Default |
|---|---|---|---|
| links | No | Link URLs to attach. Max 100, and each must be a valid URL. One of `transcript`, `links` or `audio_url` is required. | |
| audio_url | No | Public **https** URL to an audio file to upload. Supported formats: .mp3, .m4a, .wav, .aac, .ogg, .flac, .wma, .opus, .webm. Overrides `transcript` when provided. The server fetches this URL, so it must be publicly reachable — private, loopback and link-local addresses are refused, and plain http only works for hosts the operator has allowlisted. | |
| folder_id | No | Folder to file the memo in. The folder type must match the memo type. | |
| transcript | No | Text to speak via text-to-speech. 2-5000 characters. Ignored when `audio_url` is provided. One of `transcript`, `links` or `audio_url` is required. | |
| workspace_id | No | Workspace to place the memo in when it is not going into a folder. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond the minimal annotations: audio_url overrides transcript, status is often "processing" at first, and both INVALID_AUDIO_URL and BAD_REQUEST error conditions are fully explained. This gives the agent a reliable model of what happens when the tool is invoked.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is longer than average but every labeled section (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, ERRORS) carries useful information. Key selection criteria are front-loaded before return and error details.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description compensates with a return shape and the note that status is often 'processing'. It also covers the three mutually exclusive input modes via the BAD_REQUEST error, file destination options, and common failure modes. The schema covers links and formats, so nothing critical is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the input schema already documents all five parameters well. The description mostly restates schema semantics, such as "audio_url wins over transcript" and the 2-5000 character limit, without adding meaning beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: "Create a voice memo, either from text (spoken via text-to-speech) or from an audio file at a URL." It also differentiates this tool from the sibling tools by explicitly noting that a voice memo is "standalone and lives in a folder or workspace."
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description has explicit USE WHEN guidance for transcript vs audio_url vs folder_id/workspace_id, and a USE INSTEAD section naming create_conversation_message and create_direct_message with the reason. It also gives a concrete example, leaving little to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_action_itemADestructive
Permanently delete an action item.
USE WHEN: The item was created in error and should not exist at all.
USE INSTEAD: set_action_item_status with done to complete an item — that keeps the record. Deleting cannot be undone, so prefer it only when the item is genuinely spurious.
FIRST: id comes from list_my_action_items (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"ai-abc"}
RETURNS: Deletion confirmation for the removed item.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark destructiveHint: true, and the description adds meaningful context beyond that: deletions are permanent ('cannot be undone') and the tool returns a deletion confirmation. This gives the agent a fuller behavioral model than the annotation alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description uses a labeled structure (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS) that front-loads the key decision and keeps each section purposeful. Every sentence earns its place without excess.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter destructive tool with annotations already covering the danger profile, the description covers the usage context, filter condition, id origin, example, and return value. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% (the id parameter has no description), but the description fully compensates by explaining where the id comes from (list_my_action_items, field results[].id), instructing to call that first if needed, and providing a concrete example. No gap remains for the single parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Permanently delete an action item') and immediately clarifies scope ('should not exist at all'), distinguishing it from the related set_action_item_status sibling. An agent can tell exactly what this tool does and how it differs from completing an item.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Contains explicit 'USE WHEN' and 'USE INSTEAD' guidance, naming the alternative tool (set_action_item_status with `done`) and the condition that selects it. It also warns to prefer deletion only for spurious items, leaving no inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
delete_folderADestructive
Permanently delete a folder, including every nested folder and all their messages.
USE WHEN: Only when the whole subtree should be destroyed. This cascades and cannot be undone.
USE INSTEAD: move_folder to get a folder out of the way, or move_message_to_folder to relocate its messages first. Check total_nested_messages_count via get_folder before calling — the cascade is easy to underestimate.
FIRST: id comes from get_root_folders (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"folder-abc"}
RETURNS: Deletion confirmation.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the destructiveHint=true annotation, the description discloses that the deletion cascades to nested folders and messages, cannot be undone, and is easy to underestimate. It also states the return value is a deletion confirmation. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Structured with labeled sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS) that front-load critical constraints before the example. Every sentence contributes actionable information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a destructive cascade tool with one parameter and no output schema, the description covers invocation trigger, alternatives, id provenance, an example, and the return type. The agent has everything needed to call the tool correctly and safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema description coverage, the description fully compensates: it states the id comes from get_root_folders (field results[].id), instructs to call that first if needed, and provides an example {"id":"folder-abc"}. This is more meaning than the bare 'id: string' schema provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Permanently delete a folder, including every nested folder and all their messages.' The scope of deletion is explicit, distinguishing it from siblings like move_folder and get_folder.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit USE WHEN ('Only when the whole subtree should be destroyed') and USE INSTEAD sections naming move_folder and move_message_to_folder as alternatives. Also instructs to check total_nested_messages_count via get_folder before calling, leaving no ambiguity about when to invoke it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_action_itemARead-only
Get one action item by its ID, with full detail.
USE WHEN: You have an action item ID and need its notes, assignee, due date, or source message.
USE INSTEAD: list_my_action_items or list_action_items if you do not have an ID yet — they already return the same fields per item, so a follow-up call is usually wasted.
FIRST: id comes from list_my_action_items (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"ai-abc"}
RETURNS: {id, title, status, notes_text?, creator_id, assigned_to?, due_date?, container_id?, container_type?, source_message_id?, last_updated_by, ...}.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is known. The description adds valuable behavioral context by listing the returned fields (including optional ones) and explicitly noting that the list tools already return the same per-item fields, implying a redundant call is unnecessary. This goes beyond the annotation baseline without contradicting it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly organized with clear semantic labels (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS). The purpose is front-loaded, and every line carries functional information. There is zero fluff; each section earns its place by guiding the agent's decision.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-ID fetch tool with no output schema, the description provides everything an agent needs: the source of the ID, the return shape, the differentiation from siblings, and an example. The complexity is low, and the description fully covers the decision and invocation path. No essential information is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers one of two parameters (response_fields has a description), leaving id without schema-level documentation. The description compensates by explaining where id comes from (list_my_action_items results[].id) and gives an example value. However, it does not mention the response_fields parameter at all, which is a minor gap given the schema already documents it. Overall, the description adds meaningful meaning to the critical id parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb-object pair, 'Get one action item by its ID, with full detail,' and immediately distinguishes this tool from the list siblings by stating it operates on a single ID. The scope is explicit and the differentiation is clear without needing to inspect sibling schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description uses labeled sections (USE WHEN, USE INSTEAD, FIRST) to give explicit usage context. It names the alternatives (list_my_action_items, list_action_items), states when to choose them (no ID yet), and warns that a follow-up call is usually wasted because those already return the same fields. This is exceptionally clear routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ai_action_responsesARead-only
Retrieve AI Action results that were generated previously.
USE WHEN: You want existing output rather than a fresh run — e.g. showing what an AI Action already produced for a message or conversation. Combine prompt_id, message_id and channel_id to narrow.
USE INSTEAD: run_ai_action (or summarize_conversation) to generate new output; this tool only reads what already exists and returns an empty array if nothing has been generated.
EXAMPLE: {"channel_id":"conv-abc","limit":10}
RETURNS: Array of {id, prompt_id, creator_id, message_ids, channel_id?, workspace_id?, responses: [{language, text?, markdown?, html?, json?}], created_at}.
NARROW: pass response_fields ["id","prompt_id","responses"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | ISO 8601 anchor timestamp; pair with `direction` to page. | |
| limit | No | Max responses to return. | |
| direction | No | ||
| prompt_id | No | Only responses generated by this AI Action, from `list_ai_actions`. | |
| channel_id | No | Only responses in this conversation, from `list_conversations`. | |
| message_id | No | Only responses about this message, from `list_messages`. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description doesn't need to restate safety. It adds meaningful behavioral context beyond that: the return format, the fact that an empty array is returned if no data exists, and a warning that the full payload is much larger and can be shrunk with response_fields. This gives the agent a good sense of what to expect, though it doesn't explicitly explain the date/direction pagination mechanics (those are in the schema but not described in prose).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly organized with labeled sections (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW) and every sentence contributes. The core purpose is front-loaded, and the example is compact. There is zero filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool is a simple read-only retriever with no output schema, the description fully covers what an agent needs: what it does, when to use it, how to narrow results, what the return looks like, and a performance tip. The absence of a documented output schema is mitigated by the explicit RETURNS section. It is complete for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 86%, so the schema already documents most parameters. The description adds value by explaining the combined use of prompt_id, message_id, and channel_id to narrow results, and by clarifying that response_fields is an allowlist to shrink the payload. The example demonstrates a realistic usage. This exceeds the baseline for high schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific verb-resource statement: 'Retrieve AI Action results that were generated previously.' It immediately distinguishes itself from run_ai_action by noting it only reads existing output, and it lists the narrowing fields. This makes the tool's purpose unambiguous and differentiates it from siblings like run_ai_action and summarize_conversation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit 'USE WHEN' and 'USE INSTEAD' sections, telling the agent exactly when to pick this tool (when existing output is wanted) and when to pick alternatives (run_ai_action or summarize_conversation for fresh generation). It also gives a concrete example and notes that an empty array is returned if nothing exists, which sets expectations clearly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_conversationARead-only
Get one conversation by ID, with full metadata.
USE WHEN: You have a conversation_id and need its description, visibility, owner or workspace name.
USE INSTEAD: get_conversation_users for the participant list. list_messages with conversation_id for its messages — this returns neither.
FIRST: id comes from list_conversations (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"conv-abc"}
RETURNS: {id, name, description?, link, workspace_id, workspace_name, owner_id, type, visibility, ...}.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and destructiveHint, and the description adds what the response contains and explicitly states it returns neither participant lists nor messages. It also sketches the return shape; a small gap is that it doesn't mention response_fields behavior, but the schema covers that.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Seven short labeled sections each add distinct value: operation, trigger, alternatives, id source, example, return shape. No filler, and the key purpose is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-object read tool, everything needed to call it correctly is present: when, alternatives, id provenance, example JSON, return shape, and annotations for safety. The output schema is absent, but the RETURNS field compensates, and the optional response_fields parameter is documented in the input schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
id is just a string in the schema; the description adds its meaning (conversation_id), its provenance (list_conversations results[].id), and an example. response_fields is already described in the schema, so the description does not need to repeat it; with 50% schema coverage this is adequate compensation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opening line names the exact operation: get one conversation by ID, and states the payload category (full metadata). The USE INSTEAD section explicitly contrasts with get_conversation_users and list_messages, so an agent can distinguish it from the nearest siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
USE WHEN gives a concrete trigger condition (having a conversation_id and needing description, visibility, owner, workspace name). USE INSTEAD routes to the right sibling for participants/messages, and FIRST tells where the id comes from, making the decision and call sequence explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_conversation_usersARead-only
List the people in a conversation.
USE WHEN: Finding out who is in a conversation, or collecting participant user IDs.
USE INSTEAD: search_users to resolve people by name or email generally — this is scoped to one conversation.
FIRST: id comes from list_conversations (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"conv-abc"}
RETURNS: Array of user objects with id and profile fields.
NARROW: pass response_fields ["id","full_name"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already convey that the tool is read-only and non-destructive. The description adds useful behavioral context beyond this: it returns an array of user objects, states that the full payload is much larger, and recommends narrowing fields to avoid it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The capitalized micro-headings create a scannable structure, and every sentence earns its place: when to use it, what it returns, how to get the input, an example, and a performance tip. Nothing is redundant with the schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter read-only tool with no output schema, the description covers everything an agent needs: purpose, input provenance, response shape, and field-narrowing guidance. It is complete without requiring the agent to inspect siblings or infer behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 50% (the `id` parameter is undocumented). The description compensates by explaining where `id` comes from (`list_conversations` field `results[].id`), giving a concrete example, and showing exactly how to use `response_fields` to shrink the response.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: "List the people in a conversation." It also distinguishes itself from the sibling `search_users` by emphasizing that it is scoped to one conversation, making its purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The USE WHEN section states the exact use cases, and USE INSTEAD names the alternative tool and the condition selecting it. The FIRST guidance even tells the agent to call `list_conversations` first and where to find `id`, which is precise routing information.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_current_userARead-only
Get the calling user's own identity, workspaces and settings.
USE WHEN: Establishing who you are acting as, or finding the caller’s workspace IDs before a workspace-scoped call. Takes no arguments.
USE INSTEAD: get_user for somebody else (it requires an explicit ID). get_workspaces_basic_info if you only need workspace IDs and names — it is far smaller than this response.
EXAMPLE: {}
RETURNS: {success, user: {user_guid, first_name, last_name?, email_txt?, phone_txt?, workspace_guids, identities, entries, environments, lifecycle_events, notification_settings, settings, ...}, settings: {...}}. This payload is LARGE — several unbounded arrays and an open settings map.
NARROW: pass response_fields ["user.user_guid","user.first_name","user.email_txt","user.workspace_guids"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so safety is covered. The description adds meaningful behavioral context beyond annotations: the response payload is LARGE with unbounded arrays and an open settings map, and it explains the NARROW mechanism to mitigate this. This is genuinely useful for an agent deciding whether to invoke and how to shape the request.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly structured with labeled sections (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW) that are each a single line or two, no filler, and the most critical scoping information is front-loaded. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-required-parameter read-only tool, this description is complete: it explains the return shape (albeit without a formal output schema, it gives a useful sketch), warns about response size, provides an example call, and routes to alternatives when appropriate. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema description fully documents response_fields (dot-path allowlist, example, omit for full payload). The description reinforces this with a concrete recommended list (['user.user_guid','user.first_name','user.email_txt','user.workspace_guids']) and warns about the full payload size, adding practical value beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb-resource pair ('Get the calling user's own identity, workspaces and settings') and explicitly distinguishes itself from get_user (for others) and get_workspaces_basic_info (smaller response). This makes the tool's purpose unambiguous and separable from siblings without needing to inspect schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit USE WHEN conditions (establishing identity, finding workspace IDs before workspace-scoped calls) and USE INSTEAD guidance naming two alternatives with the exact condition that selects them. This gives an agent clear routing rules, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_folderARead-only
Get one folder's metadata and, optionally, its immediate subfolders.
USE WHEN: Inspecting a folder. Set include_first_level_tree: true to get subfolders. Both date AND direction are silently ignored unless you do — upstream only documented that caveat on date.
USE INSTEAD: get_folder_with_messages when you want the messages inside the folder — this returns structure and counts only.
FIRST: id comes from get_root_folders (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"folder-abc","include_first_level_tree":true}
RETURNS: {id, name, type, workspace_id, parent_folder_id?, path?, subfolder_ids?, message_ids?, total_nested_folders_count, total_nested_messages_count, subfolders?, ...}.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| date | No | Return only subfolders updated relative to this date and `direction`. Only takes effect when `include_first_level_tree` is true — otherwise it is silently ignored. | |
| direction | No | Direction of the results (newer or older). Only takes effect when `include_first_level_tree` is true — otherwise it is silently ignored. | newer |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. | |
| include_first_level_tree | No | Defines if the first level of the folder tree should be returned |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses a non-obvious and potentially dangerous behavioral quirk: both date and direction are silently ignored unless include_first_level_tree is true. It also explains that this tool returns only structure and counts, not message contents, and includes a return shape with optional fields. The annotations already declare readOnly and non-destructive, so no contradiction exists and the extra caveat meaningfully adds transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear labels and front-loaded purpose, then chunked into usage, alternative, prerequisite, example, and return sections. It is somewhat dense and includes a slightly cryptic phrase ('unless you do' and 'upstream only documented that caveat on date'), but overall every major section earns its place and improves usability.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, so the description compensates by providing a return shape and clarifying optional fields. It also covers the prerequisite call, the mutation-free read-only nature, the subfolder behavior, and the sibling distinction. For a 5-param tool with one required param, no output schema, and only read-only annotations, this is a complete and well-rounded definition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 80%, so the baseline is already fairly high. The description adds important meaning for the undocumented id parameter by identifying its source and exact extraction path (get_root_folders results[].id), and it includes a concrete example. It mostly restates the schema for date, direction, and response_fields, but the id guidance plus example lifts it above baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action and resource: retrieving one folder's metadata, with an optional subfolder tree. It explicitly names get_folder_with_messages as the alternative and clarifies this tool returns structure and counts only, so the agent can distinguish it from a related sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives explicit 'USE WHEN' context for inspecting a folder, 'USE INSTEAD' guidance for get_folder_with_messages, and a 'FIRST' instruction to call get_root_folders for the id. This gives clear routing between alternatives and prerequisites, which is exactly what an agent needs to decide when to call it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_folder_with_messagesARead-only
Get a folder together with the messages stored directly in it.
USE WHEN: Reading a folder’s contents. Only messages at that folder’s own level are returned — nested folders are not walked.
USE INSTEAD: get_folder for structure and counts without message bodies. list_messages with folder_id when you need date filtering or paging, which this tool does not support.
FIRST: id comes from get_root_folders (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"folder-abc"}
RETURNS: {folder: {...}, messages: [{...}]}.
NARROW: pass response_fields ["folder.id","folder.name","messages"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses non-obvious behavior beyond the read-only annotations: nested folders are not walked, date filtering and paging are unsupported, and the full payload is much larger than the narrow response. It also states the exact return shape, which is valuable since no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with clear labels and every block adds distinct value: purpose, when, alternatives, dependency, example, return shape, and response narrowing. It is longer than average but earns its length by front-loading the most important information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given only two parameters, no output schema, and safe read-only annotations, the description covers everything needed to invoke the tool correctly: id provenance, response shape, lack of paging/filtering, and a recommended narrow response. No critical invocation context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With only 50% schema coverage, the description compensates well: it explains where id comes from (get_root_folders results[].id) and gives a concrete response_fields example while warning that the full payload is much larger. This adds meaning the schema does not provide for id and goes beyond the schema's generic response_fields description.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-resource pair: 'Get a folder together with the messages stored directly in it.' It also distinguishes itself from sibling tools by clarifying that only messages at that folder's own level are returned and nested folders are not walked.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly provides USE WHEN, USE INSTEAD with named alternatives (get_folder, list_messages), and the conditions that select each. It also tells the agent to call get_root_folders first to obtain the id, which is essential invocation guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_messageARead-only
Get one message by ID, optionally expanded with related records.
USE WHEN: You have a message ID. fields ADDS related data (conversation, creator, labels) — it does not narrow the response. Use response_fields to narrow.
USE INSTEAD: list_messages when you do not have an ID, or need several messages — it already returns full bodies, so fetching each one again is wasted.
FIRST: id comes from list_messages (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"msg-abc","fields":"creator"}
RETURNS: {message: {id, transcript?, ai_summary?, audio_url?, creator_id, duration_ms, status, type, attachments?, created_at, ...}}.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| fields | No | Fields (optional) - Additional fields to include in the response. Possible values: conversation, creator, labels. | |
| language | No | Language (optional) - Original language will be used if not provided or not found. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the description doesn't need to repeat safety. It adds critical behavioral detail: 'fields' ADDS data rather than narrows, and 'response_fields' narrows. It also discloses the return shape with optional fields, giving agents a clear expectation of the response beyond what annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with labeled sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS) that front-load the purpose and usage. Each sentence adds value, from the example to the return format, without redundancy. It is longer than minimal but every line earns its place by preventing common mistakes.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the absence of an output schema, the description provides a return shape with optional fields. It covers parameter semantics, usage workflow, and alternatives. It does not mention error handling, but for a single-message retrieval with read-only semantics, the provided information is sufficient for an agent to call it correctly. The language parameter is covered by the schema, so no gap remains.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The description explains the id parameter's provenance (from list_messages), clarifies that 'fields' adds related records (listing possible values), and distinguishes it from 'response_fields' with an example. It covers the parameters more richly than the schema alone, especially for 'id' which lacks a schema description, thus compensating for the 75% coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Get one message by ID', and immediately distinguishes it from list_messages by clarifying it retrieves a single message. It also clarifies the optional expansion with 'fields', so the purpose is unambiguous and distinct from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides when to use ('USE WHEN: You have a message ID') and when not to use ('USE INSTEAD: list_messages when you do not have an ID, or need several messages'), including a rationale about wasted calls. It also gives a 'FIRST' instruction on obtaining the ID from list_messages, covering the typical workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_recent_messagesARead-only
Get up to 10 of the most recent messages, each with its conversation, creator and labels.
USE WHEN: A quick "what just happened" glance. Pre-joined, so no follow-up calls for creator or conversation names.
USE INSTEAD: list_messages whenever you need more than 10, any date range, paging, or a filter other than conversation and language — this tool supports none of those.
EXAMPLE: {"conversation_id":"conv-abc"}
RETURNS: {results: [{message: {...}, conversation: {...}, creator: {...}, labels: [...]}]}. No total and no paging: the cap of 10 is the whole answer.
| Name | Required | Description | Default |
|---|---|---|---|
| language | No | Language (optional) - Original language will be used if not provided or not found. | |
| conversation_id | No | Conversation ID (optional) | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate read-only and non-destructive; the description adds the 10-message cap, lack of paging/total, and filter limitations. It does not define the ordering criterion behind 'recent' or error behavior, but covers the important operational boundaries.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Labeled USE WHEN, USE INSTEAD, EXAMPLE, and RETURNS sections make each point scannable and front-loaded. No wasted sentences; every clause adds decision-relevant information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the RETURNS section supplies the response shape and explicitly disclaims paging/total. Between annotations, schema, and description, an agent has everything needed to call and interpret this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema already documents all three parameters at 100% coverage, so baseline applies. Description reinforces that conversation_id and language are the only filters and provides a usage example, but adds little per-parameter semantic detail beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Clearly states a specific verb+resource: retrieving up to 10 most recent messages with conversation, creator, and labels. Distinguishes from list_messages by count and pre-joined data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly labels USE WHEN and USE INSTEAD, naming list_messages as the alternative and listing conditions (more than 10, date range, paging, other filters). This leaves no ambiguity about tool selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_root_foldersARead-only
List the root folders of a workspace for a given folder type.
USE WHEN: Orienting in the folder tree, or finding a folder_id. type is required: voicememo or prerecorded. include_all_tree returns nested folders too.
USE INSTEAD: get_folder to inspect one folder. get_folder_with_messages when you want a folder’s messages rather than its structure.
EXAMPLE: {"type":"voicememo","workspace_id":"ws-abc"}
RETURNS: {type, workspace_id?, include_all_tree?, sort_by, sort_direction, results: [{id, name, parent_folder_id?, subfolder_ids?, total_nested_folders_count, total_nested_messages_count, ...}]}. Not paginated — this is the complete set.
NARROW: pass response_fields ["results.id","results.name","results.total_nested_messages_count"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| type | Yes | Folder Type | |
| sort_by | No | Field to sort by | |
| workspace_id | No | Workspace ID | |
| sort_direction | No | Sort order direction | ASC |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. | |
| include_all_tree | No | Return all folders tree |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds meaningful behavioral detail: the result is not paginated, it returns the complete set, and the full payload is much larger. It also shows how to narrow output with response_fields, which goes beyond annotation data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, and NARROW sections. Every sentence contributes actionable guidance, and the most important usage details are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description documents the return shape explicitly, including optional fields and non-pagination. For a 6-parameter read-only list tool, this is a complete and self-sufficient definition.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Input schema covers all parameters at 100%, so the baseline is 3. The description adds value beyond the schema by giving a concrete example, clarifying type's allowed values, explaining include_all_tree's effect, and documenting response_fields behavior with an example allowlist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the root folders of a workspace') and scopes by folder type. The USE INSTEAD section explicitly names get_folder and get_folder_with_messages as distinct alternatives, so sibling differentiation is strong.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit USE WHEN conditions ('Orienting in the folder tree, or finding a folder_id'), states that type is required, and names alternatives with the conditions that select them. This leaves no ambiguity about when to invoke this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_userARead-only
Get a user's full profile by ID — names, languages, voice settings, workspace roles.
USE WHEN: You already have a user ID and need complete details.
USE INSTEAD: search_user / search_users to FIND someone by email, phone or name. get_current_user for the caller — this tool needs an explicit ID and will not default to you.
FIRST: id comes from search_users (field id) — call it first if you don't have one.
EXAMPLE: {"id":"user-abc"}
RETURNS: {id, first_name, last_name?, languages, voice_gender, workspace_ids, workspace_roles, user_type, created_at, ...}.
NARROW: pass response_fields ["id","first_name","last_name","workspace_ids"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds valuable behavioral context: the tool requires an explicit ID and will not default to the caller, the full payload is much larger, and response_fields can shrink it. These details go beyond the annotations and help an agent manage expectations about output size and ID requirements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although longer than two sentences, every line earns its place. The description uses scannable headers (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, NARROW) and front-loads the core purpose. No filler or redundant content appears, and the structured format makes it easy for an agent to parse quickly.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description provides a return shape ('{id, first_name, last_name?, languages, voice_gender, workspace_ids, workspace_roles, user_type, created_at, ...}') and explains how to narrow it. It also covers the required parameter's provenance and an example. For a read-only get tool, this is enough for an agent to invoke it correctly and interpret results without needing additional documentation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50% (the id property has no description), and the description fully compensates. It explains that id comes from search_users' id field, gives a concrete example ('user-abc'), and interprets response_fields as a 'Dot-path allowlist to shrink the response' with examples. Both parameters gain semantic meaning beyond the raw schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Get a user's full profile by ID' and enumerates the fields covered ('names, languages, voice settings, workspace roles'), which clearly identifies the verb, resource, and scope. It also names its siblings explicitly under 'USE INSTEAD', distinguishing itself from search_user/search_users and get_current_user, so an agent can select it correctly without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'USE WHEN', 'USE INSTEAD', and 'FIRST' sections provide explicit conditions for use, alternatives to prefer, and the required precursor call to search_users. It even advises on narrow response fields with 'NARROW', giving concrete guidance on efficient invocation. This is exemplary when-to-use vs when-not-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_workspaces_basic_infoARead-only
List every workspace you belong to, as id and name only.
USE WHEN: The cheapest way to resolve a workspace name to an ID before a workspace-scoped call. Takes no arguments.
USE INSTEAD: get_current_user if you need more than ids and names — but note that response is much larger, so prefer this one when ids suffice.
EXAMPLE: {}
RETURNS: Array of {id, name}. Nothing else, and no paging.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, but the description adds useful behavioral details: 'Takes no arguments', 'Nothing else, and no paging', and emphasizes minimal cost. These go beyond the annotations and fully disclose the operation's scope.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly structured with USE WHEN, USE INSTEAD, EXAMPLE, and RETURNS sections. Every sentence adds value, the core purpose is front-loaded, and there is zero fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple no-param read-only list tool, the description fully covers usage context, output shape, and exclusions (no paging). It also names the alternative, making it self-contained. Nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With zero parameters and 100% schema coverage, the description explicitly confirms 'Takes no arguments', removing any doubt. This is more than the baseline for zero-param tools.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action ('List'), the resource ('every workspace you belong to'), and the exact output scope ('as id and name only'). It differentiates from siblings by focusing solely on workspace ID/name retrieval, and even names the alternative get_current_user.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit 'USE WHEN' context (cheapest way to resolve workspace name to ID) and an explicit 'USE INSTEAD' alternative (get_current_user) with trade-off rationale (response size). This leaves no ambiguity about when to choose this tool.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_action_itemsARead-only
List action items belonging to one container — a conversation, a folder, or home.
USE WHEN: You have a container_id and want its action items. container_type is channel for a conversation, folder for a folder, or home. A conversation id is NOT a folder id — resolve container_id with the tool matching your container_type. Filter by status or assigned_to (pass the string null for unassigned).
USE INSTEAD: list_my_action_items for everything assigned to you regardless of where it lives.
FIRST: container_id comes from list_conversations (field results[].id) when container_type is channel — call it first if you don't have one.
FIRST: container_id comes from get_root_folders (field results[].id) when container_type is folder — call it first if you don't have one.
EXAMPLE: {"container_type":"channel","container_id":"conv-abc","status":"todo"}
RETURNS: Same as list_my_action_items: {results: [...], total?, results_count?, has_more?, next_cursor?, filters?}.
NARROW: pass response_fields ["results.id","results.title","results.status","results.assigned_to"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | Anchor timestamp to start from. Uses updated_at. Ignored when starting_after/ending_before is provided. | |
| limit | No | Number of items to return (min: 1, max: 100) | |
| status | No | ||
| direction | No | newer | |
| assigned_to | No | Filter by assigned_to (user_id). Use 'null' to filter by unassigned items. | |
| container_id | Yes | ||
| ending_before | No | Cursor to fetch the previous page (use value from previous_cursor). | |
| container_type | Yes | ||
| starting_after | No | Cursor to fetch the next page (use value from next_cursor). | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the agent knows this is safe. The description adds valuable context: filtering by status or assigned_to (with 'null' for unassigned), the RETURN format matching list_my_action_items, and a performance tip to use response_fields to shrink payload. It doesn't clarify pagination behavior beyond cursors, but annotations cover safety, and the description adds meaningful usage behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, NARROW). It's front-loaded with the core purpose and uses bold headings for scannability. Every line adds value—no fluff, and it maintains brevity despite the density.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (10 params, multiple container types), the description covers all essential aspects: how to get container_id, how to filter, pagination cursors, return shape, and optimization tips. The output schema is absent, but the description explicitly states the return format. It's complete for an agent to call correctly without external lookup.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With schema coverage at 60%, the description compensates by explaining key non-schema-covered semantics: container_type and container_id are identified as the required pair, with specific values for container_type, and it notes that a conversation id is not a folder id. It also clarifies the use of the 'null' string for assigned_to filtering, which is in the schema but reinforced. The description doesn't cover all parameters, but adds significant meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists action items for a specific container, with explicit examples of container_type values (channel, folder, home). It distinguishes itself from the sibling list_my_action_items by specifying scope (one container vs. all assigned to user). The verb 'list' and resource 'action items' are specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit 'USE WHEN' and 'USE INSTEAD' instructions, naming the alternative tool list_my_action_items and the condition for choosing it. It provides clear prerequisites ('FIRST' sections) explaining how to obtain container_id for each container type, and includes an example call. This is comprehensive routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_ai_actionsARead-only
List the AI Actions (Prompts) available to you — each has an id usable as prompt_id.
USE WHEN: Before calling run_ai_action or summarize_conversation, to find a prompt_id. Also to show the user which AI Actions exist. Filter by owner_type (user = your own, workspace = shared, system = Carbon Voice built-ins).
USE INSTEAD: get_ai_action_responses if you want results that were already generated rather than the list of available actions.
EXAMPLE: {"owner_type":"system"}
RETURNS: Array of {id, name, description?, prompt, owner_type, workspace_id?, response_format?, created_at, last_updated_at}. Use id as prompt_id elsewhere.
NARROW: pass response_fields ["id","name","description","owner_type"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| owner_type | No | ||
| workspace_id | No | Limit to AI Actions owned by this workspace, from `get_workspaces_basic_info`. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description is consistent. It adds valuable behavioral context by detailing the return structure (array of specific fields) and warns that the full payload is much larger, advising response_fields to narrow it. This goes beyond annotations and helps the agent understand response size and how to control it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW) and every sentence adds essential information. It is front-loaded with the core purpose and routes to alternatives early. No redundancy or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple list tool with no output schema, the description fully covers the return format, filtering options, example usage, and performance guidance. It leaves nothing an agent needs to call the tool correctly, including how to handle the potentially large payload.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 67%, and the description compensates by explaining owner_type (which only has an enum in the schema) with the meaning of each value (user, workspace, system) and providing an example. It also explains the purpose of response_fields as a dot-path allowlist to shrink the response. Workspace_id is already documented in the schema, so the description adds value for the remaining parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool lists AI Actions (Prompts) and explicitly distinguishes it from get_ai_action_responses, which retrieves already-generated results. It provides a specific verb-resource pair and names the sibling it is not, making selection unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit when-to-use guidance: before calling run_ai_action or summarize_conversation to find a prompt_id, and to show available AI Actions. It also gives a clear alternative (get_ai_action_responses) and explains the filtering by owner_type with an example, leaving no inference required.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_conversationsARead-only
List your conversations from the last 6 months, optionally filtered by participants, type and name.
USE WHEN: Finding a conversation_id. Filter with user_ids plus match, types and name — YOUR DM with someone is user_ids: ["<their id>"], types: ["directMessage"]. You are always an implicit participant, so never pass your own ID. Filters AND together; if several match, ask which was meant.
USE INSTEAD: get_conversation when you already have an ID and want full detail — this returns only id, name, workspace_id and type.
EXAMPLE: {"user_ids":["user-abc"],"types":["directMessage"]}
RETURNS: {results_count, results: [{id, name, workspace_id, type}]} where type is directMessage | customerConversation | namedConversation | asyncMeeting. No paging: results_count is what is returned, after filtering. name adds unfiltered_count: rows the name was matched against, AFTER user_ids/types. ALWAYS check it before saying no such conversation exists. 0 of 47 means none matched that string (misspelt, or >6 months old); 0 of 0 means your other filters left nothing — not that the caller has none. Widen the filters or ask; never report it does not exist.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | Keep only conversations whose name contains this string, case-insensitively. When nothing matches, check `unfiltered_count` before reporting no such conversation — it says how many existed to match against. Several matches means ask which was meant. | |
| match | No | Match mode for `user_ids`: `any` (union, default) or `all` (intersection). YOU are always included implicitly — `user_ids: ["u1"]` already means conversations containing you and u1, so never pass your own ID. Doing so under `any` matches every conversation you are in and silently discards the filter. | |
| types | No | Keep only these conversation types. `directMessage` is the 1:1 with someone — combine with `user_ids` to find your DM with a person. `namedConversation` is a conversation somebody named. Omit for all types. | |
| user_ids | No | List of user IDs to filter conversations by. When omitted, all conversations for the caller are returned. Requires actual user IDs, not usernames or display names. If you only have a person's name, call `search_users` first (e.g. `names: ["Brett"]`) to resolve it to a user ID. If `search_users` returns more than one candidate for a name, ask the caller which person they meant instead of guessing. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds substantial behavioral detail beyond that: the 6-month retention window, implicit participant semantics, filter AND-ing, the meaning of unfiltered_count, and the absence of paging. These details are critical for correct invocation and interpretation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear section labels (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS) and front-loads the core purpose. Despite its length, each sentence contributes operational guidance rather than repetition, and the format makes it easy for an agent to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers the tool's complexity: return shape, type enum, count semantics, edge cases (0 of 47 vs 0 of 0), and when to ask the caller for clarification. Since there is no output schema, the description correctly carries the responsibility of explaining the response and filtering behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema description coverage is 100%, the description adds meaning the schema alone does not fully convey: the agent is always an implicit participant, passing the caller's own ID silently breaks filtering, and user_ids must be resolved via search_users before use. It also supplies a concrete JSON example that models correct parameter usage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The first sentence states a specific verb ('List'), resource ('your conversations'), time scope ('last 6 months'), and optional filters. It also explicitly contrasts itself with get_conversation, saying it returns only id, name, workspace_id and type, which makes its identity unambiguous among siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit 'USE WHEN' and 'USE INSTEAD' guidance, names the alternative tool, and explains the exact conditions for choosing between them. It also instructs the agent on what to do when filters produce multiple matches or when unfiltered_count suggests a nonexistent conversation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_inbox_notificationsARead-only
List your inbox notifications, with a total unread count.
USE WHEN: Answering "what did I miss" or "where was I mentioned" — pass category: "mentions" for mentions. The response includes total_unread, so you can report a count without paging.
USE INSTEAD: search_message_ids with notified_status if you want the messages themselves rather than notification records. search_messages_by_heard_status for unlistened messages.
EXAMPLE: {"category":"mentions","limit":25}
RETURNS: {results: [...], total_results, total_unread, filters}. Pages with skip/limit, not cursors.
NARROW: pass response_fields ["results","total_unread","total_results"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | Anchor as a UNIX timestamp in MILLISECONDS (e.g. 1694313500106) — not an ISO string. | |
| skip | No | Offset for paging. This endpoint uses skip/limit, not cursors. | |
| type | No | Narrow to a single notification type (e.g. `channel.users.added`). Prefer `category` unless you know the exact type string. | |
| limit | No | Number of notifications to return (max 100). | |
| category | No | Notification category. `mentions` is the one to use for "where was I mentioned". | |
| direction | No | Direction to walk from the anchor. | newer |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses pagination behavior (skip/limit, not cursors), the presence of total_unread, the response shape, and the large payload cost with a NARROW recommendation. This adds meaningful operational context that annotations alone do not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with labeled sections (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW), front-loads the core purpose, and every section earns its place. It packs substantial routing and response information into a compact, scannable format.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having no output schema, the description explains the return structure, unread count, pagination model, and payload optimization. Combined with a fully described input schema, it gives an agent everything needed to select and invoke this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is already 100%, so the baseline is 3. The description adds value by telling agents to pass category: 'mentions' for mention queriesтся and to pass response_fields ['results','total_unread','total_results'] to shrink the payload, which is practical guidance beyond schema definitions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'List your inbox notifications, with a total unread count.' It clearly distinguishes itself from message-search siblings by emphasizing notification records rather than messages, so an agent can tell it apart from search_message_ids and search_messages_by_heard_status.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'USE WHEN' explicitly maps common user intents like 'what did I miss' to this tool, including the category parameter for mentions. 'USE INSTEAD' names the exact alternative tools and the conditions under which they should be preferred, giving agents clear routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_messagesARead-only
List messages with filtering by date, conversation, folder, workspace, creator or language.
USE WHEN: The general-purpose message reader; full bodies incl. transcript and AI summary. Max date span 183 days. user_ids filters by SENDER — for messages exchanged WITH someone, pass conversation_id from list_conversations. Use presigned URLs as-is.
USE INSTEAD: get_recent_messages for a quick look at the latest few (hard-capped at 10, no paging). search_message_ids for filters this cannot express — notified state, mentions or labels.
FIRST: workspace_id comes from get_workspaces_basic_info (field id) — call it first if you don't have one.
EXAMPLE: {"workspace_id":"ws-abc","start_date":"2026-09-01T00:00:00Z","size":25}
RETURNS: {page, size, sort_direction, total, results_count, has_next_page, filters, results: [{id, transcript?, ai_summary?, audio_url?, creator_id, conversation_id?, duration_ms, reply_count, status, type, created_at, ...}]}. Page on has_next_page/total — do not guess.
NARROW: pass response_fields ["total","has_next_page","results.id","results.transcript","results.created_at"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | 1-based page number. Use with `size`; check `has_next_page` in the response. | |
| size | No | Max number of results per page is: **50** | |
| type | No | Type (optional) | |
| end_date | No | End Date range | |
| language | No | Language (optional) | |
| user_ids | No | Filter by message CREATOR (sender) — matched against `creator_id`, not participants. Requires user IDs from `search_users`, not names. To find messages exchanged WITH someone, resolve the conversation via `list_conversations` and pass `conversation_id` instead. | |
| folder_id | No | Folder ID (optional) | |
| start_date | No | Start Date range | |
| workspace_id | No | Workspace ID (optional) | |
| sort_direction | No | The field used to sort results is **Creation Date** | DESC |
| conversation_id | No | Conversation ID (optional) | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond readOnlyHint=true/destructiveHint=false, the description discloses the 183-day max date span, that user_ids filters by SENDER (matched against creator_id, not participants), the pagination contract ('Page on has_next_page/total — do not guess'), and that the full payload is much larger, warranting response_fields narrowing. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Longer than typical but every section earns its place given 12 params and ~39 siblings. The sectioned format (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, NARROW) is scannable and front-loaded with the core purpose. Slight density cost, hence not a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, and the description compensates fully by inlining the return structure, pagination guidance, and payload-shrinking advice. Covers setup prerequisites, usage routing, and behavioral constraints for a complex 12-param tool with zero required parameters.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3. The description adds genuine value on top: the user_ids semantics (filters by sender/creator_id, requires IDs from search_users, resolve conversations for the WITH-someone case), the origin of workspace_id, and the response_fields dot-path usage. These go beyond what the schema states, earning a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb and resource plus explicit filter dimensions ('date, conversation, folder, workspace, creator or language'). Differentiates from siblings by naming get_recent_messages and search_message_ids and what distinguishes them. The USE INSTEAD section preempts the agent picking the wrong sibling.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit USE WHEN / USE INSTEAD routing: get_recent_messages for a quick capped look (hard-capped at 10, no paging) and search_message_ids for filters this cannot express (notified state, mentions, labels). Also instructs FIRST to resolve workspace_id from get_workspaces_basic_info, covering the prerequisite an agent would otherwise have to infer.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_my_action_itemsARead-only
List your action items across every conversation and folder: those assigned to you, PLUS unassigned ones you created.
USE WHEN: Answering "what do I owe / what is on my plate" — but check assigned_to before calling something the user's own commitment: a null one is an item they raised that nobody has picked up. Filter by status (todo open, suggested AI-proposed, done complete). Page with starting_after.
USE INSTEAD: list_action_items when you want one specific conversation or folder rather than everything of yours.
EXAMPLE: {"status":"todo","limit":25}
RETURNS: {results: [{id, title, status, notes_text?, assigned_to?, due_date?, container_id?, container_type?, source_message_id?, creator_id, ...}], total?, results_count?, has_more?, next_cursor?, filters?}. Keep paging while has_more is true, passing next_cursor as starting_after.
NARROW: pass response_fields ["results.id","results.title","results.status","results.assigned_to","results.due_date"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| date | No | Anchor timestamp to start from. Uses updated_at. Ignored when starting_after/ending_before is provided. | |
| limit | No | Number of items to return (min: 1, max: 100) | |
| status | No | ||
| direction | No | newer | |
| ending_before | No | Cursor to fetch the previous page (use value from previous_cursor). | |
| starting_after | No | Cursor to fetch the next page (use value from next_cursor). | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, and the description adds rich context: return payload shape, pagination loop with has_more/next_cursor/starting_after, status value meanings, and the null assigned_to caveat. It also gives a concrete example, going well beyond structured annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Although long, the description is structured with clear labels (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW) and front-loads the core purpose. Every section contributes actionable guidance with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully specifies return fields, pagination behavior, and filtering semantics. It covers the main use case, common edge case (null assigned_to), and performance tip, making it self-sufficient for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 71%, and the description compensates by explaining status enum values (todo, suggested, done), pagination via starting_after, and response_fields narrowing with a performance rationale. It doesn't repeat what schema already says about date/limit/direction, but adds meaningful value.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'List your action items across every conversation and folder', with clear inclusion criteria (assigned to you PLUS unassigned ones you created). It also distinguishes itself from list_action_items by scope, so an agent can tell them apart.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly provides USE WHEN and USE INSTEAD sections. It tells the agent when to call this tool (what do I owe / what is on my plate), warns about null assigned_to semantics, and names list_action_items as the alternative for a specific conversation/folder.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
move_folderADestructive
Move a folder into another folder, or up to a workspace root.
USE WHEN: Relocating a folder. Pass folder_id for a new parent folder, or workspace_id to move it to the workspace root — one or the other, not both.
USE INSTEAD: update_folder_name to rename in place. move_message_to_folder for a single message rather than a folder.
FIRST: id comes from get_root_folders (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"folder-abc","folder_id":"folder-parent"}
RETURNS: The moved folder, same shape as get_folder.
ERROR BAD_REQUEST: Both folder_id and workspace_id were given, or neither, or the move would nest a folder inside itself. — Pass exactly one destination.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| folder_id | No | Folder ID | |
| workspace_id | No | Workspace ID |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare destructiveHint=true and readOnlyHint=false, so the mutation risk is known. The description adds valuable behavioral context: the exclusivity constraint (one destination, not both), the self-nesting error condition, and the return shape. It doesn't detail side effects on child folders, but the provided context is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact, front-loaded with the core action, and every section earns its place: use-when, alternatives, prerequisite, example, and error handling. No filler or repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter mutation tool with no output schema, the description covers the key decision (which destination parameter), the prerequisite call, the error case, and the return shape. An agent has enough to invoke it correctly without additional lookups.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 67%, and the description compensates by explaining the semantic difference between `folder_id` and `workspace_id`, including the 'one or the other, not both' rule. It also clarifies where `id` comes from. This adds meaning beyond the bare schema descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource ('Move a folder into another folder, or up to a workspace root') and clearly distinguishes it from siblings like `update_folder_name` and `move_message_to_folder`. The purpose is unambiguous and immediately actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'USE WHEN' section explicitly states when to use the tool, and 'USE INSTEAD' names alternatives with conditions. It also provides a 'FIRST' prerequisite and an example, giving an agent complete routing and sequencing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
move_message_to_folderADestructive
Move a message into a folder, or out to a workspace.
USE WHEN: Filing a memo. Only voicememo/prerecorded messages you created can be moved, and the message type must match the folder's type. Pass exactly one of folder_id or workspace_id.
USE INSTEAD: move_folder to relocate a whole folder. create_voicememo_message with folder_id to file a memo at creation time instead of moving it after.
FIRST: message_id comes from list_messages (field results[].id) — call it first if you don't have one.
EXAMPLE: {"message_id":"msg-abc","folder_id":"folder-abc"}
RETURNS: The updated message with its new placement.
ERROR BAD_REQUEST: The message type is not voicememo/prerecorded, it does not match the destination folder's type, or both/neither destination was given. — Check type via get_message and the folder type via get_folder; they must match. Pass exactly one destination.
| Name | Required | Description | Default |
|---|---|---|---|
| folder_id | No | Destination folder. Pass exactly one of `folder_id` or `workspace_id` — both together, or neither, is rejected. | |
| message_id | Yes | Message to move. Only `voicememo` and `prerecorded` messages can be moved, you must be the message creator, and the message type must match the destination folder's type (a voicememo cannot go into a prerecorded folder). | |
| workspace_id | No | Destination workspace, to take the message out of any folder. Pass exactly one of `folder_id` or `workspace_id` — both together, or neither, is rejected. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate destructiveHint=true, and the description enriches this by detailing the mutation constraints: only certain message types, creator requirement, type matching, and rejection of both/neither destination. It also explains the return value and common error cause with remediation steps. This goes well beyond the annotation, which is appropriate for a destructive action.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear headers (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, ERROR) and every sentence serves a purpose. It is concise despite covering multiple aspects, with no redundancy. The action is front-loaded and the format aids agent parsing.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-parameter mutation tool with no output schema, the description is remarkably complete. It covers prerequisites, how to obtain a required ID, the error condition and how to diagnose it, and the return value. Given the complexity and sibling interactions, nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with detailed parameter descriptions, so the baseline is 3. The description adds an example showing the correct combination and a FIRST note about where message_id comes from (list_messages), which helps clarify the practical semantics of the parameters. This additional context justifies a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the action: moving a message into a folder or out to a workspace. It distinguishes itself from siblings by naming move_folder and create_voicememo_message as alternatives. The specific constraints on message types and destination rules make the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit USE WHEN and USE INSTEAD sections provide clear conditions for this tool vs alternatives. It states when to use (filing a memo), prerequisites (voicememo/prerecorded, creator, type match), and the exactly-one-destination rule. This is exemplary routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
run_ai_actionA
Run an AI Action (Prompt) against one or more specific messages.
USE WHEN: You have concrete message_ids and a prompt_id, and want the AI Action applied to exactly those messages.
USE INSTEAD: summarize_conversation if you want a whole conversation summarized and would otherwise have to list its messages yourself — it does that selection for you.
FIRST: prompt_id comes from list_ai_actions (field id) — call it first if you don't have one.
FIRST: message_ids comes from list_messages (field results[].id) — call it first if you don't have one.
EXAMPLE: {"prompt_id":"prompt-abc","message_ids":["msg-1","msg-2"],"language":"english"}
RETURNS: {id, prompt_id, message_ids, creator_id, channel_id?, workspace_id?, responses: [{language, text?, markdown?, html?, json?}], created_at}. The generated output is in responses[], one entry per language.
ERROR BAD_REQUEST: prompt_id or one of message_ids is not a valid ID. — Call list_ai_actions for valid prompt_id values and list_messages for valid message IDs; do not retry with the same IDs.
| Name | Required | Description | Default |
|---|---|---|---|
| language | No | The language of the response. Defaults to original message. | |
| prompt_id | Yes | AI Action ID, from `list_ai_actions` (its `id`). | |
| channel_id | No | Conversation the messages belong to, from `list_conversations`. | |
| message_ids | Yes | Message IDs, from `list_messages` (`results[].id`). | |
| workspace_id | No | Workspace scope, from `get_workspaces_basic_info`. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. | |
| ignore_existing_response | No | Whether to ignore existing response and generate a new one. Defaults to false. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint=false, destructiveHint=false, and openWorldHint=false. The description does not contradict these; it adds useful behavioral context: the return shape (`RETURNS: {id, prompt_id, message_ids, ...}`), that output is in `responses[]` per language, and a specific error condition (BAD_REQUEST) with guidance not to retry with the same IDs. This adds value beyond the annotations without overloading.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is structured with clear sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, ERROR), which makes it scannable and front-loads the core purpose. It is somewhat long but every section adds information an agent needs; no filler. The example and error handling justify the length.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 7 parameters, 2 required, and no output schema, the description covers the return format, error conditions, and where to obtain IDs. It also hints at optional parameters like `channel_id` and `workspace_id` through the return shape, but the schema already covers those. The description is sufficient for an agent to call it correctly without additional lookups.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so each parameter already has a description. The tool description adds extra meaning by explaining where to source `prompt_id` and `message_ids` (from `list_ai_actions` and `list_messages`), providing a concrete example with all fields, and clarifying the default behavior of `language`. This goes beyond the schema's basic descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Run an AI Action') against a specific resource ('one or more specific messages'), and explicitly differentiates itself from the sibling tool `summarize_conversation` by naming it and the condition that selects it. This makes the purpose unmistakable and distinguishes it from alternatives without needing to inspect schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit USE WHEN and USE INSTEAD directives, naming `summarize_conversation` as the alternative for whole-conversation summaries. It also gives FIRST instructions for obtaining `prompt_id` and `message_ids` from `list_ai_actions` and `list_messages`, which is actionable guidance for an agent. This fully covers when to use this tool versus siblings.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_message_idsARead-only
Find message IDs by notified state, mentions, labels, creator, conversation or workspace — returning IDs plus cursor metadata.
USE WHEN: Any filter list_messages cannot express: whether you were notified (notified_status), whether you were tagged (tagged_user_ids), or by label_ids. Cheap in tokens because it returns IDs only — hydrate the ones you need with get_message.
USE INSTEAD: list_messages when a date range, conversation or workspace filter is all you need and you want full message bodies in one call. search_messages_by_heard_status for unread/listened state, which this tool cannot filter on.
EXAMPLE: {"notified_status":"notified","limit":50}
RETURNS: {ids: [{...}], has_more, next_cursor?}. Keep paging while has_more is true, passing next_cursor back as next_cursor.
ERROR BAD_REQUEST: An ID list contains names rather than IDs, or exceeds 50 entries. — Resolve people to IDs with search_users and conversations with list_conversations; split lists longer than 50.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of message IDs to return (max 100). | |
| has_notes | No | Filter by whether the message has notes attached. | both |
| label_ids | No | Only messages carrying these labels. Max 50 entries. Requires IDs, not names. | |
| creator_ids | No | Only messages created by these users. Max 50 entries. Requires IDs, not names. | |
| next_cursor | No | Pass the `next_cursor` from a previous response to fetch the next page. | |
| workspace_ids | No | Only messages in these workspaces. Max 50 entries. Requires IDs, not names. | |
| sort_direction | No | Direction to walk from the anchor. | newer |
| notified_status | No | Filter by whether you were notified about the message. `notified` = messages you were notified about, `not_notified` = the rest, `both` = no filter. | both |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. | |
| tagged_user_ids | No | Only messages where these users were tagged/mentioned. Max 50 entries. Requires IDs, not names. | |
| conversation_ids | No | Only messages in these conversations. Max 50 entries. Requires IDs, not names. | |
| created_or_updated_at | No | ISO 8601 timestamp anchor; a UTC `Z` suffix or a numeric offset both work. Combined with `sort_direction` to page backwards or forwards from a point in time. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint and non-destructive behavior. The description adds meaningful behavioral detail: it returns IDs only and is token-cheap, pagination follows has_more/next_cursor, and BAD_REQUEST causes are explained with remediation steps. This goes well beyond what the annotations convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections: purpose, USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, and ERROR. It is front-loaded with a one-sentence purpose statement, and every section earns its place by carrying actionable guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Even without an output schema, the description provides the return envelope and pagination contract, error handling guidance, and explicit sibling alternatives. Combined with fully documented schema parameters and read-only annotations, an agent has everything it needs to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds value by explaining which parameter groups matter (notified, tagged, label filters), how next_cursor works for paging, and that ID lists require IDs rather than names with a 50-entry limit. Most parameter-level detail still lives in the schema, but the description supplements it usefully.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Find message IDs by notified state, mentions, labels, creator, conversation or workspace — returning IDs plus cursor metadata.' It immediately differentiates the tool from siblings like list_messages and search_messages_by_heard_status, so an agent can understand what this tool uniquely does.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The USE WHEN and USE INSTEAD sections give explicit routing guidance: use this for filters list_messages cannot express (notified_status, tagged_user_ids, label_ids), and use list_messages when full bodies are needed, or search_messages_by_heard_status for heard state. This leaves little ambiguity about when to select this tool over alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_messages_by_heard_statusARead-only
Find messages by whether you have listened to them, and get per-conversation unheard counts.
USE WHEN: "What have I not listened to yet" / "catch me up". heardStatus: "unheard" is the unread filter. The response also carries unheard_counts_by_channel, so you can prioritise conversations without fetching their messages.
USE INSTEAD: search_message_ids for notified state, mentions or date anchors — this tool accepts no date filter (see note below). list_messages for plain recent history.
EXAMPLE: {"heardStatus":"unheard","limit":25}
RETURNS: {messages: [{message_guid, creator_guid, creator_first_name, channel_guids, transcript_txt, message_ts, heard_status, ...}], unheard_counts_by_channel: {conversation_id: count}, success}. Use unheard_counts_by_channel to decide where to look first.
NARROW: pass response_fields ["unheard_counts_by_channel","messages.message_guid","messages.channel_guids","messages.creator_first_name","messages.transcript_txt","messages.message_ts"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Number of messages to return (max 100). | |
| user_guids | No | Only messages created by these users. Max 50 entries. Requires IDs, not names. | |
| heardStatus | No | Filter by listened state. `unheard` = not yet listened to (your unread messages), `heard` = already listened to, `any` = no filter. | unheard |
| label_guids | No | Only messages carrying these labels. Max 50 entries. Requires IDs, not names. | |
| channel_guids | No | Only messages in these conversations. Max 50 entries. Requires IDs, not names. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. | |
| tagged_user_guids | No | Only messages where these users were tagged/mentioned. Max 50 entries. Requires IDs, not names. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds useful context beyond annotations: the return shape, the unheard_counts_by_channel aggregation, and the warning that the full payload is much larger, prompting the NARROW guidance. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with purpose, then uses clear labeled sections (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW). Every section earns its place and the format aids scanning. Length is justified by the tool's complexity, with no redundant sentences.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter tool with no output schema, the description covers the key gaps: usage context, alternatives, response shape, and payload optimization. The one limitation (no date filter) is disclosed. It stops short of describing pagination or interaction with limit, but the schema already documents limit's range and default, so the combination is adequate.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so parameters are already fully documented. The description adds real value by recommending a specific response_fields dot-path allowlist, demonstrating with an example how to shrink the payload. It also reinforces that heardStatus='unheard' is the unread filter. This goes beyond mere schema repetition.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find messages by whether you have listened to them') plus a distinct capability not implied by the name: per-conversation unheard counts. It differentiates from siblings by naming search_message_ids and list_messages as alternatives. The purpose is unambiguous and actionable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The USE WHEN section explicitly frames the intended scenario ('What have I not listened to yet' / 'catch me up') and flags the unread filter. USE INSTEAD names exact alternatives (search_message_ids for notified state/mentions/date anchors; list_messages for plain history) and discloses a key limitation: no date filter. This is exemplary when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_userARead-only
Find a single user by email, phone or name.
USE WHEN: Resolving ONE person. Supply exactly one of email, phone or name. Name search only matches your own contacts.
USE INSTEAD: search_users for several people in one call — it takes arrays and saves a round trip per person. get_user when you already have the ID.
EXAMPLE: {"email":"someone@example.com"}
RETURNS: {id, full_name, first_name, last_name?, link, image_url?, languages?, ...}. Use id wherever another tool asks for a user ID.
ERROR NOT_FOUND: Nobody matched, or a name search hit a non-contact. — Try an email or phone instead of a name; name search is limited to your contacts.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | The name of the user to search for (Only users that are part of your contacts will be returned) | |
| No | Email Address | ||
| phone | No | Phone Number |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and destructiveHint=false, so the safety profile is covered. The description adds valuable behavioral details beyond the schema: name search only matches contacts, the exact return shape, the instruction to use id in other tools, and NOT_FOUND error semantics with recovery guidance. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is organized with clear labels (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, ERROR) and front-loaded with the primary purpose. Every section serves a distinct purpose: routing, constraints, example, output format, and error handling. No redundant or filler content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no output schema, the description compensates by specifying the return fields and the NOT_FOUND error behavior. It covers prerequisites, parameter constraints, alternatives, output usage, and error recovery, making the tool fully callable by an agent without further inference. The annotations cover safety, and the description covers the rest.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Although schema coverage is 100%, the description adds crucial semantics: 'Supply exactly one' enforces exclusivity among the three parameters, and the note that name search only matches contacts clarifies the meaning of the name parameter beyond its schema description. The example also demonstrates a valid parameter combination.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find a single user by email, phone or name') and differentiates from siblings like search_users (plural) and get_user (by ID). The phrase 'Find a single user' immediately signals scope and distinguishes the tool from alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit USE WHEN conditions ('Resolving ONE person', 'Supply exactly one of email, phone or name') and USE INSTEAD alternatives with rationale (search_users for arrays, get_user when ID is known). The contact-only limitation for name search is also clearly stated, giving the agent actionable routing logic.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_usersARead-only
Resolve several users at once by emails, phones, IDs or names.
USE WHEN: Turning a list of people into user IDs in one call — the right first step before any tool that takes user IDs. Name search only matches your contacts.
USE INSTEAD: search_user for a single lookup. get_user for a full profile once you have the ID.
EXAMPLE: {"names":["Brett"]}
RETURNS: Array of {id, full_name, first_name, last_name?, link, languages?, ...}. If a name returns more than one candidate, ask which person was meant rather than guessing.
NARROW: pass response_fields ["id","full_name"] unless you need more — the full payload is much larger.
| Name | Required | Description | Default |
|---|---|---|---|
| ids | No | User IDs | |
| names | No | The names of the users to search for (Only users that are part of your contacts will be returned) | |
| emails | No | Email Addresses | |
| phones | No | Phone Numbers | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint/openWorldHint annotations, the description adds genuinely useful behavioral context: 'Name search only matches your contacts' reveals a filtering quirk, and 'If a name returns more than one candidate, ask which person was meant rather than guessing' prescribes handling for ambiguity. The NARROW note about payload size also discloses performance characteristics.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Every labeled section (USE WHEN, USE INSTEAD, EXAMPLE, RETURNS, NARROW) earns its place, and the primary purpose is front-loaded in the first sentence. The labeled format makes scanning easy, and despite the length, there is zero filler or repetition of schema content.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 5 optional params and no output schema, the description covers everything an agent needs: what it returns (array of user objects with optional fields), how to disambiguate results, how to scope names, and how to shrink the payload. Combined with annotations covering the safety profile, no critical operational detail is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds value beyond the schema: the EXAMPLE `{"names":["Brett"]}` demonstrates the argument shape, and the NARROW line explains exactly how response_fields should be used ('pass response_fields ["id","full_name"] unless you need more'). This goes beyond the schema's terse 'Dot-path allowlist' wording.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line, 'Resolve several users at once by emails, phones, IDs or names,' names a specific verb, resource, and scope explicitly. The 'at once' phrasing immediately distinguishes it from the sibling `search_user` (single lookup), so an agent can tell them apart without inspecting their schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The USE WHEN section states the exact trigger condition ('Turning a list of people into user IDs in one call') and even names the alternative routes: `search_user for single lookup, get_user for a full profile once the ID is known. This is explicit when/when-not guidance with named alternatives — nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
set_action_item_statusADestructive
Move an action item between suggested, todo and done.
USE WHEN: Completing an item (done), reopening it (todo), or accepting an AI-suggested item by promoting it from suggested to todo.
USE INSTEAD: update_action_item for title, notes, assignee or due date; this tool only sets status.
FIRST: id comes from list_my_action_items (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"ai-abc","status":"done"}
RETURNS: The updated action item, same shape as get_action_item.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| status | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the operation is not read-only and is destructive, so the description's job is lighter. It adds useful context by clarifying that this tool only sets status, not other fields, and by stating the return value. It does not explicitly warn that a previous status is overwritten, but the status enum makes that mostly self-evident.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with labeled sections: move semantics, USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS. Every sentence earns its place, and the most important scoping detail appears before the usage guidance.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter status setter, this is complete. It covers all parameter semantics, provides a source for the id, gives usage conditions, and explains the return shape by referencing `get_action_item`. No output schema exists, but the description handles that gap sufficiently.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does thoroughly. It explains where `id` comes from with an exact field path, maps each `status` enum value to a real intent ('completing', 'reopening', 'promoting'), and provides a concrete JSON example.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description leads with a specific verb and resource: 'Move an action item between `suggested`, `todo` and `done`.' It names the exact status transitions and explicitly contrasts itself with `update_action_item`, so an agent can distinguish it from siblings without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The USE WHEN block gives explicit triggering conditions for each status change, and USE INSTEAD tells the agent to prefer `update_action_item` for other fields. The FIRST instruction even explains how to obtain `id` from `list_my_action_items`, making the calling workflow concrete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
suggest_action_items_from_messageA
Extract action items from ONE message and return them immediately.
USE WHEN: Turning a single message into tasks — "what did she ask me to do?". The items come back in this call, already saved with status: "suggested"; promote the ones you want with set_action_item_status. Prefer this over suggest_action_items_from_messages whenever there is exactly one message, since it needs no polling.
USE INSTEAD: suggest_action_items_from_messages for two or more messages — it reasons over the whole set at once and can catch commitments that span messages, which calling this tool repeatedly cannot. create_action_item when you already know the task.
FIRST: message_id comes from list_messages (field results[].id) — call it first if you don't have one.
EXAMPLE: {"message_id":"msg-1"}
RETURNS: Array of the created action items, each with id, title, assigned_to, due_date, notes_text and status: "suggested". An empty array means the model found nothing actionable. Runs the extraction inline, so expect this call to take a few seconds.
| Name | Required | Description | Default |
|---|---|---|---|
| message_id | Yes | The message to extract action items from. Exactly one — for several messages at once use `suggest_action_items_from_messages`. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only include readOnlyHint=false and destructiveHint=false, so the description carries the behavioral burden. It discloses that items are saved with status 'suggested', that the extraction runs inline (no polling), that results come back immediately, and that it may take a few seconds. These are meaningful behavioral traits beyond what annotations imply, and there is no contradiction.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with labeled sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS) and front-loads the core purpose. Every section contributes value, but it is slightly verbose (~150 words). It could be trimmed slightly without losing clarity, yet the structure makes it easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With only 2 parameters and no output schema, the description provides all necessary context: return format (array of items with fields), status behavior, alternatives, prerequisites, and expected latency. An agent has everything needed to invoke it correctly and interpret results. Nothing essential is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% for both parameters, so baseline is 3. The description adds value by explaining that message_id comes from list_messages (field results[].id) and provides an example, which goes beyond the schema. However, it does not add much about response_fields beyond what the schema already states; the added context is useful but not extensive.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'Extract action items from ONE message and return them immediately.' It clearly distinguishes from the sibling suggest_action_items_from_messages by emphasizing 'ONE message' vs. multiple, and mentions the alternative create_action_item for known tasks. The purpose is unambiguous and differentiates among siblings without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit 'USE WHEN' and 'USE INSTEAD' sections state exactly when to call this tool (single message, turning into tasks) and when to use alternatives (multiple messages use suggest_action_items_from_messages; known tasks use create_action_item). It also instructs to get message_id from list_messages first if not available. This is comprehensive and leaves no ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
suggest_action_items_from_messagesA
Queue AI extraction of candidate action items from specific messages. Runs in the background.
USE WHEN: Turning a conversation into tasks — "what did we agree to?". Pass the message_ids to analyse, then POLL list_my_action_items or list_action_items with status: "suggested" for the results, and promote the ones you want with set_action_item_status.
USE INSTEAD: suggest_action_items_from_message (singular) for a SINGLE message — it returns the items directly, with no polling. create_action_item when you already know the task and do not need it inferred — that returns the item synchronously, with an id.
FIRST: message_ids comes from list_messages (field results[].id) — call it first if you don't have one.
EXAMPLE: {"message_ids":["msg-1","msg-2"]}
RETURNS: ACKNOWLEDGEMENT ONLY — no items are returned. Extraction is queued and runs in the background, so poll a listing tool with status: "suggested" to see the results.
| Name | Required | Description | Default |
|---|---|---|---|
| message_ids | Yes | List of message IDs |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint=false, destructiveHint=false, and openWorldHint=false, but the description goes far beyond: it explicitly states that the tool 'runs in the background', that it returns 'ACKNOWLEDGEMENT ONLY', and that results must be retrieved by polling a listing tool with `status: 'suggested'`. This discloses the asynchronous behavior and the fact that no items are returned directly, which is not inferable from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS). It front-loads the critical asynchronous behavior and usage context, and every sentence serves a purpose—no filler. The example is concise and the length is appropriate for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that the tool has a single parameter, no output schema, and a background execution model, the description provides all necessary context: how to trigger, what to expect (acknowledgement only), how to retrieve results (polling with status 'suggested'), and how to obtain the input (from `list_messages`). There is no missing information an agent needs to call this tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers 100% of parameters, with `message_ids` described as 'List of message IDs'. The description adds that message IDs come from `list_messages` (field `results[].id`) and provides an example usage JSON, which goes slightly beyond the schema's basic type info. However, this is mostly supplementary; the schema already documents the parameter adequately, so a baseline 3 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose: 'Queue AI extraction of candidate action items from specific messages.' It specifies the resource ('specific messages'), the action ('extract candidate action items'), and the asynchronous nature ('Runs in the background'). The sibling `suggest_action_items_from_message` (singular) is explicitly contrasted, so an agent can distinguish them immediately.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides explicit usage guidance: 'USE WHEN: Turning a conversation into tasks' and 'USE INSTEAD: 'suggest_action_items_from_message' (singular) for a SINGLE message' and 'create_action_item when you already know the task'. It also instructs to poll `list_my_action_items` or `list_action_items` with status 'suggested' and to get `message_ids` from `list_messages` if needed. This leaves no ambiguity about when to use this tool versus alternatives.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
summarize_conversationA
Summarize a conversation by running an AI Action over its recent messages.
USE WHEN: You want a conversation summarized and have a conversation_id. Message selection is handled for you — omit message_ids and the most recent messages are used.
USE INSTEAD: run_ai_action if you already know exactly which message_ids to process, or want a non-summary AI Action.
FIRST: prompt_id comes from list_ai_actions (field id) — call it first if you don't have one.
FIRST: conversation_id comes from list_conversations (field results[].id) — call it first if you don't have one.
EXAMPLE: {"conversation_id":"conv-abc","prompt_id":"prompt-abc","limit":50}
RETURNS: Same as run_ai_action: {id, prompt_id, message_ids, responses: [{language, text?, markdown?, ...}], ...}.
ERROR BAD_REQUEST: prompt_id is not a valid AI Action ID. — Call list_ai_actions to get valid prompt_id values.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many recent messages to summarize when `message_ids` is omitted. Must be a positive integer; values above 50 are clamped to 50 (the upstream page limit). Ignored when `message_ids` is provided. | |
| end_date | No | ISO 8601 upper bound on message age; a UTC `Z` suffix or a numeric offset both work. Ignored when `message_ids` is given. | |
| language | No | Summary language. Defaults to the original message language. | |
| prompt_id | Yes | AI Action ID, from `list_ai_actions` (its `id`). | |
| start_date | No | ISO 8601 lower bound on message age; a UTC `Z` suffix or a numeric offset both work. Ignored when `message_ids` is given. | |
| message_ids | No | Specific messages to summarize. Omit to use the most recent messages, which is usually what you want — it saves a `list_messages` call. | |
| conversation_id | Yes | Conversation to summarize, from `list_conversations`. | |
| response_fields | No | Dot-path allowlist to shrink the response, e.g. ["results.id","total"]. Omit for the full payload. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations are minimal (readOnlyHint=false, no destructiveHint), so the description carries the burden. It discloses that message selection is automatic, states the return format mirrors `run_ai_action`, and documents the BAD_REQUEST error case. It does not discuss side effects like resource consumption or rate limits, but it adds meaningful behavioral context beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Structured with clear headers (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS, ERROR) that front-load the most critical information. Each section earns its place, though the text is somewhat lengthy. The structure compensates for verbosity, making it easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 8 parameters, no output schema, and moderate complexity, the description covers all essential context: prerequisites, usage conditions, an example, the exact return shape, and an error condition. An agent has everything needed to invoke the tool correctly without additional lookups.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers all parameters, but the description adds significant usage guidance: it explains the `message_ids` omission behavior (uses recent messages, saves a `list_messages` call), tells the agent to get `prompt_id` from `list_ai_actions` and `conversation_id` from `list_conversations`, and provides an example. This clearly exceeds the baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise statement of what the tool does: 'Summarize a conversation by running an AI Action over its recent messages.' It clearly names the resource (conversation) and mechanism (AI Action), and explicitly differentiates from the sibling `run_ai_action` by stating when to use one over the other. No ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit USE WHEN and USE INSTEAD directives, naming `run_ai_action` as the alternative and the condition that selects it (knowing exact message_ids or wanting a non-summary action). Also includes FIRST prerequisites for obtaining `prompt_id` and `conversation_id` from sibling tools, leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_action_itemADestructive
Change an action item's title, notes, assignee, or due date.
USE WHEN: Editing item content. Send only the fields you want changed — omitted fields are left as they are.
USE INSTEAD: set_action_item_status to move an item between todo / done / suggested; status is not editable here.
FIRST: id comes from list_my_action_items (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"ai-abc","due_date":"2026-10-01"}
RETURNS: The updated action item, same shape as get_action_item.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| title | No | New title. Omit to leave unchanged. | |
| due_date | No | Due date as an ISO 8601 timestamp. | |
| notes_text | No | New notes. Omit to leave unchanged. | |
| assigned_to | No | Assignee user ID, from `search_users`. Not a name. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate mutation (readOnlyHint=false, destructiveHint=true). The description adds crucial partial-update semantics (omitted fields are left unchanged), the return shape (same as `get_action_item`), and how to source the id. This goes beyond the annotations and is not contradictory.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear section labels (USE WHEN, USE INSTEAD, FIRST, EXAMPLE, RETURNS). Each section earns its place, the main purpose is front-loaded, and there is no unnecessary repetition or fluff.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a mutation tool with five parameters and no output schema, the description covers the purpose, usage context, prerequisites, partial-update behavior, and return format. It also names the sibling to avoid confusion. No critical information is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 80% (4 of 5 parameters have descriptions). The description reinforces the key rule that only fields to change need be sent, and the example clarifies the structure. It adds a general principle the schema does not fully articulate, elevating it above the baseline for high coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Change'), the resource ('an action item'), and lists the mutable fields (title, notes, assignee, due date). It explicitly distinguishes this from the sibling `set_action_item_status`, making the tool's purpose unambiguous even without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit 'USE WHEN' and 'USE INSTEAD' sections, clearly stating when to use this tool and when to use the alternative for status changes. It also instructs the agent to obtain the `id` from `list_my_action_items` first, a concrete prerequisite that prevents errors.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_folder_nameADestructive
Rename a folder.
USE WHEN: Changing only the name. name is the sole editable field here.
USE INSTEAD: move_folder to change where a folder sits in the tree.
FIRST: id comes from get_root_folders (field results[].id) — call it first if you don't have one.
EXAMPLE: {"id":"folder-abc","name":"Q4 planning (final)"}
RETURNS: The updated folder, same shape as get_folder.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| name | Yes | New Folder Name |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal that this is a mutating operation with destructiveHint=true. The description adds useful context by stating that only the name changes, that the updated folder is returned in the same shape as `get_folder`, and that `name` is the only editable field. It does not go into irreversibility or permissions, but the annotation plus mutation semantics cover the core behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is tightly organized with labeled sections: purpose, when to use, alternative, prerequisite, example, and return shape. Every sentence adds actionable information without unnecessary filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple two-parameter rename tool, the description covers when to use it, when not to, where to get the ID, a worked example, and the return shape. It is sufficiently complete even without an output schema.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema only documents `name` as 'New Folder Name' and leaves `id` undocumented. The description compensates by explaining that `id` comes from `get_root_folders` and providing a complete JSON example that clarifies both parameter roles.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the specific operation 'Rename a folder' and explicitly distinguishes it from `move_folder` and other sibling operations. It also clarifies that `name` is the sole editable field, leaving no ambiguity about the tool's scope.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit USE WHEN and USE INSTEAD guidance, routing agents to `move_folder` for repositioning. It also gives a FIRST directive explaining where `id` comes from and that `get_root_folders` should be called first if needed.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v2.12.0- Changed
list_conversations1 field changed- added
Input schema / properties / nameAdded value: +{ + "description": "Keep only conversations whose name contains this string, case-insensitively. When nothing matches, check `unfiltered_count` before reporting no such conversation — it says how many existed to match against. Several matches means ask which was meant.", + "type": "string" +}
35 tool updates
v2.10.0- Added
create_action_item - Changed
create_conversation_message2 fields changed- changed
Input schema / properties / links / descriptionPrevious value: -"Array of links to be attached to the message"New value: +"Link URLs to attach. Either `transcript` or `links` is required." - changed
Input schema / properties / transcript / descriptionPrevious value: -"The Message transcript will be used to generate audio using text-to-speech"New value: +"Text to speak via text-to-speech. Either `transcript` or `links` is required."
- Changed
create_direct_message1 field changed- added
Input schema / properties / to / descriptionAdded value: +"Recipients: `user_ids` (from `search_users`) and/or `emails`. At least one required."
- Added
create_message_share_link - Changed
create_voicememo_message6 fields changed- removed
Input schema / properties / audio_fileRemoved value: -{ - "description": "Audio file upload on multipart requests. Supported Formats: .mp3, .m4a, .wav, .aac, .ogg, .flac, .wma, .opus, .webm. (Overwrites transcript)" -} - added
Input schema / properties / audio_urlAdded value: +{ + "description": "Public **https** URL to an audio file to upload. Supported formats: .mp3, .m4a, .wav, .aac, .ogg, .flac, .wma, .opus, .webm. Overrides `transcript` when provided. The server fetches this URL, so it must be publicly reachable — private, loopback and link-local addresses are refused, and plain http only works for hosts the operator has allowlisted.", + "format": "uri", + "type": "string" +} - changed
Input schema / properties / folder_id / descriptionPrevious value: -"Folder ID (not allowed when workspace_id specified is different from the folder_id)"New value: +"Folder to file the memo in. The folder type must match the memo type." - changed
Input schema / properties / links / descriptionPrevious value: -"Array of links to be attached to the message"New value: +"Link URLs to attach. Max 100, and each must be a valid URL. One of `transcript`, `links` or `audio_url` is required." - changed
Input schema / properties / transcript / descriptionPrevious value: -"The Message transcript will be used to generate audio using text-to-speech"New value: +"Text to speak via text-to-speech. 2-5000 characters. Ignored when `audio_url` is provided. One of `transcript`, `links` or `audio_url` is required." - changed
Input schema / properties / workspace_id / descriptionPrevious value: -"Workspace ID (not allowed when folder_id specified is different from the folder_id)"New value: +"Workspace to place the memo in when it is not going into a folder."
- Added
delete_action_item - Added
get_action_item - Changed
get_ai_action_responses6 fields changed- added
Input schema / properties / channel_id / descriptionAdded value: +"Only responses in this conversation, from `list_conversations`." - added
Input schema / properties / date / descriptionAdded value: +"ISO 8601 anchor timestamp; pair with `direction` to page." - added
Input schema / properties / limit / descriptionAdded value: +"Max responses to return." - added
Input schema / properties / message_id / descriptionAdded value: +"Only responses about this message, from `list_messages`." - added
Input schema / properties / prompt_id / descriptionAdded value: +"Only responses generated by this AI Action, from `list_ai_actions`." - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_conversation1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_conversation_users1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_current_user2 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_folder3 fields changed- changed
Input schema / properties / date / descriptionPrevious value: -"Return only Subfolders updated based on the date and direction (must inform include_first_level_tree = true)"New value: +"Return only subfolders updated relative to this date and `direction`. Only takes effect when `include_first_level_tree` is true — otherwise it is silently ignored." - changed
Input schema / properties / direction / descriptionPrevious value: -"Direction of the results (newer or older)"New value: +"Direction of the results (newer or older). Only takes effect when `include_first_level_tree` is true — otherwise it is silently ignored." - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_folder_with_messages1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_message1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Added
get_message_share_link - Changed
get_recent_messages1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_root_folders1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Changed
get_user1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Added
list_action_items - Changed
list_ai_actions2 fields changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / workspace_id / descriptionAdded value: +"Limit to AI Actions owned by this workspace, from `get_workspaces_basic_info`."
- Changed
list_conversations5 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / matchAdded value: +{ + "description": "Match mode for `user_ids`: `any` (union, default) or `all` (intersection). YOU are always included implicitly — `user_ids: [\"u1\"]` already means conversations containing you and u1, so never pass your own ID. Doing so under `any` matches every conversation you are in and silently discards the filter.", + "enum": [ + "any", + "all" + ], + "type": "string" +} - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / typesAdded value: +{ + "description": "Keep only these conversation types. `directMessage` is the 1:1 with someone — combine with `user_ids` to find your DM with a person. `namedConversation` is a conversation somebody named. Omit for all types.", + "items": { + "enum": [ + "directMessage", + "customerConversation", + "namedConversation", + "asyncMeeting" + ], + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / user_idsAdded value: +{ + "description": "List of user IDs to filter conversations by. When omitted, all conversations for the caller are returned. Requires actual user IDs, not usernames or display names. If you only have a person's name, call `search_users` first (e.g. `names: [\"Brett\"]`) to resolve it to a user ID. If `search_users` returns more than one candidate for a name, ask the caller which person they meant instead of guessing.", + "items": { + "type": "string" + }, + "type": "array" +}
- Added
list_inbox_notifications - Changed
list_messages3 fields changed- added
Input schema / properties / page / descriptionAdded value: +"1-based page number. Use with `size`; check `has_next_page` in the response." - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Input schema / properties / user_ids / descriptionPrevious value: -"User IDs (optional). List of user IDs to filter messages by. If not provided, all users will be included."New value: +"Filter by message CREATOR (sender) — matched against `creator_id`, not participants. Requires user IDs from `search_users`, not names. To find messages exchanged WITH someone, resolve the conversation via `list_conversations` and pass `conversation_id` instead."
- Added
list_my_action_items - Changed
move_message_to_folder3 fields changed- changed
Input schema / properties / folder_id / descriptionPrevious value: -"Folder ID"New value: +"Destination folder. Pass exactly one of `folder_id` or `workspace_id` — both together, or neither, is rejected." - changed
Input schema / properties / message_id / descriptionPrevious value: -"Only allowed to add messages of type: voicememo,prerecorded"New value: +"Message to move. Only `voicememo` and `prerecorded` messages can be moved, you must be the message creator, and the message type must match the destination folder's type (a voicememo cannot go into a prerecorded folder)." - changed
Input schema / properties / workspace_id / descriptionPrevious value: -"Workspace ID"New value: +"Destination workspace, to take the message out of any folder. Pass exactly one of `folder_id` or `workspace_id` — both together, or neither, is rejected."
- Changed
run_ai_action5 fields changed- added
Input schema / properties / channel_id / descriptionAdded value: +"Conversation the messages belong to, from `list_conversations`." - added
Input schema / properties / message_ids / descriptionAdded value: +"Message IDs, from `list_messages` (`results[].id`)." - added
Input schema / properties / prompt_id / descriptionAdded value: +"AI Action ID, from `list_ai_actions` (its `id`)." - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / workspace_id / descriptionAdded value: +"Workspace scope, from `get_workspaces_basic_info`."
- Changed
run_ai_action_for_shared_link4 fields changed- added
Input schema / properties / language / descriptionAdded value: +"Response language. Defaults to the original message language." - added
Input schema / properties / prompt_id / descriptionAdded value: +"AI Action ID, from `list_ai_actions` (its `id`)." - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / share_link_ids / descriptionAdded value: +"Share link IDs, from `create_message_share_link` (its `id`)."
- Added
search_message_ids - Added
search_messages_by_heard_status - Changed
search_users1 field changed- added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +}
- Added
set_action_item_status - Added
suggest_action_items_from_message - Added
suggest_action_items_from_messages - Changed
summarize_conversation10 fields changed- added
Input schema / properties / conversation_id / descriptionAdded value: +"Conversation to summarize, from `list_conversations`." - added
Input schema / properties / end_date / descriptionAdded value: +"ISO 8601 upper bound on message age; a UTC `Z` suffix or a numeric offset both work. Ignored when `message_ids` is given." - added
Input schema / properties / language / descriptionAdded value: +"Summary language. Defaults to the original message language." - added
Input schema / properties / limit / descriptionAdded value: +"How many recent messages to summarize when `message_ids` is omitted. Must be a positive integer; values above 50 are clamped to 50 (the upstream page limit). Ignored when `message_ids` is provided." - added
Input schema / properties / limit / exclusiveMinimumAdded value: +0 - changed
Input schema / properties / limit / typePrevious value: -"number"New value: +"integer" - added
Input schema / properties / message_ids / descriptionAdded value: +"Specific messages to summarize. Omit to use the most recent messages, which is usually what you want — it saves a `list_messages` call." - added
Input schema / properties / prompt_id / descriptionAdded value: +"AI Action ID, from `list_ai_actions` (its `id`)." - added
Input schema / properties / response_fieldsAdded value: +{ + "description": "Dot-path allowlist to shrink the response, e.g. [\"results.id\",\"total\"]. Omit for the full payload.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Input schema / properties / start_date / descriptionAdded value: +"ISO 8601 lower bound on message age; a UTC `Z` suffix or a numeric offset both work. Ignored when `message_ids` is given."
- Added
update_action_item
5 tool updates
v2.8.17- Changed
create_conversation_message1 field changed- changed
Input schema / properties / from_message_type / enumPrevious value: -[ - "PreRecorded", - "NewMessage", - "Forward" -]New value: +[ + "PreRecorded", + "NewMessage", + "Forward", + "MessageId" +]
- Changed
create_direct_message1 field changed- changed
Input schema / properties / from_message_type / enumPrevious value: -[ - "PreRecorded", - "NewMessage", - "Forward" -]New value: +[ + "PreRecorded", + "NewMessage", + "Forward", + "MessageId" +]
- Changed
create_voicememo_message1 field changed- added
Input schema / properties / audio_fileAdded value: +{ + "description": "Audio file upload on multipart requests. Supported Formats: .mp3, .m4a, .wav, .aac, .ogg, .flac, .wma, .opus, .webm. (Overwrites transcript)" +}
- Changed
get_root_folders1 field changed- removed
Input schema / properties / sort_by / defaultRemoved value: -"name"
- Changed
run_ai_action1 field changed- added
Input schema / properties / ignore_existing_responseAdded value: +{ + "description": "Whether to ignore existing response and generate a new one. Defaults to false.", + "type": "boolean" +}
3 tool updates
v1.0.0- Changed
get_current_user1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
get_workspaces_basic_info1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
- Changed
list_conversations1 field changed- removed
Input schema / additionalPropertiesRemoved value: -false
28 tool updates
- First observed
add_attachments_to_message - First observed
create_conversation_message - First observed
create_direct_message - First observed
create_folder - First observed
create_voicememo_message - First observed
delete_folder - First observed
get_ai_action_responses - First observed
get_conversation - First observed
get_conversation_users - First observed
get_current_user - First observed
get_folder - First observed
get_folder_with_messages - First observed
get_message - First observed
get_recent_messages - First observed
get_root_folders - First observed
get_user - First observed
get_workspaces_basic_info - First observed
list_ai_actions - First observed
list_conversations - First observed
list_messages - First observed
move_folder - First observed
move_message_to_folder - First observed
run_ai_action - First observed
run_ai_action_for_shared_link - First observed
search_user - First observed
search_users - First observed
summarize_conversation - First observed
update_folder_name
TDQS
Scored across 42 tools
Most tools have distinct purposes, and the detailed USE INSTEAD notes clarify boundaries. However, a few pairs are easy to confuse: search_user vs search_users (singular/plural), suggest_action_items_from_message vs suggest_action_items_from_messages (singular/plural, sync/async), and get_folder vs get_folder_with_messages. These are distinguishable but require careful reading.
Names are all snake_case and mostly verb_noun, but the verbs vary widely: list, get, create, update, delete, set, move, search, run, summarize, add, suggest. Some names are quite long (run_ai_action_for_shared_link, search_messages_by_heard_status) and the verb_prefix is not uniform (search_user vs list_messages vs get_conversation). Overall readable but not a tight pattern.
42 tools is high for a single server, but the domain spans messages, conversations, folders, action items, AI actions, share links, users, and workspaces, so the scope is broad. Still, several tools are near-duplicates that could be merged (search_user/search_users, get_folder/get_folder_with_messages, the two suggest_action_items variants), which suggests the surface could be tightened to ~30 tools.
The tool set covers most lifecycle operations for the core entities: messages (create, list, get, move, attach, share), folders (create, get, update, delete, move), action items (create, list, get, update, delete, set status, suggest), and AI actions (list, run, get responses). Minor gaps: no tool to delete a conversation, no way to update a message's transcript, and no explicit tool to revoke a share link (only via error handling). These are workaroundable.
Maintenance
Related MCP Connectors
Carbon Voice MCP serves as a bridge that connects AI assistants like ChatGPT, Claude, and Cursor to a user's Carbon Voice account, turning voice messages and conversations into a private, on-demand knowledge base. It provides 28 specialized tools for comprehensive voice messaging management, including creating and sending messages, accessing conversation history with instant transcription, running AI actions (summarization, TLDR generation, meeting notes), and managing workspace collaboration through folders, contacts, and team communications.
Unified inbox MCP for WhatsApp, Telegram, Email, voice — read/send messages, search, AI agents.
Send and schedule SMS and WhatsApp messages, manage contacts and templates, and track delivery.
Send SMS/MMS, manage contacts, and read campaigns, messages and media on SimpleTexting.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI assistants to read, search, and send iMessages with features like contact name resolution, session grouping, and attachment listing. It provides intent-aligned tools to efficiently navigate conversation history and manage messages through natural language queries.6MIT
- AlicenseAqualityDmaintenanceManage voice AI agents, make calls, run campaigns, and control phone numbers through natural language.5813 npm1MIT
- AlicenseBqualityBmaintenanceEnables reading, searching, sending iMessages, viewing image attachments, and managing conversations, with full support for modern macOS attributedBody parsing.141MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents on macOS to securely read and search the local Messages database, catch up on missed messages via a persistent inbox, and send texts or files to allowlisted chats, with optional voice note transcription and text-to-speech.MIT