Skip to content

hn mcp server changelog

io.github.cyanheads/hn-mcp-server

9 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.

  • server.json 1 field changed
    version
    0.6.1 0.7.0
  • server.json 1 field changed
    version
    0.6.0 0.6.1

  • AGENTS.md +19 −9

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

    − **Version:** 0.5.22− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− **MCP SDK:** `@modelcontextprotocol/server` 2.3.1− import { getHnService, settlePage, normalizeUrl, stripHtml } from '@/services/hn/hn-service.js';−     const feedIds = await hn.fetchFeed(input.feed, ctx);−     const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx));− 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− **Agent skill directory:** Copy skills into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.− `bun run bundle` produces a `.mcpb` extension bundle 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 was + **Version:** 0.6.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`+ **MCP SDK:** `@modelcontextprotocol/server` 2.4.0+ import { CALL_BUDGET_MS, getHnService, settlePage, normalizeUrl, stripHtml } from '@/services/hn/hn-service.js';+     // One time budget for the feed and every item fetch after it.+     const deadline = Date.now() + CALL_BUDGET_MS;+     const feedIds = await hn.fetchFeed(input.feed, ctx, deadline);+     const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx, deadline));+     tools/response-budget.ts           # 64,000-byte budget per response surface: measure both surfaces, largest shared body cut, code-point cut+ 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 any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |+ | `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |+ Development skills are modular instructions. 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. Their 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 them to every agent that installs it.+ A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.+ **Agent skill directory:** Copy `framework-skills/` into the directory your agent discovers (Claude Code: `.claude/skills/`, others: equivalent). Skills then load as context without referencing `framework-skills/` paths. After framework updates, run the `maintenance` skill — Phase B re-syncs the agent directory.+ | `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |+ `bun run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored and local-only files such as `claude-plans/` — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. The pack step is followed by

  • Resources +1
    • + hn-guide

  • server.json 1 field changed
    version
    0.5.22 0.6.0

  • AGENTS.md +7 −4

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

    − **Version:** 0.5.21− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.12`− **MCP SDK:** `@modelcontextprotocol/server` 2.2.0− **Zod:** ^4.6.5+ **Version:** 0.5.22+ **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+     tools/served-at-cap.ts             # notice sentence for a size input served at its per-call cap+     tools/thread-cursor.ts             # hn_get_thread cursor codec: packed frontier, JSON format still decoded+   --build-arg APP_VERSION=<version> \

  • server.json 1 field changed
    version
    0.5.21 0.5.22

  • AGENTS.md +15 −17

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

    − **Version:** 0.5.20− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.6`− **MCP SDK:** `@modelcontextprotocol/server` 2.0.0−     count: z.number().min(1).max(100).default(30).describe('Number of stories to return.'),−     offset: z.number().min(0).default(0).describe('Number of stories to skip for pagination.'),− | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any serializable value. |− | `ctx.inputs` | Reader over a retried request's responses — `.accepted(key, schema)`, `.view(key)`, `.state()`, `.dropped`. Empty on the first round. |− | `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−     throw ctx.fail('item_not_found', `Item ${input.itemId} not found`, {−       itemId: input.itemId,−       ...ctx.recoveryFor('item_not_found'),−     });− **Service-layer:** services don't have `ctx.fail`, but can carry the contract reason via `data: { reason: 'X' }` on a factory throw. The auto-classifier preserves `data` on the wire so clients see the same `error.data.reason` they'd see from `ctx.fail`. `hn-service.ts` throws all five `upstream_*` reasons this way, so each of their `errors[]` entries carries `thrownBy: 'service'` — `error-contract-unthrown` reads the handler body alone and would otherwise flag them as dead. See framework CLAUDE.− **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `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-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — no fixup, no autosquash, no force-push of any kind, so `main` keeps the r− - [ ] Domain failure modes declared in `errors[]` with `recovery` strings (≥5 words, lint-validated); `ctx.recoveryFor('reason')` spread into throw `data` to mirror onto the wire− - [ ] Service-layer throws carry `data: { reason, recovery: { hint } }` so format()-only clients see the recovery hint, and their `errors[]` entries carry `thrownBy: 'service'`+ **Version:** 0.5.21+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.12`+ **MCP SDK:** `@modelcontextprotocol/server` 2.2.0+     count: z.number().int().min(1).max(100).default(30).describe('Number of stories to return (1–100).'),+     offset: z.number().int().min(0).default(0).describe('Number of stories to skip for pagination.'),+ | `ctx.state` | Tenant-scoped KV — `.get(key)`, `.set(key, value, { ttl? })`, `.delete(key)`, `.getMany(keys)`, `.list(prefix, { cursor, limit })`. Accepts any JSON-serializable value; reads return its JSON form (a `Date` comes back as an ISO string). |+ | `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.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+     throw ctx.fail('item_not_found', `Item ${input.itemId} not found`, { itemId: input.itemId });+ **Service-layer:** services don't have `ctx.fail`, but can carry the contract reason via `data: { reason: 'X' }` on a factory throw. The auto-classifier preserves `data` on the wire so clients see the same `error.data.reason` they'd see from `ctx.fail`, and the framework fills the contract's `recovery` for a throw that carries none. `hn-service.ts` throws all five `upstream_*` reasons this way, each with an explicit `recovery: { hint }` that overrides the contract text with runtime context (the + **Every release goes through a gated release PR** — `git-wrapup`'s "Release PR mode", mode `gated`. Three separate runs, never one: `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-pr-review` reviews and fixes on that branch (each fix an ordinary commit on top of the stack, pushed plainly — no fixup, no autosquash, no force-pu+ - [ ] Domain failure modes declared in `errors[]` with `recovery` strings (≥5 words, lint-validated); the framework puts the declared `recovery` on the wire for any failure carrying that `reason` without a hint+ - [ ] Service-layer throws carry `data: { reason }` (plus an explicit `recovery: { hint }` only where runtime context sharpens the contract text), and their `errors[]` entries carry `thrownBy: 'service'`

  • server.json 1 field changed
    version
    0.5.20 0.5.21