run-dmcp 0.1.0 → 0.2.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.
Files changed (135) hide show
  1. package/README.md +76 -10
  2. package/dist/bin/run-dmcp.d.ts +2 -0
  3. package/dist/bin/run-dmcp.js +55 -0
  4. package/dist/db/connection.d.ts +32 -0
  5. package/dist/db/connection.js +38 -16
  6. package/dist/db/schema.d.ts +29 -1
  7. package/dist/db/schema.js +439 -7
  8. package/dist/http/server.js +3 -3
  9. package/dist/index.d.ts +36 -2
  10. package/dist/index.js +184 -92
  11. package/dist/mcp-server.d.ts +49 -0
  12. package/dist/mcp-server.js +127 -0
  13. package/dist/reader/turnReader.d.ts +185 -0
  14. package/dist/reader/turnReader.js +288 -0
  15. package/dist/register/batch.js +5 -79
  16. package/dist/register/mcp-resources.d.ts +9 -0
  17. package/dist/register/mcp-resources.js +16 -62
  18. package/dist/register/render.d.ts +17 -0
  19. package/dist/register/render.js +50 -0
  20. package/dist/register/resolve.d.ts +14 -0
  21. package/dist/register/resolve.js +102 -0
  22. package/dist/register/resources.js +11 -4
  23. package/dist/register/timeline.d.ts +2 -0
  24. package/dist/register/timeline.js +311 -0
  25. package/dist/rpg/index.d.ts +29 -0
  26. package/dist/rpg/index.js +55 -0
  27. package/dist/rpg/register/abilities.d.ts +2 -0
  28. package/dist/rpg/register/abilities.js +165 -0
  29. package/dist/rpg/register/batch.d.ts +2 -0
  30. package/dist/rpg/register/batch.js +92 -0
  31. package/dist/rpg/register/combat.d.ts +2 -0
  32. package/dist/rpg/register/combat.js +207 -0
  33. package/dist/rpg/register/mcp-prompts.d.ts +2 -0
  34. package/dist/rpg/register/mcp-prompts.js +684 -0
  35. package/dist/rpg/register/mcp-resources.d.ts +2 -0
  36. package/dist/rpg/register/mcp-resources.js +61 -0
  37. package/dist/rpg/register/quests.d.ts +2 -0
  38. package/dist/rpg/register/quests.js +118 -0
  39. package/dist/rpg/register/status.d.ts +2 -0
  40. package/dist/rpg/register/status.js +130 -0
  41. package/dist/rpg/register/tables.d.ts +2 -0
  42. package/dist/rpg/register/tables.js +146 -0
  43. package/dist/rpg/tools/ability.d.ts +48 -0
  44. package/dist/rpg/tools/ability.js +238 -0
  45. package/dist/rpg/tools/combat.d.ts +13 -0
  46. package/dist/rpg/tools/combat.js +195 -0
  47. package/dist/rpg/tools/dice.d.ts +23 -0
  48. package/dist/rpg/tools/dice.js +111 -0
  49. package/dist/rpg/tools/quest.d.ts +34 -0
  50. package/dist/rpg/tools/quest.js +164 -0
  51. package/dist/rpg/tools/status.d.ts +36 -0
  52. package/dist/rpg/tools/status.js +218 -0
  53. package/dist/rpg/tools/tables.d.ts +33 -0
  54. package/dist/rpg/tools/tables.js +209 -0
  55. package/dist/schemas/index.d.ts +12 -12
  56. package/dist/timeline/adjudication.d.ts +150 -0
  57. package/dist/timeline/adjudication.js +174 -0
  58. package/dist/timeline/changes.d.ts +100 -0
  59. package/dist/timeline/changes.js +161 -0
  60. package/dist/timeline/checkpoint.d.ts +69 -0
  61. package/dist/timeline/checkpoint.js +131 -0
  62. package/dist/timeline/clock.d.ts +89 -0
  63. package/dist/timeline/clock.js +173 -0
  64. package/dist/timeline/constrained.d.ts +220 -0
  65. package/dist/timeline/constrained.js +671 -0
  66. package/dist/timeline/export.d.ts +171 -0
  67. package/dist/timeline/export.js +329 -0
  68. package/dist/timeline/irreversible.d.ts +85 -0
  69. package/dist/timeline/irreversible.js +108 -0
  70. package/dist/timeline/kinds.d.ts +14 -0
  71. package/dist/timeline/kinds.js +22 -0
  72. package/dist/timeline/narration.d.ts +175 -0
  73. package/dist/timeline/narration.js +259 -0
  74. package/dist/timeline/projection.d.ts +97 -0
  75. package/dist/timeline/projection.js +330 -0
  76. package/dist/timeline/provenance.d.ts +66 -0
  77. package/dist/timeline/provenance.js +45 -0
  78. package/dist/timeline/registry.d.ts +95 -0
  79. package/dist/timeline/registry.js +124 -0
  80. package/dist/timeline/render.d.ts +121 -0
  81. package/dist/timeline/render.js +187 -0
  82. package/dist/timeline/replay.d.ts +64 -0
  83. package/dist/timeline/replay.js +104 -0
  84. package/dist/timeline/resolve.d.ts +262 -0
  85. package/dist/timeline/resolve.js +226 -0
  86. package/dist/timeline/schema.d.ts +13 -0
  87. package/dist/timeline/schema.js +262 -0
  88. package/dist/timeline/t.d.ts +80 -0
  89. package/dist/timeline/t.js +37 -0
  90. package/dist/tools/constraint.d.ts +44 -80
  91. package/dist/tools/constraint.js +115 -124
  92. package/dist/tools/relationship.d.ts +83 -2
  93. package/dist/tools/relationship.js +139 -62
  94. package/dist/tools/resource.d.ts +31 -6
  95. package/dist/tools/resource.js +106 -153
  96. package/dist/types/index.d.ts +19 -1
  97. package/dist/utils/output-schemas.d.ts +593 -2
  98. package/dist/utils/output-schemas.js +3 -0
  99. package/dist/utils/webui.d.ts +32 -0
  100. package/dist/utils/webui.js +54 -1
  101. package/package.json +20 -4
  102. package/dist/__tests__/engineVocabulary.test.d.ts +0 -1
  103. package/dist/__tests__/engineVocabulary.test.js +0 -147
  104. package/dist/db/__tests__/connection.test.d.ts +0 -1
  105. package/dist/db/__tests__/connection.test.js +0 -72
  106. package/dist/db/__tests__/testDb.d.ts +0 -33
  107. package/dist/db/__tests__/testDb.js +0 -41
  108. package/dist/test-setup.d.ts +0 -1
  109. package/dist/test-setup.js +0 -13
  110. package/dist/tools/__tests__/audio.test.d.ts +0 -1
  111. package/dist/tools/__tests__/audio.test.js +0 -59
  112. package/dist/tools/__tests__/conserved.test.d.ts +0 -1
  113. package/dist/tools/__tests__/conserved.test.js +0 -488
  114. package/dist/tools/__tests__/constraint.test.d.ts +0 -1
  115. package/dist/tools/__tests__/constraint.test.js +0 -212
  116. package/dist/tools/__tests__/expiry-consequences.test.d.ts +0 -1
  117. package/dist/tools/__tests__/expiry-consequences.test.js +0 -110
  118. package/dist/tools/__tests__/images.test.d.ts +0 -1
  119. package/dist/tools/__tests__/images.test.js +0 -59
  120. package/dist/tools/__tests__/relationship.test.d.ts +0 -1
  121. package/dist/tools/__tests__/relationship.test.js +0 -132
  122. package/dist/tools/__tests__/resource-constraints.test.d.ts +0 -1
  123. package/dist/tools/__tests__/resource-constraints.test.js +0 -131
  124. package/dist/tools/__tests__/resource.test.d.ts +0 -1
  125. package/dist/tools/__tests__/resource.test.js +0 -190
  126. package/dist/tools/__tests__/time.test.d.ts +0 -1
  127. package/dist/tools/__tests__/time.test.js +0 -404
  128. package/dist/tools/__tests__/timers.test.d.ts +0 -1
  129. package/dist/tools/__tests__/timers.test.js +0 -426
  130. package/dist/tools/__tests__/world.test.d.ts +0 -1
  131. package/dist/tools/__tests__/world.test.js +0 -70
  132. package/dist/utils/__tests__/json.test.d.ts +0 -1
  133. package/dist/utils/__tests__/json.test.js +0 -55
  134. package/dist/utils/__tests__/validation.test.d.ts +0 -1
  135. package/dist/utils/__tests__/validation.test.js +0 -90
