@cyanheads/hn-mcp-server 0.5.12 → 0.5.14
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.
- package/CLAUDE.md +17 -11
- package/Dockerfile +13 -7
- package/README.md +6 -4
- package/changelog/0.1.x/0.1.0.md +31 -0
- package/changelog/0.1.x/0.1.1.md +16 -0
- package/changelog/0.1.x/0.1.2.md +12 -0
- package/changelog/0.1.x/0.1.3.md +12 -0
- package/changelog/0.1.x/0.1.4.md +15 -0
- package/changelog/0.1.x/0.1.5.md +14 -0
- package/changelog/0.1.x/0.1.6.md +24 -0
- package/changelog/0.1.x/0.1.7.md +11 -0
- package/changelog/0.1.x/0.1.8.md +11 -0
- package/changelog/0.1.x/0.1.9.md +20 -0
- package/changelog/0.2.x/0.2.0.md +28 -0
- package/changelog/0.3.x/0.3.0.md +18 -0
- package/changelog/0.4.x/0.4.0.md +21 -0
- package/changelog/0.4.x/0.4.2.md +36 -0
- package/changelog/0.4.x/0.4.3.md +23 -0
- package/changelog/0.5.x/0.5.0.md +27 -0
- package/changelog/0.5.x/0.5.1.md +24 -0
- package/changelog/0.5.x/0.5.10.md +24 -0
- package/changelog/0.5.x/0.5.11.md +27 -0
- package/changelog/0.5.x/0.5.12.md +34 -0
- package/changelog/0.5.x/0.5.13.md +18 -0
- package/changelog/0.5.x/0.5.14.md +29 -0
- package/changelog/0.5.x/0.5.2.md +28 -0
- package/changelog/0.5.x/0.5.3.md +27 -0
- package/changelog/0.5.x/0.5.4.md +19 -0
- package/changelog/0.5.x/0.5.5.md +11 -0
- package/changelog/0.5.x/0.5.6.md +25 -0
- package/changelog/0.5.x/0.5.7.md +16 -0
- package/changelog/0.5.x/0.5.8.md +17 -0
- package/changelog/0.5.x/0.5.9.md +11 -0
- package/changelog/template.md +168 -0
- package/dist/mcp-server/tools/definitions/get-stories.tool.d.ts +31 -1
- package/dist/mcp-server/tools/definitions/get-stories.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-stories.tool.js +37 -0
- package/dist/mcp-server/tools/definitions/get-stories.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-thread.tool.d.ts +29 -0
- package/dist/mcp-server/tools/definitions/get-thread.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-thread.tool.js +34 -0
- package/dist/mcp-server/tools/definitions/get-thread.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/get-user.tool.d.ts +31 -0
- package/dist/mcp-server/tools/definitions/get-user.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/get-user.tool.js +66 -15
- package/dist/mcp-server/tools/definitions/get-user.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/search-content.tool.d.ts +35 -1
- package/dist/mcp-server/tools/definitions/search-content.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/search-content.tool.js +56 -7
- package/dist/mcp-server/tools/definitions/search-content.tool.js.map +1 -1
- package/dist/services/hn/hn-service.d.ts +6 -0
- package/dist/services/hn/hn-service.d.ts.map +1 -1
- package/dist/services/hn/hn-service.js +103 -52
- package/dist/services/hn/hn-service.js.map +1 -1
- package/package.json +32 -22
- package/server.json +5 -5
|
@@ -2,34 +2,12 @@
|
|
|
2
2
|
* @fileoverview HN API service — Firebase and Algolia HTTP clients with HTML stripping utilities.
|
|
3
3
|
* @module services/hn/hn-service
|
|
4
4
|
*/
|
|
5
|
-
import { serviceUnavailable } from '@cyanheads/mcp-ts-core/errors';
|
|
5
|
+
import { invalidParams, McpError, rateLimited, serviceUnavailable, } from '@cyanheads/mcp-ts-core/errors';
|
|
6
6
|
import { fetchWithTimeout, withRetry } from '@cyanheads/mcp-ts-core/utils';
|
|
7
7
|
import { getServerConfig } from '../../config/server-config.js';
|
|
8
8
|
const HN_API = 'https://hacker-news.firebaseio.com/v0';
|
|
9
9
|
const ALGOLIA_API = 'https://hn.algolia.com/api/v1';
|
|
10
10
|
const REQUEST_TIMEOUT_MS = 10_000;
|
|
11
|
-
/**
|
|
12
|
-
* Project a handler Context onto a RequestContext shape for framework
|
|
13
|
-
* utilities. Runtime-safe — the framework only reads log-binding fields —
|
|
14
|
-
* but RequestContext's `[key: string]: unknown` index signature is not
|
|
15
|
-
* present on Context, so we materialize a plain object whose literal shape
|
|
16
|
-
* satisfies the index signature.
|
|
17
|
-
*/
|
|
18
|
-
function toRequestContext(ctx) {
|
|
19
|
-
const base = {
|
|
20
|
-
requestId: ctx.requestId,
|
|
21
|
-
timestamp: ctx.timestamp,
|
|
22
|
-
};
|
|
23
|
-
if (ctx.tenantId !== undefined)
|
|
24
|
-
base.tenantId = ctx.tenantId;
|
|
25
|
-
if (ctx.traceId !== undefined)
|
|
26
|
-
base.traceId = ctx.traceId;
|
|
27
|
-
if (ctx.spanId !== undefined)
|
|
28
|
-
base.spanId = ctx.spanId;
|
|
29
|
-
if (ctx.auth !== undefined)
|
|
30
|
-
base.auth = ctx.auth;
|
|
31
|
-
return base;
|
|
32
|
-
}
|
|
33
11
|
// ---------------------------------------------------------------------------
|
|
34
12
|
// HTML utilities
|
|
35
13
|
// ---------------------------------------------------------------------------
|
|
@@ -111,8 +89,10 @@ function isHtmlErrorBody(text) {
|
|
|
111
89
|
return /^\s*<(!DOCTYPE\s+html|html[\s>])/i.test(text);
|
|
112
90
|
}
|
|
113
91
|
/**
|
|
114
|
-
* Parse an HN/Algolia JSON body
|
|
115
|
-
*
|
|
92
|
+
* Parse an HN/Algolia JSON body. Both ways a 200 response can still be unusable
|
|
93
|
+
* — an HTML error page (common during upstream rate limiting) and a body that
|
|
94
|
+
* is not JSON at all — throw a classified error rather than a raw `SyntaxError`,
|
|
95
|
+
* whose message quotes the offending body back at the client.
|
|
116
96
|
*/
|
|
117
97
|
function parseJsonBody(text, upstream) {
|
|
118
98
|
if (isHtmlErrorBody(text)) {
|
|
@@ -124,7 +104,87 @@ function parseJsonBody(text, upstream) {
|
|
|
124
104
|
},
|
|
125
105
|
});
|
|
126
106
|
}
|
|
127
|
-
|
|
107
|
+
try {
|
|
108
|
+
return JSON.parse(text);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
throw serviceUnavailable(`${upstream} returned a body that is not JSON.`, {
|
|
112
|
+
upstream,
|
|
113
|
+
reason: 'upstream_malformed',
|
|
114
|
+
recovery: {
|
|
115
|
+
hint: `${upstream} answered 200 with a body that is not its API response. Retry after a brief delay; no input change helps while the upstream is serving something else.`,
|
|
116
|
+
},
|
|
117
|
+
}, { cause: err });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Map an upstream HTTP status onto a contract reason, its JSON-RPC code, and
|
|
122
|
+
* the agent's next move. Three buckets: 429 is a pacing problem, 5xx is an
|
|
123
|
+
* upstream outage, and every other 4xx means the request itself was rejected
|
|
124
|
+
* and will be rejected again unchanged.
|
|
125
|
+
*
|
|
126
|
+
* The code travels with the reason rather than being taken from the framework's
|
|
127
|
+
* finer-grained status ladder, so each reason surfaces exactly the code its
|
|
128
|
+
* tools declare in `errors[]`. That ladder would otherwise split one reason
|
|
129
|
+
* across several codes — HN answers an unknown path with 401, and a 500 or 504
|
|
130
|
+
* would arrive as `InternalError` or `Timeout`, blurring the two codes that
|
|
131
|
+
* mean "this server broke" and "this server's own fetch timed out". The exact
|
|
132
|
+
* status stays on `data.status`.
|
|
133
|
+
*/
|
|
134
|
+
function upstreamFailureFor(upstream, status, cause) {
|
|
135
|
+
if (status === 429) {
|
|
136
|
+
return rateLimited(`${upstream} returned HTTP ${status}.`, {
|
|
137
|
+
upstream,
|
|
138
|
+
status,
|
|
139
|
+
reason: 'upstream_rate_limited',
|
|
140
|
+
recovery: {
|
|
141
|
+
hint: `${upstream} is rate-limiting this server. Wait several seconds before retrying the same call, and reduce how often it is called.`,
|
|
142
|
+
},
|
|
143
|
+
}, { cause });
|
|
144
|
+
}
|
|
145
|
+
if (status >= 500) {
|
|
146
|
+
return serviceUnavailable(`${upstream} returned HTTP ${status}.`, {
|
|
147
|
+
upstream,
|
|
148
|
+
status,
|
|
149
|
+
reason: 'upstream_unavailable',
|
|
150
|
+
recovery: {
|
|
151
|
+
hint: `${upstream} is failing or in maintenance. Retry after a short delay — no input change helps while the upstream is down.`,
|
|
152
|
+
},
|
|
153
|
+
}, { cause });
|
|
154
|
+
}
|
|
155
|
+
return invalidParams(`${upstream} returned HTTP ${status}.`, {
|
|
156
|
+
upstream,
|
|
157
|
+
status,
|
|
158
|
+
reason: 'upstream_rejected',
|
|
159
|
+
recovery: {
|
|
160
|
+
hint: `${upstream} rejected the request as malformed. Check the tool input values against the schema; retrying the same input fails identically.`,
|
|
161
|
+
},
|
|
162
|
+
}, { cause });
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Translate `fetchWithTimeout`'s raw non-2xx throw into a classified error
|
|
166
|
+
* carrying `reason` and `recovery.hint`, matching the shape `parseJsonBody`
|
|
167
|
+
* already produces for HTML error bodies.
|
|
168
|
+
*
|
|
169
|
+
* Wraps *outside* `withRetry` so the framework's own classification is left
|
|
170
|
+
* untouched: retry eligibility, `Retry-After` honoring, timeouts, and caller
|
|
171
|
+
* aborts all resolve against the original error, and only HTTP-status failures
|
|
172
|
+
* (`errorSource: 'FetchHttpError'`) are rewritten. The message drops the
|
|
173
|
+
* upstream URL and the verbatim response body, which stay on the `cause` chain
|
|
174
|
+
* for server-side logs.
|
|
175
|
+
*/
|
|
176
|
+
async function withUpstreamHttpErrors(upstream, fn) {
|
|
177
|
+
try {
|
|
178
|
+
return await fn();
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
if (!(err instanceof McpError) || err.data?.errorSource !== 'FetchHttpError')
|
|
182
|
+
throw err;
|
|
183
|
+
const status = err.data.status;
|
|
184
|
+
if (typeof status !== 'number')
|
|
185
|
+
throw err;
|
|
186
|
+
throw upstreamFailureFor(upstream, status, err);
|
|
187
|
+
}
|
|
128
188
|
}
|
|
129
189
|
// ---------------------------------------------------------------------------
|
|
130
190
|
// Service
|
|
@@ -134,15 +194,22 @@ export class HnService {
|
|
|
134
194
|
constructor(concurrencyLimit) {
|
|
135
195
|
this.concurrencyLimit = concurrencyLimit;
|
|
136
196
|
}
|
|
137
|
-
/**
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
197
|
+
/**
|
|
198
|
+
* GET a JSON document from an upstream, with retries around the full
|
|
199
|
+
* fetch-and-parse pipeline and both failure shapes — non-2xx status and
|
|
200
|
+
* HTML-instead-of-JSON — classified onto a contract reason.
|
|
201
|
+
*/
|
|
202
|
+
getJson(upstream, operation, url, ctx) {
|
|
203
|
+
return withUpstreamHttpErrors(upstream, () => withRetry(async () => {
|
|
204
|
+
const res = await fetchWithTimeout(url, REQUEST_TIMEOUT_MS, ctx, {
|
|
142
205
|
signal: ctx.signal,
|
|
143
206
|
});
|
|
144
|
-
return parseJsonBody(await res.text(),
|
|
145
|
-
}, { operation
|
|
207
|
+
return parseJsonBody(await res.text(), upstream);
|
|
208
|
+
}, { operation, context: ctx, signal: ctx.signal }));
|
|
209
|
+
}
|
|
210
|
+
/** Fetch a single item by ID. Returns null when HN reports the item is missing. */
|
|
211
|
+
fetchItem(id, ctx) {
|
|
212
|
+
return this.getJson('HN API', 'hn.fetchItem', `${HN_API}/item/${id}.json`, ctx);
|
|
146
213
|
}
|
|
147
214
|
/**
|
|
148
215
|
* Fetch a user profile by username. Returns null when the user does not exist.
|
|
@@ -151,22 +218,12 @@ export class HnService {
|
|
|
151
218
|
* route and resolve to a different Firebase resource.
|
|
152
219
|
*/
|
|
153
220
|
fetchUser(username, ctx) {
|
|
154
|
-
|
|
155
|
-
return withRetry(async () => {
|
|
156
|
-
const res = await fetchWithTimeout(`${HN_API}/user/${encodeURIComponent(username)}.json`, REQUEST_TIMEOUT_MS, rc, { signal: ctx.signal });
|
|
157
|
-
return parseJsonBody(await res.text(), 'HN API');
|
|
158
|
-
}, { operation: 'hn.fetchUser', context: rc, signal: ctx.signal });
|
|
221
|
+
return this.getJson('HN API', 'hn.fetchUser', `${HN_API}/user/${encodeURIComponent(username)}.json`, ctx);
|
|
159
222
|
}
|
|
160
223
|
/** Fetch a feed's ID array. Throws on upstream failure after retries. */
|
|
161
224
|
fetchFeed(type, ctx) {
|
|
162
225
|
const endpoint = type === 'jobs' ? 'jobstories' : `${type}stories`;
|
|
163
|
-
|
|
164
|
-
return withRetry(async () => {
|
|
165
|
-
const res = await fetchWithTimeout(`${HN_API}/${endpoint}.json`, REQUEST_TIMEOUT_MS, rc, {
|
|
166
|
-
signal: ctx.signal,
|
|
167
|
-
});
|
|
168
|
-
return parseJsonBody(await res.text(), 'HN API');
|
|
169
|
-
}, { operation: 'hn.fetchFeed', context: rc, signal: ctx.signal });
|
|
226
|
+
return this.getJson('HN API', 'hn.fetchFeed', `${HN_API}/${endpoint}.json`, ctx);
|
|
170
227
|
}
|
|
171
228
|
/**
|
|
172
229
|
* Batch-fetch items with concurrency limiting. Preserves input order.
|
|
@@ -224,13 +281,7 @@ export class HnService {
|
|
|
224
281
|
}
|
|
225
282
|
if (numericFilters.length)
|
|
226
283
|
url.searchParams.set('numericFilters', numericFilters.join(','));
|
|
227
|
-
|
|
228
|
-
return withRetry(async () => {
|
|
229
|
-
const res = await fetchWithTimeout(url, REQUEST_TIMEOUT_MS, rc, {
|
|
230
|
-
signal: ctx.signal,
|
|
231
|
-
});
|
|
232
|
-
return parseJsonBody(await res.text(), 'Algolia');
|
|
233
|
-
}, { operation: 'hn.search', context: rc, signal: ctx.signal });
|
|
284
|
+
return this.getJson('Algolia', 'hn.search', url, ctx);
|
|
234
285
|
}
|
|
235
286
|
}
|
|
236
287
|
// ---------------------------------------------------------------------------
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hn-service.js","sourceRoot":"","sources":["../../../src/services/hn/hn-service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,
|
|
1
|
+
{"version":3,"file":"hn-service.js","sourceRoot":"","sources":["../../../src/services/hn/hn-service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/hn-mcp-server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
4
4
|
"description": "Browse Hacker News feeds, threads, and user profiles with full-text search via MCP. STDIO or Streamable HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,24 +8,34 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"hn-mcp-server": "dist/index.js"
|
|
10
10
|
},
|
|
11
|
-
"files": [
|
|
11
|
+
"files": [
|
|
12
|
+
"changelog/",
|
|
13
|
+
"dist/",
|
|
14
|
+
"README.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"CLAUDE.md",
|
|
17
|
+
"Dockerfile",
|
|
18
|
+
"server.json"
|
|
19
|
+
],
|
|
12
20
|
"scripts": {
|
|
13
|
-
"build": "bun scripts/build.ts",
|
|
14
|
-
"rebuild": "bun scripts/clean.ts && bun scripts/build.ts",
|
|
15
|
-
"clean": "bun scripts/clean.ts",
|
|
16
|
-
"devcheck": "bun scripts/devcheck.ts",
|
|
17
|
-
"tree": "bun scripts/tree.ts",
|
|
21
|
+
"build": "bun run scripts/build.ts",
|
|
22
|
+
"rebuild": "bun run scripts/clean.ts && bun run scripts/build.ts",
|
|
23
|
+
"clean": "bun run scripts/clean.ts",
|
|
24
|
+
"devcheck": "bun run scripts/devcheck.ts",
|
|
25
|
+
"tree": "bun run scripts/tree.ts",
|
|
18
26
|
"format": "biome check --write .",
|
|
19
27
|
"format:unsafe": "biome check --write --unsafe .",
|
|
20
|
-
"list-skills": "bun scripts/list-skills.ts",
|
|
21
|
-
"lint:mcp": "bun scripts/lint-mcp.ts",
|
|
22
|
-
"lint:packaging": "bun scripts/lint-packaging.ts",
|
|
28
|
+
"list-skills": "bun run scripts/list-skills.ts",
|
|
29
|
+
"lint:mcp": "bun run scripts/lint-mcp.ts",
|
|
30
|
+
"lint:packaging": "bun run scripts/lint-packaging.ts",
|
|
23
31
|
"bundle": "bun run build && npx -y @anthropic-ai/mcpb pack . dist/hn-mcp-server.mcpb && bun run scripts/clean-mcpb.ts dist/hn-mcp-server.mcpb",
|
|
24
32
|
"audit:refresh": "rm -f bun.lock && bun install && bun audit",
|
|
25
|
-
"changelog:build": "bun scripts/build-changelog.ts",
|
|
26
|
-
"changelog:check": "bun scripts/build-changelog.ts --check",
|
|
27
|
-
"release:github": "bun scripts/release-github.ts",
|
|
28
|
-
"test": "
|
|
33
|
+
"changelog:build": "bun run scripts/build-changelog.ts",
|
|
34
|
+
"changelog:check": "bun run scripts/build-changelog.ts --check",
|
|
35
|
+
"release:github": "bun run scripts/release-github.ts",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:coverage": "vitest run --coverage",
|
|
38
|
+
"start": "node dist/index.js",
|
|
29
39
|
"start:stdio": "MCP_TRANSPORT_TYPE=stdio node dist/index.js",
|
|
30
40
|
"start:http": "MCP_TRANSPORT_TYPE=http node dist/index.js",
|
|
31
41
|
"publish-mcp": "mcp-publisher login github -token \"$(security find-generic-password -a \"$USER\" -s mcp-publisher-github-pat -w)\" && mcp-publisher publish"
|
|
@@ -68,24 +78,24 @@
|
|
|
68
78
|
"node": ">=24.0.0",
|
|
69
79
|
"bun": ">=1.3.0"
|
|
70
80
|
},
|
|
71
|
-
"packageManager": "bun@1.
|
|
81
|
+
"packageManager": "bun@1.4.0",
|
|
72
82
|
"publishConfig": {
|
|
73
83
|
"access": "public"
|
|
74
84
|
},
|
|
75
85
|
"dependencies": {
|
|
76
|
-
"@cyanheads/mcp-ts-core": "^0.
|
|
86
|
+
"@cyanheads/mcp-ts-core": "^0.12.3",
|
|
87
|
+
"pino-pretty": "^13.1.3",
|
|
77
88
|
"zod": "^4.4.3"
|
|
78
89
|
},
|
|
79
90
|
"devDependencies": {
|
|
80
|
-
"@biomejs/biome": "^2.5.
|
|
81
|
-
"@hono/otel": "^1.1.2",
|
|
91
|
+
"@biomejs/biome": "^2.5.10",
|
|
82
92
|
"@socketsecurity/bun-security-scanner": "^1.1.2",
|
|
83
|
-
"@types/node": "^26.
|
|
93
|
+
"@types/node": "^26.2.0",
|
|
94
|
+
"@vitest/coverage-istanbul": "^4.1.11",
|
|
84
95
|
"depcheck": "^1.4.7",
|
|
85
96
|
"ignore": "^7.0.6",
|
|
86
|
-
"
|
|
87
|
-
"tsc-alias": "^1.9.1",
|
|
97
|
+
"tsc-alias": "^1.9.2",
|
|
88
98
|
"typescript": "^7.0.2",
|
|
89
|
-
"vitest": "^4.1.
|
|
99
|
+
"vitest": "^4.1.11"
|
|
90
100
|
}
|
|
91
101
|
}
|
package/server.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"url": "https://github.com/cyanheads/hn-mcp-server",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.5.
|
|
9
|
+
"version": "0.5.14",
|
|
10
10
|
"remotes": [
|
|
11
11
|
{
|
|
12
12
|
"type": "streamable-http",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
20
20
|
"identifier": "@cyanheads/hn-mcp-server",
|
|
21
21
|
"runtimeHint": "bun",
|
|
22
|
-
"version": "0.5.
|
|
22
|
+
"version": "0.5.14",
|
|
23
23
|
"packageArguments": [
|
|
24
24
|
{
|
|
25
25
|
"type": "positional",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"name": "MCP_LOG_LEVEL",
|
|
43
|
-
"description": "Sets the minimum log level for output (e.g., 'debug', 'info', '
|
|
43
|
+
"description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warning').",
|
|
44
44
|
"format": "string",
|
|
45
45
|
"isRequired": false,
|
|
46
46
|
"default": "info"
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
56
56
|
"identifier": "@cyanheads/hn-mcp-server",
|
|
57
57
|
"runtimeHint": "bun",
|
|
58
|
-
"version": "0.5.
|
|
58
|
+
"version": "0.5.14",
|
|
59
59
|
"packageArguments": [
|
|
60
60
|
{
|
|
61
61
|
"type": "positional",
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
"name": "MCP_LOG_LEVEL",
|
|
107
|
-
"description": "Sets the minimum log level for output (e.g., 'debug', 'info', '
|
|
107
|
+
"description": "Sets the minimum log level for output (e.g., 'debug', 'info', 'warning').",
|
|
108
108
|
"format": "string",
|
|
109
109
|
"isRequired": false,
|
|
110
110
|
"default": "info"
|