OAuth & Scopes

OAuth 2.1 with PKCE-S256 and Dynamic Client Registration (RFC 7591) for AI clients. API-key auth for SDKs and machine-to-machine. Most MCP-aware clients negotiate the OAuth flow automatically — the curl walkthrough below is for clients that don't.

Use caseAuthWhy
Claude Desktop / claude.ai / Claude CodeOAuth 2.1 + DCRPer-user identity, scope challenges, refresh tokens
Python SDK, scripts, BI tools, cron jobsAPI keyNo browser, no user, scoped to one organization

Discovery

The discovery endpoint advertises every capability — paste the URL into your client and it does the rest:

https://{tenant}.cognethics.com/.well-known/oauth-authorization-server

Authorization-server metadata

Live response from the gtm tenant (your tenant returns the same shape with its own URLs):

{
  "issuer": "https://gtm.cognethics.com",
  "authorization_endpoint": "https://gtm.cognethics.com/api/mcp/oauth/authorize/",
  "token_endpoint": "https://gtm.cognethics.com/api/mcp/oauth/token/",
  "revocation_endpoint": "https://gtm.cognethics.com/api/mcp/oauth/revoke/",
  "registration_endpoint": "https://gtm.cognethics.com/api/mcp/oauth/register/",
  "jwks_uri": "https://gtm.cognethics.com/.well-known/jwks.json",
  "response_types_supported": [
    "code"
  ],
  "grant_types_supported": [
    "authorization_code",
    "refresh_token"
  ],
  "code_challenge_methods_supported": [
    "S256"
  ],
  "token_endpoint_auth_methods_supported": [
    "none",
    "client_secret_post"
  ],
  "scopes_supported": [
    "mcp:read",
    "mcp:write",
    "mcp:execute",
    "spare_parts.view",
    "spare_parts.add",
    "spare_parts.change",
    "spare_parts.delete"
  ],
  "response_modes_supported": [
    "query"
  ],
  "authorization_response_iss_parameter_supported": true,
  "mcp_version": "2025-03-26",
  "service_documentation": "https://gtm.cognethics.com/docs",
  "mcp_sse_endpoint": "https://gtm.cognethics.com/api/mcp/sse"
}

Protected-resource metadata (RFC 9728)

{
  "resource": "https://gtm.cognethics.com",
  "authorization_servers": [
    "https://gtm.cognethics.com"
  ],
  "scopes_supported": [
    "mcp:read",
    "mcp:write",
    "mcp:execute"
  ],
  "bearer_methods_supported": [
    "header"
  ],
  "resource_signing_alg_values_supported": [
    "RS256",
    "HS256"
  ],
  "resource_documentation": "https://gtm.cognethics.com/docs",
  "authentication_methods_supported": [
    "oauth2"
  ],
  "mcp_version": "2025-03-26",
  "platform_support": [
    "claude.ai",
    "claude-desktop",
    "claude-code"
  ],
  "mcp_sse_endpoint": "https://gtm.cognethics.com/api/mcp/sse"
}

Scopes

Cognethics advertises three platform-level scopes plus per-app permission scopes granted on-demand via Dynamic Client Registration.

Platform-level scopes

ScopeGrants
mcp:readRead-only access to all MCP tools your user account can see (list, read, search, report queries).
mcp:writeCreate and update operations through MCP tools (entity_crud writes).
mcp:executeWorkflow actions, smart actions, agent commands, notification sends — full action access.

Per-app permission scopes

Per-model Django permissions are exposed as scopes following the pattern <app>.<verb>_<model> — e.g. spare_parts.view_workorder, finance.add_invoice.

Currently advertised in scopes_supported: 4 per-app scopes (including spare_parts.view</code>, <code>spare_parts.add</code>, <code>spare_parts.change, …). The full per-model catalog (thousands of entries) is granted on-demand via DCR.

Manual OAuth flow

For clients that don't speak Dynamic Client Registration, register once by hand and run the standard authorization-code + PKCE dance. Three curl calls:

Step 1 — Register a client

curl -X POST 'https://{tenant}.cognethics.com/api/mcp/oauth/register/' \
  -H 'Content-Type: application/json' \
  -d '{
    "client_name": "My App",
    "redirect_uris": ["http://localhost:8080/callback"]
  }'

Step 2 — Authorize the user

# Open this URL in a browser:
https://{tenant}.cognethics.com/api/mcp/oauth/authorize/
  ?response_type=code
  &client_id=<from-register-step>
  &redirect_uri=http://localhost:8080/callback
  &code_challenge=<base64url-sha256-of-verifier>
  &code_challenge_method=S256
  &scope=mcp:read
  &state=<random-string>

The user authorizes in their browser. Cognethics redirects back to your redirect_uri with an authorization code. For headless / desktop clients, use the Out-Of-Band flow by setting redirect_uri=urn:ietf:wg:oauth:2.0:oob — Cognethics will display the code on a web page for the user to copy.

Step 3 — Exchange code for token

curl -X POST 'https://{tenant}.cognethics.com/api/mcp/oauth/token/' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=authorization_code' \
  -d 'code=<from-authorize-callback>' \
  -d 'code_verifier=<original-verifier>' \
  -d 'client_id=<from-register-step>'

Returns a JWT access token (validate against /.well-known/jwks.json) and a refresh token. Use Authorization: Bearer <access-token> on every request to /api/mcp/sse.

API keys (for SDKs and machine-to-machine)

For Python SDK use, scripts, cron jobs, and BI tools, Cognethics supports simpler API-key auth alongside OAuth. Keys are bound to one organization at creation time — to call across orgs, mint one key per org.

Send the key as a Bearer token:

curl -H "Authorization: Bearer mcp_..." \
  https://{tenant}.cognethics.com/api/mcp/sse

Mint a key (Django shell on the platform host):

from cognethics.core.models import User, Organization
from cognethics.mcp.models import MCPApiKey

u = User.objects.get(email='[email protected]')
org = Organization.objects.get(id='<your-org-uuid>')
key, plain = MCPApiKey.create_key(
    user=u, organization=org,
    name='my-integration',
    scope='mcp:read mcp:write mcp:execute',
    expires_in_days=90,
    created_by=u,
)
print(plain)  # the only time the plaintext is visible

A REST endpoint exists at POST /api/mcp/api-keys/register/ (rate-limited 5/min). X-Organization-Context is ignored for API-key auth — keys are bound to a single org. For cross-org dispatch, use OAuth.

Revocation

curl -X POST 'https://{tenant}.cognethics.com/api/mcp/oauth/revoke/' \
  -d 'token=<access-or-refresh-token>'

Next: try the quickstart or browse the handler catalog.