run-dmcp 0.1.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 (167) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +79 -0
  3. package/dist/__tests__/engineVocabulary.test.d.ts +1 -0
  4. package/dist/__tests__/engineVocabulary.test.js +147 -0
  5. package/dist/db/__tests__/connection.test.d.ts +1 -0
  6. package/dist/db/__tests__/connection.test.js +72 -0
  7. package/dist/db/__tests__/testDb.d.ts +33 -0
  8. package/dist/db/__tests__/testDb.js +41 -0
  9. package/dist/db/connection.d.ts +22 -0
  10. package/dist/db/connection.js +107 -0
  11. package/dist/db/schema.d.ts +1 -0
  12. package/dist/db/schema.js +725 -0
  13. package/dist/events/emitter.d.ts +22 -0
  14. package/dist/events/emitter.js +71 -0
  15. package/dist/http/server.d.ts +3 -0
  16. package/dist/http/server.js +649 -0
  17. package/dist/index.d.ts +2 -0
  18. package/dist/index.js +92 -0
  19. package/dist/register/abilities.d.ts +2 -0
  20. package/dist/register/abilities.js +165 -0
  21. package/dist/register/audio.d.ts +2 -0
  22. package/dist/register/audio.js +326 -0
  23. package/dist/register/batch.d.ts +2 -0
  24. package/dist/register/batch.js +343 -0
  25. package/dist/register/character.d.ts +2 -0
  26. package/dist/register/character.js +324 -0
  27. package/dist/register/combat.d.ts +2 -0
  28. package/dist/register/combat.js +207 -0
  29. package/dist/register/core.d.ts +2 -0
  30. package/dist/register/core.js +1040 -0
  31. package/dist/register/display.d.ts +2 -0
  32. package/dist/register/display.js +263 -0
  33. package/dist/register/factions.d.ts +2 -0
  34. package/dist/register/factions.js +186 -0
  35. package/dist/register/images.d.ts +2 -0
  36. package/dist/register/images.js +400 -0
  37. package/dist/register/inventory.d.ts +2 -0
  38. package/dist/register/inventory.js +115 -0
  39. package/dist/register/mcp-prompts.d.ts +2 -0
  40. package/dist/register/mcp-prompts.js +684 -0
  41. package/dist/register/mcp-resources.d.ts +2 -0
  42. package/dist/register/mcp-resources.js +335 -0
  43. package/dist/register/narrative.d.ts +2 -0
  44. package/dist/register/narrative.js +242 -0
  45. package/dist/register/notes.d.ts +2 -0
  46. package/dist/register/notes.js +170 -0
  47. package/dist/register/pause.d.ts +2 -0
  48. package/dist/register/pause.js +580 -0
  49. package/dist/register/quests.d.ts +2 -0
  50. package/dist/register/quests.js +118 -0
  51. package/dist/register/relationships.d.ts +2 -0
  52. package/dist/register/relationships.js +147 -0
  53. package/dist/register/resources.d.ts +2 -0
  54. package/dist/register/resources.js +277 -0
  55. package/dist/register/secrets.d.ts +2 -0
  56. package/dist/register/secrets.js +192 -0
  57. package/dist/register/status.d.ts +2 -0
  58. package/dist/register/status.js +130 -0
  59. package/dist/register/tables.d.ts +2 -0
  60. package/dist/register/tables.js +146 -0
  61. package/dist/register/tags.d.ts +2 -0
  62. package/dist/register/tags.js +114 -0
  63. package/dist/register/time.d.ts +2 -0
  64. package/dist/register/time.js +281 -0
  65. package/dist/register/world.d.ts +2 -0
  66. package/dist/register/world.js +127 -0
  67. package/dist/schemas/index.d.ts +921 -0
  68. package/dist/schemas/index.js +121 -0
  69. package/dist/test-setup.d.ts +1 -0
  70. package/dist/test-setup.js +13 -0
  71. package/dist/tools/__tests__/audio.test.d.ts +1 -0
  72. package/dist/tools/__tests__/audio.test.js +59 -0
  73. package/dist/tools/__tests__/conserved.test.d.ts +1 -0
  74. package/dist/tools/__tests__/conserved.test.js +488 -0
  75. package/dist/tools/__tests__/constraint.test.d.ts +1 -0
  76. package/dist/tools/__tests__/constraint.test.js +212 -0
  77. package/dist/tools/__tests__/expiry-consequences.test.d.ts +1 -0
  78. package/dist/tools/__tests__/expiry-consequences.test.js +110 -0
  79. package/dist/tools/__tests__/images.test.d.ts +1 -0
  80. package/dist/tools/__tests__/images.test.js +59 -0
  81. package/dist/tools/__tests__/relationship.test.d.ts +1 -0
  82. package/dist/tools/__tests__/relationship.test.js +132 -0
  83. package/dist/tools/__tests__/resource-constraints.test.d.ts +1 -0
  84. package/dist/tools/__tests__/resource-constraints.test.js +131 -0
  85. package/dist/tools/__tests__/resource.test.d.ts +1 -0
  86. package/dist/tools/__tests__/resource.test.js +190 -0
  87. package/dist/tools/__tests__/time.test.d.ts +1 -0
  88. package/dist/tools/__tests__/time.test.js +404 -0
  89. package/dist/tools/__tests__/timers.test.d.ts +1 -0
  90. package/dist/tools/__tests__/timers.test.js +426 -0
  91. package/dist/tools/__tests__/world.test.d.ts +1 -0
  92. package/dist/tools/__tests__/world.test.js +70 -0
  93. package/dist/tools/ability.d.ts +48 -0
  94. package/dist/tools/ability.js +238 -0
  95. package/dist/tools/audio.d.ts +24 -0
  96. package/dist/tools/audio.js +365 -0
  97. package/dist/tools/character.d.ts +70 -0
  98. package/dist/tools/character.js +309 -0
  99. package/dist/tools/combat.d.ts +13 -0
  100. package/dist/tools/combat.js +195 -0
  101. package/dist/tools/constraint.d.ts +132 -0
  102. package/dist/tools/constraint.js +269 -0
  103. package/dist/tools/dice.d.ts +23 -0
  104. package/dist/tools/dice.js +111 -0
  105. package/dist/tools/display.d.ts +120 -0
  106. package/dist/tools/display.js +528 -0
  107. package/dist/tools/faction.d.ts +61 -0
  108. package/dist/tools/faction.js +269 -0
  109. package/dist/tools/game.d.ts +96 -0
  110. package/dist/tools/game.js +526 -0
  111. package/dist/tools/image-prompt.d.ts +49 -0
  112. package/dist/tools/image-prompt.js +479 -0
  113. package/dist/tools/images.d.ts +47 -0
  114. package/dist/tools/images.js +449 -0
  115. package/dist/tools/inventory.d.ts +20 -0
  116. package/dist/tools/inventory.js +145 -0
  117. package/dist/tools/narrative.d.ts +58 -0
  118. package/dist/tools/narrative.js +237 -0
  119. package/dist/tools/notes.d.ts +41 -0
  120. package/dist/tools/notes.js +220 -0
  121. package/dist/tools/pause.d.ts +110 -0
  122. package/dist/tools/pause.js +1254 -0
  123. package/dist/tools/quest.d.ts +34 -0
  124. package/dist/tools/quest.js +164 -0
  125. package/dist/tools/relationship.d.ts +74 -0
  126. package/dist/tools/relationship.js +324 -0
  127. package/dist/tools/resource.d.ts +93 -0
  128. package/dist/tools/resource.js +374 -0
  129. package/dist/tools/rules.d.ts +4 -0
  130. package/dist/tools/rules.js +30 -0
  131. package/dist/tools/secrets.d.ts +49 -0
  132. package/dist/tools/secrets.js +195 -0
  133. package/dist/tools/status.d.ts +36 -0
  134. package/dist/tools/status.js +218 -0
  135. package/dist/tools/tables.d.ts +33 -0
  136. package/dist/tools/tables.js +209 -0
  137. package/dist/tools/tags.d.ts +52 -0
  138. package/dist/tools/tags.js +176 -0
  139. package/dist/tools/time.d.ts +33 -0
  140. package/dist/tools/time.js +276 -0
  141. package/dist/tools/timers.d.ts +41 -0
  142. package/dist/tools/timers.js +215 -0
  143. package/dist/tools/world.d.ts +78 -0
  144. package/dist/tools/world.js +331 -0
  145. package/dist/types/index.d.ts +969 -0
  146. package/dist/types/index.js +1 -0
  147. package/dist/utils/__tests__/json.test.d.ts +1 -0
  148. package/dist/utils/__tests__/json.test.js +55 -0
  149. package/dist/utils/__tests__/validation.test.d.ts +1 -0
  150. package/dist/utils/__tests__/validation.test.js +90 -0
  151. package/dist/utils/errors.d.ts +44 -0
  152. package/dist/utils/errors.js +121 -0
  153. package/dist/utils/json.d.ts +9 -0
  154. package/dist/utils/json.js +23 -0
  155. package/dist/utils/logger.d.ts +7 -0
  156. package/dist/utils/logger.js +50 -0
  157. package/dist/utils/output-schemas.d.ts +594 -0
  158. package/dist/utils/output-schemas.js +331 -0
  159. package/dist/utils/tool-annotations.d.ts +147 -0
  160. package/dist/utils/tool-annotations.js +98 -0
  161. package/dist/utils/validation.d.ts +34 -0
  162. package/dist/utils/validation.js +52 -0
  163. package/dist/utils/verbosity.d.ts +57 -0
  164. package/dist/utils/verbosity.js +67 -0
  165. package/dist/utils/webui.d.ts +20 -0
  166. package/dist/utils/webui.js +35 -0
  167. package/package.json +75 -0
