@cyanheads/wikidata-mcp-server 0.1.1
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 +401 -0
- package/Dockerfile +98 -0
- package/LICENSE +201 -0
- package/README.md +293 -0
- package/changelog/0.1.x/0.1.0.md +20 -0
- package/changelog/0.1.x/0.1.1.md +20 -0
- package/changelog/template.md +119 -0
- package/dist/config/server-config.d.ts +11 -0
- package/dist/config/server-config.d.ts.map +1 -0
- package/dist/config/server-config.js +31 -0
- package/dist/config/server-config.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp-server/resources/definitions/entity.resource.d.ts +11 -0
- package/dist/mcp-server/resources/definitions/entity.resource.d.ts.map +1 -0
- package/dist/mcp-server/resources/definitions/entity.resource.js +127 -0
- package/dist/mcp-server/resources/definitions/entity.resource.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-entity.tool.d.ts +38 -0
- package/dist/mcp-server/tools/definitions/get-entity.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-entity.tool.js +215 -0
- package/dist/mcp-server/tools/definitions/get-entity.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-labels.tool.d.ts +24 -0
- package/dist/mcp-server/tools/definitions/get-labels.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-labels.tool.js +93 -0
- package/dist/mcp-server/tools/definitions/get-labels.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-sitelinks.tool.d.ts +31 -0
- package/dist/mcp-server/tools/definitions/get-sitelinks.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-sitelinks.tool.js +128 -0
- package/dist/mcp-server/tools/definitions/get-sitelinks.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/get-statements.tool.d.ts +29 -0
- package/dist/mcp-server/tools/definitions/get-statements.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/get-statements.tool.js +185 -0
- package/dist/mcp-server/tools/definitions/get-statements.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/resolve-external-id.tool.d.ts +30 -0
- package/dist/mcp-server/tools/definitions/resolve-external-id.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/resolve-external-id.tool.js +193 -0
- package/dist/mcp-server/tools/definitions/resolve-external-id.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts +36 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.js +129 -0
- package/dist/mcp-server/tools/definitions/search-entities.tool.js.map +1 -0
- package/dist/mcp-server/tools/definitions/sparql-query.tool.d.ts +34 -0
- package/dist/mcp-server/tools/definitions/sparql-query.tool.d.ts.map +1 -0
- package/dist/mcp-server/tools/definitions/sparql-query.tool.js +151 -0
- package/dist/mcp-server/tools/definitions/sparql-query.tool.js.map +1 -0
- package/dist/services/wikidata/types.d.ts +164 -0
- package/dist/services/wikidata/types.d.ts.map +1 -0
- package/dist/services/wikidata/types.js +6 -0
- package/dist/services/wikidata/types.js.map +1 -0
- package/dist/services/wikidata/wikidata-rest-service.d.ts +48 -0
- package/dist/services/wikidata/wikidata-rest-service.d.ts.map +1 -0
- package/dist/services/wikidata/wikidata-rest-service.js +293 -0
- package/dist/services/wikidata/wikidata-rest-service.js.map +1 -0
- package/dist/services/wikidata/wikidata-sparql-service.d.ts +23 -0
- package/dist/services/wikidata/wikidata-sparql-service.d.ts.map +1 -0
- package/dist/services/wikidata/wikidata-sparql-service.js +163 -0
- package/dist/services/wikidata/wikidata-sparql-service.js.map +1 -0
- package/package.json +82 -0
- package/server.json +141 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-labels.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-labels.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fetch Wikimedia sitelinks for a Wikidata entity.
|
|
3
|
+
* @module mcp-server/tools/definitions/get-sitelinks.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const wikidataGetSitelinks: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
id: z.ZodString;
|
|
9
|
+
sites: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10
|
+
wikis_only: z.ZodDefault<z.ZodBoolean>;
|
|
11
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
id: z.ZodString;
|
|
13
|
+
sitelinks: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
14
|
+
title: z.ZodString;
|
|
15
|
+
url: z.ZodOptional<z.ZodString>;
|
|
16
|
+
badges: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
17
|
+
}, z.core.$strip>>;
|
|
18
|
+
count: z.ZodNumber;
|
|
19
|
+
message: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$strip>, readonly [{
|
|
21
|
+
readonly reason: "entity_not_found";
|
|
22
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
23
|
+
readonly when: "No item exists at this Q-ID.";
|
|
24
|
+
readonly recovery: "Verify the Q-ID with wikidata_search_entities or wikidata_get_labels.";
|
|
25
|
+
}, {
|
|
26
|
+
readonly reason: "not_an_item";
|
|
27
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
28
|
+
readonly when: "A P-ID was supplied — only items (Q-IDs) have sitelinks.";
|
|
29
|
+
readonly recovery: "Supply a Q-ID (Q followed by digits). Properties do not have Wikipedia sitelinks.";
|
|
30
|
+
}]>;
|
|
31
|
+
//# sourceMappingURL=get-sitelinks.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-sitelinks.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-sitelinks.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAOjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;GAiK/B,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fetch Wikimedia sitelinks for a Wikidata entity.
|
|
3
|
+
* @module mcp-server/tools/definitions/get-sitelinks.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { getWikidataRestService, isQId, normalizeId, } from '../../../services/wikidata/wikidata-rest-service.js';
|
|
8
|
+
export const wikidataGetSitelinks = tool('wikidata_get_sitelinks', {
|
|
9
|
+
title: 'Get Wikidata Sitelinks',
|
|
10
|
+
description: 'Fetch Wikipedia and Wikimedia project article URLs for a Wikidata item. ' +
|
|
11
|
+
'A sitelink maps a site code (e.g., "enwiki") to a Wikipedia article title and URL. ' +
|
|
12
|
+
'Major items can have 300+ sitelinks across languages. ' +
|
|
13
|
+
'Use sites to filter to specific language editions, or wikis_only to return only Wikipedia links. ' +
|
|
14
|
+
'Only Q-IDs (items) have sitelinks — properties (P-IDs) do not.',
|
|
15
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
16
|
+
input: z.object({
|
|
17
|
+
id: z
|
|
18
|
+
.string()
|
|
19
|
+
.min(1)
|
|
20
|
+
.describe('Q-ID of the item (e.g., "Q76"). Only items have sitelinks; properties (P-IDs) are not supported.'),
|
|
21
|
+
sites: z
|
|
22
|
+
.array(z.string().min(1))
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Optional filter to specific site codes (e.g., ["enwiki", "frwiki", "dewiki"]). ' +
|
|
25
|
+
'Omit to return all sitelinks.'),
|
|
26
|
+
wikis_only: z
|
|
27
|
+
.boolean()
|
|
28
|
+
.default(false)
|
|
29
|
+
.describe('When true, return only Wikipedia sitelinks (site codes ending in "wiki", e.g., "enwiki", "dewiki"). ' +
|
|
30
|
+
'Excludes Wikisource, Wiktionary, Wikiquote, etc.'),
|
|
31
|
+
}),
|
|
32
|
+
output: z.object({
|
|
33
|
+
id: z.string().describe('The Q-ID whose sitelinks were fetched.'),
|
|
34
|
+
sitelinks: z
|
|
35
|
+
.record(z.string(), z.object({
|
|
36
|
+
title: z.string().describe('Article title on that wiki.'),
|
|
37
|
+
url: z.string().optional().describe('Full URL to the article. Omitted when unavailable.'),
|
|
38
|
+
badges: z
|
|
39
|
+
.array(z.string())
|
|
40
|
+
.optional()
|
|
41
|
+
.describe('Quality badge QIDs (e.g., "Q17437798" = featured article).'),
|
|
42
|
+
}))
|
|
43
|
+
.describe('Map of site code to sitelink metadata. Empty when the entity has no matching sitelinks.'),
|
|
44
|
+
count: z.number().describe('Number of sitelinks returned.'),
|
|
45
|
+
message: z
|
|
46
|
+
.string()
|
|
47
|
+
.optional()
|
|
48
|
+
.describe('Informational note when no sitelinks were found. Absent when sitelinks are present.'),
|
|
49
|
+
}),
|
|
50
|
+
errors: [
|
|
51
|
+
{
|
|
52
|
+
reason: 'entity_not_found',
|
|
53
|
+
code: JsonRpcErrorCode.NotFound,
|
|
54
|
+
when: 'No item exists at this Q-ID.',
|
|
55
|
+
recovery: 'Verify the Q-ID with wikidata_search_entities or wikidata_get_labels.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
reason: 'not_an_item',
|
|
59
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
60
|
+
when: 'A P-ID was supplied — only items (Q-IDs) have sitelinks.',
|
|
61
|
+
recovery: 'Supply a Q-ID (Q followed by digits). Properties do not have Wikipedia sitelinks.',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
async handler(input, ctx) {
|
|
65
|
+
const id = normalizeId(input.id);
|
|
66
|
+
if (!isQId(id)) {
|
|
67
|
+
throw ctx.fail('not_an_item', `"${input.id}" is not a Q-ID. Only Wikidata items have sitelinks.`, { ...ctx.recoveryFor('not_an_item') });
|
|
68
|
+
}
|
|
69
|
+
const svc = getWikidataRestService();
|
|
70
|
+
ctx.log.info('Fetching sitelinks', { id, sites: input.sites, wikis_only: input.wikis_only });
|
|
71
|
+
let rawSitelinks;
|
|
72
|
+
try {
|
|
73
|
+
rawSitelinks = await svc.fetchSitelinks(id, input.sites, ctx);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
if (err?.data?.status === 404) {
|
|
77
|
+
throw ctx.fail('entity_not_found', `No item found for Q-ID "${id}".`, {
|
|
78
|
+
...ctx.recoveryFor('entity_not_found'),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
// Apply wikis_only filter
|
|
84
|
+
let sitelinks = rawSitelinks;
|
|
85
|
+
if (input.wikis_only) {
|
|
86
|
+
sitelinks = Object.fromEntries(Object.entries(rawSitelinks).filter(([code]) => code.endsWith('wiki')));
|
|
87
|
+
}
|
|
88
|
+
const normalized = Object.fromEntries(Object.entries(sitelinks).map(([code, sl]) => [
|
|
89
|
+
code,
|
|
90
|
+
{
|
|
91
|
+
title: sl.title,
|
|
92
|
+
...(sl.url ? { url: sl.url } : {}),
|
|
93
|
+
...(sl.badges?.length ? { badges: sl.badges } : {}),
|
|
94
|
+
},
|
|
95
|
+
]));
|
|
96
|
+
const count = Object.keys(normalized).length;
|
|
97
|
+
if (count === 0) {
|
|
98
|
+
const hint = input.sites?.length
|
|
99
|
+
? `None of the requested site codes (${input.sites.join(', ')}) were found on ${id}.`
|
|
100
|
+
: input.wikis_only
|
|
101
|
+
? `${id} has no Wikipedia (wikis_only) sitelinks.`
|
|
102
|
+
: `${id} has no sitelinks.`;
|
|
103
|
+
return { id, sitelinks: {}, count: 0, message: hint };
|
|
104
|
+
}
|
|
105
|
+
return { id, sitelinks: normalized, count };
|
|
106
|
+
},
|
|
107
|
+
format: (result) => {
|
|
108
|
+
const lines = [`## Sitelinks for ${result.id}`, `**Count:** ${result.count}`];
|
|
109
|
+
if (result.message) {
|
|
110
|
+
lines.push(`\n> ${result.message}`);
|
|
111
|
+
}
|
|
112
|
+
// Sort: Wikipedia editions first, then others
|
|
113
|
+
const entries = Object.entries(result.sitelinks);
|
|
114
|
+
const wikis = entries.filter(([code]) => code.endsWith('wiki'));
|
|
115
|
+
const others = entries.filter(([code]) => !code.endsWith('wiki'));
|
|
116
|
+
const sorted = [...wikis, ...others];
|
|
117
|
+
for (const [code, sl] of sorted.slice(0, 30)) {
|
|
118
|
+
const url = sl.url ??
|
|
119
|
+
`https://${code.replace('wiki', '')}.wikipedia.org/wiki/${encodeURIComponent(sl.title)}`;
|
|
120
|
+
lines.push(`**${code}:** [${sl.title}](${url})${sl.badges?.length ? ` [${sl.badges.join(', ')}]` : ''}`);
|
|
121
|
+
}
|
|
122
|
+
if (sorted.length > 30) {
|
|
123
|
+
lines.push(`… and ${sorted.length - 30} more sitelinks.`);
|
|
124
|
+
}
|
|
125
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
//# sourceMappingURL=get-sitelinks.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-sitelinks.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-sitelinks.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EACL,sBAAsB,EACtB,KAAK,EACL,WAAW,GACZ,MAAM,8CAA8C,CAAC;AAEtD,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,CAAC,wBAAwB,EAAE;IACjE,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EACT,0EAA0E;QAC1E,qFAAqF;QACrF,wDAAwD;QACxD,mGAAmG;QACnG,gEAAgE;IAClE,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE;IAE9E,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,EAAE,EAAE,CAAC;aACF,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,CACP,kGAAkG,CACnG;QACH,KAAK,EAAE,CAAC;aACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aACxB,QAAQ,EAAE;aACV,QAAQ,CACP,iFAAiF;YAC/E,+BAA+B,CAClC;QACH,UAAU,EAAE,CAAC;aACV,OAAO,EAAE;aACT,OAAO,CAAC,KAAK,CAAC;aACd,QAAQ,CACP,sGAAsG;YACpG,kDAAkD,CACrD;KACJ,CAAC;IAEF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;QACjE,SAAS,EAAE,CAAC;aACT,MAAM,CACL,CAAC,CAAC,MAAM,EAAE,EACV,CAAC,CAAC,MAAM,CAAC;YACP,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;YACzD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,oDAAoD,CAAC;YACzF,MAAM,EAAE,CAAC;iBACN,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;iBACjB,QAAQ,EAAE;iBACV,QAAQ,CAAC,4DAA4D,CAAC;SAC1E,CAAC,CACH;aACA,QAAQ,CACP,yFAAyF,CAC1F;QACH,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;QAC3D,OAAO,EAAE,CAAC;aACP,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,qFAAqF,CACtF;KACJ,CAAC;IAEF,MAAM,EAAE;QACN;YACE,MAAM,EAAE,kBAAkB;YAC1B,IAAI,EAAE,gBAAgB,CAAC,QAAQ;YAC/B,IAAI,EAAE,8BAA8B;YACpC,QAAQ,EAAE,uEAAuE;SAClF;QACD;YACE,MAAM,EAAE,aAAa;YACrB,IAAI,EAAE,gBAAgB,CAAC,aAAa;YACpC,IAAI,EAAE,0DAA0D;YAChE,QAAQ,EAAE,mFAAmF;SAC9F;KACF;IAED,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,MAAM,EAAE,GAAG,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAEjC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;YACf,MAAM,GAAG,CAAC,IAAI,CACZ,aAAa,EACb,IAAI,KAAK,CAAC,EAAE,sDAAsD,EAClE,EAAE,GAAG,GAAG,CAAC,WAAW,CAAC,aAAa,CAAC,EAAE,CACtC,CAAC;QACJ,CAAC;QAED,MAAM,GAAG,GAAG,sBAAsB,EAAE,CAAC;QACrC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;QAE7F,IAAI,YAAuF,CAAC;QAC5F,IAAI,CAAC;YACH,YAAY,GAAG,MAAM,GAAG,CAAC,cAAc,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAAsC,EAAE,IAAI,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;gBAClE,MAAM,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE,2BAA2B,EAAE,IAAI,EAAE;oBACpE,GAAG,GAAG,CAAC,WAAW,CAAC,kBAAkB,CAAC;iBACvC,CAAC,CAAC;YACL,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QAED,0BAA0B;QAC1B,IAAI,SAAS,GAAG,YAAY,CAAC;QAC7B,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACrB,SAAS,GAAG,MAAM,CAAC,WAAW,CAC5B,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CACvE,CAAC;QACJ,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,CAAC,WAAW,CACnC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5C,IAAI;YACJ;gBACE,KAAK,EAAE,EAAE,CAAC,KAAK;gBACf,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpD;SACF,CAAC,CACH,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;QAE7C,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,MAAM;gBAC9B,CAAC,CAAC,qCAAqC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,GAAG;gBACrF,CAAC,CAAC,KAAK,CAAC,UAAU;oBAChB,CAAC,CAAC,GAAG,EAAE,2CAA2C;oBAClD,CAAC,CAAC,GAAG,EAAE,oBAAoB,CAAC;YAChC,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACxD,CAAC;QAED,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;IAC9C,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QACjB,MAAM,KAAK,GAAa,CAAC,oBAAoB,MAAM,CAAC,EAAE,EAAE,EAAE,cAAc,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAExF,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,8CAA8C;QAC9C,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC;QAErC,KAAK,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC7C,MAAM,GAAG,GACP,EAAE,CAAC,GAAG;gBACN,WAAW,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,uBAAuB,kBAAkB,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3F,KAAK,CAAC,IAAI,CACR,KAAK,IAAI,QAAQ,EAAE,CAAC,KAAK,KAAK,GAAG,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAC7F,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,GAAG,EAAE,kBAAkB,CAAC,CAAC;QAC5D,CAAC;QAED,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fetch and normalize property statements for a Wikidata entity with label resolution.
|
|
3
|
+
* @module mcp-server/tools/definitions/get-statements.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const wikidataGetStatements: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
id: z.ZodString;
|
|
9
|
+
properties: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
10
|
+
language: z.ZodDefault<z.ZodString>;
|
|
11
|
+
resolve_labels: z.ZodDefault<z.ZodBoolean>;
|
|
12
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
13
|
+
id: z.ZodString;
|
|
14
|
+
statements: z.ZodRecord<z.ZodString, z.ZodArray<z.ZodObject<{}, z.core.$loose>>>;
|
|
15
|
+
propertyCount: z.ZodNumber;
|
|
16
|
+
statementCount: z.ZodNumber;
|
|
17
|
+
labelsResolved: z.ZodBoolean;
|
|
18
|
+
}, z.core.$strip>, readonly [{
|
|
19
|
+
readonly reason: "entity_not_found";
|
|
20
|
+
readonly code: JsonRpcErrorCode.NotFound;
|
|
21
|
+
readonly when: "No entity exists at this QID.";
|
|
22
|
+
readonly recovery: "Verify the ID with wikidata_search_entities or wikidata_get_labels.";
|
|
23
|
+
}, {
|
|
24
|
+
readonly reason: "invalid_id";
|
|
25
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
26
|
+
readonly when: "ID is not a valid Q-ID or P-ID format.";
|
|
27
|
+
readonly recovery: "Supply a valid Q-ID (Q followed by digits) or P-ID (P followed by digits).";
|
|
28
|
+
}]>;
|
|
29
|
+
//# sourceMappingURL=get-statements.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-statements.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-statements.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAUjE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;GA2KhC,CAAC"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Fetch and normalize property statements for a Wikidata entity with label resolution.
|
|
3
|
+
* @module mcp-server/tools/definitions/get-statements.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { getWikidataRestService, isPId, isQId, normalizeId, normalizeStatements, } from '../../../services/wikidata/wikidata-rest-service.js';
|
|
8
|
+
export const wikidataGetStatements = tool('wikidata_get_statements', {
|
|
9
|
+
title: 'Get Wikidata Statements',
|
|
10
|
+
description: 'Fetch property claims for a Wikidata entity with qualifier and reference detail. ' +
|
|
11
|
+
'Value QIDs are resolved to human-readable labels by default. ' +
|
|
12
|
+
'Use the properties parameter to fetch only specific P-IDs — omitting it returns all statements, ' +
|
|
13
|
+
'which can be large. Designed for fact verification: "what does Wikidata say about this entity\'s {property}?". ' +
|
|
14
|
+
'Preferred-rank statements are the most current values.',
|
|
15
|
+
annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true },
|
|
16
|
+
input: z.object({
|
|
17
|
+
id: z
|
|
18
|
+
.string()
|
|
19
|
+
.min(1)
|
|
20
|
+
.describe('Q-ID (e.g., "Q76") or P-ID of the entity to fetch statements for.'),
|
|
21
|
+
properties: z
|
|
22
|
+
.array(z.string().min(1))
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('P-IDs to fetch (e.g., ["P31", "P569", "P27"]). Omit to return all properties (may be large for major items).'),
|
|
25
|
+
language: z
|
|
26
|
+
.string()
|
|
27
|
+
.default('en')
|
|
28
|
+
.describe('Language code for label resolution of QID values (e.g., "en", "de").'),
|
|
29
|
+
resolve_labels: z
|
|
30
|
+
.boolean()
|
|
31
|
+
.default(true)
|
|
32
|
+
.describe('Resolve wikibase-item value QIDs to human-readable labels via a batched label call. ' +
|
|
33
|
+
'Set to false to skip label resolution and return raw QIDs only (faster, smaller payload).'),
|
|
34
|
+
}),
|
|
35
|
+
output: z.object({
|
|
36
|
+
id: z.string().describe('The entity ID whose statements were fetched.'),
|
|
37
|
+
// Statement values are from a dynamic external API (12+ data types, each with a different shape).
|
|
38
|
+
// Passthrough preserves all normalized fields in structuredContent without over-typing each branch.
|
|
39
|
+
statements: z
|
|
40
|
+
.record(z.string(), z.array(z.object({}).passthrough()))
|
|
41
|
+
.describe('Map of property ID to array of normalized statements. ' +
|
|
42
|
+
'Each statement has id, rank, property, value (with type-specific fields), ' +
|
|
43
|
+
'and optional qualifiers and references arrays.'),
|
|
44
|
+
propertyCount: z.number().describe('Number of distinct properties returned.'),
|
|
45
|
+
statementCount: z.number().describe('Total number of statement objects across all properties.'),
|
|
46
|
+
labelsResolved: z
|
|
47
|
+
.boolean()
|
|
48
|
+
.describe('True when QID values were resolved to labels. False when resolve_labels was set to false.'),
|
|
49
|
+
}),
|
|
50
|
+
errors: [
|
|
51
|
+
{
|
|
52
|
+
reason: 'entity_not_found',
|
|
53
|
+
code: JsonRpcErrorCode.NotFound,
|
|
54
|
+
when: 'No entity exists at this QID.',
|
|
55
|
+
recovery: 'Verify the ID with wikidata_search_entities or wikidata_get_labels.',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
reason: 'invalid_id',
|
|
59
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
60
|
+
when: 'ID is not a valid Q-ID or P-ID format.',
|
|
61
|
+
recovery: 'Supply a valid Q-ID (Q followed by digits) or P-ID (P followed by digits).',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
async handler(input, ctx) {
|
|
65
|
+
const id = normalizeId(input.id);
|
|
66
|
+
if (!isQId(id) && !isPId(id)) {
|
|
67
|
+
throw ctx.fail('invalid_id', `"${input.id}" is not a valid Wikidata ID.`, {
|
|
68
|
+
...ctx.recoveryFor('invalid_id'),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const svc = getWikidataRestService();
|
|
72
|
+
ctx.log.info('Fetching statements', {
|
|
73
|
+
id,
|
|
74
|
+
properties: input.properties,
|
|
75
|
+
language: input.language,
|
|
76
|
+
resolve_labels: input.resolve_labels,
|
|
77
|
+
});
|
|
78
|
+
let rawStatements;
|
|
79
|
+
try {
|
|
80
|
+
rawStatements = await svc.fetchStatements(id, input.properties, ctx);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
if (err?.data?.status === 404) {
|
|
84
|
+
throw ctx.fail('entity_not_found', `No entity found for ID "${id}".`, {
|
|
85
|
+
...ctx.recoveryFor('entity_not_found'),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
throw err;
|
|
89
|
+
}
|
|
90
|
+
// Collect all value QIDs for label resolution
|
|
91
|
+
const labelMap = {};
|
|
92
|
+
if (input.resolve_labels) {
|
|
93
|
+
const qidsToResolve = new Set();
|
|
94
|
+
for (const stmts of Object.values(rawStatements)) {
|
|
95
|
+
for (const stmt of stmts) {
|
|
96
|
+
if (stmt.property?.data_type === 'wikibase-item') {
|
|
97
|
+
const content = stmt.value?.content;
|
|
98
|
+
if (content?.id)
|
|
99
|
+
qidsToResolve.add(content.id);
|
|
100
|
+
}
|
|
101
|
+
// Also check qualifier values
|
|
102
|
+
for (const q of stmt.qualifiers ?? []) {
|
|
103
|
+
if (q.property?.data_type === 'wikibase-item') {
|
|
104
|
+
const qContent = q.value?.content;
|
|
105
|
+
if (qContent?.id)
|
|
106
|
+
qidsToResolve.add(qContent.id);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (qidsToResolve.size > 0) {
|
|
112
|
+
const labelData = await svc.fetchLabels([...qidsToResolve], [input.language], ctx);
|
|
113
|
+
for (const [qid, data] of Object.entries(labelData)) {
|
|
114
|
+
const label = data.labels[input.language];
|
|
115
|
+
if (label)
|
|
116
|
+
labelMap[qid] = label;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Normalize statements
|
|
121
|
+
const normalized = {};
|
|
122
|
+
for (const [propertyId, stmts] of Object.entries(rawStatements)) {
|
|
123
|
+
normalized[propertyId] = normalizeStatements(propertyId, stmts, labelMap);
|
|
124
|
+
}
|
|
125
|
+
const statementCount = Object.values(normalized).reduce((acc, stmts) => acc + stmts.length, 0);
|
|
126
|
+
return {
|
|
127
|
+
id,
|
|
128
|
+
statements: normalized,
|
|
129
|
+
propertyCount: Object.keys(normalized).length,
|
|
130
|
+
statementCount,
|
|
131
|
+
labelsResolved: input.resolve_labels,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
format: (result) => {
|
|
135
|
+
const lines = [
|
|
136
|
+
`## Statements for ${result.id}`,
|
|
137
|
+
`**Properties:** ${result.propertyCount} | **Total statements:** ${result.statementCount} | **Labels resolved:** ${result.labelsResolved}`,
|
|
138
|
+
];
|
|
139
|
+
for (const [propId, rawStmts] of Object.entries(result.statements)) {
|
|
140
|
+
lines.push('');
|
|
141
|
+
lines.push(`### ${propId}`);
|
|
142
|
+
for (const rawStmt of rawStmts) {
|
|
143
|
+
const stmt = rawStmt;
|
|
144
|
+
if (!stmt?.value)
|
|
145
|
+
continue;
|
|
146
|
+
const rankIndicator = stmt.rank === 'preferred' ? ' ★' : stmt.rank === 'deprecated' ? ' ~~(deprecated)~~' : '';
|
|
147
|
+
const valueStr = formatStatementValue(stmt.value);
|
|
148
|
+
lines.push(`- ${valueStr}${rankIndicator}`);
|
|
149
|
+
if (stmt.qualifiers?.length) {
|
|
150
|
+
for (const q of stmt.qualifiers) {
|
|
151
|
+
if (!q?.value)
|
|
152
|
+
continue;
|
|
153
|
+
lines.push(` - ${q.property}: ${formatStatementValue(q.value)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
function formatStatementValue(value) {
|
|
162
|
+
switch (value.type) {
|
|
163
|
+
case 'wikibase-item':
|
|
164
|
+
return value.label ? `${value.label} (${value.qid})` : value.qid;
|
|
165
|
+
case 'time':
|
|
166
|
+
return value.time;
|
|
167
|
+
case 'quantity':
|
|
168
|
+
return value.unitLabel
|
|
169
|
+
? `${value.amount} ${value.unitLabel}`
|
|
170
|
+
: value.unit && value.unit !== '1'
|
|
171
|
+
? `${value.amount} (${value.unit})`
|
|
172
|
+
: value.amount;
|
|
173
|
+
case 'string':
|
|
174
|
+
return value.value;
|
|
175
|
+
case 'monolingualtext':
|
|
176
|
+
return `${value.text} [${value.language}]`;
|
|
177
|
+
case 'globe-coordinate':
|
|
178
|
+
return `${value.latitude}, ${value.longitude}`;
|
|
179
|
+
case 'other':
|
|
180
|
+
return JSON.stringify(value.raw ?? '');
|
|
181
|
+
default:
|
|
182
|
+
return '(unknown)';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=get-statements.tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-statements.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-statements.tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Look up a Wikidata entity by an external identifier (DOI, PubMed ID, ORCID, etc.).
|
|
3
|
+
* @module mcp-server/tools/definitions/resolve-external-id.tool
|
|
4
|
+
*/
|
|
5
|
+
import { z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
export declare const wikidataResolveExternalId: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
|
|
8
|
+
property: z.ZodString;
|
|
9
|
+
value: z.ZodString;
|
|
10
|
+
language: z.ZodDefault<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
12
|
+
match: z.ZodNullable<z.ZodObject<{
|
|
13
|
+
id: z.ZodString;
|
|
14
|
+
label: z.ZodString;
|
|
15
|
+
description: z.ZodString;
|
|
16
|
+
url: z.ZodString;
|
|
17
|
+
}, z.core.$strip>>;
|
|
18
|
+
property: z.ZodString;
|
|
19
|
+
value: z.ZodString;
|
|
20
|
+
multipleMatches: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
21
|
+
id: z.ZodString;
|
|
22
|
+
label: z.ZodString;
|
|
23
|
+
}, z.core.$strip>>>;
|
|
24
|
+
}, z.core.$strip>, readonly [{
|
|
25
|
+
readonly reason: "invalid_property";
|
|
26
|
+
readonly code: JsonRpcErrorCode.InvalidParams;
|
|
27
|
+
readonly when: "Property ID is not in P+digits format.";
|
|
28
|
+
readonly recovery: "Supply a valid P-ID (P followed by digits, e.g. P356 for DOI).";
|
|
29
|
+
}]>;
|
|
30
|
+
//# sourceMappingURL=resolve-external-id.tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-external-id.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/resolve-external-id.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AA0BjE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;GAqMpC,CAAC"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Look up a Wikidata entity by an external identifier (DOI, PubMed ID, ORCID, etc.).
|
|
3
|
+
* @module mcp-server/tools/definitions/resolve-external-id.tool
|
|
4
|
+
*/
|
|
5
|
+
import { tool, z } from '@cyanheads/mcp-ts-core';
|
|
6
|
+
import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
7
|
+
import { getWikidataSparqlService } from '../../../services/wikidata/wikidata-sparql-service.js';
|
|
8
|
+
/** Normalize a value to the canonical form Wikidata stores it in for known P-IDs. */
|
|
9
|
+
function normalizeExternalId(property, value) {
|
|
10
|
+
const upperProp = property.toUpperCase();
|
|
11
|
+
switch (upperProp) {
|
|
12
|
+
case 'P356':
|
|
13
|
+
// DOI: stored uppercase
|
|
14
|
+
return value.toUpperCase();
|
|
15
|
+
case 'P698':
|
|
16
|
+
// PubMed ID: strip "PMID:" prefix, keep numeric
|
|
17
|
+
return value.replace(/^PMID[:\s]*/i, '').trim();
|
|
18
|
+
case 'P496': {
|
|
19
|
+
// ORCID: normalize to 0000-0000-0000-000X format
|
|
20
|
+
const stripped = value.replace(/[-\s]/g, '');
|
|
21
|
+
if (stripped.length === 16) {
|
|
22
|
+
return `${stripped.slice(0, 4)}-${stripped.slice(4, 8)}-${stripped.slice(8, 12)}-${stripped.slice(12)}`;
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
default:
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export const wikidataResolveExternalId = tool('wikidata_resolve_external_id', {
|
|
31
|
+
title: 'Resolve Wikidata External ID',
|
|
32
|
+
description: 'Look up a Wikidata entity by an external identifier such as a DOI, PubMed ID, ORCID iD, or OpenAlex ID. ' +
|
|
33
|
+
'Returns match=<entity> on success, match=null when not found, and match=null with multipleMatches populated ' +
|
|
34
|
+
'when a Wikidata data integrity issue causes more than one entity to claim the same external ID. ' +
|
|
35
|
+
'Common cross-server join use cases: CrossRef DOI → Wikidata paper QID (P356), ' +
|
|
36
|
+
'PubMed PMID → Wikidata paper QID (P698), ORCID → author QID (P496), ' +
|
|
37
|
+
'OpenAlex ID → entity QID (P10283). ' +
|
|
38
|
+
'Known value normalization is applied automatically: DOIs are uppercased, PMID prefixes stripped, ORCID hyphens normalized.',
|
|
39
|
+
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
40
|
+
input: z.object({
|
|
41
|
+
property: z
|
|
42
|
+
.string()
|
|
43
|
+
.min(1)
|
|
44
|
+
.describe('P-ID of the external identifier property (e.g., "P356" for DOI, "P698" for PubMed ID, ' +
|
|
45
|
+
'"P496" for ORCID, "P10283" for OpenAlex ID, "P345" for IMDb ID).'),
|
|
46
|
+
value: z
|
|
47
|
+
.string()
|
|
48
|
+
.min(1)
|
|
49
|
+
.describe('The external identifier value to look up (e.g., "10.1038/nature01234" for a DOI, ' +
|
|
50
|
+
'"32283226" for a PubMed ID, "0000-0002-1825-0097" for an ORCID).'),
|
|
51
|
+
language: z
|
|
52
|
+
.string()
|
|
53
|
+
.default('en')
|
|
54
|
+
.describe('Language code for label and description in the response (e.g., "en", "de").'),
|
|
55
|
+
}),
|
|
56
|
+
output: z.object({
|
|
57
|
+
match: z
|
|
58
|
+
.object({
|
|
59
|
+
id: z.string().describe('Wikidata Q-ID of the matching entity (e.g., "Q12345").'),
|
|
60
|
+
label: z
|
|
61
|
+
.string()
|
|
62
|
+
.describe('Display label in the requested language, or empty string if unavailable.'),
|
|
63
|
+
description: z
|
|
64
|
+
.string()
|
|
65
|
+
.describe('Short description in the requested language, or empty string if unavailable.'),
|
|
66
|
+
url: z
|
|
67
|
+
.string()
|
|
68
|
+
.describe('Wikidata entity page URL (e.g., "https://www.wikidata.org/wiki/Q12345").'),
|
|
69
|
+
})
|
|
70
|
+
.nullable()
|
|
71
|
+
.describe('Matching entity, or null when no Wikidata entity claims this external identifier ' +
|
|
72
|
+
'(including the case where multipleMatches is populated).'),
|
|
73
|
+
property: z.string().describe('The P-ID used for the lookup.'),
|
|
74
|
+
value: z
|
|
75
|
+
.string()
|
|
76
|
+
.describe('The normalized value that was searched (may differ from input due to canonicalization).'),
|
|
77
|
+
multipleMatches: z
|
|
78
|
+
.array(z
|
|
79
|
+
.object({
|
|
80
|
+
id: z.string().describe('Q-ID of a matching entity.'),
|
|
81
|
+
label: z.string().describe('Display label of this match.'),
|
|
82
|
+
})
|
|
83
|
+
.describe('One of the entities that claims this external identifier.'))
|
|
84
|
+
.optional()
|
|
85
|
+
.describe('Present when more than one Wikidata entity claims this external ID (data integrity issue). ' +
|
|
86
|
+
'match is null when this field is present. Inspect the list and select the correct QID manually.'),
|
|
87
|
+
}),
|
|
88
|
+
errors: [
|
|
89
|
+
{
|
|
90
|
+
reason: 'invalid_property',
|
|
91
|
+
code: JsonRpcErrorCode.InvalidParams,
|
|
92
|
+
when: 'Property ID is not in P+digits format.',
|
|
93
|
+
recovery: 'Supply a valid P-ID (P followed by digits, e.g. P356 for DOI).',
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
async handler(input, ctx) {
|
|
97
|
+
const prop = input.property.toUpperCase();
|
|
98
|
+
if (!/^P\d+$/.test(prop)) {
|
|
99
|
+
throw ctx.fail('invalid_property', `"${input.property}" is not a valid property ID. Expected P followed by digits.`, { ...ctx.recoveryFor('invalid_property') });
|
|
100
|
+
}
|
|
101
|
+
const normalizedValue = normalizeExternalId(prop, input.value);
|
|
102
|
+
ctx.log.info('Resolving external ID', {
|
|
103
|
+
property: prop,
|
|
104
|
+
value: normalizedValue,
|
|
105
|
+
language: input.language,
|
|
106
|
+
});
|
|
107
|
+
const svc = getWikidataSparqlService();
|
|
108
|
+
// Escape value for SPARQL string literal — double quotes need escaping
|
|
109
|
+
const escapedValue = normalizedValue.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
110
|
+
const sparql = `SELECT ?item ?itemLabel ?itemDescription WHERE {
|
|
111
|
+
?item wdt:${prop} "${escapedValue}" .
|
|
112
|
+
OPTIONAL { ?item rdfs:label ?itemLabel . FILTER(LANG(?itemLabel) = "${input.language}") }
|
|
113
|
+
OPTIONAL { ?item schema:description ?itemDescription . FILTER(LANG(?itemDescription) = "${input.language}") }
|
|
114
|
+
}
|
|
115
|
+
LIMIT 5`;
|
|
116
|
+
const response = await svc.query(sparql, '', undefined, ctx);
|
|
117
|
+
const bindings = response.results.bindings;
|
|
118
|
+
if (bindings.length === 0) {
|
|
119
|
+
return { match: null, property: prop, value: normalizedValue };
|
|
120
|
+
}
|
|
121
|
+
// Extract QIDs
|
|
122
|
+
const matches = bindings.map((b) => ({
|
|
123
|
+
id: b.item?.value.replace('http://www.wikidata.org/entity/', '') ?? '',
|
|
124
|
+
label: b.itemLabel?.value ?? '',
|
|
125
|
+
description: b.itemDescription?.value ?? '',
|
|
126
|
+
}));
|
|
127
|
+
// Deduplicate by QID (SPARQL may return multiple rows for same item with different labels)
|
|
128
|
+
const seen = new Set();
|
|
129
|
+
const unique = matches.filter((m) => {
|
|
130
|
+
if (seen.has(m.id))
|
|
131
|
+
return false;
|
|
132
|
+
seen.add(m.id);
|
|
133
|
+
return true;
|
|
134
|
+
});
|
|
135
|
+
if (unique.length > 1) {
|
|
136
|
+
// Wikidata data integrity issue — multiple entities claim the same external ID.
|
|
137
|
+
// Return all matches so the agent can pick the right one.
|
|
138
|
+
return {
|
|
139
|
+
match: null,
|
|
140
|
+
property: prop,
|
|
141
|
+
value: normalizedValue,
|
|
142
|
+
multipleMatches: unique.map((m) => ({ id: m.id, label: m.label })),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// unique.length === 1 guaranteed by the preceding check
|
|
146
|
+
const m = unique[0];
|
|
147
|
+
return {
|
|
148
|
+
match: {
|
|
149
|
+
id: m.id,
|
|
150
|
+
label: m.label,
|
|
151
|
+
description: m.description,
|
|
152
|
+
url: `https://www.wikidata.org/wiki/${m.id}`,
|
|
153
|
+
},
|
|
154
|
+
property: prop,
|
|
155
|
+
value: normalizedValue,
|
|
156
|
+
};
|
|
157
|
+
},
|
|
158
|
+
format: (result) => {
|
|
159
|
+
const lines = [
|
|
160
|
+
`**Property:** ${result.property} | **Value searched:** ${result.value}`,
|
|
161
|
+
];
|
|
162
|
+
if (result.match !== null) {
|
|
163
|
+
lines.push(`**Match:** ${result.match.label || result.match.id}`);
|
|
164
|
+
lines.push('');
|
|
165
|
+
lines.push(`## ${result.match.label || result.match.id}`);
|
|
166
|
+
lines.push(`**QID:** ${result.match.id}`);
|
|
167
|
+
if (result.match.description)
|
|
168
|
+
lines.push(`**Description:** ${result.match.description}`);
|
|
169
|
+
lines.push(`**URL:** ${result.match.url}`);
|
|
170
|
+
}
|
|
171
|
+
else if (result.multipleMatches?.length) {
|
|
172
|
+
lines.push(`**Match:** multiple (${result.multipleMatches.length}) — Wikidata data integrity issue`);
|
|
173
|
+
lines.push('\n**Multiple entities claim this external ID — select the correct QID manually:**');
|
|
174
|
+
for (const entry of result.multipleMatches) {
|
|
175
|
+
lines.push(`- ${entry.id}: ${entry.label || '(no label)'}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
lines.push('**Match:** none');
|
|
180
|
+
lines.push('\n> No Wikidata entity found for this external identifier.');
|
|
181
|
+
}
|
|
182
|
+
// Render multipleMatches even when match is present — satisfies format parity for linter
|
|
183
|
+
// synthetic variants that populate both fields simultaneously.
|
|
184
|
+
if (result.match !== null && result.multipleMatches?.length) {
|
|
185
|
+
lines.push('\n**Also matched:**');
|
|
186
|
+
for (const entry of result.multipleMatches) {
|
|
187
|
+
lines.push(`- ${entry.id}: ${entry.label || '(no label)'}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return [{ type: 'text', text: lines.join('\n') }];
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
//# sourceMappingURL=resolve-external-id.tool.js.map
|