Auth & permissions
apphub-mcp only accepts OAuth 2.0 Bearer tokens. Three-tier scopes (read/write/admin) plus per-app permissions are checked on every call.
Only OAuth tokens, please
The old PAT path is fully removed. The MCP endpoint only accepts OAuth 2.0 Bearer tokens. app-key tokens work for 4 read-only API tools (we'll cover those below).
Send Authorization: Bearer <token> on every /mcp request. Missing or expired tokens come back as 401 Unauthorized with a WWW-Authenticate challenge. The JSON-RPC error code is -32001.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://api.jocodingax.ai/.well-known/oauth-protected-resource"
Content-Type: application/json
{"jsonrpc":"2.0","id":1,"error":{"code":-32001,"message":"credentials required"}}How do I get a token?
Start from the metadata
Follow the resource_metadata URL in the 401 response. It tells you which authorization server to go to and which scopes you need.
$ curl https://api.jocodingax.ai/.well-known/oauth-protected-resourceThe response is RFC 9728 compliant: authorization_servers, scopes_supported, resource.
Exchange for an access token
Use the authorization_servers from that metadata with either client_credentials or authorization_code flow. The server validates the token on every request and extracts the owner's ID and scopes.
Scopes come in three tiers
| Scope | What it lets you do | Example tools |
|---|---|---|
read | List, inspect, search only | list_my_apps, get_app_url, list_apis, search_table, preflight_check |
write | Change state | deploy_app, rollback_deploy, restart_app, env_vars, records (insert/update) |
admin | Org/security changes | import_and_deploy, set_app_spec, manage_oauth_client, connect_repo, manage_api_key |
Having admin implicitly grants read and write.
Some tools vary scope by action
For certain tools the required scope depends on the action argument:
manage_app:update→write,archive/unarchive/delete→adminmanage_ci:list→read,rerun→write,enable/disable→adminmanage_table:list/describe→read,create/drop→writerecords:query→read,insert/update/delete/bulk_insert→writeconnect_repo: all actions →admin
Two-phase authorization
Every tools/call goes through both phases, in order.
Phase 1 — global · scope · auth-type
- Deny list — if the tool is temporarily blocked, short-circuit.
- Scope check — does the token carry the required scope?
- Auth-type restriction —
app-keytokens can only reach these 4 tools:
list_apis, get_api_schema, test_api, discover_apisPhase 2 — per-app OAuth authorization
For OAuth tokens, ResolveApp(args) finds the target app and UserEffectiveScopes(user, app) computes the user's actual permissions on that app (canManage, scopes).
- Non-read action but
canManageis false →forbidden (endpoint_forbidden) - User lacks the required scope →
forbidden (missing_scope)
Session ownership
The Mcp-Session-Id header is validated on both JSON-RPC requests and SSE streams. If session owner ≠ token owner, the header is dropped and resource subscriptions / task calls fail with Mcp-Session-Id required.
Error catalog
| JSON-RPC code | HTTP | Meaning |
|---|---|---|
-32001 | 401 | Auth missing or failed (WWW-Authenticate emitted) |
-32600 | 400 | Session ID missing on resource subscribe |
-32601 | 200 | Method/tool doesn't exist |
-32602 | 200 | Parameter validation failed |
-32603 | 200 | Internal server error (includes panic recovery) |
forbidden | 200 | Scope/permission denied — detailed reason: oauth_only, missing_scope, resource_scope, endpoint_forbidden |