Context
Future feature on roadmap: change detection / temporal tracking. External reviewer flagged a common anti-pattern in stateful MCP tools that we should design around before implementation:
The diff_jobs tool will silently return empty diffs on first run (no baseline = no changes). Seen this bite people multiple times with stateful MCP tools.
The anti-pattern
When a stateful tool (diff, watch, poll) runs with no prior state, the instinct is to return "0 changes." Looks successful. LLM treats it as "nothing to report." User sees empty result, tries again, same thing. Agent loops on empty diffs before giving up or giving misleading answers.
The root issue: an LLM can't distinguish "truly zero changes" from "no baseline to compare against." Both look like success with empty data.
Design requirements for the eventual `diff_jobs` (or equivalent)
1. First-run behavior must NOT return empty-success
Two acceptable options:
Option A: Auto-record baseline on first `fetch_jobs` call
- Every `fetch_jobs` call writes a timestamped snapshot to disk/cache
- `diff_jobs` compares current fetch against most recent prior snapshot
- First run of `diff_jobs` finds a baseline automatically (assuming user has run `fetch_jobs` at least once)
- Pro: simplest UX, always works
- Con: storage grows; needs cleanup strategy
Option B: Explicit "no baseline" error
- `diff_jobs` returns `{status: "error", error: {code: "no_baseline", message: "No baseline stored. Run fetch_jobs first to establish one."}}`
- Clearly signals initialization dependency
- Pro: no implicit state, explicit contract
- Con: user-hostile on first run
Option C (likely best): Hybrid
- `fetch_jobs` snapshots opt-in via a `record_baseline: true` arg (default false to avoid unwanted writes)
- `diff_jobs` returns explicit error if no baseline, with guidance on how to record one
- Clear agency: user or AI decides when to establish state
2. Tool description MUST teach the dependency
LLMs don't infer initialization order from tool names. The `diff_jobs` description must explicitly say:
USE WHEN: comparing current job postings against a prior snapshot.
PREREQUISITE: a baseline must exist. Baselines are recorded automatically on `fetch_jobs` calls with `record_baseline: true`, or via `record_baseline` tool directly.
IF NO BASELINE: this tool returns a clear error with instructions. Do NOT loop — follow the error guidance.
3. Document explicitly which tool establishes state
If we add a `record_baseline` or `save_snapshot` tool, its relationship to `diff_jobs` must be in BOTH tool descriptions. Cross-reference like:
- `record_baseline.description`: "Call before `diff_jobs` to establish a comparison baseline..."
- `diff_jobs.description`: "Requires a prior `record_baseline` call or a `fetch_jobs` with snapshot enabled."
Generalized principle for future stateful tools
Stateful tools in MCP must either:
- Auto-initialize on first use (hide the state from the AI), or
- Return explicit init-required errors with guidance (make the state legible to the AI)
Empty-success on missing state is always wrong. LLM can't distinguish it from legitimate empty result.
Acceptance criteria when this ships
Credit
External reviewer feedback. Captured as a design constraint so we don't rediscover it the hard way.
Context
Future feature on roadmap: change detection / temporal tracking. External reviewer flagged a common anti-pattern in stateful MCP tools that we should design around before implementation:
The anti-pattern
When a stateful tool (diff, watch, poll) runs with no prior state, the instinct is to return "0 changes." Looks successful. LLM treats it as "nothing to report." User sees empty result, tries again, same thing. Agent loops on empty diffs before giving up or giving misleading answers.
The root issue: an LLM can't distinguish "truly zero changes" from "no baseline to compare against." Both look like success with empty data.
Design requirements for the eventual `diff_jobs` (or equivalent)
1. First-run behavior must NOT return empty-success
Two acceptable options:
Option A: Auto-record baseline on first `fetch_jobs` call
Option B: Explicit "no baseline" error
Option C (likely best): Hybrid
2. Tool description MUST teach the dependency
LLMs don't infer initialization order from tool names. The `diff_jobs` description must explicitly say:
3. Document explicitly which tool establishes state
If we add a `record_baseline` or `save_snapshot` tool, its relationship to `diff_jobs` must be in BOTH tool descriptions. Cross-reference like:
Generalized principle for future stateful tools
Stateful tools in MCP must either:
Empty-success on missing state is always wrong. LLM can't distinguish it from legitimate empty result.
Acceptance criteria when this ships
Credit
External reviewer feedback. Captured as a design constraint so we don't rediscover it the hard way.