Skip to content

Agent-native (MCP)

SchedStack ships a first-class Model Context Protocol server so an agent can schedule durable HTTP deliveries directly — no glue code, no hand-written API client. It speaks MCP over stdio, exposes ten scheduling tools, and is scoped to exactly one (project, mode) by the API key you give it.

Two things make it agent-native rather than a thin API wrapper:

  • A forgiving when parser — agents pass natural timing strings ("24h", "in 2h", "2026-07-01 09:00", "0 9 * * *") and the server resolves them, including DST-correct wall-clock and recurring schedules.
  • Self-correcting errors — a bad input comes back as a plain-language hint the agent can act on ("that time is in the past — give a future time, or a duration like \"24h\""), not an opaque protocol failure.

Run the sched mcp subcommand. It speaks MCP over stdio and reaches SchedStack over its HTTP /v1 API using your API key — it holds no database connection, so anyone with a key can run it (including against the managed service). Set two environment variables:

Terminal window
SCHED_API_KEY=sk_live_… SCHED_API_URL=https://api.schedstack.com sched mcp
  • SCHED_API_KEY (required) — your sk_live_… or sk_test_… key. Every tool call is scoped to that key’s (project, mode), so the agent only ever sees and acts on schedules in that scope. The command exits with SCHED_API_KEY is required if it’s missing.
  • SCHED_API_URL (optional) — the engine base URL. Defaults to http://localhost:8080; set it to https://api.schedstack.com for the managed service.

Use an sk_test_… key to drive the test mode sandbox, or an sk_live_… key for production.

Most clients launch the server as a child process and pass env through. Register sched as a stdio server with args: ["mcp"] and your key (and SCHED_API_URL for the managed service):

In claude_desktop_config.json:

{
"mcpServers": {
"schedstack": {
"command": "sched",
"args": ["mcp"],
"env": {
"SCHED_API_KEY": "sk_live_…",
"SCHED_API_URL": "https://api.schedstack.com"
}
}
}
}

The server announces itself as sched / SchedStack. Once connected, the ten tools below are available to the agent.

Every timing input — preview_schedule’s and create_schedule’s when — runs through one parser. It accepts four forms and picks the kind (one_shot vs recurring) for you:

Form Examples Resolves to
Relative duration "24h", "in 2h", "90s", "1h30m" one-shot, now + duration
RFC3339 instant "2026-07-01T09:00:00Z", "2026-07-01T05:00:00-04:00" one-shot, that exact instant
Offset-less wall-clock "2026-07-01 09:00", "2026-07-01T09:00:00" one-shot, that local time in timezone (DST-correct)
5-field cron "0 9 * * *", "*/15 * * * *" recurring, in timezone (default UTC)

How it decides: five or more space-separated fields is treated as cron; an "in " prefix is stripped and the rest parsed as a Go duration; otherwise it tries the timestamp layouts. An offset-less timestamp is interpreted in the timezone you pass (not UTC), so "2026-07-01 09:00" with timezone: "America/New_York" means 9am New York — mapped across daylight-saving transitions the same way recurring schedules fire. An RFC3339 string carries its own offset and is taken as-is.

What it deliberately does not accept:

  • ISO8601 durations like "PT2H" — use "2h" instead.
  • Seconds-precision cron — only standard 5-field cron (minute hour day-of-month month day-of-week).
  • Sub-second delays — the minimum is ~1s ("500ms" is rejected). SchedStack is a durable scheduler, not a sub-second timer.
  • Times in the past — both relative and absolute inputs must be in the future.

Bad inputs return guidance, e.g. "couldn't parse \"PT2H\" — try a duration (\"24h\", \"in 2h\"), an ISO8601 time, or a cron (\"0 9 * * *\")".

The agent sees exactly these tools — no more. There is intentionally no update, reschedule, or endpoint-management tool over MCP (to change timing, cancel and create again; endpoint profiles are managed via the HTTP API).

Resolve a timing input and return the next fire times without creating a schedule.

Arg Required Notes
when yes duration, RFC3339, offset-less wall-clock, or 5-field cron
timezone no IANA name (e.g. America/New_York); applies to cron and offset-less times. Default UTC

Returns kind (one_shot or recurring) and next_runs (up to 5 RFC3339 instants).

Create a durable schedule. Delivery is at-least-once and retried until it lands — see Retries & dead-letter.

Arg Required Notes
endpoint yes the https URL to deliver to
when yes same forms as preview_schedule
method no HTTP method (default POST)
body no request body; sent as application/json
timezone no IANA name for cron / wall-clock times (default UTC)
ttl no deliver-by deadline as a duration, e.g. "1h"
metadata no string→string labels

New schedules use the default retry policy (8 attempts, exponential backoff from 5s to 1h, jittered). The response is a schedule summary including id, state, kind, next_fire_at, and the resolved next_runs.

Retrieve a schedule by id. Arg: id (required).

List schedules in this scope, cursor-paginated.

Arg Required Notes
state no active | paused | completed | canceled
kind no one_shot | recurring
cursor no opaque pagination cursor from a prior call
limit no page size (default 20, max 100). On list_schedules an out-of-range value (<= 0 or > 100) falls back to 20 — it is not clamped to 100

Returns schedules, plus next_cursor and has_more for paging.

Pause a schedule (reversible — future occurrences stop until resumed). Arg: id (required).

Resume a paused schedule. Arg: id (required).

Cancel a schedule (terminal — stops all future occurrences). Arg: id (required).

A delivery is one occurrence of a schedule. See Dead-letter & replay for the lifecycle.

List deliveries (occurrences), cursor-paginated.

Arg Required Notes
status no e.g. scheduled | succeeded | dead_letter | expired
schedule_id no restrict to one schedule
cursor no opaque pagination cursor
limit no page size

Returns deliveries, plus next_cursor and has_more.

Retrieve a delivery by id. Arg: id (required). The summary includes status, scheduled_for, attempt_count, and replay_of (set when this delivery is a replay).

Replay a terminal delivery (dead-lettered, expired, or succeeded) as a new occurrence. Arg: id (required). The new delivery records replay_of pointing at the original.

The MCP server is a control surface, not a replacement for the delivery contract. Tell your agent — or your receiver — that:

  • Delivery is at-least-once. A schedule can fire more than once. Your endpoint must verify the Sched-Signature and dedupe on the Idempotency-Key header. See Verify webhook signatures and Idempotency.
  • The SLO covers dispatch initiation, not delivery completion — see the timing guarantee in Quickstart.
  • There is no edit-in-place. Changing a schedule’s timing is cancel-then-create; the HTTP API (not MCP) owns endpoint profiles and richer retry policies.