MCP authentication
mdedit keeps two authentication paths:
- Scoped API keys work with local stdio, hosted MCP clients that can send
X-API-Key, CI, and headless automation. - Browser OAuth uses pre-registered public Cognito clients with authorization code and PKCE S256 when the host supports it. It is intentionally allowlisted and is not universal MCP OAuth.
The hosted OAuth rollout remains closed until each real host passes the
version-bound compatibility and security gate. No host is advertised as
launch-supported by OAuth while its row is candidate or blocked.
For PKCE, admission is based on the real host flow. Cognito validates an S256 challenge when one is supplied, but it cannot be configured to reject every authorization request that omits PKCE. PKCE remains preferred. During the current time-limited risk acceptance, mdedit may enable a known host without PKCE only after its real, version-bound exact callback, state, resource/scopes, token exchange, refresh/reconnect, revocation, and document workflow pass. Clients that already use PKCE keep it, and Cognito must validate their verifier.
Current host matrix
| Host | Local stdio with API key | Hosted API key | Hosted browser OAuth |
|---|---|---|---|
| mdedit CLI | Not an MCP host | Not an MCP host | Supported with S256 PKCE |
| Claude Code | Supported | Supported when custom headers are available | Candidate, release-gated |
| Gemini CLI | Supported | Supported when custom headers are available | Candidate, release-gated |
| Cursor | Supported | Supported when custom headers are available | Blocked on exact callback/public-client proof |
| ChatGPT | Not applicable | Depends on custom-header support | Candidate, live workflow pending |
| Perplexity | Not applicable | Depends on custom-header support | Blocked on public-client proof |
The source-controlled evidence matrix is maintained in
docs/security/supported-mcp-oauth-hosts.md (opens in a new tab).
A blocked or incompatible host does not trigger dynamic client registration,
CIMD, a client secret in a public package, another authorization server, or a
protocol translation service.
Local stdio with a scoped API key
claude mcp add --transport stdio \
--env MDEDIT_API_KEY=mdh_your_key \
mdedit -- npx -y @mdedit/mcp-serverAPI keys are a first-class path, not a deprecated fallback. Create separate, least-privilege keys for each agent and keep literal keys out of shared project configuration.
Local stdio with the shared OAuth profile
Once the mdedit public OAuth client is enabled for your environment:
mdedit auth login
claude mcp add --transport stdio mdedit -- npx -y @mdedit/mcp-serverThe MCP package reads the same OS-keychain profile directly. It does not spawn
the mdedit executable. Set MDEDIT_PROFILE=work to select a named profile.
Generic hosted API-key setup
Clients that support Streamable HTTP and custom headers can connect to:
https://mcp.mdedit.ai/mcpSend:
X-API-Key: mdh_your_keyBrowser-origin requests and caller-supplied session IDs are rejected. Hosted MCP is stateless, so clients must not depend on load-balancer stickiness.
Scopes
Common least-privilege sets:
- Review only:
articles:read,reviews:write - Direct editing:
articles:write - Read publication state:
publishing:read - Explicit publish and unpublish:
publishing:write
Write scopes satisfy the corresponding read operation. Missing scopes fail closed and MCP tools return an incremental authorization challenge.
Public-link modes
publish_article always requires confirmPublic: true because it changes public
visibility. For a new link, omitting mode creates a Live link: later durable edits
update the same URL. Pass mode: "snapshot" for a frozen artifact.
For an existing link, omit mode to preserve its stored setting, or pass "live" /
"snapshot" to switch deliberately. get_publish_status returns the stable URL,
mode, source revision/version, last successful sync, and sync status. A failed Live
sync leaves the last good public content available.
Existing links created before Live mode are Snapshots until explicitly changed.
Revocation
Cognito does not provide truthful per-host revocation for this public-client model. Settings therefore offers Revoke all sessions, which signs out all mdedit web and OAuth refresh sessions for the account. It does not revoke API keys.
Already-issued access tokens may remain usable for up to 15 minutes. After that bound, refresh and new MCP or collaboration connections fail until the user signs in again.
Troubleshooting
- A callback mismatch is not fixed with a wildcard. The host remains unsupported until its exact callback is verified and registered.
invalid_clientmeans the host is not admitted or used the wrong public client ID.insufficient_scopemeans the tool needs an additional listed permission.- Headless machines should use a scoped API key.
--no-browserstill requires a browser on the same machine and is not device flow. - Unknown OAuth clients are rejected. Use an API key if the host supports custom headers.