@iamjameslennon/ddb-mcp 2.7.0 → 2.8.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 +43 -68
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +59 -0
- package/dist/browser.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +49 -20
- package/dist/index.js.map +1 -1
- package/dist/open5e.d.ts +2 -0
- package/dist/open5e.d.ts.map +1 -1
- package/dist/open5e.js +4 -0
- package/dist/open5e.js.map +1 -1
- package/dist/tools/character.d.ts +2 -0
- package/dist/tools/character.d.ts.map +1 -1
- package/dist/tools/character.js +53 -3
- package/dist/tools/character.js.map +1 -1
- package/dist/tools/monster.d.ts +2 -0
- package/dist/tools/monster.d.ts.map +1 -1
- package/dist/tools/monster.js +4 -0
- package/dist/tools/monster.js.map +1 -1
- package/dist/tools/reference.d.ts +5 -0
- package/dist/tools/reference.d.ts.map +1 -1
- package/dist/tools/reference.js +76 -69
- package/dist/tools/reference.js.map +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -222,25 +222,37 @@ Can a character use the Help action to assist with a skill check?
|
|
|
222
222
|
|
|
223
223
|
## Installation
|
|
224
224
|
|
|
225
|
-
|
|
225
|
+
> 🆕 **New to all this?** If you're on a Mac starting from scratch (no Homebrew, no Node, no Claude Desktop), follow the [step-by-step beginner tutorial](INSTALL-MACOS.md) instead — it covers everything below plus how to install the prerequisites.
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
Add this to your MCP client's config — no separate install step needed.
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
{
|
|
231
|
+
"mcpServers": {
|
|
232
|
+
"dndbeyond": {
|
|
233
|
+
"command": "npx",
|
|
234
|
+
"args": ["-y", "@iamjameslennon/ddb-mcp"]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
229
238
|
```
|
|
230
239
|
|
|
231
|
-
Playwright Chromium is
|
|
240
|
+
On first launch, `npx` fetches the package itself (small — under 200 kB unpacked of JS). Playwright Chromium (~140 MB) is downloaded **on demand the first time you run `ddb_login`**, with progress printed to the server log; subsequent logins reuse the cached browser. This keeps server startup fast and the heavy download happens at a moment you expect to wait.
|
|
232
241
|
|
|
233
|
-
|
|
242
|
+
Configure the path to your client's config file in the [Connecting to your MCP client](#connecting-to-your-mcp-client) section below.
|
|
234
243
|
|
|
235
|
-
|
|
244
|
+
To pin a version (recommended for production setups), change the args to `["-y", "@iamjameslennon/ddb-mcp@2.7.0"]`.
|
|
245
|
+
|
|
246
|
+
### Alternative: install globally
|
|
247
|
+
|
|
248
|
+
If you'd rather have a persistent binary on `PATH` (offline use, air-gapped networks, faster startup):
|
|
236
249
|
|
|
237
250
|
```bash
|
|
238
|
-
|
|
239
|
-
cd ddb-mcp
|
|
240
|
-
npm ci
|
|
241
|
-
npx playwright install chromium
|
|
251
|
+
npm install -g @iamjameslennon/ddb-mcp
|
|
242
252
|
```
|
|
243
253
|
|
|
254
|
+
Then use `"command": "ddb-mcp"` (no args) in your client config. Chromium is still fetched on first `ddb_login` rather than during install.
|
|
255
|
+
|
|
244
256
|
---
|
|
245
257
|
|
|
246
258
|
## Security & Privacy
|
|
@@ -258,7 +270,7 @@ npx playwright install chromium
|
|
|
258
270
|
- Screenshots (opt-in): `~/Downloads` only
|
|
259
271
|
- **Transport**: stdio only — the server opens no HTTP listeners and no ports.
|
|
260
272
|
- **Untrusted content**: tools that return D&D Beyond page text (`ddb_navigate`, `ddb_get_page`) wrap the scraped output in `<untrusted_dndbeyond_content>` tags. Character notes, campaign descriptions, party-member backstories, and book content can be authored by other DDB users (DMs, party members, forum posters) and may contain prompt-injection attempts — treat them as untrusted input, never as instructions. The `confirm_click` / `confirm_fill` gates on `ddb_interact` exist for exactly this reason.
|
|
261
|
-
- **Recommendation**: pin the version
|
|
273
|
+
- **Recommendation**: pin the version in your MCP client config — `"@iamjameslennon/ddb-mcp@2.7.0"` — rather than letting `npx` auto-update on every launch.
|
|
262
274
|
|
|
263
275
|
---
|
|
264
276
|
|
|
@@ -266,85 +278,44 @@ npx playwright install chromium
|
|
|
266
278
|
|
|
267
279
|
This server was built and tested with Claude — it will work with any MCP-compatible client, but response quality for D&D-specific reasoning will vary depending on the model used.
|
|
268
280
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
npm root -g
|
|
273
|
-
# macOS/Linux example: /usr/local/lib/node_modules
|
|
274
|
-
# Windows example: C:\Users\<you>\AppData\Roaming\npm\node_modules
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
> In all config examples below, replace `/usr/local/lib/node_modules` with the output of `npm root -g` on your system.
|
|
278
|
-
|
|
279
|
-
> **Windows note**: JSON treats `\` as an escape character. Either use forward slashes (Node accepts them on Windows) — `"C:/Users/<you>/AppData/Roaming/npm/node_modules/@iamjameslennon/ddb-mcp/dist/index.js"` — or double every backslash: `"C:\\Users\\<you>\\AppData\\Roaming\\npm\\node_modules\\@iamjameslennon\\ddb-mcp\\dist\\index.js"`. Forward slashes are simpler and harder to get wrong.
|
|
281
|
+
All clients below use the same JSON config from the [Installation](#installation) section. Drop it into your client's config file (paths below), then restart the client.
|
|
280
282
|
|
|
281
283
|
### Claude Desktop (recommended)
|
|
282
284
|
|
|
283
|
-
Edit your Claude Desktop config file:
|
|
284
|
-
|
|
285
285
|
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
286
286
|
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
287
|
-
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
|
287
|
+
- **Linux**: `~/.config/Claude/claude_desktop_config.json` (community builds only — Claude Desktop has no official Linux release; use [Claude Code](#claude-code) or [Cursor](#cursor) instead)
|
|
288
288
|
|
|
289
|
-
|
|
289
|
+
### Claude Code
|
|
290
290
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
"command": "node",
|
|
296
|
-
"args": ["/usr/local/lib/node_modules/@iamjameslennon/ddb-mcp/dist/index.js"]
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
291
|
+
One-liner — no manual JSON editing:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
claude mcp add dndbeyond -- npx -y @iamjameslennon/ddb-mcp
|
|
300
295
|
```
|
|
301
296
|
|
|
302
|
-
|
|
297
|
+
Or if you installed globally:
|
|
303
298
|
|
|
304
299
|
```bash
|
|
305
|
-
claude mcp add dndbeyond
|
|
300
|
+
claude mcp add dndbeyond ddb-mcp
|
|
306
301
|
```
|
|
307
302
|
|
|
308
303
|
### Cursor
|
|
309
304
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
```json
|
|
313
|
-
{
|
|
314
|
-
"mcpServers": {
|
|
315
|
-
"dndbeyond": {
|
|
316
|
-
"command": "node",
|
|
317
|
-
"args": ["/usr/local/lib/node_modules/@iamjameslennon/ddb-mcp/dist/index.js"]
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
```
|
|
305
|
+
`~/.cursor/mcp.json`
|
|
322
306
|
|
|
323
307
|
### Windsurf
|
|
324
308
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
```json
|
|
328
|
-
{
|
|
329
|
-
"mcpServers": {
|
|
330
|
-
"dndbeyond": {
|
|
331
|
-
"command": "node",
|
|
332
|
-
"args": ["/usr/local/lib/node_modules/@iamjameslennon/ddb-mcp/dist/index.js"]
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
```
|
|
309
|
+
`~/.codeium/windsurf/mcp_config.json`
|
|
337
310
|
|
|
338
311
|
### LM Studio
|
|
339
312
|
|
|
340
|
-
MCP support was added in LM Studio 0.3.x. Configure through the UI under **Settings → MCP Servers** using the same JSON shape as
|
|
313
|
+
MCP support was added in LM Studio 0.3.x. Configure through the UI under **Settings → MCP Servers** using the same JSON shape as above. Steps may vary between versions — see [lmstudio.ai/docs](https://lmstudio.ai/docs) for current instructions.
|
|
341
314
|
|
|
342
315
|
### Open WebUI
|
|
343
316
|
|
|
344
317
|
MCP servers are configured through the admin panel under **Settings → Tools**. See [docs.openwebui.com](https://docs.openwebui.com) for current instructions — the UI changes frequently between releases.
|
|
345
318
|
|
|
346
|
-
> LM Studio and Open WebUI use the same underlying `node` command and path as the other clients above.
|
|
347
|
-
|
|
348
319
|
---
|
|
349
320
|
|
|
350
321
|
## First-time login
|
|
@@ -547,15 +518,17 @@ Remove-Item "$env:APPDATA\ddb-mcp\session.json"
|
|
|
547
518
|
Your session has expired. Run `ddb_login` to re-authenticate.
|
|
548
519
|
|
|
549
520
|
**Chromium not found / browser won't launch**
|
|
521
|
+
Chromium is installed lazily on first `ddb_login`. If the download failed (network or sandbox issue), run `ddb_login` again — the server retries on each call. If it keeps failing, fetch the browser manually:
|
|
550
522
|
```bash
|
|
551
523
|
npx playwright install chromium
|
|
552
524
|
```
|
|
525
|
+
The same global Playwright cache is shared by every install path (npx, global, local clone) — one successful install is reused everywhere.
|
|
553
526
|
|
|
554
527
|
**Character returns 403 or "private"**
|
|
555
528
|
The character is set to private on D&D Beyond. You must be logged in as the owner, or the owner must make it public.
|
|
556
529
|
|
|
557
530
|
**MCP server not appearing in Claude Code**
|
|
558
|
-
Run `/mcp` in Claude Code to reconnect. If it still doesn't appear,
|
|
531
|
+
Run `/mcp` in Claude Code to reconnect. If it still doesn't appear, run `claude mcp list` to confirm the `dndbeyond` entry exists.
|
|
559
532
|
|
|
560
533
|
**Server crashes on startup**
|
|
561
534
|
Make sure you're running Node.js 20 or later: `node --version`.
|
|
@@ -565,10 +538,12 @@ Make sure you're running Node.js 20 or later: `node --version`.
|
|
|
565
538
|
## Development
|
|
566
539
|
|
|
567
540
|
```bash
|
|
568
|
-
# Install dependencies
|
|
541
|
+
# Install dependencies — prefer npm ci to respect the lockfile
|
|
569
542
|
npm ci
|
|
570
543
|
|
|
571
|
-
# Run in development mode (no build step needed)
|
|
544
|
+
# Run in development mode (no build step needed). Chromium is fetched lazily
|
|
545
|
+
# on first `ddb_login`; if you want to pre-warm the cache:
|
|
546
|
+
# npx playwright install chromium
|
|
572
547
|
npm run dev
|
|
573
548
|
|
|
574
549
|
# Build
|
|
@@ -577,7 +552,7 @@ npm run build
|
|
|
577
552
|
# Watch mode
|
|
578
553
|
npm run build:watch
|
|
579
554
|
|
|
580
|
-
# Run tests
|
|
555
|
+
# Run tests (browser-free — they mock the Playwright surface)
|
|
581
556
|
npm test
|
|
582
557
|
```
|
|
583
558
|
|
package/dist/browser.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQrE,OAAO,EAA0B,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvF,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,CAAC;AA0DrC,wBAAsB,UAAU,CAAC,QAAQ,UAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAkBlE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,CAmB1E;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCxE;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAIpE;AAED,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAUlD"}
|
package/dist/browser.js
CHANGED
|
@@ -1,10 +1,65 @@
|
|
|
1
1
|
import { chromium } from "playwright";
|
|
2
2
|
import { existsSync, mkdirSync, openSync, closeSync, writeFileSync, fchmodSync, renameSync, constants, } from "fs";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
import { createRequire } from "module";
|
|
5
|
+
import { dirname, join } from "path";
|
|
3
6
|
import { invalidateSessionCache, SESSION_DIR, SESSION_PATH } from "./session-fetch.js";
|
|
4
7
|
export { SESSION_DIR, SESSION_PATH };
|
|
5
8
|
let browserInstance = null;
|
|
6
9
|
let browserHeadless = null;
|
|
7
10
|
let contextInstance = null;
|
|
11
|
+
// Singleton Promise so concurrent getBrowser() callers share one install run.
|
|
12
|
+
// Cleared on failure so subsequent calls can retry.
|
|
13
|
+
let chromiumInstallPromise = null;
|
|
14
|
+
async function ensureChromiumInstalled() {
|
|
15
|
+
if (chromiumInstallPromise)
|
|
16
|
+
return chromiumInstallPromise;
|
|
17
|
+
// chromium.executablePath() returns the *expected* path; if the binary is
|
|
18
|
+
// not there, we need to fetch it. Wrapped in try because some Playwright
|
|
19
|
+
// versions throw on this call when no browsers are registered yet.
|
|
20
|
+
let executablePath = "";
|
|
21
|
+
try {
|
|
22
|
+
executablePath = chromium.executablePath();
|
|
23
|
+
}
|
|
24
|
+
catch { /* fall through to install */ }
|
|
25
|
+
if (executablePath && existsSync(executablePath))
|
|
26
|
+
return;
|
|
27
|
+
chromiumInstallPromise = (async () => {
|
|
28
|
+
process.stderr.write("[ddb-mcp] Chromium not found — downloading (~140 MB, one-time)…\n");
|
|
29
|
+
// Resolve Playwright's CLI relative to *this* module (not CWD), so the
|
|
30
|
+
// install works whether the server was launched via npx, a global install,
|
|
31
|
+
// or a local clone. Note: Playwright's `exports` map doesn't expose
|
|
32
|
+
// `cli.js`, so we resolve `package.json` (which IS exported) and walk
|
|
33
|
+
// sideways. process.execPath ensures we use the same Node binary.
|
|
34
|
+
const require = createRequire(import.meta.url);
|
|
35
|
+
const cliPath = join(dirname(require.resolve("playwright/package.json")), "cli.js");
|
|
36
|
+
await new Promise((resolve, reject) => {
|
|
37
|
+
// stdout → parent stderr: critical because the MCP server uses stdout
|
|
38
|
+
// for JSON-RPC frames; leaking install progress into stdout would break
|
|
39
|
+
// the client connection.
|
|
40
|
+
const child = spawn(process.execPath, [cliPath, "install", "chromium"], {
|
|
41
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
42
|
+
});
|
|
43
|
+
child.stdout?.pipe(process.stderr);
|
|
44
|
+
child.stderr?.pipe(process.stderr);
|
|
45
|
+
child.on("error", reject);
|
|
46
|
+
child.on("exit", code => {
|
|
47
|
+
if (code === 0) {
|
|
48
|
+
process.stderr.write("[ddb-mcp] Chromium installed.\n");
|
|
49
|
+
resolve();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
reject(new Error(`Chromium install failed (exit ${code}). ` +
|
|
53
|
+
`Network or sandbox issue? Run \`npx playwright install chromium\` manually to retry.`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
})();
|
|
58
|
+
// Allow retry on failure — clear the cached promise so the next caller
|
|
59
|
+
// doesn't immediately re-fail against the same rejected Promise.
|
|
60
|
+
chromiumInstallPromise.catch(() => { chromiumInstallPromise = null; });
|
|
61
|
+
return chromiumInstallPromise;
|
|
62
|
+
}
|
|
8
63
|
export async function getBrowser(headless = true) {
|
|
9
64
|
// If a browser is already running with a different headless setting, close it
|
|
10
65
|
// first so ddb_login always gets a visible window even if headless tools ran before.
|
|
@@ -13,6 +68,10 @@ export async function getBrowser(headless = true) {
|
|
|
13
68
|
}
|
|
14
69
|
if (browserInstance)
|
|
15
70
|
return browserInstance;
|
|
71
|
+
// Lazy Chromium provisioning: fetched on first use (during ddb_login) so
|
|
72
|
+
// package install stays fast and the ~140 MB download happens at a moment
|
|
73
|
+
// the user expects work to happen and we can surface progress.
|
|
74
|
+
await ensureChromiumInstalled();
|
|
16
75
|
const args = ["--disable-blink-features=AutomationControlled"];
|
|
17
76
|
// Sandbox should stay enabled. Only disable it in constrained container
|
|
18
77
|
// environments (e.g. CI/Docker) where the kernel doesn't support it.
|
package/dist/browser.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
4
|
import { z } from "zod";
|
|
4
5
|
import { getBrowser, getContext, closeBrowser } from "./browser.js";
|
|
5
6
|
import { login } from "./auth.js";
|
|
6
|
-
import { getCharacter, downloadCharacter, listCharacters, parseCharacter, findCharacterByName, getDefinition } from "./tools/character.js";
|
|
7
|
-
import { getCampaign, listMyCampaigns } from "./tools/campaign.js";
|
|
7
|
+
import { getCharacter, downloadCharacter, listCharacters, parseCharacter, findCharacterByName, getDefinition, clearCharacterCache } from "./tools/character.js";
|
|
8
|
+
import { getCampaign, listMyCampaigns, invalidateCampaignCache } from "./tools/campaign.js";
|
|
8
9
|
import { getParty } from "./tools/party.js";
|
|
9
10
|
import { navigate, interact, getCurrentPageContent } from "./tools/navigate.js";
|
|
10
11
|
import { search } from "./tools/search.js";
|
|
11
12
|
import { listLibrary, readBook } from "./tools/library.js";
|
|
12
|
-
import { searchMonsters, getMonster } from "./tools/monster.js";
|
|
13
|
+
import { searchMonsters, getMonster, clearMonsterCache } from "./tools/monster.js";
|
|
13
14
|
import { rateEncounter, targetEncounterCr } from "./tools/encounter.js";
|
|
14
15
|
import { generateTreasure } from "./tools/treasure.js";
|
|
15
|
-
import { getCondition, searchSpells, getSpell, searchItems, getItem, searchRaces, searchClasses, searchBackgrounds, searchFeats, searchClassFeatures, searchRacialTraits, searchRules, getRule } from "./tools/reference.js";
|
|
16
|
+
import { getCondition, searchSpells, getSpell, searchItems, getItem, searchRaces, searchClasses, searchBackgrounds, searchFeats, searchClassFeatures, searchRacialTraits, searchRules, getRule, clearReferenceCache } from "./tools/reference.js";
|
|
17
|
+
import { clearOpen5eCache } from "./open5e.js";
|
|
16
18
|
const server = new McpServer({
|
|
17
19
|
name: "dndbeyond",
|
|
18
20
|
version: "1.0.0",
|
|
@@ -59,8 +61,35 @@ server.tool("ddb_close_browser", "Close the background browser window if one is
|
|
|
59
61
|
return { content: [{ type: "text", text: `Failed to close browser: ${msg}` }], isError: true };
|
|
60
62
|
}
|
|
61
63
|
});
|
|
64
|
+
// ─── ddb_clear_cache ─────────────────────────────────────────────────────────
|
|
65
|
+
server.tool("ddb_clear_cache", "Wipe in-process caches so the next call re-fetches from D&D Beyond. Use this when search results look like the SRD/Open5e fallback (e.g. 2024 cantrips missing from spell results) after you've logged in — the partial compendium build is cached for up to 5 minutes and this forces an immediate retry. cache='spells' (default) clears just the spell/reference compendium; 'all' wipes every cache.", {
|
|
66
|
+
cache: z
|
|
67
|
+
.enum(["spells", "characters", "monsters", "all"])
|
|
68
|
+
.default("spells")
|
|
69
|
+
.describe("Which cache to clear. 'spells' (default) — the spell / equipment / races / classes / feats compendium. 'characters' — character JSON cache. 'monsters' — monster stat-block cache. 'all' — everything, including campaigns and Open5e responses."),
|
|
70
|
+
}, async ({ cache }) => {
|
|
71
|
+
const cleared = [];
|
|
72
|
+
if (cache === "spells" || cache === "all") {
|
|
73
|
+
clearReferenceCache();
|
|
74
|
+
cleared.push("spells/reference compendium");
|
|
75
|
+
}
|
|
76
|
+
if (cache === "characters" || cache === "all") {
|
|
77
|
+
clearCharacterCache();
|
|
78
|
+
cleared.push("character JSON");
|
|
79
|
+
}
|
|
80
|
+
if (cache === "monsters" || cache === "all") {
|
|
81
|
+
clearMonsterCache();
|
|
82
|
+
cleared.push("monsters");
|
|
83
|
+
}
|
|
84
|
+
if (cache === "all") {
|
|
85
|
+
invalidateCampaignCache();
|
|
86
|
+
clearOpen5eCache();
|
|
87
|
+
cleared.push("campaigns", "Open5e responses");
|
|
88
|
+
}
|
|
89
|
+
return { content: [{ type: "text", text: `Cleared: ${cleared.join(", ")}. The next relevant tool call will re-fetch from D&D Beyond.` }] };
|
|
90
|
+
});
|
|
62
91
|
// ─── ddb_list_characters ──────────────────────────────────────────────────────
|
|
63
|
-
server.tool("ddb_list_characters", "List all characters in your D&D Beyond account, including their ID, level, race, and class.", {}, async () => {
|
|
92
|
+
server.tool("ddb_list_characters", "List all characters in your D&D Beyond account, including their ID, level, race, and class. Requires login — run ddb_login first if you haven't already.", {}, async () => {
|
|
64
93
|
try {
|
|
65
94
|
const result = await listCharacters();
|
|
66
95
|
return { content: [{ type: "text", text: result }] };
|
|
@@ -72,7 +101,7 @@ server.tool("ddb_list_characters", "List all characters in your D&D Beyond accou
|
|
|
72
101
|
}
|
|
73
102
|
});
|
|
74
103
|
// ─── ddb_get_character_raw ────────────────────────────────────────────────────
|
|
75
|
-
server.tool("ddb_get_character_raw", "Returns raw 300–500 KB character JSON. Requires confirm_large_response: true. Use ddb_get_character instead for all normal use.", {
|
|
104
|
+
server.tool("ddb_get_character_raw", "Returns raw 300–500 KB character JSON. Requires confirm_large_response: true. Use ddb_get_character instead for all normal use. Requires login — run ddb_login first if you haven't already.", {
|
|
76
105
|
character_id: z.string().min(1).optional().describe("The D&D Beyond character ID (e.g. '12345678')"),
|
|
77
106
|
character_name: z.string().min(1).optional().describe("Character name to look up (fuzzy matched against your account)"),
|
|
78
107
|
confirm_large_response: z.literal(true).describe("Must be true to proceed — acknowledges this call returns 300–500 KB."),
|
|
@@ -99,7 +128,7 @@ server.tool("ddb_get_character_raw", "Returns raw 300–500 KB character JSON. R
|
|
|
99
128
|
}
|
|
100
129
|
});
|
|
101
130
|
// ─── ddb_download_character ───────────────────────────────────────────────────
|
|
102
|
-
server.tool("ddb_download_character", "Download a character's full JSON data to a local file.", {
|
|
131
|
+
server.tool("ddb_download_character", "Download a character's full JSON data to a local file. Requires login — run ddb_login first if you haven't already.", {
|
|
103
132
|
character_id: z.string().min(1).describe("The D&D Beyond character ID"),
|
|
104
133
|
output_path: z
|
|
105
134
|
.string()
|
|
@@ -118,7 +147,7 @@ server.tool("ddb_download_character", "Download a character's full JSON data to
|
|
|
118
147
|
}
|
|
119
148
|
});
|
|
120
149
|
// ─── ddb_get_character ───────────────────────────────────────────────────────
|
|
121
|
-
server.tool("ddb_get_character", "Parse and display a character sheet. Use sections to reduce output: summary (vitals+stats), combat (adds actions/weapons), spells (spellcasting only), inventory, features, concentration, notes (backstory, traits, bonds), or full (default).", {
|
|
150
|
+
server.tool("ddb_get_character", "Parse and display a character sheet. Use sections to reduce output: summary (vitals+stats), combat (adds actions/weapons), spells (spellcasting only), inventory, features, concentration, notes (backstory, traits, bonds), or full (default). Requires login — run ddb_login first if you haven't already.", {
|
|
122
151
|
character_id: z.string().min(1).optional().describe("The D&D Beyond character ID (e.g. '12345678')"),
|
|
123
152
|
character_name: z.string().min(1).optional().describe("Character name to look up (fuzzy matched — e.g. 'Throin' finds 'Thorin Ironforge')"),
|
|
124
153
|
sections: z.enum(["summary", "combat", "spells", "inventory", "features", "concentration", "notes", "full"])
|
|
@@ -147,7 +176,7 @@ server.tool("ddb_get_character", "Parse and display a character sheet. Use secti
|
|
|
147
176
|
}
|
|
148
177
|
});
|
|
149
178
|
// ─── ddb_character_lookup ────────────────────────────────────────────────────
|
|
150
|
-
server.tool("ddb_character_lookup", "Look up the full description of a spell, feat, class feature, subclass feature, racial trait, background feature, or equipped item by name. Supports partial and fuzzy name matching (e.g. 'cutting' finds Cutting Words, 'sheild' finds Shield). Accepts either a numeric character_id or a character_name.", {
|
|
179
|
+
server.tool("ddb_character_lookup", "Look up the full description of a spell, feat, class feature, subclass feature, racial trait, background feature, or equipped item by name. Supports partial and fuzzy name matching (e.g. 'cutting' finds Cutting Words, 'sheild' finds Shield). Accepts either a numeric character_id or a character_name. Requires login — run ddb_login first if you haven't already.", {
|
|
151
180
|
character_id: z.string().min(1).optional().describe("The D&D Beyond character ID"),
|
|
152
181
|
character_name: z.string().min(1).optional().describe("Character name (fuzzy matched against your account)"),
|
|
153
182
|
name: z.string().min(1).describe("Name to search for — partial match, e.g. 'hunter' finds Hunter's Mark"),
|
|
@@ -205,7 +234,7 @@ server.tool("ddb_get_monster", "Get the full stat block for a specific monster f
|
|
|
205
234
|
}
|
|
206
235
|
});
|
|
207
236
|
// ─── ddb_get_campaign ─────────────────────────────────────────────────────────
|
|
208
|
-
server.tool("ddb_get_campaign", "Fetch campaign information including player characters from a D&D Beyond campaign.", {
|
|
237
|
+
server.tool("ddb_get_campaign", "Fetch campaign information including player characters from a D&D Beyond campaign. Requires login — run ddb_login first if you haven't already.", {
|
|
209
238
|
campaign_id: z.string().min(1).describe("The D&D Beyond campaign ID (found in the campaign URL)"),
|
|
210
239
|
}, async ({ campaign_id }) => {
|
|
211
240
|
try {
|
|
@@ -219,7 +248,7 @@ server.tool("ddb_get_campaign", "Fetch campaign information including player cha
|
|
|
219
248
|
}
|
|
220
249
|
});
|
|
221
250
|
// ─── ddb_list_campaigns ───────────────────────────────────────────────────────
|
|
222
|
-
server.tool("ddb_list_campaigns", "List all D&D Beyond campaigns you are part of (as DM or player).", {}, async () => {
|
|
251
|
+
server.tool("ddb_list_campaigns", "List all D&D Beyond campaigns you are part of (as DM or player). Requires login — run ddb_login first if you haven't already.", {}, async () => {
|
|
223
252
|
try {
|
|
224
253
|
const data = await listMyCampaigns();
|
|
225
254
|
return { content: [{ type: "text", text: data }] };
|
|
@@ -231,7 +260,7 @@ server.tool("ddb_list_campaigns", "List all D&D Beyond campaigns you are part of
|
|
|
231
260
|
}
|
|
232
261
|
});
|
|
233
262
|
// ─── ddb_get_party ────────────────────────────────────────────────────────────
|
|
234
|
-
server.tool("ddb_get_party", "Fetch a compact summary of every character in a campaign. Returns HP, AC, initiative, passive scores, ability scores, and skills for the whole party in one call.", {
|
|
263
|
+
server.tool("ddb_get_party", "Fetch a compact summary of every character in a campaign. Returns HP, AC, initiative, passive scores, ability scores, and skills for the whole party in one call. Requires login — run ddb_login first if you haven't already.", {
|
|
235
264
|
campaign_id: z.string().min(1).describe("The D&D Beyond campaign ID (found in the campaign URL)"),
|
|
236
265
|
}, async ({ campaign_id }) => {
|
|
237
266
|
try {
|
|
@@ -339,7 +368,7 @@ server.tool("ddb_search_site", "Search D&D Beyond for spells, monsters, magic it
|
|
|
339
368
|
}
|
|
340
369
|
});
|
|
341
370
|
// ─── ddb_list_library ─────────────────────────────────────────────────────────
|
|
342
|
-
server.tool("ddb_list_library", "List all books and sourcebooks you own in your D&D Beyond library.", {}, async () => {
|
|
371
|
+
server.tool("ddb_list_library", "List all books and sourcebooks you own in your D&D Beyond library. Requires login — run ddb_login first if you haven't already.", {}, async () => {
|
|
343
372
|
try {
|
|
344
373
|
const context = await getSharedContext();
|
|
345
374
|
const books = await listLibrary(context);
|
|
@@ -354,7 +383,7 @@ server.tool("ddb_list_library", "List all books and sourcebooks you own in your
|
|
|
354
383
|
}
|
|
355
384
|
});
|
|
356
385
|
// ─── ddb_read_book ────────────────────────────────────────────────────────────
|
|
357
|
-
server.tool("ddb_read_book", "Read a D&D Beyond book. Accepts either a slug (e.g. dnd/phb-2024) or a plain title (e.g. \"Player's Handbook\" or \"monster manual\") — the server resolves titles against your library automatically. Specify chapter_slug for a chapter, query to jump to a heading, and max_chars to control response size.", {
|
|
386
|
+
server.tool("ddb_read_book", "Read a D&D Beyond book. Accepts either a slug (e.g. dnd/phb-2024) or a plain title (e.g. \"Player's Handbook\" or \"monster manual\") — the server resolves titles against your library automatically. Specify chapter_slug for a chapter, query to jump to a heading, and max_chars to control response size. Requires login — run ddb_login first if you haven't already.", {
|
|
358
387
|
book_slug: z
|
|
359
388
|
.string()
|
|
360
389
|
.min(1)
|
|
@@ -451,7 +480,7 @@ server.tool("ddb_get_equipment", "Get the full stats and description of any item
|
|
|
451
480
|
return { content: [{ type: "text", text: `Item lookup failed: ${msg}` }], isError: true };
|
|
452
481
|
}
|
|
453
482
|
});
|
|
454
|
-
server.tool("ddb_search_races", "Search all D&D Beyond races and subraces (including homebrew). Not character-specific.", {
|
|
483
|
+
server.tool("ddb_search_races", "Search all D&D Beyond races and subraces (including homebrew). Not character-specific. Requires login — run ddb_login first if you haven't already.", {
|
|
455
484
|
name: z.string().optional().describe("Partial race name (e.g. 'elf', 'tiefling')"),
|
|
456
485
|
limit: z.number().int().min(1).max(100).default(30).describe("Max results (default 30)"),
|
|
457
486
|
offset: z.number().int().min(0).default(0).describe("Skip N results for pagination"),
|
|
@@ -466,7 +495,7 @@ server.tool("ddb_search_races", "Search all D&D Beyond races and subraces (inclu
|
|
|
466
495
|
return { content: [{ type: "text", text: `Race search failed: ${msg}` }], isError: true };
|
|
467
496
|
}
|
|
468
497
|
});
|
|
469
|
-
server.tool("ddb_search_classes", "Search all D&D Beyond classes with hit die, spellcasting, and subclasses.", {
|
|
498
|
+
server.tool("ddb_search_classes", "Search all D&D Beyond classes with hit die, spellcasting, and subclasses. Requires login — run ddb_login first if you haven't already.", {
|
|
470
499
|
name: z.string().optional().describe("Partial class name (e.g. 'fighter', 'wizard')"),
|
|
471
500
|
limit: z.number().int().min(1).max(100).default(30).describe("Max results (default 30)"),
|
|
472
501
|
offset: z.number().int().min(0).default(0).describe("Skip N results for pagination"),
|
|
@@ -481,7 +510,7 @@ server.tool("ddb_search_classes", "Search all D&D Beyond classes with hit die, s
|
|
|
481
510
|
return { content: [{ type: "text", text: `Class search failed: ${msg}` }], isError: true };
|
|
482
511
|
}
|
|
483
512
|
});
|
|
484
|
-
server.tool("ddb_search_backgrounds", "Search all D&D Beyond backgrounds (including homebrew).", {
|
|
513
|
+
server.tool("ddb_search_backgrounds", "Search all D&D Beyond backgrounds (including homebrew). Requires login — run ddb_login first if you haven't already.", {
|
|
485
514
|
name: z.string().optional().describe("Partial background name (e.g. 'sage', 'criminal')"),
|
|
486
515
|
limit: z.number().int().min(1).max(100).default(30).describe("Max results (default 30)"),
|
|
487
516
|
offset: z.number().int().min(0).default(0).describe("Skip N results for pagination"),
|
|
@@ -496,7 +525,7 @@ server.tool("ddb_search_backgrounds", "Search all D&D Beyond backgrounds (includ
|
|
|
496
525
|
return { content: [{ type: "text", text: `Background search failed: ${msg}` }], isError: true };
|
|
497
526
|
}
|
|
498
527
|
});
|
|
499
|
-
server.tool("ddb_search_feats", "Search feats by name or prerequisite text.", {
|
|
528
|
+
server.tool("ddb_search_feats", "Search feats by name or prerequisite text. Requires login — run ddb_login first if you haven't already.", {
|
|
500
529
|
name: z.string().optional().describe("Partial feat name (e.g. 'sharpshooter', 'magic')"),
|
|
501
530
|
prerequisite: z.string().optional().describe("Filter by prerequisite text (e.g. 'spellcaster', 'level 4')"),
|
|
502
531
|
limit: z.number().int().min(1).max(100).default(30).describe("Max results (default 30)"),
|
|
@@ -512,7 +541,7 @@ server.tool("ddb_search_feats", "Search feats by name or prerequisite text.", {
|
|
|
512
541
|
return { content: [{ type: "text", text: `Feat search failed: ${msg}` }], isError: true };
|
|
513
542
|
}
|
|
514
543
|
});
|
|
515
|
-
server.tool("ddb_search_class_features", "Search class features by name, class, or level gained.", {
|
|
544
|
+
server.tool("ddb_search_class_features", "Search class features by name, class, or level gained. Requires login — run ddb_login first if you haven't already.", {
|
|
516
545
|
name: z.string().optional().describe("Partial feature name (e.g. 'action surge', 'sneak attack')"),
|
|
517
546
|
class_name: z.string().optional().describe("Class name filter (e.g. 'fighter', 'rogue')"),
|
|
518
547
|
level: z.number().int().min(1).max(20).optional().describe("Level at which the feature is gained"),
|
|
@@ -529,7 +558,7 @@ server.tool("ddb_search_class_features", "Search class features by name, class,
|
|
|
529
558
|
return { content: [{ type: "text", text: `Class feature search failed: ${msg}` }], isError: true };
|
|
530
559
|
}
|
|
531
560
|
});
|
|
532
|
-
server.tool("ddb_search_racial_traits", "Search racial traits by name or race.", {
|
|
561
|
+
server.tool("ddb_search_racial_traits", "Search racial traits by name or race. Requires login — run ddb_login first if you haven't already.", {
|
|
533
562
|
name: z.string().optional().describe("Partial trait name (e.g. 'darkvision', 'breath weapon')"),
|
|
534
563
|
race_name: z.string().optional().describe("Race name filter (e.g. 'elf', 'dragonborn')"),
|
|
535
564
|
limit: z.number().int().min(1).max(100).default(30).describe("Max results (default 30)"),
|