cloudflare / cloudflare/mcp-server-cloudflare
`query_worker_observability` fails to parse cron-triggered Worker events (`$workers.outcome` is absent)
- Dominant language
- TypeScript
- Stars
- 4.2k
- Forks
- 514
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 2
Description
### Summary
The observability MCP server (`https://observability.mcp.cloudflare.com/mcp`) cannot return any cron-triggered Worker event. Its response schema requires `$workers.outcome` to be a string, but the underlying Cloudflare telemetry API omits that field entirely for `eventType: "cron"` events. The tool therefore rejects valid data returned by the very API it wraps, and cron/scheduled Worker invocations become unqueryable through MCP.
### Reproduce
Any Worker with a `[triggers] crons` schedule and observability enabled. Call `query_worker_observability`:
```json
{
"view": "events",
"queryId": "repro",
"limit": 20,
"dry": false,
"parameters": {
"datasets": ["cloudflare-workers"],
"filters": [
{ "key": "$metadata.service", "operation": "eq", "type": "string", "value": "" },
{ "key": "$metadata.origin", "operation": "eq", "type": "string", "value": "cron" }
]
},
"timeframe": { "reference": "", "offset": "-24h" }
}
```
### Actual
```
Error analyzing worker logs: [
{
"code": "invalid_union",
"errors": [[{ "expected": "string", "code": "invalid_type",
"path": ["outcome"],
"message": "Invalid input: expected string, received undefined" }]],
"path": ["result", "events", "events", 0, "$workers"],
"message": "Invalid input"
},
...
]
```
Note the error names `events[0]`, `events[2]`, etc. — the events **were** retrieved; only deserialization fails.
### Expected
The cron events are returned, as they are via the REST API.
### Root cause
`POST /accounts/{account_id}/workers/observability/telemetry/query` returns different `$workers` shapes by event type. Same Worker, same account, same timeframe:
| eventType | `$workers` keys | `outcome` |
| --- | --- | --- |
| `cron` | `truncated, event, scriptName, eventType, scriptVersion, executionModel, requestId` | **absent** |
| `fetch` | `event, truncated, scriptName, outcome, eventType, executionModel, scriptVersion, requestId, cpuTimeMs, wallTimeMs` | `"ok"` |
Cron events also omit `cpuTimeMs` and `wallTimeMs`.
The MCP layer's zod schema for `$workers` requires `outcome`, so every cron event fails validation. The REST endpoint returns these same events without complaint, which is the workaround (see below).
### Suggested fix
Make `outcome` optional in the `$workers` schema (and likely `cpuTimeMs` / `wallTimeMs` too), or discriminate the schema on `eventType`.
### Impact
Cron-triggered Workers cannot be observed through MCP at all. This matters for scheduled jobs, where the log line emitted by the `scheduled()` handler is often the only record that the job ran and what it decided — there is no request/response to inspect, and `wrangler tail` only shows live traffic, so a missed window is unrecoverable.
### Secondary note
`$metadata.origin` for cron-triggered events is `"cron"`, not `"scheduled"`. Filtering on `"scheduled"` returns an empty array rather than an error, which is indistinguishable from "the cron never fired" — a misleading result when debugging a scheduled job. Worth documenting in the tool description alongside the existing `$metadata.origin` guidance.
### Workaround
Query the REST API directly; it returns the events correctly:
```
POST /accounts/{account_id}/workers/observability/telemetry/query
{
"queryId": "…", "view": "events", "limit": 25, "dry": false,
"parameters": {
"datasets": ["cloudflare-workers"],
"filters": [
{ "id": "1", "key": "$metadata.service", "operation": "eq", "type": "string", "value": "" },
{ "id": "2", "key": "$metadata.origin", "operation": "eq", "type": "string", "value": "cron" }
],
"calculations": [], "groupBys": [], "havings": []
},
"timeframe": { "from": , "to": }
}
```
(The REST endpoint wants numeric epoch-ms `from`/`to` and `id` on each filter; the MCP tool accepts ISO strings and omits `id`.)
Contributor guide
Research direction
Start at the query_worker_observability entry point and locate the Zod schema that validates the $workers event object. Reproduce with the supplied cron-events query, then verify that events without outcome, cpuTimeMs, or wallTimeMs are accepted and returned; also check the tool description's guidance for the cron origin value.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, observability
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100