@cyanheads/hn-mcp-server 0.5.22 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/AGENTS.md +22 -9
  2. package/CLAUDE.md +22 -9
  3. package/Dockerfile +9 -3
  4. package/README.md +19 -12
  5. package/changelog/0.6.x/0.6.0.md +38 -0
  6. package/dist/index.js +1 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp-server/tools/definitions/get-stories.tool.d.ts.map +1 -1
  9. package/dist/mcp-server/tools/definitions/get-stories.tool.js +9 -6
  10. package/dist/mcp-server/tools/definitions/get-stories.tool.js.map +1 -1
  11. package/dist/mcp-server/tools/definitions/get-thread.tool.d.ts +1 -0
  12. package/dist/mcp-server/tools/definitions/get-thread.tool.d.ts.map +1 -1
  13. package/dist/mcp-server/tools/definitions/get-thread.tool.js +296 -182
  14. package/dist/mcp-server/tools/definitions/get-thread.tool.js.map +1 -1
  15. package/dist/mcp-server/tools/definitions/get-user.tool.d.ts +1 -0
  16. package/dist/mcp-server/tools/definitions/get-user.tool.d.ts.map +1 -1
  17. package/dist/mcp-server/tools/definitions/get-user.tool.js +160 -104
  18. package/dist/mcp-server/tools/definitions/get-user.tool.js.map +1 -1
  19. package/dist/mcp-server/tools/definitions/search-content.tool.d.ts +16 -3
  20. package/dist/mcp-server/tools/definitions/search-content.tool.d.ts.map +1 -1
  21. package/dist/mcp-server/tools/definitions/search-content.tool.js +263 -188
  22. package/dist/mcp-server/tools/definitions/search-content.tool.js.map +1 -1
  23. package/dist/mcp-server/tools/response-budget.d.ts +111 -0
  24. package/dist/mcp-server/tools/response-budget.d.ts.map +1 -0
  25. package/dist/mcp-server/tools/response-budget.js +204 -0
  26. package/dist/mcp-server/tools/response-budget.js.map +1 -0
  27. package/dist/services/hn/hn-service.d.ts +45 -9
  28. package/dist/services/hn/hn-service.d.ts.map +1 -1
  29. package/dist/services/hn/hn-service.js +73 -17
  30. package/dist/services/hn/hn-service.js.map +1 -1
  31. package/package.json +8 -7
  32. package/server.json +3 -3
  33. package/skills/hn-guide/SKILL.md +39 -0
package/AGENTS.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** hn-mcp-server
4
- **Version:** 0.5.22
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
4
+ **Version:** 0.6.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` 2.3.1
7
+ **MCP SDK:** `@modelcontextprotocol/server` 2.4.0
8
8
  **Zod:** ^4.6.7
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
@@ -48,7 +48,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
48
48
 
49
49
  ```ts
50
50
  import { tool, z } from '@cyanheads/mcp-ts-core';
51
- import { getHnService, settlePage, normalizeUrl, stripHtml } from '@/services/hn/hn-service.js';
51
+ import { CALL_BUDGET_MS, getHnService, settlePage, normalizeUrl, stripHtml } from '@/services/hn/hn-service.js';
52
52
 
