Research-Stack/6-Documentation/wiki/Authentik-MCP-Bridge.md
2026-05-25 16:24:21 -05:00

9.3 KiB

Authentik MCP Bridge Specification

Purpose: Define a Model Context Protocol (MCP) server surface so any LLM can safely manage Authentik identities through structured tools. Status: Draft — not yet implemented Last updated: 2026-05-20


1. What is MCP?

Model Context Protocol is an open standard for exposing tools, resources, and prompts to LLMs. An MCP server provides a JSON-RPC interface that a client (Claude Desktop, Cursor, a custom agent harness) can discover and invoke.

For Authentik, an MCP bridge means:

  • No raw curl in the LLM prompt. The LLM calls create_agent("openclaw-7", ...) and the bridge handles the REST API.
  • Type safety: input schemas are JSON Schema; the bridge validates before touching Authentik.
  • Auditability: every tool call is logged with arguments and results.
  • Rate limiting / circuit breaking: the bridge can throttle or deny dangerous operations.

2. Bridge Architecture

┌─────────────────┐     MCP (stdio/sse)      ┌──────────────────┐     REST + Bearer     ┌─────────────┐
│   LLM Client    │ ───────────────────────▶ │  Authentik MCP   │ ───────────────────▶  │  Authentik  │
│  (Claude /      │   JSON-RPC 2.0          │  Bridge Server   │    Token             │   API       │
│   Cursor / etc) │                         │  (Python/Node)   │                      │  :9000      │
└─────────────────┘                         └──────────────────┘                      └─────────────┘
                                                   │
                                                   │
                                          ┌────────▼─────────┐
                                          │  Audit Log       │
                                          │  (JSONL + S3)   │
                                          └─────────────────┘

Authentication required: The bridge itself must be authenticated by Authentik. It reads AUTHENTIK_TOKEN at startup (from env var or the credential server). The token must belong to an Authentik account with admin-level privileges (e.g., llm-agent-controller in the authentik Admins group). Without a valid token, every tool call returns an authentication error.

Transport options

Transport Use case Endpoint
stdio Local CLI tools, Claude Desktop authentik-mcp-bridge stdio
sse Remote/web clients, persistent connection authentik-mcp-bridge sse --port 8080

3. Tool Surface

3.1 create_agent

Create a service-account user in Authentik.

Input schema:

{
  "username": "openclaw-instance-7",
  "name": "OpenClaw Instance 7",
  "email": "openclaw-7@researchstack.info",
  "group": "AgentManager"
}

Output:

{
  "pk": 42,
  "username": "openclaw-instance-7",
  "uuid": "...",
  "group_assigned": true
}

Implementation: POST /core/users/ then POST /core/groups/{uuid}/add_user/


3.2 create_token

Generate an API token for an existing agent.

Input schema:

{
  "user_pk": 42,
  "identifier": "openclaw-7-token"
}

Output:

{
  "identifier": "openclaw-7-token",
  "key": "keJxas4I...",
  "warning": "Store immediately — shown once"
}

Implementation: POST /core/tokens/ then GET /core/tokens/{identifier}/view_key/


3.3 list_agents

List all service-account users.

Input schema:

{
  "active_only": true
}

Output:

{
  "agents": [
    {"pk": 7, "username": "llm-agent-controller", "is_active": true},
    {"pk": 42, "username": "openclaw-instance-7", "is_active": true}
  ]
}

Implementation: GET /core/users/ with ?type=service_account filter


3.4 suspend_agent

Soft-delete (deactivate) an agent.

Input schema:

{
  "user_pk": 42
}

Output:

{
  "ok": true,
  "pk": 42,
  "is_active": false
}

Implementation: PATCH /core/users/{pk}/ with {"is_active": false}


3.5 revoke_agent

Hard-delete an agent and all its tokens.

Input schema:

{
  "user_pk": 42
}

Output:

{
  "ok": true,
  "pk": 42,
  "status": "deleted"
}

Implementation: DELETE /core/users/{pk}/


3.6 rotate_token

Regenerate the key for an existing token.

Input schema:

