Professor' Harvester
Enables control of OpenAI Codex chat sessions, including listing, messaging, and coordinating Codex instances across accounts.
A fleet controller and a discipline layer for Claude Code, Codex, and OpenCode — chats that talk to each other, agents that follow the rules, and a harvester that reads what the web won't show a bot.
Why Professor?
You already run more than one AI chat. They cannot see each other, they forget the rules the moment context compacts, and a growing share of the web answers them with a 403. Professor is the layer that fixes all three — without touching what your harness is, only what it does.
Without Professor | With Professor | |
Many chats, many accounts | Scattered terminal tabs; a closed tab is a lost chat | One picker — every chat on every harness, live or resumable |
One chat needs another | You copy-paste between windows |
|
The rules, after compaction | Whatever the prompt still remembers | Hooks that refuse, with the unlock steps in the refusal |
A bot-blocked page | A 403, or an empty page reported as content | A seven-rung fetch ladder; a block is reported as a block |
Claude and Codex rules | Two files that drift apart | One |
An account hits its limit | Start over in a new chat |
|
Related MCP server: CrossRef MCP Server
Six things you can watch it do
Every transcript below is real output from this repository, redacted only of names.
1. See the fleet
pfm 🥇 account 1 · ⚡1h · 12 rows · 38 killed · 64 empty
tabs Chats Stats Limits tab/shift+tab
Chats · fuzzy search and all existing chat controls
find › type project or name 12/12 visible
╭─ fleet 12 ───────────────────────────────────────────────────────────────────────────────────────╮
│╭─ api │
│› ✦ [ Claude ] Codex OpenCode 🥇 0p 0B 0s│
││ ● PAYMENTS_REFACTOR ⬢ 🥇 ⇄ 118p 14M 2m│
││ ⚙ SCHEMA_MIGRATION ⚙ agent 🥈 20p 6.1M 7h│
│╭─ webapp │
││ ● ORCHESTRATOR 🥇 ⇄ ←here 37p 2.7M 1m│
││ ⚙ DESIGN_PASS ⚙ agent 🥇 18p 1.9M 58m│
││ ↻ DEPLOY_PROD 🥇 59p 8.6M 2d│
│╭─ ops │
││ ● FLEET_BUILDER ⬢ ⇄ 77p 94M 0s│
││ ↻ CCC 🥈 452p 32M 54m│
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
↑↓ move enter open esc cancel type to fuzzy-find
⌃X hide ⌃E 1h ⌃S account ⌃O reboot
pfm ls: every AI chat on the machine — Claude Code, Codex (⬢), and OpenCode — across accounts (🥇🥈), grouped by repo, live (●), resumable (↻), or agent-run (⚙).⇄marks chats that talk to other chats;←hereis the one you are sitting in;✦opens a new one on any harness. Pick one, attach, or fire it a goal without ever attaching. A chat that scrolled off a closed terminal tab is not gone — it is a resumable transcript, and now somebody can find it.
tab once more and the same fleet is drawn as a sky:

