#CAS Control-Plane MCP
The @unicas/service-cloudflare Worker exposes the UniCAS control plane to
GitHub Copilot and other remote MCP clients at:
https://api.unicas.work/mcp
The unified Worker accepts an exact allowlist of /mcp, OAuth discovery,
authorization, token, and registration paths. It does not expose an arbitrary
/oauth/* prefix. A present browser
Origin on /mcp must exactly match the configured MCP_PUBLIC_ORIGIN; requests
without Origin remain valid for non-browser MCP clients.
#GitHub Copilot configuration
No API key or OAuth client secret belongs in MCP configuration:
{
"servers": {
"unicas-control-plane": {
"type": "http",
"url": "https://api.unicas.work/mcp"
}
}
}
On first use, the client discovers UniCAS OAuth, opens Google sign-in, and shows
the UniCAS consent page. The resulting access and refresh tokens are UniCAS
tokens. Google tokens are discarded after identity verification and are never
accepted by /mcp.
#Authorization scopes
| Scope | Operations |
|---|---|
control:read |
Identity, stacks, membership, Stack OAuth issuer configuration, observed refDomains, and audit reads |
control:write |
Stack creation and stack metadata updates |
control:security |
Member invitation/removal and Stack OAuth issuer activation |
Scopes do not imply each other. Current stack membership is checked during each
tool call, so removing a member takes effect without waiting for token expiry.
ADMIN_EMAIL_ALLOWLIST, when configured, is also checked during every MCP call.
Access tokens expire after 15 minutes. Refresh grants expire after 8 hours and
refresh tokens rotate. Tokens are audience-bound to the canonical /mcp
resource. The token endpoint also implements RFC 7009 revocation.
#Tools
Read tools:
whoamilist_stacksget_stacklist_membersget_oauth_issuerlist_ref_domainslist_control_audit_eventslist_root_domain_refslist_root_domain_events
Ordinary write tools:
create_stackupdate_stack
Security tools:
invite_memberremove_memberinspect_oauth_issueractivate_oauth_issuer
A stack's signing authority is exclusively a discovered OAuth issuer:
inspect_oauth_issuer validates and persists the issuer's metadata and JWKS
snapshot and returns a control challenge, which the operator signs with a key
the issuer currently advertises and submits as a compact-JWS activation proof
to activate_oauth_issuer. There is no manual issuer or JWK upload path, and
private key material is never a valid MCP input.
Creation tools require an idempotency key. Existing-resource mutations require a current ETag. Member invitations are email-bound and require the email twice. Member removal requires explicit target confirmation. Destructive annotations are advisory metadata; the server always enforces scopes, membership, ETags, confirmations, and service invariants.
list_ref_domains is an audit discovery tool. It lists domains observed in
successful Root Ref writes; domains are not pre-registered or lifecycle-managed
through MCP. A ref domain is an event field used to filter and aggregate Root
Ref audit data, not a separately managed stack resource.
#Worker configuration
Required bindings:
CAS_CONTROL_DB shared UniCAS control D1
OAUTH_KV dedicated OAuth clients/grants/token hashes
Required secrets:
GOOGLE_OIDC_CLIENT_SECRET
OAUTH_STATE_ENCRYPTION_KEY base64url-encoded 32-byte AES key
ADMIN_EMAIL_ALLOWLIST comma-separated emails allowed to log in
CAS_AUDIT_READER_KEY shared key for the private audit-reader RPC
Variables (non-secret; GOOGLE_OIDC_CLIENT_ID is a var, not a secret):
MCP_PUBLIC_ORIGIN=https://api.unicas.work
MCP_MUTATIONS_ENABLED=true
MCP_ALLOWED_ORIGIN_HOSTNAMES=
OIDC_ISSUER=... optional, defaults to Google
OIDC_DISCOVERY_URL=... optional test/local override
MCP_MUTATIONS_ENABLED is the emergency and rollout kill switch. An absent or
non-true value fails closed; read tools remain available while all
write/security handlers reject mutations. Production enables it explicitly
after read-only telemetry and cross-stack
isolation checks pass.
Configure the OAUTH_KV ID in
packages/service-cloudflare/wrangler.toml before deployment. OAuth KV is
not a control-data backup: business state remains in CAS_CONTROL_DB. KV stores
client registrations, grants, and token hashes; deleting a client or revoking a
grant invalidates its tokens.
#Build and validation
pnpm --filter @unicas/control-auth test
pnpm --filter @unicas/service test
pnpm --filter @unicas/service-cloudflare typecheck
pnpm --filter @unicas/service-cloudflare test
pnpm --filter @unicas/service-cloudflare build
pnpm --filter @unicas/service-cloudflare exec wrangler deploy --dry-run
pnpm --filter @unicas/admin-webui test
node stacks/unicas/deploy/mcp-oauth-smoke.mjs
The release gate additionally requires a real GitHub Copilot flow through the
custom domain: discovery, Google login, consent, whoami, a paginated read,
refresh, revoke, and reauthorization. A manually injected bearer token does not
replace that test.
The interactive production smoke prints a loopback OAuth authorization URL and never logs access or refresh tokens. Complete Google account selection and the UniCAS consent page in a browser while its callback listener is running.
#Rollout and incident response
- Create the dedicated production OAuth KV namespace and replace its binding ID.
- Register
https://console.unicas.work/admin/auth/callbackandhttps://api.unicas.work/oauth/google/callbackwith Google before enabling split-origin administrator or MCP login. Keep the existing apex callbacks during the migration window. - Set Worker secrets and deploy
@unicas/service-cloudflarewith mutations disabled. - Validate OAuth discovery and read tools from GitHub Copilot.
- Observe authorization failures, scope/member denials, D1/KV errors, and audit attribution before enabling mutations.
- Enable ordinary and security operations in a controlled maintenance window.
For suspected token theft, keep mutations disabled, revoke the affected grant or
delete the OAuth client, and review cas_control_audit_events by client handle
and tool name. Rotate Google credentials or the state-encryption key only through
Worker secret management. Rotating the state key invalidates in-flight login and
consent transactions but does not decrypt or expose existing OAuth tokens.