Skip to content

datacite mcp server AGENTS.md changelog

io.github.cyanheads/datacite-mcp-server

3 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • AGENTS.md +26 −12

    https://raw.githubusercontent.com/cyanheads/datacite-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.1.3− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1−   // rendered text. Depositor text goes through the _text.ts helpers (flattenInline, blockquote,−   // fenced), never raw.− This server registers no resources or prompts. Scaffold one with the `add-resource` or `add-prompt` skill.− import { parseEnvConfig } from '@cyanheads/mcp-ts-core/config';− `parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`DATACITE_CONTACT_EMAIL`) not the path (`contactEmail`). Throws `ConfigurationError`, which the framework prints as a clean startup banner. `initDataCiteServices()` reads the config in `setup()`, so a malformed value fails at startup. The contact email rides the `User-Agent` only — never a URL parameter, cache key, or log line.−     server-config.ts                    # DATACITE_CONTACT_EMAIL, DATACITE_MAX_REQUESTS_PER_5MIN (Zod schema)− Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand these development skills to every agent that installs it. Keep `skills/` free for skills meant f− | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |− `bun run bundle` produces `dist/datacite-mcp-server.mcpb` for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it+ **Version:** 0.2.1+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0+   // rendered text. Depositor text goes through the _text.ts helpers: flattenInline for inline+   // values, descriptionQuote for deposited descriptions, blockquote for other prose, urlLink+   // for URLs, and fenced for machine documents. Escape only at the rendering boundary.+ This server registers no explicit resources or prompts. Its root `skills/` tree is served through the Skills extension by `createApp({ skills: 'skills' })`. Scaffold an explicit resource or prompt with the `add-resource` or `add-prompt` skill.+ import { config, parseEnvConfig } from '@cyanheads/mcp-ts-core/config';+   clampLimits: z.stringbool().default(true).describe('Clamp oversized read counts; false advertises and enforces strict upstream caps.'),+   void config.pkg; // Load the framework config and Node .env before tool schemas read the switch.+     clampLimits: 'DATACITE_CLAMP_LIMITS',+ `parseEnvConfig` maps Zod schema paths → env var names so errors name the variable (`DATACITE_CONTACT_EMAIL`) not the path (`contactEmail`), and throws `ConfigurationError` for malformed values. Count schemas read the config during tool-definition construction; the framework config accessor loads Node's `.env` first. `DATACITE_CLAMP_LIMITS` defaults to `true`: search counts apply at most 1,000 and graph counts at most 100, with requested/applied notices, logs, and a counter. `false` advertises a+     server-config.ts                    # Contact email, request budget, and read-count clamping (Zod schema)+       jats-notice.ts                    # Detect empty names and inconsistent years without rewriting JATS+       _counts.ts                        # Count schemas, applied caps, and clamp telemetry+ Two skill trees live at the project root, written for different readers:+ | Tree | For | Written by |+ |:-----|:----|:-----------|+ | `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus server-specific development skills | `init` and `maintenance` for framework skills; the author for local skills |+ | `skills/` | Agents installing the server and calling its tools — tool chains, identifier sources, gates | The author, using `add-skill` |+ Read development skills directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Claude Code and Codex auto-load a plugin's root `skills/`, so development skills stay in `framework-skills/`. Published root skills ship through plugins, npm, Docker, the `.mcpb` bundle, and the MCP Skills extension; `lint:packaging` validates that tree.+ | `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — structure, validation, delivery |+ | `bun run lint:packaging` | Packaging checks — env-var parity, `.mcpbignore` coverage, plugin manifests, published `skills/` (run by devcheck) |+ `bun run bundle` produces `dist/datacite-mcp-server.mcpb` for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`; root development paths need root-anchored exclusions, while root `skills/` ships. `scripts/clean-mcpb.ts` prunes dev dependencies and strips dependency agent docs, platform-specific native bindings, node-gyp generator output containing build-host paths, and build-only package files. Compiled `*.node` addons stay. MCPB is stdio-only. `release-and-pu+ - [ ] Root `plugin.json` + `mcp.json` populated from `package.json`; the root MCP entry carries fixed env values only. User variables stay in the plugin overlays, and `.codex-plugin/plugin.json` keeps `"mcpServers": "./.codex-plugin/mcp.json"` so Codex forwards `env_vars`.+ - [ ] New root development paths have root-anchored `.mcpbignore` exclusions; root `skills/` ships, and `lint:packaging` validates it

  • AGENTS.md +7 −5

    https://raw.githubusercontent.com/cyanheads/datacite-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.1.2− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`− **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0− **Zod:** ^4.6.5− `teardown(core)` is the `setup()` counterpart — release a watcher, socket, or non-`unref()`'d timer there. It runs after the transport stops and before the logger closes, on every shutdown path, and a signal-triggered shutdown then exits the process explicitly (0, or 1 if a step never settles within the framework's 10 s ceiling). Here `setup()` builds the DataCite and doi.org services over one shared response cache, and `teardown()` disposes both request pacers.+ **Version:** 0.1.3+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1+ **Zod:** ^4.6.7+ `teardown(core)` is the `setup()` counterpart — release a watcher, socket, or non-`unref()`'d timer there. It runs after the transport stops and before the logger closes, on every shutdown path, and a signal-triggered shutdown then exits the process explicitly (0, or 1 if a step never settles within the framework's 10 s ceiling). Here `setup()` builds the DataCite, doi.org and ROR services over one shared response cache, and `teardown()` disposes all three request pacers.+     ror/+       ror-service.ts                    # Exact FundRef-to-ROR mapping; bounded literal-search fallback

  • AGENTS.md +18 −11

    https://raw.githubusercontent.com/cyanheads/datacite-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.1.1− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.8`− **MCP SDK:** `@modelcontextprotocol/server` ^2.0.0−     if (!doi) throw ctx.fail('invalid_doi', 'The doi input is not a DOI after normalization; …', ctx.recoveryFor('invalid_doi'));−   // format() populates content[] — the markdown twin of structuredContent. Depositor text−   // goes through the _text.ts helpers (flattenInline, blockquote, fenced), never raw.− | `ctx.recoveryFor(reason)` | Typed lookup of the contract `recovery` for a declared reason. Returns `{ recovery: { hint } }` for known reasons, `{}` otherwise. Pass it as `ctx.fail` data to mirror the contract hint into `content[]`. |− | `ctx.requestId` | Unique request ID. |− **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. Pass `ctx.recoveryFor('reaso−   if (!item) throw ctx.fail('no_match', `No item ${input.id}`, ctx.recoveryFor('no_match'));− **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. A caller's bri+ **Version:** 0.1.2+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.14`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.2.0+     if (!doi) throw ctx.fail('invalid_doi', 'The doi input is not a DOI after normalization; …');+   // format() populates content[] — the markdown twin of structuredContent. Different clients+   // read different surfaces (Claude Code → structuredContent, Claude Desktop → content[]); both+   // must carry the same data, enforced at lint time: every field in `output` must appear in the+   // rendered text. Depositor text goes through the _text.ts helpers (flattenInline, blockquote,+   // fenced), never raw.+ | `ctx.requestInput` | Suspend and ask the caller for more input — `return ctx.requestInput({ inputRequests: { key: inputRequired.elicit({ message, requestedSchema }) } })`. Never returns; the handler is re-entered with the answers. Always present. This server uses none (stateless). |+ | `ctx.inputs` | The request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped` — limited to what the client declared (`elicitation` and its form/url modes, `sampling`, `roots`). Client-supplied: a consent gate trusts only a `ctx.state` record it stored when it asked, bound to the operation, caller, and target (see the `api-context` skill). |+ | `ctx.clientCapabilities` | What the client declared for this request, `undefined` when no view exists. Decides whether to ask for optional context (e.g. roots); never a reason to skip a consent prompt. |+ | `ctx.recoveryFor(reason)` | Typed lookup of the contract `recovery` for a declared reason. Returns `{ recovery: { hint } }` for known reasons, `{}` otherwise. Not needed to put a declared hint on the wire — the framework fills it. Use it only when the hint must ride the thrown error itself (a test of the handler's own throw) or a site deliberately sends another entry's guidance. |+ | `ctx.content` | Non-text content blocks — `.image(data, mimeType)`, `.audio(data, mimeType)`, or `ctx.content(block)` for a raw block. Prepended to `content[]` after `format()`; never enters `structuredContent`. |+ | `ctx.requestId` | Request ID — the one every log record of the call carries and its error envelope returns as `data.requestId`. |+ **Recommended: typed error contract.** Declare `errors: [{ reason, code, when, recovery, retryable?, severity?, thrownBy? }]` on `tool()` / `resource()` to receive `ctx.fail(reason, …)` typed against the reason union. TypeScript catches typos at compile time, `data.reason` is auto-populated for observability, linter enforces conformance against the handler body. `recovery` is required (≥ 5 words, lint-validated) — the single source of truth for the agent's next move. The framework puts it on the+   if (!item) throw ctx.fail('no_match', `No item ${input.id}`);+ **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the