53
53
  export const getStories = tool('hn_get_stories', {
54
54
  description: 'Fetch stories from an HN feed (top, new, best, ask, show, jobs).',
@@ -70,10 +70,12 @@ export const getStories = tool('hn_get_stories', {
70
70
 
71
71
  async handler(input, ctx) {
72
72
  const hn = getHnService();
73
- const feedIds = await hn.fetchFeed(input.feed, ctx);
73
+ // One time budget for the feed and every item fetch after it.
74
+ const deadline = Date.now() + CALL_BUDGET_MS;
75
+ const feedIds = await hn.fetchFeed(input.feed, ctx, deadline);
74
76
  const sliced = feedIds.slice(input.offset, input.offset + input.count);
75
77
  // Live items plus the IDs whose fetch failed; throws when every item failed.
76
- const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx));
78
+ const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx, deadline));
77
79
  if (failedIds.length > 0) ctx.enrich({ failedIds });
78
80
  ctx.log.info('Fetched stories', { feed: input.feed, count: items.length });
79
81
  return { stories: items.map(/* ... */), feed: input.feed, total: feedIds.length };
@@ -218,6 +220,7 @@ src/
218
220
  types.ts # HN domain types
219
221
  mcp-server/
220
222
  tools/markdown-escape.ts # format() boundary: escape titles, quote bodies
223
+ tools/response-budget.ts # 64,000-byte budget per response surface: measure both surfaces, largest shared body cut, code-point cut
221
224
  tools/served-at-cap.ts # notice sentence for a size input served at its per-call cap
222
225
  tools/thread-cursor.ts # hn_get_thread cursor codec: packed frontier, JSON format still decoded
223
226
  tools/definitions/
@@ -242,9 +245,18 @@ src/
242
245
 
243
246
  ## Skills
244
247
 
245
- 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 for those agents.
248
+ Two skill trees live at the project root, written for different readers:
246
249
 
247
- **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.
250
+ | Tree | For | Written by |
251
+ |:-----|:----|:-----------|
252
+ | `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) |
253
+ | `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 |
254
+
255
+ 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.
256
+
257
+ 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.
258
+
259
+ **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.
248
260
 
249
261
  Available skills:
250
262
 
@@ -256,6 +268,7 @@ Available skills:
256
268
  | `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
257
269
  | `add-resource` | Scaffold a new resource definition |
258
270
  | `add-prompt` | Scaffold a new prompt definition |
271
+ | `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 |
259
272
  | `add-service` | Scaffold a new service integration |
260
273
  | `add-test` | Scaffold test file for a tool, resource, or service |
261
274
  | `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
@@ -322,7 +335,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
322
335
 
323
336
  ## Bundling
324
337
 
325
- `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 packed on. MCPB is stdio-only — HTTP deployments are unaffected.
338
+ `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 `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips four classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/` at a package's root, and any `SKILL.md`); platform-specific native bindings; node-gyp build output beside a `binding.gyp`; and the framework's build-only files. The agent-doc selector is anchored at each package root in both `clean-mcpb.ts` and `lint-packaging.ts` — a local divergence from the synced scripts until cyanheads/mcp-ts-core#794 ships — because MCP SDK 2.4 ships runtime code at `@modelcontextprotocol/{server,core}/dist/ext/skills/`, and the upstream selector strips it and breaks the bundle's startup. Re-apply it after a script resync; `tests/scripts/bundle-agent-docs.test.ts` fails if it is lost. MCPB is stdio-only — HTTP deployments are unaffected.
326
339
 
327
340
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, that every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"` (the host substitutes nothing else — `"${X}"` reaches the server as that literal string), and that an optional string option carries `"default": ""`.
328
341
 
package/CLAUDE.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Developer Protocol
2
2
 
3
3
  **Server:** hn-mcp-server
4
- **Version:** 0.5.22
5
- **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`
4
+ **Version:** 0.6.0
5
+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.18`
6
6
  **Engines:** Bun ≥1.4.0, Node ≥24.0.0
7
- **MCP SDK:** `@modelcontextprotocol/server` 2.3.1
7
+ **MCP SDK:** `@modelcontextprotocol/server` 2.4.0
8
8
  **Zod:** ^4.6.7
9
9
 
10
10
  > **Read the framework docs first:** `node_modules/@cyanheads/mcp-ts-core/CLAUDE.md` contains the full API reference — builders, Context, error codes, exports, patterns. This file covers server-specific conventions only.
@@ -48,7 +48,7 @@ Tailor suggestions to what's actually missing or stale — don't recite the full
48
48
 
49
49
  ```ts
50
50
  import { tool, z } from '@cyanheads/mcp-ts-core';
51
- import { getHnService, settlePage, normalizeUrl, stripHtml } from '@/services/hn/hn-service.js';
51
+ import { CALL_BUDGET_MS, getHnService, settlePage, normalizeUrl, stripHtml } from '@/services/hn/hn-service.js';
52
52
 
53
53
  export const getStories = tool('hn_get_stories', {
54
54
  description: 'Fetch stories from an HN feed (top, new, best, ask, show, jobs).',
@@ -70,10 +70,12 @@ export const getStories = tool('hn_get_stories', {
70
70
 
71
71
  async handler(input, ctx) {
72
72
  const hn = getHnService();
73
- const feedIds = await hn.fetchFeed(input.feed, ctx);
73
+ // One time budget for the feed and every item fetch after it.
74
+ const deadline = Date.now() + CALL_BUDGET_MS;
75
+ const feedIds = await hn.fetchFeed(input.feed, ctx, deadline);
74
76
  const sliced = feedIds.slice(input.offset, input.offset + input.count);
75
77
  // Live items plus the IDs whose fetch failed; throws when every item failed.
76
- const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx));
78
+ const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx, deadline));
77
79
  if (failedIds.length > 0) ctx.enrich({ failedIds });
78
80
  ctx.log.info('Fetched stories', { feed: input.feed, count: items.length });
79
81
  return { stories: items.map(/* ... */), feed: input.feed, total: feedIds.length };
@@ -218,6 +220,7 @@ src/
218
220
  types.ts # HN domain types
219
221
  mcp-server/
220
222
  tools/markdown-escape.ts # format() boundary: escape titles, quote bodies
223
+ tools/response-budget.ts # 64,000-byte budget per response surface: measure both surfaces, largest shared body cut, code-point cut
221
224
  tools/served-at-cap.ts # notice sentence for a size input served at its per-call cap
222
225
  tools/thread-cursor.ts # hn_get_thread cursor codec: packed frontier, JSON format still decoded
223
226
  tools/definitions/
@@ -242,9 +245,18 @@ src/
242
245
 
243
246
  ## Skills
244
247
 
245
- 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 for those agents.
248
+ Two skill trees live at the project root, written for different readers:
246
249
 
247
- **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.
250
+ | Tree | For | Written by |
251
+ |:-----|:----|:-----------|
252
+ | `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) |
253
+ | `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 |
254
+
255
+ 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.
256
+
257
+ 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.
258
+
259
+ **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.
248
260
 
249
261
  Available skills:
250
262
 
@@ -256,6 +268,7 @@ Available skills:
256
268
  | `add-app-tool` | Scaffold an MCP App tool + paired UI resource |
257
269
  | `add-resource` | Scaffold a new resource definition |
258
270
  | `add-prompt` | Scaffold a new prompt definition |
271
+ | `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 |
259
272
  | `add-service` | Scaffold a new service integration |
