hawkapi-mcp
Officialby Hawk-API
README.md
# hawkapi-mcp
MCP (Model Context Protocol) server for [HawkAPI](https://github.com/Hawk-API/HawkAPI). Auto-exports every route as an agent tool — any MCP-compatible client can call your API.
## Install
```bash
pip install hawkapi-mcp
```
## Quickstart
```python
from hawkapi import HawkAPI
from hawkapi.responses import JSONResponse
from hawkapi_mcp import mount_mcp
app = HawkAPI()
@app.get("/users/{user_id:int}")
async def get_user(user_id: int) -> JSONResponse:
return JSONResponse({"id": user_id, "name": "Alice"})
@app.post("/items")
async def create_item(body: dict) -> JSONResponse:
return JSONResponse({"created": body})
mount_mcp(app, allow_unauthenticated=True) # serves POST /mcp — dev only; see Auth
```
> `/mcp` MUST be protected in production — pass `dependencies=[...]` to require
> auth, or `allow_unauthenticated=True` to opt out explicitly. See [Auth](#auth).
Point any MCP-compatible client at `http://your-host/mcp`. Every HawkAPI route becomes a tool — its `operationId` is the tool name, the OpenAPI schema becomes the input schema.
## Tool naming
| Route definition | Generated tool name |
|---|---|
| `@app.get("/users/{id}", operation_id="get_user")` | `get_user` |
| `@app.get("/users/{id}")` (no `operation_id`) | `get_users_id` |
## Tool input schema
The decorator combines path / query / header parameters and the JSON request body into a single object schema. Parameter names are namespaced so they cannot collide:
| Source | Schema key |
|---|---|
| Path parameter | `path.<name>` |
| Query parameter | `query.<name>` |
| Header parameter | `header.<name>` |
| JSON body | `body` |
Cookie parameters are deliberately **not** exposed as tool inputs — exposing session cookies as data trains agents to handle credentials as ordinary fields. Forward credentials via the request to `/mcp` instead (see [Auth](#auth)).
`tools/call` example:
```json
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_user",
"arguments": {"path.user_id": "42"}
}
}
```
The tool result has the response body in `content[0].text` and the raw HTTP status / headers in `structuredContent`. `isError` is `true` for any 4xx/5xx response.
Credential-bearing response headers are stripped from `structuredContent` before the agent sees them: `set-cookie`, `authorization`, and anything matching `x-*-token` / `x-*-secret`. Add more via `strip_response_headers={...}`.
## Filtering tools
```python
mount_mcp(app, include_only={"get_user", "create_item"})
mount_mcp(app, exclude={"internal_admin_route"})
```
## Supported JSON-RPC methods
* `initialize` — handshake. Returns the MCP protocol version, server info, and tool capability.
* `ping` — keepalive.
* `tools/list` — return the tool catalog.
* `tools/call` — invoke a tool. Returns response body + HTTP status.
* `notifications/initialized` — accepted, no response.
The endpoint accepts both single JSON-RPC objects and batches.
## Auth
**The `/mcp` endpoint exposes every route and MUST be protected.** A single MCP
tool call can invoke any route, and the bridge synthesises an inner ASGI request
— so middleware that only guards inner routes can be bypassed. To make this
hard to get wrong, `mount_mcp()` raises `RuntimeError` at mount time unless you
either:
* pass `dependencies=[...]` to attach auth (e.g. `Depends`) to the `/mcp` route, or
* explicitly opt out with `allow_unauthenticated=True` (local dev, or auth
enforced upstream).
```python
from hawkapi import Depends
from hawkapi_mcp import mount_mcp
mount_mcp(app, dependencies=[Depends(verify_token)]) # protected
mount_mcp(app, allow_unauthenticated=True) # explicit opt-out
```
`hawkapi-mcp` does not define its own auth layer — wire your HawkAPI dependencies
(HTTPBearer, OAuth2, API key) on the MCP route just like any other path. The
caller's `Authorization` and `Cookie` headers on the outer `/mcp` request are
forwarded into the synthetic inner request, so inner-route `Depends(auth)` sees
the real credentials (and the real client address is propagated where available).
Header arguments forwarded by the client land in the request before middleware runs.
## Tool catalog freshness
The tool catalog is derived from `app.openapi()` and cached. By default it is
cached for the process lifetime, so routes added/removed/disabled at runtime are
**not** reflected — call `server.invalidate_tools()` to force a refresh, or pass
`cache_ttl_seconds=...` to `mount_mcp()` (or `MCPServer`) to auto-refresh after
the given interval:
```python
mount_mcp(app, dependencies=[...], cache_ttl_seconds=60)
```
## Development
```bash
git clone https://github.com/Hawk-API/hawkapi-mcp.git
cd hawkapi-mcp
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/
```
## Specification
Implements a subset of the [Model Context Protocol](https://modelcontextprotocol.io) sufficient to advertise and invoke tools. Streamable HTTP transport only — stdio is out of scope (deploy your app behind any ASGI server and the agent connects to the `/mcp` URL).
## License
MIT.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues