@fre4x/hn 1.0.51 → 1.0.54
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/dist/index.js +198 -73
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -42026,6 +42026,14 @@ function applyPagination(items, params) {
|
|
|
42026
42026
|
};
|
|
42027
42027
|
}
|
|
42028
42028
|
|
|
42029
|
+
// ../packages/shared/dist/package.js
|
|
42030
|
+
import { createRequire as createJsonRequire } from "node:module";
|
|
42031
|
+
function getPackageVersion(moduleUrl) {
|
|
42032
|
+
const require2 = createJsonRequire(moduleUrl);
|
|
42033
|
+
const packageJson = require2("../package.json");
|
|
42034
|
+
return packageJson.version ?? "0.0.0";
|
|
42035
|
+
}
|
|
42036
|
+
|
|
42029
42037
|
// src/api.ts
|
|
42030
42038
|
import https2 from "node:https";
|
|
42031
42039
|
|
|
@@ -45806,6 +45814,46 @@ var {
|
|
|
45806
45814
|
mergeConfig: mergeConfig2
|
|
45807
45815
|
} = axios_default;
|
|
45808
45816
|
|
|
45817
|
+
// src/mock.ts
|
|
45818
|
+
function isMock() {
|
|
45819
|
+
return process.env.MOCK === "true" || process.env.HN_MOCK === "true";
|
|
45820
|
+
}
|
|
45821
|
+
var MOCK_ITEM = {
|
|
45822
|
+
id: 1001,
|
|
45823
|
+
type: "story",
|
|
45824
|
+
by: "fre4x",
|
|
45825
|
+
time: 1712232e3,
|
|
45826
|
+
title: "Mock HN Story",
|
|
45827
|
+
url: "https://example.com/mock-hn-story",
|
|
45828
|
+
score: 42,
|
|
45829
|
+
descendants: 7,
|
|
45830
|
+
kids: [1002, 1003],
|
|
45831
|
+
text: "Mock story content for offline development."
|
|
45832
|
+
};
|
|
45833
|
+
var MOCK_USER = {
|
|
45834
|
+
id: "fre4x",
|
|
45835
|
+
created: 16e8,
|
|
45836
|
+
karma: 1337,
|
|
45837
|
+
about: "Mock Hacker News user for MCP development.",
|
|
45838
|
+
submitted: [1001, 1002, 1003]
|
|
45839
|
+
};
|
|
45840
|
+
var MOCK_UPDATES = {
|
|
45841
|
+
items: [1003, 1002, 1001],
|
|
45842
|
+
profiles: ["fre4x", "b1te"]
|
|
45843
|
+
};
|
|
45844
|
+
var MOCK_FIXTURES = {
|
|
45845
|
+
item: MOCK_ITEM,
|
|
45846
|
+
user: MOCK_USER,
|
|
45847
|
+
maxItem: 1003,
|
|
45848
|
+
topStories: [1003, 1002, 1001],
|
|
45849
|
+
newStories: [1003, 1002, 1001],
|
|
45850
|
+
bestStories: [1001, 1003, 1002],
|
|
45851
|
+
askStories: [2003, 2002, 2001],
|
|
45852
|
+
showStories: [3003, 3002, 3001],
|
|
45853
|
+
jobStories: [4003, 4002, 4001],
|
|
45854
|
+
updates: MOCK_UPDATES
|
|
45855
|
+
};
|
|
45856
|
+
|
|
45809
45857
|
// src/api.ts
|
|
45810
45858
|
var BASE_URL = "https://hacker-news.firebaseio.com/v0";
|
|
45811
45859
|
var httpClient = axios_default.create({
|
|
@@ -45842,36 +45890,69 @@ var HNApiClient = class {
|
|
|
45842
45890
|
return response.data;
|
|
45843
45891
|
}
|
|
45844
45892
|
async getItem(id) {
|
|
45893
|
+
if (isMock()) {
|
|
45894
|
+
return { ...MOCK_FIXTURES.item, id };
|
|
45895
|
+
}
|
|
45845
45896
|
const response = await httpClient.get(`/item/${id}.json`);
|
|
45846
45897
|
return response.data;
|
|
45847
45898
|
}
|
|
45848
45899
|
async getUser(id) {
|
|
45900
|
+
if (isMock()) {
|
|
45901
|
+
return { ...MOCK_FIXTURES.user, id };
|
|
45902
|
+
}
|
|
45849
45903
|
const response = await httpClient.get(`/user/${id}.json`);
|
|
45850
45904
|
return response.data;
|
|
45851
45905
|
}
|
|
45852
45906
|
async getMaxItem() {
|
|
45907
|
+
if (isMock()) {
|
|
45908
|
+
return MOCK_FIXTURES.maxItem;
|
|
45909
|
+
}
|
|
45853
45910
|
const response = await httpClient.get(`/maxitem.json`);
|
|
45854
45911
|
return response.data;
|
|
45855
45912
|
}
|
|
45856
45913
|
async getTopStories() {
|
|
45914
|
+
if (isMock()) {
|
|
45915
|
+
return [...MOCK_FIXTURES.topStories];
|
|
45916
|
+
}
|
|
45857
45917
|
return this.fetchWithCache(`/topstories`);
|
|
45858
45918
|
}
|
|
45859
45919
|
async getNewStories() {
|
|
45920
|
+
if (isMock()) {
|
|
45921
|
+
return [...MOCK_FIXTURES.newStories];
|
|
45922
|
+
}
|
|
45860
45923
|
return this.fetchWithCache(`/newstories`);
|
|
45861
45924
|
}
|
|
45862
45925
|
async getBestStories() {
|
|
45926
|
+
if (isMock()) {
|
|
45927
|
+
return [...MOCK_FIXTURES.bestStories];
|
|
45928
|
+
}
|
|
45863
45929
|
return this.fetchWithCache(`/beststories`);
|
|
45864
45930
|
}
|
|
45865
45931
|
async getAskStories() {
|
|
45932
|
+
if (isMock()) {
|
|
45933
|
+
return [...MOCK_FIXTURES.askStories];
|
|
45934
|
+
}
|
|
45866
45935
|
return this.fetchWithCache(`/askstories`);
|
|
45867
45936
|
}
|
|
45868
45937
|
async getShowStories() {
|
|
45938
|
+
if (isMock()) {
|
|
45939
|
+
return [...MOCK_FIXTURES.showStories];
|
|
45940
|
+
}
|
|
45869
45941
|
return this.fetchWithCache(`/showstories`);
|
|
45870
45942
|
}
|
|
45871
45943
|
async getJobStories() {
|
|
45944
|
+
if (isMock()) {
|
|
45945
|
+
return [...MOCK_FIXTURES.jobStories];
|
|
45946
|
+
}
|
|
45872
45947
|
return this.fetchWithCache(`/jobstories`);
|
|
45873
45948
|
}
|
|
45874
45949
|
async getUpdates() {
|
|
45950
|
+
if (isMock()) {
|
|
45951
|
+
return {
|
|
45952
|
+
items: [...MOCK_FIXTURES.updates.items],
|
|
45953
|
+
profiles: [...MOCK_FIXTURES.updates.profiles]
|
|
45954
|
+
};
|
|
45955
|
+
}
|
|
45875
45956
|
const response = await httpClient.get(`/updates.json`);
|
|
45876
45957
|
return response.data;
|
|
45877
45958
|
}
|
|
@@ -45887,22 +45968,28 @@ var CHARACTER_LIMIT = 12e3;
|
|
|
45887
45968
|
|
|
45888
45969
|
// src/index.ts
|
|
45889
45970
|
var api = new HNApiClient();
|
|
45971
|
+
var PACKAGE_VERSION = getPackageVersion(import.meta.url);
|
|
45890
45972
|
var server = new McpServer({
|
|
45891
45973
|
name: "hackernews-mcp-server",
|
|
45892
|
-
version:
|
|
45974
|
+
version: PACKAGE_VERSION
|
|
45893
45975
|
});
|
|
45894
45976
|
function formatItemMarkdown(item) {
|
|
45895
45977
|
if (!item) return "Item not found.";
|
|
45896
45978
|
const lines = [];
|
|
45897
45979
|
if (item.title) lines.push(`# ${item.title}
|
|
45898
45980
|
`);
|
|
45899
|
-
lines.push(
|
|
45900
|
-
[
|
|
45901
|
-
|
|
45902
|
-
|
|
45903
|
-
|
|
45904
|
-
|
|
45905
|
-
|
|
45981
|
+
lines.push(
|
|
45982
|
+
formatFields([
|
|
45983
|
+
["By", item.by],
|
|
45984
|
+
[
|
|
45985
|
+
"Time",
|
|
45986
|
+
item.time ? new Date(item.time * 1e3).toLocaleString() : void 0
|
|
45987
|
+
],
|
|
45988
|
+
["Score", item.score],
|
|
45989
|
+
["URL", item.url],
|
|
45990
|
+
["Comments", item.descendants]
|
|
45991
|
+
])
|
|
45992
|
+
);
|
|
45906
45993
|
if (item.text) lines.push(`
|
|
45907
45994
|
${item.text}`);
|
|
45908
45995
|
if (item.kids && item.kids.length > 0) {
|
|
@@ -45918,18 +46005,17 @@ function handleLimit(content, format) {
|
|
|
45918
46005
|
|
|
45919
46006
|
> [!NOTE]
|
|
45920
46007
|
> Content truncated due to size limit. Use pagination (limit/offset) to fetch specific parts if applicable.`;
|
|
45921
|
-
} else {
|
|
45922
|
-
return JSON.stringify({
|
|
45923
|
-
content: content.substring(0, CHARACTER_LIMIT),
|
|
45924
|
-
truncated: true,
|
|
45925
|
-
hint: "Content truncated due to size limit. Use limit/offset for lists."
|
|
45926
|
-
});
|
|
45927
46008
|
}
|
|
46009
|
+
return JSON.stringify({
|
|
46010
|
+
content: content.substring(0, CHARACTER_LIMIT),
|
|
46011
|
+
truncated: true,
|
|
46012
|
+
hint: "Content truncated due to size limit. Use limit/offset for lists."
|
|
46013
|
+
});
|
|
45928
46014
|
}
|
|
45929
46015
|
var storySchema = paginationSchema.extend({
|
|
45930
46016
|
response_format: external_exports3.enum(Object.values(ResponseFormat)).default("markdown" /* MARKDOWN */).describe("Output format (markdown or json)")
|
|
45931
46017
|
});
|
|
45932
|
-
async function
|
|
46018
|
+
async function handleHNStories(ids, params) {
|
|
45933
46019
|
const paginated = applyPagination(ids, params);
|
|
45934
46020
|
const result = {
|
|
45935
46021
|
total: paginated.total,
|
|
@@ -45957,7 +46043,11 @@ async function handleStories(ids, params) {
|
|
|
45957
46043
|
` + formatListItems(
|
|
45958
46044
|
paginated.items,
|
|
45959
46045
|
(id, i) => `${i + 1 + paginated.offset}. [${id}](https://news.ycombinator.com/item?id=${id})`
|
|
45960
|
-
) + (paginated.hasMore ? formatPaginationFooter(
|
|
46046
|
+
) + (paginated.hasMore ? formatPaginationFooter(
|
|
46047
|
+
paginated.offset,
|
|
46048
|
+
paginated.limit,
|
|
46049
|
+
paginated.total
|
|
46050
|
+
) : "");
|
|
45961
46051
|
return {
|
|
45962
46052
|
content: [
|
|
45963
46053
|
{
|
|
@@ -45967,6 +46057,45 @@ async function handleStories(ids, params) {
|
|
|
45967
46057
|
]
|
|
45968
46058
|
};
|
|
45969
46059
|
}
|
|
46060
|
+
async function handleHNGetTopStories(params) {
|
|
46061
|
+
try {
|
|
46062
|
+
return await handleHNStories(await api.getTopStories(), params);
|
|
46063
|
+
} catch (error48) {
|
|
46064
|
+
return createInternalError(error48);
|
|
46065
|
+
}
|
|
46066
|
+
}
|
|
46067
|
+
async function handleHNGetItem(params) {
|
|
46068
|
+
try {
|
|
46069
|
+
const item = await api.getItem(params.id);
|
|
46070
|
+
if (params.response_format === "json" /* JSON */) {
|
|
46071
|
+
return {
|
|
46072
|
+
content: [
|
|
46073
|
+
{
|
|
46074
|
+
type: "text",
|
|
46075
|
+
text: handleLimit(
|
|
46076
|
+
JSON.stringify(item, null, 2),
|
|
46077
|
+
"json" /* JSON */
|
|
46078
|
+
)
|
|
46079
|
+
}
|
|
46080
|
+
],
|
|
46081
|
+
structuredContent: item
|
|
46082
|
+
};
|
|
46083
|
+
}
|
|
46084
|
+
return {
|
|
46085
|
+
content: [
|
|
46086
|
+
{
|
|
46087
|
+
type: "text",
|
|
46088
|
+
text: handleLimit(
|
|
46089
|
+
formatItemMarkdown(item),
|
|
46090
|
+
"markdown" /* MARKDOWN */
|
|
46091
|
+
)
|
|
46092
|
+
}
|
|
46093
|
+
]
|
|
46094
|
+
};
|
|
46095
|
+
} catch (error48) {
|
|
46096
|
+
return createInternalError(error48);
|
|
46097
|
+
}
|
|
46098
|
+
}
|
|
45970
46099
|
server.registerTool(
|
|
45971
46100
|
"hn_get_top_stories",
|
|
45972
46101
|
{
|
|
@@ -45979,13 +46108,8 @@ server.registerTool(
|
|
|
45979
46108
|
openWorldHint: true
|
|
45980
46109
|
}
|
|
45981
46110
|
},
|
|
45982
|
-
|
|
45983
|
-
|
|
45984
|
-
return await handleStories(await api.getTopStories(), params);
|
|
45985
|
-
} catch (error48) {
|
|
45986
|
-
return createInternalError(error48);
|
|
45987
|
-
}
|
|
45988
|
-
}
|
|
46111
|
+
// @ts-expect-error
|
|
46112
|
+
handleHNGetTopStories
|
|
45989
46113
|
);
|
|
45990
46114
|
server.registerTool(
|
|
45991
46115
|
"hn_get_new_stories",
|
|
@@ -46001,7 +46125,10 @@ server.registerTool(
|
|
|
46001
46125
|
},
|
|
46002
46126
|
async (params) => {
|
|
46003
46127
|
try {
|
|
46004
|
-
return await
|
|
46128
|
+
return await handleHNStories(
|
|
46129
|
+
await api.getNewStories(),
|
|
46130
|
+
params
|
|
46131
|
+
);
|
|
46005
46132
|
} catch (error48) {
|
|
46006
46133
|
return createInternalError(error48);
|
|
46007
46134
|
}
|
|
@@ -46021,7 +46148,10 @@ server.registerTool(
|
|
|
46021
46148
|
},
|
|
46022
46149
|
async (params) => {
|
|
46023
46150
|
try {
|
|
46024
|
-
return await
|
|
46151
|
+
return await handleHNStories(
|
|
46152
|
+
await api.getBestStories(),
|
|
46153
|
+
params
|
|
46154
|
+
);
|
|
46025
46155
|
} catch (error48) {
|
|
46026
46156
|
return createInternalError(error48);
|
|
46027
46157
|
}
|
|
@@ -46041,7 +46171,10 @@ server.registerTool(
|
|
|
46041
46171
|
},
|
|
46042
46172
|
async (params) => {
|
|
46043
46173
|
try {
|
|
46044
|
-
return await
|
|
46174
|
+
return await handleHNStories(
|
|
46175
|
+
await api.getAskStories(),
|
|
46176
|
+
params
|
|
46177
|
+
);
|
|
46045
46178
|
} catch (error48) {
|
|
46046
46179
|
return createInternalError(error48);
|
|
46047
46180
|
}
|
|
@@ -46061,7 +46194,10 @@ server.registerTool(
|
|
|
46061
46194
|
},
|
|
46062
46195
|
async (params) => {
|
|
46063
46196
|
try {
|
|
46064
|
-
return await
|
|
46197
|
+
return await handleHNStories(
|
|
46198
|
+
await api.getShowStories(),
|
|
46199
|
+
params
|
|
46200
|
+
);
|
|
46065
46201
|
} catch (error48) {
|
|
46066
46202
|
return createInternalError(error48);
|
|
46067
46203
|
}
|
|
@@ -46081,7 +46217,10 @@ server.registerTool(
|
|
|
46081
46217
|
},
|
|
46082
46218
|
async (params) => {
|
|
46083
46219
|
try {
|
|
46084
|
-
return await
|
|
46220
|
+
return await handleHNStories(
|
|
46221
|
+
await api.getJobStories(),
|
|
46222
|
+
params
|
|
46223
|
+
);
|
|
46085
46224
|
} catch (error48) {
|
|
46086
46225
|
return createInternalError(error48);
|
|
46087
46226
|
}
|
|
@@ -46102,38 +46241,8 @@ server.registerTool(
|
|
|
46102
46241
|
openWorldHint: true
|
|
46103
46242
|
}
|
|
46104
46243
|
},
|
|
46105
|
-
|
|
46106
|
-
|
|
46107
|
-
const item = await api.getItem(params.id);
|
|
46108
|
-
if (params.response_format === "json" /* JSON */) {
|
|
46109
|
-
return {
|
|
46110
|
-
content: [
|
|
46111
|
-
{
|
|
46112
|
-
type: "text",
|
|
46113
|
-
text: handleLimit(
|
|
46114
|
-
JSON.stringify(item, null, 2),
|
|
46115
|
-
"json" /* JSON */
|
|
46116
|
-
)
|
|
46117
|
-
}
|
|
46118
|
-
],
|
|
46119
|
-
structuredContent: item
|
|
46120
|
-
};
|
|
46121
|
-
}
|
|
46122
|
-
return {
|
|
46123
|
-
content: [
|
|
46124
|
-
{
|
|
46125
|
-
type: "text",
|
|
46126
|
-
text: handleLimit(
|
|
46127
|
-
formatItemMarkdown(item),
|
|
46128
|
-
"markdown" /* MARKDOWN */
|
|
46129
|
-
)
|
|
46130
|
-
}
|
|
46131
|
-
]
|
|
46132
|
-
};
|
|
46133
|
-
} catch (error48) {
|
|
46134
|
-
return createInternalError(error48);
|
|
46135
|
-
}
|
|
46136
|
-
}
|
|
46244
|
+
// @ts-expect-error
|
|
46245
|
+
handleHNGetItem
|
|
46137
46246
|
);
|
|
46138
46247
|
server.registerTool(
|
|
46139
46248
|
"hn_get_user",
|
|
@@ -46242,15 +46351,23 @@ server.registerTool(
|
|
|
46242
46351
|
structuredContent: result
|
|
46243
46352
|
};
|
|
46244
46353
|
}
|
|
46245
|
-
let markdown =
|
|
46246
|
-
|
|
46247
|
-
|
|
46248
|
-
|
|
46249
|
-
|
|
46250
|
-
|
|
46251
|
-
|
|
46252
|
-
|
|
46253
|
-
|
|
46354
|
+
let markdown = "# HN Recent Updates\n\n";
|
|
46355
|
+
markdown += "## Items\n" + formatListItems(
|
|
46356
|
+
paginatedItems.items,
|
|
46357
|
+
(id) => `- [${id}](https://news.ycombinator.com/item?id=${id})`
|
|
46358
|
+
) + (paginatedItems.hasMore ? formatPaginationFooter(
|
|
46359
|
+
paginatedItems.offset,
|
|
46360
|
+
paginatedItems.limit,
|
|
46361
|
+
paginatedItems.total
|
|
46362
|
+
) : "") + "\n\n";
|
|
46363
|
+
markdown += "## Profiles\n" + formatListItems(
|
|
46364
|
+
paginatedProfiles.items,
|
|
46365
|
+
(profile) => `- [${profile}](https://news.ycombinator.com/user?id=${profile})`
|
|
46366
|
+
) + (paginatedProfiles.hasMore ? formatPaginationFooter(
|
|
46367
|
+
paginatedProfiles.offset,
|
|
46368
|
+
paginatedProfiles.limit,
|
|
46369
|
+
paginatedProfiles.total
|
|
46370
|
+
) : "");
|
|
46254
46371
|
return {
|
|
46255
46372
|
content: [
|
|
46256
46373
|
{
|
|
@@ -46298,10 +46415,18 @@ async function run() {
|
|
|
46298
46415
|
await server.connect(transport);
|
|
46299
46416
|
console.error("Hacker News MCP server running via stdio");
|
|
46300
46417
|
}
|
|
46301
|
-
|
|
46302
|
-
|
|
46303
|
-
|
|
46304
|
-
|
|
46418
|
+
if (process.env.NODE_ENV !== "test") {
|
|
46419
|
+
run().catch((error48) => {
|
|
46420
|
+
console.error("Fatal error:", error48);
|
|
46421
|
+
process.exit(1);
|
|
46422
|
+
});
|
|
46423
|
+
}
|
|
46424
|
+
export {
|
|
46425
|
+
handleHNGetItem,
|
|
46426
|
+
handleHNGetTopStories,
|
|
46427
|
+
handleHNStories,
|
|
46428
|
+
server
|
|
46429
|
+
};
|
|
46305
46430
|
/*! Bundled license information:
|
|
46306
46431
|
|
|
46307
46432
|
mime-db/index.js:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fre4x/hn",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.54",
|
|
4
4
|
"description": "A Hacker News MCP server for LLMs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "node
|
|
14
|
+
"build": "node ../scripts/build-package.mjs",
|
|
15
15
|
"typecheck": "cross-env NODE_OPTIONS=--max-old-space-size=4096 tsc --noEmit",
|
|
16
16
|
"start": "node dist/index.js",
|
|
17
17
|
"dev": "tsx src/index.ts",
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"author": "fritzprix",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
31
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
32
32
|
"axios": "^1.13.5",
|
|
33
|
-
"zod": "^4.
|
|
33
|
+
"zod": "^4.3.6"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@types/node": "^25.3.
|
|
36
|
+
"@types/node": "^25.3.5",
|
|
37
37
|
"tsx": "^4.21.0",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
39
|
"vitest": "^4.0.18"
|