Skip to main content

delegus-mcp

Delegus for Python MCP servers. An ASGI middleware checks every MCP tools/call against the calling agent's delegated authority before your tool runs. It works with the official Python MCP SDK's MCPServer over streamable HTTP, with Starlette, and with FastAPI. It follows the same rules as the Node middleware, delegus.mcp() in @delegus/sdk.

pip install delegus-mcp
import os
from mcp.server.mcpserver import MCPServer
from delegus_mcp import DelegusMCPMiddleware

mcp = MCPServer("tickets")

@mcp.tool()
def create_ticket(title: str) -> str:
    return f"created ticket: {title}"

app = DelegusMCPMiddleware(
    mcp.streamable_http_app(),
    api_key=os.environ["DELEGUS_RP_KEY"],
    public_base_url="https://tools.example.com",
)

Run it with any ASGI server, for example uvicorn server:app. public_base_url is the address agents call and sign for; behind a proxy, it's the public one. For FastAPI or Starlette, wrap the app the same way, or use app.add_middleware(DelegusMCPMiddleware, api_key=…, public_base_url=…).

Options:

  • tools: a list or a predicate, to enforce only some tools (others pass unchecked);
  • relying_party: skips a lookup when credentials are missing;
  • on_decision: called with each decision, for logs;
  • max_body_bytes: default 1 MiB; a larger body is refused;
  • api_base_url;
  • timeout_seconds;
  • protocol: "auto" (the default), "v0.2" or "v0.3" (see below);
  • requires: v0.3 only, the prior decisions every checked call must rest on, as [{"fingerprint": …, "mode": …}].

Every receipt is checked

Each relying party receives receipts in its own profile's format: v0.2 (delegus-base-v1) or v0.3 (delegus-base-v3, whose receipts carry the matched permission as a commitment, with the permission and the key to open it returned beside the receipt). With the default, protocol="auto", the middleware accepts either, chosen by the version the receipt was signed with, and checks it with delegus-core before the tool runs:

  • the signature, against Delegus's published receipt keys (<api_base_url>/.well-known/did.json, which must name did:web:delegus.ai in production), fetched once and reused;
  • that the receipt is for this request: its Grant, Proof and action hashes match what was sent;
  • that the decision, the reason and the individual checks agree;
  • for v0.3, that the permission opens its commitment, and that the dependency results and requires match.

protocol="v0.2" or "v0.3" pins one format. An answer that fails any check, or comes in the other format under a pin, is refused with SERVICE_UNAVAILABLE, and on_decision gets an error field naming the cause. There is one check per call, never a retry: a second check with the same Proof would be refused as a replay.

What happens to each request

  • tools/call: the agent's Delegus-Grant and Delegus-Proof headers are checked, including that the Proof was signed for this tool at this address.

    • On ALLOW, the tool runs. The receipt is in scope["state"]["delegus"] (in Starlette, request.state.delegus) and the response carries Delegus-Receipt-Id.
    • On DENY, the middleware answers HTTP 403 with JSON-RPC error -32040 and the Delegus reason, and the tool never runs.
  • Anything it can't read is refused:

    • a missing, unparseable or oversized body, or one with duplicate keys (REQUEST_UNREADABLE);
    • a JSON-RPC batch carrying a checked tool call (BATCH_NOT_SUPPORTED);
    • a tool call without a string tool name (TOOL_NAME_INVALID).

    These are the middleware's own refusals and carry no signed receipt.

  • Delegus can't be reached, or its answer doesn't check: 403 SERVICE_UNAVAILABLE. It fails closed.

  • Everything that can't run a tool passes through: initialize, tools/list, ping, notifications, JSON-RPC responses, GET (the SSE stream), HEAD, OPTIONS, and a DELETE that ends a session.

  • The app receives exactly the bytes that were checked.

Limits

  • No stdio. It checks HTTP requests, so it covers MCP servers served over HTTP, not stdio-only servers.
  • No argument inspection. Delegus binds the tool, not its arguments (v0.2 and v0.3 alike): a permission can allow create_ticket, not "tickets under a priority". Validate arguments in the tool.
  • Server-to-client messages aren't checked. Requests your server sends to the client on an SSE stream (sampling, elicitation) pass as they are.
  • Only requests that reach this app are checked. If the same tools are exposed some other way, those routes aren't protected.

Tested against

The shared MCP request-shape suite in @delegus/conformance: the same rows the Node middleware and the Delegus MCP gateway run. It asserts that a refused request never reaches the tool, and that a forwarded one arrives byte for byte. End to end, with the official Python MCP SDK (mcp 2.x) and a local Delegus, for a relying party on each profile:

  • a delegated call runs with a receipt;
  • after a revoke, the same call is refused with AUTHORITY_REVOKED;
  • a batch is refused.

Python 3.9 or later. Apache-2.0.

Release files for delegus-mcp 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for delegus-mcp 0.2.0
File Size Uploaded
delegus_mcp-0.2.0.tar.gz 19.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for delegus-mcp 0.2.0
File Interpreter ABI Platform
delegus_mcp-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.9 kB

Release files / delegus_mcp-0.2.0.tar.gz

Download URL delegus_mcp-0.2.0.tar.gz
Size 19.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b7d8e57460c2dfffff92879009f5e907ee1ef7eb167327b7a4b6f1fa8f589e14
BLAKE2b-256 checksum
How to use checksums
3aa9f69cfafb12c735334a4861398f34cacb43b62f3e81f729bfb0b8d43a8656
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / delegus_mcp-0.2.0-py3-none-any.whl

Download URL delegus_mcp-0.2.0-py3-none-any.whl
Size 14.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e7a699ce1df3ed04794740d3f80208916215772ece18f8772336c0a902a8baf6
BLAKE2b-256 checksum
How to use checksums
d78818ee190aca317892f90c8114bbc2b4a24f61d7878d00c0a9e497465fb785
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page