@gesslar/mediawiki-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/.github/dependabot.yml +10 -0
- package/.github/workflows/Quality.yaml +21 -0
- package/.github/workflows/Release.yaml +17 -0
- package/LICENSE.txt +12 -0
- package/README.md +144 -0
- package/eslint.config.js +11 -0
- package/package.json +50 -0
- package/src/index.js +360 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "github-actions" # See documentation for possible values
|
|
4
|
+
directory: "/" # Location of package manifests
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "daily"
|
|
7
|
+
- package-ecosystem: "npm"
|
|
8
|
+
directory: "/"
|
|
9
|
+
schedule:
|
|
10
|
+
interval: "daily"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Quality
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
schedule:
|
|
10
|
+
- cron: "20 14 * * 1"
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
Quality:
|
|
14
|
+
permissions:
|
|
15
|
+
contents: read
|
|
16
|
+
uses: gesslar/Maint/.github/workflows/Quality.yaml@main
|
|
17
|
+
secrets: inherit
|
|
18
|
+
with:
|
|
19
|
+
package_manager: "auto"
|
|
20
|
+
perform_linting: "${{ vars.PERFORM_LINTING || 'yes' }}"
|
|
21
|
+
perform_testing: "${{ vars.PERFORM_TESTING || 'no' }}"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [closed]
|
|
6
|
+
branches: [main]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
Release:
|
|
10
|
+
if: github.event.pull_request.merged == true
|
|
11
|
+
uses: gesslar/Maint/.github/workflows/Release.yaml@main
|
|
12
|
+
secrets: inherit
|
|
13
|
+
with:
|
|
14
|
+
package_manager: "auto"
|
|
15
|
+
quality_check: "Quality"
|
|
16
|
+
permissions:
|
|
17
|
+
contents: write
|
package/LICENSE.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Copyright (C) 2026 by Brian M. Workman bmw@gesslar.dev
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
4
|
+
purpose with or without fee is hereby granted.
|
|
5
|
+
|
|
6
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
7
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
8
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
9
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
10
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
11
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
12
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# @gesslar/mediawiki-mcp
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io/) server that lets
|
|
4
|
+
MCP-compatible clients create, edit, delete, read, and search articles on a
|
|
5
|
+
MediaWiki instance.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js `>=24.11.0`
|
|
10
|
+
- A MediaWiki instance with API access
|
|
11
|
+
- A [bot account](https://www.mediawiki.org/wiki/Manual:Bot_passwords) with
|
|
12
|
+
sufficient permissions for the operations you intend to perform
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install -g @gesslar/mediawiki-mcp
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Or run it directly via `npx`:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx @gesslar/mediawiki-mcp
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Configuration
|
|
27
|
+
|
|
28
|
+
The server is configured through environment variables:
|
|
29
|
+
|
|
30
|
+
| Variable | Description |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| `MEDIAWIKI_URL` | Base URL of the MediaWiki instance (e.g. `https://wiki.example.com`) |
|
|
33
|
+
| `MEDIAWIKI_BOT_USERNAME` | Bot account username |
|
|
34
|
+
| `MEDIAWIKI_BOT_PASSWORD` | Bot account password |
|
|
35
|
+
|
|
36
|
+
All three variables are required. The server will exit on startup if any are
|
|
37
|
+
missing.
|
|
38
|
+
|
|
39
|
+
> **Note:** The server currently authenticates all reads and writes
|
|
40
|
+
> (`private: true` on the underlying `Wikid` client). This is intentional for
|
|
41
|
+
> wikis that require authentication for read access. If you're pointing this at
|
|
42
|
+
> a fully public wiki, reads will still work — they'll just be authenticated
|
|
43
|
+
> when they wouldn't strictly need to be. This package is released under
|
|
44
|
+
> [0BSD](LICENSE.txt), so if the hardcoded behaviour doesn't suit your setup,
|
|
45
|
+
> you're welcome to fork it and tweak the `Wikid` constructor options to taste.
|
|
46
|
+
|
|
47
|
+
## Usage with an MCP client
|
|
48
|
+
|
|
49
|
+
Add the server to your MCP client configuration. For example, in a Claude
|
|
50
|
+
Desktop `claude_desktop_config.json`:
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"mediawiki": {
|
|
56
|
+
"command": "npx",
|
|
57
|
+
"args": ["-y", "@gesslar/mediawiki-mcp"],
|
|
58
|
+
"env": {
|
|
59
|
+
"MEDIAWIKI_URL": "https://wiki.example.com",
|
|
60
|
+
"MEDIAWIKI_BOT_USERNAME": "YourBot@YourBotPassword",
|
|
61
|
+
"MEDIAWIKI_BOT_PASSWORD": "your-bot-password"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The server communicates over stdio.
|
|
69
|
+
|
|
70
|
+
## Tools
|
|
71
|
+
|
|
72
|
+
The server exposes the following tools:
|
|
73
|
+
|
|
74
|
+
### `mediawiki_create_article`
|
|
75
|
+
|
|
76
|
+
Create a new article. Fails if the article already exists.
|
|
77
|
+
|
|
78
|
+
| Parameter | Type | Required | Description |
|
|
79
|
+
| --- | --- | --- | --- |
|
|
80
|
+
| `title` | string | yes | Title of the article to create |
|
|
81
|
+
| `content` | string | yes | Wikitext content for the article |
|
|
82
|
+
| `summary` | string | no | Edit summary |
|
|
83
|
+
|
|
84
|
+
### `mediawiki_edit_article`
|
|
85
|
+
|
|
86
|
+
Edit an existing article. Supports full replacement, append, or prepend.
|
|
87
|
+
|
|
88
|
+
| Parameter | Type | Required | Description |
|
|
89
|
+
| --- | --- | --- | --- |
|
|
90
|
+
| `title` | string | yes | Title of the article to edit |
|
|
91
|
+
| `content` | string | yes | New wikitext content |
|
|
92
|
+
| `summary` | string | no | Edit summary |
|
|
93
|
+
| `append` | boolean | no | Append `content` instead of replacing |
|
|
94
|
+
| `prepend` | boolean | no | Prepend `content` instead of replacing |
|
|
95
|
+
|
|
96
|
+
### `mediawiki_delete_article`
|
|
97
|
+
|
|
98
|
+
Delete an article. Requires delete permissions on the bot account.
|
|
99
|
+
|
|
100
|
+
| Parameter | Type | Required | Description |
|
|
101
|
+
| --- | --- | --- | --- |
|
|
102
|
+
| `title` | string | yes | Title of the article to delete |
|
|
103
|
+
| `reason` | string | no | Reason for deletion |
|
|
104
|
+
|
|
105
|
+
### `mediawiki_get_article`
|
|
106
|
+
|
|
107
|
+
Return the current wikitext content of an article.
|
|
108
|
+
|
|
109
|
+
| Parameter | Type | Required | Description |
|
|
110
|
+
| --- | --- | --- | --- |
|
|
111
|
+
| `title` | string | yes | Title of the article to retrieve |
|
|
112
|
+
|
|
113
|
+
### `mediawiki_search`
|
|
114
|
+
|
|
115
|
+
Search articles on the wiki.
|
|
116
|
+
|
|
117
|
+
| Parameter | Type | Required | Description |
|
|
118
|
+
| --- | --- | --- | --- |
|
|
119
|
+
| `query` | string | yes | Search query |
|
|
120
|
+
| `limit` | number | no | Maximum number of results (default: 10) |
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Start the server locally
|
|
126
|
+
npm start
|
|
127
|
+
|
|
128
|
+
# Lint
|
|
129
|
+
npm run lint
|
|
130
|
+
npm run lint:fix
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
`@gesslar/mediawiki-mcp` is released under the [0BSD](LICENSE.txt).
|
|
136
|
+
|
|
137
|
+
This package includes or depends on third-party components under their own
|
|
138
|
+
licenses:
|
|
139
|
+
|
|
140
|
+
| Dependency | License |
|
|
141
|
+
| --- | --- |
|
|
142
|
+
| [@gesslar/wikid](https://github.com/gesslar/wikid) | 0BSD |
|
|
143
|
+
| [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) | MIT |
|
|
144
|
+
| [zod](https://github.com/colinhacks/zod) | MIT |
|
package/eslint.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gesslar/mediawiki-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Integrating with MediaWiki",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mediawiki-mcp": "./src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=24.11.0"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node src/index.js",
|
|
15
|
+
"lint": "eslint src/",
|
|
16
|
+
"lint:fix": "eslint src/ --fix",
|
|
17
|
+
"submit": "npm publish --access public --//registry.npmjs.org/:_authToken=\"${NPM_ACCESS_TOKEN}\"",
|
|
18
|
+
"update": "npx npm-check-updates -u && npm install",
|
|
19
|
+
"pr": "gt submit --ai -p",
|
|
20
|
+
"patch": "npm version patch",
|
|
21
|
+
"minor": "npm version minor",
|
|
22
|
+
"major": "npm version major"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/gesslar/mediawiki-mcp.git"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"wiki",
|
|
30
|
+
"mediawiki",
|
|
31
|
+
"mcp",
|
|
32
|
+
"model-context-protocol",
|
|
33
|
+
"janet",
|
|
34
|
+
"jackson",
|
|
35
|
+
"was",
|
|
36
|
+
"played"
|
|
37
|
+
],
|
|
38
|
+
"author": "gesslar",
|
|
39
|
+
"license": "0BSD",
|
|
40
|
+
"homepage": "https://github.com/gesslar/mediawiki-mcp#readme",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@gesslar/wikid": "^2.3.1",
|
|
43
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
44
|
+
"zod": "^4.3.6"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@gesslar/uglier": "^2.4.1",
|
|
48
|
+
"eslint": "^10.2.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import Wikid from "@gesslar/wikid"
|
|
4
|
+
import {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js"
|
|
5
|
+
import {StdioServerTransport} from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
6
|
+
import * as z from "zod/v4"
|
|
7
|
+
import pkg from "../package.json" with {type: "json"}
|
|
8
|
+
|
|
9
|
+
class MediaWikiMCPServer {
|
|
10
|
+
#client
|
|
11
|
+
|
|
12
|
+
constructor() {
|
|
13
|
+
this.server = new McpServer(
|
|
14
|
+
{
|
|
15
|
+
name: "mediawiki-mcp-server",
|
|
16
|
+
version: pkg.version,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
capabilities: {
|
|
20
|
+
tools: {},
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
// Required environment variables
|
|
26
|
+
this.wikiUrl = process.env.MEDIAWIKI_URL
|
|
27
|
+
this.botUsername = process.env.MEDIAWIKI_BOT_USERNAME
|
|
28
|
+
this.botPassword = process.env.MEDIAWIKI_BOT_PASSWORD
|
|
29
|
+
|
|
30
|
+
if(!this.wikiUrl || !this.botUsername || !this.botPassword) {
|
|
31
|
+
console.error("Error: Required environment variables not set:")
|
|
32
|
+
console.error(" MEDIAWIKI_URL - The base URL of your MediaWiki instance")
|
|
33
|
+
console.error(" MEDIAWIKI_BOT_USERNAME - Bot account username")
|
|
34
|
+
console.error(" MEDIAWIKI_BOT_PASSWORD - Bot account password")
|
|
35
|
+
process.exit(1)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Normalize URL (remove trailing slash)
|
|
39
|
+
this.wikiUrl = this.wikiUrl.replace(/\/$/, "")
|
|
40
|
+
|
|
41
|
+
this.setupTools()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Create and authenticate a MediaWiki client
|
|
46
|
+
*
|
|
47
|
+
* @returns {Promise<Wikid>} Authenticated client
|
|
48
|
+
*/
|
|
49
|
+
async #assureClient() {
|
|
50
|
+
if(!this.#client) {
|
|
51
|
+
const client = new Wikid({
|
|
52
|
+
baseUrl: this.wikiUrl,
|
|
53
|
+
botUsername: this.botUsername,
|
|
54
|
+
botPassword: this.botPassword,
|
|
55
|
+
private: true // Your wiki requires auth for reads
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const loginResult = await client.login()
|
|
59
|
+
|
|
60
|
+
if(!loginResult.ok)
|
|
61
|
+
throw new Error(`Login failed: ${loginResult.error?.message ?? "unknown error"}`)
|
|
62
|
+
|
|
63
|
+
this.#client = client
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.#client
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
setupTools() {
|
|
70
|
+
this.server.registerTool("mediawiki_create_article", {
|
|
71
|
+
description:
|
|
72
|
+
"Create a new article on the MediaWiki instance. Will fail if the article already exists.",
|
|
73
|
+
inputSchema: z.object({
|
|
74
|
+
title: z.string().describe("Title of the article to create"),
|
|
75
|
+
content: z.string().describe("Wikitext content for the article"),
|
|
76
|
+
summary: z.string().optional().describe("Edit summary (reason for creating the article)"),
|
|
77
|
+
}),
|
|
78
|
+
}, async({title, content, summary}) => {
|
|
79
|
+
try {
|
|
80
|
+
await this.#assureClient()
|
|
81
|
+
|
|
82
|
+
const result = await this.#client.post("api.php", {
|
|
83
|
+
action: "edit",
|
|
84
|
+
title,
|
|
85
|
+
text: content,
|
|
86
|
+
summary: summary || "Created via MediaWiki MCP",
|
|
87
|
+
createonly: "true",
|
|
88
|
+
format: "json"
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
if(result.edit?.result !== "Success")
|
|
92
|
+
throw new Error(`Edit failed: ${JSON.stringify(result)}`)
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: `✓ Successfully created article "${title}"\nRevision ID: ${result.edit.newrevid}\nTimestamp: ${result.edit.newtimestamp}`,
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
}
|
|
102
|
+
} catch(error) {
|
|
103
|
+
return {
|
|
104
|
+
content: [
|
|
105
|
+
{
|
|
106
|
+
type: "text",
|
|
107
|
+
text: `Error: ${error.message}`,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
isError: true,
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
this.server.registerTool("mediawiki_edit_article", {
|
|
116
|
+
description:
|
|
117
|
+
"Edit an existing article on the MediaWiki instance. Creates the article if it does not already exist.",
|
|
118
|
+
inputSchema: z.object({
|
|
119
|
+
title: z.string().describe("Title of the article to edit"),
|
|
120
|
+
content: z.string().describe("New wikitext content for the article"),
|
|
121
|
+
summary: z.string().optional().describe("Edit summary (reason for the edit)"),
|
|
122
|
+
append: z.boolean().optional().describe("If true, append content instead of replacing. Default: false"),
|
|
123
|
+
prepend: z.boolean().optional().describe("If true, prepend content instead of replacing. Default: false"),
|
|
124
|
+
}),
|
|
125
|
+
}, async({title, content, summary, append, prepend}) => {
|
|
126
|
+
try {
|
|
127
|
+
await this.#assureClient()
|
|
128
|
+
|
|
129
|
+
const editParams = {
|
|
130
|
+
action: "edit",
|
|
131
|
+
title,
|
|
132
|
+
summary: summary || "Edited via MediaWiki MCP",
|
|
133
|
+
format: "json"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if(append)
|
|
137
|
+
editParams.appendtext = content
|
|
138
|
+
else if(prepend)
|
|
139
|
+
editParams.prependtext = content
|
|
140
|
+
else
|
|
141
|
+
editParams.text = content
|
|
142
|
+
|
|
143
|
+
const result = await this.#client.post("api.php", editParams)
|
|
144
|
+
|
|
145
|
+
if(result.edit?.result !== "Success")
|
|
146
|
+
throw new Error(`Edit failed: ${JSON.stringify(result)}`)
|
|
147
|
+
|
|
148
|
+
const action = result.edit.new ? "created" : "edited"
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: "text",
|
|
154
|
+
text: `✓ Successfully ${action} article "${title}"\nRevision ID: ${result.edit.newrevid}\nTimestamp: ${result.edit.newtimestamp}`,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
}
|
|
158
|
+
} catch(error) {
|
|
159
|
+
return {
|
|
160
|
+
content: [
|
|
161
|
+
{
|
|
162
|
+
type: "text",
|
|
163
|
+
text: `Error: ${error.message}`,
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
isError: true,
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
this.server.registerTool("mediawiki_delete_article", {
|
|
172
|
+
description:
|
|
173
|
+
"Delete an article from the MediaWiki instance. Requires appropriate permissions.",
|
|
174
|
+
inputSchema: z.object({
|
|
175
|
+
title: z.string().describe("Title of the article to delete"),
|
|
176
|
+
reason: z.string().optional().describe("Reason for deletion"),
|
|
177
|
+
}),
|
|
178
|
+
}, async({title, reason}) => {
|
|
179
|
+
try {
|
|
180
|
+
await this.#assureClient()
|
|
181
|
+
|
|
182
|
+
const deleteReason = reason || "Deleted via MediaWiki MCP"
|
|
183
|
+
const result = await this.#client.post("api.php", {
|
|
184
|
+
action: "delete",
|
|
185
|
+
title,
|
|
186
|
+
reason: deleteReason,
|
|
187
|
+
format: "json"
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
if(!result.delete)
|
|
191
|
+
throw new Error(`Delete failed: ${JSON.stringify(result)}`)
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
content: [
|
|
195
|
+
{
|
|
196
|
+
type: "text",
|
|
197
|
+
text: `✓ Successfully deleted article "${title}"\nReason: ${deleteReason}`,
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
}
|
|
201
|
+
} catch(error) {
|
|
202
|
+
return {
|
|
203
|
+
content: [
|
|
204
|
+
{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: `Error: ${error.message}`,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
isError: true,
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
this.server.registerTool("mediawiki_get_article", {
|
|
215
|
+
description:
|
|
216
|
+
"Get the current content of an article from the MediaWiki instance.",
|
|
217
|
+
inputSchema: z.object({
|
|
218
|
+
title: z.string().describe("Title of the article to retrieve"),
|
|
219
|
+
}),
|
|
220
|
+
}, async({title}) => {
|
|
221
|
+
try {
|
|
222
|
+
await this.#assureClient()
|
|
223
|
+
|
|
224
|
+
const data = await this.#client.get("api.php", {
|
|
225
|
+
action: "query",
|
|
226
|
+
titles: title,
|
|
227
|
+
prop: "revisions",
|
|
228
|
+
rvprop: "content",
|
|
229
|
+
rvslots: "main",
|
|
230
|
+
format: "json"
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
const pages = data.query.pages
|
|
234
|
+
const pageId = Object.keys(pages)[0]
|
|
235
|
+
const page = pages[pageId]
|
|
236
|
+
|
|
237
|
+
if(page.missing) {
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: `Article "${title}" does not exist.`,
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const content = page.revisions?.[0]?.slots?.main?.["*"] ?? ""
|
|
249
|
+
|
|
250
|
+
if(!content) {
|
|
251
|
+
return {
|
|
252
|
+
content: [
|
|
253
|
+
{
|
|
254
|
+
type: "text",
|
|
255
|
+
text: `Article "${title}" exists but has no readable content.`,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: `Content of "${title}":\n\n${content}`,
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
}
|
|
269
|
+
} catch(error) {
|
|
270
|
+
return {
|
|
271
|
+
content: [
|
|
272
|
+
{
|
|
273
|
+
type: "text",
|
|
274
|
+
text: `Error: ${error.message}`,
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
isError: true,
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
this.server.registerTool("mediawiki_search", {
|
|
283
|
+
description: "Search for articles on the MediaWiki instance.",
|
|
284
|
+
inputSchema: z.object({
|
|
285
|
+
query: z.string().describe("Search query"),
|
|
286
|
+
limit: z.number().optional().describe("Maximum number of results to return (default: 10)"),
|
|
287
|
+
}),
|
|
288
|
+
}, async({query, limit}) => {
|
|
289
|
+
try {
|
|
290
|
+
await this.#assureClient()
|
|
291
|
+
|
|
292
|
+
const data = await this.#client.get("api.php", {
|
|
293
|
+
action: "query",
|
|
294
|
+
list: "search",
|
|
295
|
+
srsearch: query,
|
|
296
|
+
srlimit: limit || 10,
|
|
297
|
+
format: "json"
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
const results = data.query.search
|
|
301
|
+
|
|
302
|
+
if(results.length === 0) {
|
|
303
|
+
return {
|
|
304
|
+
content: [
|
|
305
|
+
{
|
|
306
|
+
type: "text",
|
|
307
|
+
text: `No results found for "${query}".`,
|
|
308
|
+
},
|
|
309
|
+
],
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const stripTags = s => {
|
|
314
|
+
let prev
|
|
315
|
+
let curr = s
|
|
316
|
+
|
|
317
|
+
do {
|
|
318
|
+
prev = curr
|
|
319
|
+
curr = curr.replace(/<[^<>]*>/g, "")
|
|
320
|
+
} while(curr !== prev)
|
|
321
|
+
|
|
322
|
+
return curr
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const formatted = results.map((r, i) =>
|
|
326
|
+
`${i + 1}. ${r.title}\n Snippet: ${stripTags(r.snippet)}`
|
|
327
|
+
).join("\n\n")
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
content: [
|
|
331
|
+
{
|
|
332
|
+
type: "text",
|
|
333
|
+
text: `Found ${results.length} result(s) for "${query}":\n\n${formatted}`,
|
|
334
|
+
},
|
|
335
|
+
],
|
|
336
|
+
}
|
|
337
|
+
} catch(error) {
|
|
338
|
+
return {
|
|
339
|
+
content: [
|
|
340
|
+
{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: `Error: ${error.message}`,
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
isError: true,
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async run() {
|
|
352
|
+
const transport = new StdioServerTransport()
|
|
353
|
+
await this.server.connect(transport)
|
|
354
|
+
|
|
355
|
+
console.error("MediaWiki MCP Server running on stdio")
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
const server = new MediaWikiMCPServer()
|
|
360
|
+
server.run().catch(console.error)
|