Reusable FastMCP authorization middleware (PEP) for KODA-ecosystem MCP servers.
Project description
koda-mcp-authz-middleware
Reusable FastMCP authorization middleware for KODA-ecosystem MCP servers. It is a pure Policy Enforcement Point (PEP): it reads identity + an already-resolved permission subset from proxy-injected headers and allows / denies / filters every MCP operation by pattern matching.
It does not know about roles, servers, Okta, or the grants permission
model — that intelligence lives upstream (usrv-koda = data, koda-proxy = subset
resolution). Any MCP behind a proxy that honors the header contract can use it
unchanged.
Install
uv add koda-mcp-authz-middleware
# or with the optional standalone Okta verifier (local-dev):
uv add "koda-mcp-authz-middleware[okta]"
# or: pip install koda-mcp-authz-middleware
Pin it in your project as:
# pyproject.toml
koda-mcp-authz-middleware>=0.1,<0.2
Use
from fastmcp import FastMCP
from koda_mcp_authz_middleware import KodaAuthzMiddleware
mcp = FastMCP("My MCP")
mcp.add_middleware(KodaAuthzMiddleware()) # defaults: AgentCore prefix, fail-closed
With configuration (observability + prompts enforcement):
from koda_mcp_authz_middleware import GuardConfig, KodaAuthzMiddleware
def audit(d):
logger.info("authz action=%s target=%s decision=%s roles=%s",
d.action, d.target, d.decision, d.identity.roles if d.identity else [])
mcp.add_middleware(KodaAuthzMiddleware(GuardConfig(
enforce_prompts=True,
audit_hook=audit,
)))
Read identity from inside a tool:
from koda_mcp_authz_middleware import get_current_identity
@mcp.tool()
async def whoami() -> dict:
ident = get_current_identity()
return {"sub": ident.sub, "roles": ident.roles, "tenant": ident.tenant_id}
Standalone local-dev (validate the Okta JWT itself, requires [okta] extra):
from koda_mcp_authz_middleware.verifiers.okta import OktaTokenVerifier
from fastmcp.server.auth import RemoteAuthProvider
if IS_LOCAL:
mcp.auth = RemoteAuthProvider(
token_verifier=OktaTokenVerifier(),
authorization_servers=[OKTA_ISSUER],
base_url=MCP_BASE_URL,
)
Header contract
The upstream proxy injects, under a configurable prefix (default
x-amzn-bedrock-agentcore-runtime-custom-koda-):
| Header suffix | Meaning |
|---|---|
sub |
caller subject (required; absent → no identity) |
email |
caller email (defaults to sub) |
roles |
space-separated roles (resolved upstream) |
auth-groups |
space-separated Okta groups |
tenant-id |
tenant |
authorization |
raw Okta JWT (for tools calling backends) |
permissions |
base64(JSON) — the already-resolved subset for this MCP |
discovery |
"true" → registry catalogue scan, no filtering |
permissions decodes to a flat object — already server-scoped (tools/components)
plus the global catalogs (skills/agents):
{ "tools": ["my_profile"], "components": ["ui://koda/*"],
"skills": ["pm_challenge"], "agents": { "roadmap-agent": { "actions": ["*"] } } }
Behavior
| Operation | perms | Result |
|---|---|---|
tools/call |
match in tools |
execute / AuthorizationError |
tools/list |
tools |
filtered list |
resources/read |
per resource_gate |
serve / AuthorizationError |
resources/list |
per resource_gate |
filtered list |
| any | None (stdio / discovery) |
passthrough (no filter) |
| any | header missing/invalid + not stdio/discovery | AuthorizationError (fail-closed) |
resource_gate: by_mcp_access (default — any MCP access grants resources),
by_component_list (strict URI match against components), or open.
Develop
uv sync --group dev
uv run pytest
uv run ruff check src/ tests/
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file koda_mcp_authz_middleware-0.1.0.tar.gz.
File metadata
- Download URL: koda_mcp_authz_middleware-0.1.0.tar.gz
- Upload date:
- Size: 89.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7f86546db882e4fb257945f787194bfe424cca860f16041013ae8b27b580ab53
|
|
| MD5 |
22e25597b27e765d3e93cdc73f049af6
|
|
| BLAKE2b-256 |
df8543e35f29677967f1c5778ac84ae20bbadc549e2809a56c31e72598009819
|
File details
Details for the file koda_mcp_authz_middleware-0.1.0-py3-none-any.whl.
File metadata
- Download URL: koda_mcp_authz_middleware-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
779ca5cbbf66eb7932f4f0ee0983c3b58ac8524ee8f78f68964a18eb7c980706
|
|
| MD5 |
1f162ba41e179108e5f7f427dbba05da
|
|
| BLAKE2b-256 |
96deeb80a3033036b5fd2bf550ac59373f7a4b3f211b7d500850f984c527930e
|