Skip to content

feat: Structured tool errors, breakdown paging, and inline query rules - #66

Merged
sergical merged 2 commits into
mainfrom
feat/agent-friendly-errors-and-paging
Oct 26, 2026
Merged

sergical merged 2 commits into
mainfrom
feat/agent-friendly-errors-and-paging

Conversation

@sergical

Copy link
Copy Markdown
Member

Stacked on #65. Merge #65 first; GitHub then retargets this PR to main.

Makes the tools easier to drive for every kind of MCP client, including clients that never read SERVER_INSTRUCTIONS and harnesses that call tools from code.

Changes

Structured errors. A failed call now returns two text blocks: the readable message, then {"error":{"code","message","retryable","retry_after_seconds"?}}. The codes are invalid_input, unauthorized, rate_limited, upstream_unavailable, invalid_query and internal. Retry-After is read as seconds or as an HTTP-date, for both 429 and 5xx. Error results have no structuredContent, because MCP SDK v1 clients (1.27–1.30) check it against the success outputSchema and throw -32602.

get_breakdown paging. There is a new offset input, and results carry pagination {limit, offset, has_more} in both the text and the structured output. has_more comes from fetching one extra row; older self-hosted versions do not have include.total_rows. Rows are sorted by the first metric, then by the dimension, so tied rows do not skip or repeat between pages. event:goal gets the metric sort only, because Plausible rejects it in order_by.

Rules in parameter descriptions. The metric, dimension, filter and date rules are now in the zod .describe() texts. The metrics text uses #65's corrected rules.

Future dates. The instructions said a range that ends after today returns a 400. In a live test, Plausible accepted it and counted those days as zero, so both texts now say that.

Output-shape test. Results with 0, 1 and 5 rows all parse against outputSchema and have the same top-level keys.

Testing

  • pnpm build, pnpm typecheck and pnpm test pass (269 tests).
  • End-to-end run against live Plausible (mcp.sentry.dev), with a real MCP client over stdio against dist/index.js:
  • Unit tests cover the 429 and 5xx retry paths only; I did not cause real rate limits.

Created with Claude Code

Make the tools easier for every kind of MCP client to drive without
reading SERVER_INSTRUCTIONS first.

- Errors: each failed call returns a second text block with
  {"error":{code,message,retryable,retry_after_seconds?}}. Codes are
  invalid_input, unauthorized, rate_limited, upstream_unavailable,
  invalid_query and internal. Retry-After is parsed as seconds or an
  HTTP-date. No structuredContent on errors: MCP SDK v1 clients validate it
  against the success outputSchema and throw.
- get_breakdown paging: new offset input; the result carries
  pagination {limit, offset, has_more} in both the text and structured
  output. has_more comes from fetching one extra row. Rows are ordered by
  the first metric with the dimension as a tiebreaker so pages do not
  skip or repeat tied rows (event:goal excluded: Plausible rejects it in
  order_by).
- Parameter descriptions now carry the metric/dimension/filter rules, so
  clients that ignore server instructions still see them.
- Future dates: Plausible accepts ranges that end after today and counts
  those days as zero; the instructions said they return a 400.
- Output-shape test: 0, 1 and 5 rows all parse against outputSchema with
  the same top-level keys.

Co-authored-by: Claude <claude@anthropic.com>
@linear-code

linear-code Bot commented Oct 26, 2026

Copy link
Copy Markdown

DEVEX-772

- A rejected fetch (DNS, TLS, dropped connection) now returns
  upstream_unavailable with retryable true instead of a non-retryable
  internal error.
- Plausible rejects event:goal in order_by, so tied goals could swap
  between requests and offset pages skipped or repeated them. Goal
  breakdowns now fetch every goal and page over a stable order.
- The server instructions say that the SDK rejects arguments that fail
  the input schema before the tool runs, so those errors have no JSON
  block.

Co-authored-by: Claude <claude@anthropic.com>
@sergical
sergical changed the base branch from fix/entry-page-event-metrics to main October 26, 2026 18:56
@sergical sergical closed this Oct 26, 2026
@sergical sergical reopened this Oct 26, 2026
@sergical
sergical merged commit 3ebf616 into main Oct 26, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant