# MCP server

cronpad includes a Model Context Protocol (MCP) server at `/mcp`. Connect an MCP-capable assistant with an API key and it can validate and preview schedules, create, pause, trigger and delete jobs, and read run history to debug failures. Everything goes through the same permission checks as the REST API, scoped to the API key's workspace.

## Connect

### Over HTTP (recommended)

Create an API key (**Settings → API keys**), then add the server to your assistant's MCP configuration. Most clients accept this shape:

```json
{
  "mcpServers": {
    "cronpad": {
      "type": "http",
      "url": "https://app.cronpad.com/mcp",
      "headers": {"Authorization": "Bearer cp_…"}
    }
  }
}
```

Replace the URL with your server's `public_url` + `mcp.path` (default `/mcp`) when self-hosting.

### Over stdio

For clients that only launch local processes, the cronpad binary can act as a stdio bridge to a remote server:

```json
{
  "mcpServers": {
    "cronpad": {
      "command": "cronpad",
      "args": ["mcp", "--url", "https://app.cronpad.com"],
      "env": {"CRONPAD_API_KEY": "cp_…"}
    }
  }
}
```

For a custom endpoint path, add `--path /custom-mcp` to the stdio arguments.
The bridge also accepts `--api-key`; environment variables are recommended to
keep credentials out of process arguments. MCP never returns signing secrets.

## Tools

| Tool | Purpose | Changes data |
|---|---|---|
| `validate_schedule` | Check an expression; returns the detected kind, canonical form, English description and errors with suggestions | No |
| `preview_schedule` | Next N fire times for an expression in a timezone | No |
| `list_jobs` | Jobs, filterable by state, tag and text | No |
| `get_job` | One job with its next fire times and 24-hour stats | No |
| `create_job` | Create a job. Its description tells the model to preview the schedule and confirm with the user first. | Yes |
| `update_job` | Change any job field | Yes |
| `pause_job`, `resume_job` | Stop or restart firing | Yes |
| `trigger_job` | Run now, without changing the schedule | Yes |
| `delete_job` | Delete a job (marked destructive) | Yes |
| `list_runs` | Runs of a job, filterable by status | No |
| `get_run` | One run with every attempt, status code, error and response | No |
| `get_stats` | Success rate, p95 start lag and SLA breaches, per job or workspace | No |

Destructive tools carry the MCP `destructiveHint` annotation, so assistants that support it ask before calling them. Read-only tools carry `readOnlyHint`.

Tool arguments use JSON objects. Schedule tools accept `expr`, `timezone`, and
optional `kind`; preview also accepts `count` (1–100) and RFC3339 `after`.
`create_job` uses the API job body: `name`, `schedule: {expr, timezone}`, `sink:
{kind, config}`, and optional `retry`, `tags`, timeout and policy fields. A
top-level `timezone` is also accepted when `schedule` is supplied. `update_job`
accepts `job_id` plus supplied job fields. State tools take `job_id`;
`get_run` takes `run_id`. Lists accept `limit` (1–200) and `cursor`; job lists
also accept `state`, `tag`, `query`, and run lists accept `status`.
`get_stats` returns 24-hour counts and success rate (0–1), with the REST
statistics' lag and SLA fields. Workspace stats require `*` scope, as in REST.

## Resources and prompts

| Kind | Name | Content |
|---|---|---|
| Resource | `cronpad://jobs/{id}` | A job as JSON |
| Resource | `cronpad://runs/{id}` | A run with its attempts |
| Prompt | `debug_failing_job(job_id)` | Walks the model through reading recent runs and attempts, classifying the failure and proposing a fix |

## Example

> **You:** Every second Tuesday at 9am India time, call https://api.example.com/payroll/reminders.
>
> **Assistant** calls `preview_schedule("second tuesday of every month at 9am", "Asia/Kolkata")`, shows you the next three dates, and after you confirm calls `create_job`. It then offers to call `trigger_job` once to test the endpoint.

## Turning it off

Set `[mcp] enabled = false` to remove the `/mcp` endpoint. API keys used by assistants can be given narrower scopes (`jobs:read`, `runs:read`) for read-only access. See [API authentication](../api/index.md#authentication).