Let your LLMs call each other — a local MCP switchboard for coding agents
TeLLMphone has well-structured tool definitions with comprehensive descriptions (avg 250+ chars) and clear parameter documentation. All 7 tools are explicitly registered with @mcp.tool decorators and include detailed docstrings. However, input schemas are not visible in the provided source code, only parameter names and types appear in function signatures. Output schemas are completely undocumented. Error handling guidance is minimal. The 'call' tool's write parameter and 'reply' tool lack explicit safety warnings despite state-modifying behavior. No tool annotations (readOnlyHint/destructiveHint) are present. Descriptions are LLM-optimized and contextual (e.g., 'mode=wait' vs 'mode=voicemail'), but parameter constraints (enums, ranges) are not formally declared in schemas.
Place a call to a coding agent about a project. The callee runs headlessly in `project_dir` (absolute path — usually your current working directory) and can read the code there, so don't paste file contents; reference paths instead. Put the actual ask in `message` and any background in `context`. Pick a `personality` from the phonebook to shape the callee (e.g. 'grumpy-reviewer'), or omit for its default behavior. `model` independently picks which model the callee runs — it is pinned for the whole call, so follow-up replies keep talking to the same model; omit it for the callee's (or TeLLMphone-configured) default. Don't guess model names: copy an exact `id` from phonebook().agents[].models. Prefer default=true for ordinary second opinions; read each model's description for personality fit (e.g. hostile review → codex terra/sol; architect → claude opus or fable; rubber-duck → cheap leaves), or omit. mode='wait' (default) starts the callee and returns status='ringing'; poll check_messages for the answer. Live calls take minutes and burn real tokens — one thoughtful call beats three lazy ones. mode='voicemail' just leaves the message for the next time that agent is active in the project; nothing runs now. write=True lets the callee edit files in `project_dir` for the whole call — use it when you're deliberately delegating changes, not just asking for opinions. Callees themselves cannot grant write; such calls are refused. Keep the returned call_id to continue the conversation with reply().
Check for messages from other agents in a project ("anything for me?"). Returns unread messages addressed to you, including their full body, and all open calls you're a party to. status='ringing' means a turn is still running; kind='message' with is_final=true is an answer. Fetching marks messages as read, so relay anything important to your human before moving on. Reply with reply(call_id).
Retrieve one call and its full transcript by id. Use this when an unread notification was consumed by another session, or when you need to recover the context of an interrupted call. This does not change unread state.
Input schemas not visible in source code. Function signatures show parameter names and types, but no JSON Schema definitions with constraints, enums, or ranges are provided. Cannot verify schema completeness.
Output schemas completely undocumented. Tools return dict objects with no documented field structure, types, or required fields. LLMs cannot plan downstream tool calls or extract data reliably.
No tool annotations for destructive operations. 'call' (with write=True), 'reply', 'hang_up', and 'report_progress' modify state but lack destructiveHint or idempotentHint annotations to signal safety implications to agents.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 57 | 2026-07-28+ | v2 |
Close a call when the conversation has served its purpose. The transcript is kept, but no further replies are possible on this call_id — a new topic deserves a new call.
List who you can call and how to shape the call. For each agent: availability; configured_model (TeLLMphone config.toml override, or null if the CLI chooses); and models — each entry's id is a string you can pass to call's model= as-is. Exactly one model has default=true (catalog suggestion for ordinary second opinions). Read description for personality-aware routing (which roles fit this tier/vendor). Also lists personalities. Check here before calling if you're unsure a personality, agent, or model id exists.
Send a follow-up message on an existing call. The callee's session is resumed with full context of the conversation so far — no need to repeat anything. If you've lost track of your call ids, check_messages lists the open calls for a project. status='busy' means a turn is still running; try again later.
Send an intermediate update while working on a detached call. This is only available to a callee running inside an active call. The update is delivered through the caller's mailbox without completing the turn; the final answer should still be returned normally.
Parameter 'mode' in 'call' tool lacks enum constraint. Description mentions 'wait' and 'voicemail' but no formal enum prevents LLMs from hallucinating invalid modes like 'async' or 'defer'.
Error handling lacks recovery guidance. Descriptions do not explain what errors are retryable, what indicates user misconfiguration, or what the LLM should do if a call fails. No error classification pattern.