ctm-number-provisioner
OfficialClick 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., "@ctm-number-provisionerSearch for available tracking numbers in area code 415 and show me a dry-run purchase plan."
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.
ctm-number-provisioner
A small, standalone MCP server that lets an AI assistant search for, buy, and configure CallTrackingMetrics (CTM) tracking numbers using your own CTM API credentials.
It exposes six tools: identify the account, search available numbers, buy numbers (exact numbers or "any N in this area code"), list routing targets, apply a name / tracking source / call route to one or many numbers, and release numbers.
It is a plain Python package with no internal dependencies. It talks only to the
public CTM API at https://api.calltrackingmetrics.com/api/v1.
Contents
Related MCP server: AgentPhone MCP Server
Safety model
Buying phone numbers costs money and cannot be undone except by releasing the number. This server is built so an assistant cannot spend your money or touch the wrong account by accident:
buy_numbersdefaults todry_run=True. It returns a plan and makes zero write calls. You must see the plan and explicitly confirm before the assistant calls it again withdry_run=False.test=Truebuys free test numbers. Use it for all evaluation and development. The README and the tool docstrings both default you towardtest=Trueuntil you are ready for real numbers.whoamiis the first tool you should call. It shows exactly which CTM account the credentials resolve to, so you never buy on an unexpected account. See How the wrong-account risk is prevented.release_numbersrefuses to run unlessconfirm=True. It is destructive and irreversible.Every successful purchase is appended to
purchases.log(JSON lines: UTC timestamp, account id, TPN id, number, test flag) as a local audit trail. This file is git-ignored.Secrets are never printed. The token is read into memory, used as an HTTP
Authorizationheader, and reported only as its source (envorenv.txt:<name>), never its value.
Requirements
Python 3.10+
A CTM API basic-auth token (base64
access:secret) for the account you want to work in.
Install
git clone https://github.com/<your-org>/ctm-number-provisioner.git
cd ctm-number-provisioner
python3.12 -m venv .venv # any Python >= 3.10
.venv/bin/pip install -e '.[dev]' # omit [dev] if you don't want test toolsThis installs a console script at .venv/bin/ctm-number-provisioner. Use that
absolute path in the MCP client configuration below.
Configure credentials
The server resolves credentials in this order:
CTM_BASIC_AUTH— the raw base64access:secrettoken. Use this for a single account.CTM_ENV_FILE(default~/.ctm/env) plusCTM_TOKEN_NAME— a named line in aname:tokencredentials file. Use this to keep several accounts side by side and switch per tool call.
The CTM account id comes from CTM_ACCOUNT_ID. Every tool also accepts an
optional account_id override, and an optional token_name override to select
a different line from the credentials file.
Option A: single account, environment variable
export CTM_BASIC_AUTH="$(printf '%s' 'ACCESS:SECRET' | base64)"
export CTM_ACCOUNT_ID="12345"Option B: multiple accounts, credentials file
Create ~/.ctm/env (chmod it 600) with one line per account:
acme_main:<base64-access-secret>
acme_test:<base64-access-secret>Then select a line:
export CTM_ENV_FILE="$HOME/.ctm/env"
export CTM_TOKEN_NAME="acme_main"
export CTM_ACCOUNT_ID="12345"Lines starting with # and blank lines are ignored. Only the first : on a
line splits the name from the token, so tokens containing : are preserved.
Never commit your token.
.gitignorealready excludes.env,*.env,.ctm/, andcredentials.json.
Run the server
The server speaks MCP over stdio (stdout is reserved for the protocol):
CTM_BASIC_AUTH="…" CTM_ACCOUNT_ID="12345" .venv/bin/ctm-number-provisionerOn startup it prints the resolved account to stderr only, for example:
[ctm-number-provisioner] account 12345 (Acme Tracking) via envMost users never run this by hand — the MCP client launches it. The sections below wire it into each client.
Use with Claude Code / Claude Desktop
Claude Code (CLI)
Register the server once at user scope:
claude mcp add ctm-numbers -s user \
-e CTM_BASIC_AUTH="<base64-access:secret>" \
-e CTM_ACCOUNT_ID="12345" \
-- "$PWD/.venv/bin/ctm-number-provisioner"Verify it connected and lists the six tools:
claude mcp listThen, in a session, try:
Call
ctm-numberswhoami, then search for available numbers in area code 443.
Multiple accounts: register one server per account with different names and
CTM_TOKEN_NAME values, e.g. ctm-numbers-acme and ctm-numbers-acme-test.
Claude Desktop
Edit claude_desktop_config.json
(~/Library/Application Support/Claude/ on macOS,
%APPDATA%\Claude\ on Windows):
{
"mcpServers": {
"ctm-numbers": {
"command": "/absolute/path/to/ctm-number-provisioner/.venv/bin/ctm-number-provisioner",
"env": {
"CTM_BASIC_AUTH": "<base64-access:secret>",
"CTM_ACCOUNT_ID": "12345"
}
}
}
}Restart Claude Desktop. The ctm-numbers tools appear in the tool picker.
Bundled skill (optional)
If you use Claude Code, copy skills/ctm-buy-numbers/SKILL.md
into ~/.claude/skills/ctm-buy-numbers/SKILL.md. It teaches the assistant the
safe order of operations (whoami → search → dry-run → confirm → buy → configure)
so users don't have to prompt step by step.
Use with Codex CLI
Codex supports MCP servers over stdio. Add a block to ~/.codex/config.toml:
[mcp_servers.ctm-numbers]
command = "/absolute/path/to/ctm-number-provisioner/.venv/bin/ctm-number-provisioner"
env = { CTM_BASIC_AUTH = "<base64-access:secret>", CTM_ACCOUNT_ID = "12345" }Then start Codex and ask it to use the ctm-numbers tools:
Use the ctm-numbers MCP server: call whoami, then search area code 443.
Prefer CTM_ENV_FILE + CTM_TOKEN_NAME in the env table if you'd rather not
put the token literal in config.toml.
Use with a local LLM
Any MCP-capable local client works, because this server is just a stdio process.
Generic stdio MCP config (Open WebUI, LibreChat, Continue, Cline, custom scripts, etc.):
{
"mcpServers": {
"ctm-numbers": {
"command": "/absolute/path/to/ctm-number-provisioner/.venv/bin/ctm-number-provisioner",
"env": { "CTM_BASIC_AUTH": "…", "CTM_ACCOUNT_ID": "12345" }
}
}
}Local model quality note. Number search returns large lists and the tools
return JSON. A capable tool-calling model (e.g. a 30B+ instruct model with
function calling, such as Qwen or Llama derivatives) handles this well. Smaller
models often forget to call whoami first or skip the dry-run confirmation —
the bundled skill file is worth including in the system prompt for those.
No MCP support? Drive the tools directly from Python. Each tool is an ordinary async function:
import asyncio, json
from ctm_numbers.server import whoami, search_available_numbers
async def main():
print(json.dumps(await whoami(), indent=2))
found = await search_available_numbers(area_code="443")
print(found["count"], "numbers available")
asyncio.run(main())Set CTM_BASIC_AUTH and CTM_ACCOUNT_ID in the environment first.
Tools
Tool | Writes? | What it does |
| no | Resolves and reports the account id + name and the token source. |
| no | Finds available numbers by area code, ZIP/address, prefix, toll-free, or international pattern. |
| yes | Buys numbers. |
| no | Lists tracking sources, receiving numbers, queues, voice menus, users, smart/conditional routers, geo routers, routing tables, and VoiceAI bots. |
| yes | Applies a name, tracking source, and one call route to one or many numbers. |
| yes | Releases numbers. Requires |
buy_numbers arguments
phone_numbers— exact numbers fromsearch_available_numbers, orarea_code+quantity— let CTM pick N numbers in that area codetest— buy free test numbers (defaultFalse)dry_run— plan only, no writes (defaultTrue)
configure_numbers routes
Pick at most one route. All of these are verified against the live API
except routing_table_id (see the note below).
Argument | Id format | Routes to |
|
| One or more receiving numbers ( |
|
| A call queue. |
|
| A voice menu / IVR. |
|
| An agent, with |
| numeric (e.g. | A smart / conditional router. |
|
| A geo router. |
|
| A VoiceAI bot. |
|
| A routing table. |
| — | A raw |
name accepts {n} (1-based index) and {number} placeholders, e.g.
"Google Ads {n}".
Recommended workflow
whoami— confirm the account. Tell the user which account you'll act on.search_available_numbers— show the results and let the user pick.buy_numbers(dry_run=True)— show the plan.Get explicit confirmation, then
buy_numbers(dry_run=False)withtest=Trueunless the user says it's for real.list_routing_targets— present the options; let the user choose the tracking source and route target. Route kinds include basic routes (receiving number, queue, voice menu, agent) and advanced routers (smart/conditional router, geo router, routing table, VoiceAI bot).configure_numbers— apply the name, source, and exactly one route.release_numbers(…, confirm=True)— only if the user explicitly asks, typically to clean up test numbers.
The skill at skills/ctm-buy-numbers/SKILL.md
encodes this flow.
CTM API endpoints used
Base URL: https://api.calltrackingmetrics.com/api/v1 (override with
CTM_BASE_URL).
All requests send Authorization: Basic <token>, Accept: application/json,
and Content-Type: application/json. Query parameters with None values are
dropped. Non-2xx responses raise CTMError(status, body, method, path); tools
catch it per item so one failure can't abort a batch.
Purpose | Method + path |
Identify account ( |
|
Search available numbers |
|
Buy an exact number |
|
Buy in an area code |
|
Read a number |
|
Rename / set custom fields |
|
Attach a tracking source |
|
Add a receiving number |
|
Set the call route |
|
Release a number |
|
List tracking sources |
|
List receiving numbers |
|
List queues |
|
List voice menus |
|
List users |
|
List smart / conditional routers |
|
List geo routers |
|
List routing tables |
|
List VoiceAI bots |
|
Search parameters
US/Canada (country=US, the default). searchby is inferred when omitted:
Intent | Params |
Area code |
|
ZIP / street address |
|
Area code + prefix |
|
Toll-free |
|
International |
|
Search returns up to ~50 numbers per call and includes overlay area codes.
Call-route bodies
// queue (CQU...)
{"virtual_phone_number": {"dial_route": "call_queue", "call_queue_id": "CQU..."}}
// voice menu (VOM...)
{"virtual_phone_number": {"dial_route": "voice_menu", "voice_menu_id": "VOM..."}}
// agent (USR...)
{"virtual_phone_number": {
"dial_route": "call_agent",
"user_id": "USR...",
"user_default_action_label": "voicemail",
"user_no_answer_seconds": 25
}}
// smart / conditional router (the numeric id from GET /conditional_routers)
{"virtual_phone_number": {
"dial_route": "conditional_router",
"conditional_router_id": 196
}}
// geo router (GEO...)
{"virtual_phone_number": {"dial_route": "geo_config", "geo_config_id": "GEO..."}}
// routing table (RTT...)
{"virtual_phone_number": {"dial_route": "routing_table", "routing_table_id": "RTT..."}}
// VoiceAI bot (VBT...)
{"virtual_phone_number": {"dial_route": "voice_bot", "voice_bot_id": "VBT..."}}Verified against the live API:
After each of the above, re-reading the number shows
route_to.typeequal tocall_queue,voice_menu,receiving_number,conditional_router,geo_config, orvoice_bot, with the id echoed underroute_to.dial.The queue body sets
route_to.type == "call_queue"withroute_to.dial.idequal to theCQU...id.The tracking-source add works with the
TSO...id fromGET /sources(the numericfilter_idis not required).GET /numbers/{TPN}returns the number object directly (not wrapped in anumberkey). Route changes are confirmed by re-readingroute_to.
Unverified:
routing_table_id. The parameter name anddial_routevalue come from CTM's own frontend, and the request is accepted, but the test account had no routing tables to route to, so a positiveroute_to.type == "routing_table"could not be observed. Treat it as best-effort until confirmed on an account that has routing tables.Note on silent failures. CTM returns
200 {"status": "success"}for adial_routesPUT even when it ignores an unrecognized route. Always verify by re-reading the number'sroute_torather than trusting the PUT response.list_routing_targetsalso returns an empty list for a kind with no entries (e.g.routing_tables), so "0 results" may mean "none configured" rather than "wrong endpoint".
Rate limits
CTM allows roughly 10 requests/second. The server uses a global concurrency
limit of 4, retries transport errors twice, and retries 429/5xx with
exponential backoff (1s, 2s, 4s). Paginated list endpoints fetch page 1, then
all remaining pages concurrently.
How the wrong-account risk is prevented
A CTM token does not guarantee the account you expect: the same token can map to a different sub-account than its label suggests, and some CTM clients silently fall back to a default account. This server defends against that in several layers:
whoamiresolves the account from the API, not from configuration. It callsGET /accounts/{CTM_ACCOUNT_ID}and returns the name CTM reports for that id, alongside the id and the token source. If the returned name is not the account you intended, you stop before buying anything.The server prints the resolved account name on startup (to stderr), so a misconfigured client is visible immediately in the logs.
Every tool takes an optional
account_idoverride that is resolved per call, so a single server can safely target different accounts without relying on a stale global default.buy_numbersdry-runs by default and includes the resolved account id and name in the plan, so the last thing a human sees before approving a purchase is exactly which account it will hit.buy_numbersnever proceeds on ambiguity: it requires exactly one ofphone_numbersorarea_code, and validates the quantity range up front.
The recommended assistant behavior (encoded in the bundled skill) is: call
whoami first, state the account name to the user, and refuse to buy until the
user confirms that name.
Development
.venv/bin/pytest -q # 20 tests, all offline (respx, no live calls)
.venv/bin/ruff check src testsProject layout:
src/ctm_numbers/
__main__.py console entry point + stderr startup check
auth.py credential resolution (env var or named line in a file)
client.py async httpx wrapper: retries, backoff, concurrent pagination
server.py FastMCP instance and the six tools
tests/test_tools.pyLicense
Available Tools
6 toolsbuy_numbersA
Purchase CTM tracking numbers. Real purchases cost money unless test=True.
ALWAYS call with dry_run=True first (the default), show the user the plan (account name, count, numbers or area code), and get explicit confirmation before calling again with dry_run=False. There is no undo except release_numbers.
Args: phone_numbers: Exact numbers from search_available_numbers. area_code: Let CTM pick numbers in this area code (see quantity). quantity: How many numbers to buy in area_code mode (1-500). test: Buy free test numbers. Default False (real, billable). dry_run: Plan only, no write calls. Default True.
Returns rows with tpn_id; feed those tpn_ids to configure_numbers.
| Name | Required | Description | Default |
|---|---|---|---|
| test | No | ||
| dry_run | No | ||
| quantity | No | ||
| area_code | No | ||
| account_id | No | ||
| token_name | No | ||
| phone_numbers | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It discloses that real purchases are billable, that test=True avoids charges, that dry_run makes no write calls, that there is no undo except release_numbers, and that the return rows contain tpn_id. This is excellent transparency for a mutating, money-spending tool.
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 and front-loaded with the most critical warning and workflow requirement. The Args block is compact and each line adds value. The return-value note is brief and actionable. No sentence is wasted.
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, lack of annotations, and lack of output schema, the description is nearly complete: it covers purpose, mandatory dry-run workflow, cost, irreversibility, parameter semantics, and the next step. The only notable gaps are the undocumented account_id and token_name parameters, which prevent a perfect score.
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. It adds meaningful semantics for phone_numbers, area_code, quantity, test, and dry_run, including the 1-500 range, the mode distinction, and the exact source for phone numbers. However, account_id and token_name are not explained at all, which is a noticeable gap given the schema provides only titles and defaults.
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: 'Purchase CTM tracking numbers.' It clearly distinguishes this from sibling tools by positioning it as the purchasing step, and even references the downstream configure_numbers and the undo path release_numbers. The cost warning ('Real purchases cost money unless test=True') further clarifies the tool's real-world effect.
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, actionable usage rules: ALWAYS call with dry_run=True first, show the user the plan, and get explicit confirmation before dry_run=False. It also explains the relationship to search_available_numbers and configure_numbers, and names release_numbers as the only undo path. This is model behavior for guiding an agent through a high-stakes workflow.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
configure_numbersB
Apply a name, tracking source, and one call route to one or many numbers.
Get ids from list_routing_targets and let the user choose them. Pick at most ONE route: receiving_number_ids, queue_id (CQU...), voice_menu_id (VOM...), user_id (USR..., rings an agent), or route_override (raw {"virtual_phone_number": {...}} dial_routes body). Pick at most ONE route: receiving_number_ids, queue_id (CQU...), voice_menu_id (VOM...), user_id (USR...), conditional_router_id (smart router), geo_route_id (GEO...), routing_table_id (RTT...), voice_bot_id (VBT...), or route_override.
Args: tpn_ids: Tracking numbers to configure (TPN...). name: Label. Supports {n} (1-based index) and {number} placeholders, e.g. "Google Ads {n}". custom_fields: Custom field values to set on each number. source_id: Tracking source id (TSO... or numeric) to attach the numbers to. receiving_number_ids: RPN ids to forward calls to. route_override: Raw dial_routes body; only if the named routes don't fit.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| tpn_ids | Yes | ||
| user_id | No | ||
| queue_id | No | ||
| source_id | No | ||
| account_id | No | ||
| token_name | No | ||
| geo_route_id | No | ||
| voice_bot_id | No | ||
| custom_fields | No | ||
| voice_menu_id | No | ||
| route_override | No | ||
| routing_table_id | No | ||
| user_default_action | No | voicemail | |
| receiving_number_ids | No | ||
| conditional_router_id | No | ||
| user_no_answer_seconds | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It does imply mutation ('apply'), states the at-most-one-route constraint, gives ID prefix conventions (CQU..., VOM..., USR...), and notes that user_id 'rings an agent'. But it never states whether existing number configuration gets overwritten, whether the call is idempotent, or what happens to unspecified settings — a notable gap for a config-mutation tool with zero annotation coverage.
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 structure is sensible on paper — summary line, guidance paragraph, then args list — but it repeats the 'Pick at most ONE route' sentence nearly verbatim twice with different route enumerations, wasting space and creating ambiguity about which enumeration is authoritative. This is a real structural defect, not merely verbose.
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 17-parameter tool with no annotations, no output schema, and 0% schema coverage, this description is incomplete. Four parameters (account_id, token_name, user_default_action, user_no_answer_seconds) go unmentioned, and there's no guidance on defaults that materially affect behavior, such as user_default_action='voicemail' and user_no_answer_seconds=25, which an agent would need to make sound choices.
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%, so the description must compensate, and it does for several parameters: tpn_ids (TPN...), name (with {n} and {number} placeholders and an example), source_id (TSO... or numeric), receiving_number_ids (RPN), and route_override. It also enumerates route params with ID formats. But account_id, token_name, user_default_action, and user_no_answer_seconds are entirely undocumented, and the route parameters lack semantic explanations of what each route type actually does.
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?
'Apply a name, tracking source, and one call route to one or many numbers' is a specific verb+resource+scope statement that clearly identifies the tool as a configuration operation over tracking numbers. It is distinguishable from siblings like buy_numbers (acquisition) and release_numbers (teardown). Not a 5 because the purpose is somewhat buried under the duplicated route-guidance paragraph, and the identity isn't crisply framed against the configure family.
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 a concrete prerequisite and interaction pattern — 'Get ids from list_routing_targets and let the user choose them' — and an explicit exclusion for route_override ('only if the named routes don't fit'). However, there is no contrast with buy_numbers or release_numbers on when to configure vs acquire/release, and the duplicated 'Pick at most ONE route' sentence lists different route sets each time, which could mislead the agent about which routes are valid.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_routing_targetsA
List everything a tracking number can be attached or routed to.
Present these options to the user and let them choose; do not pick for them. With more than four options, show a numbered list and have the user reply with a number, or use search to narrow it down.
Args: kinds: Subset of ["sources", "receiving_numbers", "queues", "voice_menus", "users", "conditional_routers", "geo_routes", "routing_tables", "voice_bots"]. Default: the first four. search: Case-insensitive substring filter on the row's fields.
| Name | Required | Description | Default |
|---|---|---|---|
| kinds | No | ||
| search | No | ||
| account_id | No | ||
| token_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It does disclose that the tool is a listing operation and instructs the agent to defer to the user's choice, which is useful behavioral context. However, it does not mention any side effects (likely none, but not stated), authentication requirements, rate limits, or the exact return format. The description adds some behavioral nuance but leaves significant gaps.
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 and front-loaded with the purpose, followed by usage guidance and then parameter details. It is reasonably concise, though the parameter list for kinds is somewhat verbose. Every section earns its place, and the overall 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?
The tool has no output schema, so the description should explain what the tool returns, but it does not. It also fails to explain the purpose of account_id and token_name, and gives no indication of pagination or error behavior. While it provides usage instructions, it is incomplete for a tool with four parameters and no output schema, leaving an agent to guess about return format and omitted 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?
The schema has 0% description coverage, so the description must explain all parameters. It covers 'kinds' and 'search' with detailed semantics: the allowed subset for kinds and the default set, and the case-insensitive substring behavior for search. However, it completely omits 'account_id' and 'token_name', which are likely authentication/context parameters. This leaves half of the parameters unexplained, making the description insufficient for full parameter understanding.
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 verb and resource: 'List everything a tracking number can be attached or routed to.' This unambiguously distinguishes it from sibling tools that handle phone numbers (search_available_numbers, buy_numbers, etc.) or identity (whoami). The purpose is specific 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 description gives explicit instructions on how to use the tool's results: present options to the user, never pick for them, and use a numbered list or search for more than four results. This is strong practical guidance. However, it does not explicitly state when NOT to use this tool or name alternative tools for other scenarios, relying on the sibling names to imply that.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
release_numbersA
Release (delete) tracking numbers. Destructive and irreversible.
Use this only when the user explicitly asks, typically to clean up test numbers. Refuses unless confirm=True; ask the user first.
| Name | Required | Description | Default |
|---|---|---|---|
| confirm | No | ||
| tpn_ids | Yes | ||
| account_id | No | ||
| token_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so excellently. It explicitly warns 'Destructive and irreversible' and discloses the confirmation requirement: 'Refuses unless confirm=True; ask the user first.' This is exactly the safety-critical behavioral disclosure an agent needs.
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 three short sentences with the most critical warning ('Destructive and irreversible') front-loaded. Every sentence adds value, and there is no repetition or 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?
The description adequately covers safety and usage, but it leaves two optional parameters (account_id, token_name) unexplained and mentions nothing about return behavior or failure modes. Since those parameters are optional and the key confirmation behavior is documented, the gap is moderate rather than severe.
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 needed to compensate for the four parameters. It only addresses confirm, mentioning that the tool refuses unless confirm=True. It does not explain tpn_ids, account_id, or token_name, leaving their semantics largely undocumented.
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 verb and resource: 'Release (delete) tracking numbers.' The parenthetical 'delete' removes ambiguity, and the destructive nature distinguishes it from sibling tools like buy_numbers or configure_numbers. It doesn't explicitly name a sibling, so it misses the top score.
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 a clear trigger condition: 'Use this only when the user explicitly asks,' and a typical scenario: 'to clean up test numbers.' It also describes the expected flow of asking for confirmation. It doesn't explicitly mention alternatives to prefer in non-deletion cases, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_available_numbersA
Search available CTM tracking numbers before buying them.
US/CA modes (searchby is inferred when omitted):
area_code: numbers in an area code (e.g. "443").
address: street or ZIP (e.g. "21201").
number_prefix: area code + prefix (e.g. "917563").
tollfree: the default when nothing else is given.
International: pass country (e.g. "GB") with pattern, and optionally operator="start_with" or "includes" (default includes).
Returns compact rows: number, friendly_name, type, region, postal_code, lata, sms, mms, addr_required, hipaa_friendly. Show them to the user and let them pick before calling buy_numbers.
| Name | Required | Description | Default |
|---|---|---|---|
| address | No | ||
| country | No | US | |
| pattern | No | ||
| operator | No | ||
| searchby | No | ||
| area_code | No | ||
| account_id | No | ||
| token_name | No | ||
| number_prefix | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description must carry the full burden of behavioral disclosure. It does a good job: it explains search behavior (inference of searchby modes), the return format (compact rows with specific fields), and instructs the agent to show results to the user before buying, which is a behavioral expectation. However, it doesn't disclose potential side effects (e.g., whether this is a read-only operation) or rate limits, but the description is still informative.
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 bullet points for different modes, making it easy to scan. It fronts the purpose and then provides essential details without redundancy. Every sentence adds value: it explains modes, international usage, return fields, and next steps. It's concise yet comprehensive for a tool with many parameters.
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 (9 optional parameters, no output schema), the description is quite complete: it covers the main search modes, international options, return fields, and the expected workflow (show results to user). However, it doesn't explain the meaning of all return fields (e.g., lata, hipaa_friendly) or account_id/token_name usage, which could be critical for correct invocation in some scenarios. Minor gaps remain.
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%, meaning the schema provides no descriptions for parameters. The description explains the semantics of area_code, address, number_prefix, searchby, and country/operator/pattern, which are the main parameters. However, it does not explain account_id or token_name, which are likely identifiers for multi-tenant use. Given the low coverage, the description compensates partially but misses some parameters, warranting a 3.
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 a specific verb ('Search') and resource ('available CTM tracking numbers') and distinguishes the tool's purpose from buying them, explicitly saying 'before buying them' and later 'let them pick before calling buy_numbers'. This differentiates it from sibling tools like buy_numbers. The scope (US/CA and International modes) is clearly outlined, making 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?
The description provides explicit guidance on when to use the tool: it is for searching before buying, and it explains how searchby modes are inferred when omitted. It also mentions the alternative (buy_numbers) implicitly, but doesn't explicitly state when NOT to use it or name alternatives beyond 'buy_numbers' in the last line. Sibling tools like configure_numbers and release_numbers are not mentioned as alternatives, but the description's context is sufficient for most agents.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
whoamiA
Show which CTM account the server will act on, and where the token came from.
Call this first and tell the user the resolved account name before buying anything: a token can map to a different account than its label suggests.
| Name | Required | Description | Default |
|---|---|---|---|
| account_id | No | ||
| token_name | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full disclosure burden. It conveys that this is a read-only resolution operation and surfaces the key behavioral trait — token labels may not match the resolved account. It omits the output format, but for a simple identity check that is a minor gap.
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?
Two tight sentences with zero filler. The core purpose is front-loaded in the first sentence, and the usage directive follows in the second. Every word 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?
Complete for a simple identity tool with two optional params and no output schema: the description covers purpose, sequencing, and the one critical caveat. The exact return shape would be a nice addition but is not essential for a whoami-style call.
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 should compensate, but it never explains what account_id and token_name do or how passing them alters resolution. The two optional nullable parameters are left to inference, though the description's mention of 'token' and 'account' loosely maps to them.
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 ('show') and a precise resource ('which CTM account the server will act on, and where the token came from'). It clearly differentiates itself from siblings that buy, search, configure, and release numbers — this is the identity-resolution tool in a purchase workflow.
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 instructs the agent to 'call this first' and to relay the resolved account name before buying anything, with the underlying reason stated (a token can map to a different account than its label suggests). This is direct, actionable routing guidance relative to buy_numbers and the rest of the workflow.
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.
6 tool updates
v0.1.0- First observed
buy_numbers - First observed
configure_numbers - First observed
list_routing_targets - First observed
release_numbers - First observed
search_available_numbers - First observed
whoami
TDQS
Scored across 6 tools
Each tool addresses a distinct stage of the number lifecycle: identity (whoami), discovery (search_available_numbers), acquisition (buy_numbers), routing options (list_routing_targets), configuration (configure_numbers), and removal (release_numbers). There is no meaningful overlap between tool purposes.
Most tools follow a clear verb_noun snake_case pattern (search_available_numbers, buy_numbers, list_routing_targets, configure_numbers, release_numbers). The lone exception is 'whoami', a standard Unix-style command name, which is a minor deviation rather than a systemic inconsistency.
Six tools provide a focused, well-scoped surface for a number-provisioning server. Each tool corresponds to a necessary step in the purchase-to-configuration workflow, and none feels redundant or extraneous.
The lifecycle is well covered from search/buy through configure/release, but there is no tool to list or inspect already-owned/configured numbers. This is a notable gap for agents needing to manage existing inventory, though the core provisioning flow can still be completed.
Maintenance
Related MCP Connectors
Give AI agents a phone number. Voice calls, SMS, and phone number management for MCP clients.
Virtual phone numbers for AI agents — rent numbers in 200+ countries, receive SMS.
Give AI agents secure access to RevDesk calling, SMS, phone numbers, caller IDs, and usage.
- DialMCPOAuthcom.dialmcp
Let AI agents place real phone calls from your verified number, with transcripts and recordings.
Related MCP Servers
FlicenseBqualityFmaintenanceEnables interaction with Telnyx's telephony, messaging, and AI assistant APIs to manage phone numbers, send messages, make calls, and create AI assistants. Includes webhook support for real-time event handling and comprehensive tools for voice, SMS, cloud storage, and embeddings.4625-- AlicenseNot gradedqualityCmaintenanceEnables AI agents to manage phone numbers, send/receive SMS, and place voice calls through natural language, connecting to the phone network via the AgentPhone API.296 npm123MIT
- AlicenseAqualityBmaintenanceEnables querying telecom routing data (LRN, CNAM, DNO, LERG, toll-free routing) directly from AI assistants like Claude, ChatGPT.1471 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to purchase virtual phone numbers, retrieve SMS verification codes, and manage activations through natural language by wrapping the VirtualSMS Consumer API.9 npmMIT