{
  "identifier": "openclaw-7-token"
}

Output:

{
  "identifier": "openclaw-7-token",
  "key": "newKey...",
  "warning": "Old key invalidated immediately"
}

Implementation: POST /core/tokens/{identifier}/set_key/


3.7 create_application

Create an application that agents can be granted access to.

Input schema:

{
  "name": "OpenClaw Data Sink",
  "slug": "openclaw-data-sink"
}

Output:

{
  "slug": "openclaw-data-sink",
  "name": "OpenClaw Data Sink",
  "meta_launch_url": ""
}

Implementation: POST /core/applications/


4. Safety Boundaries

Boundary MCP Bridge Rule
Token exposure create_token returns the key exactly once. The bridge does not log the key. The LLM client must store it in SOPS/RDS.
Scope The bridge authenticates with the llm-controller-token. It cannot escalate beyond what that token permits.
Rate limit Configurable: max 10 create_agent calls per minute, max 3 revoke_agent calls per minute.
Deny list The bridge refuses to operate on akadmin (pk=6) and llm-agent-controller (pk=7).
Audit Every call writes a JSON line to ~/.cache/authentik-mcp-bridge.jsonl and optionally to s3://research-stack/agent-receipts/.

5. Reference Implementation (Python, stdio)

A minimal skeleton using the official mcp Python SDK:

# /// script
# requires-python = ">=3.10"
# dependencies = [
#   "mcp>=1.0.0",
#   "httpx",
# ]
# ///

import os
import httpx
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool

AUTHENTIK_TOKEN = os.environ["AUTHENTIK_TOKEN"]
BASE_URL = "https://researchstack.info"

app = Server("authentik-mcp-bridge")

@app.list_tools()
async def list_tools():
    return [
        Tool(name="create_agent", description="Create a service account", inputSchema={...}),
        Tool(name="create_token", description="Create an API token", inputSchema={...}),
        Tool(name="list_agents", description="List agents", inputSchema={...}),
        Tool(name="suspend_agent", description="Deactivate an agent", inputSchema={...}),
        Tool(name="revoke_agent", description="Hard-delete an agent", inputSchema={...}),
        Tool(name="rotate_token", description="Rotate a token key", inputSchema={...}),
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict):
    async with httpx.AsyncClient(timeout=30) as client:
        headers = {"Authorization": f"Bearer {AUTHENTIK_TOKEN}"}
        if name == "create_agent":
            r = await client.post(f"{BASE_URL}/api/v3/core/users/", headers=headers, json={...})
            return [TextContent(type="text", text=r.text)]
        # ... etc

async def main():
    async with stdio_server() as (read, write):
        await app.run(read, write, app.create_initialization_options())

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

6. Deployment Plan

Step Action Owner
1 Implement the 7 tools in Python using mcp SDK Future agent
2 Add JSONL audit log + optional S3 sink Future agent
3 Add rate limiting + deny-list for protected accounts Future agent
4 Deploy as systemd service on qfox-1 or nixos-laptop Future agent
5 Register with Claude Desktop / Cursor / OpenClaw harness User
6 Document per-LLM configuration (env vars, transport mode) Future agent

7. Relationship to Existing Components

Component How MCP Bridge Uses It
authentik_agent_manager (Rust) The bridge is a higher-level wrapper around the same REST calls. The Rust shim supports both individual CLI commands and full DAG execution. The bridge is for LLM tool use.
Credential Server (RDS) The bridge reads AUTHENTIK_TOKEN from the credential server at runtime: curl http://100.101.247.127:8444/credentials/authentik
SOPS secrets Source of truth for the token. The bridge can optionally load from sops -d if the credential server is down.
Authentik-Agent-Management.md Operational docs — the bridge implements the API patterns documented there.

8. Open Questions

  • Should the bridge support application entitlement management (granting an agent access to a specific app)? This requires POST /core/application_entitlements/ which is more complex.
  • Should the bridge support group creation so LLMs can define new scopes dynamically?
  • Should the bridge support impersonation (POST /core/users/{pk}/impersonate/) for testing agent permissions?