This page covers the threat model behind each Model Context Protocol (MCP) surface: where secrets are stored, what a write requires, and how to take access back. For the request path each of these sits on, see How MCP works.

Where credentials live

A deployment keeps three secrets in the OS keychain (macOS Keychain, Linux Secret Service, or Windows Credential Manager, all under the service name moodle-cli-mcp, falling back to an OS-protected file only when the native backend is unavailable):
  • The MCP access token, what a bridge or native header client presents as its Bearer token.
  • The session sync token, which lets your machine push a refreshed Moodle session to the Worker.
  • The session encryption key, which the Worker uses to encrypt the Moodle session at rest.
The Worker itself never stores your tokens in plain text: it only keeps a way to check that a token presented to it is correct, plus the key it uses to encrypt your session. Your Moodle sign-in — the browser cookie and related account details — is encrypted and stored in your own private Worker, one per deployment.

What is never logged

Your Moodle session cookie and MCP access token are never logged or printed. Tool results only ever contain what a tool explicitly returns, error responses never echo back your request or a stack trace, and a status check reports only timestamps and a shortened, unusable fragment of your credential, never the credential itself. moodle --verbose prints sanitized request paths and timing, never a URL query or a credential.

Posting and write gates

submit only exists on the local server, because it uploads files from the machine the server runs on — the remote Worker has no filesystem to read them from. It defaults to dry_run: true, so a client has to show you the plan and call it again with dry_run: false before anything uploads, and final: true submits for grading, which can’t be undone.

Revocation

Revoking a client (or all of them) deletes its OAuth access and refresh tokens, any pending authorization code, and closes any open pairing window — not only the token itself. A static-credential rotation (--rotate-token) invalidates every OAuth grant immediately, because the Worker checks the owner’s credential identity on every OAuth request and wipes all clients the moment it changes.

Token and key rotation

Rotating the static token doesn’t cut your existing bridge or header client off mid-request: the previous token stays valid for a ten-minute overlap window while the new one takes effect, and a local bridge configuration resolves the new token automatically from the keychain. A native remote-header client, which has the old token baked into its own config, has to be given the new one by hand. Rotating the encryption key re-encrypts your stored session under the new key and checks it decrypts correctly before the previous key is dropped. --repair repairs your stored session after an interrupted upload; a deploy itself updates the Worker’s code and secrets together, so a failed deploy never leaves it half-updated.

Pairing window

moodle mcp pair opens a ten-minute, single-use window: the code it prints allows five wrong guesses before the window closes, and a correct guess consumes it immediately. The Worker refuses every OAuth approval while no window is open, so an approval page you didn’t ask for has nothing to approve.

Limits

A Moodle Worker accepts at most 20 approved OAuth clients. If you hit that limit, revoke a client you no longer use (see Revocation, above) before pairing a new one. By default, a managed Moodle deployment doesn’t turn on Cloudflare’s request logging, so sign-in form data and query details are never retained in Cloudflare’s own logs.

Hosted Ed Discussion: data retention

The hosted OAuth service verifies a token against Ed’s API before it ever stores it. OAuth authorization codes are deleted once used or expired; access and refresh tokens are kept until they expire, are revoked, or are cleaned up; your encrypted Ed credential is kept until you rotate it, delete your account, or the service removes it; a token that stops working is marked invalid rather than deleted outright, so a later valid token can replace it. Deleting your account removes your account, encrypted Ed credential, browser session, and OAuth records from the live database — backups follow the hosting provider’s own retention and can outlast that. There is no uptime guarantee on the hosted service.

Practical advice

  • One Worker per account. A Moodle Worker is pinned to a single account and rejects a session upload for any other; treat Ed the same way rather than sharing one deployment’s credential across accounts.
  • Never put a token in a URL. The Moodle Worker rejects a credential passed as a query parameter (access_token, api_key, apikey, authorization, bearer, or token) before it matches any route, precisely so a token doesn’t end up in a server access log or browser history.
  • Prefer the bridge over native headers. moodle mcp bridge keeps your Bearer token out of every client’s config file; a native remote-header connection puts it in plain text on disk, and you’re the one who has to remember to update it after a rotation.