Every project is a star its chats orbit; a spawned chat rises as a moon at its parent's angle, so lineage is visible in the sky itself. When chats talk to each other the sky draws an edge between them, read from a durable comms ledger — an edge is a fact, not a guess. The chronoscope replays the last 24h, and a chat that is dead now still renders as the ghost it was back then.
2. Chats that talk to each other
Two panes, two harnesses. You type one line into the Claude chat on the left; the Codex chat on the right receives it as a signed turn and gets to work.
┌────────────────────────────────────────────────────────────┐ ┌────────────────────────────────────────────────────────────┐
│ ▐▛███▛█ Claude Code v2.1.270 │ │ ╭──────────────────────────────────────────╮ │
│ ▝▜██████▀ Fable 5.1 with low effort · Claude Max │ │ │ >_ OpenAI Codex (v0.154.0) │ │
│ ▝▝ ▝▝ ~/.professor │ │ │ model: gpt-6-astra xhigh │ │
│ │ │ │ directory: ~/.professor │ │
│ ❯ Tell the Codex chat named DEMO_CODEX to run the │ │ │ permissions: YOLO mode │ │
│ migration test suite and report back when green. │ │ ╰──────────────────────────────────────────╯ │
│ │ │ › Run the migration test suite and report back when │
│ Called chat_inject │ -----> │ it is green. — sid 0a98b7fe · to reply: │
│ ⏺ Sent — DEMO_CODEX has the instruction (delivered │ │ chat_inject DEMO_CLAUDE <message> │
│ live, it's already working) and will report back │ │ │
│ here when the migration suite is green. │ │ • Explored │
│ Verdict: message delivered to DEMO_CODEX — awaiting │ │ └ Read dev.sh · Search migration (suite|test) │
│ its green report in this chat. ☕ │ │ • The runner has no separate migration target, so │
│ ✻ Baked for 9s · done 1:11 AM │ │ I'll run the full PFM suite, which includes the │
│ ─────────────────────────────────────── DEMO_CLAUDE ─ │ │ migration tests, inside the container fence. │
│ ❯ │ │ • Waiting for background terminal (2m 17s) │
│ ──────────────────────────────────────────────────── │ │ └ .claude/scripts/dev.sh iso test pfm │
│ 🥈 ✦ Fable 5.1 │ 🔖 DEMO_CLAUDE │ 🔹 low │ 🌿 develop │ │ › Ask Codex to do anything │
│ ▰▱▱▱▱ 5h-used:36% │ ▰▱▱▱▱ 7d-used:35% ↻5d3h │ │ gpt-6-astra xhigh · Working · Context 82% left │
└────────────────────────────────────────────────────────────┘ └────────────────────────────────────────────────────────────┘The footer on the Codex side is the signature: who spoke (
sid 0a98b7fe) and the exact command to answer them. A message no sender could be derived for is refused, never delivered anonymously.
pfm chat inject is what the Claude chat called — it types a real, signed turn into another chat's pane — under a per-target lock, safe against a busy target (--force-now) and shell-hostile payloads (--file). pfm chat ask waits for the answer, with named exit codes: 0 done · 2 usage · 3 chat dead · 4 no such chat · 5 answer timed out · 6 message not delivered. The same verbs are an MCP server, so an agent can spawn, message, read, and retire other chats across all three harnesses — and issue_servicedesk lets it file a bug against its own host tool.
3. Rules that bite
A subagent tries to Edit a file under .claude/. The PreToolUse guard answers:
DENIED — infra edits route through /pfm: open this session's gate from the repo root …
Do NOT route around this by disabling the hook or editing infra outside /pfm.The refusal carries its own unlock steps. That is one of 26 mandatory rules every install ships with: only gitter writes git; fix loops cap at three attempts, then BLOCKED-DEFERRED; read-only mappers (tracer) are separated from judges (reviewer); and every check names what its own broken state reports — a gate that says "fine" when healthy and when broken is a coincidence detector.
4. Read what the web hides from bots
About one in ten of the world's top 10,000 websites now tells AI crawlers to stay out — among news publishers, more than half (HasData AI Crawler Block Index, July 2026). Harvester fetches the way a reader's browser does and climbs a seven-rung ladder — direct → Chrome-fingerprint TLS → reader proxy → extractor → headless-then-headed browser → Wayback → OCR — until it holds real content:
$ pfm harvest https://www.nytimes.com/ # robots.txt: every AI crawler Disallow: /
→ 2,930 chars, the live front page
$ pfm harvest https://www.reuters.com/
→ ERROR: The source is protected by an access challenge. Choose another copy.The second line is the design: an app shell is never stored as the page, and a block is reported as a block — never as an empty success. DOIs, ISBNs, PMIDs and PMCIDs route through twelve open-access resolvers in parallel; pfm harvest ask -p "…" <sources> feeds the full cached artifacts to a Claude or Codex ask engine, failed sources kept visible as receipts. The whole surface is also an MCP server. The browser rung never solves anything interactive.
5. One contract, three runtimes
$ head -1 AGENTS.md
<!-- Generated by pfm codex build from CLAUDE.md; do not edit — edit the source, then re-run: pfm codex build -->
$ pfm codex check .
CODEX CHECK PASSCLAUDE.md compiles to AGENTS.md; .claude/** compiles to .codex/** and .opencode/**; global agents get .toml twins. The Stop hook recompiles the mirrors and a drifted mirror fails the check, so the three harnesses can never disagree about the law. Even the verification engine is one TypeScript source compiled for both the Claude Workflow runtime and the Codex SDK.
6. Reload without losing the conversation
Account 🥇 is at 94% of its weekly limit. You type /reload --account 2; the same pane comes back on 🥈 with the whole conversation, and remembers the codeword it was given on the other account.
┌────────────────────────────────────────────────────────────┐ ┌────────────────────────────────────────────────────────────┐
│ ▐▛███▛█ Claude Code v2.1.270 │ │ ▐▛███▛█ Claude Code v2.1.270 │
│ ▝▜██████▀ Opus 5 (1M context) · Claude Max │ │ ▝▜██████▀ Opus 5 · Claude Max │
│ ▝▝ ▝▝ ~/.professor │ │ ▝▝ ▝▝ ~/.professor │
│ │ │ │
│ ❯ Remember this codeword for later: BLUE-HERON. │ │ ❯ Remember this codeword for later: BLUE-HERON. │
│ ⏺ BLUE-HERON — held, my friend. ☕ │ │ ⏺ BLUE-HERON — held, my friend. ☕ │
│ Verdict: codeword stored — note the 7-day cap │ │ Verdict: codeword stored — note the 7-day cap │
│ is at 95%, so /reload may be needed. │ reload │ is at 95%, so /reload may be needed. │
│ ✻ Baked for 4s · done 1:34 AM │ -----> │ ✻ Baked for 4s · done 1:34 AM │
│ │ │ ⏺ UserPromptSubmit operation blocked by hook: │
│ │ │ Original prompt: /reload --account 2 │
│ │ │ ❯ What was the codeword I gave you? One line. │
│ │ │ ⏺ The codeword is BLUE-HERON. │
│ │ │ ✻ Brewed for 12s · done 1:35 AM │
│ ─────────────────────────────────────── RELOAD_DEMO ─ │ │ ─────────────────────────────────────── RELOAD_DEMO ─ │
│ ❯ /reload --account 2 │ │ ❯ │
│ ──────────────────────────────────────────────────── │ │ ──────────────────────────────────────────────────── │
│ 🥇 ◆ Opus 5 (1M context) │ 🔖 RELOAD_DEMO │ │ 🥈 ◆ Opus 5 │ 🔖 RELOAD_DEMO │
│ ▱▱▱▱▱ 5h-used:2% │ ▰▰▰▰▱ 7d-used:94% ↻4d6h │ │ ▰▱▱▱▱ 5h-used:39% │ ▰▱▱▱▱ 7d-used:36% ↻5d3h │
└────────────────────────────────────────────────────────────┘ └────────────────────────────────────────────────────────────┘"Blocked by hook" is the design: the
/reloadhook takes the prompt before the model sees it, so the reboot never spends a turn.
The running chat reboots in place — same pane, same history, new account, model, or effort. --then "prompt" hands the baton unattended; /reload typed by a human runs through a hook without spending a model turn; /handoff --branch carries the full context into a detached successor.
Install
Two independent halves; adopt either without the other.
pfm, the fleet CLI — one Go binary, touches only your$HOME. Binary or source: INSTALL.md.The discipline layer — cloned, then scaffolded into your repo through an interview:
REPO=rezzminator/professor
TAG=$(git ls-remote --tags --sort=-v:refname "https://github.com/${REPO}.git" 'v*' \
| grep -v '\^{}' | head -1 | sed 's#.*/##')
git clone "https://github.com/${REPO}.git" "$HOME/.professor"
git -C "$HOME/.professor" checkout "$TAG"
cat "$HOME/.professor/docs/SETUP.md" # the install interview — start hereThe checkout is pinned to the latest semantic version tag. A maintainer checkout also runs git config core.hooksPath .githooks so pfm doctor reports pre-push gate=armed. Upgrading? Follow the update workflow: pfm update check reports UPDATED / NEW / GONE-UPSTREAM / LOCAL-DELETED with the exact diff, and pin / ignore / drop record your decision — pfm never rewrites a project file after init.
Read before opting in: pfm defaults Claude to bypass mode and Codex to approval bypass; machine and per-account configuration can select the prompted posture. Both MCP servers ship disabled. The trade-off is deliberate and documented, not hidden.
The discipline layer (templates/)
Clone it into a repo and you get the complete agent, command, hook, script, and CLAUDE.md template set. docs/SETUP.md walks an interview that substitutes your project's names into every placeholder; docs/PLACEHOLDERS.md is the substitution law. Every template is the live source file verbatim — never a skeleton.
The single idea underneath it is the honest-looking absence — an instrument that answers "nothing found" both when nothing is there and when the instrument itself is broken. The wave walker says it out loud:
SCOUT FAILURE… An empty enumeration is never a verdict.
One agent writes git.
gitterruns six named phases (SETUP, COMMIT, MERGE, PUSH, PULL, TAG). No other agent commits.Guarded files.
.claude/**and everyCLAUDE.mdsit behind/pfmplus a session that has read the quality-prompt contract.The judge is never the thing being judged. Verdicts are read from disk, never from a brief that asserts green.
The wave pipeline. refine → scheduler → orchestrator → builder → walker: the walker is a 19-seat verification engine (scout, thread walkers, slice sensor, gate sweep, security auditor, invariant hunter, anomaly judge, coverage critic, second opinion, final judge) whose failure states are named.
The persona is load-bearing. The Professor prompt replaces the vendor system prompt; the vendor baselines are pinned by sha256 so
pfm doctorreportsMATCHES / DRIFT / CHECK FAILED— never silence.
Optional roles ship for teams that want them — /officer, /km, /pm, /mentor, /marketer — along with a legal skill shelf. The philosophy lives in docs/BLUEPRINT.md.
The fleet CLI (pfm/)
One Go binary with embedded installer assets. Beyond the six moments above:
Limits, honestly. The
Limitstab shows every provider window on the box; a provider it cannot reach never renders as a 0% bar — the panel saysno usage source registeredinstead.Crash-safety by construction.
reap,archive,heal, andinstalldefault to a dry run, and the dry run is the apply's preview.healbacks up the store before it deletes a row.Headless exec.
pfm headless execis one scriptable interface for Claude and Codex: prompt, system prompt, schema, timeout in; normalized result and native streaming out. See HEADLESS.md.Housekeeping.
doctorruns the dependency registry and fleet DB checks;tokensattributes spend per agent;context-meterprices every prompt surface;statuslinerenders identity, session and spend;codex build|checkis the single writer of the Codex mirror.pfm dreamkeeps its repository-memory commands, with automatic injection removed.Editor.
pfm install --vscodeinstalls the Professor VS Code extension and makes the PFM terminal the default, so each new integrated terminal opens at the fleet picker.
From pfm doctor's own registry: Linux or macOS, amd64 or arm64, plus tmux ≥ 1.8, git, sh, bash, zsh, and sleep; setsid on Linux, ps/lsof/launchctl on macOS. Go 1.24.13 or newer for source builds and pfm update. The claude and codex CLIs are optional diagnostics. The harvester provisions its own pinned uv and CPython (about 3.1 GB to download and 5.8 GB on disk for the current Linux amd64 lock), skippable with --skip-harvest; themes with --skip-themes; the Codex probe with --skip-engine codex. Run the dry preview before applying. Harvester configuration: HARVESTER.md.
Engines (engines/)
deep-rr (
engines/deep-rr/) — background research that returns a cited report: a scout swarm, a brainer steering the crawl, quote-pinned claims audited mechanically, lineage clustering so corroboration counts independent sources. Compiled for the Claude Workflow runtime. Start at engines/deep-rr/README.md.wave-walker (
engines/wave-walker/engine/) — post-merge wiring verification: a scout, parallel walkers, a rule engine and a final judge. One TypeScript source compiled bycross-workflowfor both the Claude Workflow runtime and the Codex SDK. Node ≥ 22.13. Design: engines/wave-walker/engine/design.md.
Origin
Extracted from a live production monorepo, not designed in the abstract. Every rule here exists because something went wrong without it — the gate that reads disk instead of chat exists because an agent once claimed green; the scoped-commit rule exists because two concurrent commits once swallowed each other's files; the prevention step exists because the same bug class shipped twice. The characters exist because a generic agent wasn't good enough to argue with.
Built by @rezzminator. Issues and PRs welcome.
License: MIT
Available Tools
5 toolsarchiveARead-only
Browses a compressed archive — "open / list / what is in this .zip, .tar.gz, .7z, .rar", then "extract member X from it". Not a web page fetcher — a URL to a page goes to fetch. Call archive{source:"https://…/data.zip"} for the member listing (names and sizes, nothing extracted); then archive{source:"…", member:"docs/readme.md"} converts that one member to Markdown. 0 members = an empty archive; an ERROR item = the archive could not be fetched or opened, or the member was refused (traversal, symlink, size).
| Name | Required | Description | Default |
|---|---|---|---|
| member | No | Omit to get the SAFE member listing (names + sizes; nothing is extracted). Give one member name from that listing to fetch just that member, converted to Markdown. | |
| source | Yes | URL or local path of a .zip / .tar(.gz/.bz2/.xz) / .7z / .rar archive. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Despite readOnlyHint=true, the description goes well beyond annotations: it discloses that listing extracts nothing, that member retrieval converts to Markdown, that 0 members means an empty archive, and that an ERROR item represents fetch/open/refusal failures including traversal, symlink, and size cases. This gives the agent accurate expectations for edge cases.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence earns its place: it states the resource, gives example phrasings, excludes the sibling tool, defines the two-phase call pattern, and enumerates error/empty outcomes. No filler or redundant restatement is present.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a two-parameter tool with rich annotations, this is complete. There is no output schema, but the description explains the return semantics well enough: listing returns names and sizes, member returns Markdown, 0 members means empty, and ERROR signals the failure modes. An agent has everything needed to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful workflow semantics beyond the schema by establishing the ordering of listing first and then member selection, and it reinforces the safety distinction between the two modes. This is a modest but real enhancement over the schema text.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb plus resource ('Browses a compressed archive'), then clarifies it is not a web page fetcher by naming the destination for URLs. It clearly distinguishes itself from the fetch sibling while defining both listing and member-extraction modes.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly says a URL to a page goes to fetch, naming the alternative tool and the condition that routes there. It also provides a concrete two-step usage pattern: call with source first for the listing, then with member to convert that entry to Markdown.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetchARead-only
Retrieves 1–50 documents as Markdown, input order kept. Call fetch{sources:["https://…","doi:10.…","harvest:…"]} — a URL/path, DOI, ISBN, PMID/PMCID, or a handle from findWorks/searchCache; a title → findWorks first. Each item returns content (may be truncated), size, cache status, and the path to the COMPLETE artifact — read the path for the rest. A failing item carries its own error and the others still return; an empty extraction is reported as that item's error, never as a blank body.
| Name | Required | Description | Default |
|---|---|---|---|
| refresh | No | Force a fresh fetch: bypass the cache entirely, re-download, overwrite the cached artifact, and return the NEW content. | |
| sources | Yes | 1–50 things to fetch, each returned as clean Markdown in the SAME order. Each is a LOCATION or an UNAMBIGUOUS identifier of a DOCUMENT: URL / local path / file://; DOI; ISBN; PMID / PMCID; harvest: handle. Use a DIFFERENT tool for a TITLE (findWorks), an IMAGE (fetchImage), or an archive (archive). A failing item returns a descriptive per-item error and the rest still return. | |
| size_only | No | When true, fetch and cache the full content but return NO body—just {size, chars, path}; full content remains cached at path. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond readOnlyHint, the description discloses important behavioral details: content may be truncated and the path to the full artifact is provided, failing items return their own errors while others still succeed, and empty extractions are reported as errors rather than blank bodies. It also surfaces cache-related behavior, which the annotation does not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and informative: four sentences cover purpose, call syntax, return shape, and failure behavior without filler. Every sentence earns its place, and core operational constraints are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description fully explains what each item returns, including truncation, size, cache status, and the path to the complete artifact. Combined with the schema's parameter documentation and the readOnlyHint annotation, an agent has all the context needed to invoke fetch correctly and interpret its results.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3. The description adds meaningful parameter semantics by showing valid source forms (URL, doi:, harvest:) and stating that results preserve input order, which goes beyond the schema's property descriptions. Refresh and size_only remain covered by the schema, so the description did not need to restate them.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a precise verb and resource: retrieves 1–50 documents as Markdown while preserving input order. It distinguishes itself from siblings by specifying what fetch accepts (URLs, DOIs, ISBNs, PMID/PMCIDs, harvest handles) and explicitly routes titles to findWorks, images to fetchImage, and archives to archive.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives concrete usage guidance: identifiers from locations or unambiguous document identifiers go to fetch, while a title should go to findWorks first, with the resulting handle then passed to fetch. The schema reinforces exclusions for titles, images, and archives, leaving no ambiguity about when this tool is the right choice.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fetchImageARead-only
Fetches images as local files for vision — "get this figure / photo / scanned page". Call fetchImage{sources:["https://…/fig1.png"]}, 1–50 URLs, local paths, or harvest handles, results in input order. Returns each item's path and bytes — open the path with vision; images are never OCR'd or converted. A failing item carries its own error and the rest still return. For a document, PDF, or web page use fetch.
| Name | Required | Description | Default |
|---|---|---|---|
| sources | Yes | 1–50 image URLs or local image paths. Each is downloaded into the type-partitioned cache and its LOCAL FILE PATH is returned in order — images are NOT OCR'd. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses that images are never OCR'd or converted, that results preserve input order and include paths plus bytes, and that a failing item is isolated while the rest still return. This is substantial behavioral context that an agent cannot infer from the schema alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences carry the purpose, a usage example, parameter constraints, return format, partial-failure behavior, and sibling routing with no filler. The vision-oriented use case is front-loaded, and every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with no output schema, the description fully covers accepted input types, limits, ordering, return values, error isolation, and the key alternative. An agent has everything needed to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents the sources array, its 1–50 item boundaries, and the path/order behavior. The description adds value by including a concrete invocation example and mentioning 'harvest handles' as an accepted input form not listed in the schema. This is meaningful but not essential enrichment over near-total schema coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific action and resource: fetching images as local files for vision. It includes natural-language triggers ('get this figure / photo / scanned page') and explicitly distinguishes itself from the sibling fetch by routing documents, PDFs, and web pages elsewhere.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It states exactly when to invoke fetchImage (for images to be passed to vision) and demonstrates the call shape with an explicit example. It also names the alternative tool, fetch, for non-image content, providing clear selection criteria without ambiguity.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
findWorksARead-only
Finds scholarly papers and books by TITLE or bibliographic query — "find the paper about X", "is there a PDF of ". No download. Call findWorks{query:"Attention Is All You Need"}. Returns ranked candidates (title, authors, year, kind, access) each with a fetch handle — pass that value unchanged to fetch. Empty candidates = nothing matched (give the exact title); a tool error = discovery itself failed, retry or fetch an exact identifier. For a general web query use search.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of candidate works to return. | |
| query | Yes | A paper or book TITLE, or a free-text bibliographic query. Returns a ranked list of candidate works with a fetch handle; pick one and pass it to fetch. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate readOnlyHint=true, but description goes further: states 'No download' (a behavioral trait not implied by read-only), explains return format (ranked candidates with fields), clarifies empty vs error semantics, and instructs to pass the handle unchanged to fetch. This is rich context beyond annotations, though not exhaustive (e.g., no rate limiting or auth notes).
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Description is moderately long but dense with essential info. It opens with purpose, gives a call example, then details output, error handling, and alternative. Each sentence adds value; no redundancy. Slightly verbose but well-structured and front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the description fully explains what is returned (ranked candidates with title, authors, year, kind, access) and how to use the fetch handle. It covers error semantics, empty results, and alternative routes. Given the tool's complexity and lack of output schema, this is complete for an agent to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers 100% of parameters (query and limit both have descriptions). The description adds value by explicitly defining query as a title or bibliographic query and explaining the output's relationship to the query (ranked candidates with fetch handle). The limit parameter is adequately described in schema, so no extra needed. Overall, description complements schema effectively.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description states a specific verb (Finds) and resource (scholarly papers and books) with examples of acceptable queries. It explicitly distinguishes itself from search for general web queries and from fetch for retrieving documents. This leaves no ambiguity about the tool's role.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit call example, explains when to use this tool vs search, and gives guidance on error handling (retry or fetch exact identifier). Also clarifies the workflow: findWorks returns fetch handles to be passed to fetch. This gives clear when-to-use and when-not-to-use instructions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchCacheARead-only
Greps the local cache of already-fetched documents — "did we already fetch X", "which cached pages mention Y". Call searchCache{pattern:"transformer attention"}. Returns WHICH cached pages match (url, md_path, match count, sample line), not their text — read md_path or fetch the url for content. Empty matches = nothing held matches (the web is not searched; search or fetch first); a tool error = the pattern was invalid or the cache could not be read.
| Name | Required | Description | Default |
|---|---|---|---|
| pattern | Yes | Regex pattern to search across every cached markdown body in the cache. | |
| ignore_case | No | Case-insensitive search. | |
| max_results | No | Maximum number of matching cached pages to return. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only provide readOnlyHint=true, but the description adds substantial behavior: returns which cached pages match rather than their text, lists the return fields, and explains the difference between empty matches and tool errors. This aligns with and extends the read-only annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences cover target, output shape, empty-result semantics, and error semantics with no wasted words. The core distinction — local cache vs web search — is front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
As a read-only tool with no output schema, the description is complete: it specifies the return shape, how to access full content, how to interpret empty results, and what causes errors. An agent has everything required to invoke it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the schema already fully documents pattern, ignore_case, and max_results. The description adds a call example for pattern but does not need to compensate for any missing parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: 'Greps the local cache of already-fetched documents', and gives two concrete questions the tool answers. It clearly distinguishes itself from siblings by emphasizing 'local cache' and explicitly noting 'the web is not searched'.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells the agent when to use it ('did we already fetch X') and when not to rely on it ('the web is not searched; search or fetch first'). Empty matches are given a defined meaning, so the agent knows to fall back to another tool rather than treating it as an error.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
archive - First observed
fetch - First observed
fetchImage - First observed
findWorks - First observed
searchCache
TDQS
Scored across 5 tools
Each tool has a clearly distinct purpose: archive handles compressed archives, fetch retrieves textual documents, fetchImage handles images, findWorks discovers scholarly works, and searchCache searches the local cache. No two tools overlap in function, and the descriptions explicitly clarify boundaries (e.g., fetchImage for images, fetch for documents).
All tool names follow a consistent lowerCamelCase convention with single-word verbs or verb-noun combinations (archive, fetch, fetchImage, findWorks, searchCache). No mixing of styles like snake_case or inconsistent verb patterns, making the naming predictable and coherent.
With 5 tools, the server is well-scoped for a document harvesting and discovery service. Each tool addresses a core aspect (fetching text, images, archives, discovery, and cache search) without redundancy, and the count is within the ideal 3-15 range.
The tool surface covers the main lifecycle: discovery (findWorks), retrieval (fetch, fetchImage), archive handling (archive), and cache search (searchCache). Minor gaps exist, such as lack of explicit cache management (e.g., clearing or deleting) or a dedicated bulk download tool, but agents can work around these with the provided tools.
Maintenance
Related MCP Connectors
Search PubMed/Europe PMC, fetch articles and full text (PMC/EPMC/Unpaywall), citations, MeSH terms.
Resolve DOIs, search ~155M scholarly works, and fetch references via the Crossref REST API.
Search biomedical literature, get article details, find related articles, and explore MeSH terms
Search biomedical papers, inspect publication records, and traverse citation or semantic graphs.
Related MCP Servers
- AlicenseAqualityBmaintenanceSearches and fetches research datasets across Zenodo, DataCite (Dryad/Figshare/Dataverse/OSF), NCBI omics archives (GEO/SRA/BioProject), and the literature (PubMed/OpenAIRE) through one normalized model — deduplicating by DOI, expanding organism queries with NCBI Taxonomy synonyms, and bridging papers to the datasets they produced. Resolves citations and open-access full text, and downloads files.673 PyPI3MIT
- AlicenseNot gradedqualityDmaintenanceSearches and retrieves scholarly metadata from the CrossRef REST API, covering over 150 million records across all disciplines, without requiring an API key.24 PyPIMIT
- AlicenseBqualityCmaintenanceEnables scholarly search, paper metadata, citation graphs, author and venue resolution, candidate harvesting, API quota visibility, and guarded full-text downloads through the OpenAlex API.94MIT
- AlicenseNot gradedqualityAmaintenanceResolve DOIs, search ~155M scholarly works, fetch references, and look up publishers via the Crossref REST API. Supports STDIO and Streamable HTTP transports.571 npm3Apache 2.0