@@ -0,0 +1,400 @@
1
+ import { z } from "zod";
2
+ import * as imageTools from "../tools/images.js";
3
+ import * as gameTools from "../tools/game.js";
4
+ import { LIMITS } from "../utils/validation.js";
5
+ import { ANNOTATIONS } from "../utils/tool-annotations.js";
6
+ export function registerImageTools(server) {
7
+ server.registerTool("store_image", {
8
+ description: "Store an image for an entity. Provide a URL and the image will be downloaded and stored locally.",
9
+ inputSchema: {
10
+ gameId: z.string().max(100).describe("The game ID"),
11
+ entityId: z
12
+ .string()
13
+ .max(100)
14
+ .describe("ID of the entity (character, location, item, etc.)"),
15
+ entityType: z
16
+ .string()
17
+ .max(50)
18
+ .describe("Type of entity (e.g., character, location, item, scene, faction, quest, ability)"),
19
+ url: z.string().max(2000).describe("URL to fetch the image from"),
20
+ label: z
21
+ .string()
22
+ .max(LIMITS.NAME_MAX)
23
+ .optional()
24
+ .describe("Label for the image (e.g., 'Portrait', 'Battle Pose')"),
25
+ description: z
26
+ .string()
27
+ .max(LIMITS.DESCRIPTION_MAX)
28
+ .optional()
29
+ .describe("Description of what's in the image"),
30
+ mimeType: z
31
+ .string()
32
+ .max(100)
33
+ .optional()
34
+ .describe("MIME type (e.g., 'image/png'). Inferred from response headers if not provided."),
35
+ generationTool: z
36
+ .string()
37
+ .max(100)
38
+ .optional()
39
+ .describe("Tool used to generate (e.g., 'dalle', 'sdxl', 'midjourney', 'comfyui')"),
40
+ generationPrompt: z
41
+ .string()
42
+ .max(LIMITS.CONTENT_MAX)
43
+ .optional()
44
+ .describe("The prompt used to generate the image"),
45
+ setAsPrimary: z
46
+ .boolean()
47
+ .optional()
48
+ .describe("Set this as the primary image for the entity"),
49
+ },
50
+ annotations: ANNOTATIONS.CREATE,
51
+ }, async (params) => {
52
+ try {
53
+ const image = await imageTools.storeImage(params);
54
+ return {
55
+ content: [{ type: "text", text: JSON.stringify(image, null, 2) }],
56
+ };
57
+ }
58
+ catch (error) {
59
+ return {
60
+ content: [{ type: "text", text: `Error storing image: ${error}` }],
61
+ isError: true,
62
+ };
63
+ }
64
+ });
65
+ server.registerTool("get_image", {
66
+ description: "Get image metadata by ID",
67
+ inputSchema: {
68
+ imageId: z.string().max(100).describe("The image ID"),
69
+ },
70
+ annotations: ANNOTATIONS.READ_ONLY,
71
+ }, async ({ imageId }) => {
72
+ const image = imageTools.getImage(imageId);
73
+ if (!image) {
74
+ return {
75
+ content: [{ type: "text", text: "Image not found" }],
76
+ isError: true,
77
+ };
78
+ }
79
+ return {
80
+ content: [{ type: "text", text: JSON.stringify(image, null, 2) }],
81
+ };
82
+ });
83
+ server.registerTool("get_image_data", {
84
+ description: "Get image with base64 data included (for displaying or processing). Supports format conversion, resizing, and quality control.",
85
+ inputSchema: {
86
+ imageId: z.string().max(100).describe("The image ID"),
87
+ format: z
88
+ .enum(["jpeg", "webp", "png"])
89
+ .optional()
90
+ .describe("Output format. If not specified, returns original format."),
91
+ width: z
92
+ .number()
93
+ .int()
94
+ .positive()
95
+ .max(4096)
96
+ .optional()
97
+ .describe("Target width in pixels. Image will be resized proportionally if only width is specified."),
98
+ height: z
99
+ .number()
100
+ .int()
101
+ .positive()
102
+ .max(4096)
103
+ .optional()
104
+ .describe("Target height in pixels. Image will be resized proportionally if only height is specified."),
105
+ quality: z
106
+ .number()
107
+ .int()
108
+ .min(1)
109
+ .max(100)
110
+ .optional()
111
+ .describe("Quality for JPEG/WebP (1-100). Default is 80. For PNG, controls compression level."),
112
+ fit: z
113
+ .enum(["cover", "contain", "fill", "inside", "outside"])
114
+ .optional()
115
+ .describe("How to fit the image when both width and height are specified. Default is 'inside' (preserve aspect ratio, fit within dimensions)."),
116
+ },
117
+ annotations: ANNOTATIONS.READ_ONLY,
118
+ }, async ({ imageId, format, width, height, quality, fit }) => {
119
+ const result = await imageTools.getImageData(imageId, {
120
+ format,
121
+ width,
122
+ height,
123
+ quality,
124
+ fit,
125
+ });
126
+ if (!result) {
127
+ return {
128
+ content: [{ type: "text", text: "Image not found" }],
129
+ isError: true,
130
+ };
131
+ }
132
+ return {
133
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
134
+ };
135
+ });
136
+ server.registerTool("list_entity_images", {
137
+ description: "List all images for an entity",
138
+ inputSchema: {
139
+ entityId: z.string().max(100).describe("ID of the entity"),
140
+ entityType: z
141
+ .string()
142
+ .max(50)
143
+ .describe("Type of entity (e.g., character, location, item, scene, faction, quest, ability)"),
144
+ },
145
+ annotations: ANNOTATIONS.READ_ONLY,
146
+ }, async ({ entityId, entityType }) => {
147
+ const result = imageTools.listEntityImages(entityId, entityType);
148
+ return {
149
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
150
+ };
151
+ });
152
+ server.registerTool("list_entities_missing_images", {
153
+ description: "List all entities in a game that don't have a primary image. Useful for batch image generation workflows. Returns entities grouped by type with counts.",
154
+ inputSchema: {
155
+ gameId: z.string().max(100).describe("The game ID"),
156
+ entityType: z
157
+ .string()
158
+ .max(50)
159
+ .optional()
160
+ .describe("Filter by entity type (optional, returns all types if not specified). Common types: character, location, item, faction."),
161
+ },
162
+ annotations: ANNOTATIONS.READ_ONLY,
163
+ }, async ({ gameId, entityType }) => {
164
+ const result = imageTools.listEntitiesMissingImages(gameId, entityType);
165
+ return {
166
+ content: [{
167
+ type: "text",
168
+ text: JSON.stringify(result, null, 2),
169
+ }],
170
+ };
171
+ });
172
+ server.registerTool("delete_image", {
173
+ description: "Delete a stored image (removes file and database record)",
174
+ inputSchema: {
175
+ imageId: z.string().max(100).describe("The image ID"),
176
+ },
177
+ annotations: ANNOTATIONS.DESTRUCTIVE,
178
+ }, async ({ imageId }) => {
179
+ const success = imageTools.deleteImage(imageId);
180
+ return {
181
+ content: [
182
+ { type: "text", text: success ? "Image deleted" : "Image not found" },
183
+ ],
184
+ isError: !success,
185
+ };
186
+ });
187
+ server.registerTool("set_primary_image", {
188
+ description: "Set an image as the primary image for its entity",
189
+ inputSchema: {
190
+ imageId: z.string().max(100).describe("The image ID to set as primary"),
191
+ },
192
+ annotations: ANNOTATIONS.UPDATE,
193
+ }, async ({ imageId }) => {
194
+ const image = imageTools.setPrimaryImage(imageId);
195
+ if (!image) {
196
+ return {
197
+ content: [{ type: "text", text: "Image not found" }],
198
+ isError: true,
199
+ };
200
+ }
201
+ return {
202
+ content: [{ type: "text", text: JSON.stringify(image, null, 2) }],
203
+ };
204
+ });
205
+ server.registerTool("update_image_metadata", {
206
+ description: "Update label, description, or entity association of an image",
207
+ inputSchema: {
208
+ imageId: z.string().max(100).describe("The image ID"),
209
+ label: z.string().max(LIMITS.NAME_MAX).optional().describe("New label"),
210
+ description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("New description"),
211
+ entityId: z.string().max(100).optional().describe("New entity ID to associate the image with"),
212
+ entityType: z.string().max(50).optional().describe("New entity type (required if changing to a different type of entity)"),
213
+ },
214
+ annotations: ANNOTATIONS.UPDATE,
215
+ }, async ({ imageId, label, description, entityId, entityType }) => {
216
+ try {
217
+ const image = imageTools.updateImageMetadata(imageId, {
218
+ label,
219
+ description,
220
+ entityId,
221
+ entityType,
222
+ });
223
+ if (!image) {
224
+ return {
225
+ content: [{ type: "text", text: "Image not found" }],
226
+ isError: true,
227
+ };
228
+ }
229
+ return {
230
+ content: [{ type: "text", text: JSON.stringify(image, null, 2) }],
231
+ };
232
+ }
233
+ catch (error) {
234
+ return {
235
+ content: [{ type: "text", text: `Error updating image: ${error}` }],
236
+ isError: true,
237
+ };
238
+ }
239
+ });
240
+ // ============================================================================
241
+ // BUILD IMAGE PROMPT - construct prompt from entity's structured imageGen data
242
+ // ============================================================================
243
+ server.registerTool("build_image_prompt", {
244
+ description: "Build an image generation prompt from an entity's structured imageGen data. If a template exists for the entity type (or a specific templateId is provided), uses template-based building. Otherwise combines imageGen schema, notes/description, and preset defaults.",
245
+ inputSchema: {
246
+ entityId: z.string().max(100).describe("The entity ID"),
247
+ entityType: z.string().max(50).describe("Type of entity (e.g., character, location, item, faction)"),
248
+ gameId: z.string().max(100).optional().describe("Game ID (optional, inferred from entity if not provided)"),
249
+ templateId: z.string().max(100).optional().describe("Specific template ID to use (optional, uses default for entity type if not provided)"),
250
+ },
251
+ outputSchema: {
252
+ entityId: z.string(),
253
+ entityType: z.string(),
254
+ entityName: z.string(),
255
+ prompt: z.string().describe("The constructed positive prompt"),
256
+ negativePrompt: z.string().describe("The constructed negative prompt"),
257
+ summary: z.string().describe("Human-readable summary for verification"),
258
+ source: z.object({
259
+ fromImageGen: z.boolean().describe("Whether imageGen schema was used"),
260
+ fromNotes: z.boolean().describe("Whether notes/description was used"),
261
+ fromPreset: z.boolean().describe("Whether game preset was applied"),
262
+ fromTemplate: z.boolean().describe("Whether a template was used"),
263
+ }),
264
+ templateUsed: z.string().optional().describe("Name of template used, if any"),
265
+ },
266
+ annotations: ANNOTATIONS.READ_ONLY,
267
+ }, async ({ entityId, entityType, gameId, templateId }) => {
268
+ const { buildImagePrompt } = await import("../tools/image-prompt.js");
269
+ const result = buildImagePrompt(entityId, entityType, gameId, templateId);
270
+ if (!result) {
271
+ return {
272
+ content: [{ type: "text", text: `Entity not found: ${entityType} ${entityId}` }],
273
+ isError: true,
274
+ };
275
+ }
276
+ return {
277
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
278
+ structuredContent: result,
279
+ };
280
+ });
281
+ // ============================================================================
282
+ // IMAGE PROMPT TEMPLATES - Template-based prompt building
283
+ // ============================================================================
284
+ server.registerTool("list_image_prompt_templates", {
285
+ description: "List all image prompt templates for a game. Templates allow configurable prompt building from entity data using placeholder syntax.",
286
+ inputSchema: {
287
+ gameId: z.string().max(100).describe("The game ID"),
288
+ },
289
+ annotations: ANNOTATIONS.READ_ONLY,
290
+ }, async ({ gameId }) => {
291
+ const templates = gameTools.listImagePromptTemplates(gameId);
292
+ const summary = templates.map(t => ({
293
+ id: t.id,
294
+ name: t.name,
295
+ entityType: t.entityType,
296
+ description: t.description,
297
+ isDefault: t.isDefault,
298
+ priority: t.priority,
299
+ }));
300
+ return {
301
+ content: [{
302
+ type: "text",
303
+ text: JSON.stringify({
304
+ count: templates.length,
305
+ templates: summary,
306
+ }, null, 2),
307
+ }],
308
+ };
309
+ });
310
+ server.registerTool("get_image_prompt_template", {
311
+ description: "Get full details of an image prompt template including the template strings.",
312
+ inputSchema: {
313
+ gameId: z.string().max(100).describe("The game ID"),
314
+ templateId: z.string().max(100).describe("The template ID"),
315
+ },
316
+ annotations: ANNOTATIONS.READ_ONLY,
317
+ }, async ({ gameId, templateId }) => {
318
+ const template = gameTools.getImagePromptTemplate(gameId, templateId);
319
+ if (!template) {
320
+ return {
321
+ content: [{ type: "text", text: "Template not found" }],
322
+ isError: true,
323
+ };
324
+ }
325
+ return {
326
+ content: [{ type: "text", text: JSON.stringify(template, null, 2) }],
327
+ };
328
+ });
329
+ server.registerTool("create_image_prompt_template", {
330
+ description: `Create a new image prompt template for building prompts from entity data. Templates use placeholder syntax:
331
+ - {{field.path}} - simple value substitution
332
+ - {{field.path|"default"}} - value with default if missing
333
+ - {{field.path|}} - value or empty string if missing
334
+
335
+ Available fields include: name, notes, subject.primaryDescription, subject.physicalTraits.*, subject.attire.*, subject.environment.*, style.*, composition.*, and any imageGen data.`,
336
+ inputSchema: {
337
+ gameId: z.string().max(100).describe("The game ID"),
338
+ name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Template name"),
339
+ entityType: z.string().max(50).describe("Which entity type this template handles (e.g., character, location, item, faction)"),
340
+ promptTemplate: z.string().max(LIMITS.CONTENT_MAX).describe("Main prompt template with placeholders"),
341
+ description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("What this template is for"),
342
+ negativePromptTemplate: z.string().max(LIMITS.CONTENT_MAX).optional().describe("Negative prompt template"),
343
+ promptPrefix: z.string().max(500).optional().describe("Text added before the filled template"),
344
+ promptSuffix: z.string().max(500).optional().describe("Text added after the filled template (e.g., quality tags)"),
345
+ fieldAliases: z.record(z.string().max(100), z.string().max(200)).optional().describe("Alias mappings for complex paths"),
346
+ defaults: z.record(z.string().max(100), z.string().max(200)).optional().describe("Default values for missing fields"),
347
+ priority: z.number().optional().describe("Priority for template selection (higher = preferred)"),
348
+ isDefault: z.boolean().optional().describe("Make this the default template for its entity type"),
349
+ },
350
+ annotations: ANNOTATIONS.CREATE,
351
+ }, async ({ gameId, ...params }) => {
352
+ const template = gameTools.createImagePromptTemplate(gameId, params);
353
+ return {
354
+ content: [{ type: "text", text: JSON.stringify(template, null, 2) }],
355
+ };
356
+ });
357
+ server.registerTool("update_image_prompt_template", {
358
+ description: "Update an existing image prompt template. Only specified fields are updated.",
359
+ inputSchema: {
360
+ gameId: z.string().max(100).describe("The game ID"),
361
+ templateId: z.string().max(100).describe("The template ID to update"),
362
+ name: z.string().min(1).max(LIMITS.NAME_MAX).optional().describe("New template name"),
363
+ description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("New description"),
364
+ promptTemplate: z.string().max(LIMITS.CONTENT_MAX).optional().describe("New prompt template"),
365
+ negativePromptTemplate: z.string().max(LIMITS.CONTENT_MAX).optional().describe("New negative prompt template"),
366
+ promptPrefix: z.string().max(500).optional().describe("New prefix"),
367
+ promptSuffix: z.string().max(500).optional().describe("New suffix"),
368
+ fieldAliases: z.record(z.string().max(100), z.string().max(200)).optional().describe("New field aliases"),
369
+ defaults: z.record(z.string().max(100), z.string().max(200)).optional().describe("New defaults"),
370
+ priority: z.number().optional().describe("New priority"),
371
+ isDefault: z.boolean().optional().describe("Make this the default for its entity type"),
372
+ },
373
+ annotations: ANNOTATIONS.UPDATE,
374
+ }, async ({ gameId, templateId, ...updates }) => {
375
+ const template = gameTools.updateImagePromptTemplate(gameId, templateId, updates);
376
+ if (!template) {
377
+ return {
378
+ content: [{ type: "text", text: "Template not found" }],
379
+ isError: true,
380
+ };
381
+ }
382
+ return {
383
+ content: [{ type: "text", text: JSON.stringify(template, null, 2) }],
384
+ };
385
+ });
386
+ server.registerTool("delete_image_prompt_template", {
387
+ description: "Delete an image prompt template. This is IRREVERSIBLE.",
388
+ inputSchema: {
389
+ gameId: z.string().max(100).describe("The game ID"),
390
+ templateId: z.string().max(100).describe("The template ID to delete"),
391
+ },
392
+ annotations: ANNOTATIONS.DESTRUCTIVE,
393
+ }, async ({ gameId, templateId }) => {
394
+ const success = gameTools.deleteImagePromptTemplate(gameId, templateId);
395
+ return {
396
+ content: [{ type: "text", text: success ? "Template deleted" : "Template not found" }],
397
+ isError: !success,
398
+ };
399
+ });
400
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerInventoryTools(server: McpServer): void;
@@ -0,0 +1,115 @@
1
+ import { z } from "zod";
2
+ import * as inventoryTools from "../tools/inventory.js";
3
+ import { imageGenSchema } from "../schemas/index.js";
4
+ import { LIMITS } from "../utils/validation.js";
5
+ import { ANNOTATIONS } from "../utils/tool-annotations.js";
6
+ export function registerInventoryTools(server) {
7
+ server.registerTool("create_item", {
8
+ description: "Create a new item. IMPORTANT: Call this IMMEDIATELY when introducing any item in narrative - whether found, given, purchased, or mentioned. Items must exist in the database before being referenced in dialogue or description.",
9
+ inputSchema: {
10
+ gameId: z.string().max(100).describe("The game ID"),
11
+ ownerId: z.string().max(100).describe("Character or location ID that owns the item"),
12
+ ownerType: z.enum(["character", "location"]).describe("Owner type"),
13
+ name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Item name"),
14
+ properties: z.object({
15
+ description: z.string().max(LIMITS.DESCRIPTION_MAX).optional(),
16
+ type: z.string().max(100).optional(),
17
+ weight: z.number().optional(),
18
+ value: z.number().optional(),
19
+ effects: z.array(z.string().max(LIMITS.NAME_MAX)).max(LIMITS.ARRAY_MAX).optional(),
20
+ }).optional().describe("Item properties"),
21
+ imageGen: imageGenSchema.optional().describe("Image generation metadata for item art"),
22
+ },
23
+ annotations: ANNOTATIONS.CREATE,
24
+ }, async (params) => {
25
+ const item = inventoryTools.createItem(params);
26
+ return {
27
+ content: [{ type: "text", text: JSON.stringify(item, null, 2) }],
28
+ };
29
+ });
30
+ server.registerTool("get_item", {
31
+ description: "Get item details",
32
+ inputSchema: {
33
+ itemId: z.string().max(100).describe("The item ID"),
34
+ },
35
+ annotations: ANNOTATIONS.READ_ONLY,
36
+ }, async ({ itemId }) => {
37
+ const item = inventoryTools.getItem(itemId);
38
+ if (!item) {
39
+ return {
40
+ content: [{ type: "text", text: "Item not found" }],
41
+ isError: true,
42
+ };
43
+ }
44
+ return {
45
+ content: [{ type: "text", text: JSON.stringify(item, null, 2) }],
46
+ };
47
+ });
48
+ server.registerTool("update_item", {
49
+ description: "Update an item",
50
+ inputSchema: {
51
+ itemId: z.string().max(100).describe("The item ID"),
52
+ name: z.string().max(LIMITS.NAME_MAX).optional().describe("New name"),
53
+ properties: z.record(z.string(), z.unknown()).optional().describe("Property updates"),
54
+ imageGen: imageGenSchema.nullable().optional().describe("Image generation metadata (null to remove)"),
55
+ },
56
+ annotations: ANNOTATIONS.UPDATE,
57
+ }, async ({ itemId, name, properties, imageGen }) => {
58
+ const item = inventoryTools.updateItem(itemId, { name, properties, imageGen });
59
+ if (!item) {
60
+ return {
61
+ content: [{ type: "text", text: "Item not found" }],
62
+ isError: true,
63
+ };
64
+ }
65
+ return {
66
+ content: [{ type: "text", text: JSON.stringify(item, null, 2) }],
67
+ };
68
+ });
69
+ server.registerTool("delete_item", {
70
+ description: "Delete an item permanently",
71
+ inputSchema: {
72
+ itemId: z.string().max(100).describe("The item ID"),
73
+ },
74
+ annotations: ANNOTATIONS.DESTRUCTIVE,
75
+ }, async ({ itemId }) => {
76
+ const success = inventoryTools.deleteItem(itemId);
77
+ return {
78
+ content: [{ type: "text", text: success ? "Item deleted" : "Item not found" }],
79
+ isError: !success,
80
+ };
81
+ });
82
+ server.registerTool("transfer_item", {
83
+ description: "Transfer an item to a new owner. Call this whenever an item changes hands - given, dropped, picked up, stolen, purchased, etc. The database should always reflect current item ownership.",
84
+ inputSchema: {
85
+ itemId: z.string().max(100).describe("The item ID"),
86
+ newOwnerId: z.string().max(100).describe("New owner ID"),
87
+ newOwnerType: z.enum(["character", "location"]).describe("New owner type"),
88
+ },
89
+ annotations: ANNOTATIONS.UPDATE,
90
+ }, async ({ itemId, newOwnerId, newOwnerType }) => {
91
+ const item = inventoryTools.transferItem(itemId, newOwnerId, newOwnerType);
92
+ if (!item) {
93
+ return {
94
+ content: [{ type: "text", text: "Item not found" }],
95
+ isError: true,
96
+ };
97
+ }
98
+ return {
99
+ content: [{ type: "text", text: JSON.stringify(item, null, 2) }],
100
+ };
101
+ });
102
+ server.registerTool("get_inventory", {
103
+ description: "Get inventory for a character or location",
104
+ inputSchema: {
105
+ ownerId: z.string().max(100).describe("Owner ID"),
106
+ ownerType: z.enum(["character", "location"]).describe("Owner type"),
107
+ },
108
+ annotations: ANNOTATIONS.READ_ONLY,
109
+ }, async ({ ownerId, ownerType }) => {
110
+ const items = inventoryTools.getInventory(ownerId, ownerType);
111
+ return {
112
+ content: [{ type: "text", text: JSON.stringify(items, null, 2) }],
113
+ };
114
+ });
115
+ }
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerMcpPrompts(server: McpServer): void;