rosetta-mcp
Allows searching, reading, and drafting emails (no send/delete) with attachment transcription.
Integrates with Google services for Gmail (search, read, draft) and Google Calendar (list, create, update) via server-side credential storage.
Provides access to Google Maps services including Routes, Places, and Weather data.
Provides access to SNCF (French national railway) transit information.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
rosetta-mcp
Rosetta is a modular MCP hub: one HTTP service hosting many thin MCP servers (addons), each mounted under its own path, behind a single OIDC bearer-token authentication layer.
Like the stone: one endpoint, and every agent reads it in its own tongue.
agent ──Bearer JWT──► https://rosetta.example.com/<addon>/ (one path per addon, see below)
https://rosetta.example.com/health (unauthenticated, per-addon state)Why a hub: provider API keys stay server-side (one deployment, one place to rotate), agents only ever hold an access token, and adding another MCP is a new module and nothing else - no new pod, no key sprayed into agent environments.
Where to go next: ARCHITECTURE.md for the map of the code and its
invariants; docs/addons.md for each addon; docs/donnees.md for the files the
hub writes; docs/parcours.md and docs/ecrans.md for the user journeys; docs/recommandations.md for the
improvements that would change behaviour, written as options for the operator to decide (none applied).
Addons
An addon is a module in src/rosetta/addons/ exposing:
from ._common import new_server
mcp = new_server("name") # FastMCP: stateless streamable HTTP at "/"
required_env = ["SOME_API_KEY"] # optional
identity = "user" # optional: refuse machine tokens (403)The loader mounts each addon under /<module-name> and isolates failures.
The MCP endpoint is /<name>/ (with the trailing slash; /<name> answers 307).
Addon state | Meaning |
| mounted, fully operational |
| mounted, but some |
| import/startup failed - addon skipped, hub and other addons stay up |
| excluded by |
Per-addon state is reported on GET /health. Modules starting with _ are shared
helpers, never mounted. Most addons also run standalone over stdio for local debugging
(python -m rosetta.addons.maps); courrier, git, github, postier, quotas and verbatim do not (no __main__ block).
An addon with identity = "user" holds per-user credentials server-side, keyed on the
caller's username; machine tokens are refused on its path. Its enrolment routes sit
behind the ingress SSO instead of the hub JWT. Details: docs/addons.md and
docs/donnees.md.
Addon | Role | Details |
| The family's own Zimbra mailboxes over IMAP: search, read, drafts, disposable OVH aliases (user) | |
| Open Food Facts, read-only: product by barcode, text search; no key | |
| Smart-HTTP git proxy to GitHub, using the | |
| GitHub reads and bounded writes (commit, tag, repo creation, PR merge) as the caller, via a GitHub App (user) | |
| Gmail and Google Calendar as the caller: read, drafts, send, events (user) | |
| Google Maps Platform behind one key: routes, places, weather | |
| Tide times and coefficient for the French coast (api-maree.fr) | |
| Hourly wind in knots for sailing spots (Open-Meteo); no key | |
| Machine-class mail sending from the assistant's mailbox: locked sender, allowlisted recipients, hourly cap | |
| Usage gauges of a Claude subscription and when they reset | |
| Walking and cycling routes on OpenStreetMap (BRouter) with elevation, and Overpass points of interest | |
| Next departures and disruptions, SNCF and RATP/IDFM (Navitia) | |
| What was said in a video: published subtitles split into timestamped sentences ( | |
| The caller's Withings data, read-only: activity, body measures, sleep, workouts, devices (user) |
Related MCP server: Cortex Gateway
Authentication
Rosetta is an OAuth 2.1 resource server. It stores no credentials and no
users: it validates JWT access tokens (RFC 9068)
issued by an external OIDC provider (tested with
Authelia >= 4.39, clients configured with
access_token_signed_response_alg != none), against the provider's JWKS.
Machine agents use the
client_credentialsgrant - no human in the loop.User-delegated access (the
identity = "user"addons) usesauthorization_coderefresh, or the device code flow for headless bodies.
RFC 9728 protected-resource metadata is served at
/.well-known/oauth-protected-resource(and per addon), and every 401 carries theWWW-Authenticatepointer, so OAuth-aware MCP clients can discover the authorization server on their own.
The token normally travels as Authorization: Bearer. A Basic header is
accepted as an alternative envelope, taking the password as the token - which
exists for exactly one caller: git cannot be taught to send a Bearer header,
since a credential helper hands it a username and a password and nothing else
(GitHub's own x-access-token:<token> convention). This widens the envelope, not
the trust: the token inside faces the same signature, issuer, audience and expiry
checks.
The challenge had to follow, and only under /git: git does not understand a
Bearer challenge, and faced with one it gives up on "Authentication failed"
without ever asking its credential helper — so a pod holding a valid token
could not push, and the helper meant to carry the channel and the shield could
never have run. A 401 under /git therefore answers Basic realm="rosetta";
everywhere else the RFC 9728 pointer is unchanged, and no browser is ever invited
to prompt on an MCP endpoint.
Note the trailing slash: the MCP endpoint of an addon is /<name>/ - /<name>
answers with a 307 redirect.
One deliberate exception to "everything needs a token": the MCP
surface-reading methods - initialize, notifications/initialized,
ping, tools/list - are served without a token on the mount roots, so
an external catalog observer (a policy gateway watching the tool surface for
drift) can poll them while deliberately holding no hub credential. The window
is narrow and fail-closed on every edge: POST only (the SSE stream stays
authenticated), single-segment paths only (never /git/'s bare-HTTP routes),
one well-formed JSON-RPC object only (a batch is refused even when every
element is listed, as are unreadable or oversized bodies), and only when no
token was presented at all - an invalid token is refused even on a listed
method. Everything else, tools/call first, keeps the exact 401 + discovery
pointer above. Tool names and schemas thereby become readable without a
token: an accepted trade-off for a hub on an internal network, behind a
gateway meant to be its only caller.
Configuration
Everything is read from the environment (with ROSETTA_AUTH=off, ROSETTA_ISSUER and ROSETTA_EXTERNAL_URL are not needed to start). "Required" means the hub or the addon does not
work without it. The hub does not refuse to start on a missing one; an addon's
required_env mounts that addon degraded and nothing else.
Variable | Scope | Required | Default | Meaning |
| hub | no |
|
|
| hub | yes (unset, no default: tokens cannot be validated without it) | none | OIDC issuer (token |
| hub, | yes (unset, no default: metadata, callbacks and links are unusable without it) | none | public URL of the hub (RFC 9728 metadata, OAuth callbacks, enrolment links) |
| hub | no |
| accepted token |
| hub | no |
| JWKS endpoint override |
| hub | no | empty | extra issuers accepted for validation only, comma-separated, each |
| hub | no | all discovered | comma-separated allowlist of addons to mount |
|
| no |
| local zone for bare dates and "today" |
|
| yes | none | Google Maps Platform key |
|
| yes | none | SNCF Navitia key |
|
| yes | none | IDFM PRIM key |
|
| yes | none | api-maree.fr key |
|
| no | empty | named spots as JSON, |
|
| no |
| per-user credential store (volume) |
|
| no |
| account index in the draft web links ( |
|
| yes | none | GitHub App client id |
|
| yes | none | GitHub App client secret |
|
| no |
| account under which |
|
| no |
| per-user token store (volume) |
|
| no | empty |
|
|
| yes | none | Withings OAuth app client id |
|
| yes | none | Withings OAuth app secret |
|
| no |
| per-user credential store (volume) |
|
| no |
| per-user credential store (volume) |
|
| only with several enrolled subjects | inferred when exactly one is enrolled | whose gauges a machine token reads |
|
| no | empty = every machine identity the hub authenticated | comma-separated machine subjects allowed to read |
|
| no |
| User-Agent sent to Anthropic's usage endpoint; an unrecognised one gets rate-limited |
|
| no |
| User-Agent for Open Food Facts |
|
| no |
| routing engine; read per call |
|
| no |
| OSM point lookups |
|
| no |
| User-Agent sent to Overpass |
|
| no | IGN Géoplateforme elevation endpoint | used only for |
|
| yes | none | IMAP host of the mailboxes (port 993, TLS) |
|
| yes | none | family mail domain |
|
| no |
| IMAP folder receiving drafts |
|
| yes in practice (mailbox passwords come only from the vault) | empty | Vault (OpenBao) address from which mailbox passwords are read |
|
| no |
| Vault JWT auth mount |
|
| no |
| Vault role |
|
| no | none | OVH API key, needed for the alias tools |
|
| no | none | OVH API secret, same |
|
| no | none | OVH consumer key, same |
|
| yes | none | sender address and login, fixed |
|
| yes | none | mailbox password |
|
| no, but without it every recipient is refused | empty | comma-separated recipient patterns ( |
|
| no |
| sliding-window hourly cap |
|
| no |
| SMTP host |
|
| no |
| IMAP host for the copy in the sent folder |
|
| no |
| sent folder |
Development
No GitHub job plays the suite. A commit is proved by its tests-complets attestation, posted by
attester --publier (a command of the chain, run from a workstation or a pod; AD-preuve-1). The
suite is declared in .sdlc.yaml and played alone by scripts/suite-attestee.sh
(fresh .venv-suite, same steps, any Python ≥ 3.12). By hand, the equivalent:
python3 -m venv .venv
.venv/bin/pip install -e .[dev]
.venv/bin/ruff check . # lint
.venv/bin/pytest --cov --cov-report=term-missing # tests + coverage
ROSETTA_AUTH=off .venv/bin/uvicorn rosetta.main:app --port 8200 # local start (not part of the suite)Once started with auth off, an addon answers MCP over POST on its slash-terminated path, e.g.
curl -s -X POST localhost:8200/food/ -H 'content-type: application/json' -H 'accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'.
To run it as the container (auth off, no secrets, local only):
docker build -t rosetta-mcp .
docker run --rm -p 8200:8200 -e ROSETTA_AUTH=off rosetta-mcp # then: curl localhost:8200/healthPython ≥ 3.12 is required (pyproject.toml). Coverage fails below fail_under in
pyproject.toml; lint ignores are listed there with their reason. ROSETTA_AUTH=off disables
token validation, for local development only. With no secrets set, /health
answers degraded and names the addons whose variables are missing: the expected state of a
bare local start. tests/test_surface.py pins the tools, routes, health and environment
variables read by the code against tests/surface.json; after an intended change, rewrite the snapshot with
python tests/test_surface.py --write and review the diff.
Deployment
Published as ghcr.io/antorfr/rosetta-mcp (SemVer tags and sha-, latest for the highest stable version; the docker-publish workflow
runs only on a v* tag, after checking the tests-complets attestation of the tagged commit; no :main image). Plain
container, port 8200, runs as user rosetta (uid 1000), configured by environment.
The addons that store credentials (google, github, withings, quotas) write per-user files under
/data (/data/google, /data/github, /data/withings, /data/quotas by default, each
overridable, see Configuration). The image creates no /data and declares no VOLUME:
mount a persistent volume at /data, writable by uid 1000, or credentials are lost with the
container. Formats: docs/donnees.md.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Managed MCP gateway with OAuth, per-agent permissions, shared accounts and activity logs.
One OAuth connector for your team's remote MCP servers, with Tool Packs, guardrails and logs.
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
One MCP endpoint for your company's tools, with per-person sign-in, policy and audit.
Related MCP Servers
- AlicenseAqualityDmaintenanceProduction-ready MCP server for Google Maps Platform APIs, providing 11 tools for directions, places, geocoding, traffic, and road data to empower AI agents with location intelligence.1134 PyPI4Apache 2.0
- AlicenseAqualityBmaintenanceA federated MCP gateway that consolidates multiple plain-HTTP backends into a single, OAuth-protected MCP server, enabling agents to access diverse tools through one endpoint with centralized authentication and audit.512MIT
- AlicenseAqualityBmaintenanceMCP server exposing Google Maps Platform to MCP clients with seven tools for geocoding, place search/details, traffic-aware travel times, and time zones. Runs as a local stdio server or a containerized HTTP service with bearer auth, needing only a single API key.7MIT
- AlicenseNot gradedqualityBmaintenanceEnables clients to access multiple backend MCP servers through a single endpoint, with OAuth 2.1 authorization, namespaced tools, and secure credential management.2MIT