For the complete documentation index, see llms.txt. This page is also available as Markdown.

Build a custom MCP server with OAuth 2.1

What you'll build:

A minimal, runnable Python MCP server that Blockbrain can connect to via OAuth 2.1 β€” so internal tools or data you expose can be safely consumed by a Blockbrain agent.

Audience: a developer at your side. Copy the four code blocks below into a folder, run five commands, point Blockbrain at the resulting URL, and you have a working authenticated integration.

Time required: ~20 minutes for a working local server; ~45 minutes including hardening.

See also: MCP Server β€” admin UI walkthrough.


Prerequisites

  • Python 3.11+

  • A public HTTPS URL for your local server while testing β€” easiest options: an HTTPS tunnelling tool such as cloudflared tunnel or ngrok

  • Tenant Administrator access to your Blockbrain tenant

  • Familiarity with OAuth 2.1 Authorization Code flow with PKCE (helpful but not required)


When to use which authentication mode

Blockbrain's MCP-server registration UI offers four authentication methods. Choose based on how your MCP server should know who is calling:

Method
Use when…
Trade-off

None

Internal/dev only and the server URL is not public

Anyone who reaches the URL can call the tools

API Key (Fixed Token)

Service-to-service. One shared secret. No per-user identity needed.

Cannot distinguish individual Blockbrain users on your side

OAuth 2.1 ← this guide

Production scenarios. Blockbrain's tenant admin authorizes the connection once via consent flow.

More moving parts, but the standard MCP-spec way

User Token (delegated)

You want per-user authorization on your MCP server (e.g. enforce that User A only sees their own data)

Your server must validate Blockbrain's forwarded JWT β€” see section 8


The OAuth 2.1 discovery flow Blockbrain uses

When you select OAuth 2.1 in the admin UI and click Discover OAuth, Blockbrain follows RFC 9728 + RFC 8414 to find your authorization server, then runs Authorization Code + PKCE:


The Python example

Three files in one folder. Copy each block verbatim.

server.py

requirements.txt

.env.example

Replace the demo client credentials with strong random values before deploying anywhere reachable from the public internet.


Run it locally


Verify the endpoints with curl


Register your MCP server in Blockbrain

  1. Expose your local server publicly: cloudflared tunnel --url http://localhost:8080 (or ngrok http 8080). Note the public HTTPS URL.

  2. Set SERVER_HOST in .env to that public URL and restart the server.

  3. Open Blockbrain β†’ Admin β†’ Agents β†’ MCP Servers β†’ + Add MCP Server.

  4. Fill in:

    • Server Name: e.g. my-mcp-demo

    • Server URL: https://<your-tunnel>/mcp

    • Transport: HTTP

    • Authentication: OAuth 2.1

  5. Click Discover OAuth. Blockbrain reads your two .well-known endpoints and pre-fills the OAuth config.

  6. Click Configure, complete the consent flow, and confirm the access token comes back.

  7. Save. Assign the integration to a test agent. In a chat, ask the agent to call echo "hello" β€” you should see echo: hello come back.

Full admin-UI walkthrough with screenshots: MCP Server β€” for admins.


Optional β€” validate Blockbrain's forwarded user JWT

If you also want per-user authorization (delegated-access mode), set VALIDATE_USER_TOKEN=1. The validate_user_jwt helper in server.py checks every incoming bearer token against Blockbrain's public JWKs at https://auth.theblockbrain.ai/oauth/v2/keys, verifies signature, expiration, and audience, and returns the claims (including external_user_id, urn:zitadel:iam:org:id, etc.).


Headers Blockbrain sends with every request

Your MCP server can read these to identify the calling user, tenant, and thread context:

Header
Description
Example

X-User-ID

End-user identifier

user_12345

X-External-User-ID

Your system's user identifier (if activated)

ext_user_abc

X-Tenant-ID

Tenant identifier

tenant_xyz

X-Agent-ID

Agent making the request

agent_007

X-Data-Room-ID

Associated data room

room_456

X-Thread-ID

Conversation thread

thread_789

Authorization

Bearer token (OAuth access token, or forwarded user JWT)

Bearer ...


Troubleshooting

Symptom
Likely cause
Fix

"Discover OAuth" returns 404

.well-known paths not exposed at the public URL

Confirm the tunnel forwards root paths; re-curl both well-known endpoints

Redirect-URI mismatch on consent

IdP does not support Dynamic Client Registration (e.g. Microsoft Entra)

Use the static OAUTH_CLIENT_ID in this example rather than a dynamically registered one

Token returned but tools list is empty

Scopes not preserved during configure

Make sure your authorization-server metadata includes scopes_supported


Production hardening checklist

Before shipping to a production environment, replace or add:

  • The in-memory auth_codes / access_tokens dicts β†’ a persistent store (e.g. Redis), so tokens survive restarts and can be shared across replicas.

  • Refresh-token rotation. The example issues access tokens only.

  • Structured logging and an audit trail for every /authorize and /token call.

  • Move OAUTH_CLIENT_SECRET to a secrets manager β€” never check it into source control.

  • Terminate HTTPS in front of the server (reverse proxy, load balancer, or your platform's ingress).

  • Rate limiting on /token and /authorize.

  • If multi-tenant, scope OAUTH_CLIENT_ID per tenant rather than reusing one shared value.


Next steps

  • SSE transport β€” Blockbrain also supports SSE (https://your-server/sse). To switch, replace the app.mount("/mcp", ...) line with the SSE app from mcp.server.sse.

  • Real tools β€” replace the echo tool with calls into your domain (database queries, internal APIs, etc.).

  • JavaScript / TypeScript example β€” a Node/Express equivalent of this guide using @modelcontextprotocol/sdk is being prepared as a follow-up.

Last updated