Metadata-Version: 2.4
Name: fdkey
Version: 0.1.1
Summary: FDKEY verification middleware for MCP servers — gate AI-agent access behind LLM-only puzzles.
Project-URL: Homepage, https://fdkey.com
Project-URL: Dashboard, https://app.fdkey.com
Project-URL: Repository, https://github.com/fdkey/sdks
Project-URL: Issues, https://github.com/fdkey/sdks/issues
Author: FDKEY
License: MIT
License-File: LICENSE
Keywords: ai-agent,anti-bot,captcha,fdkey,mcp,middleware,model-context-protocol,verification
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Security
Requires-Python: >=3.9
Requires-Dist: cryptography>=42.0.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp>=1.0.0
Requires-Dist: pyjwt[crypto]>=2.8.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# fdkey

> **FDKEY verification middleware for MCP servers (Python).** Gate AI-agent
> access to your tools behind LLM-only puzzles. Drop-in for any
> [Model Context Protocol](https://modelcontextprotocol.io) server built
> on the official Python SDK's `FastMCP`.

## What it does

- Injects two MCP tools into your server: `fdkey_get_challenge` and
  `fdkey_submit_challenge`.
- Wraps the tools you want to protect — they return
  `fdkey_verification_required` until the connecting agent has solved a
  challenge.
- Talks to `https://api.fdkey.com` for challenge issuance and scoring.
- Verifies the Ed25519 JWT response **offline** using the public key
  from `https://api.fdkey.com/.well-known/fdkey.json`.

## Install

```bash
pip install fdkey
```

You also need the official MCP Python SDK:

```bash
pip install mcp
```

Get an API key at [app.fdkey.com](https://app.fdkey.com).

## Usage

```python
import os
from mcp.server.fastmcp import FastMCP
from fdkey import with_fdkey

server = FastMCP("my-server")

with_fdkey(
    server,
    api_key=os.environ["FDKEY_API_KEY"],
    protect={
        "sensitive_action": {"policy": "each_call"},
        "register": {"policy": "once_per_session"},
    },
)

@server.tool()
def sensitive_action() -> str:
    # Reaches here only after the agent has solved a challenge.
    return "verified"
```

## Policies

Per-tool gating policy — passed as `{"policy": ...}` in the `protect` map:

- `"each_call"` — verification required for every invocation. Use for
  irreversible actions (payments, deletes).
- `"once_per_session"` — verification required once per connection. Use
  for account creation, signup-style flows.
- `{"type": "every_minutes", "minutes": N}` — verification good for N
  minutes after the puzzle was solved. Middle ground when "every call"
  is too aggressive but "once forever" is too loose. The timer does NOT
  extend on calls — it expires `minutes` after the solve, regardless
  of activity.

```python
protect={
    "delete_account":    {"policy": "each_call"},
    "register":          {"policy": "once_per_session"},
    "refresh_dashboard": {"policy": {"type": "every_minutes", "minutes": 15}},
}
```

## Configuration reference

```python
with_fdkey(
    server,
    api_key="fdk_...",          # required
    protect={...},               # tool name -> {"policy": ...}
    vps_url="https://api.fdkey.com",  # override for self-hosted
    difficulty="medium",         # easy | medium | hard
    on_fail="block",             # block | allow (puzzle failed)
    on_vps_error="allow",        # block | allow — see below
    inline_challenge=False,      # embed puzzle in blocked-tool error
    tags={"env": "prod"},        # forwarded to FDKEY for analytics
)
```

### Failure-mode defaults

`on_vps_error="allow"` is the default — if the FDKEY scoring service is
unreachable, the protected tool falls through to your handler instead of
blocking. We chose this so an FDKEY outage doesn't brick your workflow
(e.g. if we shut down or DNS can't resolve `api.fdkey.com`). FDKEY is
verification, not gating — your service should still serve traffic when
ours is down. Set `on_vps_error="block"` if you'd rather drop traffic
than admit unverified callers during an outage.

## Reading verification context

```python
from fdkey import get_fdkey_context

@server.tool()
def whoami(ctx) -> str:
    fdkey = get_fdkey_context(server, ctx)
    if fdkey and fdkey.verified:
        # `score` and `tier` are first-class fields on the context.
        # `score` is a 0..1 float — today effectively binary
        # (1.0 passed / 0.0 failed) but reserved for future capability
        # scoring without an API change. `tier` is the VPS-issued
        # capability bucket label.
        return f"verified (score={fdkey.score}, tier={fdkey.tier})"
    return "not verified"
```

## What FDKEY sees

- The MCP `clientInfo` your agent reports (when forwarded by your server).
- Challenge IDs, scores, timestamps.
- Your integrator-supplied `tags`.

## Security notes

- **JWT `aud` is not validated by the SDK.** The audience claim binds the
  JWT to the integrator's `vps_users.id`, which the SDK doesn't know at
  verify time. The VPS already binds `aud` to the API key that requested
  the challenge — defense in depth — but in principle, a JWT issued for
  one FDKEY-protected service could be replayed against a different one
  within the JWT lifetime (~5 min default). Keep the JWT lifetime short
  on the VPS side if your threat model includes cross-integrator replay.

## What FDKEY does NOT see

- Your prompts.
- Tool inputs or outputs.
- Your end users' identities or PII.

## Links

- Marketing + docs: <https://fdkey.com>
- Dashboard (sign up + manage keys): <https://app.fdkey.com>
- Source: <https://github.com/fdkey/sdks>
- Issues: <https://github.com/fdkey/sdks/issues>

## License

MIT — see [LICENSE](./LICENSE).
