MCP server for Matrix chat (Rust + matrix-sdk). matrix-mcp.kampong.social.
  • Rust 98.6%
  • Python 1%
  • Dockerfile 0.4%
Find a file
Julian Lindner d138b09413
All checks were successful
CI / docker (push) Successful in 22s
CI / cargo (push) Successful in 1m13s
fix(mcp): tolerate loosely-serialized integer tool params
Some MCP clients (observed with LangDock) send numeric arguments as JSON
strings ("50") or as an empty string "" when the model leaves a numeric
field blank. Strict serde rejected these with JSON-RPC -32602
("invalid type: string, expected u64"), surfacing as a failed tool call
("read messages" never ran).

Add lenient integer deserializers: accept a JSON number or numeric
string, and treat null / empty / unparseable as absent so the field's
existing #[serde(default)] applies. Wired into every integer input param
(7 `limit` fields, `max_depth`, and the optional `duration_ms`,
`max_room_members`, `since_unix_ms`). JSON schema is unchanged (still
advertises integer); only runtime parsing is relaxed. Regression test
locks blank/string/number/missing behaviour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:41:06 +08:00
.forgejo/workflows feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00
docs feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00
examples docs(readme): bump image pin to v0.3.20, re-add CI badge (Forgejo) 2026-05-19 21:15:00 +08:00
scripts chore: pre-public-release polish (license, README, docs, generalize Gruyere) 2026-05-19 20:27:48 +08:00
src fix(mcp): tolerate loosely-serialized integer tool params 2026-06-22 12:41:06 +08:00
.gitignore chore(repo): publish-ready polish (#72) 2026-05-17 08:54:17 +08:00
AGENTS.md feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00
Cargo.lock feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00
Cargo.toml feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00
CHANGELOG.md feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00
deny.toml chore: pre-public-release polish (license, README, docs, generalize Gruyere) 2026-05-19 20:27:48 +08:00
Dockerfile feat(tts): transcode WAV → Ogg/Opus + emit real waveform (#91) 2026-05-22 15:17:14 +00:00
LICENSE chore: pre-public-release polish (license, README, docs, generalize Gruyere) 2026-05-19 20:27:48 +08:00
README.md feat: voice + TTS voice message MCP tools (#89) 2026-05-22 13:45:28 +00:00
rustfmt.toml feat: phase 0 — repo skeleton, design docs, CI 2026-05-14 11:51:00 +08:00
SECURITY.md docs: tighten README, add SECURITY/THREAT_MODEL/multi-user for OSS release (#63) 2026-05-16 23:14:26 +08:00
THREAT_MODEL.md feat(mcp): paginate encrypted history and harden fetches 2026-06-13 08:45:16 +08:00

matrix-mcp

CI

A remote MCP server that lets claude.ai (or any other MCP client) read, search, and write in your Matrix account including in end-to-end-encrypted rooms using your existing cross-signing identity and Secret Storage recovery key.

It speaks OAuth 2.1 / MCP / RFC 9728 on one side and the Matrix Client-Server API on the other. matrix-rust-sdk handles olm/megolm/cross-signing encrypted history decrypts normally as long as the device is cross-signed, which /setup arranges with your recovery key.

What you get

~58 tools, all carrying MCP annotations (read_only_hint, destructive_hint, idempotent_hint) so client UIs can auto-approve reads and warn before writes.

  • Reads: read_recent_messages, read_thread, search_messages, get_unread_summary, list_joined_rooms, list_recent_activity, room_info, room_members, list_threads_in_room, download_attachment, verify_status, whoami, users_get_profile, get_room_state, get_space_hierarchy, get_account_data, get_user_receipts, get_event_receipts, get_presence, list_ignored_users, invites_list
  • Writes: send_text_message, send_image_from_url, send_file, send_audio, send_voice_message, send_tts_voice_message, send_video, send_reaction, send_bulk, send_broadcast, mark_read, message_edit, message_forward, redact_message, upload_media_from_url, set_account_data, set_presence, set_room_name, set_room_topic, me_set_displayname, me_set_avatar, ignore_user, unignore_user, request_room_keys
  • Room management: room_create, room_create_dm, join_room, leave_room, invite_user, invites_accept, invites_reject, room_pin_message, room_unpin_message
  • Moderation / admin: room_kick, room_ban, room_unban, admin_get_power_levels, admin_set_power_level (constrained by the user's Matrix power level on the room)
  • Self-audit: set_audit_room designate a Matrix room where matrix-mcp posts an m.notice for every write it makes on your behalf

See docs/api-reference.md for arguments and return shapes.

Prerequisites

  • A Synapse homeserver (≥ 1.130) with Matrix Authentication Service / MSC3861. Legacy password-only homeservers won't work.
  • A Matrix account on that homeserver with cross-signing already set up (the standard Element "Set up secure backup" flow). You'll need the recovery key that flow produced.
  • An MCP client that speaks the streamable-HTTP transport (claude.ai Custom Connectors, etc.).

Quick start

docker run --rm -p 3000:3000 \
  -v matrix-mcp-store:/var/lib/matrix-mcp \
  -e MATRIX_MCP_RESOURCE_URL=https://matrix-mcp.your-domain.example \
  -e MATRIX_MCP_AUTHORIZATION_SERVER=https://your-mas.example \
  -e MATRIX_MCP_HOMESERVER_URL=https://matrix.your-domain.example \
  -e MATRIX_MCP_SERVER_NAME=your-domain.example \
  -e MATRIX_MCP_INTROSPECTION_CLIENT_ID=... \
  -e MATRIX_MCP_INTROSPECTION_CLIENT_SECRET=... \
  -e MATRIX_MCP_STORE_DIR=/var/lib/matrix-mcp \
  -e MATRIX_MCP_STORE_PEPPER="$(openssl rand -hex 32)" \
  ghcr.io/jlxq0/matrix-mcp:v0.3.20

Then point a public hostname at it over HTTPS (claude.ai requires https://) and follow docs/onboarding.md to connect. For a Kubernetes deployment with cert-manager, External Secrets, and Traefik Gateway API, see docs/installation.md. For a single-host homelab setup with Caddy or Traefik, see examples/docker-compose.yml.

Multi-user

Every authenticated user gets their own encrypted SQLite store, their own matrix-sdk client, their own audit room (held in their Matrix account_data), and their own rate-limit bucket. Two users share zero state. The MAS introspection middleware accepts any active token, so by default every user on your homeserver can connect same surface as Element. Restrict with an upstream allowlist if you want fewer.

docs/multi-user.md walks through the isolation guarantees.

Security

matrix-mcp gives claude.ai access to your decrypted Matrix content that's the entire point. So:

  1. You extend trust to claude.ai (and Anthropic). If that's not OK, don't connect.
  2. You extend trust to whoever runs the matrix-mcp instance. Run your own.

SECURITY.md covers reporting; THREAT_MODEL.md covers what's in and out of scope.

Development

Rust 1.93+ with edition = "2024".

cargo run                                           # build + run
curl http://127.0.0.1:3000/health                   # health check
cargo fmt --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features --locked

PRs welcome. Run the three checks above before pushing; CI will reject otherwise. Conventional Commits for messages.

Documentation

File Contents
docs/onboarding.md Connecting from claude.ai end-to-end
docs/installation.md Deploy on a VPS or Kubernetes
docs/architecture.md Component diagrams, OAuth dance, sync loop, storage layout
docs/api-reference.md Arguments + return shapes for the most-used tools (full set returned by tools/list)
docs/operations.md Running in production, debug recipes, pepper rotation
docs/security.md Implementation-level security notes
docs/multi-user.md Multi-user isolation guarantees
docs/cross-signing-recover-flow.md E2EE bootstrap, recovery, auto-rotating device id
docs/decisions.md Architecture decision records
SECURITY.md Reporting vulnerabilities
THREAT_MODEL.md What this defends against
CHANGELOG.md Version history

Licence

MIT.

Where this code lives

The canonical source repository is forge.oddie.app/julian/matrix-mcp. github.com/jlxq0/matrix-mcp is a public push-mirror so the code is discoverable and forkable from GitHub. PRs opened on either side are fine; merges happen on Forgejo and are mirrored.