260
273
  | `add-test` | Scaffold test file for a tool, resource, or service |
261
274
  | `field-test` | Exercise tools/resources/prompts with real inputs, verify behavior, report issues |
@@ -322,7 +335,7 @@ When you complete a skill's checklist, check the boxes and add a completion time
322
335
 
323
336
  ## Bundling
324
337
 
325
- `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 packed on. MCPB is stdio-only — HTTP deployments are unaffected.
338
+ `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 `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips four classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/` at a package's root, and any `SKILL.md`); platform-specific native bindings; node-gyp build output beside a `binding.gyp`; and the framework's build-only files. The agent-doc selector is anchored at each package root in both `clean-mcpb.ts` and `lint-packaging.ts` — a local divergence from the synced scripts until cyanheads/mcp-ts-core#794 ships — because MCP SDK 2.4 ships runtime code at `@modelcontextprotocol/{server,core}/dist/ext/skills/`, and the upstream selector strips it and breaks the bundle's startup. Re-apply it after a script resync; `tests/scripts/bundle-agent-docs.test.ts` fails if it is lost. MCPB is stdio-only — HTTP deployments are unaffected.
326
339
 
327
340
  **Adding an env var requires both files:** `server.json` (registry discovery, `environmentVariables[]`) and `manifest.json` (bundle install UX, `mcp_config.env` + `user_config`). `lint:packaging` (run by `devcheck`) verifies the env var names match, that every `user_config` option is wired into `mcp_config.env` as `"X": "${user_config.X}"` (the host substitutes nothing else — `"${X}"` reaches the server as that literal string), and that an optional string option carries `"default": ""`.
328
341
 
package/Dockerfile CHANGED
@@ -14,7 +14,7 @@
14
14
  # output. A stage that compiles a native addon needs the target-arch toolchain
15
15
  # and cannot cross-compile this way — drop the flag there.
16
16
  # ==============================================================================
17
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS build
17
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS build
18
18
 
19
19
  WORKDIR /usr/src/app
20
20
 
@@ -29,6 +29,11 @@ RUN --mount=type=cache,target=/root/.bun/install/cache \
29
29
  # Copy the rest of the source code
30
30
  COPY . .
31
31
 
32
+ # The skills a server publishes (`createApp({ skills: 'skills' })`) are read at
33
+ # runtime from the application root. Created when the project has none, so the
34
+ # production stage's copy below holds with or without one.
35
+ RUN mkdir -p skills
36
+
32
37
  # Build the application
33
38
  RUN bun run build
34
39
 
@@ -46,7 +51,7 @@ RUN bun run build
46
51
  # A clean image rather than `FROM build`: the build stage's node_modules holds
47
52
  # devDependencies.
48
53
  # ==============================================================================
49
- FROM --platform=$BUILDPLATFORM oven/bun:1.4.3 AS deps
54
+ FROM --platform=$BUILDPLATFORM oven/bun:1.4.4 AS deps
50
55
 
51
56
  WORKDIR /usr/src/app
52
57
 
@@ -114,7 +119,7 @@ RUN rm -rf node_modules/@socketsecurity/bun-security-scanner
114
119
  # dependencies and build artifacts. Its only Bun invocations are HEALTHCHECK
115
120
  # and CMD, which run on the real target at container start.
116
121
  # ==============================================================================
117
- FROM oven/bun:1.4.3-slim AS production
122
+ FROM oven/bun:1.4.4-slim AS production
118
123
 
119
124
  WORKDIR /usr/src/app
120
125
 
@@ -136,6 +141,7 @@ COPY --from=deps /usr/src/app/node_modules ./node_modules
136
141
 
137
142
  # Copy the compiled application code from the build stage
138
143
  COPY --from=build /usr/src/app/dist ./dist
144
+ COPY --from=build /usr/src/app/skills ./skills
139
145
 
140
146
  # The 'oven/bun' image already provides a non-root user named 'bun'.
141
147
  # We will use this existing user for enhanced security.
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <div align="center">
9
9
 
