{
  "serverInfo": {
    "name": "cronspell",
    "title": "CronSpell",
    "version": "0.1.0"
  },
  "description": "Validate cron expressions, explain them in plain English and list next/previous run times in any IANA time zone (DST-aware). No API key: about 60 checks per minute per IP.",
  "homepage": "https://cronspell.mike-tusa.workers.dev/",
  "contact": {
    "email": "digitalpromohub.support@gmail.com"
  },
  "transport": {
    "type": "streamable-http",
    "url": "https://cronspell.mike-tusa.workers.dev/mcp"
  },
  "protocolVersions": [
    "2026-07-28",
    "2025-11-25",
    "2025-06-18",
    "2025-03-26",
    "2024-11-05"
  ],
  "capabilities": {
    "tools": {
      "listChanged": false
    }
  },
  "authentication": {
    "required": false
  },
  "instructions": "CronSpell validates cron expressions, explains them in plain English and lists their run times in any IANA time zone with DST handled. Supports 5-field crontab syntax (minute hour day-of-month month day-of-week), a 6-field variant with a leading seconds field, month/weekday names and the shorthands @yearly @annually @monthly @weekly @daily @midnight @hourly. Not supported: Quartz L/W/#, 7-field expressions with a year, @reboot. Use validate_cron or explain_cron first if unsure; next_cron_runs lists up to 20 upcoming (or previous) runs; cron_runs_between lists up to 100 runs in a window. Times are wall-clock in the given time zone (default UTC): a local time skipped by DST does not run and a repeated local time is listed twice (flagged dst \"repeated\"); check notes. Pure computation, nothing stored, no network calls. No API key: about 60 checks per minute per IP.",
  "tools": [
    {
      "name": "validate_cron",
      "title": "Validate a cron expression",
      "description": "Check whether a cron expression is valid. Returns valid true with the expanded values of every field (minute, hour, day of month, month, day of week, and seconds for 6-field expressions), or valid false with an error code, the field at fault and a message. Detects schedules that can never run (e.g. February 30). Read-only. Same engine as GET /api/validate.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Cron expression, e.g. \"0 9 * * 1-5\", \"*/15 * * * *\", \"0 30 8 * * *\" (6 fields = leading seconds) or \"@daily\"."
          }
        },
        "required": [
          "expression"
        ],
        "additionalProperties": false
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean"
          },
          "expression": {
            "type": "string"
          },
          "normalized": {
            "type": "string"
          },
          "format": {
            "type": "string",
            "enum": [
              "standard",
              "seconds",
              "shorthand"
            ]
          },
          "dayMatching": {
            "type": "string",
            "enum": [
              "or",
              "and"
            ]
          },
          "fields": {
            "type": "object"
          },
          "description": {
            "type": "string"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              },
              "field": {
                "type": "string"
              }
            },
            "required": [
              "code",
              "message"
            ]
          },
          "version": {
            "type": "string"
          }
        },
        "required": [
          "valid",
          "expression",
          "version"
        ]
      },
      "annotations": {
        "title": "Validate a cron expression",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "explain_cron",
      "title": "Explain a cron expression",
      "description": "Explain a cron expression in plain English, e.g. \"0 9 * * 1-5\" → \"At 09:00, on Monday through Friday.\" Also returns notes on cron rules that often surprise people (day-of-month OR day-of-week, seconds field, shorthand expansion). Invalid expressions return isError with the reason. Read-only. Same engine as GET /api/explain.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Cron expression, e.g. \"0 9 * * 1-5\", \"*/15 * * * *\", \"0 30 8 * * *\" (6 fields = leading seconds) or \"@daily\"."
          }
        },
        "required": [
          "expression"
        ],
        "additionalProperties": false
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string"
          },
          "normalized": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "format": {
            "type": "string"
          },
          "dayMatching": {
            "type": "string"
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "version": {
            "type": "string"
          }
        },
        "required": [
          "expression",
          "description",
          "version"
        ]
      },
      "annotations": {
        "title": "Explain a cron expression",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "next_cron_runs",
      "title": "List next (or previous) cron run times",
      "description": "List the next (or, with direction \"previous\", the most recent past) run times of a cron expression in an IANA time zone, DST-aware. Each run has the local time with its UTC offset, the UTC time, unix seconds and weekday. Default 5 runs, max 20; searches up to 50 years. Read-only. Same engine as GET /api/next and GET /api/previous.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Cron expression, e.g. \"0 9 * * 1-5\", \"*/15 * * * *\", \"0 30 8 * * *\" (6 fields = leading seconds) or \"@daily\"."
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_+\\-/]+$",
            "default": "UTC",
            "description": "IANA time zone the schedule runs in, e.g. \"America/Denver\", \"Europe/London\". Default UTC."
          },
          "count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 20,
            "default": 5,
            "description": "How many runs (1-20)."
          },
          "from": {
            "type": "string",
            "maxLength": 40,
            "description": "Start point (exclusive); default now. ISO 8601 with Z or an offset (2026-11-01T09:00:00Z), a local time read in the time zone (2026-11-01T09:00), or unix seconds."
          },
          "direction": {
            "type": "string",
            "enum": [
              "next",
              "previous"
            ],
            "default": "next",
            "description": "\"next\" (default) or \"previous\" (most recent first)."
          }
        },
        "required": [
          "expression"
        ],
        "additionalProperties": false
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string"
          },
          "normalized": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "runs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "time": {
                  "type": "string",
                  "description": "Local ISO 8601 time with UTC offset"
                },
                "utc": {
                  "type": "string"
                },
                "unix": {
                  "type": "number"
                },
                "weekday": {
                  "type": "string"
                },
                "offset": {
                  "type": "string"
                },
                "dst": {
                  "type": "string",
                  "enum": [
                    "repeated"
                  ]
                }
              },
              "required": [
                "time",
                "utc",
                "unix",
                "weekday",
                "offset"
              ]
            }
          },
          "complete": {
            "type": "boolean"
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "version": {
            "type": "string"
          },
          "from": {
            "type": "string"
          },
          "direction": {
            "type": "string"
          }
        },
        "required": [
          "expression",
          "timezone",
          "runs",
          "complete",
          "notes",
          "version"
        ]
      },
      "annotations": {
        "title": "List cron run times",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "cron_runs_between",
      "title": "List cron runs between two times",
      "description": "List every run of a cron expression between start and end (both inclusive) in an IANA time zone, DST-aware, oldest first. Default limit 20, max 100; truncated is true when there are more. Read-only. Same engine as GET /api/between.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200,
            "description": "Cron expression, e.g. \"0 9 * * 1-5\", \"*/15 * * * *\", \"0 30 8 * * *\" (6 fields = leading seconds) or \"@daily\"."
          },
          "timezone": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "pattern": "^[A-Za-z0-9_+\\-/]+$",
            "default": "UTC",
            "description": "IANA time zone the schedule runs in, e.g. \"America/Denver\", \"Europe/London\". Default UTC."
          },
          "start": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40,
            "description": "Window start (inclusive). ISO 8601 with Z or an offset (2026-11-01T09:00:00Z), a local time read in the time zone (2026-11-01T09:00), or unix seconds."
          },
          "end": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40,
            "description": "Window end (inclusive). ISO 8601 with Z or an offset (2026-11-01T09:00:00Z), a local time read in the time zone (2026-11-01T09:00), or unix seconds."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 20,
            "description": "Max runs to return (1-100)."
          }
        },
        "required": [
          "expression",
          "start",
          "end"
        ],
        "additionalProperties": false
      },
      "outputSchema": {
        "type": "object",
        "properties": {
          "expression": {
            "type": "string"
          },
          "normalized": {
            "type": "string"
          },
          "timezone": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "runs": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "time": {
                  "type": "string",
                  "description": "Local ISO 8601 time with UTC offset"
                },
                "utc": {
                  "type": "string"
                },
                "unix": {
                  "type": "number"
                },
                "weekday": {
                  "type": "string"
                },
                "offset": {
                  "type": "string"
                },
                "dst": {
                  "type": "string",
                  "enum": [
                    "repeated"
                  ]
                }
              },
              "required": [
                "time",
                "utc",
                "unix",
                "weekday",
                "offset"
              ]
            }
          },
          "complete": {
            "type": "boolean"
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "version": {
            "type": "string"
          },
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          },
          "truncated": {
            "type": "boolean"
          }
        },
        "required": [
          "expression",
          "timezone",
          "runs",
          "truncated",
          "notes",
          "version"
        ]
      },
      "annotations": {
        "title": "List cron runs in a window",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    }
  ]
}