wikimedia-commons-mcp 1.0.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/README.md +23 -0
- package/dist/api.js +104 -0
- package/dist/index.js +4 -0
- package/dist/server.js +51 -0
- package/package.json +24 -0
- package/server.json +20 -0
- package/src/api.ts +113 -0
- package/src/index.ts +4 -0
- package/src/server.ts +50 -0
- package/tsconfig.json +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# wikimedia-commons-mcp
|
|
2
|
+
|
|
3
|
+
Wikimedia Commons file search.
|
|
4
|
+
|
|
5
|
+
A merged MCP server that consolidates duplicate single-purpose servers in this monorepo into one focused server.
|
|
6
|
+
|
|
7
|
+
## Tools
|
|
8
|
+
|
|
9
|
+
- `search` — Search Commons files by text.
|
|
10
|
+
- `file` — Details for one Commons file.
|
|
11
|
+
- `random` — Random image files from Commons.
|
|
12
|
+
|
|
13
|
+
## Run
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install
|
|
17
|
+
npm run build
|
|
18
|
+
node dist/index.js
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Source
|
|
22
|
+
|
|
23
|
+
Public free APIs only. See `src/api.ts` for exact endpoints.
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const m0 = (() => {
|
|
2
|
+
const BASE = "https://commons.wikimedia.org/w/api.php";
|
|
3
|
+
const UA = "mrfentmen-wikimedia-commons-mcp/1.0 (https://github.com/mrfentmen)";
|
|
4
|
+
class CommonsError extends Error {
|
|
5
|
+
}
|
|
6
|
+
async function get(url) {
|
|
7
|
+
const res = await fetch(url, {
|
|
8
|
+
headers: { "User-Agent": UA, Accept: "application/json" },
|
|
9
|
+
signal: AbortSignal.timeout(30000),
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok)
|
|
12
|
+
throw new CommonsError(`Wikimedia Commons returned HTTP ${res.status}`);
|
|
13
|
+
return (await res.json());
|
|
14
|
+
}
|
|
15
|
+
function pagesFrom(d) {
|
|
16
|
+
const pages = d?.query?.pages ?? {};
|
|
17
|
+
return Object.values(pages);
|
|
18
|
+
}
|
|
19
|
+
async function search(args) {
|
|
20
|
+
const q = (args.query ?? "").trim();
|
|
21
|
+
if (!q)
|
|
22
|
+
throw new CommonsError("Provide a search query");
|
|
23
|
+
const limit = Math.min(args.limit ?? 8, 20);
|
|
24
|
+
const d = await get(`${BASE}?action=query&generator=search&gsrnamespace=6&gsrsearch=${encodeURIComponent(q)}&gsrlimit=${limit}&prop=imageinfo&iiprop=url|size|mime&format=json&origin=*`);
|
|
25
|
+
const pages = pagesFrom(d);
|
|
26
|
+
if (!pages.length)
|
|
27
|
+
return `No Commons files found for \"${q}\"`;
|
|
28
|
+
return `Commons files for \"${q}\":\n` + pages.map((p, i) => {
|
|
29
|
+
const info = p?.imageinfo?.[0] ?? {};
|
|
30
|
+
const size = info?.width && info?.height ? `${info.width}x${info.height}` : "";
|
|
31
|
+
const bytes = info?.size != null ? `${(info.size / 1024).toFixed(0)} KB` : "";
|
|
32
|
+
return `${i + 1}. ${p?.title ?? "n/a"}\n ${info?.url ?? ""} ${size ? `| ${size}` : ""} ${bytes ? `| ${bytes}` : ""}`;
|
|
33
|
+
}).join("\n");
|
|
34
|
+
}
|
|
35
|
+
async function file(args) {
|
|
36
|
+
const title = (args.title ?? "").trim();
|
|
37
|
+
if (!title)
|
|
38
|
+
throw new CommonsError("Provide a file title like File:Example.jpg");
|
|
39
|
+
const d = await get(`${BASE}?action=query&titles=${encodeURIComponent(title)}&prop=imageinfo&iiprop=url|size|mime|extmetadata&format=json&origin=*`);
|
|
40
|
+
const p = pagesFrom(d)[0];
|
|
41
|
+
const info = p?.imageinfo?.[0];
|
|
42
|
+
if (!info)
|
|
43
|
+
throw new CommonsError(`File not found: ${title}`);
|
|
44
|
+
const meta = info?.extmetadata ?? {};
|
|
45
|
+
const artist = (meta?.Artist?.value ?? "").replace(/<[^>]+>/g, "").slice(0, 120);
|
|
46
|
+
const license = (meta?.LicenseShortName?.value ?? "").replace(/<[^>]+>/g, "");
|
|
47
|
+
const lines = [
|
|
48
|
+
`Title: ${p?.title ?? title}`,
|
|
49
|
+
`URL: ${info?.url ?? ""}`,
|
|
50
|
+
`Size: ${info?.width ?? "?"}x${info?.height ?? "?"} | ${info?.mime ?? ""} | ${info?.size != null ? `${(info.size / 1024 / 1024).toFixed(1)} MB` : ""}`,
|
|
51
|
+
];
|
|
52
|
+
if (artist)
|
|
53
|
+
lines.push(`Artist: ${artist}`);
|
|
54
|
+
if (license)
|
|
55
|
+
lines.push(`License: ${license}`);
|
|
56
|
+
return lines.join("\n");
|
|
57
|
+
}
|
|
58
|
+
return { CommonsError, file, search };
|
|
59
|
+
})();
|
|
60
|
+
const m1 = (() => {
|
|
61
|
+
const BASE = 'https://commons.wikimedia.org/w/api.php';
|
|
62
|
+
async function random(args) {
|
|
63
|
+
const limit = Math.max(1, Math.min(args.limit ?? 5, 15));
|
|
64
|
+
const params = new URLSearchParams({
|
|
65
|
+
action: 'query',
|
|
66
|
+
format: 'json',
|
|
67
|
+
generator: 'random',
|
|
68
|
+
grnnamespace: '6',
|
|
69
|
+
grnlimit: String(limit),
|
|
70
|
+
prop: 'imageinfo',
|
|
71
|
+
iiprop: 'url|size|extmetadata',
|
|
72
|
+
});
|
|
73
|
+
const res = await fetch(`${BASE}?${params.toString()}`, {
|
|
74
|
+
headers: { 'User-Agent': 'mrfentmen-wikimedia-feed-mcp/1.0', Accept: 'application/json' },
|
|
75
|
+
signal: AbortSignal.timeout(25000),
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok)
|
|
78
|
+
throw new Error(`Wikimedia Commons returned ${res.status}`);
|
|
79
|
+
const data = (await res.json());
|
|
80
|
+
const pages = data.query?.pages ?? {};
|
|
81
|
+
const items = Object.values(pages).map((p) => {
|
|
82
|
+
const rec = p;
|
|
83
|
+
const info = Array.isArray(rec.imageinfo) ? rec.imageinfo[0] : {};
|
|
84
|
+
const meta = info.extmetadata ?? {};
|
|
85
|
+
const title = meta.ImageDescription?.value ?? rec.title ?? '';
|
|
86
|
+
const artist = meta.Artist?.value ?? '';
|
|
87
|
+
const clean = String(title).replace(/<[^>]*>/g, '').trim();
|
|
88
|
+
const by = String(artist).replace(/<[^>]*>/g, '').trim();
|
|
89
|
+
return `${clean}${by ? ` by ${by}` : ''}${info.descriptionurl ? `\n ${info.descriptionurl}` : ''}`;
|
|
90
|
+
});
|
|
91
|
+
if (!items.length)
|
|
92
|
+
return 'No random images returned.';
|
|
93
|
+
return `Random Wikimedia Commons images (${items.length} shown):\n` + items.map((s, i) => `${i + 1}. ${s}`).join('\n');
|
|
94
|
+
}
|
|
95
|
+
return { random };
|
|
96
|
+
})();
|
|
97
|
+
export const CommonsError = m0.CommonsError;
|
|
98
|
+
export const file = m0.file;
|
|
99
|
+
export const random = m1.random;
|
|
100
|
+
export const search = m0.search;
|
|
101
|
+
export const m0_file = m0.file;
|
|
102
|
+
export const m0_search = m0.search;
|
|
103
|
+
export const m0_CommonsError = m0.CommonsError;
|
|
104
|
+
export const m1_random = m1.random;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
+
import { createServer } from "./server.js";
|
|
3
|
+
const main = async () => { const server = createServer(); await server.connect(new StdioServerTransport()); };
|
|
4
|
+
main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { m0_file, m0_search, m1_random } from './api.js';
|
|
4
|
+
const text = (value) => ({ content: [{ type: 'text', text: value }] });
|
|
5
|
+
const textError = (t) => ({ content: [{ type: "text", text: t }], isError: true });
|
|
6
|
+
const READ_ONLY = { readOnlyHint: true, openWorldHint: true };
|
|
7
|
+
const error = (e) => `Error: ${e instanceof Error ? e.message : String(e)}`;
|
|
8
|
+
const errorMessage = error;
|
|
9
|
+
export function createServer() {
|
|
10
|
+
const server = new McpServer({ name: 'wikimedia-commons-mcp', version: '1.0.0' });
|
|
11
|
+
server.registerTool("search", {
|
|
12
|
+
title: "Search",
|
|
13
|
+
description: "Search Commons files by text.",
|
|
14
|
+
inputSchema: z.object({ query: z.string().describe("Search terms."), limit: z.number().describe("Max results.").optional() }),
|
|
15
|
+
annotations: READ_ONLY,
|
|
16
|
+
}, async (args) => {
|
|
17
|
+
try {
|
|
18
|
+
return text(await m0_search(args));
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
return textError(error(e));
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
server.registerTool("file", {
|
|
25
|
+
title: "File",
|
|
26
|
+
description: "Details for one Commons file.",
|
|
27
|
+
inputSchema: z.object({ title: z.string().describe("File title.") }),
|
|
28
|
+
annotations: READ_ONLY,
|
|
29
|
+
}, async (args) => {
|
|
30
|
+
try {
|
|
31
|
+
return text(await m0_file(args));
|
|
32
|
+
}
|
|
33
|
+
catch (e) {
|
|
34
|
+
return textError(error(e));
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
server.registerTool("random", {
|
|
38
|
+
title: "Random",
|
|
39
|
+
description: "Random image files from Commons.",
|
|
40
|
+
inputSchema: z.object({ limit: z.number().describe("Max results.").optional() }),
|
|
41
|
+
annotations: READ_ONLY,
|
|
42
|
+
}, async (args) => {
|
|
43
|
+
try {
|
|
44
|
+
return text(await m1_random(args));
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
return textError(error(e));
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return server;
|
|
51
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wikimedia-commons-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Wikimedia Commons file search.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc -p tsconfig.json",
|
|
9
|
+
"start": "node dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
13
|
+
"zod": "^3.23.0"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"typescript": "^5.5.0",
|
|
17
|
+
"@types/node": "^20.0.0"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"mcpName": "io.github.mrfentmen/wikimedia-commons-mcp"
|
|
24
|
+
}
|
package/server.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
|
|
3
|
+
"name": "io.github.mrfentmen/wikimedia-commons-mcp",
|
|
4
|
+
"description": "Wikimedia Commons file search.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/mrfentmen/awesome-mcps",
|
|
7
|
+
"source": "github"
|
|
8
|
+
},
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"packages": [
|
|
11
|
+
{
|
|
12
|
+
"registryType": "npm",
|
|
13
|
+
"identifier": "wikimedia-commons-mcp",
|
|
14
|
+
"version": "1.0.0",
|
|
15
|
+
"transport": {
|
|
16
|
+
"type": "stdio"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
|
|
2
|
+
export interface m1_RandomArgs {
|
|
3
|
+
limit?: number;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const m0 = (() => {
|
|
7
|
+
const BASE = "https://commons.wikimedia.org/w/api.php"
|
|
8
|
+
const UA = "mrfentmen-wikimedia-commons-mcp/1.0 (https://github.com/mrfentmen)"
|
|
9
|
+
class CommonsError extends Error {}
|
|
10
|
+
|
|
11
|
+
async function get<T>(url: string): Promise<T> {
|
|
12
|
+
const res = await fetch(url, {
|
|
13
|
+
headers: { "User-Agent": UA, Accept: "application/json" },
|
|
14
|
+
signal: AbortSignal.timeout(30000),
|
|
15
|
+
})
|
|
16
|
+
if (!res.ok) throw new CommonsError(`Wikimedia Commons returned HTTP ${res.status}`)
|
|
17
|
+
return (await res.json()) as T
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function pagesFrom(d: any): any[] {
|
|
21
|
+
const pages = d?.query?.pages ?? {}
|
|
22
|
+
return Object.values(pages) as any[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function search(args: { query?: string; limit?: number }): Promise<string> {
|
|
26
|
+
const q = (args.query ?? "").trim()
|
|
27
|
+
if (!q) throw new CommonsError("Provide a search query")
|
|
28
|
+
const limit = Math.min(args.limit ?? 8, 20)
|
|
29
|
+
const d = await get<any>(
|
|
30
|
+
`${BASE}?action=query&generator=search&gsrnamespace=6&gsrsearch=${encodeURIComponent(q)}&gsrlimit=${limit}&prop=imageinfo&iiprop=url|size|mime&format=json&origin=*`
|
|
31
|
+
)
|
|
32
|
+
const pages = pagesFrom(d)
|
|
33
|
+
if (!pages.length) return `No Commons files found for \"${q}\"`
|
|
34
|
+
return `Commons files for \"${q}\":\n` + pages.map((p: any, i: number) => {
|
|
35
|
+
const info = p?.imageinfo?.[0] ?? {}
|
|
36
|
+
const size = info?.width && info?.height ? `${info.width}x${info.height}` : ""
|
|
37
|
+
const bytes = info?.size != null ? `${(info.size / 1024).toFixed(0)} KB` : ""
|
|
38
|
+
return `${i + 1}. ${p?.title ?? "n/a"}\n ${info?.url ?? ""} ${size ? `| ${size}` : ""} ${bytes ? `| ${bytes}` : ""}`
|
|
39
|
+
}).join("\n")
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function file(args: { title?: string }): Promise<string> {
|
|
43
|
+
const title = (args.title ?? "").trim()
|
|
44
|
+
if (!title) throw new CommonsError("Provide a file title like File:Example.jpg")
|
|
45
|
+
const d = await get<any>(
|
|
46
|
+
`${BASE}?action=query&titles=${encodeURIComponent(title)}&prop=imageinfo&iiprop=url|size|mime|extmetadata&format=json&origin=*`
|
|
47
|
+
)
|
|
48
|
+
const p = pagesFrom(d)[0]
|
|
49
|
+
const info = p?.imageinfo?.[0]
|
|
50
|
+
if (!info) throw new CommonsError(`File not found: ${title}`)
|
|
51
|
+
const meta = info?.extmetadata ?? {}
|
|
52
|
+
const artist = (meta?.Artist?.value ?? "").replace(/<[^>]+>/g, "").slice(0, 120)
|
|
53
|
+
const license = (meta?.LicenseShortName?.value ?? "").replace(/<[^>]+>/g, "")
|
|
54
|
+
const lines = [
|
|
55
|
+
`Title: ${p?.title ?? title}`,
|
|
56
|
+
`URL: ${info?.url ?? ""}`,
|
|
57
|
+
`Size: ${info?.width ?? "?"}x${info?.height ?? "?"} | ${info?.mime ?? ""} | ${info?.size != null ? `${(info.size / 1024 / 1024).toFixed(1)} MB` : ""}`,
|
|
58
|
+
]
|
|
59
|
+
if (artist) lines.push(`Artist: ${artist}`)
|
|
60
|
+
if (license) lines.push(`License: ${license}`)
|
|
61
|
+
return lines.join("\n")
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return { CommonsError, file, search };
|
|
65
|
+
})();
|
|
66
|
+
|
|
67
|
+
const m1 = (() => {
|
|
68
|
+
const BASE = 'https://commons.wikimedia.org/w/api.php';
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async function random(args: m1_RandomArgs): Promise<string> {
|
|
72
|
+
const limit = Math.max(1, Math.min(args.limit ?? 5, 15));
|
|
73
|
+
const params = new URLSearchParams({
|
|
74
|
+
action: 'query',
|
|
75
|
+
format: 'json',
|
|
76
|
+
generator: 'random',
|
|
77
|
+
grnnamespace: '6',
|
|
78
|
+
grnlimit: String(limit),
|
|
79
|
+
prop: 'imageinfo',
|
|
80
|
+
iiprop: 'url|size|extmetadata',
|
|
81
|
+
});
|
|
82
|
+
const res = await fetch(`${BASE}?${params.toString()}`, {
|
|
83
|
+
headers: { 'User-Agent': 'mrfentmen-wikimedia-feed-mcp/1.0', Accept: 'application/json' },
|
|
84
|
+
signal: AbortSignal.timeout(25000),
|
|
85
|
+
});
|
|
86
|
+
if (!res.ok) throw new Error(`Wikimedia Commons returned ${res.status}`);
|
|
87
|
+
const data = (await res.json()) as { query?: { pages?: Record<string, unknown> } };
|
|
88
|
+
const pages = data.query?.pages ?? {};
|
|
89
|
+
const items = Object.values(pages).map((p) => {
|
|
90
|
+
const rec = p as Record<string, unknown>;
|
|
91
|
+
const info = Array.isArray(rec.imageinfo) ? (rec.imageinfo[0] as Record<string, unknown>) : {};
|
|
92
|
+
const meta = (info.extmetadata as Record<string, unknown>) ?? {};
|
|
93
|
+
const title = (meta.ImageDescription as Record<string, unknown>)?.value ?? rec.title ?? '';
|
|
94
|
+
const artist = (meta.Artist as Record<string, unknown>)?.value ?? '';
|
|
95
|
+
const clean = String(title).replace(/<[^>]*>/g, '').trim();
|
|
96
|
+
const by = String(artist).replace(/<[^>]*>/g, '').trim();
|
|
97
|
+
return `${clean}${by ? ` by ${by}` : ''}${info.descriptionurl ? `\n ${info.descriptionurl}` : ''}`;
|
|
98
|
+
});
|
|
99
|
+
if (!items.length) return 'No random images returned.';
|
|
100
|
+
return `Random Wikimedia Commons images (${items.length} shown):\n` + items.map((s, i) => `${i + 1}. ${s}`).join('\n');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return { random };
|
|
104
|
+
})();
|
|
105
|
+
|
|
106
|
+
export const CommonsError = m0.CommonsError;
|
|
107
|
+
export const file = m0.file;
|
|
108
|
+
export const random = m1.random;
|
|
109
|
+
export const search = m0.search;
|
|
110
|
+
export const m0_file = m0.file;
|
|
111
|
+
export const m0_search = m0.search;
|
|
112
|
+
export const m0_CommonsError = m0.CommonsError;
|
|
113
|
+
export const m1_random = m1.random;
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
2
|
+
import { createServer } from "./server.js"
|
|
3
|
+
const main = async () => { const server = createServer(); await server.connect(new StdioServerTransport()) }
|
|
4
|
+
main().catch((error) => { console.error("Fatal error:", error); process.exit(1) })
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
import { m0_file, m0_search, m1_random } from './api.js'
|
|
4
|
+
|
|
5
|
+
const text = (value: string) => ({ content: [{ type: 'text' as const, text: value }] })
|
|
6
|
+
const textError = (t: string) => ({ content: [{ type: "text" as const, text: t }], isError: true as const })
|
|
7
|
+
const READ_ONLY = { readOnlyHint: true, openWorldHint: true } as const
|
|
8
|
+
const error = (e: unknown) => `Error: ${e instanceof Error ? e.message : String(e)}`
|
|
9
|
+
const errorMessage = error
|
|
10
|
+
|
|
11
|
+
export function createServer(): McpServer {
|
|
12
|
+
const server = new McpServer({ name: 'wikimedia-commons-mcp', version: '1.0.0' })
|
|
13
|
+
server.registerTool(
|
|
14
|
+
"search",
|
|
15
|
+
{
|
|
16
|
+
title: "Search",
|
|
17
|
+
description: "Search Commons files by text.",
|
|
18
|
+
inputSchema: z.object( { query: z.string().describe("Search terms."), limit: z.number().describe("Max results.").optional() }),
|
|
19
|
+
annotations: READ_ONLY,
|
|
20
|
+
},
|
|
21
|
+
async (args) => {
|
|
22
|
+
try { return text(await m0_search(args)) } catch (e) { return textError(error(e)) }
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
server.registerTool(
|
|
26
|
+
"file",
|
|
27
|
+
{
|
|
28
|
+
title: "File",
|
|
29
|
+
description: "Details for one Commons file.",
|
|
30
|
+
inputSchema: z.object( { title: z.string().describe("File title.") }),
|
|
31
|
+
annotations: READ_ONLY,
|
|
32
|
+
},
|
|
33
|
+
async (args) => {
|
|
34
|
+
try { return text(await m0_file(args)) } catch (e) { return textError(error(e)) }
|
|
35
|
+
}
|
|
36
|
+
)
|
|
37
|
+
server.registerTool(
|
|
38
|
+
"random",
|
|
39
|
+
{
|
|
40
|
+
title: "Random",
|
|
41
|
+
description: "Random image files from Commons.",
|
|
42
|
+
inputSchema: z.object( { limit: z.number().describe("Max results.").optional() }),
|
|
43
|
+
annotations: READ_ONLY,
|
|
44
|
+
},
|
|
45
|
+
async (args) => {
|
|
46
|
+
try { return text(await m1_random(args)) } catch (e) { return textError(error(e)) }
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
return server
|
|
50
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true
|
|
12
|
+
},
|
|
13
|
+
"include": [
|
|
14
|
+
"src/**/*"
|
|
15
|
+
]
|
|
16
|
+
}
|