run-dmcp 0.2.0 → 0.4.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 +51 -13
- package/dist/bin/run-dmcp.js +1 -1
- package/dist/db/schema.js +170 -18
- package/dist/http/server.js +22 -1
- package/dist/index.d.ts +34 -2
- package/dist/index.js +86 -2
- package/dist/mcp-server.d.ts +1 -1
- package/dist/mcp-server.js +1 -1
- package/dist/register/resources.js +2 -2
- package/dist/rpg/index.d.ts +0 -16
- package/dist/rpg/index.js +4 -22
- package/dist/rpg/server.d.ts +16 -0
- package/dist/rpg/server.js +22 -0
- package/dist/schemas/index.d.ts +62 -62
- package/dist/server.d.ts +1 -0
- package/dist/server.js +20 -0
- package/dist/timeline/changes.d.ts +8 -0
- package/dist/timeline/changes.js +8 -0
- package/dist/timeline/export.d.ts +10 -0
- package/dist/timeline/export.js +10 -0
- package/dist/timeline/irreversible.d.ts +2 -0
- package/dist/timeline/replay.d.ts +22 -0
- package/dist/timeline/replay.js +22 -0
- package/dist/timeline/schema.js +2 -0
- package/dist/tools/audio.js +13 -9
- package/dist/tools/game.js +33 -1
- package/dist/tools/images.js +17 -10
- package/dist/tools/resource.d.ts +2 -2
- package/dist/tools/time.js +18 -3
- package/dist/types/index.d.ts +1 -1
- package/dist/utils/media-path.d.ts +52 -0
- package/dist/utils/media-path.js +106 -0
- package/dist/utils/output-schemas.d.ts +63 -63
- package/dist/utils/output-schemas.js +1 -1
- package/package.json +14 -2
- package/dist/register/abilities.d.ts +0 -2
- package/dist/register/abilities.js +0 -165
- package/dist/register/combat.d.ts +0 -2
- package/dist/register/combat.js +0 -207
- package/dist/register/mcp-prompts.d.ts +0 -2
- package/dist/register/mcp-prompts.js +0 -684
- package/dist/register/quests.d.ts +0 -2
- package/dist/register/quests.js +0 -118
- package/dist/register/status.d.ts +0 -2
- package/dist/register/status.js +0 -130
- package/dist/register/tables.d.ts +0 -2
- package/dist/register/tables.js +0 -146
- package/dist/tools/ability.d.ts +0 -48
- package/dist/tools/ability.js +0 -238
- package/dist/tools/combat.d.ts +0 -13
- package/dist/tools/combat.js +0 -195
- package/dist/tools/dice.d.ts +0 -23
- package/dist/tools/dice.js +0 -111
- package/dist/tools/quest.d.ts +0 -34
- package/dist/tools/quest.js +0 -164
- package/dist/tools/status.d.ts +0 -36
- package/dist/tools/status.js +0 -218
- package/dist/tools/tables.d.ts +0 -33
- package/dist/tools/tables.js +0 -209
package/README.md
CHANGED
|
@@ -12,23 +12,26 @@ holds full adjudication discretion over a world that only ever stores *now*.
|
|
|
12
12
|
|
|
13
13
|
## Status
|
|
14
14
|
|
|
15
|
-
**0.
|
|
16
|
-
|
|
15
|
+
**0.3.0.** The base is the predecessor's engine plus four pieces of generic mechanism that were built
|
|
16
|
+
for it and offered back to it:
|
|
17
17
|
|
|
18
18
|
- **Atomicity** — `withTransaction()` wired into the multi-write operations that were running
|
|
19
19
|
non-atomically.
|
|
20
20
|
- **Declarative constraints** — resources can be declared `bounded` or `monotonic`, and the store
|
|
21
|
-
enforces it rather than trusting every caller.
|
|
22
|
-
value can move only through an adjudicating call —
|
|
23
|
-
|
|
21
|
+
enforces it rather than trusting every caller. `resolve_only` — every direct write rejected, so a
|
|
22
|
+
value can move only through an adjudicating call — joined them alongside the resolve protocol, and
|
|
23
|
+
is enforced by a trigger rather than by a checker anyone has to remember to call; see
|
|
24
|
+
[docs/DESIGN.md](docs/DESIGN.md) §5.2a.
|
|
24
25
|
- **Conserved resource sets** — a set of resources can be declared conserved, with an atomic transfer
|
|
25
26
|
that never silently clamps.
|
|
26
27
|
- **On-expiry consequences** — scheduled events and timers can carry a consequence that actually
|
|
27
28
|
lands when they expire, rather than expiring into nothing.
|
|
28
29
|
|
|
29
|
-
0.1.1
|
|
30
|
-
the consuming application rather than into `node_modules`, and a consumer can bring up its own
|
|
31
|
-
through the migration hook below.
|
|
30
|
+
0.1.1 added the packaging half of that: importing the library starts nothing, the database resolves
|
|
31
|
+
to the consuming application rather than into `node_modules`, and a consumer can bring up its own
|
|
32
|
+
tables through the migration hook below. 0.3.0 adds the consuming half — the entity/property spine is
|
|
33
|
+
exported as library functions, so a consumer that deletes its own vendored copy has something to
|
|
34
|
+
import rather than a server to make tool calls into.
|
|
32
35
|
|
|
33
36
|
The timeline that gives this project its reason to exist — interval-versioned facts, `replay(t)`,
|
|
34
37
|
irreversibility, timeline export, `changes_within` — is built, and every write of world state appends
|
|
@@ -56,14 +59,28 @@ schema comes up, where the database lives, and whether anything listens.
|
|
|
56
59
|
|
|
57
60
|
**The package root is the core, and the tabletop surface is a layer above it.** Dice, combat,
|
|
58
61
|
abilities, status effects, random tables and quests are genuinely game-shaped — an optional
|
|
59
|
-
dependency, not part of the engine (see [docs/DESIGN.md](docs/DESIGN.md) §8).
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
dependency, not part of the engine (see [docs/DESIGN.md](docs/DESIGN.md) §8).
|
|
63
|
+
|
|
64
|
+
**Mechanism and assembly are separate entries, and importing one never loads the other** (since
|
|
65
|
+
0.4.0). Four specifiers, two layers by two kinds:
|
|
66
|
+
|
|
67
|
+
| | mechanism — functions, constants, types | assembly — builds a server |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| **core** | `run-dmcp` | `run-dmcp/server` → `createCoreMcpServer` |
|
|
70
|
+
| **+ tabletop** | `run-dmcp/rpg` | `run-dmcp/rpg/server` → `createMcpServer` |
|
|
71
|
+
|
|
72
|
+
Assembling a server means loading the MCP SDK, twenty-one register modules and (for the full
|
|
73
|
+
assembly) the web UI. Wanting `createGame` or `LIMITS` does not, and until 0.4.0 both entries
|
|
74
|
+
charged for it anyway: the core entry cost 97.4ms per process cold and costs 47.0ms now, which is
|
|
75
|
+
below the MCP SDK's own 55.0ms, because without the assembly it no longer loads the SDK at all.
|
|
76
|
+
|
|
77
|
+
A consumer that *does* build a server loads the SDK regardless, so it saves less than that
|
|
78
|
+
difference suggests — measured with the SDK warm, importing both entries, 67.4ms → 38.0ms, about
|
|
79
|
+
29ms per process. The functions did not change; their specifiers did.
|
|
63
80
|
|
|
64
81
|
```ts
|
|
65
82
|
import { initializeSchema, type SchemaMigration } from "run-dmcp";
|
|
66
|
-
import { createMcpServer } from "run-dmcp/rpg";
|
|
83
|
+
import { createMcpServer } from "run-dmcp/rpg/server";
|
|
67
84
|
|
|
68
85
|
const migrations: SchemaMigration[] = [
|
|
69
86
|
{
|
|
@@ -93,6 +110,27 @@ The database lands in the consuming application: `DMCP_DB_PATH` if set, else an
|
|
|
93
110
|
`~/.local/share/dmcp`, else `./data/games.db` relative to the working directory. Never inside the
|
|
94
111
|
installed package.
|
|
95
112
|
|
|
113
|
+
**The spine is importable, not only callable over a transport.** Games, locations, characters,
|
|
114
|
+
factions, relationships, resources, secrets, items, notes, tags, time and timers are library
|
|
115
|
+
functions first and MCP tools second, so a consumer reads and writes its own world directly and only
|
|
116
|
+
serves the tools it actually wants an LLM to reach. That includes the constrained-write choke point:
|
|
117
|
+
a number that moves through `updateResourceValue` is checked against its declared constraints and its
|
|
118
|
+
history is `facts`, queryable with `valueHistory`.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { createGame, createResource, updateResourceValue, valueHistory } from "run-dmcp";
|
|
122
|
+
|
|
123
|
+
const game = createGame({ name: "The Granary", setting: "…", style: "…" });
|
|
124
|
+
const grain = createResource({ gameId: game.id, ownerType: "game", name: "grain", value: 100 });
|
|
125
|
+
|
|
126
|
+
updateResourceValue({ resourceId: grain.id, mode: "delta", value: -30, reason: "the winter ration" });
|
|
127
|
+
valueHistory(grain.id, "value"); // → the transitions, with the reason each one carried
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The annotations, input limits, error envelope and logger the engine's own tools are built from are
|
|
131
|
+
exported too (`ANNOTATIONS`, `LIMITS`, `errors`, `createLogger`), so a consumer's own tools can
|
|
132
|
+
refuse and bound the way these do instead of re-implementing it.
|
|
133
|
+
|
|
96
134
|
## Provenance
|
|
97
135
|
|
|
98
136
|
This continues [DMCP](https://github.com/shawnrushefsky/dmcp) by Shawn Rushefsky (MIT), whose last
|
package/dist/bin/run-dmcp.js
CHANGED
|
@@ -18,7 +18,7 @@ import { startHttpServer } from "../http/server.js";
|
|
|
18
18
|
// (design §8, issue #17). The application always served the full surface,
|
|
19
19
|
// so it reaches for it here rather than the core-only `createCoreMcpServer`
|
|
20
20
|
// in ../mcp-server.js.
|
|
21
|
-
import { createMcpServer } from "../rpg/
|
|
21
|
+
import { createMcpServer } from "../rpg/server.js";
|
|
22
22
|
import { httpPortFromEnv, setHttpPort, webUiEnabled } from "../utils/webui.js";
|
|
23
23
|
import { createLogger } from "../utils/logger.js";
|
|
24
24
|
const log = createLogger("bin");
|
package/dist/db/schema.js
CHANGED
|
@@ -3,6 +3,49 @@ import { createLogger } from "../utils/logger.js";
|
|
|
3
3
|
import { initializeTimelineSchema } from "../timeline/schema.js";
|
|
4
4
|
import { initializeAdjudicationSchema } from "../timeline/adjudication.js";
|
|
5
5
|
const log = createLogger("schema");
|
|
6
|
+
/**
|
|
7
|
+
* Run one whole-table CHECK rebuild with foreign-key enforcement suspended,
|
|
8
|
+
* and verify afterwards that nothing was left dangling.
|
|
9
|
+
*
|
|
10
|
+
* SQLite cannot ALTER a CHECK, so widening one means rebuilding the table:
|
|
11
|
+
* copy the rows aside, DROP, CREATE with the new CHECK, copy back. Two
|
|
12
|
+
* migrations in this file do that, and both need the same two guarantees.
|
|
13
|
+
*
|
|
14
|
+
* ENFORCEMENT MUST BE OFF ACROSS THE DROP. With it on, `DROP TABLE` performs
|
|
15
|
+
* an implicit per-row DELETE first, precisely so that any ON DELETE action
|
|
16
|
+
* declared against that table fires as though each row had genuinely been
|
|
17
|
+
* deleted -- which for both of these tables means cascade-emptying the tables
|
|
18
|
+
* that reference them. Suspending it is what makes the drop a schema
|
|
19
|
+
* operation instead of a silent mass deletion. It is toggled out here rather
|
|
20
|
+
* than inside `withTransaction`, because `PRAGMA foreign_keys` is a
|
|
21
|
+
* documented no-op while a transaction is pending.
|
|
22
|
+
*
|
|
23
|
+
* AND IT MUST GO BACK ON WHEN THE REBUILD THROWS, which is why the restore is
|
|
24
|
+
* in a `finally` and why this is a shared function rather than two copies.
|
|
25
|
+
* Both call sites previously restored it on the success path only.
|
|
26
|
+
* `getDatabase()` caches one connection at module scope, so a rebuild that
|
|
27
|
+
* threw handed the rest of the process a handle with foreign keys still
|
|
28
|
+
* disabled -- and a disabled foreign key does not announce itself. It means
|
|
29
|
+
* every ON DELETE CASCADE in this schema quietly stops working: deleting a
|
|
30
|
+
* game orphans its characters, resources, locations and secrets instead of
|
|
31
|
+
* taking them with it, and nothing errors. The scenario is not exotic -- a
|
|
32
|
+
* staging table left behind by a rebuild that died partway is exactly what
|
|
33
|
+
* makes the next startup's `CREATE TABLE ..._staging` throw. Covered by
|
|
34
|
+
* `src/db/__tests__/foreignKeysRestored.test.ts`.
|
|
35
|
+
*/
|
|
36
|
+
function rebuildWithForeignKeysSuspended(db, label, rebuild) {
|
|
37
|
+
db.pragma("foreign_keys = OFF");
|
|
38
|
+
try {
|
|
39
|
+
withTransaction(rebuild);
|
|
40
|
+
const violations = db.pragma("foreign_key_check");
|
|
41
|
+
if (violations.length > 0) {
|
|
42
|
+
throw new Error(`${label} CHECK migration left dangling foreign keys: ` + JSON.stringify(violations));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
finally {
|
|
46
|
+
db.pragma("foreign_keys = ON");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
6
49
|
export function initializeSchema(options) {
|
|
7
50
|
const db = getDatabase();
|
|
8
51
|
// ============================================================================
|
|
@@ -203,12 +246,22 @@ export function initializeSchema(options) {
|
|
|
203
246
|
)
|
|
204
247
|
`);
|
|
205
248
|
// Resources table (for tracking currency, reputation, counters, etc.)
|
|
206
|
-
|
|
249
|
+
//
|
|
250
|
+
// `RESOURCES_DDL` is a shared constant, not a literal inlined here, for the
|
|
251
|
+
// same reason `RESOURCE_CONSTRAINTS_DDL` further down is one: the CHECK-
|
|
252
|
+
// rebuild migration immediately below needs this exact text executed in
|
|
253
|
+
// TWO places -- here, for a fresh database, and again inside the rebuild,
|
|
254
|
+
// for a database that still carries the OLD two-member CHECK. Sharing one
|
|
255
|
+
// JS string is what makes "a fresh database and a migrated database
|
|
256
|
+
// converge on byte-identical `sqlite_master.sql` for this table" true BY
|
|
257
|
+
// CONSTRUCTION rather than by two hand-written literals happening to agree
|
|
258
|
+
// today.
|
|
259
|
+
const RESOURCES_DDL = `
|
|
207
260
|
CREATE TABLE IF NOT EXISTS resources (
|
|
208
261
|
id TEXT PRIMARY KEY,
|
|
209
262
|
game_id TEXT NOT NULL,
|
|
210
263
|
owner_id TEXT,
|
|
211
|
-
owner_type TEXT NOT NULL CHECK (owner_type IN ('game', 'character')),
|
|
264
|
+
owner_type TEXT NOT NULL CHECK (owner_type IN ('game', 'character', 'faction', 'location')),
|
|
212
265
|
name TEXT NOT NULL,
|
|
213
266
|
description TEXT,
|
|
214
267
|
category TEXT,
|
|
@@ -218,7 +271,115 @@ export function initializeSchema(options) {
|
|
|
218
271
|
created_at TEXT NOT NULL,
|
|
219
272
|
FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE
|
|
220
273
|
)
|
|
221
|
-
|
|
274
|
+
`;
|
|
275
|
+
db.exec(RESOURCES_DDL);
|
|
276
|
+
// Migration: widen `resources.owner_type` to admit 'faction' and
|
|
277
|
+
// 'location' alongside 'game' and 'character'. A resource owned by a
|
|
278
|
+
// faction or a location is generic mechanism -- the engine already has
|
|
279
|
+
// `factions` and `locations` tables; this just lets `resources` point at
|
|
280
|
+
// either the way it already points at a `game` or a `character` -- so it
|
|
281
|
+
// belongs here rather than behind a downstream application's own migration.
|
|
282
|
+
//
|
|
283
|
+
// SQLite cannot ALTER a CHECK constraint, so a database that already has
|
|
284
|
+
// `resources` rows under the OLD two-member CHECK needs the same full-
|
|
285
|
+
// table-rebuild recipe the `resource_constraints` CHECK-widening migration
|
|
286
|
+
// uses further down this function (see that block's comment for the
|
|
287
|
+
// detailed reasoning this one leans on): build a replacement table with
|
|
288
|
+
// the new CHECK, copy every row across, drop the old table, put the
|
|
289
|
+
// replacement in its place.
|
|
290
|
+
//
|
|
291
|
+
// DETECTION IS IDEMPOTENT AND LITERAL, not a guess: read this codebase's
|
|
292
|
+
// OWN generated DDL back out of `sqlite_master` and check whether it
|
|
293
|
+
// already contains the token 'faction' -- the same "a literal check for a
|
|
294
|
+
// token we defined in output we generated is fine" carve-out the
|
|
295
|
+
// `resource_constraints` migration's own comment cites (hard rule 4 in the
|
|
296
|
+
// downstream game's own engineering standards; this engine has no
|
|
297
|
+
// narrative-language rule of its own to point at, but the reasoning is the
|
|
298
|
+
// same: this is a substring check against SQL text THIS FUNCTION generated
|
|
299
|
+
// a few lines above, never against anything a player or a model wrote). A
|
|
300
|
+
// truly fresh database never takes the branch below: `RESOURCES_DDL`'s
|
|
301
|
+
// `CREATE TABLE IF NOT EXISTS` a few lines up already carries the widened
|
|
302
|
+
// CHECK, so by the time this runs, this database's own `resources` already
|
|
303
|
+
// contains 'faction'.
|
|
304
|
+
const resourcesDdl = db
|
|
305
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'resources'`)
|
|
306
|
+
.get();
|
|
307
|
+
if (resourcesDdl && !resourcesDdl.sql.includes("faction")) {
|
|
308
|
+
// DROP THE PROJECTION TRIGGERS FIRST, UNCONDITIONALLY, IF THEY EXIST.
|
|
309
|
+
// `timeline_resources_ai`/`_au`/`_ad` are defined ON `resources` itself
|
|
310
|
+
// (`AFTER INSERT/UPDATE/DELETE ON resources`), so SQLite drops them
|
|
311
|
+
// automatically the moment `DROP TABLE resources` below runs -- but
|
|
312
|
+
// dropping them here too, explicitly, costs nothing and removes any
|
|
313
|
+
// dependence on that implicit behaviour being exactly right. They are
|
|
314
|
+
// unconditionally reinstalled, generated fresh off the rebuilt table's
|
|
315
|
+
// own `pragma_table_info`, by `installProjectionTriggers()`
|
|
316
|
+
// (`src/timeline/projection.ts`), which `initializeTimelineSchema()`
|
|
317
|
+
// calls LAST in this function -- see the comment on that call for why it
|
|
318
|
+
// runs after every migration above it, this one included.
|
|
319
|
+
db.exec(`DROP TRIGGER IF EXISTS timeline_resources_ai`);
|
|
320
|
+
db.exec(`DROP TRIGGER IF EXISTS timeline_resources_au`);
|
|
321
|
+
db.exec(`DROP TRIGGER IF EXISTS timeline_resources_ad`);
|
|
322
|
+
// Enforcement has to be suspended across the drop, and put back
|
|
323
|
+
// afterwards on every path -- see `rebuildWithForeignKeysSuspended`. Here
|
|
324
|
+
// the tables that would be cascade-emptied are `resource_history` and
|
|
325
|
+
// `resource_constraint_members`, both of which declare
|
|
326
|
+
// `FOREIGN KEY (resource_id) REFERENCES resources(id) ON DELETE CASCADE`.
|
|
327
|
+
rebuildWithForeignKeysSuspended(db, "resources.owner_type", () => {
|
|
328
|
+
// 1. Copy the OLD table's rows into a staging table under a temporary
|
|
329
|
+
// name, already carrying the widened CHECK.
|
|
330
|
+
db.exec(`
|
|
331
|
+
CREATE TABLE resources_staging (
|
|
332
|
+
id TEXT PRIMARY KEY,
|
|
333
|
+
game_id TEXT NOT NULL,
|
|
334
|
+
owner_id TEXT,
|
|
335
|
+
owner_type TEXT NOT NULL CHECK (owner_type IN ('game', 'character', 'faction', 'location')),
|
|
336
|
+
name TEXT NOT NULL,
|
|
337
|
+
description TEXT,
|
|
338
|
+
category TEXT,
|
|
339
|
+
value REAL NOT NULL DEFAULT 0,
|
|
340
|
+
min_value REAL,
|
|
341
|
+
max_value REAL,
|
|
342
|
+
created_at TEXT NOT NULL
|
|
343
|
+
)
|
|
344
|
+
`);
|
|
345
|
+
db.exec(`
|
|
346
|
+
INSERT INTO resources_staging (id, game_id, owner_id, owner_type, name, description, category, value, min_value, max_value, created_at)
|
|
347
|
+
SELECT id, game_id, owner_id, owner_type, name, description, category, value, min_value, max_value, created_at FROM resources
|
|
348
|
+
`);
|
|
349
|
+
// 2. Drop the OLD table outright -- not renamed. Renaming it out of
|
|
350
|
+
// the way first would rewrite `resource_history` and
|
|
351
|
+
// `resource_constraint_members`'s own stored FOREIGN KEY clauses to
|
|
352
|
+
// point at the temporary name (RENAME TO rewrites every OTHER
|
|
353
|
+
// table's FK text that references the renamed table, regardless of
|
|
354
|
+
// the `foreign_keys` pragma), leaving them permanently dangling
|
|
355
|
+
// once that temporary table is dropped a few steps later. `DROP
|
|
356
|
+
// TABLE`, unlike `RENAME TO`, does not rewrite other tables'
|
|
357
|
+
// references -- there is nothing to rewrite them TO -- so this
|
|
358
|
+
// recipe never renames the table other tables' foreign keys point
|
|
359
|
+
// at; the FINAL name is produced by a genuine `CREATE TABLE`
|
|
360
|
+
// instead, and `resource_history`/`resource_constraint_members`'s
|
|
361
|
+
// FK text is never touched by anything in this block.
|
|
362
|
+
db.exec(`DROP TABLE resources`);
|
|
363
|
+
// 3. Recreate under the FINAL name using the exact same DDL text the
|
|
364
|
+
// fresh-database path executed above -- `RESOURCES_DDL` itself, not
|
|
365
|
+
// a second hand-copied literal -- so its stored SQL matches the
|
|
366
|
+
// fresh-database path byte for byte.
|
|
367
|
+
db.exec(RESOURCES_DDL);
|
|
368
|
+
// 4. Copy every row back across from the staging table with an
|
|
369
|
+
// EXPLICIT column list -- never SELECT * -- and drop the staging
|
|
370
|
+
// table. Every id was copied verbatim, so `resource_history` and
|
|
371
|
+
// `resource_constraint_members`'s own `FOREIGN KEY (resource_id)
|
|
372
|
+
// REFERENCES resources(id)` -- never touched by any of the steps
|
|
373
|
+
// above -- is satisfied by the replacement table throughout,
|
|
374
|
+
// verified for real by the `PRAGMA foreign_key_check` below, not
|
|
375
|
+
// merely assumed here.
|
|
376
|
+
db.exec(`
|
|
377
|
+
INSERT INTO resources (id, game_id, owner_id, owner_type, name, description, category, value, min_value, max_value, created_at)
|
|
378
|
+
SELECT id, game_id, owner_id, owner_type, name, description, category, value, min_value, max_value, created_at FROM resources_staging
|
|
379
|
+
`);
|
|
380
|
+
db.exec(`DROP TABLE resources_staging`);
|
|
381
|
+
});
|
|
382
|
+
}
|
|
222
383
|
// Resource history table (tracks all changes) -- FROZEN, see the trigger
|
|
223
384
|
// immediately below. Kept for existing rows only; nothing writes here any
|
|
224
385
|
// more.
|
|
@@ -393,15 +554,12 @@ export function initializeSchema(options) {
|
|
|
393
554
|
// means it is unconditionally reinstalled before this function returns
|
|
394
555
|
// regardless -- this is that discipline paying for itself a second time.
|
|
395
556
|
db.exec(`DROP TRIGGER IF EXISTS timeline_facts_resolve_only`);
|
|
396
|
-
//
|
|
397
|
-
//
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
|
|
402
|
-
// for putting it back.
|
|
403
|
-
db.pragma("foreign_keys = OFF");
|
|
404
|
-
withTransaction(() => {
|
|
557
|
+
// Enforcement has to be suspended across the drop, and put back
|
|
558
|
+
// afterwards on every path -- see `rebuildWithForeignKeysSuspended`. Here
|
|
559
|
+
// the table that would be cascade-emptied is `resource_constraint_members`,
|
|
560
|
+
// which declares
|
|
561
|
+
// `FOREIGN KEY (constraint_id) REFERENCES resource_constraints(id) ON DELETE CASCADE`.
|
|
562
|
+
rebuildWithForeignKeysSuspended(db, "resource_constraints (resolve_only, issue #13)", () => {
|
|
405
563
|
// EMPIRICALLY MEASURED, NOT ASSUMED (see resolveOnly.test.ts, whose
|
|
406
564
|
// FK-check assertion caught a real bug in an earlier version of this
|
|
407
565
|
// block): `ALTER TABLE ... RENAME TO` does not just rename the table
|
|
@@ -501,12 +659,6 @@ export function initializeSchema(options) {
|
|
|
501
659
|
// below, not merely assumed here.
|
|
502
660
|
db.exec(`DROP TABLE resource_constraints_staging`);
|
|
503
661
|
});
|
|
504
|
-
const fkViolations = db.pragma("foreign_key_check");
|
|
505
|
-
if (fkViolations.length > 0) {
|
|
506
|
-
throw new Error(`resource_constraints CHECK migration (resolve_only, issue #13) left dangling foreign keys: ` +
|
|
507
|
-
JSON.stringify(fkViolations));
|
|
508
|
-
}
|
|
509
|
-
db.pragma("foreign_keys = ON");
|
|
510
662
|
}
|
|
511
663
|
// Members of a resource constraint. 'bounded' and 'monotonic' constraints
|
|
512
664
|
// have exactly one member (the resource they govern); 'conserved'
|
package/dist/http/server.js
CHANGED
|
@@ -583,7 +583,28 @@ export function createHttpServer(_port = 3456) {
|
|
|
583
583
|
next();
|
|
584
584
|
return;
|
|
585
585
|
}
|
|
586
|
-
|
|
586
|
+
// `dotfiles: "allow"` is load-bearing, and its absence was a real bug.
|
|
587
|
+
//
|
|
588
|
+
// res.sendFile() with no `root` option hands the WHOLE absolute path to
|
|
589
|
+
// `send`, whose `dotfiles` default is "ignore" -- so if ANY segment of
|
|
590
|
+
// the path this package is installed under begins with a dot, `send`
|
|
591
|
+
// refuses the file with NotFoundError and the error handler below turns
|
|
592
|
+
// that into a 500. Every client-side route breaks at once, while `/`
|
|
593
|
+
// keeps working (it is served by express.static above, which only
|
|
594
|
+
// dotfile-checks the REQUEST path, not its own root). That is a
|
|
595
|
+
// spectacularly confusing failure: the app is fine, the file is there,
|
|
596
|
+
// and the deep link 500s because of where the checkout happens to live.
|
|
597
|
+
//
|
|
598
|
+
// Installing under a dot directory is ordinary -- a worktree beneath
|
|
599
|
+
// `.claude/`, a deploy under `~/.local/share`, a CI checkout in a dotted
|
|
600
|
+
// cache path -- so this is not a hypothetical.
|
|
601
|
+
//
|
|
602
|
+
// Safe, because the dotfiles guard is protecting nothing here: this path
|
|
603
|
+
// is a server-controlled constant (CLIENT_DIST plus a literal file name)
|
|
604
|
+
// and no part of it comes from the request. The guard exists to stop a
|
|
605
|
+
// user-supplied path from reaching `.env` or `.git`; there is no
|
|
606
|
+
// user-supplied path in it.
|
|
607
|
+
res.sendFile(join(CLIENT_DIST, "index.html"), { dotfiles: "allow" });
|
|
587
608
|
});
|
|
588
609
|
}
|
|
589
610
|
else {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export { createCoreMcpServer, SERVER_NAME, SERVER_VERSION } from "./mcp-server.js";
|
|
2
1
|
export { getDatabase, closeDatabase, withTransaction, getDatabasePath, getDataDir, resolveDataPathFrom, } from "./db/connection.js";
|
|
3
2
|
export type { DataPathInputs } from "./db/connection.js";
|
|
4
3
|
export { initializeSchema } from "./db/schema.js";
|
|
@@ -32,5 +31,38 @@ export { exportTimeline, importTimeline, exportTimelineToFile, importTimelineFro
|
|
|
32
31
|
export type { TimelineExport, TimelineExportEntity, TimelineExportFact, TimelineExportEvent, TimelineExportClock, TimelineImportResult, } from "./timeline/export.js";
|
|
33
32
|
export { ENTITY_KINDS } from "./timeline/kinds.js";
|
|
34
33
|
export type { EntityKind } from "./timeline/kinds.js";
|
|
35
|
-
export { DEFAULT_HTTP_PORT, httpPortFromEnv, webUiEnabled, setHttpPort, getWebUiBaseUrl, } from "./utils/webui.js";
|
|
34
|
+
export { DEFAULT_HTTP_PORT, httpPortFromEnv, webUiEnabled, setHttpPort, getWebUiBaseUrl, getGameUrl, getCharacterUrl, getLocationUrl, } from "./utils/webui.js";
|
|
35
|
+
export * from "./tools/game.js";
|
|
36
|
+
export * from "./tools/world.js";
|
|
37
|
+
export * from "./tools/character.js";
|
|
38
|
+
export * from "./tools/faction.js";
|
|
39
|
+
export * from "./tools/relationship.js";
|
|
40
|
+
export * from "./tools/resource.js";
|
|
41
|
+
export * from "./tools/constraint.js";
|
|
42
|
+
export * from "./tools/inventory.js";
|
|
43
|
+
export * from "./tools/secrets.js";
|
|
44
|
+
export * from "./tools/narrative.js";
|
|
45
|
+
export * from "./tools/notes.js";
|
|
46
|
+
export * from "./tools/tags.js";
|
|
47
|
+
export * from "./tools/time.js";
|
|
48
|
+
export * from "./tools/timers.js";
|
|
49
|
+
export * from "./tools/rules.js";
|
|
50
|
+
export * from "./tools/pause.js";
|
|
51
|
+
export * from "./tools/display.js";
|
|
52
|
+
export * from "./tools/images.js";
|
|
53
|
+
export * from "./tools/audio.js";
|
|
54
|
+
export * from "./tools/image-prompt.js";
|
|
55
|
+
export { gameEvents } from "./events/emitter.js";
|
|
56
|
+
export type { GameEvent } from "./events/emitter.js";
|
|
57
|
+
export { ANNOTATIONS, withAnnotations } from "./utils/tool-annotations.js";
|
|
58
|
+
export { LIMITS, validatedSchemas, boundedString, boundedArray } from "./utils/validation.js";
|
|
59
|
+
export { createError, formatErrorResponse, errors } from "./utils/errors.js";
|
|
60
|
+
export type { AgentError } from "./utils/errors.js";
|
|
61
|
+
export { createLogger } from "./utils/logger.js";
|
|
62
|
+
export type { Logger } from "./utils/logger.js";
|
|
63
|
+
export { verbositySchema, applyVerbosity, filterFields } from "./utils/verbosity.js";
|
|
64
|
+
export type { VerbosityLevel } from "./utils/verbosity.js";
|
|
65
|
+
export { safeJsonParse, safeJsonParseOrNull } from "./utils/json.js";
|
|
66
|
+
export { successResponseSchema, textResultSchema, deletedResponseSchema, listResponseSchema, characterOutputSchema, characterStatusSchema, conditionModifyOutputSchema, tagModifyOutputSchema, } from "./utils/output-schemas.js";
|
|
67
|
+
export { imageGenSchema, voiceSchema } from "./schemas/index.js";
|
|
36
68
|
export type * from "./types/index.js";
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,13 @@
|
|
|
26
26
|
// depending up into it. `src/__tests__/layerBoundary.test.ts` walks the
|
|
27
27
|
// static import graph from this file and fails if anything under `src/rpg/`
|
|
28
28
|
// is reachable from it.
|
|
29
|
-
|
|
29
|
+
// The assembled core server is NOT here. It moved to "run-dmcp/server"
|
|
30
|
+
// (src/server.ts) so that importing mechanism stops loading it: building a
|
|
31
|
+
// server costs the MCP SDK and twenty-one register modules, and a consumer
|
|
32
|
+
// reaching for `LIMITS` or `createGame` was paying for both -- 97.4ms per
|
|
33
|
+
// process against 46.8ms without it, cold, at 0.3.0. Enforced by
|
|
34
|
+
// src/__tests__/assemblyBoundary.test.ts, which walks this file's runtime
|
|
35
|
+
// import graph and fails if the assembly is reachable from it again.
|
|
30
36
|
// The database, and where it lives. The path resolves against the consuming
|
|
31
37
|
// application (DMCP_DB_PATH, else an existing XDG data directory, else the
|
|
32
38
|
// working directory) and never against this package's install location.
|
|
@@ -181,4 +187,82 @@ export { ENTITY_KINDS } from "./timeline/kinds.js";
|
|
|
181
187
|
// ability and combat tools and is exported from "run-dmcp/rpg" instead
|
|
182
188
|
// (src/rpg/index.ts), even though the file that implements it stays at
|
|
183
189
|
// src/http/server.ts on disk.
|
|
184
|
-
export { DEFAULT_HTTP_PORT, httpPortFromEnv, webUiEnabled, setHttpPort, getWebUiBaseUrl, } from "./utils/webui.js";
|
|
190
|
+
export { DEFAULT_HTTP_PORT, httpPortFromEnv, webUiEnabled, setHttpPort, getWebUiBaseUrl, getGameUrl, getCharacterUrl, getLocationUrl, } from "./utils/webui.js";
|
|
191
|
+
// ===========================================================================
|
|
192
|
+
// The core's own tool modules, as library functions (design §8, §11 Phase 5).
|
|
193
|
+
//
|
|
194
|
+
// §8's layer table puts factions, relationships-with-history, secrets,
|
|
195
|
+
// resources, locations and items in the CORE, and gives the reason: they are
|
|
196
|
+
// "the client's spine. If these go up into the RPG layer, the client cannot
|
|
197
|
+
// consume the package without dragging the RPG layer with it -- which defeats
|
|
198
|
+
// the split." That reason is only satisfied if a consumer can actually IMPORT
|
|
199
|
+
// them. Until this block, it could not: everything above is the timeline and
|
|
200
|
+
// the database, and the only door to the spine was `createCoreMcpServer` --
|
|
201
|
+
// the whole assembled server, which a consumer would then have to make tool
|
|
202
|
+
// calls into, over a transport, to read its own tables.
|
|
203
|
+
//
|
|
204
|
+
// The layer ABOVE this one already got it right. src/rpg/index.ts ends with
|
|
205
|
+
// six export-stars over its own tool modules, under a comment saying it is
|
|
206
|
+
// using "the same shape core's index.ts uses for the timeline... a consumer
|
|
207
|
+
// that wants to call combat/quest/table/status/ability/dice logic directly,
|
|
208
|
+
// without going through an MCP tool call, can." The core never did
|
|
209
|
+
//
|
|
210
|
+
// (Those six are named here in prose rather than quoted as import lines on
|
|
211
|
+
// purpose: layerBoundary.test.ts walks this file's import graph with a
|
|
212
|
+
// deliberately syntactic scan for `from "<specifier>"`, and it cannot tell a
|
|
213
|
+
// quoted example in a comment from a real edge. That is the guard being
|
|
214
|
+
// conservative rather than clever, which is the right trade -- it fails loud
|
|
215
|
+
// and names the chain. Do not teach it to strip comments; reword instead.)
|
|
216
|
+
// the same for its own tools, which left the OPTIONAL layer more consumable
|
|
217
|
+
// than the thing it is optional on top of. That is an oversight and not a
|
|
218
|
+
// decision: §6's rule is "library functions first, MCP tools second", and the
|
|
219
|
+
// narration-constraint and timeline-export blocks above both invoke it.
|
|
220
|
+
//
|
|
221
|
+
// This changes no behaviour and adds no dependency. Every module below is
|
|
222
|
+
// ALREADY in this file's static import graph, reached through
|
|
223
|
+
// ./mcp-server.js -> ./register/* -> ./tools/*, so src/__tests__/
|
|
224
|
+
// layerBoundary.test.ts walks exactly the same file set before and after --
|
|
225
|
+
// nothing under src/rpg/ becomes reachable, and none of the six tool modules
|
|
226
|
+
// that moved up there is named here.
|
|
227
|
+
export * from "./tools/game.js";
|
|
228
|
+
export * from "./tools/world.js";
|
|
229
|
+
export * from "./tools/character.js";
|
|
230
|
+
export * from "./tools/faction.js";
|
|
231
|
+
export * from "./tools/relationship.js";
|
|
232
|
+
export * from "./tools/resource.js";
|
|
233
|
+
export * from "./tools/constraint.js";
|
|
234
|
+
export * from "./tools/inventory.js";
|
|
235
|
+
export * from "./tools/secrets.js";
|
|
236
|
+
export * from "./tools/narrative.js";
|
|
237
|
+
export * from "./tools/notes.js";
|
|
238
|
+
export * from "./tools/tags.js";
|
|
239
|
+
export * from "./tools/time.js";
|
|
240
|
+
export * from "./tools/timers.js";
|
|
241
|
+
export * from "./tools/rules.js";
|
|
242
|
+
export * from "./tools/pause.js";
|
|
243
|
+
export * from "./tools/display.js";
|
|
244
|
+
export * from "./tools/images.js";
|
|
245
|
+
export * from "./tools/audio.js";
|
|
246
|
+
export * from "./tools/image-prompt.js";
|
|
247
|
+
// The event emitter the tool modules above write to. A consumer's own write
|
|
248
|
+
// paths emit through the same singleton, so its tables and the engine's reach
|
|
249
|
+
// one SSE subscriber rather than two competing ones.
|
|
250
|
+
export { gameEvents } from "./events/emitter.js";
|
|
251
|
+
// ===========================================================================
|
|
252
|
+
// What a consumer needs to register tools OF ITS OWN onto the core server.
|
|
253
|
+
//
|
|
254
|
+
// A client keeps its own MCP surface -- that is the whole point of the split;
|
|
255
|
+
// its mechanics are its own and the engine never learns their names. But a
|
|
256
|
+
// tool it registers should refuse, bound and annotate the way the engine's do,
|
|
257
|
+
// and today a client re-implements these or copies them and lets the copy
|
|
258
|
+
// drift. Named one by one rather than star-exported: these modules contain
|
|
259
|
+
// identifiers like `CREATE` and `UPDATE` that have no business in a package's
|
|
260
|
+
// root namespace.
|
|
261
|
+
export { ANNOTATIONS, withAnnotations } from "./utils/tool-annotations.js";
|
|
262
|
+
export { LIMITS, validatedSchemas, boundedString, boundedArray } from "./utils/validation.js";
|
|
263
|
+
export { createError, formatErrorResponse, errors } from "./utils/errors.js";
|
|
264
|
+
export { createLogger } from "./utils/logger.js";
|
|
265
|
+
export { verbositySchema, applyVerbosity, filterFields } from "./utils/verbosity.js";
|
|
266
|
+
export { safeJsonParse, safeJsonParseOrNull } from "./utils/json.js";
|
|
267
|
+
export { successResponseSchema, textResultSchema, deletedResponseSchema, listResponseSchema, characterOutputSchema, characterStatusSchema, conditionModifyOutputSchema, tagModifyOutputSchema, } from "./utils/output-schemas.js";
|
|
268
|
+
export { imageGenSchema, voiceSchema } from "./schemas/index.js";
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { type Mechanic } from "./timeline/resolve.js";
|
|
3
3
|
import { type RenderVocabulary } from "./timeline/render.js";
|
|
4
4
|
export declare const SERVER_NAME = "dmcp";
|
|
5
|
-
export declare const SERVER_VERSION = "0.
|
|
5
|
+
export declare const SERVER_VERSION = "0.3.0";
|
|
6
6
|
/**
|
|
7
7
|
* Build an MCP server with every CORE tool, resource and prompt this engine
|
|
8
8
|
* serves -- entities, facts, events, the timeline, and the entity/property
|
package/dist/mcp-server.js
CHANGED
|
@@ -41,7 +41,7 @@ import { registerRenderTools } from "./register/render.js";
|
|
|
41
41
|
import { createResolver } from "./timeline/resolve.js";
|
|
42
42
|
import { createStateRenderer } from "./timeline/render.js";
|
|
43
43
|
export const SERVER_NAME = "dmcp";
|
|
44
|
-
export const SERVER_VERSION = "0.
|
|
44
|
+
export const SERVER_VERSION = "0.3.0";
|
|
45
45
|
/**
|
|
46
46
|
* Build an MCP server with every CORE tool, resource and prompt this engine
|
|
47
47
|
* serves -- entities, facts, events, the timeline, and the entity/property
|
|
@@ -9,7 +9,7 @@ export function registerResourceTools(server) {
|
|
|
9
9
|
description: "Create a new resource (currency, reputation, counter, etc.)",
|
|
10
10
|
inputSchema: {
|
|
11
11
|
gameId: z.string().max(100).describe("The game ID"),
|
|
12
|
-
ownerType: z.enum(["game", "character"]).describe("Owner type: 'game' for party/global resources, 'character' for personal resources"),
|
|
12
|
+
ownerType: z.enum(["game", "character", "faction", "location"]).describe("Owner type: 'game' for party/global resources, 'character' for personal resources, 'faction' or 'location' for resources owned by one of those entities"),
|
|
13
13
|
ownerId: z.string().max(100).optional().describe("Character ID if ownerType is 'character' (omit for game-level resources)"),
|
|
14
14
|
name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Resource name (e.g., 'Gold', 'Sanity', 'Thieves Guild Reputation')"),
|
|
15
15
|
description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("Resource description"),
|
|
@@ -105,7 +105,7 @@ export function registerResourceTools(server) {
|
|
|
105
105
|
description: "List resources in a game",
|
|
106
106
|
inputSchema: {
|
|
107
107
|
gameId: z.string().max(100).describe("The game ID"),
|
|
108
|
-
ownerType: z.enum(["game", "character"]).optional().describe("Filter by owner type"),
|
|
108
|
+
ownerType: z.enum(["game", "character", "faction", "location"]).optional().describe("Filter by owner type"),
|
|
109
109
|
ownerId: z.string().max(100).optional().describe("Filter by owner ID (for character resources)"),
|
|
110
110
|
category: z.string().max(100).optional().describe("Filter by category"),
|
|
111
111
|
},
|
package/dist/rpg/index.d.ts
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
import type { Mechanic } from "../timeline/resolve.js";
|
|
3
|
-
import type { RenderVocabulary } from "../timeline/render.js";
|
|
4
2
|
/**
|
|
5
3
|
* Register every RPG-layer tool, resource and prompt onto an existing
|
|
6
4
|
* server. Combat brings dice with it (registerCombatTools registers both,
|
|
7
5
|
* matching how the pre-split assembly registered them as one domain).
|
|
8
6
|
*/
|
|
9
7
|
export declare function registerRpgTools(server: McpServer): void;
|
|
10
|
-
/**
|
|
11
|
-
* Build the FULL assembly: every core tool plus every RPG tool, resource and
|
|
12
|
-
* prompt this engine has always served. Same name, same `{ mechanics?,
|
|
13
|
-
* vocabulary? }` options as the pre-split `createMcpServer` in
|
|
14
|
-
* src/mcp-server.ts -- this is that function, now composed from two layers
|
|
15
|
-
* instead of one, so src/bin/run-dmcp.ts's zero-argument call site keeps
|
|
16
|
-
* working unchanged and the registered tool/resource/prompt set is
|
|
17
|
-
* unchanged too (src/__tests__/layerBoundary.test.ts asserts this exactly).
|
|
18
|
-
*/
|
|
19
|
-
export declare function createMcpServer(options?: {
|
|
20
|
-
mechanics?: readonly Mechanic[];
|
|
21
|
-
vocabulary?: RenderVocabulary;
|
|
22
|
-
}): McpServer;
|
|
23
|
-
export { createHttpServer, startHttpServer } from "../http/server.js";
|
|
24
8
|
export * from "./tools/dice.js";
|
|
25
9
|
export * from "./tools/combat.js";
|
|
26
10
|
export * from "./tools/ability.js";
|
package/dist/rpg/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createCoreMcpServer } from "../mcp-server.js";
|
|
2
1
|
import { registerCombatTools } from "./register/combat.js";
|
|
3
2
|
import { registerAbilityTools } from "./register/abilities.js";
|
|
4
3
|
import { registerStatusTools } from "./register/status.js";
|
|
@@ -22,27 +21,10 @@ export function registerRpgTools(server) {
|
|
|
22
21
|
registerRpgMcpResources(server); // game-quests, quest
|
|
23
22
|
registerMcpPrompts(server); // Reusable prompt templates (game-master session library)
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
* src/mcp-server.ts -- this is that function, now composed from two layers
|
|
30
|
-
* instead of one, so src/bin/run-dmcp.ts's zero-argument call site keeps
|
|
31
|
-
* working unchanged and the registered tool/resource/prompt set is
|
|
32
|
-
* unchanged too (src/__tests__/layerBoundary.test.ts asserts this exactly).
|
|
33
|
-
*/
|
|
34
|
-
export function createMcpServer(options) {
|
|
35
|
-
const server = createCoreMcpServer(options);
|
|
36
|
-
registerRpgTools(server);
|
|
37
|
-
return server;
|
|
38
|
-
}
|
|
39
|
-
// The web UI. An application opts into serving it; importing this never
|
|
40
|
-
// does. It lives on disk at src/http/server.ts, not under src/rpg/ -- it
|
|
41
|
-
// imports RPG tools (quest/ability/combat), which is why it cannot be
|
|
42
|
-
// reachable from the core entry point, but its CLIENT_DIST path resolution
|
|
43
|
-
// is depth-sensitive in both src/ and compiled dist/, so it stays put and
|
|
44
|
-
// only its EXPORT moves up to this layer.
|
|
45
|
-
export { createHttpServer, startHttpServer } from "../http/server.js";
|
|
24
|
+
// `createMcpServer` and the web UI are NOT here. Both are assembly, and both
|
|
25
|
+
// moved to "run-dmcp/rpg/server" (src/rpg/server.ts) so that importing the
|
|
26
|
+
// layer's tool functions stops loading a server and an HTTP framework with
|
|
27
|
+
// them.
|
|
46
28
|
// The RPG tool modules, exported as library functions -- the same shape
|
|
47
29
|
// core's index.ts uses for the timeline (replay, changesWithin, and so on):
|
|
48
30
|
// a consumer that wants to call combat/quest/table/status/ability/dice logic
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { Mechanic } from "../timeline/resolve.js";
|
|
3
|
+
import type { RenderVocabulary } from "../timeline/render.js";
|
|
4
|
+
/**
|
|
5
|
+
* Build the FULL assembly: every core tool plus every RPG tool, resource and
|
|
6
|
+
* prompt this engine has always served. Same name and same `{ mechanics?,
|
|
7
|
+
* vocabulary? }` options as the pre-split `createMcpServer` in
|
|
8
|
+
* src/mcp-server.ts, so src/bin/run-dmcp.ts's zero-argument call site keeps
|
|
9
|
+
* working unchanged and the registered tool/resource/prompt set is unchanged
|
|
10
|
+
* too (src/__tests__/layerBoundary.test.ts asserts this exactly).
|
|
11
|
+
*/
|
|
12
|
+
export declare function createMcpServer(options?: {
|
|
13
|
+
mechanics?: readonly Mechanic[];
|
|
14
|
+
vocabulary?: RenderVocabulary;
|
|
15
|
+
}): McpServer;
|
|
16
|
+
export { createHttpServer, startHttpServer } from "../http/server.js";
|