MCP Server Cron Monitoring: Tools, Client Setup, and Limits
Updated
Contents
CronRadar's hosted MCP server lets an MCP client such as Claude Code, Cursor, VS Code, or Codex inspect and manage your scheduled-job monitors: list jobs and their health, read check-ins and alerts, pause or resume monitoring, and create monitors. It runs at https://api.cronradar.com/api/agent/mcp over Streamable HTTP and authenticates with the same temporary bearer token as CronRadar's management REST API, with the same permissions and expiry. It is optional. This page is the reference for clients, tools, tasks, and errors; choosing access and reviewing an agent's setup work are covered in AI agent cron monitoring setup.
What the Hosted MCP Server Is#
| Setting | Value |
|---|---|
| Name | cronradar |
| Transport | Streamable HTTP |
| Server URL | https://api.cronradar.com/api/agent/mcp |
| Authentication | Authorization: Bearer header carrying a temporary token from the console |
What that means in practice (the agent quickstart has the full rules):
- The token is a workspace grant, not a deployment key. At app.cronradar.com/assistant you pick one workspace, an expiry (15 minutes to 24 hours; one hour by default), and permissions: read is always included, "Set up applications and jobs" adds write, and "Allow deletion" requires write. The token sits inside the private setup prompt after
Authorization: Bearerand starts withcr_agent_. Copy only that value into your client's secret configuration. - No OAuth, no refresh. When the token expires or you revoke it, requests fail with 401 until you create new access and replace the value.
- Same rules as REST. Both transports enforce the same permissions and expiry, and the server rechecks the grant and your current workspace role on every tool call. A grant never exceeds your role.
- Header only. The server refuses a token sent as a
tokenoraccess_tokenquery parameter. - Management, not ingestion. The tools don't send check-ins (deployed jobs report to
https://cron.lifewith their own ingestion key), run jobs, read application logs, or change billing settings, alert destinations, or notification secrets. Creating or resuming a job does count toward your plan like any other monitor.
Configure Your Client Without Committing the Token#
Each configuration below names the server cronradar and reads the token from an environment variable or secret store, so no committed file holds it. The syntax was checked against each client's documentation on October 24, 2026. Set the variable in the environment your client starts from; in bash or zsh, read -s keeps the pasted value off the screen and out of shell history:
# Paste the cr_agent_ token and press Enter; nothing is echoed.
read -rs CRONRADAR_AGENT_TOKEN && export CRONRADAR_AGENT_TOKENClaude Code#
Add the server for yourself in the current project (local scope, stored in ~/.claude.json):
claude mcp add --transport http cronradar https://api.cronradar.com/api/agent/mcp \
--header 'Authorization: Bearer ${CRONRADAR_AGENT_TOKEN}'The single quotes stop your shell from expanding the variable, so Claude Code stores the reference and expands it in headers when it connects. To share the definition with a team, commit a project-root .mcp.json instead; it holds only the variable name, and interactive sessions ask each person to approve a project-scoped server:
{
"mcpServers": {
"cronradar": {
"type": "http",
"url": "https://api.cronradar.com/api/agent/mcp",
"headers": {
"Authorization": "Bearer ${CRONRADAR_AGENT_TOKEN}"
}
}
}
}Check it with claude mcp list or /mcp. If the variable isn't set, claude mcp list warns, and the unexpanded ${CRONRADAR_AGENT_TOKEN} text is sent and rejected with 401; Claude Code reports that as a failed connection rather than offering OAuth.
The variable is read when Claude Code starts, so a new token means a new session. A headersHelper avoids that: Claude Code runs the command on each connection, and after a 401 or 403 during a tool call it reruns it, reconnects, and retries once. On macOS, the helper can read the Keychain:
#!/bin/sh
# ~/bin/cronradar-mcp-headers: print the current token as a JSON header object.
token=$(security find-generic-password -s cronradar-agent-token -w) || exit 1
printf '{"Authorization": "Bearer %s"}\n' "$token"Make it executable with chmod +x. Store or replace the token with security add-generic-password -U -a "$USER" -s cronradar-agent-token -w, which prompts for the value when -w comes last (man security). Then register the helper in place of headers:
claude mcp add-json cronradar '{"type":"http","url":"https://api.cronradar.com/api/agent/mcp","headersHelper":"~/bin/cronradar-mcp-headers"}'When a grant expires, update the Keychain item and keep working.
Cursor#
Use ~/.cursor/mcp.json for every project or .cursor/mcp.json for one. Cursor resolves ${env:NAME} in url and headers from your shell profile or system environment, and asks for approval before using MCP tools by default:
{
"mcpServers": {
"cronradar": {
"url": "https://api.cronradar.com/api/agent/mcp",
"headers": {
"Authorization": "Bearer ${env:CRONRADAR_AGENT_TOKEN}"
}
}
}
}VS Code With GitHub Copilot#
Copilot sessions in VS Code read a portable configuration: ~/.copilot/mcp-config.json for your user (Copilot Global in MCP: Add Server) or .mcp.json at the workspace root. Header values accept ${NAME}; use the braced form, since VS Code's MCP client treats a bare $NAME as literal text:
{
"mcpServers": {
"cronradar": {
"type": "http",
"url": "https://api.cronradar.com/api/agent/mcp",
"headers": {
"Authorization": "Bearer ${CRONRADAR_AGENT_TOKEN}"
},
"tools": ["*"]
}
}
}The variable must be in the environment of the host that connects; after changing it, restart VS Code so the agent host inherits it. The older .vscode/mcp.json format can prompt for the token with a password-style ${input:...} variable, but servers that need input variables work only in Local sessions, not Copilot harness sessions.
Codex#
In ~/.codex/config.toml, or a trusted project's .codex/config.toml, bearer_token_env_var names the variable sent as the bearer token:
[mcp_servers.cronradar]
url = "https://api.cronradar.com/api/agent/mcp"
bearer_token_env_var = "CRONRADAR_AGENT_TOKEN"Claude on the Web and Desktop#
Claude adds remote servers as custom connectors under Customize > Connectors. Choose No sign-in, then under Request headers pick authorization and enter Bearer followed by the token. Anthropic's docs describe request headers as a beta for a limited set of organizations, suited to a credential everyone using the connector shares, and not editable after the connector is added. That fits a personal plan; on Team and Enterprise plans an Owner adds connectors for the whole organization, which would share one person's grant. Expect to remove and re-add the connector for each new token.
Any other client needs documented support for a custom header on a remote Streamable HTTP server; without it, use the private setup prompt over REST.
The 16 Tools and the Permissions They Need#
Your client's tool discovery returns the authoritative argument schemas; this table summarizes them. "Write" and "Delete" are grant permissions, and the role is the minimum workspace role you must hold.
| Tool | What it does | Needs | Key arguments |
|---|---|---|---|
get_context |
Workspace, token expiry, and effective permissions; call it first | Read | none |
list_applications |
Applications in the workspace | Read | limit, offset |
get_application |
One application's settings, without API keys or notification secrets | Read | applicationId |
create_application |
New application; creates no ingestion key | Write, Member | request: name, description, environment, timezone, defaultGracePeriod |
update_application |
Change name, description, environment, time zone, or default grace period | Write, Admin | applicationId, request |
delete_application |
Permanently delete an application with its jobs, check-ins, alerts, and ingestion keys | Delete, Admin | applicationId, confirmName |
create_ingestion_key |
New deployment key for one application, returned once | Write, Admin | applicationId, name, expiresInDays (1 to 365) |
list_jobs |
Jobs in one application, with schedule and health | Read | applicationId, limit, offset |
get_job |
One job's schedule, grace period, pause state, and health | Read | jobId |
create_job |
Register a job without recording a run | Write, Member | applicationId, request: name, monitorKey, schedule, gracePeriod |
update_job |
Change name, schedule, or grace period; the monitor key can't change | Write, Admin | jobId, request |
delete_job |
Permanently delete a job with its check-ins and alerts | Delete, Admin | jobId, confirmName |
pause_job |
Pause monitoring, optionally until a UTC time | Write, Admin | jobId, until, reason |
resume_job |
Resume monitoring, subject to plan capacity | Write, Admin | jobId |
list_check_ins |
Recent check-ins, newest first, without payloads or source addresses | Read | jobId, limit, offset |
list_alerts |
Recent alerts' type, delivery status, and timing, without destinations | Read | jobId, limit, offset |
Three details trip agents up:
- Nested
request. The create and update tools for applications and jobs take their fields inside arequestobject; the others take flat arguments. An update changes only the fields it supplies. - Two identifiers per job.
jobIdis a GUID naming the management resource;monitorKeyis the key deployed check-ins use. Neither substitutes for the other. Application IDs look likeapp_3f9c2a7b1d4e. - UTC schedules.
scheduleis five-field cron evaluated in UTC, and an application'stimezonedoesn't change when runs are expected. Schedule syntax lists what's rejected (a seconds field,@daily,L,7for Sunday) and how restricting both day fields differs from Linux cron.
Four Tasks From Prompt to Tool Calls#
These sequences follow the quickstart's task recipes: confirm access with get_context, read before writing, and change only what you asked for.
"Which Jobs Are Failing?"#
Read-only: list every job in the production application that is warning or
critical, with its last execution result and next expected run.get_context
list_applications {"limit": 100}
list_jobs {"applicationId": "app_3f9c2a7b1d4e", "limit": 100}
repeat with "offset" set to nextOffset until it is nulllist_jobs returns each job's status (pending, healthy, warning, critical, or paused), lastExecutionResult (success, failed, timeout, outcomefailed when a completed run broke one of its outcome checks, or unknown), hasActiveAlert, lastPingAt, and nextExpectedAt, so a sweep needs no per-job calls. There is no workspace-wide search: one list_jobs per application. A paused job with isPlanLimited: true isn't failing; plan capacity is keeping it unprotected.
"Why Was nightly-export Late Last Night?"#
Read-only: why was nightly-export in production late last night? Read the job,
its recent check-ins, and its alerts. Tell me whether it never reported,
started late, failed, or started without finishing, and what to check in the
scheduler.list_jobs {"applicationId": "app_3f9c2a7b1d4e"} then find monitorKey "nightly-export"
get_job {"jobId": "7d0f5c62-9b1e-4f3a-8c2d-5e6a1b9f0c47"}
list_check_ins {"jobId": "7d0f5c62-9b1e-4f3a-8c2d-5e6a1b9f0c47", "limit": 20}
list_alerts {"jobId": "7d0f5c62-9b1e-4f3a-8c2d-5e6a1b9f0c47", "limit": 10}get_job supplies the schedule, gracePeriod, and nextExpectedAt. Each check-in has receivedAt, type (heartbeat, start, complete, or fail), attempt, and expectedAt, the scheduled occurrence CronRadar was waiting for when it arrived. Every check-in, including start, moves that expectation to the next occurrence, so only a run's first check-in lines up with the run it answers: its receivedAt minus expectedAt is how late the run began.
| Evidence around the expected time | What it means |
|---|---|
| No check-in | Never reported: the scheduler didn't start it, or it ran and couldn't reach CronRadar |
start well after expectedAt |
Started late: look at the scheduler, host, or queue |
start, then fail |
Failed explicitly; the fail opened an incident immediately |
start with no complete or fail |
Started without finishing; marked stuck once the next expected time plus grace passes |
A lone heartbeat well after expectedAt |
Finished late: heartbeat-only jobs report at completion, so a slow run and a late start look alike |
list_alerts adds type (InitialFailure, ContinuedFailure, Recovered, NeverExecuted, or OutcomeFailure), triggeredAt, downSince, and delivery state in status and sentAt. The tools return no failure message text, logs, or payloads, so the agent can report what CronRadar recorded; the cause is in the scheduler's logs. History is bounded by plan: 30 days of runs on Free, 90 on Grow.
"Pause Monitoring for Tonight's Maintenance"#
Pause monitoring for nightly-export until 06:00 UTC tomorrow, reason
"database migration". Show me the job before and after.get_job {"jobId": "7d0f5c62-9b1e-4f3a-8c2d-5e6a1b9f0c47"}
pause_job {"jobId": "7d0f5c62-9b1e-4f3a-8c2d-5e6a1b9f0c47", "until": "2026-10-25T06:00:00Z", "reason": "database migration"}
get_job {"jobId": "7d0f5c62-9b1e-4f3a-8c2d-5e6a1b9f0c47"}This needs write access and the Admin role. Timed maintenance resumes automatically when until passes; without until, the job stays paused until resume_job. until must be in the future, and reason is at most 300 characters. Pausing clears any active alert; resuming computes the next expected run from the schedule and can fail with 402 if the plan has no room. A framework integration's reconciliation doesn't undo a manual pause. The pause only stops CronRadar from expecting runs: the job still runs unless you stop it in the scheduler. There is no bulk pause, so each job is one call against the rate limit.
"Create a Monitor for a New Job"#
Our new export job runs daily at 02:00 UTC. If the production application has
no job with monitor key nightly-export, create one with a 15-minute grace
period, then tell me what the job's code still needs.After get_context and list_jobs show the key is free, the agent calls create_job, which needs write access and the Member role:
{
"applicationId": "app_3f9c2a7b1d4e",
"request": {
"name": "Nightly export",
"monitorKey": "nightly-export",
"schedule": "0 2 * * *",
"gracePeriod": 900
}
}The job shows pending, and if no check-in arrives by its first scheduled occurrence plus grace, CronRadar raises a NeverExecuted alert. Creating the monitor doesn't make the job report. Its code still needs check-ins with the monitor key nightly-export and an ingestion key from the deployment's secret store, like the wrapper in the dead man's switch guide. A key from create_ingestion_key is shown once and belongs in that secret store, never in the chat.
Whether to call create_job depends on how the job reports:
- HTTP check-ins that send
schedule. The first check-in creates the monitor, socreate_jobis optional. It adds coverage before that run: a self-registered monitor doesn't exist until the job reports, so a job that never runs goes unnoticed, while a pre-created one raisesNeverExecuted. - Hangfire or Quartz.NET jobs with a CronRadar integration. Don't. Discovery registers them, and a hand-made monitor covering a discovery failure hides it.
An existing key returns 409; a monitor beyond the Free plan's 10 or a Grow account's budget returns 402. If the agent also writes the reporting code, review it with the AI-generated cron job checklist and confirm a real run as the verification guide describes.
Errors, Limits, and Pagination#
Over MCP, authentication and rate-limit failures arrive as HTTP statuses on the connection, which your client shows as an unauthorized or failed server. Other failures come back from the tool call with a message whose meaning matches the REST status the quickstart lists:
| Status | Meaning | What to do |
|---|---|---|
| 400 | Invalid input: a bad cron schedule, a changed monitor key, or a mismatched confirmName |
Fix the arguments; don't retry unchanged |
| 401 | Token missing, expired, revoked, or invalid | Create new access and replace the value; don't retry in a loop |
| 402 | A new active monitor would exceed the Free plan's 10 or a Grow account's monthly budget | Pause a job, add a card, or raise the budget yourself; the agent shouldn't |
| 403 | The grant or your current role doesn't allow the operation | Stop; grant more access only if you decide the task needs it |
| 404 | The resource isn't in this workspace | Recheck the ID; one token covers one workspace |
| 409 | The monitor key or another identifier exists, or the active key limit is reached | Read the existing resource and update it instead |
| 429 | Over the rate limit | Slow down and respect Retry-After |
- Rate limit: 60 requests per minute per source IP across all tokens, also counted against the API's overall limit. Streamable HTTP sends each MCP message as its own POST, so the client's setup and tool listing count too. Sweep health with
list_jobsand fetch check-ins only where needed. - Pagination: list tools return
{items, nextOffset}; passnextOffsetback asoffsetuntil it is null. Pages default to 50 items, at most 100, and the offset caps at 10,000. These are bounded operational views, not an export.
Treat Tool Output as Data and Grant the Least Access#
- Tool output is data. Application and job names and descriptions come from your code, your scheduler's registry, and your teammates. CronRadar's instructions tell assistants to treat every such field, failure messages included, as data, never instructions, and
get_contextrepeats that in itscontentSafetyfield. A tool result asking the agent to act is a prompt injection attempt, not a request from you. - Least privilege per session. Read-only for investigation, write for a pause or a new monitor, deletion only for a deliberate cleanup. Approve write and delete calls one at a time instead of allowlisting them; the MCP specification recommends a human who can deny any tool invocation.
- Changes can be undone. A framework integration's reconciliation can overwrite an edited schedule with the scheduler's own, and a later check-in carrying
schedulerecreates a deleted monitor, so decide whether a change belongs in the scheduler or in CronRadar. - Revoke when done. Revoke the grant at app.cronradar.com/assistant and the next tool call fails. Revoking doesn't undo changes or revoke ingestion keys the agent created; review those in the application's Setup tab. Then clear the variable, Keychain item, or connector, and confirm
git grep -n cr_agent_finds nothing in your repositories.
Frequently Asked Questions#
Does CronRadar have an MCP server?#
Yes. CronRadar hosts a remote MCP server at https://api.cronradar.com/api/agent/mcp that uses Streamable HTTP and a temporary bearer token from app.cronradar.com/assistant. Its 16 tools cover applications, jobs, check-ins, alerts, pausing, and ingestion keys, and there is nothing to install.
How do I add the CronRadar MCP server to Claude Code?#
Export the token as CRONRADAR_AGENT_TOKEN and run claude mcp add --transport http cronradar https://api.cronradar.com/api/agent/mcp --header 'Authorization: Bearer ${CRONRADAR_AGENT_TOKEN}', or commit a .mcp.json that references the variable. Check it with /mcp. Because tokens expire within 24 hours, restart with a new value or use a headersHelper that reads the current token from a secret store.
Which MCP tools does CronRadar provide?#
Sixteen: get_context; list, get, create, update, and delete for applications; create_ingestion_key; list, get, create, update, and delete for jobs; pause_job, resume_job, list_check_ins, and list_alerts. Read tools work with any grant; the rest need write or delete permission and a Member or Admin role.
Can an AI agent delete my monitors through MCP?#
Only if you allow it. The delete tools need a grant with deletion explicitly allowed, the Admin role, and a confirmName that exactly matches the resource's current name, and their descriptions tell the model to ask you first. Without deletion in the grant, they return a permission error.
Do I need MCP to use CronRadar with an AI agent?#
No. The private setup prompt gives an agent with an HTTP or code-execution tool the same access over the management REST API, and the public setup prompt needs no workspace access. MCP helps when you'd rather keep the token in your client's configuration than in the conversation.
Put this guide to work
Know when the next run is missing
Connect one scheduled job, confirm a real completion, then verify the alert destination. Keep your existing scheduler in charge of running the work.