riffado-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., "@riffado-mcplist my recent voice recordings with summaries"
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.
riffado-mcp
An MCP server for the Riffado voice-recording archive (Plaud device → Riffado app): transcripts, AI summaries, key points, action items. Strictly read-only. Talks to the Riffado Postgres database directly (not the Riffado API), decrypts at-rest ciphertext in-process, and serves it over MCP — stdio for Claude Code, Streamable HTTP + OAuth for Claude Web/iOS.
Tools
Tool | What it does |
| List recordings (newest first by default), with title, date, duration, transcript sources, optional summary snippet. |
| Two-stage search over titles/summaries/key points/action items, then transcripts, in-process (the DB only holds ciphertext). |
| Full detail for one recording: metadata, summary, key points, action items, a pageable transcript slice (fetched on demand). |
| Flattened action items across recordings, each tagged with its source recording. |
| Recording count, total/median duration, first/last date, transcripts per source/provider, coverage gaps. |
riffado_search: two-stage, and the deep parameter
Search runs in two stages. Stage 1 scores the whole corpus against
pre-normalized titles/summaries/key points/action items only (cheap, always
in memory). Stage 2 fetches and scans transcript text, but only for the
top-ranked stage-1 candidates (K = min(max(limit * 3, 30), 200)) — not the
whole corpus. scope: "summary" stops after stage 1 (no transcript fetch
at all); scope: "all" (default) and scope: "transcript" run stage 2 over
the candidate set.
This means a term that appears only in one recording's transcript, and
nowhere in any title/summary/key point/action item, may not surface unless
that recording happens to rank in the top-K by cheap-field score. Pass
deep: true to scan every recording's transcript that passes the
from/to date filter instead of just the candidates — slower, and its
cost scales with corpus size, so combine it with from/to when possible.
The tool's response says explicitly when results were narrowed this way
(i.e. whenever deep is false), so a client can tell a no-hit result from
a real absence rather than assume one.
Also: resource riffado://index (markdown index), resource template
riffado://recording/{id}, and prompt riffado_ask (carries the answering
rules — cite date+title, quote verbatim, transcript beats AI summary, flag
ASR misreads, never fill gaps from general knowledge).
Related MCP server: gilbert-mcp
Example questions
Things you can ask Claude once the server is connected:
"What did I record yesterday?"
"What action items came out of my recordings this week?"
"Give me the transcript of my last call with Sarah." (paged in slices for long recordings)
"How many recordings do I have, and what's my average recording length?"
"Which of my recordings don't have a transcript yet?"
More complex, research-style questions Claude can answer by combining several tool calls (search → pull the matching recordings → read/quote transcripts):
"Across all my calls with customer Acme, what's the recurring technical pain point they keep bringing up?"
"What does customer Acme's current tool stack look like, based on everything they've mentioned across our calls?"
"When does Acme's contract expire, and did we discuss a renewal date in any recent call?"
"Do a deep search (
deep: true) through all transcripts for 'Meier contract' — this might not show up in summaries — and tell me what was agreed.""Compare what customer X and customer Y said about pricing across all our calls with them — where do their objections differ?"
"Build a timeline of everything discussed with Acme this quarter, with dates and direct quotes."
Environment variables
Var | Default | Notes |
| required |
|
| required | 64 hex chars (32-byte AES key) |
| — | restrict to one user |
| — | e.g. |
|
|
|
|
| container sets host |
| — | shared secret, min 32 chars when set; unset = HTTP transport runs unauthenticated (loud warning logged) |
|
| |
|
| only effective with a token set |
| — | OAuth issuer; must be HTTPS unless |
|
| container: |
|
| Express |
|
| idle-session expiry; |
|
| decrypted-store TTL |
|
| passed to the pg pool |
Health checks
GET /health is the one route auth never gates — liveness only:
{ status, timestamp }. GET /health/details adds session count, DB
reachability and the cached recording count, and requires the same
auth as every other route (shared token/OAuth, or open if
HTTP_AUTH_TOKEN is unset).
Quickstart: Claude Code (stdio)
npm install
npm run build{
"mcpServers": {
"riffado": {
"command": "node",
"args": ["/path/to/riffado-mcp/dist/index.js"],
"env": {
"DATABASE_URL": "postgresql://postgres:...@riffado-db:5432/riffado",
"ENCRYPTION_KEY": "..."
}
}
}
}Quickstart: Claude Web / iOS (HTTP + OAuth)
Run the container with
TRANSPORT=http,HTTP_PUBLIC_URLset to the public HTTPS URL, andHTTP_AUTH_TOKENset to a shared secret.In Claude, add a custom connector pointing at
https://riffado-mcp.example.com/mcp.Claude opens the login page; paste the
HTTP_AUTH_TOKENvalue. Claude then holds a normal OAuth bearer token — the connector survives process restarts because OAuth state is persisted toHTTP_OAUTH_STATE_FILE.
Docker Compose example
services:
riffado-mcp:
image: ghcr.io/l480/riffado-mcp:latest
restart: unless-stopped
read_only: true
cap_drop: [ALL]
security_opt: [no-new-privileges:true]
user: "7333:7333"
environment:
TZ: Europe/Berlin
DATABASE_URL: postgresql://postgres:...@riffado-db:5432/riffado
ENCRYPTION_KEY: "..."
HTTP_PUBLIC_URL: https://riffado-mcp.example.com
HTTP_AUTH_TOKEN: "..."
volumes:
- /opt/riffado-mcp/data:/app/data
networks: [root_default]/app/data must be a writable volume — the image runs --read-only
otherwise, so it needs somewhere to persist oauth-state.json across
container recreation.
Development
npm run lint # eslint
npm run format:check # prettier --check
npm run typecheck # tsc --noEmit, src + test
npm test # vitest, unit tests only
npm run build # tsc -> dist/Integration tests need a real Postgres:
docker compose -f docker-compose.test.yml up -d
TEST_DATABASE_URL=postgresql://postgres:postgres@localhost:5433/riffado_test \
npm run test:integration
docker compose -f docker-compose.test.yml downPerformance
Search is two-stage: cheap fields (title/summary/key points/action items)
rank the whole corpus first, then only the top candidates get their
transcripts fetched and scanned — deep: true trades that speed for full
recall by scanning every date-filtered recording's transcript instead, and
is far slower by design. Headline number: a 3-term scope: "all" search at
20 000 recordings runs in ~175 ms (p50).
Full measured numbers and limitations: docs/performance.md.
Harness + reproduction steps: bench/README.md.
Security notes
Read-only, enforced by Postgres: the pool's session starts with
default_transaction_read_only=on— a bug or prompt injection in a tool handler cannot mutate Riffado, because Postgres itself rejects the write.No plaintext at rest, ever: recordings are decrypted in memory on each cache refresh (default TTL 60s) and never written to disk.
No audio, no storage paths, no credentials, no other users' rows are exposed by any tool.
Container runs rootless (
7333:7333),--read-only,cap-drop: ALL.
License
This server cannot be deployed
Maintenance
Related MCP Connectors
- RecordXOAuthio.recordx
Read-only access to your RecordX meetings: search transcripts, summaries, action items.
- PithflowOAuthcom.pithflow
Read-only access to your own Pithflow meeting notes, transcripts, dictionary and usage.
Search and read your recorded meetings: notes, action items, participants, transcripts.
- SupercutOAuthai.supercut
Read recordings, transcripts, frames, and comments with your permissions.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables read-first interaction with Apple Voice Memos: search transcripts, review action candidates, mark reviewed, prepare tasks for Codex, and render digests.1Apache 2.0
- AlicenseAqualityDmaintenanceRead-only access to your Gilbert meetings, transcripts and summaries over MCP — list, search, and fetch transcripts and summaries.523 npm1MIT
- AlicenseNot gradedqualityBmaintenanceRead-only MCP server that exposes locally generated voice notes to any MCP client, allowing listing, searching, and reading transcripts, summaries, and knowledge graphs from voice-notes sessions.1MIT
- AlicenseAqualityBmaintenanceProvides read-only access to finished meeting transcripts for AI assistants like Claude Code or Codex, enabling them to answer questions or draft summaries based on the transcriptions.43Apache 2.0