10
- [![Version](https://img.shields.io/badge/Version-0.5.22-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/hn-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-2.3.1-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/hn-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/hn-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun->=1.4.0-f9f1e1.svg?style=flat-square)](https://bun.sh/)
10
+ [![Version](https://img.shields.io/badge/Version-0.6.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/hn-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-2.4.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/hn-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/hn-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun->=1.4.0-f9f1e1.svg?style=flat-square)](https://bun.sh/)
11
11
 
12
12
  </div>
13
13
 
@@ -40,6 +40,12 @@ Hacker News feeds, threads, and profiles. Browse ranked feeds, read full comment
40
40
  | `hn_get_user` | Fetch a user profile with karma, about, and optionally a page of resolved submissions |
41
41
  | `hn_search_content` | Search stories, comments, polls, and jobs via Algolia by keyword, by filters alone, or both — type, author, parent story, date range, and minimum points |
42
42
 
43
+ ### Agent skill
44
+
45
+ | Skill | Description |
46
+ |:---|:---|
47
+ | [`hn-guide`](skills/hn-guide/SKILL.md) | How to find a link's thread, search without Algolia's fuzzy matches, read large comment trees in full, and search what one user said. Served over MCP as `skill://hn-guide/SKILL.md` and shipped in the npm package |
48
+
43
49
  ## Capability reference
44
50
 
45
51
  ### `hn_get_stories` <sub>tool</sub>
@@ -47,19 +53,19 @@ Hacker News feeds, threads, and profiles. Browse ranked feeds, read full comment
47
53
  - Six feed types: `top`, `new`, `best`, `ask`, `show`, `jobs`; `count` (default 30) and `offset` for pagination. One call serves at most 100 stories; a `count` up to 1,000 is served at 100, with a `notice` naming the next `offset`
48
54
  - Returns id, type, title, url, domain, score, author, timestamp, comment count, and body text — each field omitted (not null) when HN doesn't provide it
49
55
  - Enrichment reports `total`, `offset`, and `hasMore`; while more stories remain, `truncated` is set and `notice` names the next `offset`, and a `notice` explains empty pages (empty feed, offset past end, or every item on the page deleted/flagged). The last page carries no truncation
50
- - Items whose fetch failed after retries are listed in `failedIds` with a `notice` naming the retry; a page where every item failed returns the classified upstream error instead of an empty list
56
+ - Items whose fetch failed after retries, or ran past the call's 50-second time budget, are listed in `failedIds` with a `notice` naming the retry; a page where every item failed returns the classified upstream error instead of an empty list
51
57
 
52
58
  ---
53
59
 
54
60
  ### `hn_get_thread` <sub>tool</sub>
55
61
 
56
- - `itemId` plus `depth` (default 3; 0 returns the item with no comments) and `maxComments` (default 50) capping one response across all levels, alongside a fixed 64,000-byte text budget that cuts only between comments. One call resolves at most 10 levels and 200 comments; a `depth` up to 100 is served at 10 and a `maxComments` up to 1,000 at 200, with a `notice` naming the comment to pass as `itemId` or the `nextCursor` that reaches the rest
62
+ - `itemId` plus `depth` (default 3; 0 returns the item with no comments) and `maxComments` (default 50) capping one response across all levels, alongside a fixed 64,000-byte budget on each response surface (all `content[]` text, `nextCursor` included, and the `structuredContent` JSON) that cuts only between comments and always returns a page's first comment whole. One call resolves at most 10 levels and 200 comments; a `depth` up to 100 is served at 10 and a `maxComments` up to 1,000 at 200, with a `notice` naming the comment to pass as `itemId` or the `nextCursor` that reaches the rest
57
63
  - Breadth-first traversal ranked like HN — top-ranked top-level comments resolve first, replies fill in only after the level above is exhausted
58
- - Resumable: a response stopped by `maxComments`, the byte budget, or an upstream rate limit returns `nextCursor`; passing it back as `cursor` with the same `itemId` continues at the next unseen comment. The cursor carries the unvisited comments themselves, so re-ranked replies cannot shift it, and the server keeps no session state. A cursor the tool did not issue, an altered one, or one passed with a different `itemId` fails with `invalid_cursor`
64
+ - Resumable: a response stopped by `maxComments`, the byte budget, an upstream rate limit, or the call's 50-second time budget returns `nextCursor`; passing it back as `cursor` with the same `itemId` continues at the next unseen comment. The cursor carries the unvisited comments themselves, so re-ranked replies cannot shift it, and the server keeps no session state. A cursor the tool did not issue, an altered one, or one passed with a different `itemId` fails with `invalid_cursor`
59
65
  - The root carries its relationships and state: `parent` and `storyId` (the story or poll the discussion belongs to) for a comment, `poll` for a poll option, `parts` and resolved `options` (text and votes, at every depth) for a poll, and `deleted`/`dead` markers. The `content[]` heading names the type — a title, `Comment by <author>`, `Poll option by <author> on poll <id>` — and marks a deleted or dead root
60
66
  - Flat comment list carries `depth`/`parentId` for tree reconstruction, plus `childCount` and an `isOp` flag when the comment author submitted the thread's story — on a comment root, the story at `storyId`, never the comment's own author. Dead and deleted comments are left out, but their live replies are still returned at their true `depth`, with `parentId` naming the omitted comment
61
- - `truncated` and `truncationReason` (`count`, `size`, `rate_limited`, or `depth` when replies lie below the depth limit) are set only while comments remain unread — never on a terminal page, including a thread that loads in full at exactly the cap. `notice` reports deleted/dead comments omitted during traversal and the next step: the cursor, or a larger `depth` or a comment id as `itemId`, naming by ID any omitted comment whose replies the depth limit cut
62
- - Comments and poll options whose fetch failed after retries are listed in `failedIds`, and `notice` names them with the retry; failed comments ride in `nextCursor`, so paging on retries them rather than skipping them. The root item is still returned, and a failure is never passed off as a `maxComments`/`depth` limit
67
+ - `truncated` and `truncationReason` (`count`, `size`, `rate_limited`, `time`, or `depth` when replies lie below the depth limit) are set only while comments remain unread — never on a terminal page, including a thread that loads in full at exactly the cap. `notice` reports deleted/dead comments omitted during traversal and the next step: the cursor, or a larger `depth` or a comment id as `itemId`, naming by ID any omitted comment whose replies the depth limit cut
68
+ - Comments and poll options whose fetch failed after retries or ran past the call's time budget are listed in `failedIds`, and `notice` names them with the retry; failed comments ride in `nextCursor`, so paging on retries them rather than skipping them. The root item is still returned, and a failure is never passed off as a `maxComments`/`depth` limit
63
69
 
64
70
  ---
65
71
 
@@ -69,22 +75,23 @@ Hacker News feeds, threads, and profiles. Browse ranked feeds, read full comment
69
75
  - Profile includes karma, creation date, and about text (HTML stripped); submissions filter out dead/deleted items
70
76
  - A comment submission carries its `parent` and a poll-option submission its `poll`, so either links back to what it answered
71
77
  - Enrichment echoes `submissionOffset` and the offset to send next, or a notice when the requested offset is past the end of the history
72
- - Submissions whose fetch failed after retries are listed in `failedIds` with a `notice` naming the retry; a window where every submission failed returns the classified upstream error
78
+ - Submissions whose fetch failed after retries, or that the call's 50-second time budget cut, are listed in `failedIds` with a `notice` naming the retry (and the budget, when it cut them); a window where every submission failed returns the classified upstream error — `Timeout` when the budget cut them all
79
+ - Each response surface is held to a fixed 64,000-byte budget: an over-budget page keeps every submission and cuts the longest bodies to one shared length, flagging each with `textTruncated` and pointing to `hn_get_thread` for the whole text
73
80
 
74
81
  ---
75
82
 
76
83
  ### `hn_search_content` <sub>tool</sub>
77
84
 
78
- - Free-text `query` plus `tags` (`story`/`comment`/`poll`/`job`/`ask_hn`/`show_hn`/`front_page`), `author` (the exact, case-sensitive username), `storyId`, `dateRange`, and `minPoints` filters; `sort` by relevance or date; `count` (default 30) and `page` for pagination. One call serves at most 50 hits; a `count` up to 1,000 is served as the page of 50 starting at the requested page's first hit, with a `notice` naming the `count: 50` page to pass next; a non-zero `page` whose first hit begins no page of 50 fails with `count_page_unaligned`, naming a `count` and `page` that start there and, when that count is under 25, the page of 50 that holds that hit
85
+ - Free-text `query` — typo-tolerant, with the last term also matching as a prefix; wrap a term in double quotes to match it exactly and put `-` before one to exclude it — plus `tags` (`story`/`comment`/`poll`/`job`/`ask_hn`/`show_hn`/`front_page`), `author` (the exact, case-sensitive username), `storyId`, `dateRange`, and `minPoints` filters; `sort` by relevance or date; `count` (default 30) and `page` for pagination. One call serves at most 50 hits; a `count` up to 1,000 is served as the page of 50 starting at the requested page's first hit, with a `notice` naming the `count: 50` page to pass next; a non-zero `page` whose first hit begins no page of 50 fails with `count_page_unaligned`, naming a `count` and `page` that start there and, when that count is under 25, the page of 50 that holds that hit
79
86
  - `tags: "front_page"` is the front page right now (about 30 stories), never past front pages; for an earlier window, use `tags: "story"` with `dateRange` and `minPoints`
80
87
  - `query` is optional when a filter is set — list Ask HN, Show HN, polls, or jobs on their own, or pass a story's `storyId` with `tags: "comment"` to search within one thread. No query and no filter fails with `missing_query_or_filter`
81
88
  - To check whether a page was discussed, pass its URL alone as `query`: the leading `http://`, `https://`, or `www.` is dropped before searching, so submissions under either scheme, with or without `www.`, all match. The output `query` and the notice show what was searched
82
89
  - `dateRange` bounds take ISO 8601 (`YYYY`, `YYYY-MM`, `YYYY-MM-DD`, or a date-time with optional offset), read as UTC and both exclusive; an empty or out-of-order range fails with `invalid_date_range`
83
90
  - `minPoints` applies to stories and polls — comments and jobs carry no points, so pairing it with `tags: "comment"` or `"job"` fails with `min_points_unscored_type`
84
- - `view: "compact"` drops the two body-text fields (`text`, `highlights.text`), which otherwise repeat a long comment twice per hit — pass a hit id to `hn_get_thread` to read the body
91
+ - `view: "compact"` drops the two body-text fields (`text`, `highlights.text`), which otherwise repeat a long comment twice per hit — pass a hit id to `hn_get_thread` to read the body. In `full`, a page over 64,000 bytes per response surface drops `highlights.text` from every hit (`highlightsTextOmitted`), then cuts the longest bodies to one shared length (`textTruncated`, with an `hn_get_thread` pointer) — every hit stays
85
92
  - Each hit carries its `type` (`story`, `comment`, `poll`, `pollopt`, `job`) when Algolia records one. Untagged searches also return poll options, which carry only an id, author, vote count, and date — `hn_get_thread` reads an option's text and poll. A poll hit has no `storyId`; its own `id` is the discussion root
86
93
  - Job descriptions come back as `text`, though keywords don't search them
87
- - Highlight metadata (`highlights.title`, `highlights.text`, `matchedWords`) shows which terms matched and where
94
+ - Highlight metadata (`highlights.title`, `highlights.text`, `matchedWords`) shows which terms matched and where; `highlights.text` is the matched body in full with `<em>` markers
88
95
  - Enrichment echoes the applied `criteria` — each filter set (`author` trimmed, `dateRange` as the exclusive UTC bounds applied, to the second), plus `sort`, `count`, and `view` with defaults filled — so a saved result says what it answers
89
96
  - Enrichment reports `totalHits`, `page`, and the actual reachable `totalPages` — not derived from `totalHits`, since broad queries report far more hits than Algolia will serve. `totalHitsExact: false` marks `totalHits` as an estimate, and `reachableHits: 1000` marks a search that matches more than Algolia's 1,000-hit ceiling; the last page of such a search gets a notice naming the ceiling and narrower `dateRange` windows to reach the rest. While more pages remain, `truncated` is set and `notice` names the next `page`; a page past the end gets a notice naming the last valid page (or `page: 0` past the ceiling)
90
97
 
@@ -102,10 +109,10 @@ HN-specific:
102
109
 
103
110
  Agent-friendly output:
104
111
 
105
- - Graceful partial failure — an item whose fetch fails after retries is reported by ID in `failedIds`, apart from deleted, dead, or nonexistent items, rather than silently shrinking the result; `hn_get_thread` also counts the deleted and dead comments it drops, and a rate-limited item stops the batch instead of sending every remaining item through its own retry ladder
112
+ - Graceful partial failure — an item whose fetch fails after retries is reported by ID in `failedIds`, apart from deleted, dead, or nonexistent items, rather than silently shrinking the result; each `hn_get_thread`, `hn_get_stories`, and `hn_get_user` call runs under one 50-second time budget, so hung fetches cannot hold it past an MCP client's 60-second timeout; `hn_get_thread` also counts the deleted and dead comments it drops, and a rate-limited item stops the batch instead of sending every remaining item through its own retry ladder
106
113
  - Discriminated output contracts — typed error reasons (`item_not_found`, `upstream_rate_limited`, `upstream_html`, …) with per-reason recovery text (a rate limit passes on the upstream's `Retry-After` as `retryAfter`), and a `depth`/`parentId` pair on every comment so callers reconstruct the tree without guessing nesting
107
114
  - Pagination provenance — every paged listing echoes the offset it used (`offset`, `submissionOffset`, `page`) plus the exact next-offset value in `notice`, `hn_search_content` also echoes the `criteria` it searched with, and `hn_get_thread` returns a self-contained `nextCursor`, so an agent can resume without recomputing state
108
- - Response shaping — HTML stripping, URL normalization, and domain extraction remove upstream markup noise; `hn_search_content`'s `view: "compact"` drops the two body-text fields that otherwise duplicate a hit's full text
115
+ - Response shaping — HTML stripping, URL normalization, and domain extraction remove upstream markup noise; `hn_search_content`'s `view: "compact"` drops the two body-text fields that otherwise duplicate a hit's full text, and a 64,000-byte budget on each response surface cuts the longest bodies of an over-budget page
109
116
  - Fenced upstream text — in `content[]`, every line of a body is block-quoted and titles are escaped only where they could break the surrounding Markdown, so a comment cannot pass for a server-authored line
110
117
 
111
118
  ## Getting started
@@ -0,0 +1,38 @@
1
+ ---
2
+ summary: "hn_search_content, hn_get_user, and hn_get_thread hold each response surface to 64,000 bytes; hn_get_thread, hn_get_stories, and hn_get_user run under one 50-second time budget; the hn-guide agent skill is served as a skill:// resource."
3
+ breaking: false
4
+ security: false
5
+ ---
6
+
7
+ # 0.6.0 — 2026-10-28
8
+
9
+ ## Added
10
+
11
+ - **Published `hn-guide` Agent Skill**: `resources/list` now lists `skill://hn-guide/SKILL.md` and `resources/templates/list` lists the `skill://{+path}` template, served through mcp-ts-core 0.13.18's `createApp({ skills })`. The skill also ships in the npm package and the Docker image.
12
+ - **Root `plugin.json` and `mcp.json`**: the Agent Plugins 1.0.0 manifest pair, alongside the Claude and Codex plugin overlays.
13
+
14
+ ## Changed
15
+
16
+ - **`hn_search_content` `query` description** documents typo tolerance, prefix matching on the last term, `"quoted"` exact terms, and `-` exclusion. `highlights.text` is described as the matched body in full, not a snippet.
17
+ - **Framework rendering in `content[]`** (mcp-ts-core 0.13.16–0.13.18): error text and trailer text gain Markdown escapes, every line of a `notice` is block-quoted, and string trailer values such as `nextCursor` and `truncationReason` render as code spans. `structuredContent` is unchanged.
18
+ - **`.mcpb` bundle** leaves out repo-local root paths and build-only package files; the agent-doc strip is anchored at each package root so MCP SDK 2.4's `dist/ext/skills/` stays in the bundle.
19
+ - **Framework sync**: `framework-skills/` synced to 0.13.18 (adds `add-skill`), `scripts/` and `.github/ISSUE_TEMPLATE/bug_report.yml` resynced, and `bunfig.toml` `minimumReleaseAge` is 1 day (was 3).
20
+
21
+ ## Fixed
22
+
23
+ - **`hn_search_content` holds the `full` view to 64,000 bytes** on all of `content[]` and on `structuredContent`, each. An over-budget page drops `highlights.text` from every hit (`highlightsTextOmitted: true`), then cuts the longest `text` bodies to one shared limit (`textTruncated: true`, with an `hn_get_thread` pointer in `content[]`). Every hit stays, and `notice` names the budget and the cut ids. (#46)
24
+ - **`hn_get_user` holds a submissions page to the same 64,000 bytes per surface**: the longest submission bodies are cut to one shared limit and flagged `textTruncated`, every submission stays, and `user.about` is never cut. (#49)
25
+ - **`hn_get_thread` counts the whole response against its `size` stop**: `content[]` text with the trailer and `nextCursor`, and the `structuredContent` JSON, are each held to 64,000 bytes. A page's first comment is still returned whole. (#50)
26
+ - **`hn_get_thread`, `hn_get_stories`, and `hn_get_user` run under one 50-second time budget** counted from the call's start. A fetch the budget cuts or leaves unstarted is listed in `failedIds` and `notice` names the budget; `hn_get_thread` stops with `truncationReason: "time"` and a `nextCursor`, and a stories or user call where the budget cuts every fetch fails with `Timeout`. (#47)
27
+
28
+ ## Dependencies
29
+
30
+ - `@cyanheads/mcp-ts-core` ^0.13.15 → ^0.13.18 (0.13.16: Markdown-escaped error and trailer text; 0.13.17: `@modelcontextprotocol/server` 2.4; 0.13.18: `createApp({ skills })`, code-span trailer values)
31
+ - `@modelcontextprotocol/server` 2.3.1 → 2.4.0 (via the framework)
32
+ - `@modelcontextprotocol/core` 2.3.1 → 2.4.0 (transitive)
33
+ - `@opentelemetry/api` 1.9.1 → 1.10.0 (transitive)
34
+ - `jose` 6.2.12 → 6.2.13 (transitive)
35
+ - Dev: `@biomejs/biome` ^2.5.15 → ^2.6.3
36
+ - Dev: `ignore` ^7.0.12 → ^7.0.14
37
+ - Dev: `tsc-alias` ^1.9.7 → ^1.9.9
38
+ - `packageManager` bun@1.4.3 → bun@1.4.4; Docker base images `oven/bun` 1.4.3 → 1.4.4
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ await createApp({
16
16
  instructions: 'Use the hn_* tools to access Hacker News via the Firebase API and Algolia search: ranked feeds; search by keyword, by type/author/story/date/score filters alone, or both; user profiles; and items with their comment trees. Items are addressed by integer ID, reused across tools and item types (story, comment, job, poll, pollopt); passing a comment id to hn_get_thread drills into that subtree. Usernames are case-sensitive; HN sparsely populates fields, so treat absent values as unknown.',
17
17
  landing: { requireAuth: false },
18
18
  sessionMode: 'stateless',
19
+ skills: 'skills',
19
20
  setup() {
20
21
  initHnService();
21
22
  },
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,mDAAmD,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,iDAAiD,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,uDAAuD,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC;IACjD,YAAY,EACV,0eAA0e;IAC5e,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,WAAW,EAAE,WAAW;IACxB,KAAK;QACH,aAAa,EAAE,CAAC;IAClB,CAAC;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,oDAAoD,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,mDAAmD,CAAC;AAC9E,OAAO,EAAE,OAAO,EAAE,MAAM,iDAAiD,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,uDAAuD,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,eAAe;IACrB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC;IACjD,YAAY,EACV,0eAA0e;IAC5e,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;IAC/B,WAAW,EAAE,WAAW;IACxB,MAAM,EAAE,QAAQ;IAChB,KAAK;QACH,aAAa,EAAE,CAAC;IAClB,CAAC;CACF,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"get-stories.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-stories.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAkBjE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA4QrB,CAAC"}
1
+ {"version":3,"file":"get-stories.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-stories.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAoBjE,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+QrB,CAAC"}
@@ -6,7 +6,7 @@ import { tool, z } from '@cyanheads/mcp-ts-core';
6
6
  import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
7
  import { escapeInline, quoteBody } from '../markdown-escape.js';
8
8
  import { servedAtCap } from '../served-at-cap.js';
9
- import { decodeHtmlEntities, extractDomain, getHnService, normalizeUrl, settlePage, stripHtml, } from '../../../services/hn/hn-service.js';
9
+ import { CALL_BUDGET_MS, decodeHtmlEntities, extractDomain, getHnService, normalizeUrl, settlePage, stripHtml, timeBudgetCutSentence, } from '../../../services/hn/hn-service.js';
10
10
  /** Most stories one call serves; a larger `count` is served at this cap. */
11
11
  const COUNT_CAP = 100;
12
12
  /** Largest `count` accepted; anything above it is rejected. */
@@ -134,21 +134,24 @@ export const getStories = tool('hn_get_stories', {
134
134
  failedIds: z
135
135
  .array(z.number())
136
136
  .optional()
137
- .describe('IDs on this page whose fetch failed after retries. They are not deleted or missing and may load on a later call: retry the page with the same offset, or pass an ID to hn_get_thread to fetch that story alone. Absent when every item on the page loaded.'),
137
+ .describe(`IDs on this page whose fetch failed after retries or ran past the call's ${CALL_BUDGET_MS / 1000}-second time budget. They are not deleted or missing and may load on a later call: retry the page with the same offset, or pass an ID to hn_get_thread to fetch that story alone. Absent when every item on the page loaded.`),
138
138
  notice: z
139
139
  .string()
140
140
  .optional()
141
- .describe('Agent guidance: the offset to pass for the next page while more stories remain, the IDs that failed to load and how to retry them, or why a page came back empty — offset past the end of the feed, an empty feed, or every item on the page deleted or flagged — noting when count was served at its per-call cap. Absent on the last page of a non-empty result with no failed IDs.'),
141
+ .describe(`Agent guidance: the offset to pass for the next page while more stories remain, the IDs that failed to load and how to retry them (naming the call's ${CALL_BUDGET_MS / 1000}-second time budget when it cut them), or why a page came back empty — offset past the end of the feed, an empty feed, or every item on the page deleted or flagged — noting when count was served at its per-call cap. Absent on the last page of a non-empty result with no failed IDs.`),
142
142
  },
143
143
  enrichmentTrailer: {
144
144
  failedIds: { render: (ids) => `**Failed to load:** ${(ids ?? []).join(', ')}` },
145
145
  },
146
146
  async handler(input, ctx) {
147
147
  const hn = getHnService();
148
- const feedIds = await hn.fetchFeed(input.feed, ctx);
148
+ /** One budget for the feed and every item fetch after it. */
149
+ const deadline = Date.now() + CALL_BUDGET_MS;
150
+ const feedIds = await hn.fetchFeed(input.feed, ctx, deadline);
149
151
  const count = Math.min(input.count, COUNT_CAP);
150
152
  const sliced = feedIds.slice(input.offset, input.offset + count);
151
- const { items, failedIds } = settlePage(await hn.fetchItems(sliced, ctx));
153
+ const slots = await hn.fetchItems(sliced, ctx, deadline);
154
+ const { items, failedIds } = settlePage(slots);
152
155
  const stories = items.map((item) => {
153
156
  const url = normalizeUrl(item.url);
154
157
  const domain = extractDomain(url);
@@ -181,7 +184,7 @@ export const getStories = tool('hn_get_stories', {
181
184
  });
182
185
  const deadPageMessage = `No live stories on this page of the ${input.feed} feed (offset:${input.offset}, total:${total}). Items may have been deleted or flagged.`;
183
186
  const failureMessage = failedIds.length > 0
184
- ? `Could not fetch ${failedIds.length} of ${sliced.length} items on this page (id${failedIds.length === 1 ? '' : 's'} ${failedIds.join(', ')}). Retry with offset: ${input.offset}, or pass an id as itemId to hn_get_thread to fetch that story alone.`
187
+ ? `Could not fetch ${failedIds.length} of ${sliced.length} items on this page (id${failedIds.length === 1 ? '' : 's'} ${failedIds.join(', ')}).${timeBudgetCutSentence(slots)} Retry with offset: ${input.offset}, or pass an id as itemId to hn_get_thread to fetch that story alone.`
185
188
  : undefined;
186
189
  if (hasMore) {
187
190
  const raiseCount = input.count < COUNT_CAP ? `, or raise count (max ${COUNT_CAP})` : '';
@@ -1 +1 @@
1
- {"version":3,"file":"get-stories.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-stories.tool.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"get-stories.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-stories.tool.ts"],"names":[],"mappings":"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"}
@@ -100,6 +100,7 @@ export declare const getThread: import("@cyanheads/mcp-ts-core").ToolDefinition<
100
100
  depth: "depth";
101
101
  rate_limited: "rate_limited";
102
102
  size: "size";
103
+ time: "time";
103
104
  }>>;
104
105
  readonly nextCursor: z.ZodOptional<z.ZodString>;
105
106
  readonly shown: z.ZodOptional<z.ZodNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"get-thread.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-thread.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAiB,MAAM,+BAA+B,CAAC;AAmfhF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsZpB,CAAC"}
1
+ {"version":3,"file":"get-thread.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-thread.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAsB,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAiB,MAAM,+BAA+B,CAAC;AA4nBhF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmcpB,CAAC"}