cleat-mcp
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., "@cleat-mcpWait for the SMS verification code on my Cleat line and read it to me."
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.
cleat-mcp
A local MCP server that lets an assistant read the texts and transcribed calls arriving on your own Cleat US mobile line, including waiting for the next verification code.
Cleat rents ID-verified US mobile numbers that receive SMS/2FA codes and transcripts of incoming calls. It is receive-only: no outbound texts, no outbound calls, no 911. This server exposes four read tools over stdio, so an assistant can finish a sign-in on an account you already hold instead of asking you to read a code off your phone.
Install
Node 20 or newer. This is not on npm; clone it.
git clone https://github.com/davidcleat/cleat-mcp.git
cd cleat-mcp
npm installRelated MCP server: botcall-mcp
Use it
Add this to your MCP client's config, with the absolute path to your clone, and restart the client.
Claude Desktop — claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):
{
"mcpServers": {
"cleat": {
"command": "node",
"args": ["/absolute/path/to/cleat-mcp/bin/cleat-mcp.js"],
"env": {
"CLEAT_API_KEY": "clt_your_api_key"
}
}
}
}Claude Code — .mcp.json in the project root, same shape:
{
"mcpServers": {
"cleat": {
"command": "node",
"args": ["/absolute/path/to/cleat-mcp/bin/cleat-mcp.js"],
"env": { "CLEAT_API_KEY": "clt_your_api_key" }
}
}
}Check it before you wire it up — this prints the tool list and exits:
printf '%s\n%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"smoke","version":"0"}}}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| CLEAT_API_KEY=clt_your_api_key node bin/cleat-mcp.jsThen ask for the code:
Log into my staging account at localhost:3000 with the credentials in
.env.test. When it asks for the SMS code, callwait_for_codeon my Cleat line first, then submit whatever arrives.
The assistant calls list_lines to find the line, wait_for_code before the step that sends the text, and reads the code out of the result.
The four tools
Tool | Arguments | Returns |
| — | Every line in the key's workspace, newest first: |
|
| Recent texts and call transcripts, newest first. Each one is a Cleat message: |
|
| The most recent code already on the line, or |
|
| The next code to arrive, or |
from is a case-insensitive substring of the sender (a short code, say 32665). service is matched against the service Cleat recognised from the text — facebook, stripe — and against the workspace label.
Why wait_for_code and not polling
wait_for_code reads where the inbox stands before it starts waiting, then only accepts messages that arrive after that point. A code left over from an earlier sign-in is never returned as this one. That baseline is the receivedAt of the newest message the line already has — a server timestamp — so a local clock that is off does not widen or narrow the window.
The order that matters: call wait_for_code, then do the thing that sends the text. It is already listening.
A timeout is an ordinary result (found: false, timedOut: true), not an error, so the assistant can send the code again and retry. Errors the key causes — an expired key, a line the key cannot reach — come back as readable tool errors instead of crashing the server.
Hosted or local
Cleat already runs a hosted MCP server at https://cleat.so/api/mcp, listed in the MCP registry as so.cleat/cleat. It exposes the same four tool names, the same arguments and the same message fields, so a prompt written for one works with the other. Pick on these differences:
Hosted ( | This package (local, stdio) | |
Transport | HTTP JSON-RPC | stdio — works with clients that only speak stdio |
Install | none | Node 20+ and a clone |
Where the key lives | in your client's headers config | in your client's |
Who Cleat sees | your client's requests | your machine's requests |
| 55 seconds | 300 seconds, subject to your client's own request timeout |
How a wait notices a code | the line's own live event, so it answers as the text lands | polls every 2 seconds |
A timeout's answer |
| the same, plus |
Code you can read and patch | no | yes |
Both read the same REST API and can see exactly what the API key allows. If you have no reason to self-host, the hosted endpoint is less to maintain. Use this one to raise the wait ceiling, to keep the key off a headers config, to run behind your own egress rules, or to change the tools.
Get an API key
Create a Cleat account at cleat.so and subscribe to a line — $24.99/month or $249.90/year.
Verify your identity once, with a government ID. Until the workspace owner has verified, reading a line answers
403withcodeverify_first: the line runs and keeps every text, but nobody can read them.list_linesstill works, so you can find the line id before verifying.In workspace settings → API keys, create a key. It starts with
clt_and is shown once.
Scope the key before you hand it to an agent
When you create a key you can narrow it two ways, and both matter here:
To named lines. A line outside the key's scope answers
404, exactly as another workspace's line does. Give the key the one line the assistant is meant to read, not the workspace.To an expiry date. After it, the key answers
401with codekey_expired. Set one — an assistant's key should stop working by itself.
That is what makes a key safe to put in a config file an assistant can reach. Revoking a key locks the assistant out on its next call and touches nothing else: no password, no session, no other line.
Only the workspace owner can create keys.
Limits
Receive-only. A Cleat line cannot send a text, place a call, or reach 911 or any other emergency number. There is no tool here that tries; the API has no endpoint for it. An incoming call is transcribed and lands like a text, with the transcript in
bodyand the caller infrom. Nothing marks a message as having been a call.US numbers. One line, one subscription, no area code choice, no pools.
One verified owner per line. Identity is checked once, for the account, by a person. An agent cannot verify, and cannot sign itself up.
120 requests per minute per key.
wait_for_codepolls once every 2 seconds while it waits, so one wait costs about 30 requests a minute. Over the limit the API answers429; a wait in progress rides it out and keeps waiting.codeis best effort. Cleat extracts what looks like a one-time code, and that field can benull. The full text is always inbody— read it when it matters.A line on hold cannot be read. If a subscription lapses, texts are kept but the API answers
402until it resumes.Cleat cannot tell you that a particular service will accept its numbers. No provider can. Try yours.
What this is for
Accounts you or your company already hold: a shared business login whose 2FA lands on the company line, a staging account an end-to-end suite signs into, a console an on-call engineer needs a code for. It is not for creating extra accounts, signing up in bulk, or working around a platform's limits — that is outside what Cleat sells, and an account used that way is closed.
Links
Cleat for developers — the REST API and signed webhooks
Cleat for AI agents — the hosted MCP server
MIT licensed.
Available Tools
4 toolslatest_codeA
The most recent verification code already on a line, or found: false if none has arrived. Use wait_for_code instead when the code has not been sent yet.
| Name | Required | Description | Default |
|---|---|---|---|
| from | No | Optional. Only consider senders containing this text, e.g. a short code. | |
| lineId | Yes | The line's id, from list_lines. | |
| service | No | Optional. Only consider messages Cleat attributed to this service, e.g. 'facebook'. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full behavioral burden. It discloses that the tool returns found: false if no code has arrived, implying it does not wait or error, and clarifies that it only considers codes already present. It does not mention side effects, but the semantics strongly imply a read-only lookup; the main behavioral ambiguity is handled.
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 sentences with no waste: the first states the core behavior and fallback, the second routes to the alternative when appropriate. The key behavioral constraint is front-loaded before the alternative reference.
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 read-style tool with three well-documented parameters and no output schema, the description is largely sufficient: it states what is returned, the no-result case, and the correct alternative when the code has not arrived. A precise return shape or example would strengthen it, but 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%, with each parameter already documented including optionality and example semantics. The tool description adds no additional parameter detail, so the baseline score of 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 states a specific verb and resource: it retrieves the most recent verification code already on a line, and explicitly includes the fallback behavior when none has arrived. It also distinguishes itself from the sibling wait_for_code, so an agent can tell them apart 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 gives clear usage guidance: use this tool when the code has already arrived, and explicitly directs the agent to wait_for_code when the code has not been sent yet. This directly addresses the main decision point between two sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_linesA
List the US mobile lines in this API key's workspace, newest first. Every other tool takes a lineId from here.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden. It conveys a read-only listing operation, scopes it to US mobile lines, states the sort order, and implies that returned items include a lineId for use in other tools. It stops short of describing output fields or pagination, but for a zero-parameter list this is sufficient.
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 sentences with no filler. The core behavior is front-loaded, and the second sentence adds the cross-tool usage note. 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?
For a zero-parameter, read-only listing tool, the description covers scope, ordering, and how the result feeds sibling tools. The lineId mention compensates for the absence of an output schema by giving the agent the most important detail it needs.
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 tool accepts no parameters, so the schema is fully complete and there is nothing for the description to add. The 0-parameter baseline of 4 applies.
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 names a concrete operation ('List'), a specific resource ('US mobile lines'), and the workspace scope ('API key's workspace'), plus the ordering ('newest first'). This is clearly distinguishable from sibling tools about messages and codes, so an agent knows exactly what this tool returns.
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 sentence 'Every other tool takes a lineId from here' is an explicit when-to-use instruction: call this first to obtain a lineId before using sibling tools. No alternative listing tool exists among the siblings, so the guidance is complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_messagesA
Recent texts and transcribed calls received by one line, newest first. Read body as well as code: code is a best-effort extraction and can be null.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many messages to return. 1 to 200. | |
| lineId | Yes | The line's id, from list_lines. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the behavioral burden, and it adds useful context beyond the schema by disclosing order (newest first) and the note that code is a best-effort extraction and can be null. It also explicitly tells the agent to read body, which is genuinely useful behavioral guidance. It omits broader details like pagination or full return shape, but covers the key warning.
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 concise, front-loaded with the core behavior, and adds a targeted warning in a second sentence. Every element earns its place and nothing 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?
The description gives enough for an agent to invoke correctly by identifying resource, ordering, key fields, and the nullable code field. Given the tool's modest complexity, the key missing pieces are minor since the schema is simple and output schema is absent but largely inferred.
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%, and parameters are described in the schema with descriptions. The tool description contributes no extra parameter semantics beyond clarifying that one line is involved, so the baseline 3 applies.
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 identifies what is returned: recent texts and transcribed calls for a single line, ordered newest first. This distinguishes it from siblings like list_lines and code-focused tools such as latest_code. The resource and scope are unmistakable.
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 implies the main use case: fetching recent messages or transcribed calls for a line. However, it never explicitly states when to prefer this tool instead of latest_code or wait_for_code. Guidance is implied rather than stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
wait_for_codeA
Wait for the NEXT code to arrive on a line and return it. A code that was already in the inbox when the call started is never returned. Call this immediately before the step that sends the text, then complete that step while it waits.
| Name | Required | Description | Default |
|---|---|---|---|
| from | No | Optional. Only accept senders containing this text. | |
| lineId | Yes | The line's id, from list_lines. | |
| service | No | Optional. Only accept messages Cleat attributed to this service. | |
| timeoutSeconds | No | How long to wait. 1 to 300 seconds. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses the waiting behavior, the boundary that it never returns a pre-existing code, and the recommended call pattern. It does not describe timeout/error behavior or side effects, but these are partly encoded in the timeoutSeconds parameter and the operation is conceptually read-only. Overall, the core behavior is transparent.
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?
Three sentences, each contributes distinct information: the operation, the exclusion rule, and the usage timing. No filler 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?
For a tool with no output schema and no annotations, the description covers the essential selection criteria (what it returns, when to call it, and what it ignores). The schema fully documents parameters, and sibling context (latest_code) clarifies the distinction. Minor gaps like timeout behavior are delegated to the schema's timeoutSeconds description, so overall it is complete enough 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 100% – every parameter (from, lineId, service, timeoutSeconds) has a description in the input schema. The description only mentions 'line' in passing and adds no semantic detail beyond the schema, 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?
Description states a specific verb ('Wait... return') and resource ('a code on a line'), and explicitly contrasts with existing inbox codes ('A code that was already in the inbox... is never returned'), distinguishing it from the sibling latest_code. The purpose is unambiguous and does not require reading 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 timing advice ('Call this immediately before the step that sends the text, then complete that step while it waits') and describes what it will not do ('a code that was already in the inbox is never returned'), which is an implicit when-not. It does not name the alternative latest_code directly, but the context is clear enough for an agent to choose correctly.
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.
4 tool updates
v0.1.0- First observed
latest_code - First observed
list_lines - First observed
list_messages - First observed
wait_for_code
TDQS
Scored across 4 tools
list_lines and list_messages are clearly distinct, and latest_code versus wait_for_code is resolved by existing-code versus next-code semantics. There is mild overlap in code retrieval, but the descriptions make the intended usage clear.
list_lines and list_messages follow a consistent verb_noun pattern, while latest_code and wait_for_code deviate stylistically. The names are still readable and semantically grouped, though not perfectly uniform.
Four tools is well-scoped for a focused SMS verification code server: line discovery, message browsing, and two code-retrieval modes. Each tool earns its place without unnecessary bloat.
The tool set covers the full OTP retrieval workflow: find a line, read messages, retrieve an already-received code, or wait for the next incoming code. No critical operation appears to be missing for its stated purpose.
Maintenance
Related MCP Connectors
A real US mobile number that receives verification codes, by text or transcribed call.
Connect your account, prepare US number registration, and hold SMS for human approval.
Give AI agents secure access to RevDesk calling, SMS, phone numbers, caller IDs, and usage.
Give AI agents a phone number. Voice calls, SMS, and phone number management for MCP clients.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI-powered SMS messaging through Twilio with automatic conversation threading, message status tracking, and webhook support for receiving inbound messages.79 npm1MIT
- AlicenseAqualityDmaintenanceGives AI agents real phone numbers to receive SMS and extract verification codes through tool calls.624 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
- FlicenseNot gradedqualityBmaintenanceEnables agents to read Android SMS verification codes via ADB and use them to complete web-based SMS login flows.-