@xiedada/nodemw-mcp-server 0.0.11 → 0.1.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.
- package/dist/index.js +79 -77
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,7 +37,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
37
37
|
// package.json
|
|
38
38
|
var package_default = {
|
|
39
39
|
name: "@xiedada/nodemw-mcp-server",
|
|
40
|
-
version: "0.0
|
|
40
|
+
version: "0.1.0",
|
|
41
41
|
description: "MCP server for nodemw - MediaWiki API client",
|
|
42
42
|
type: "module",
|
|
43
43
|
main: "dist/index.js",
|
|
@@ -301,7 +301,7 @@ async function handleGetArticleTool(title, followRedirect, redirectInfo, revisio
|
|
|
301
301
|
});
|
|
302
302
|
const pages = info.pages;
|
|
303
303
|
const page = getFirstItem(pages);
|
|
304
|
-
if (!page || page.missing) {
|
|
304
|
+
if (!page || page.missing !== void 0) {
|
|
305
305
|
return {
|
|
306
306
|
content: [{ type: "text", text: `Page "${title}" not found.` }],
|
|
307
307
|
isError: true
|
|
@@ -334,7 +334,7 @@ async function handleGetArticleTool(title, followRedirect, redirectInfo, revisio
|
|
|
334
334
|
const [content, redirect] = result;
|
|
335
335
|
if (content == null) {
|
|
336
336
|
return {
|
|
337
|
-
content: [{ type: "text", text: `Page "${title}" not found
|
|
337
|
+
content: [{ type: "text", text: `Page "${title}" not found.` }],
|
|
338
338
|
isError: true
|
|
339
339
|
};
|
|
340
340
|
}
|
|
@@ -344,7 +344,7 @@ ${content}
|
|
|
344
344
|
|
|
345
345
|
Redirect Information:
|
|
346
346
|
|
|
347
|
-
${JSON.stringify(redirect, null, 2)}` : content;
|
|
347
|
+
${JSON.stringify(redirect, null, 2)}` : content === "" ? "(empty page)" : content;
|
|
348
348
|
await recordReadState(title);
|
|
349
349
|
return {
|
|
350
350
|
content: [{ type: "text", text: responseText }]
|
|
@@ -358,13 +358,13 @@ ${JSON.stringify(redirect, null, 2)}` : content;
|
|
|
358
358
|
);
|
|
359
359
|
if (result == null) {
|
|
360
360
|
return {
|
|
361
|
-
content: [{ type: "text", text: `Page "${title}" not found
|
|
361
|
+
content: [{ type: "text", text: `Page "${title}" not found.` }],
|
|
362
362
|
isError: true
|
|
363
363
|
};
|
|
364
364
|
}
|
|
365
365
|
await recordReadState(title);
|
|
366
366
|
return {
|
|
367
|
-
content: [{ type: "text", text: result }]
|
|
367
|
+
content: [{ type: "text", text: result === "" ? "(empty page)" : result }]
|
|
368
368
|
};
|
|
369
369
|
}
|
|
370
370
|
} catch (error) {
|
|
@@ -809,7 +809,7 @@ async function handleGetArticlePropertiesTool(title) {
|
|
|
809
809
|
);
|
|
810
810
|
return jsonResult({
|
|
811
811
|
title,
|
|
812
|
-
properties
|
|
812
|
+
properties: properties || {}
|
|
813
813
|
});
|
|
814
814
|
} catch (error) {
|
|
815
815
|
return errorResult("Failed to get article properties", error);
|
|
@@ -866,41 +866,79 @@ import { z as z14 } from "zod";
|
|
|
866
866
|
function getUserContribsTool(server) {
|
|
867
867
|
const tool = server.tool(
|
|
868
868
|
"get-user-contribs",
|
|
869
|
-
"Get contributions made by a specific user",
|
|
869
|
+
"Get contributions made by a specific user. Pagination: the response includes total (matching edits found) and displayed (returned in this batch). If displayed < total, more results exist \u2014 use the timestamp of the LAST returned contribution as the start parameter for the next page. Repeat until displayed < limit to get all results.",
|
|
870
870
|
{
|
|
871
871
|
username: z14.string().describe("Username to get contributions for"),
|
|
872
872
|
namespace: z14.number().optional().describe("Filter contributions by namespace"),
|
|
873
|
-
limit: z14.number().optional().default(50).describe("Maximum number of contributions to return")
|
|
873
|
+
limit: z14.number().optional().default(50).describe("Maximum number of contributions to return"),
|
|
874
|
+
start: z14.string().optional().describe(
|
|
875
|
+
'Timestamp to start listing from \u2014 only return edits before this time (not inclusive). Accepts ISO 8601 (e.g. "2026-05-10T22:54:37Z"), MediaWiki format "YYYYMMDDHHMMSS", or unix timestamp. All times are UTC \u2014 MW ignores timezone offsets. To paginate: pass the timestamp of the LAST item from the previous page as start. The returned contributions are guaranteed to be strictly older than this timestamp.'
|
|
876
|
+
)
|
|
874
877
|
},
|
|
875
878
|
{
|
|
876
879
|
title: "Get user contributions",
|
|
877
880
|
readOnlyHint: true,
|
|
878
881
|
destructiveHint: false
|
|
879
882
|
},
|
|
880
|
-
async ({ username, namespace, limit }) => handleGetUserContribsTool(username, namespace, limit)
|
|
883
|
+
async ({ username, namespace, limit, start }) => handleGetUserContribsTool(username, namespace, limit, start)
|
|
881
884
|
);
|
|
882
|
-
tool.update({ outputSchema: { username: z14.string(), namespace: z14.number().optional(), limit: z14.number(), total: z14.number(), displayed: z14.number(), contributions: z14.array(z14.record(z14.unknown())) } });
|
|
885
|
+
tool.update({ outputSchema: { username: z14.string(), namespace: z14.number().optional(), limit: z14.number(), start: z14.string().optional(), total: z14.number(), displayed: z14.number(), contributions: z14.array(z14.record(z14.unknown())) } });
|
|
883
886
|
return tool;
|
|
884
887
|
}
|
|
885
|
-
async function handleGetUserContribsTool(username, namespace, limit = 50) {
|
|
888
|
+
async function handleGetUserContribsTool(username, namespace, limit = 50, start) {
|
|
886
889
|
try {
|
|
887
890
|
const bot = await getBot();
|
|
888
|
-
const
|
|
889
|
-
user: username,
|
|
890
|
-
...namespace !== void 0 && { namespace }
|
|
891
|
-
};
|
|
892
|
-
const callbackArgs = await promisifyBotMethod(
|
|
891
|
+
const userInfo = await promisifyBotMethod(
|
|
893
892
|
bot,
|
|
894
|
-
"
|
|
895
|
-
|
|
893
|
+
"whois",
|
|
894
|
+
username
|
|
896
895
|
);
|
|
897
|
-
|
|
898
|
-
|
|
896
|
+
if (userInfo.missing !== void 0) {
|
|
897
|
+
return errorResult(`User "${username}" not found.`);
|
|
898
|
+
}
|
|
899
|
+
const allContribs = [];
|
|
900
|
+
const perPage = Math.min(limit, 500);
|
|
901
|
+
const baseParams = {
|
|
902
|
+
action: "query",
|
|
903
|
+
list: "usercontribs",
|
|
904
|
+
ucuser: username,
|
|
905
|
+
uclimit: perPage,
|
|
906
|
+
ucprop: "ids|title|timestamp|comment|size|flags",
|
|
907
|
+
...namespace !== void 0 && { ucnamespace: namespace },
|
|
908
|
+
...start && { ucstart: start }
|
|
909
|
+
};
|
|
910
|
+
let continueParams;
|
|
911
|
+
do {
|
|
912
|
+
const params = { ...baseParams, ...continueParams || {} };
|
|
913
|
+
const rawData = await new Promise((resolve, reject) => {
|
|
914
|
+
bot.api.call(params, (_err, _info, _next, raw) => {
|
|
915
|
+
if (_err) reject(_err);
|
|
916
|
+
else resolve(raw);
|
|
917
|
+
});
|
|
918
|
+
});
|
|
919
|
+
const usercontribs = rawData.query?.usercontribs;
|
|
920
|
+
if (usercontribs) {
|
|
921
|
+
allContribs.push(...usercontribs);
|
|
922
|
+
}
|
|
923
|
+
if (allContribs.length >= limit) {
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
if (rawData.continue) {
|
|
927
|
+
continueParams = rawData.continue;
|
|
928
|
+
} else if (rawData["query-continue"]) {
|
|
929
|
+
const qc = rawData["query-continue"];
|
|
930
|
+
continueParams = qc.usercontribs || qc[Object.keys(qc)[0]];
|
|
931
|
+
} else {
|
|
932
|
+
continueParams = void 0;
|
|
933
|
+
}
|
|
934
|
+
} while (continueParams);
|
|
935
|
+
const limitedContribs = allContribs.slice(0, limit);
|
|
899
936
|
return jsonResult({
|
|
900
937
|
username,
|
|
901
938
|
namespace,
|
|
902
939
|
limit,
|
|
903
|
-
|
|
940
|
+
start,
|
|
941
|
+
total: allContribs.length,
|
|
904
942
|
displayed: limitedContribs.length,
|
|
905
943
|
contributions: limitedContribs
|
|
906
944
|
});
|
|
@@ -923,17 +961,7 @@ function whoamiTool(server) {
|
|
|
923
961
|
},
|
|
924
962
|
async () => handleWhoamiTool()
|
|
925
963
|
);
|
|
926
|
-
tool.update({ outputSchema: { user: z15.
|
|
927
|
-
name: z15.string(),
|
|
928
|
-
id: z15.number(),
|
|
929
|
-
groups: z15.array(z15.string()),
|
|
930
|
-
rights: z15.array(z15.string()),
|
|
931
|
-
ratelimits: z15.union([z15.record(z15.unknown()), z15.array(z15.unknown())]),
|
|
932
|
-
editcount: z15.number(),
|
|
933
|
-
realname: z15.string().optional(),
|
|
934
|
-
email: z15.string().optional(),
|
|
935
|
-
emailauthenticated: z15.string().optional()
|
|
936
|
-
}) } });
|
|
964
|
+
tool.update({ outputSchema: { user: z15.record(z15.unknown()) } });
|
|
937
965
|
return tool;
|
|
938
966
|
}
|
|
939
967
|
async function handleWhoamiTool() {
|
|
@@ -965,18 +993,7 @@ function whoisTool(server) {
|
|
|
965
993
|
},
|
|
966
994
|
async ({ username }) => handleWhoisTool(username)
|
|
967
995
|
);
|
|
968
|
-
tool.update({ outputSchema: { user: z16.
|
|
969
|
-
userid: z16.number(),
|
|
970
|
-
name: z16.string(),
|
|
971
|
-
groups: z16.array(z16.string()),
|
|
972
|
-
implicitgroups: z16.array(z16.string()).optional(),
|
|
973
|
-
rights: z16.array(z16.string()),
|
|
974
|
-
editcount: z16.number(),
|
|
975
|
-
registration: z16.string().optional(),
|
|
976
|
-
emailable: z16.string().optional(),
|
|
977
|
-
gender: z16.string().optional(),
|
|
978
|
-
blockinfo: z16.record(z16.unknown()).optional()
|
|
979
|
-
}) } });
|
|
996
|
+
tool.update({ outputSchema: { user: z16.record(z16.unknown()) } });
|
|
980
997
|
return tool;
|
|
981
998
|
}
|
|
982
999
|
async function handleWhoisTool(username) {
|
|
@@ -987,7 +1004,7 @@ async function handleWhoisTool(username) {
|
|
|
987
1004
|
"whois",
|
|
988
1005
|
username
|
|
989
1006
|
);
|
|
990
|
-
if (userInfo.missing) {
|
|
1007
|
+
if (userInfo.missing !== void 0) {
|
|
991
1008
|
return errorResult(`User "${username}" not found.`);
|
|
992
1009
|
}
|
|
993
1010
|
return jsonResult({ user: userInfo });
|
|
@@ -1012,18 +1029,7 @@ function whoareTool(server) {
|
|
|
1012
1029
|
},
|
|
1013
1030
|
async (params) => handleWhoareTool(params)
|
|
1014
1031
|
);
|
|
1015
|
-
tool.update({ outputSchema: { users: z17.array(z17.
|
|
1016
|
-
userid: z17.number(),
|
|
1017
|
-
name: z17.string(),
|
|
1018
|
-
groups: z17.array(z17.string()),
|
|
1019
|
-
implicitgroups: z17.array(z17.string()).optional(),
|
|
1020
|
-
rights: z17.array(z17.string()),
|
|
1021
|
-
editcount: z17.number(),
|
|
1022
|
-
registration: z17.string().optional(),
|
|
1023
|
-
emailable: z17.string().optional(),
|
|
1024
|
-
gender: z17.string().optional(),
|
|
1025
|
-
blockinfo: z17.record(z17.unknown()).optional()
|
|
1026
|
-
})), count: z17.number() } });
|
|
1032
|
+
tool.update({ outputSchema: { users: z17.array(z17.record(z17.unknown())), count: z17.number() } });
|
|
1027
1033
|
return tool;
|
|
1028
1034
|
}
|
|
1029
1035
|
async function handleWhoareTool(params) {
|
|
@@ -1034,7 +1040,10 @@ async function handleWhoareTool(params) {
|
|
|
1034
1040
|
"whoare",
|
|
1035
1041
|
params.usernames
|
|
1036
1042
|
);
|
|
1037
|
-
|
|
1043
|
+
const normalized = users.map(
|
|
1044
|
+
(u) => u && u.missing !== void 0 ? { ...u, missing: true } : u
|
|
1045
|
+
);
|
|
1046
|
+
return jsonResult({ users: normalized, count: normalized.length });
|
|
1038
1047
|
} catch (error) {
|
|
1039
1048
|
return errorResult("Failed to get user information", error);
|
|
1040
1049
|
}
|
|
@@ -1203,10 +1212,11 @@ import { z as z22 } from "zod";
|
|
|
1203
1212
|
function getLogTool(server) {
|
|
1204
1213
|
const tool = server.tool(
|
|
1205
1214
|
"get-log",
|
|
1206
|
-
"Get log entries of a specific type"
|
|
1207
|
-
{
|
|
1215
|
+
"Get log entries of a specific type (e.g. delete, block, move). Pagination: the response includes total (matching entries found) and displayed (returned in this batch). If displayed < total, more results exist \u2014 use the timestamp of the LAST returned entry as the start parameter for the next page." + {
|
|
1208
1216
|
type: z22.string().describe("Log type (e.g. delete, block, move)"),
|
|
1209
|
-
start: z22.string().optional().
|
|
1217
|
+
start: z22.string().optional().describe(
|
|
1218
|
+
'Timestamp to start listing from \u2014 only return entries before this time. Accepts ISO 8601 (e.g. "2026-05-10T22:54:37Z"), MediaWiki format "YYYYMMDDHHMMSS", or unix timestamp. All times are UTC \u2014 MW ignores timezone offsets. To paginate: pass the timestamp of the LAST item from the previous page as start.'
|
|
1219
|
+
),
|
|
1210
1220
|
limit: z22.number().optional().default(50).describe("Maximum number of entries to return")
|
|
1211
1221
|
},
|
|
1212
1222
|
{
|
|
@@ -1216,10 +1226,10 @@ function getLogTool(server) {
|
|
|
1216
1226
|
},
|
|
1217
1227
|
async ({ type, start, limit }) => handleGetLogTool(type, start, limit)
|
|
1218
1228
|
);
|
|
1219
|
-
tool.update({ outputSchema: { type: z22.string(), start: z22.string(), limit: z22.number(), total: z22.number(), displayed: z22.number(), entries: z22.array(z22.record(z22.unknown())) } });
|
|
1229
|
+
tool.update({ outputSchema: { type: z22.string(), start: z22.string().optional(), limit: z22.number(), total: z22.number(), displayed: z22.number(), entries: z22.array(z22.record(z22.unknown())) } });
|
|
1220
1230
|
return tool;
|
|
1221
1231
|
}
|
|
1222
|
-
async function handleGetLogTool(type, start, limit) {
|
|
1232
|
+
async function handleGetLogTool(type, start, limit = 50) {
|
|
1223
1233
|
try {
|
|
1224
1234
|
const bot = await getBot();
|
|
1225
1235
|
const entries = await new Promise((resolve, reject) => {
|
|
@@ -1342,9 +1352,10 @@ import { z as z25 } from "zod";
|
|
|
1342
1352
|
function getRecentChangesTool(server) {
|
|
1343
1353
|
const tool = server.tool(
|
|
1344
1354
|
"get-recent-changes",
|
|
1345
|
-
"Get recent changes on the wiki"
|
|
1346
|
-
|
|
1347
|
-
|
|
1355
|
+
"Get recent changes on the wiki. Pagination: the response includes total (matching changes found) and displayed (returned in this batch). If displayed < total, more results exist \u2014 use the timestamp of the LAST returned change as the start parameter for the next page." + {
|
|
1356
|
+
start: z25.string().optional().describe(
|
|
1357
|
+
'Timestamp to start listing from \u2014 only return changes before this time. Accepts ISO 8601 (e.g. "2026-05-10T22:54:37Z"), MediaWiki format "YYYYMMDDHHMMSS", or unix timestamp. All times are UTC \u2014 MW ignores timezone offsets. To paginate: pass the timestamp of the LAST item from the previous page as start.'
|
|
1358
|
+
),
|
|
1348
1359
|
limit: z25.number().optional().default(50).describe("Maximum number of changes to return")
|
|
1349
1360
|
},
|
|
1350
1361
|
{
|
|
@@ -1429,16 +1440,7 @@ function getSiteStatsTool(server) {
|
|
|
1429
1440
|
},
|
|
1430
1441
|
async () => handleGetSiteStatsTool()
|
|
1431
1442
|
);
|
|
1432
|
-
tool.update({ outputSchema: { statistics: z27.
|
|
1433
|
-
pages: z27.number(),
|
|
1434
|
-
articles: z27.number(),
|
|
1435
|
-
edits: z27.number(),
|
|
1436
|
-
images: z27.number(),
|
|
1437
|
-
users: z27.number(),
|
|
1438
|
-
activeusers: z27.number().optional(),
|
|
1439
|
-
admins: z27.number().optional(),
|
|
1440
|
-
jobs: z27.number().optional()
|
|
1441
|
-
}) } });
|
|
1443
|
+
tool.update({ outputSchema: { statistics: z27.record(z27.unknown()) } });
|
|
1442
1444
|
return tool;
|
|
1443
1445
|
}
|
|
1444
1446
|
async function handleGetSiteStatsTool() {
|