telldone-mcp
Voice recording is available on Android (coming soon), allowing users to dictate thoughts that AI automatically structures into notes, tasks, events, and reports.
Voice recording is available on iOS and Apple Watch, enabling voice-first planning where dictated thoughts are automatically structured into notes, tasks, events, and reports by AI.
The iOS app is available on the App Store, providing voice recording capabilities that feed into the AI analysis pipeline for structured planning.
Integrates with Windsurf through Codeium's MCP configuration, allowing AI tools to access and manage TellDone productivity data within the development environment.
Users can reference Figma tasks in their voice notes and manage them through the MCP server, such as marking Figma-related tasks as done.
Voice recording is available on iOS, enabling users to dictate thoughts that are automatically structured into notes, tasks, events, and reports by AI.
Used in example scripts to process JSON responses from the MCP server, demonstrating how to interact with TellDone data programmatically.
TellDone MCP Server
Connect your TellDone voice notes, tasks, events, and reports to AI tools like Claude Code, Cursor, Windsurf, Codex, and any MCP-compatible client.
TellDone is a voice-first planning app. Dictate your thoughts, and AI automatically creates structured notes, tasks, events, and daily productivity reports.
Voice recording is available on iOS and Apple Watch. Android coming soon. You can also send text through MCP using process_note for the same AI analysis pipeline.
Use promo code
MCPBETA26after signup to get free MCP access (read & write for 30 days, then read-only for a year).
Quick Start
1. Get Your Token
Sign up at app.telldone.app, then go to Settings > AI Agents (MCP) and click Enable.
2. Connect
Claude Code
claude mcp add telldone --transport http \
https://api.telldone.app/mcp/user/mcp \
--header "Authorization: Bearer YOUR_TOKEN"Cursor .cursor/mcp.json
{
"mcpServers": {
"telldone": {
"url": "https://api.telldone.app/mcp/user/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}Windsurf .codeium/windsurf/mcp_config.json
{
"mcpServers": {
"telldone": {
"serverUrl": "https://api.telldone.app/mcp/user/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}Codex codex.json
{
"mcpServers": {
"telldone": {
"type": "http",
"url": "https://api.telldone.app/mcp/user/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}OpenClaw
Settings > MCP Servers > Add > Name: TellDone, URL: https://api.telldone.app/mcp/user/mcp, Auth: Bearer YOUR_TOKEN
3. Start Using
Ask your AI tool things like:
"What did I work on today?"
"Create a task: review quarterly report, high priority, deadline Friday"
"Find all notes about the marketing strategy"
"Mark the Figma task as done"
"Create an event: team standup tomorrow at 10am, remind me 15 min before"
"Process this meeting summary and extract tasks"
"What events do I have next week?"
"Show me my daily report from yesterday"
Data Formats — Read This Before Parsing Output
Every tool returns JSON. The MCP wire response wraps payloads in result.content[0].text as a JSON-encoded string — parse it with json.loads() (or equivalent) to get the actual data.
All datetimes, dates, and UUIDs in the decoded JSON are STRINGS, not native language types. Do not call .toordinal(), .weekday(), or any datetime method directly on them — you will get TypeError: 'str' has no attribute 'toordinal'. Parse them first.
Scalar output types
Field shape | Wire format | Example | Parse with |
UUID | string (lowercase hex with dashes) |
| use as-is |
Datetime (timestamp) | ISO 8601 string with timezone offset |
|
|
Date (calendar day, no time) |
|
|
|
Boolean |
|
| native |
Integer | JSON number |
| native |
Nullable field | JSON |
|
|
Array / object output types
Field | Wire format |
| array of strings, OR |
| array of ints on input/output |
| array of strings (names/emails) |
| JSON object or |
| always present, possibly empty |
Enum values
priority—"low","medium","high", ornullnote.type—"task","idea","info","status","meeting","event","reflection"note.status—"active","archived"(deleted records are excluded from every read tool)task.status—"todo","done"(query paramstatus="all"means "all not-deleted")event.status—"confirmed","tentative","cancelled"report.type—"daily","weekly","monthly","yearly"source(tasks),completed_by(tasks) — free-form strings:"mcp","app","sync","audio","todoist","notion", etc.
Per-tool output fields
Tool | Returned fields (all at top level of each array item unless noted) |
|
|
|
|
| note fields ( |
| array of notes with |
|
|
|
|
|
|
|
|
|
|
Write tools | minimal: |
Delete tools |
|
|
|
Any tool on error |
|
Input parameter formats
note_id,task_id,event_id,parent_*_id— UUID stringsdate_from,date_to,deadline—YYYY-MM-DDstrings (empty string means "no filter")start_at,end_at,reminder_at— ISO 8601 datetime strings, e.g."2026-04-15T09:00:00Z"or"2026-04-15T09:00:00+00:00"tags— comma-separated string on input (e.g."work,urgent"); stored/returned asstring[]reminder_minutes,attendees— comma-separated strings on input; stored/returned as arraysis_all_day— boolean oncreate_event; string"true"/"false"onupdate_eventrecurrence_rule— RRULE string, e.g."FREQ=WEEKLY;BYDAY=MO,WE,FR"
Parsing example (Python)
import json
from datetime import datetime, date
# tools/call response → text → decode
payload = json.loads(response["result"]["content"][0]["text"])
# Check for error first
if "error" in payload:
raise RuntimeError(payload["error"])
# Events: start_at is a STRING like "2026-04-18T11:30:00+00:00"
for e in payload: # payload is list from get_events
start = datetime.fromisoformat(e["start_at"]) # -> tz-aware datetime
if e["end_at"]:
end = datetime.fromisoformat(e["end_at"])
# Tasks: deadline is a STRING like "2026-04-18" (date only)
for t in tasks_payload:
if t["deadline"]:
d = date.fromisoformat(t["deadline"])
days_left = (d - date.today()).daysNote Fields: title, summary, transcript
Every note has three text fields with different roles and different limits. Choosing the right field matters — LLM clients (Claude Desktop, Cursor, etc.) should split content appropriately when using create_note or update_note.
Field | Role | Limit | Included in report LLM prompts? |
| One-line subject shown in lists, previews, push, email subjects. | 200 chars | ✅ (as list header) |
| 1–3 sentence teaser. | Hard cap 1000 chars — product decision. | ✅ Verbatim. Keep it concise. |
| Full note body. Shown in detail view. | Plan-based (see below) | ❌ Never. Safe to be long. |
Plan-based transcript limits (subscription_plans.max_text_length):
Plan | Max transcript chars |
Free | 2 000 |
Basic | 8 000 |
Pro | 20 000 |
Ultra | 50 000 |
Custom | 100 000 |
Rule of thumb for LLM clients:
Short output (notes, reminders, todos): just
title+summary. Leavetranscriptempty.Long output (meeting notes, drafts, brainstorms, research dumps): put a 1–3 sentence
summaryand the full body intranscript. Do not pack everything into summary — you'll hit the 1000-char error.
Overflow error messages:
summary too long (max 1000 chars, got N). For long-form content use the 'transcript' parameter (plan-based limit).transcript too long (max 20000 chars for pro plan, got N)
Correct usage example (Claude Desktop captures a meeting):
create_note({
title: "Weekly engineering sync — API versioning",
summary: "Team agreed on semver deprecation policy with 6-month sunset window. Owner: Alex. Next sync: Thu.",
transcript: "Full meeting transcript: Alice raised the question of how to deprecate v1...\n\n[... 5 KB of detail ...]",
tags: "engineering,versioning,meeting"
})Why two different caps?
summaryis included verbatim in daily/weekly/monthly report LLM prompts. If every summary could be 20 KB, report prompts would blow up in cost and latency (and risk context-window overflow for heavy users). 1000 chars was set in April 2026 after measuring prod data (median 247, max 554).transcriptis never in report prompts — it only shows up in the UI detail view. Large transcripts cost only storage, not LLM tokens. Capping by plan prevents abuse but otherwise lets you be generous.
Backward compatibility: transcript is an optional parameter (default ""). Old clients calling create_note(title, summary, tags, type) continue to work unchanged — the database stores transcript = NULL. No migration needed.
Tools (20)
All tools include MCP annotations — title, readOnlyHint, destructiveHint, idempotentHint, openWorldHint — so MCP clients can surface the right confirmation UI. Every tool runs against the Telldone database only (openWorldHint: false) — the server never reaches out to external APIs.
Read Tools (9)
Tool | Description |
| Returns the authenticated user's profile including display name, email, locale, timezone, subscription plan, and usage statistics (total notes, tasks, events). Use this to check account status or quota. |
| Lists voice notes with optional filters: |
| Returns a single note by UUID |
| Bulk retrieval of notes with embedded children (tasks + events). Same filters as |
| Lists tasks with filters: |
| Lists calendar events with |
| Returns AI-generated productivity reports. Filter by |
| Returns all user-defined tags sorted by usage frequency (most used first). No parameters. Use to discover available tags before filtering notes or tasks. |
| Hybrid text + semantic search across notes, tasks, and events. Parameter: |
Write Tools (11)
Tool | Description |
| Runs the full AI analysis pipeline on text or audio input — identical to recording a voice note in the mobile app. Accepts |
| Creates a plain text note without AI analysis. Parameters: |
| Creates a task with |
| Creates a calendar event with |
| Updates note fields by |
| Updates task fields by |
| Updates event fields by |
| Marks a task as done by |
| Soft-deletes a note by |
| Soft-deletes a task by |
| Soft-deletes an event by |
All write tools sync in real-time to connected mobile and web clients via WebSocket.
Full Pipeline: process_note
The process_note tool runs the same pipeline as recording in the mobile app:
Text or Audio --> STT (if audio) --> LLM Analysis --> Note + Tasks + Events + TagsText mode (skip STT):
{"name": "process_note", "arguments": {"text": "Need to buy groceries. Meeting with Katie at 3pm."}}Audio mode (base64-encoded):
{"name": "process_note", "arguments": {"audio_base64": "...", "audio_format": "m4a"}}Returns immediately with audio_id. Results arrive via WebSocket or poll with get_notes().
Examples
examples/test-connection.sh
#!/bin/bash
# Test your TellDone MCP connection
TOKEN="${1:?Usage: ./test-connection.sh YOUR_TOKEN}"
URL="https://api.telldone.app/mcp/user/mcp"
echo "=== Testing connection ==="
curl -s -X POST "$URL" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_profile"}}' \
| python3 -m json.tool
echo ""
echo "=== Listing tools ==="
curl -s -X POST "$URL" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' \
| python3 -c "import sys,json; tools=json.load(sys.stdin).get('result',{}).get('tools',[]); print(f'{len(tools)} tools available'); [print(f' {t[\"name\"]}') for t in tools]"examples/daily-summary.sh
#!/bin/bash
# Get today's tasks and notes summary
TOKEN="${1:?Usage: ./daily-summary.sh YOUR_TOKEN}"
URL="https://api.telldone.app/mcp/user/mcp"
TODAY=$(date +%Y-%m-%d)
call() {
curl -s -X POST "$URL" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "$1"
}
echo "=== Today's Notes ($TODAY) ==="
call "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"get_notes\",\"arguments\":{\"date_from\":\"$TODAY\",\"limit\":20}}}" \
| python3 -c "
import sys, json
r = json.loads(json.load(sys.stdin)['result']['content'][0]['text'])
for n in r: print(f' [{n[\"type\"]}] {n[\"title\"]}')" 2>/dev/null
echo ""
echo "=== Active Tasks ==="
call '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_tasks","arguments":{"status":"todo","limit":10}}}' \
| python3 -c "
import sys, json
r = json.loads(json.load(sys.stdin)['result']['content'][0]['text'])
for t in r: print(f' [{t[\"priority\"]}] {t[\"title\"]}')" 2>/dev/null
echo ""
echo "=== Upcoming Events ==="
call "{\"jsonrpc\":\"2.0\",\"id\":3,\"method\":\"tools/call\",\"params\":{\"name\":\"get_events\",\"arguments\":{\"date_from\":\"$TODAY\",\"limit\":5}}}" \
| python3 -c "
import sys, json
r = json.loads(json.load(sys.stdin)['result']['content'][0]['text'])
# NOTE: start_at is a STRING like '2026-04-18T11:30:00+00:00' — parse before date math
for e in r: print(f' {e[\"start_at\"][:16]} {e[\"title\"]}')" 2>/dev/nullexamples/create-task.sh
#!/bin/bash
# Create a task via MCP
TOKEN="${1:?Usage: ./create-task.sh YOUR_TOKEN 'Task title'}"
TITLE="${2:?Usage: ./create-task.sh YOUR_TOKEN 'Task title'}"
PRIORITY="${3:-medium}"
curl -s -X POST "https://api.telldone.app/mcp/user/mcp" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":{\"name\":\"create_task\",\"arguments\":{\"title\":\"$TITLE\",\"priority\":\"$PRIORITY\"}}}" \
| python3 -m json.toolPlans and Access
Plan | MCP Access | Read | Write | Price |
Free | -- | -- | -- | $0 |
Basic | -- | -- | -- | $4.99/mo |
Pro | Read & Write | 9 tools | 11 tools | $11.99/mo |
Ultra | Read & Write | 9 tools | 11 tools | $24.99/mo |
Pro and Ultra have the same MCP tools. Ultra has higher quotas (unlimited notes, 1500 STT min/mo, 300 uploads/day).
Authentication
Every request requires a Bearer token in the Authorization header. Tokens are generated in the web app settings.
Regenerate: Settings > AI Agents > Regenerate (old token revoked instantly)
Disable: Settings > AI Agents > Disable (token deleted)
Rate limit: 5 requests/second
Transport
MCP Streamable HTTP (stateless). Each request is independent.
POST https://api.telldone.app/mcp/user/mcp
Authorization: Bearer <token>
Content-Type: application/json
Accept: application/jsonLinks
App: app.telldone.app
Website: telldone.app
Docs: docs.telldone.app
iOS App: App Store
This server cannot be installed
Maintenance
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/exp78/telldone-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server