On this page
Alfie MCP server
Give an AI agent Alfie's tools to transcribe recordings, poll jobs and read notes. It uses the same API keys, scopes, credits and rate limits as the REST API.
MCP setup#
Point your client at https://alfienotes.com/api/mcp with an API key in the Authorization header, replacing alfie_YOUR_API_KEY.
Add to claude_desktop_config.json (Settings → Developer → Edit Config), then restart Claude Desktop. It uses the mcp-remote bridge, so Node.js must be installed.
{
"mcpServers": {
"alfie": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://alfienotes.com/api/mcp",
"--header",
"Authorization:${ALFIE_AUTH_HEADER}"
],
"env": {
"ALFIE_AUTH_HEADER": "Bearer alfie_YOUR_API_KEY"
}
}
}
}Run once in your terminal. Add --scope user to make it available in every project.
claude mcp add --transport http alfie https://alfienotes.com/api/mcp \
--header "Authorization: Bearer alfie_YOUR_API_KEY"Add to ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project).
{
"mcpServers": {
"alfie": {
"url": "https://alfienotes.com/api/mcp",
"headers": {
"Authorization": "Bearer alfie_YOUR_API_KEY"
}
}
}
}Streamable HTTP, stateless, JSON responses. Send one JSON-RPC message per POST with the Accept header shown. No session id is needed.
curl -X POST "https://alfienotes.com/api/mcp" \
-H "Authorization: Bearer $ALFIE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
curl -X POST "https://alfienotes.com/api/mcp" \
-H "Authorization: Bearer $ALFIE_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_credits","arguments":{}}}'The server is stateless streamable HTTP: POST only, one JSON-RPC message per request (no batches), bodies up to 256 kB. Bearer keys only, no cookies or OAuth.
Tools#
| Tool | Scope | What it does |
|---|---|---|
| get_credits | notes:read | Your plan and remaining credit, in seconds of media. |
| submit_youtube | notes:write | Transcribe a public YouTube video. Spends credits. |
| create_upload_url | notes:write | Step 1 for a local WAV file: a pre-signed URL to PUT the bytes to. Free. |
| submit_upload | notes:write | Step 2: transcribe the uploaded WAV. Spends credits. |
| get_job | notes:read | Job status: queued, processing, succeeded, failed or expired. |
| list_notes | notes:read | Your notes, newest first, with cursor paging. |
| get_note | notes:read | A note with its timestamped transcript and latest summary. |
| export_note | notes:read | The transcript as plain text. |
| set_note_sharing | notes:write | Make a note public or private. Only when the user asks. |
Tool errors come back as isError results carrying the same {"error":{"code",…}} body as the REST API. The write tools accept an optional idempotency_key for safe retries.
End-to-end example#
From an MCP client
Ask your agent something like this:
Transcribe https://www.youtube.com/watch?v=YOUR_VIDEO_ID with Alfie and give me a one-page study guide.get_credits({})Check there is enough credit for the recording.submit_youtube({"url":"https://www.youtube.com/watch?v=YOUR_VIDEO_ID","idempotency_key":"week-1-lecture"})Returns a job at once. Spends credits equal to the video length.get_job({"job_id":"YOUR_NOTE_ID"})Poll every 10–30 seconds until succeeded or failed.get_note({"note_id":"YOUR_NOTE_ID"})Timestamped transcript and the latest summary.
With the REST API (shell script)
This script uploads a local recording, waits for the transcript and saves it to lecture.txt.
#!/usr/bin/env bash
# Transcribe a local recording with the Alfie REST API. Needs curl, jq and ffmpeg.
set -euo pipefail
export ALFIE_API_KEY="alfie_YOUR_API_KEY"
API="https://alfienotes.com/api/v1"
AUTH="Authorization: Bearer $ALFIE_API_KEY"
# 1. Convert to WAV (the only accepted upload format).
ffmpeg -i lecture.m4a -ac 1 -ar 16000 lecture.wav
# 2. Get a pre-signed upload URL (no credits spent).
UPLOAD=$(curl -sf -X POST "$API/uploads" -H "$AUTH" -H "Content-Type: application/json" \
-d '{"filename":"lecture.wav","content_type":"audio/wav"}')
UPLOAD_ID=$(echo "$UPLOAD" | jq -r .upload_id)
UPLOAD_URL=$(echo "$UPLOAD" | jq -r .upload_url)
# 3. PUT the bytes, with the required Content-Type, within 10 minutes.
curl -sf -X PUT "$UPLOAD_URL" -H "Content-Type: audio/wav" --data-binary @lecture.wav
# 4. Submit the job. Credits for the audio length are reserved now.
JOB_ID=$(curl -sf -X POST "$API/jobs" -H "$AUTH" -H "Content-Type: application/json" \
-H "Idempotency-Key: lecture-$UPLOAD_ID" \
-d "{\"type\":\"upload\",\"upload_id\":\"$UPLOAD_ID\"}" | jq -r .job.id)
# 5. Poll every 15 seconds until the job finishes.
while true; do
STATUS=$(curl -sf "$API/jobs/$JOB_ID" -H "$AUTH" | jq -r .job.status)
echo "status: $STATUS"
case "$STATUS" in succeeded) break ;; failed|expired) exit 1 ;; esac
sleep 15
done
# 6. Save the transcript.
curl -sf "$API/notes/$JOB_ID/export?format=txt" -H "$AUTH" > lecture.txt