hatchet-ops-mcp
by TavenYin
README.md
# hatchet-ops-mcp
A small, read-only [MCP](https://modelcontextprotocol.io/) server that exposes
[Hatchet](https://hatchet.run) **client / REST observability APIs** to coding
agents — across multiple environments from one process.
Hatchet itself does **not** ship an official ops MCP. This project is a thin
wrapper around the official Python [`hatchet-sdk`](https://pypi.org/project/hatchet-sdk/):
each tool call picks an `env`, resolves that environment's Hatchet JWT from
`envs.yaml`, uses a cached `Hatchet` client, and returns the SDK response as
structured MCP output.
The default transport is local stdio (no host / API key). For a shared process
on a trusted network, streamable HTTP (and legacy SSE) are also available.
## Why this exists
Hatchet's official agent integrations today are mainly **docs MCP**, **CLI
skills**, and SDK helpers that turn *your* workflows/tasks into agent tools.
None of those is a multi-tenant ops console for listing runs, workers, and
queue metrics.
This server fills that gap by mapping read-only `hatchet-sdk` feature clients
(`workflows`, `runs`, `workers`, `metrics`, logs, …) to MCP tools, with one
extra capability: explicit `env` routing so one MCP process can talk to several
Hatchet tenants/instances.
## Requirements
- Python 3.12 or newer
- [uv](https://docs.astral.sh/uv/)
- A Hatchet API token (JWT) for each environment you want to query
- Docker and Docker Compose (optional, for HTTP deployment)
## Setup
```bash
git clone <repository-url>
cd hatchet-ops-mcp
cp envs.example.yaml envs.yaml
uv sync
```
Edit `envs.yaml`. Each environment has its own Hatchet JWT (`token`). Entries
left as `TODO` remain visible in `list_envs`, but cannot be queried until a
token is provided.
```yaml
envs:
development:
description: "Development"
token: "eyJ..." # Hatchet API JWT for this env
server_url: "http://localhost:8080"
tls_strategy: "none"
production:
description: "Production"
token: "TODO"
server_url: "https://hatchet.example.com"
```
`server_url` and `tls_strategy` are optional. When `server_url` is omitted,
Hatchet resolves it from the JWT.
> **Note:** `envs.yaml` `token` is the **Hatchet** credential. The MCP HTTP
> Bearer key is separate: set `HATCHET_OPS_MCP_API_KEY` (only needed for
> streamable-http / sse).
## Local use (stdio)
This is the usual path for Cursor / Claude Code on your laptop — no bind
address, no API key:
```bash
uv run hatchet-ops-mcp
```
Set `HATCHET_OPS_MCP_ENVS` when the config is outside the current checkout:
```bash
HATCHET_OPS_MCP_ENVS=/absolute/path/to/envs.yaml uv run hatchet-ops-mcp
```
MCP client configuration:
```json
{
"mcpServers": {
"hatchet-ops": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/hatchet-ops-mcp",
"run",
"hatchet-ops-mcp"
],
"env": {
"HATCHET_OPS_MCP_ENVS": "/absolute/path/to/hatchet-ops-mcp/envs.yaml"
}
}
}
}
```
## Shared HTTP (trusted network)
For one process that several clients hit on a LAN / VPN. Auth is a single
shared Bearer API key checked by middleware — not OAuth.
HTTP refuses to start without an API key unless you pass `--allow-insecure`
(trusted network only). Default bind is `127.0.0.1`; only override `--host`
when something other than this machine must connect (Docker Compose already
sets `0.0.0.0`).
```bash
export HATCHET_OPS_MCP_API_KEY='replace-with-a-strong-random-value'
uv run hatchet-ops-mcp --transport streamable-http
# optional: --port 8765
# Docker / LAN only: --host 0.0.0.0
```
Endpoints: `/mcp` (MCP), `/health` (no auth).
Docker Compose mounts local `envs.yaml` and binds `0.0.0.0` for you:
```bash
export HATCHET_OPS_MCP_API_KEY='replace-with-a-strong-random-value'
docker compose up --build
```
Remote MCP client example:
```json
{
"mcpServers": {
"hatchet-ops": {
"url": "http://hatchet-ops.internal:8765/mcp",
"headers": {
"Authorization": "Bearer replace-with-the-shared-api-key"
}
}
}
}
```
| Environment variable | Purpose |
| --- | --- |
| `HATCHET_OPS_MCP_ENVS` | Path to `envs.yaml` |
| `HATCHET_OPS_MCP_TRANSPORT` | `stdio` (default), `streamable-http`, or `sse` |
| `HATCHET_OPS_MCP_PORT` | HTTP port (default `8765`) |
| `HATCHET_OPS_MCP_HOST` | HTTP bind address (default `127.0.0.1`; Docker uses `0.0.0.0`) |
| `HATCHET_OPS_MCP_API_KEY` | Shared Bearer key for MCP HTTP (not a Hatchet JWT) |
| `HATCHET_OPS_MCP_ALLOW_INSECURE` | Allow HTTP with no API key |
## Tools
| Tool | Purpose |
| --- | --- |
| `list_envs` | List environment names and configuration status without secrets |
| `list_workflows` | List workflow definitions (optional `workflow_name` filter) |
| `get_workflow` | Get one workflow definition by ID |
| `list_runs` | List recent workflow or task runs (status / name / metadata / worker filters) |
| `get_run` | Get one workflow run (status, tasks; payloads opt-in) |
| `get_run_events` | Get lifecycle events for one workflow run |
| `get_run_logs` | Get logs for one task **or** merged logs for a whole workflow run |
| `list_workers` | List workers and their status |
| `get_worker` | Get one worker by ID |
| `get_queue_metrics` | Inspect current queue backlog depth |
| `get_task_metrics` | Task counts by status over a time window |
| `get_task_stats` | Per-task-name statistics for the tenant |
All tools except `list_envs` require `env`.
### Debug playbook
Typical failed / stuck run investigation:
1. `list_runs` with `statuses`, `workflow_name` (exact name), and/or `additional_metadata`
2. `get_run` for status and per-task summaries (`include_payloads=true` for I/O)
3. `get_run_events` for QUEUED / STARTED / FAILED / CANCELLED timeline
4. `get_run_logs` with `workflow_run_id` for merged application logs (newest lines kept)
5. If stuck QUEUED: `list_workers` / `get_worker`
6. For environment health: `get_queue_metrics`, `get_task_metrics`, `get_task_stats`
`list_runs` / `get_run` omit payloads by default. `include_payloads=true` on
`list_runs` also disables `minimal_output`.
## Security
- `envs.yaml` is ignored by Git. Never commit real Hatchet tokens.
- Prefer stdio on developer machines; treat shared HTTP as an internal service.
- HTTP refuses to start without a Bearer API key unless `--allow-insecure` is set.
- Prefer read-only Hatchet tokens when the deployment supports scoped tokens.
- Tool results may contain workflow inputs, outputs, errors, or logs. Treat MCP
output according to the sensitivity of the underlying environment.
- The server intentionally exposes no mutating Hatchet operations.
## Development
```bash
uv sync
uv run pytest
uv build
```
## License
[MIT](LICENSE)
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues