Skip to main content
Glama

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. Published as cleatapi-mcp; nothing to clone unless you want to change it.

Related MCP server: adb-sms

Use it

Add this to your MCP client's config and restart the client. npx fetches the server the first time it runs.

Claude Desktop — claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):

{
  "mcpServers": {
    "cleat": {
      "command": "npx",
      "args": ["-y", "cleatapi-mcp"],
      "env": {
        "CLEAT_API_KEY": "clt_your_api_key"
      }
    }
  }
}

Claude Code — .mcp.json in the project root, same shape:

{
  "mcpServers": {
    "cleat": {
      "command": "npx",
      "args": ["-y", "cleatapi-mcp"],
      "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 npx -y cleatapi-mcp

To run it from a clone instead — which is the point of it being here — use "command": "node" with "args": ["/absolute/path/to/cleat-mcp/bin/cleat-mcp.js"]:

git clone https://github.com/davidcleat/cleat-mcp.git
cd cleat-mcp && npm install && npm test

Then 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, call wait_for_code on 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

list_lines

—

Every line in the key's workspace, newest first: id, phone, label, status, createdAt. Start here; the rest take a lineId.

list_messages

lineId, limit (1–200, default 20)

Recent texts and call transcripts, newest first. Each one is a Cleat message: id, line, from, body, code, receivedAt, service, contact, label — the same fields the REST API returns.

latest_code

lineId, from?, service?

The most recent code already on the line, or found: false. Returns found: true and the message's fields.

wait_for_code

lineId, timeoutSeconds (1–300, default 30), from?, service?

The next code to arrive, or found: false, timedOut: true.

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 (https://cleat.so/api/mcp)

This package (local, stdio)

Transport

HTTP JSON-RPC

stdio — works with clients that only speak stdio

Install

none

npx -y cleatapi-mcp, or a clone to change it

Where the key lives

in your client's headers config

in your client's env block, on your machine

Who Cleat sees

your client's requests

your machine's requests

wait_for_code ceiling

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

found: false, timedOut: true

the same, plus waitedSeconds and a reason for the model to read

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

  1. Create a Cleat account at cleat.so and subscribe to a line — $24.99/month or $249.90/year.

  2. Verify your identity once, with a government ID. Until the workspace owner has verified, reading a line answers 403 with code verify_first: the line runs and keeps every text, but nobody can read them. list_lines still works, so you can find the line id before verifying.

  3. 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 401 with code key_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 body and the caller in from. 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_code polls once every 2 seconds while it waits, so one wait costs about 30 requests a minute. Over the limit the API answers 429; a wait in progress rides it out and keeps waiting.

  • code is best effort. Cleat extracts what looks like a one-time code, and that field can be null. The full text is always in body — read it when it matters.

  • A line on hold cannot be read. If a subscription lapses, texts are kept but the API answers 402 until 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.

MIT licensed.

Available Tools

4 tools
latest_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.

Inputs: lineId (required), from, service.

list_linesA

List the US mobile lines in this API key's workspace, newest first. Every other tool takes a lineId from here.

Inputs: no named parameters.

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.

Inputs: lineId (required), limit.

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.

Inputs: lineId (required), from, service, timeoutSeconds.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4.3/5.0

    Scored across 4 tools

    Disambiguation4/5

    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.

    Naming Consistency4/5

    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.

    Tool Count5/5

    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.

    Completeness5/5

    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

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      C
      maintenance
      Enables agents to read Android SMS verification codes via ADB and use them to complete web-based SMS login flows.
      -
    • F
      license
      Not graded
      quality
      B
      maintenance
      Enables AI agents to use a real Android phone with a SIM to retrieve phone numbers, receive SMS verification codes, and manage SMS messages.
      -
    • A
      license
      A
      quality
      B
      maintenance
      Enables an AI agent to wait for and retrieve SMS verification codes forwarded from your iPhone, returning each code once, and to list recent texts for login links or non-numeric messages. It runs in your own Cloudflare account and deletes stored texts after 15 minutes.
      2
      MIT