@mistereman22/osrs-wiki-mcp-server 0.0.0-stage → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mistereman22
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,123 @@
1
- # Temporary Holding Version
1
+ # @mistereman22/osrs-wiki-mcp-server
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ MCP server for the Old School RuneScape Wiki (`https://oldschool.runescape.wiki`). Serves the full tool surface of [`@professional-wiki/mediawiki-mcp-server`](https://github.com/ProfessionalWiki/MediaWiki-MCP-Server) against a pinned, read-only wiki definition.
4
+
5
+ | Capability | Details |
6
+ | --- | --- |
7
+ | MediaWiki tools | Full upstream tool surface (page reads, search, watchlists, siteinfo, ...) with wiki fixed to `oldschool.runescape.wiki`. **Read-only** - write and wiki-management tools are gated off. |
8
+ | `context://bucket-index` | On-demand hint list of known Bucket buckets for `bucket-query`; read fresh per request with runtime discovery as fallback. |
9
+
10
+ ## Quick start (users)
11
+
12
+ **Requirement:** Node.js >= 22.19.0
13
+
14
+ ### OpenCode
15
+ ```json
16
+ {
17
+ "$schema": "https://opencode.ai/config.json",
18
+ "mcp": {
19
+ "servers": {
20
+ "osrs-wiki": {
21
+ "type": "local",
22
+ "command": ["npx", "@mistereman22/osrs-wiki-mcp-server@1.0.0"],
23
+ "environment": { "MCP_CONTACT": "you@example.com" }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ ```
29
+
30
+ ### Cursor
31
+ ```json
32
+ {
33
+ "mcpServers": {
34
+ "osrs-wiki": {
35
+ "command": "npx",
36
+ "args": ["@mistereman22/osrs-wiki-mcp-server@1.0.0"],
37
+ "env": { "MCP_CONTACT": "you@example.com" }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ ### Claude Code
44
+ ```sh
45
+ /plugin marketplace add mistereman22/osrs-wiki-mcp-server
46
+ /plugin install osrs-wiki-mcp-server@osrs-wiki-mcp-server
47
+ ```
48
+
49
+ ### Run directly
50
+ ```sh
51
+ MCP_CONTACT="you@example.com" npx @mistereman22/osrs-wiki-mcp-server@1.0.0
52
+ ```
53
+
54
+ ## Configuration
55
+
56
+ | Variable | Required | Meaning |
57
+ | --- | --- | --- |
58
+ | `MCP_CONTACT` | **Yes** | Your contact (email or name + URL), appended to the server's base User-Agent on every OSRS wiki request. Example: `you@example.com`. Server fails fast at startup if missing. |
59
+
60
+ ## Composed User-Agent
61
+ ```
62
+ osrs-wiki-mcp-server/1.0.0 (https://github.com/mistereman22/osrs-wiki-mcp-server) you@example.com
63
+ ```
64
+
65
+ ## Rate limits
66
+
67
+ Two independent sliding-window budgets (tuned in `src/server.ts`):
68
+
69
+ | Surface | Budget | Counted per |
70
+ | --- | --- | --- |
71
+ | MediaWiki API | 30 requests / 60 s | each upstream tool call |
72
+ | External APIs | 30 requests / 60 s | each HTTP request by external API tool |
73
+
74
+ Over budget: waits up to 10s for a slot, then returns rate-limit error with retry hint.
75
+
76
+ ## Development
77
+
78
+ ### Requirements
79
+ - Node.js >= 22.19.0
80
+ - `npm install` in this directory
81
+
82
+ ### Commands
83
+ ```sh
84
+ npm run build # TypeScript -> dist/
85
+ npm start # Run built server (requires MCP_CONTACT)
86
+ npm run dev # Build + run
87
+ ```
88
+
89
+ ### Repository layout
90
+ ```
91
+ .
92
+ +-- src/
93
+ | +-- index.ts # Entry point: env gate, stdio transport, lifecycle
94
+ | +-- env.ts # MCP_CONTACT fail-fast gate + User-Agent composition
95
+ | +-- server.ts # McpServer assembly + tool gating + wiki config + rate limiting
96
+ | +-- resources.ts # Generic resource loader (bucket-index)
97
+ | +-- external.ts # HTTP utilities (callHttp) for future external APIs
98
+ | +-- declarations.d.ts # Type declarations for upstream JS-only package
99
+ +-- resources/
100
+ | +-- bucket-index.md # Bucket hint list for context://bucket-index
101
+ +-- .claude-plugin/
102
+ | +-- marketplace.json # Claude Code marketplace manifest
103
+ +-- dist/ # Compiled output (generated by build)
104
+ +-- package.json
105
+ +-- tsconfig.json
106
+ +-- README.md
107
+ +-- LICENSE
108
+ ```
109
+
110
+ ### Rate limit constants
111
+ Tuned in `src/server.ts`:
112
+ - `WINDOW_MS = 60_000` (60s window)
113
+ - `MAX_WAIT_MS = 10_000` (max 10s wait)
114
+ - `WIKI_MAX_REQUESTS = 30`
115
+ - `EXTERNAL_MAX_REQUESTS = 30`
116
+
117
+ ## Contact
118
+ - Maintainer: [github.com/mistereman22](https://github.com/mistereman22)
119
+ - Issues: [github.com/mistereman22/osrs-wiki-mcp-server/issues](https://github.com/mistereman22/osrs-wiki-mcp-server/issues)
120
+
121
+ ## License
122
+
123
+ [MIT](LICENSE)
package/dist/env.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ export declare const CONTACT: string | undefined;
2
+ export declare const USER_AGENT: string;
3
+ //# sourceMappingURL=env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,OAAO,oBAAkC,CAAC;AAcvD,eAAO,MAAM,UAAU,QAAkC,CAAC"}
package/dist/env.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * MCP_CONTACT gate + User-Agent composition.
3
+ * Must be imported first - upstream reads MCP_USER_AGENT at module evaluation time.
4
+ */
5
+ import process from 'node:process';
6
+ import { createRequire } from 'node:module';
7
+ import { fileURLToPath } from 'node:url';
8
+ const pkg = createRequire(import.meta.url)(fileURLToPath(new URL('../package.json', import.meta.url)));
9
+ const SERVER_NAME = 'osrs-wiki-mcp-server';
10
+ const SERVER_VERSION = pkg.version ?? '1.0.0';
11
+ const SERVER_REPO = typeof pkg.repository?.url === 'string'
12
+ ? pkg.repository.url.replace(/^git\+/, '').replace(/\.git$/, '')
13
+ : 'https://github.com/mistereman22/osrs-wiki-mcp-server';
14
+ const BASE_USER_AGENT = `${SERVER_NAME}/${SERVER_VERSION} (${SERVER_REPO})`;
15
+ export const CONTACT = process.env.MCP_CONTACT?.trim();
16
+ if (!CONTACT) {
17
+ console.error('[osrs-wiki-mcp-server] MCP_CONTACT is required but not set.\n' +
18
+ 'Your contact is appended to the server User-Agent so anyone\n' +
19
+ 'seeing our requests can reach you, e.g.\n' +
20
+ ' MCP_CONTACT="you@example.com"\n' +
21
+ `Base User-Agent: ${BASE_USER_AGENT}\n` +
22
+ `Repository: ${SERVER_REPO}`);
23
+ process.exit(1);
24
+ }
25
+ export const USER_AGENT = `${BASE_USER_AGENT} ${CONTACT}`;
26
+ // Upstream reads MCP_USER_AGENT at import time - set it before other imports load.
27
+ process.env.MCP_USER_AGENT = USER_AGENT;
28
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAIpG,CAAC;AAEF,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAC3C,MAAM,cAAc,GAAG,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC;AAC9C,MAAM,WAAW,GAChB,OAAO,GAAG,CAAC,UAAU,EAAE,GAAG,KAAK,QAAQ;IACtC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;IAChE,CAAC,CAAC,sDAAsD,CAAC;AAC3D,MAAM,eAAe,GAAG,GAAG,WAAW,IAAI,cAAc,KAAK,WAAW,GAAG,CAAC;AAE5E,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;AAEvD,IAAI,CAAC,OAAO,EAAE,CAAC;IACd,OAAO,CAAC,KAAK,CACZ,+DAA+D;QAC9D,+DAA+D;QAC/D,2CAA2C;QAC3C,mCAAmC;QACnC,oBAAoB,eAAe,IAAI;QACvC,eAAe,WAAW,EAAE,CAC7B,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,eAAe,IAAI,OAAO,EAAE,CAAC;AAE1D,mFAAmF;AACnF,OAAO,CAAC,GAAG,CAAC,cAAc,GAAG,UAAU,CAAC"}
@@ -0,0 +1,49 @@
1
+ export declare class HttpToolError extends Error {
2
+ }
3
+ interface CallHttpResult {
4
+ status: number;
5
+ statusText: string;
6
+ finalUrl: string;
7
+ elapsedMs: number;
8
+ headerLines: string[];
9
+ body: string;
10
+ truncated: boolean;
11
+ shownBody: string;
12
+ }
13
+ interface CallHttpRequest {
14
+ url: unknown;
15
+ method?: string;
16
+ headers?: Record<string, string>;
17
+ params?: Record<string, unknown>;
18
+ data?: unknown;
19
+ timeout_ms?: number;
20
+ }
21
+ export declare function errorMessage(error: unknown): string;
22
+ export declare function textResult(text: string, isError?: boolean): {
23
+ content: {
24
+ type: 'text';
25
+ text: string;
26
+ }[];
27
+ isError: boolean;
28
+ };
29
+ export declare function callHttp(request: CallHttpRequest): Promise<CallHttpResult>;
30
+ export declare function formatHttpResponse(result: CallHttpResult): string;
31
+ interface ApiTool {
32
+ name: string;
33
+ title?: string;
34
+ description: string;
35
+ inputSchema: any;
36
+ annotations?: Record<string, unknown>;
37
+ call: (args: unknown) => Promise<{
38
+ content: {
39
+ type: 'text';
40
+ text: string;
41
+ }[];
42
+ isError: boolean;
43
+ }>;
44
+ }
45
+ export declare const API_TOOLS: ApiTool[];
46
+ import type { McpServer } from '@modelcontextprotocol/server';
47
+ export declare function registerExternalApiTools(server: McpServer): void;
48
+ export {};
49
+ //# sourceMappingURL=external.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external.d.ts","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":"AAeA,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAE3C,UAAU,cAAc;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,eAAe;IACxB,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGnD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,UAAQ,GAAG;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAEzH;AAqCD,wBAAsB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CA4EhF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAWjE;AAED,UAAU,OAAO;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAClG;AAED,eAAO,MAAM,SAAS,EAAE,OAAO,EAE9B,CAAC;AAEF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAahE"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * HTTP utilities for external API tools (rate-limited, fixed User-Agent, https-only).
3
+ * Add tools to API_TOOLS array below; each uses callHttp internally.
4
+ */
5
+ import axios from 'axios';
6
+ import { externalLimiter, RateLimitError } from './server.js';
7
+ import { USER_AGENT } from './env.js';
8
+ const HTTP_TIMEOUT_MS = 30_000;
9
+ const HTTP_MIN_TIMEOUT_MS = 1_000;
10
+ const HTTP_MAX_TIMEOUT_MS = 300_000;
11
+ const HTTP_MAX_BODY_BYTES = 5 * 1024 * 1024;
12
+ const HTTP_MAX_BODY_CHARS = 100_000;
13
+ export class HttpToolError extends Error {
14
+ }
15
+ export function errorMessage(error) {
16
+ if (error instanceof Error)
17
+ return error.message;
18
+ return String(error);
19
+ }
20
+ export function textResult(text, isError = false) {
21
+ return { content: [{ type: 'text', text }], isError };
22
+ }
23
+ function normalizeTimeout(raw) {
24
+ if (raw === undefined || raw === null)
25
+ return HTTP_TIMEOUT_MS;
26
+ const parsed = Number(raw);
27
+ if (!Number.isFinite(parsed))
28
+ return HTTP_TIMEOUT_MS;
29
+ return Math.min(Math.max(parsed, HTTP_MIN_TIMEOUT_MS), HTTP_MAX_TIMEOUT_MS);
30
+ }
31
+ function normalizeBody(raw) {
32
+ if (raw === undefined || raw === null)
33
+ return { jsonBody: false };
34
+ if (typeof raw === 'object')
35
+ return { data: JSON.stringify(raw), jsonBody: true };
36
+ if (typeof raw === 'string')
37
+ return { data: raw, jsonBody: false };
38
+ return { data: String(raw), jsonBody: true };
39
+ }
40
+ function parseHttpUrl(rawUrl) {
41
+ const trimmed = typeof rawUrl === 'string' ? rawUrl.trim() : '';
42
+ if (trimmed === '')
43
+ throw new HttpToolError('Missing required argument: "url".');
44
+ let parsed;
45
+ try {
46
+ parsed = new URL(trimmed);
47
+ }
48
+ catch {
49
+ throw new HttpToolError(`Invalid URL: ${trimmed}`);
50
+ }
51
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
52
+ throw new HttpToolError(`Unsupported protocol "${parsed.protocol}" in ${trimmed}. Only http: and https: allowed.`);
53
+ }
54
+ return trimmed;
55
+ }
56
+ function hasHeader(headers, name) {
57
+ return Object.keys(headers).some((key) => key.toLowerCase() === name);
58
+ }
59
+ export async function callHttp(request) {
60
+ const url = parseHttpUrl(request.url);
61
+ try {
62
+ await externalLimiter.acquire();
63
+ }
64
+ catch (error) {
65
+ if (error instanceof RateLimitError) {
66
+ throw new HttpToolError(error.message);
67
+ }
68
+ throw error;
69
+ }
70
+ const method = request.method ?? 'GET';
71
+ const headers = { ...(request.headers ?? {}) };
72
+ if (!hasHeader(headers, 'user-agent')) {
73
+ headers['user-agent'] = USER_AGENT;
74
+ }
75
+ const params = { ...(request.params ?? {}) };
76
+ const { data, jsonBody } = normalizeBody(request.data);
77
+ if (jsonBody && !hasHeader(headers, 'content-type')) {
78
+ headers['content-type'] = 'application/json';
79
+ }
80
+ const timeout = normalizeTimeout(request.timeout_ms);
81
+ const startedAt = Date.now();
82
+ let response;
83
+ try {
84
+ response = await axios.request({
85
+ url,
86
+ method,
87
+ headers,
88
+ params,
89
+ data,
90
+ timeout,
91
+ maxRedirects: 5,
92
+ maxContentLength: HTTP_MAX_BODY_BYTES,
93
+ responseType: 'text',
94
+ validateStatus: () => true,
95
+ transformResponse: [(body) => body],
96
+ });
97
+ }
98
+ catch (error) {
99
+ const axiosError = error;
100
+ const code = typeof axiosError.code === 'string' ? `${axiosError.code}: ` : '';
101
+ throw new HttpToolError(`Request to ${url} failed - ${code}${errorMessage(error)}`);
102
+ }
103
+ const elapsedMs = Date.now() - startedAt;
104
+ const body = typeof response.data === 'string'
105
+ ? response.data
106
+ : response.data === undefined || response.data === null
107
+ ? ''
108
+ : String(response.data);
109
+ const truncated = body.length > HTTP_MAX_BODY_CHARS;
110
+ const shownBody = truncated ? body.slice(0, HTTP_MAX_BODY_CHARS) : body;
111
+ const headerLines = [];
112
+ for (const [key, value] of Object.entries(response.headers ?? {})) {
113
+ headerLines.push(` ${key}: ${Array.isArray(value) ? value.join(', ') : String(value)}`);
114
+ }
115
+ const finalUrl = response.request?.res?.responseUrl ??
116
+ response.request?.responseURL ??
117
+ url;
118
+ return {
119
+ status: response.status,
120
+ statusText: response.statusText ?? '',
121
+ finalUrl,
122
+ elapsedMs,
123
+ headerLines,
124
+ body,
125
+ truncated,
126
+ shownBody,
127
+ };
128
+ }
129
+ export function formatHttpResponse(result) {
130
+ const sections = [
131
+ `Status: ${result.status} ${result.statusText}`.trimEnd(),
132
+ `Final URL: ${result.finalUrl}`,
133
+ `Elapsed: ${result.elapsedMs}ms`,
134
+ `Response headers:\n${result.headerLines.length > 0 ? result.headerLines.join('\n') : ' (none)'}`,
135
+ result.truncated
136
+ ? `Body (${result.body.length} characters, truncated to ${HTTP_MAX_BODY_CHARS}):\n${result.shownBody}`
137
+ : `Body:\n${result.shownBody === '' ? '(empty)' : result.shownBody}`,
138
+ ];
139
+ return sections.join('\n\n');
140
+ }
141
+ export const API_TOOLS = [
142
+ // ▸▸▸ EXTERNAL API TOOL DEFINITIONS GO HERE ◂◂◂
143
+ ];
144
+ export function registerExternalApiTools(server) {
145
+ for (const tool of API_TOOLS) {
146
+ server.registerTool(tool.name, {
147
+ title: tool.title ?? tool.name,
148
+ description: tool.description,
149
+ inputSchema: tool.inputSchema,
150
+ ...(tool.annotations ? { annotations: tool.annotations } : {}),
151
+ }, tool.call);
152
+ }
153
+ }
154
+ //# sourceMappingURL=external.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external.js","sourceRoot":"","sources":["../src/external.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP server for the Old School RuneScape Wiki.
4
+ * Serves the full MediaWiki tool surface against a pinned, read-only wiki.
5
+ * Transport: stdio (newline-delimited JSON-RPC).
6
+ */
7
+ import './env.js';
8
+ declare function main(): Promise<void>;
9
+ export { main };
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAIH,OAAO,UAAU,CAAC;AAUlB,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBnC;AAWD,OAAO,EAAE,IAAI,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP server for the Old School RuneScape Wiki.
4
+ * Serves the full MediaWiki tool surface against a pinned, read-only wiki.
5
+ * Transport: stdio (newline-delimited JSON-RPC).
6
+ */
7
+ // Must be first: composes User-Agent from MCP_CONTACT before upstream modules
8
+ // evaluate it at import time.
9
+ import './env.js';
10
+ import { serveStdio } from '@modelcontextprotocol/server/stdio';
11
+ import { logger } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/logger.js';
12
+ import { registerShutdownHandlers } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/shutdown.js';
13
+ import { SERVER_NAME, SERVER_VERSION } from './server.js';
14
+ import { errorMessage } from './external.js';
15
+ import { buildServer } from './server.js';
16
+ async function main() {
17
+ process.on('uncaughtException', (error) => {
18
+ const stack = error instanceof Error ? error.stack : undefined;
19
+ logger.error(`Uncaught exception: ${stack ?? errorMessage(error)}`);
20
+ process.exit(1);
21
+ });
22
+ process.on('unhandledRejection', (reason) => {
23
+ logger.error(`Unhandled rejection: ${errorMessage(reason)}`);
24
+ });
25
+ const server = await buildServer();
26
+ logger.info(`${SERVER_NAME} ${SERVER_VERSION} serving over stdio`);
27
+ const handle = serveStdio(() => server, {
28
+ onerror: (error) => logger.error(`stdio serving error: ${error.message}`),
29
+ });
30
+ registerShutdownHandlers({
31
+ transport: 'stdio',
32
+ graceMs: 0,
33
+ stdioTransport: handle,
34
+ });
35
+ }
36
+ main().catch((error) => {
37
+ const stack = error instanceof Error ? error.stack : undefined;
38
+ console.error(`[${SERVER_NAME}] fatal error:`, stack ?? errorMessage(error));
39
+ process.exit(1);
40
+ });
41
+ export { main };
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;GAIG;AAEH,8EAA8E;AAC9E,8BAA8B;AAC9B,OAAO,UAAU,CAAC;AAElB,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,gEAAgE,CAAC;AACxF,OAAO,EAAE,wBAAwB,EAAE,MAAM,kEAAkE,CAAC;AAE5G,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,KAAK,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAAc,EAAE,EAAE;QAClD,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,MAAM,CAAC,KAAK,CAAC,uBAAuB,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAe,EAAE,EAAE;QACpD,MAAM,CAAC,KAAK,CAAC,wBAAwB,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,MAAM,WAAW,EAAE,CAAC;IACnC,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,IAAI,cAAc,qBAAqB,CAAC,CAAC;IAEnE,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE;QACvC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,KAAK,CAAC,OAAO,EAAE,CAAC;KAChF,CAAC,CAAC;IACH,wBAAwB,CAAC;QACxB,SAAS,EAAE,OAAO;QAClB,OAAO,EAAE,CAAC;QACV,cAAc,EAAE,MAAM;KACtB,CAAC,CAAC;AACJ,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC/B,MAAM,KAAK,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC/D,OAAO,CAAC,KAAK,CACZ,IAAI,WAAW,gBAAgB,EAC/B,KAAK,IAAI,YAAY,CAAC,KAAK,CAAC,CAC5B,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC,CAAC,CAAC;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
+ export declare function registerResources(server: McpServer): void;
3
+ //# sourceMappingURL=resources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAE9D,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmBzD"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Generic resource loader - registers all static file resources.
3
+ * Add new resources to the RESOURCES array below.
4
+ */
5
+ import { readFile } from 'node:fs/promises';
6
+ import { fileURLToPath } from 'node:url';
7
+ const RESOURCES = [
8
+ {
9
+ name: 'bucket-index',
10
+ uri: 'context://bucket-index',
11
+ file: fileURLToPath(new URL('../../resources/bucket-index.md', import.meta.url)),
12
+ title: 'Bucket Index',
13
+ description: 'Curated hint list of known Bucket buckets on the OSRS wiki (names, query forms, purposes). Not authoritative - if nothing matches, fall back to discovery via search-page-by-prefix in the Bucket: namespace.',
14
+ mimeType: 'text/markdown',
15
+ },
16
+ // Add more resources here:
17
+ // {
18
+ // name: 'my-resource',
19
+ // uri: 'context://my-resource',
20
+ // file: fileURLToPath(new URL('../../resources/my-file.txt', import.meta.url)),
21
+ // title: 'My Resource',
22
+ // description: 'Description for the model',
23
+ // mimeType: 'text/plain',
24
+ // },
25
+ ];
26
+ export function registerResources(server) {
27
+ for (const resource of RESOURCES) {
28
+ server.registerResource(resource.name, resource.uri, {
29
+ title: resource.title,
30
+ description: resource.description,
31
+ mimeType: resource.mimeType,
32
+ }, async (uri) => {
33
+ const text = await readFile(resource.file, 'utf8');
34
+ const href = typeof uri === 'string' ? uri : uri.href;
35
+ return {
36
+ contents: [{ uri: href, mimeType: resource.mimeType, text }],
37
+ };
38
+ });
39
+ }
40
+ }
41
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAWzC,MAAM,SAAS,GAAqB;IACnC;QACC,IAAI,EAAE,cAAc;QACpB,GAAG,EAAE,wBAAwB;QAC7B,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,CAAC,iCAAiC,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;QAChF,KAAK,EAAE,cAAc;QACrB,WAAW,EACV,+MAA+M;QAChN,QAAQ,EAAE,eAAe;KACzB;IACD,2BAA2B;IAC3B,IAAI;IACJ,wBAAwB;IACxB,iCAAiC;IACjC,iFAAiF;IACjF,yBAAyB;IACzB,6CAA6C;IAC7C,2BAA2B;IAC3B,KAAK;CACL,CAAC;AAIF,MAAM,UAAU,iBAAiB,CAAC,MAAiB;IAClD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,MAAM,CAAC,gBAAgB,CACtB,QAAQ,CAAC,IAAI,EACb,QAAQ,CAAC,GAAG,EACZ;YACC,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;SAC3B,EACD,KAAK,EAAE,GAAiB,EAAE,EAAE;YAC3B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;YACtD,OAAO;gBACN,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC;aAC5D,CAAC;QACH,CAAC,CACD,CAAC;IACH,CAAC;AACF,CAAC"}
@@ -0,0 +1,23 @@
1
+ export declare class RateLimitError extends Error {
2
+ }
3
+ declare class SlidingWindowLimiter {
4
+ #private;
5
+ private readonly maxRequests;
6
+ private readonly windowMs;
7
+ private readonly maxWaitMs;
8
+ private readonly label;
9
+ private readonly stamps;
10
+ constructor(maxRequests: number, windowMs: number, maxWaitMs: number, label: string);
11
+ acquire(): Promise<void>;
12
+ }
13
+ export declare const externalLimiter: SlidingWindowLimiter;
14
+ export declare const SERVER_NAME = "osrs-wiki-mcp-server";
15
+ export declare const SERVER_VERSION = "1.0.0";
16
+ /**
17
+ * Assembles the MCP server: upstream MediaWiki tools wired to pinned OSRS wiki.
18
+ */
19
+ import './env.js';
20
+ import { McpServer } from '@modelcontextprotocol/server';
21
+ export declare function buildServer(): Promise<McpServer>;
22
+ export {};
23
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AA2BA,qBAAa,cAAe,SAAQ,KAAK;CAAG;AAM5C,cAAM,oBAAoB;;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IAEvC,YAAY,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAKlF;IAQK,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAkB7B;CACD;AASD,eAAO,MAAM,eAAe,sBAK3B,CAAC;AAyDF,eAAO,MAAM,WAAW,yBAAyB,CAAC;AAClD,eAAO,MAAM,cAAc,UAAU,CAAC;AA4CtC;;GAEG;AACH,OAAO,UAAU,CAAC;AAElB,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAWzD,wBAAsB,WAAW,IAAI,OAAO,CAAC,SAAS,CAAC,CAgDtD"}
package/dist/server.js ADDED
@@ -0,0 +1,193 @@
1
+ import { reconcileTools } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/reconcile.js';
2
+ import { allTools, registerAllTools, } from '@professional-wiki/mediawiki-mcp-server/dist/tools/index.js';
3
+ import { extensionPacks } from '@professional-wiki/mediawiki-mcp-server/dist/tools/extensions/index.js';
4
+ import { textResult } from './external.js';
5
+ /**
6
+ * Two independent sliding-window rate limiters (package-owned constants).
7
+ * wikiLimiter: per MediaWiki tool call at registration seam.
8
+ * externalLimiter: per HTTP request via callHttp.
9
+ * Over budget: waits up to 10s, then fails with RateLimitError.
10
+ */
11
+ const WINDOW_MS = 60_000;
12
+ const MAX_WAIT_MS = 10_000;
13
+ const WIKI_MAX_REQUESTS = 30;
14
+ const EXTERNAL_MAX_REQUESTS = 30;
15
+ export class RateLimitError extends Error {
16
+ }
17
+ function sleep(ms) {
18
+ return new Promise((resolve) => setTimeout(resolve, ms));
19
+ }
20
+ class SlidingWindowLimiter {
21
+ maxRequests;
22
+ windowMs;
23
+ maxWaitMs;
24
+ label;
25
+ stamps = [];
26
+ constructor(maxRequests, windowMs, maxWaitMs, label) {
27
+ this.maxRequests = maxRequests;
28
+ this.windowMs = windowMs;
29
+ this.maxWaitMs = maxWaitMs;
30
+ this.label = label;
31
+ }
32
+ #prune(now) {
33
+ while (this.stamps.length > 0 && this.stamps[0] + this.windowMs <= now) {
34
+ this.stamps.shift();
35
+ }
36
+ }
37
+ async acquire() {
38
+ for (;;) {
39
+ const now = Date.now();
40
+ this.#prune(now);
41
+ if (this.stamps.length < this.maxRequests) {
42
+ this.stamps.push(now);
43
+ return;
44
+ }
45
+ const waitMs = this.stamps[0] + this.windowMs - now + 1;
46
+ if (waitMs > this.maxWaitMs) {
47
+ throw new RateLimitError(`${this.label} rate limit reached ` +
48
+ `(${this.maxRequests} requests per ${Math.round(this.windowMs / 1000)}s). ` +
49
+ `Retry in ~${Math.ceil(waitMs / 1000)}s.`);
50
+ }
51
+ await sleep(waitMs);
52
+ }
53
+ }
54
+ }
55
+ const wikiLimiter = new SlidingWindowLimiter(WIKI_MAX_REQUESTS, WINDOW_MS, MAX_WAIT_MS, 'MediaWiki API');
56
+ export const externalLimiter = new SlidingWindowLimiter(EXTERNAL_MAX_REQUESTS, WINDOW_MS, MAX_WAIT_MS, 'External API');
57
+ function createToolRegistry(server, ctx) {
58
+ const tools = new Map();
59
+ const applyGates = () => reconcileTools(tools, {
60
+ wikiRegistry: ctx.wikis,
61
+ transport: ctx.auth.transport,
62
+ wikiProbe: ctx.wikiProbe,
63
+ extensionPacks,
64
+ descriptors: allTools,
65
+ });
66
+ // Extension probes can flip tools after initial gate; re-gate and notify.
67
+ const reconcile = () => {
68
+ const promise = (async () => {
69
+ await applyGates();
70
+ await server.sendResourceListChanged?.();
71
+ })();
72
+ return promise;
73
+ };
74
+ // Rate-limit each upstream MediaWiki tool call at registration seam.
75
+ // Must wrap before upstream's register() collects RegisteredTool objects.
76
+ const baseRegisterTool = server.registerTool.bind(server);
77
+ server.registerTool = (name, config, callback) => {
78
+ if (typeof callback !== 'function') {
79
+ return baseRegisterTool(name, config);
80
+ }
81
+ return baseRegisterTool(name, config, async (...callbackArgs) => {
82
+ try {
83
+ await wikiLimiter.acquire();
84
+ }
85
+ catch (error) {
86
+ if (error instanceof RateLimitError) {
87
+ return textResult(error.message, true);
88
+ }
89
+ throw error;
90
+ }
91
+ return callback(...callbackArgs);
92
+ });
93
+ };
94
+ const registered = registerAllTools(server, reconcile, ctx);
95
+ server.registerTool = baseRegisterTool;
96
+ for (const [name, tool] of registered) {
97
+ tools.set(name, tool);
98
+ }
99
+ return { tools, applyGates, reconcile };
100
+ }
101
+ import { USER_AGENT } from './env.js';
102
+ export const SERVER_NAME = 'osrs-wiki-mcp-server';
103
+ export const SERVER_VERSION = '1.0.0';
104
+ const SERVER_TITLE = 'OSRS Wiki MCP Server';
105
+ const SERVER_DESCRIPTION = 'MCP server for the Old School RuneScape Wiki: the full MediaWiki MCP tool surface against a pinned read-only wiki.';
106
+ const BASE_USER_AGENT = `osrs-wiki-mcp-server/1.0.0 (https://github.com/mistereman22/osrs-wiki-mcp-server)`;
107
+ const OSRS_WIKI = Object.freeze({
108
+ key: 'oldschool.runescape.wiki',
109
+ sitename: 'Old School RuneScape Wiki',
110
+ server: 'https://oldschool.runescape.wiki',
111
+ articlepath: '/wiki',
112
+ scriptpath: '',
113
+ });
114
+ // Cache hint: lists change only when wiki probe flips; don't share across deployments.
115
+ const CACHE_HINT = { ttlMs: 60_000, cacheScope: 'private' };
116
+ const SERVER_INSTRUCTIONS = [
117
+ `Tools and resources for the Old School RuneScape Wiki at ${OSRS_WIKI.server}, served by ${SERVER_NAME}.`,
118
+ 'The wiki target is fixed by the package: no tool can add, remove, or switch wikis.',
119
+ `Deployment is read-only and anonymous. Outbound calls use User-Agent \`${BASE_USER_AGENT} <MCP_CONTACT>\`.`,
120
+ 'MediaWiki tools follow upstream behavior from @professional-wiki/mediawiki-mcp-server.',
121
+ 'For structured data prefer `bucket-query`; use `context://bucket-index` to discover buckets, fall back to runtime discovery.',
122
+ 'Outbound traffic is rate-limited; calls over budget wait or return rate-limit error.',
123
+ ].join(' ');
124
+ function buildWikiConfig() {
125
+ return {
126
+ defaultWiki: OSRS_WIKI.key,
127
+ allowWikiManagement: false,
128
+ uploadDirs: [],
129
+ wikis: {
130
+ [OSRS_WIKI.key]: {
131
+ sitename: OSRS_WIKI.sitename,
132
+ server: OSRS_WIKI.server,
133
+ articlepath: OSRS_WIKI.articlepath,
134
+ scriptpath: OSRS_WIKI.scriptpath,
135
+ readOnly: true,
136
+ token: null,
137
+ private: false,
138
+ },
139
+ },
140
+ };
141
+ }
142
+ /**
143
+ * Assembles the MCP server: upstream MediaWiki tools wired to pinned OSRS wiki.
144
+ */
145
+ import './env.js';
146
+ import { McpServer } from '@modelcontextprotocol/server';
147
+ import { createAppState } from '@professional-wiki/mediawiki-mcp-server/dist/wikis/state.js';
148
+ import { createSharedContext } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/createContext.js';
149
+ import { logger } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/logger.js';
150
+ import { readAuthPolicy } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/authShape.js';
151
+ import { StdioSignIn } from '@professional-wiki/mediawiki-mcp-server/dist/auth/stdioSignIn.js';
152
+ import { readMetrics } from '@professional-wiki/mediawiki-mcp-server/dist/runtime/metrics.js';
153
+ import { registerResources } from './resources.js';
154
+ import { registerExternalApiTools } from './external.js';
155
+ export async function buildServer() {
156
+ logger.info(`${SERVER_NAME} ${SERVER_VERSION} starting - wiki ${OSRS_WIKI.server}, ` +
157
+ `read-only, user agent ${JSON.stringify(USER_AGENT)}`);
158
+ const state = createAppState(buildWikiConfig());
159
+ const auth = readAuthPolicy(process.env, 'stdio', null, new StdioSignIn());
160
+ const ctx = createSharedContext({
161
+ logger,
162
+ state,
163
+ auth,
164
+ metrics: readMetrics(process.env, 'stdio'),
165
+ });
166
+ const server = new McpServer({
167
+ name: SERVER_NAME,
168
+ title: SERVER_TITLE,
169
+ version: SERVER_VERSION,
170
+ description: SERVER_DESCRIPTION,
171
+ }, {
172
+ capabilities: {
173
+ resources: { listChanged: true },
174
+ tools: { listChanged: true },
175
+ },
176
+ instructions: SERVER_INSTRUCTIONS,
177
+ cacheHints: {
178
+ 'tools/list': CACHE_HINT,
179
+ 'resources/list': CACHE_HINT,
180
+ 'resources/templates/list': CACHE_HINT,
181
+ 'resources/read': CACHE_HINT,
182
+ 'server/discover': CACHE_HINT,
183
+ },
184
+ });
185
+ const { applyGates } = createToolRegistry(server, ctx);
186
+ registerResources(server);
187
+ // Package tools registered outside upstream tool map; never gated.
188
+ registerExternalApiTools(server);
189
+ // Extension-gated tools enabled after initial reconcile once wiki probe completes.
190
+ await applyGates();
191
+ return server;
192
+ }
193
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
- "name": "@mistereman22/osrs-wiki-mcp-server",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@mistereman22/osrs-wiki-mcp-server",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for the Old School RuneScape Wiki: the full MediaWiki MCP tool surface against a pinned read-only wiki.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "osrs-wiki-mcp-server": "./dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist/**/*",
12
+ "resources/**/*"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "start": "node dist/index.js",
17
+ "dev": "tsc && node dist/index.js"
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "engines": {
23
+ "node": ">=22.19.0"
24
+ },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/mistereman22/osrs-wiki-mcp-server.git"
28
+ },
29
+ "keywords": [
30
+ "mcp",
31
+ "model-context-protocol",
32
+ "mediawiki",
33
+ "osrs",
34
+ "runescape",
35
+ "wiki"
36
+ ],
37
+ "author": "mistereman22",
38
+ "license": "MIT",
39
+ "bugs": {
40
+ "url": "https://github.com/mistereman22/osrs-wiki-mcp-server/issues"
41
+ },
42
+ "homepage": "https://github.com/mistereman22/osrs-wiki-mcp-server#readme",
43
+ "dependencies": {
44
+ "@modelcontextprotocol/server": "^2.4.0",
45
+ "@professional-wiki/mediawiki-mcp-server": "^0.21.0",
46
+ "axios": "^1.21.0",
47
+ "zod": "^4.6.7"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^26.6.6",
51
+ "typescript": "^7.0.2"
52
+ }
53
+ }
@@ -0,0 +1,77 @@
1
+ # Bucket Index (hint list)
2
+
3
+ > On-demand hints for `bucket-query` on the Old School RuneScape Wiki.
4
+ > **This index is not authoritative.** Purpose descriptions are best-effort
5
+ > summaries; fields always come from the schema page. If nothing here fits
6
+ > your need, or a name fails at runtime, fall back to discovery:
7
+ > `search-page-by-prefix` in the `Bucket:` namespace (id 9592), then
8
+ > `get-page` on `Bucket:<Name>` to see exact fields and a data preview.
9
+ >
10
+ > Naming rule: pass the page title lowercased with spaces as underscores
11
+ > (`Bucket:Clue equipment` -> `bucket("clue_equipment")`). All bucket and
12
+ > field names are lowercase; fields are verified on the schema page, never
13
+ > guessed from this file.
14
+
15
+ ## Known buckets
16
+
17
+ | Query as | Page | Holds |
18
+ | --- | --- | --- |
19
+ | `bucket("bountytaskline")` | Bucket:Bountytaskline | Bounty Hunter task line entries |
20
+ | `bucket("clue_equipment")` | Bucket:Clue equipment | Equipment associated with clue scroll steps |
21
+ | `bucket("clue_info")` | Bucket:Clue info | Clue scroll metadata/step info |
22
+ | `bucket("collection_log_source")` | Bucket:Collection log source | Sources for collection log slots |
23
+ | `bucket("combat_achievement")` | Bucket:Combat achievement | Combat achievement tasks, tiers, requirements |
24
+ | `bucket("couriertaskline")` | Bucket:Couriertaskline | Courier activity task line entries |
25
+ | `bucket("demonicpactleaguetask")` | Bucket:Demonicpactleaguetask | Demonic Pact league task entries |
26
+ | `bucket("dependency_list")` | Bucket:Dependency list | Item/object dependency relations |
27
+ | `bucket("drop_table_sources")` | Bucket:Drop table sources | Which activities/NPCs use which drop tables |
28
+ | `bucket("dropsline")` | Bucket:Dropsline | Drop table line entries per source |
29
+ | `bucket("exchange")` | Bucket:Exchange | Grand Exchange prices (name, value, high/low, volumes) |
30
+ | `bucket("feedback")` | Bucket:Feedback | Wiki feedback entries (site-internal) |
31
+ | `bucket("ge_index_header")` | Bucket:Ge index header | Grand Exchange index header/snapshot metadata |
32
+ | `bucket("infobox_activity")` | Bucket:Infobox activity | Skilling/activity infobox data |
33
+ | `bucket("infobox_bonuses")` | Bucket:Infobox bonuses | Equipment combat stats and bonuses |
34
+ | `bucket("infobox_construction")` | Bucket:Infobox construction | Construction object/buildable data |
35
+ | `bucket("infobox_grid_master_unlock")` | Bucket:Infobox grid master unlock | Grid Master unlock data |
36
+ | `bucket("infobox_item")` | Bucket:Infobox item | Core item data: id, name, examine, flags, stats |
37
+ | `bucket("infobox_location")` | Bucket:Infobox location | Location/area data |
38
+ | `bucket("infobox_monster")` | Bucket:Infobox monster | Monster combat stats: level, hitpoints, max hit |
39
+ | `bucket("infobox_npc")` | Bucket:Infobox npc | NPC data: id, combat level, examine |
40
+ | `bucket("infobox_pure")` | Bucket:Infobox pure | Pure-entity infobox data (verify schema) |
41
+ | `bucket("infobox_scenery")` | Bucket:Infobox scenery | Scenery/object entity data |
42
+ | `bucket("infobox_ship_part")` | Bucket:Infobox ship part | Ship part data |
43
+ | `bucket("infobox_shop")` | Bucket:Infobox shop | Shop stock and prices |
44
+ | `bucket("infobox_spell")` | Bucket:Infobox spell | Spell data: requirements, effects, costs |
45
+ | `bucket("interface")` | Bucket:Interface | Game interface definitions |
46
+ | `bucket("item_id")` | Bucket:Item id | Item name to id mappings |
47
+ | `bucket("locline")` | Bucket:Locline | Location line entries |
48
+ | `bucket("logs")` | Bucket:Logs | Woodcutting log/tree data |
49
+ | `bucket("map")` | Bucket:Map | Map marker/region data |
50
+ | `bucket("mine")` | Bucket:Mine | Mining node/rock data |
51
+ | `bucket("mmg_output")` | Bucket:Mmg output | Money making guide output calculations |
52
+ | `bucket("money_making_guide")` | Bucket:Money making guide | Money making methods: profit, requirements |
53
+ | `bucket("music")` | Bucket:Music | Music track unlock data |
54
+ | `bucket("music_file")` | Bucket:Music file | Music file metadata |
55
+ | `bucket("music_map")` | Bucket:Music map | Music tracks by region/map area |
56
+ | `bucket("music_versions")` | Bucket:Music versions | Music version/history entries |
57
+ | `bucket("npc_id")` | Bucket:Npc id | NPC name to id mappings |
58
+ | `bucket("object_id")` | Bucket:Object id | Object/scenery id mappings |
59
+ | `bucket("quest")` | Bucket:Quest | Quest data: requirements, rewards, difficulty |
60
+ | `bucket("recipe")` | Bucket:Recipe | Creation/processing recipes: inputs, outputs, skills |
61
+ | `bucket("recommended_equipment")` | Bucket:Recommended equipment | Recommended gear setups by activity/slot |
62
+ | `bucket("seachart")` | Bucket:Seachart | Sea chart/naval data |
63
+ | `bucket("sound_effect")` | Bucket:Sound effect | Sound effect metadata |
64
+ | `bucket("soundtrack")` | Bucket:Soundtrack | Soundtrack track data |
65
+ | `bucket("storeline")` | Bucket:Storeline | Store line entries |
66
+ | `bucket("testing_bucket")` | Bucket:Testing bucket | Wiki-internal testing data (not real content) |
67
+ | `bucket("transcript")` | Bucket:Transcript | NPC/dialogue transcripts |
68
+ | `bucket("update")` | Bucket:Update | Game update records: dates, summaries |
69
+ | `bucket("varbit")` | Bucket:Varbit | Varbit id to meaning mappings |
70
+
71
+ ## Query reminders
72
+
73
+ - Query must end in `.run()`; `limit` max 500, paginate with `continueFrom`.
74
+ - Equality: `where("field","value")`; operators: `where({"field",">",value})`
75
+ with `=`, `!=`, `>`, `<`, `>=`, `<=`; compose with `Bucket.And/Or/Not`.
76
+ - Example: `bucket("infobox_item").select("item_id","examine").where("item_name","Raw lobster").run()`
77
+ - Full Lua reference: https://meta.weirdgloop.org/w/Extension:Bucket/Usage