Skip to main content

keycardai-fastmcp

FastMCP integration for Keycard OAuth: protect FastMCP servers with Keycard authentication and run delegated OAuth 2.0 token exchange (RFC 8693) for downstream APIs.

This is the canonical home for the integration. The old name, keycardai-mcp-fastmcp, is retired; its final release (0.21.0) remains on PyPI so existing pins keep resolving.

Installation

pip install keycardai-fastmcp

Quick Start

from fastmcp import FastMCP
from keycardai.fastmcp import AccessContext, AuthProvider

auth_provider = AuthProvider(
    zone_id="abc1234",
    mcp_server_name="My Server",
    mcp_base_url="http://localhost:8000",
)

mcp = FastMCP("My Server", auth=auth_provider.get_remote_auth_provider())

@mcp.tool()
async def call_external_api(
    query: str,
    access: AccessContext = auth_provider.grant("https://api.example.com"),
):
    token = access.access("https://api.example.com").access_token
    return f"Results for {query} (token starts with {token[:8]})"

Declaring the grant as a typed parameter default injects the populated AccessContext per request; the parameter never appears in the tool's input schema. Exchange failures are recorded on the AccessContext (check access.has_errors() / access.get_errors()), never raised. Granting multiple resources is all-or-nothing: if any exchange fails, the context carries that resource's error and no tokens.

If you lint with flake8-bugbear or Ruff's B008 rule (function call in argument default), exempt your tool modules: the call-in-default is the intended spelling here, the same pattern as FastAPI's Depends. In pyproject.toml:

[tool.ruff.lint.per-file-ignores]
"src/my_server/tools/*.py" = ["B008"]

Migrating from the decorator form

The decorator form (@auth_provider.grant(...) above the tool) still works from the same object. Reading the result via ctx.get_state("keycardai") is deprecated and emits a DeprecationWarning; helpers that only hold the FastMCP Context can use await AccessContext.from_context(ctx) instead.

The warning fires once per tool, at decoration time (module import). If your test or CI setup escalates warnings to errors (-W error, filterwarnings = ["error"] in pytest config), importing a server module that still uses the old form will raise instead of warn. Either migrate the tools to the injected-parameter form, or allow this warning explicitly:

filterwarnings = ["error", 'default:Tool .* uses the grant decorator:DeprecationWarning']

Configuration from the environment

AuthProvider reads its zone and application credential from the environment when they are not passed explicitly.

Zone: set KEYCARD_ZONE_URL to the full zone URL. KEYCARD_ZONE_ID and KEYCARD_BASE_URL still work but emit a DeprecationWarning; migrate as follows:

Deprecated Canonical replacement
KEYCARD_ZONE_ID="abc1234" KEYCARD_ZONE_URL="https://abc1234.keycard.cloud"
KEYCARD_ZONE_ID="abc1234" + KEYCARD_BASE_URL="https://custom.example.com" KEYCARD_ZONE_URL="https://abc1234.custom.example.com"

Application credential: discovery is delegated to keycardai.oauth.server.discover_credential. KEYCARD_APPLICATION_CREDENTIAL_TYPE selects client_secret, workload_identity or web_identity (eks_workload_identity is accepted as a legacy alias). Without a selector, exactly one source may be configured: KEYCARD_CLIENT_ID + KEYCARD_CLIENT_SECRET, an injected workload token file (AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE, AWS_WEB_IDENTITY_TOKEN_FILE, AZURE_FEDERATED_TOKEN_FILE or KEYCARD_EKS_WORKLOAD_IDENTITY_TOKEN_FILE), or KEYCARD_WEB_IDENTITY_KEY_STORAGE_DIR. More than one source with no selector fails at startup with an ambiguity error naming the selector as the remedy.

Testing

Fake delegated access without patching internals:

from keycardai.fastmcp.testing import mock_access_context

with mock_access_context(access_token="fake_token"):
    ...  # grants resolve to an AccessContext serving fake_token

The bare access_token form serves the token for any resource, so it will not catch a mistyped resource URL in an access(...) call. Pass resource_tokens={...} when the test should enforce which resources the tool reads; resources outside the dict raise ResourceAccessError, matching production.

Migration from keycardai-mcp-fastmcp

The keycardai-mcp-fastmcp package is retired. Its final release, 0.21.0, stays on PyPI so existing pins keep resolving: from keycardai.mcp.integrations.fastmcp import AuthProvider emits a DeprecationWarning pointing here and returns the same class. New code should depend on keycardai-fastmcp and import from keycardai.fastmcp.

Release files for keycardai-fastmcp 0.7.1

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

Source distribution (sdist)

Source distribution for keycardai-fastmcp 0.7.1
File Size Uploaded
keycardai_fastmcp-0.7.1.tar.gz 164.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for keycardai-fastmcp 0.7.1
File Interpreter ABI Platform
keycardai_fastmcp-0.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 186.3 kB

Release files / keycardai_fastmcp-0.7.1.tar.gz

Download URL keycardai_fastmcp-0.7.1.tar.gz
Size 164.1 kB
Tags Source
SHA-256 checksum
How to use checksums
b9010d64f10ac47343b650ed3f46af1a43a5d7b0152dadbe7b7f0e6c613dac3c
BLAKE2b-256 checksum
How to use checksums
9867a979cf45c2a95d2073f4e7844f315e03e5318f2b85dc456cdb124432e5b5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / keycardai_fastmcp-0.7.1-py3-none-any.whl

Download URL keycardai_fastmcp-0.7.1-py3-none-any.whl
Size 22.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e706c7b25eaec80a68a03222e2f92f12854718a6a3efed407e6090064f2755e1
BLAKE2b-256 checksum
How to use checksums
85fa92d7b5ee7407c59b578d3021f78bbd35464e4656862aab3ee7212f9c8f80
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.15 {"installer":{"name":"uv","version":"0.12.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.7.1 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.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