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.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 keycardai-fastmcp 0.7.0
File Size Uploaded
keycardai_fastmcp-0.7.0.tar.gz 163.1 kB Details

Built distribution (wheel)

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

Total release size: 184.7 kB

Release files / keycardai_fastmcp-0.7.0.tar.gz

Download URL keycardai_fastmcp-0.7.0.tar.gz
Size 163.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9af5d3883092a574033179c8659c1330cdcaa7233bac82d4102351fe059c64c4
BLAKE2b-256 checksum
How to use checksums
91ebfae59b1d050f81ab05bc4f9730494bb1cb598cadbc427f3e7a7799efd057
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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.0-py3-none-any.whl

Download URL keycardai_fastmcp-0.7.0-py3-none-any.whl
Size 21.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2a8fd7db94ff7b6d75a88d7bda881175d85466b9129d4830db9dba733eae657d
BLAKE2b-256 checksum
How to use checksums
ff96c488ebd1b45161250f3827b38955d8e4739159d19f89e9f9540028c6855b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.10 {"installer":{"name":"uv","version":"0.12.10","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

0.7.1

2 release files

This release

0.7.0 This release

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