package/dist/index.js CHANGED
@@ -1,92 +1,184 @@
1
- #!/usr/bin/env node
2
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
- import { initializeSchema } from "./db/schema.js";
5
- import { closeDatabase } from "./db/connection.js";
6
- import { startHttpServer } from "./http/server.js";
7
- import { setHttpPort } from "./utils/webui.js";
8
- // Import registration functions
9
- import { registerCoreTools } from "./register/core.js";
10
- import { registerWorldTools } from "./register/world.js";
11
- import { registerCharacterTools } from "./register/character.js";
12
- import { registerCombatTools } from "./register/combat.js";
13
- import { registerInventoryTools } from "./register/inventory.js";
14
- import { registerQuestTools } from "./register/quests.js";
15
- import { registerNarrativeTools } from "./register/narrative.js";
16
- import { registerResourceTools } from "./register/resources.js";
17
- import { registerTimeTools } from "./register/time.js";
18
- import { registerTableTools } from "./register/tables.js";
19
- import { registerSecretTools } from "./register/secrets.js";
20
- import { registerRelationshipTools } from "./register/relationships.js";
21
- import { registerTagTools } from "./register/tags.js";
22
- import { registerStatusTools } from "./register/status.js";
23
- import { registerFactionTools } from "./register/factions.js";
24
- import { registerAbilityTools } from "./register/abilities.js";
25
- import { registerNoteTools } from "./register/notes.js";
26
- import { registerPauseTools } from "./register/pause.js";
27
- import { registerImageTools } from "./register/images.js";
28
- import { registerAudioTools } from "./register/audio.js";
29
- import { registerDisplayTools } from "./register/display.js";
30
- import { registerBatchTools } from "./register/batch.js";
31
- import { registerMcpResources } from "./register/mcp-resources.js";
32
- import { registerMcpPrompts } from "./register/mcp-prompts.js";
33
- // Initialize database
34
- initializeSchema();
35
- // Create MCP server
36
- const server = new McpServer({
37
- name: "dmcp",
38
- version: "0.1.0",
39
- });
40
- // Register all tools by domain
41
- registerCoreTools(server); // Game, Interview, Rules
42
- registerWorldTools(server); // Locations, Connections, Map
43
- registerCharacterTools(server); // Characters (PC/NPC)
44
- registerCombatTools(server); // Combat, Dice, Checks
45
- registerInventoryTools(server); // Items
46
- registerQuestTools(server); // Quests, Objectives
47
- registerNarrativeTools(server); // Events, History, Export, Player Choices
48
- registerResourceTools(server); // Custom Resources
49
- registerTimeTools(server); // Calendar, Time, Timers
50
- registerTableTools(server); // Random Tables
51
- registerSecretTools(server); // Secrets, Knowledge
52
- registerRelationshipTools(server); // Relationships
53
- registerTagTools(server); // Tags
54
- registerStatusTools(server); // Status Effects
55
- registerFactionTools(server); // Factions
56
- registerAbilityTools(server); // Abilities/Powers
57
- registerNoteTools(server); // Game Notes
58
- registerPauseTools(server); // Pause/Resume, Context Snapshots, External Updates
59
- registerImageTools(server); // Stored Images
60
- registerAudioTools(server); // Stored Audio (TTS, Voice References)
61
- registerDisplayTools(server); // Display/Theme Configuration
62
- registerBatchTools(server); // Batch Operations (multi-entity, workflows)
63
- // Register MCP Resources and Prompts
64
- registerMcpResources(server); // Read-only data access via URI
65
- registerMcpPrompts(server); // Reusable prompt templates
66
- // ============================================================================
67
- // START SERVER
68
- // ============================================================================
69
- // HTTP server port (configurable via environment variable)
70
- const HTTP_PORT = parseInt(process.env.DMCP_HTTP_PORT || "3456", 10);
71
- async function main() {
72
- // Start HTTP server for web UI (runs alongside MCP)
73
- const actualPort = await startHttpServer(HTTP_PORT);
74
- setHttpPort(actualPort);
75
- // Start MCP server with stdio transport
76
- const transport = new StdioServerTransport();
77
- await server.connect(transport);
78
- }
79
- // Handle cleanup
80
- process.on("SIGINT", () => {
81
- closeDatabase();
82
- process.exit(0);
83
- });
84
- process.on("SIGTERM", () => {
85
- closeDatabase();
86
- process.exit(0);
87
- });
88
- main().catch((error) => {
89
- console.error("Server error:", error);
90
- closeDatabase();
91
- process.exit(1);
92
- });
1
+ // The library entry point: exports, and nothing else.
2
+ //
3
+ // Importing this module must do nothing to the machine -- no database file, no
4
+ // listener, no transport, no timer, no work of any kind. 0.1.0 shipped one
5
+ // entry that was both library and application, so importing the package opened
6
+ // a database inside node_modules and bound the web UI's port, and the listener
7
+ // outlived the import by hours.
8
+ //
9
+ // The rule is asserted from the outside in src/__tests__/entrypoints.test.ts:
10
+ // a child process imports this file and must be able to EXIT. Anything left
11
+ // open keeps it alive and fails the test, so the guarantee does not depend on
12
+ // anyone remembering to enumerate what must not start.
13
+ //
14
+ // Starting things is src/bin/run-dmcp.ts, which is what `bin` points at.
15
+ //
16
+ // THIS PACKAGE ROOT IS THE CORE (design §8, issue #17): entities, facts,
17
+ // events, the timeline, and the entity/property domains a consumer cannot
18
+ // lose without losing its own spine -- resources, relationships, factions,
19
+ // secrets, locations, items. Dice, combat, abilities, status effects, random
20
+ // tables and quests are genuinely game-shaped, and they are an OPTIONAL
21
+ // layer above this, not part of it: import "run-dmcp/rpg" for
22
+ // `createMcpServer`, the full assembly this package used to export under
23
+ // that name. That is design §8's line, not an inversion of it -- the core
24
+ // stays importable, buildable and useful with nothing above it, and the
25
+ // layer stays free to depend down into the core without the core ever
26
+ // depending up into it. `src/__tests__/layerBoundary.test.ts` walks the
27
+ // static import graph from this file and fails if anything under `src/rpg/`
28
+ // is reachable from it.
29
+ export { createCoreMcpServer, SERVER_NAME, SERVER_VERSION } from "./mcp-server.js";
30
+ // The database, and where it lives. The path resolves against the consuming
31
+ // application (DMCP_DB_PATH, else an existing XDG data directory, else the
32
+ // working directory) and never against this package's install location.
33
+ export { getDatabase, closeDatabase, withTransaction, getDatabasePath, getDataDir, resolveDataPathFrom, } from "./db/connection.js";
34
+ // The schema, and the hook a consuming application uses to bring up its own
35
+ // tables in the same startup pass, in the same database, under the same rules.
36
+ export { initializeSchema } from "./db/schema.js";
37
+ // The timeline (design §5.1) -- the reason this engine exists, and therefore
38
+ // something a consuming application reaches directly rather than only through
39
+ // a tool call. `replay` answers what the world looked like at any `t`;
40
+ // `declareTimeAxis` is where an application says what its `t` actually is,
41
+ // which it must do before its first write for that game if it wants its own
42
+ // origin (see clock.ts).
43
+ //
44
+ // `timelineDivergences` is exported for the same reason design §13 makes it a
45
+ // stop condition rather than a nicety: an application that keeps its world
46
+ // here is entitled to ask, of its own database, whether the log it is trusting
47
+ // still reproduces its live tables. It returns rows and never a verdict.
48
+ export { replay } from "./timeline/replay.js";
49
+ export { declareTimeAxis, setStoryTime, currentStoryTime } from "./timeline/clock.js";
50
+ // `changes_within(t0, t1)` (design §5.5) -- the range companion to `replay`,
51
+ // for a consumer whose units have duration rather than instants. It returns
52
+ // transitions and never a verdict: one caller reads a change inside a window
53
+ // as a defect, another reads the same rows to build a summary of what has
54
+ // happened since it last looked. Same primitive, opposite readings, which is
55
+ // how you can tell it belongs in the core rather than to whoever asked first.
56
+ export { changesWithin } from "./timeline/changes.js";
57
+ export { compareT, assertT } from "./timeline/t.js";
58
+ export { timelineDivergences } from "./timeline/checkpoint.js";
59
+ // `irreversible` (design §5.3) -- the temporal member of the constraint family.
60
+ // Exported because the enforcement is structural (triggers on `facts`), which
61
+ // means an application never calls a checker: it declares, and later
62
+ // contradictions are refused at the write that attempts them. What it does need
63
+ // from here is the declaration itself, and the ability to ask which facts carry
64
+ // it -- with the one hop of provenance (§5.2c) that makes a refusal reviewable
65
+ // rather than merely obeyed.
66
+ export { declareIrreversible, irreversibleFactFor, listIrreversibleFacts, } from "./timeline/irreversible.js";
67
+ export { openingEventId } from "./timeline/provenance.js";
68
+ // The narration constraint (design §5.2b/§5.2c, GitHub issues #11 and #12)
69
+ // -- the outbound half of authority, and the half with two consumers.
70
+ // "Here is what is true; depict it, do not argue with it." Exported as a
71
+ // library function FIRST, before any MCP tool wraps it (the one tool that
72
+ // does, `narration_constraint_at`, is a thin JSON-over-stdio wrapper around
73
+ // this same call), because the consumer that most needs it is a process
74
+ // that must never call a model at runtime: its units have duration and
75
+ // everything in them is already known in advance, so its narrator output
76
+ // is generated once, reviewed by a human, committed as a file, and
77
+ // rendered hours later by a lint over that finished artifact, with no
78
+ // engine and no model in the loop (§6's "library functions first, MCP
79
+ // tools second," restated here because this is the export where it matters
80
+ // most). `contradictions` is exported alongside it for the same reason and
81
+ // takes no database handle at all -- it is a pure function over the plain
82
+ // object `narrationConstraintAt` returns, so a caller can serialize a
83
+ // constraint once, hand the JSON to an entirely separate process, and run
84
+ // the check there hours or days later. Prohibitions in the returned shape
85
+ // are derived and structural, never authored and lexical (hard rule 5): the
86
+ // engine records that a fact holds and lets a claim disagree or not,
87
+ // exactly the way `changes_within` (§5.5) records transitions rather than a
88
+ // verdict -- there is no `mustNotSay`, no severity, nothing this project's
89
+ // four recorded negative-prompt failures would recognise.
90
+ export { narrationConstraintAt, contradictions, NARRATION_CONSTRAINT_FORMAT_VERSION, } from "./timeline/narration.js";
91
+ // The constrained-write choke point (design §5.4 option (C)) -- the one place
92
+ // a constrained numeric fact key changes, and the reason there is no longer a
93
+ // `resource_history` table beside the timeline answering the same question.
94
+ // Exported because an application that keeps invariant-bearing numbers here
95
+ // has to be able to write them, and writing them anywhere else is precisely
96
+ // the second path option (C) exists to close: a direct UPDATE still lands in
97
+ // `facts` (the projection triggers see to that), but it arrives unchecked and
98
+ // unannotated.
99
+ //
100
+ // `valueHistory` is the read half, and it is deliberately not a separate
101
+ // mechanism -- it is `facts` and `events`, assembled. It returns rows and
102
+ // never a verdict.
103
+ //
104
+ // `ConstraintViolationError` is exported because a refusal is only reviewable
105
+ // if the caller can tell it apart from a failure. For the `irreversible`
106
+ // member it carries §5.2c's one hop -- the contradicted fact, its
107
+ // `valid_from_t`, and the event that opened it -- so a reviewer can answer
108
+ // "is the fact wrong, or is the claim wrong", which is undecidable without it.
109
+ export { writeConstrainedValue, transferConstrainedValue, valueHistory, } from "./timeline/constrained.js";
110
+ export { ConstraintViolationError, constraintsFor, conservedConstraintFor } from "./timeline/registry.js";
111
+ // The resolve protocol (design §5.2a, GitHub issue #10) -- the inbound half of
112
+ // authority. The engine enforces the PROTOCOL -- resolution happens before
113
+ // narration, writes go through the audited path, declared expectations are
114
+ // checked -- without knowing what any particular mechanic MEANS. An application
115
+ // registers its mechanics by passing them to `createResolver`, and the engine
116
+ // dispatches them and never learns their names.
117
+ //
118
+ // Registration is injection at construction, exactly like `initializeSchema({
119
+ // migrations })` and for exactly the same reason: a global registry would make
120
+ // behaviour depend on module import order and on side effects at import time,
121
+ // which is the disease the entry-point split above cured. A parameter cannot be
122
+ // registered too late.
123
+ //
124
+ // NOTE WHAT IS DELIBERATELY *NOT* EXPORTED HERE: `withAdjudicationOpen` and
125
+ // `adjudicationOpen` (./timeline/adjudication.js). `resolve_only` (issue #13)
126
+ // means a value moves only through an adjudicating call, and the adjudication
127
+ // window is what "adjudicating" is measured against -- so exporting the ability
128
+ // to open one would hand every caller a one-line bypass of the constraint, and
129
+ // the fourth member of the family would be enforced against everybody except
130
+ // whoever read the export list. `resolver.resolve()` is the only public door to
131
+ // a `resolve_only` value. That is the whole point of the constraint, so the
132
+ // window stays internal to the engine that opens it.
133
+ export { createResolver, ResolveProtocolError } from "./timeline/resolve.js";
134
+ // The state-to-text projection (design §7, GitHub issue #16) -- "say what IS
135
+ // true, never what is absent."
136
+ //
137
+ // What is exported here is the MECHANISM and the TYPE of a vocabulary. There
138
+ // is deliberately no vocabulary value, no default and no example anywhere in
139
+ // this package: a vocabulary rich enough to render a real world contains a
140
+ // caller's own nouns, and either sitting in the engine would fail
141
+ // `engineVocabulary.test.ts` on day one -- correctly (§7's closing paragraph,
142
+ // §10). `RenderVocabulary` is a parameter type; a caller supplies the words.
143
+ //
144
+ // The rule is enforced at CONSTRUCTION and never by scanning output.
145
+ // `createStateRenderer` refuses a vocabulary entry carrying any field but
146
+ // `noun` and `adjectives`, by name -- which is what stops an `avoid:` or a
147
+ // `negate:` being bolted on later -- and the renderer's only source of state
148
+ // is `replay(t)`, so a fact that does not hold produces NOTHING rather than a
149
+ // phrase about its absence. There is no differential form and never will be:
150
+ // nothing here takes two `t`s, because "render the change between these two
151
+ // states" is precisely the shape that produces "no longer" (hard rules 3 and
152
+ // 4; the four recorded negative-prompt failures across two codebases).
153
+ export { createStateRenderer } from "./timeline/render.js";
154
+ // The turn reader (design §12 seam 3, GitHub issue #15) -- one model call per
155
+ // unit of progress, answering the questions a server cannot answer with code.
156
+ //
157
+ // The engine owns the call, the citation rule, coercion to keys that actually
158
+ // exist, the safe-direction default and the fallback ladder; the caller owns
159
+ // the questions and the key vocabulary they are answered in. Answers come back
160
+ // as KEYS, never prose.
161
+ //
162
+ // NOTE WHAT IS NOT HERE, BECAUSE IT IS THE POINT: no transport. A
163
+ // `ReaderTransport` is a plain async function the CALLER writes and injects,
164
+ // so this package contains no vendor SDK, no API key, no endpoint, and no
165
+ // network code of any kind -- enforced mechanically by
166
+ // `src/reader/__tests__/noVendorTransports.test.ts`, which scans this
167
+ // directory for vendor and credential tokens the way the vocabulary test
168
+ // scans for a consumer's language. The engine is provably ignorant of what is
169
+ // on the other end of a rung, which is also why the ladder's ORDER is the
170
+ // caller's: it never learns which rung is local and which is hosted.
171
+ export { createTurnReader } from "./reader/turnReader.js";
172
+ // Timeline export (design §6) -- the boundary that keeps both halves honest:
173
+ // conversational authoring upstream of a frozen artifact, deterministic
174
+ // consumers downstream of it. These are exported as library functions first
175
+ // and served as MCP tools second, because the consumer that most needs them
176
+ // is a process that must never call a model at runtime.
177
+ export { exportTimeline, importTimeline, exportTimelineToFile, importTimelineFromFile, TIMELINE_FORMAT_VERSION, } from "./timeline/export.js";
178
+ export { ENTITY_KINDS } from "./timeline/kinds.js";
179
+ // The web UI's port helpers -- no RPG dependency, so they stay here. The web
180
+ // UI server itself (`createHttpServer`/`startHttpServer`) imports quest,
181
+ // ability and combat tools and is exported from "run-dmcp/rpg" instead
182
+ // (src/rpg/index.ts), even though the file that implements it stays at
183
+ // src/http/server.ts on disk.
184
+ export { DEFAULT_HTTP_PORT, httpPortFromEnv, webUiEnabled, setHttpPort, getWebUiBaseUrl, } from "./utils/webui.js";
@@ -0,0 +1,49 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type Mechanic } from "./timeline/resolve.js";
3
+ import { type RenderVocabulary } from "./timeline/render.js";
4
+ export declare const SERVER_NAME = "dmcp";
5
+ export declare const SERVER_VERSION = "0.2.0";
6
+ /**
7
+ * Build an MCP server with every CORE tool, resource and prompt this engine
8
+ * serves -- entities, facts, events, the timeline, and the entity/property
9
+ * domains (resources, relationships, factions, secrets, locations, items)
10
+ * design §8 keeps out of the RPG layer because a consumer that lost them
11
+ * would have to drag that layer back in to get them. No dice, no combat, no
12
+ * abilities, no status effects, no random tables, no quests, and no
13
+ * game-master prompt library -- those are `registerRpgTools` in
14
+ * src/rpg/index.ts, which calls this function first and adds its layer on
15
+ * top of what it returns. `createMcpServer` there is the full assembly this
16
+ * function used to be, under the same name, with the same behaviour, so
17
+ * nothing that already depends on the full server sees any difference.
18
+ *
19
+ * The returned server is not connected to anything. Connect it to a transport
20
+ * yourself, and call `initializeSchema()` before serving a request -- both are
21
+ * the caller's to decide, and neither happens on import.
22
+ *
23
+ * `mechanics` (design §5.2a, issue #10) is injection at construction, the
24
+ * same way `initializeSchema({ migrations })` (src/db/schema.ts) is -- there
25
+ * is no global resolver anywhere in this codebase, only the one a caller
26
+ * builds by passing its mechanics here. Every existing zero-argument call
27
+ * site (src/bin/run-dmcp.ts, via src/rpg/index.ts) keeps working unchanged:
28
+ * `options` and `options.mechanics` are both optional, and calling
29
+ * `createCoreMcpServer()` with nothing at all registers every core tool this
30
+ * engine serves and no resolve surface. That absence is deliberate, not an
31
+ * oversight -- an engine with no mechanics registered has nothing a
32
+ * `resolve` tool could ever dispatch, so it gets no `resolve`/`list_mechanics`
33
+ * tools rather than a pair that could only ever answer "unknown-mechanic"
34
+ * and "[]".
35
+ *
36
+ * `vocabulary` (design §7, issue #16) is injected the same way and for the
37
+ * same reason -- and here the injection is not merely a style choice, it is
38
+ * the design's own line: the state-to-text projection's MECHANISM is core,
39
+ * and the NOUNS it may emit belong to each caller. A vocabulary rich enough
40
+ * to render a real world would fail this engine's vocabulary hygiene test on
41
+ * day one, correctly, so this package ships none and there is no default to
42
+ * fall back to. An engine with no vocabulary injected has nothing it could
43
+ * name, so it registers no `render_state_at` tool at all -- the identical
44
+ * shape (and the identical silence) as `mechanics` above.
45
+ */
46
+ export declare function createCoreMcpServer(options?: {
47
+ mechanics?: readonly Mechanic[];
48
+ vocabulary?: RenderVocabulary;
49
+ }): McpServer;
@@ -0,0 +1,127 @@
1
+ // The CORE MCP server, built but not started.
2
+ //
3
+ // This module exists so that "which tools does this engine serve" is separable
4
+ // from "and now go listen on stdio and bind a port". Constructing the server
5
+ // opens no database, binds no port and connects no transport; a caller decides
6
+ // all three. src/bin/run-dmcp.ts is the caller that decides them the way an
7
+ // application would.
8
+ //
9
+ // This is the CORE half of design §8's split (issue #17): entities, facts,
10
+ // events, the timeline, resource/relationship/faction/secret/location/item
11
+ // concepts, and the resolve/render/timeline surfaces that sit on top of them.
12
+ // Dice, combat, abilities, status effects, random tables and quests are
13
+ // genuinely game-shaped and live one layer up, in src/rpg/index.ts, which
14
+ // calls `registerRpgTools(server)` on a server this file already built.
15
+ // `createMcpServer` (the full assembly, unchanged in name and behaviour from
16
+ // before the split) lives there too now, not here -- this file only knows
17
+ // how to build the core.
18
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
19
+ // Import registration functions
20
+ import { registerCoreTools } from "./register/core.js";
21
+ import { registerWorldTools } from "./register/world.js";
22
+ import { registerCharacterTools } from "./register/character.js";
23
+ import { registerInventoryTools } from "./register/inventory.js";
24
+ import { registerNarrativeTools } from "./register/narrative.js";
25
+ import { registerResourceTools } from "./register/resources.js";
26
+ import { registerTimeTools } from "./register/time.js";
27
+ import { registerSecretTools } from "./register/secrets.js";
28
+ import { registerRelationshipTools } from "./register/relationships.js";
29
+ import { registerTagTools } from "./register/tags.js";
30
+ import { registerFactionTools } from "./register/factions.js";
31
+ import { registerNoteTools } from "./register/notes.js";
32
+ import { registerPauseTools } from "./register/pause.js";
33
+ import { registerImageTools } from "./register/images.js";
34
+ import { registerAudioTools } from "./register/audio.js";
35
+ import { registerDisplayTools } from "./register/display.js";
36
+ import { registerBatchTools } from "./register/batch.js";
37
+ import { registerMcpResources } from "./register/mcp-resources.js";
38
+ import { registerTimelineTools } from "./register/timeline.js";
39
+ import { registerResolveTools } from "./register/resolve.js";
40
+ import { registerRenderTools } from "./register/render.js";
41
+ import { createResolver } from "./timeline/resolve.js";
42
+ import { createStateRenderer } from "./timeline/render.js";
43
+ export const SERVER_NAME = "dmcp";
44
+ export const SERVER_VERSION = "0.2.0";
45
+ /**
46
+ * Build an MCP server with every CORE tool, resource and prompt this engine
47
+ * serves -- entities, facts, events, the timeline, and the entity/property
48
+ * domains (resources, relationships, factions, secrets, locations, items)
49
+ * design §8 keeps out of the RPG layer because a consumer that lost them
50
+ * would have to drag that layer back in to get them. No dice, no combat, no
51
+ * abilities, no status effects, no random tables, no quests, and no
52
+ * game-master prompt library -- those are `registerRpgTools` in
53
+ * src/rpg/index.ts, which calls this function first and adds its layer on
54
+ * top of what it returns. `createMcpServer` there is the full assembly this
55
+ * function used to be, under the same name, with the same behaviour, so
56
+ * nothing that already depends on the full server sees any difference.
57
+ *
58
+ * The returned server is not connected to anything. Connect it to a transport
59
+ * yourself, and call `initializeSchema()` before serving a request -- both are
60
+ * the caller's to decide, and neither happens on import.
61
+ *
62
+ * `mechanics` (design §5.2a, issue #10) is injection at construction, the
63
+ * same way `initializeSchema({ migrations })` (src/db/schema.ts) is -- there
64
+ * is no global resolver anywhere in this codebase, only the one a caller
65
+ * builds by passing its mechanics here. Every existing zero-argument call
66
+ * site (src/bin/run-dmcp.ts, via src/rpg/index.ts) keeps working unchanged:
67
+ * `options` and `options.mechanics` are both optional, and calling
68
+ * `createCoreMcpServer()` with nothing at all registers every core tool this
69
+ * engine serves and no resolve surface. That absence is deliberate, not an
70
+ * oversight -- an engine with no mechanics registered has nothing a
71
+ * `resolve` tool could ever dispatch, so it gets no `resolve`/`list_mechanics`
72
+ * tools rather than a pair that could only ever answer "unknown-mechanic"
73
+ * and "[]".
74
+ *
75
+ * `vocabulary` (design §7, issue #16) is injected the same way and for the
76
+ * same reason -- and here the injection is not merely a style choice, it is
77
+ * the design's own line: the state-to-text projection's MECHANISM is core,
78
+ * and the NOUNS it may emit belong to each caller. A vocabulary rich enough
79
+ * to render a real world would fail this engine's vocabulary hygiene test on
80
+ * day one, correctly, so this package ships none and there is no default to
81
+ * fall back to. An engine with no vocabulary injected has nothing it could
82
+ * name, so it registers no `render_state_at` tool at all -- the identical
83
+ * shape (and the identical silence) as `mechanics` above.
84
+ */
85
+ export function createCoreMcpServer(options) {
86
+ const server = new McpServer({
87
+ name: SERVER_NAME,
88
+ version: SERVER_VERSION,
89
+ });
90
+ // Register all core tools by domain
91
+ registerCoreTools(server); // Game, Interview, Rules
92
+ registerWorldTools(server); // Locations, Connections, Map
93
+ registerCharacterTools(server); // Characters (PC/NPC)
94
+ registerInventoryTools(server); // Items
95
+ registerNarrativeTools(server); // Events, History, Export, Player Choices
96
+ registerResourceTools(server); // Custom Resources
97
+ registerTimeTools(server); // Calendar, Time, Timers
98
+ registerSecretTools(server); // Secrets, Knowledge
99
+ registerRelationshipTools(server); // Relationships
100
+ registerTagTools(server); // Tags
101
+ registerFactionTools(server); // Factions
102
+ registerNoteTools(server); // Game Notes
103
+ registerPauseTools(server); // Pause/Resume, Context Snapshots, External Updates
104
+ registerImageTools(server); // Stored Images
105
+ registerAudioTools(server); // Stored Audio (TTS, Voice References)
106
+ registerDisplayTools(server); // Display/Theme Configuration
107
+ registerBatchTools(server); // Batch Operations (multi-entity, workflows -- entity/property only)
108
+ registerTimelineTools(server); // replay(t), story-time axis declaration
109
+ const mechanics = options?.mechanics;
110
+ if (mechanics && mechanics.length > 0) {
111
+ const resolver = createResolver({ mechanics });
112
+ registerResolveTools(server, resolver); // resolve(), list_mechanics -- only when mechanics are registered
113
+ }
114
+ const vocabulary = options?.vocabulary;
115
+ if (vocabulary) {
116
+ // Built here rather than accepted pre-built so the vocabulary is
117
+ // validated at construction of the SERVER too, not merely at
118
+ // construction of a renderer a caller might have made anywhere:
119
+ // `createStateRenderer` throws on a malformed vocabulary, so a server
120
+ // that would have served an unnameable projection never comes up.
121
+ registerRenderTools(server, createStateRenderer({ vocabulary })); // render_state_at -- only when a vocabulary is injected
122
+ }
123
+ // Register core MCP Resources (RPG resources -- game-quests, quest -- are
124
+ // registered by registerRpgMcpResources, one layer up)
125
+ registerMcpResources(server); // Read-only data access via URI
126
+ return server;
127
+ }