Skip to main content
Give different MCP clients different API keys, each with different powers — reader can list tools, agent-bot can call them, admin can do everything.
No auth configured? Nothing changes. With no keys set, granted_scopes is None and every method is allowed. Scoped keys are 100% opt-in — existing setups keep working untouched.

Quick Start

1

Create a keys file

Give each client a key and a list of scopes. * means “all scopes”.
keys.json
{
  "keys": [
    { "key": "reader-abc", "scopes": ["tools:read", "resources:read"] },
    { "key": "agent-def",  "scopes": ["tools:call", "tools:read"] },
    { "key": "admin-ghi",  "scopes": ["*"] }
  ]
}
2

Serve with the keys file

praisonai mcp serve --transport http-stream --port 8080 --keys-file ./keys.json
Each request sends its key as a Bearer token. The server looks up the key, resolves its scopes, and enforces them per method.
3

Single key (unchanged)

A single scalar key still works and gets wildcard (*) scope — every method allowed.
praisonai mcp serve --transport http-stream --api-key MY_SECRET

How It Works

Each method has a required scope. The transport resolves the caller’s key to granted_scopes and the server checks it before running the handler.
StepWhat happens
LookupTransport matches the Bearer key to an APIKeyAuth entry
ResolveThe key’s scopes become granted_scopes
CheckThe method’s required scope is compared against granted_scopes
Allow / DenyMatch → run handler; miss → -32001 insufficient_scope
When no key store is configured, granted_scopes is None and the check is skipped — allow all.

Two Ways to Configure

Pick the shortest path for your setup.

A) Single key (wildcard)

praisonai mcp serve --transport http-stream --api-key MY_SECRET
Behaviour is unchanged. The scalar --api-key is wrapped as a single key with wildcard (*) scope, so it satisfies every method — the scalar path and the multi-key path share the same enforcement.

B) Multiple scoped keys

praisonai mcp serve --transport http-stream --keys-file ./keys.json
keys.json
{
  "keys": [
    { "key": "reader-abc", "scopes": ["tools:read", "resources:read"] },
    { "key": "agent-def",  "scopes": ["tools:call", "tools:read"] },
    { "key": "admin-ghi",  "scopes": ["*"] }
  ]
}
FieldTypeDescription
keystringThe Bearer token the client sends
scopesstring[]Scopes this key grants. "*" satisfies every requirement

Scopes and Methods

Each MCP method maps to a required scope in OPERATION_SCOPES.
MethodRequired scope
tools/listtools:read
tools/calltools:call
resources/listresources:read
resources/readresources:read
resources/subscriberesources:subscribe
prompts/listprompts:read
prompts/getprompts:read
sampling/createMessagesampling:create
tasks/createtasks:write
tasks/gettasks:read
tasks/listtasks:read
tasks/canceltasks:write
Available scopes:
ScopeGrants
tools:readRead tool definitions
tools:callExecute tools
resources:readRead resources
resources:subscribeSubscribe to resource changes
prompts:readRead prompts
prompts:executeExecute prompts
sampling:createCreate sampling requests
tasks:readRead tasks
tasks:writeCreate and manage tasks
adminAdministrative access (implies all)
*Wildcard — satisfies every requirement
Scopes are hierarchical: tools:call implies tools:read, resources:subscribe implies resources:read, tasks:write implies tasks:read, and admin implies everything.

Error Response

A request without the required scope returns a JSON-RPC error with code -32001.
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32001,
    "message": "insufficient_scope",
    "data": { "required": "tools:call" }
  }
}
Over HTTP the response also carries a challenge header naming the missing scope:
WWW-Authenticate: Bearer realm="mcp", scope="tools:call", error="insufficient_scope"
The client can read scope="…" to know exactly which grant to request next.

Common Patterns

Match a key’s scopes to what each client actually needs.
A monitoring bot that only lists tools and reads status — no execution.
{ "key": "monitor-key", "scopes": ["tools:read", "resources:read"] }

Best Practices

Start from tools:read and add only what a client needs. Reserve * and admin for maintenance keys, not day-to-day clients.
Give each bot or service its own key so you can rotate or revoke it without affecting others. Name keys by role (reader, agent-bot, admin).
Add the replacement key alongside the old one, point clients at the new key, then delete the old entry. No restart of clients is forced mid-rotation.
Treat keys.json like any secret — store it outside the repo and restrict file permissions. Never commit real keys.

PraisonAI MCP Server

Run PraisonAI as an MCP server over STDIO or HTTP Stream.

MCP Authentication

API key and OAuth options for securing MCP.