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.
- package/LICENSE +22 -0
- package/README.md +79 -0
- package/dist/__tests__/engineVocabulary.test.d.ts +1 -0
- package/dist/__tests__/engineVocabulary.test.js +147 -0
- package/dist/db/__tests__/connection.test.d.ts +1 -0
- package/dist/db/__tests__/connection.test.js +72 -0
- package/dist/db/__tests__/testDb.d.ts +33 -0
- package/dist/db/__tests__/testDb.js +41 -0
- package/dist/db/connection.d.ts +22 -0
- package/dist/db/connection.js +107 -0
- package/dist/db/schema.d.ts +1 -0
- package/dist/db/schema.js +725 -0
- package/dist/events/emitter.d.ts +22 -0
- package/dist/events/emitter.js +71 -0
- package/dist/http/server.d.ts +3 -0
- package/dist/http/server.js +649 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +92 -0
- package/dist/register/abilities.d.ts +2 -0
- package/dist/register/abilities.js +165 -0
- package/dist/register/audio.d.ts +2 -0
- package/dist/register/audio.js +326 -0
- package/dist/register/batch.d.ts +2 -0
- package/dist/register/batch.js +343 -0
- package/dist/register/character.d.ts +2 -0
- package/dist/register/character.js +324 -0
- package/dist/register/combat.d.ts +2 -0
- package/dist/register/combat.js +207 -0
- package/dist/register/core.d.ts +2 -0
- package/dist/register/core.js +1040 -0
- package/dist/register/display.d.ts +2 -0
- package/dist/register/display.js +263 -0
- package/dist/register/factions.d.ts +2 -0
- package/dist/register/factions.js +186 -0
- package/dist/register/images.d.ts +2 -0
- package/dist/register/images.js +400 -0
- package/dist/register/inventory.d.ts +2 -0
- package/dist/register/inventory.js +115 -0
- package/dist/register/mcp-prompts.d.ts +2 -0
- package/dist/register/mcp-prompts.js +684 -0
- package/dist/register/mcp-resources.d.ts +2 -0
- package/dist/register/mcp-resources.js +335 -0
- package/dist/register/narrative.d.ts +2 -0
- package/dist/register/narrative.js +242 -0
- package/dist/register/notes.d.ts +2 -0
- package/dist/register/notes.js +170 -0
- package/dist/register/pause.d.ts +2 -0
- package/dist/register/pause.js +580 -0
- package/dist/register/quests.d.ts +2 -0
- package/dist/register/quests.js +118 -0
- package/dist/register/relationships.d.ts +2 -0
- package/dist/register/relationships.js +147 -0
- package/dist/register/resources.d.ts +2 -0
- package/dist/register/resources.js +277 -0
- package/dist/register/secrets.d.ts +2 -0
- package/dist/register/secrets.js +192 -0
- package/dist/register/status.d.ts +2 -0
- package/dist/register/status.js +130 -0
- package/dist/register/tables.d.ts +2 -0
- package/dist/register/tables.js +146 -0
- package/dist/register/tags.d.ts +2 -0
- package/dist/register/tags.js +114 -0
- package/dist/register/time.d.ts +2 -0
- package/dist/register/time.js +281 -0
- package/dist/register/world.d.ts +2 -0
- package/dist/register/world.js +127 -0
- package/dist/schemas/index.d.ts +921 -0
- package/dist/schemas/index.js +121 -0
- package/dist/test-setup.d.ts +1 -0
- package/dist/test-setup.js +13 -0
- package/dist/tools/__tests__/audio.test.d.ts +1 -0
- package/dist/tools/__tests__/audio.test.js +59 -0
- package/dist/tools/__tests__/conserved.test.d.ts +1 -0
- package/dist/tools/__tests__/conserved.test.js +488 -0
- package/dist/tools/__tests__/constraint.test.d.ts +1 -0
- package/dist/tools/__tests__/constraint.test.js +212 -0
- package/dist/tools/__tests__/expiry-consequences.test.d.ts +1 -0
- package/dist/tools/__tests__/expiry-consequences.test.js +110 -0
- package/dist/tools/__tests__/images.test.d.ts +1 -0
- package/dist/tools/__tests__/images.test.js +59 -0
- package/dist/tools/__tests__/relationship.test.d.ts +1 -0
- package/dist/tools/__tests__/relationship.test.js +132 -0
- package/dist/tools/__tests__/resource-constraints.test.d.ts +1 -0
- package/dist/tools/__tests__/resource-constraints.test.js +131 -0
- package/dist/tools/__tests__/resource.test.d.ts +1 -0
- package/dist/tools/__tests__/resource.test.js +190 -0
- package/dist/tools/__tests__/time.test.d.ts +1 -0
- package/dist/tools/__tests__/time.test.js +404 -0
- package/dist/tools/__tests__/timers.test.d.ts +1 -0
- package/dist/tools/__tests__/timers.test.js +426 -0
- package/dist/tools/__tests__/world.test.d.ts +1 -0
- package/dist/tools/__tests__/world.test.js +70 -0
- package/dist/tools/ability.d.ts +48 -0
- package/dist/tools/ability.js +238 -0
- package/dist/tools/audio.d.ts +24 -0
- package/dist/tools/audio.js +365 -0
- package/dist/tools/character.d.ts +70 -0
- package/dist/tools/character.js +309 -0
- package/dist/tools/combat.d.ts +13 -0
- package/dist/tools/combat.js +195 -0
- package/dist/tools/constraint.d.ts +132 -0
- package/dist/tools/constraint.js +269 -0
- package/dist/tools/dice.d.ts +23 -0
- package/dist/tools/dice.js +111 -0
- package/dist/tools/display.d.ts +120 -0
- package/dist/tools/display.js +528 -0
- package/dist/tools/faction.d.ts +61 -0
- package/dist/tools/faction.js +269 -0
- package/dist/tools/game.d.ts +96 -0
- package/dist/tools/game.js +526 -0
- package/dist/tools/image-prompt.d.ts +49 -0
- package/dist/tools/image-prompt.js +479 -0
- package/dist/tools/images.d.ts +47 -0
- package/dist/tools/images.js +449 -0
- package/dist/tools/inventory.d.ts +20 -0
- package/dist/tools/inventory.js +145 -0
- package/dist/tools/narrative.d.ts +58 -0
- package/dist/tools/narrative.js +237 -0
- package/dist/tools/notes.d.ts +41 -0
- package/dist/tools/notes.js +220 -0
- package/dist/tools/pause.d.ts +110 -0
- package/dist/tools/pause.js +1254 -0
- package/dist/tools/quest.d.ts +34 -0
- package/dist/tools/quest.js +164 -0
- package/dist/tools/relationship.d.ts +74 -0
- package/dist/tools/relationship.js +324 -0
- package/dist/tools/resource.d.ts +93 -0
- package/dist/tools/resource.js +374 -0
- package/dist/tools/rules.d.ts +4 -0
- package/dist/tools/rules.js +30 -0
- package/dist/tools/secrets.d.ts +49 -0
- package/dist/tools/secrets.js +195 -0
- package/dist/tools/status.d.ts +36 -0
- package/dist/tools/status.js +218 -0
- package/dist/tools/tables.d.ts +33 -0
- package/dist/tools/tables.js +209 -0
- package/dist/tools/tags.d.ts +52 -0
- package/dist/tools/tags.js +176 -0
- package/dist/tools/time.d.ts +33 -0
- package/dist/tools/time.js +276 -0
- package/dist/tools/timers.d.ts +41 -0
- package/dist/tools/timers.js +215 -0
- package/dist/tools/world.d.ts +78 -0
- package/dist/tools/world.js +331 -0
- package/dist/types/index.d.ts +969 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/__tests__/json.test.d.ts +1 -0
- package/dist/utils/__tests__/json.test.js +55 -0
- package/dist/utils/__tests__/validation.test.d.ts +1 -0
- package/dist/utils/__tests__/validation.test.js +90 -0
- package/dist/utils/errors.d.ts +44 -0
- package/dist/utils/errors.js +121 -0
- package/dist/utils/json.d.ts +9 -0
- package/dist/utils/json.js +23 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.js +50 -0
- package/dist/utils/output-schemas.d.ts +594 -0
- package/dist/utils/output-schemas.js +331 -0
- package/dist/utils/tool-annotations.d.ts +147 -0
- package/dist/utils/tool-annotations.js +98 -0
- package/dist/utils/validation.d.ts +34 -0
- package/dist/utils/validation.js +52 -0
- package/dist/utils/verbosity.d.ts +57 -0
- package/dist/utils/verbosity.js +67 -0
- package/dist/utils/webui.d.ts +20 -0
- package/dist/utils/webui.js +35 -0
- package/package.json +75 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as secretTools from "../tools/secrets.js";
|
|
3
|
+
import { LIMITS } from "../utils/validation.js";
|
|
4
|
+
import { ANNOTATIONS } from "../utils/tool-annotations.js";
|
|
5
|
+
export function registerSecretTools(server) {
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// CREATE SECRET
|
|
8
|
+
// ============================================================================
|
|
9
|
+
server.registerTool("create_secret", {
|
|
10
|
+
description: "Create a secret that can be revealed to specific characters. Call this whenever you introduce plot-relevant hidden information - NPC backstories, hidden motives, location history, quest twists. Even if revealed immediately, secrets should be tracked.",
|
|
11
|
+
inputSchema: {
|
|
12
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
13
|
+
name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Secret name (for DM reference)"),
|
|
14
|
+
description: z.string().max(LIMITS.DESCRIPTION_MAX).describe("The actual secret information"),
|
|
15
|
+
category: z.string().max(100).optional().describe("Category (e.g., 'plot', 'character', 'location', 'item')"),
|
|
16
|
+
relatedEntityId: z.string().max(100).optional().describe("ID of related entity"),
|
|
17
|
+
relatedEntityType: z.string().max(100).optional().describe("Type of related entity"),
|
|
18
|
+
clues: z.array(z.string().max(LIMITS.DESCRIPTION_MAX)).max(LIMITS.ARRAY_MAX).optional().describe("Clues that hint at this secret"),
|
|
19
|
+
},
|
|
20
|
+
annotations: ANNOTATIONS.CREATE,
|
|
21
|
+
}, async (params) => {
|
|
22
|
+
const secret = secretTools.createSecret(params);
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: "text", text: JSON.stringify(secret, null, 2) }],
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// GET SECRET - read-only
|
|
29
|
+
// ============================================================================
|
|
30
|
+
server.registerTool("get_secret", {
|
|
31
|
+
description: "Get a secret by ID (DM view - shows all info)",
|
|
32
|
+
inputSchema: {
|
|
33
|
+
secretId: z.string().max(100).describe("The secret ID"),
|
|
34
|
+
},
|
|
35
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
36
|
+
}, async ({ secretId }) => {
|
|
37
|
+
const secret = secretTools.getSecret(secretId);
|
|
38
|
+
if (!secret) {
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: "text", text: "Secret not found" }],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: "text", text: JSON.stringify(secret, null, 2) }],
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// UPDATE SECRET
|
|
50
|
+
// ============================================================================
|
|
51
|
+
server.registerTool("update_secret", {
|
|
52
|
+
description: "Update a secret's details",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
secretId: z.string().max(100).describe("The secret ID"),
|
|
55
|
+
name: z.string().max(LIMITS.NAME_MAX).optional().describe("New name"),
|
|
56
|
+
description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("New description"),
|
|
57
|
+
category: z.string().max(100).nullable().optional().describe("New category"),
|
|
58
|
+
relatedEntityId: z.string().max(100).nullable().optional().describe("New related entity ID"),
|
|
59
|
+
relatedEntityType: z.string().max(100).nullable().optional().describe("New related entity type"),
|
|
60
|
+
},
|
|
61
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
62
|
+
}, async ({ secretId, ...updates }) => {
|
|
63
|
+
const secret = secretTools.updateSecret(secretId, updates);
|
|
64
|
+
if (!secret) {
|
|
65
|
+
return {
|
|
66
|
+
content: [{ type: "text", text: "Secret not found" }],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
content: [{ type: "text", text: JSON.stringify(secret, null, 2) }],
|
|
72
|
+
};
|
|
73
|
+
});
|
|
74
|
+
// ============================================================================
|
|
75
|
+
// DELETE SECRET - destructive
|
|
76
|
+
// ============================================================================
|
|
77
|
+
server.registerTool("delete_secret", {
|
|
78
|
+
description: "Delete a secret permanently. This is IRREVERSIBLE.",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
secretId: z.string().max(100).describe("The secret ID"),
|
|
81
|
+
},
|
|
82
|
+
annotations: ANNOTATIONS.DESTRUCTIVE,
|
|
83
|
+
}, async ({ secretId }) => {
|
|
84
|
+
const success = secretTools.deleteSecret(secretId);
|
|
85
|
+
return {
|
|
86
|
+
content: [{ type: "text", text: success ? "Secret deleted" : "Secret not found" }],
|
|
87
|
+
isError: !success,
|
|
88
|
+
};
|
|
89
|
+
});
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// LIST SECRETS - read-only
|
|
92
|
+
// ============================================================================
|
|
93
|
+
server.registerTool("list_secrets", {
|
|
94
|
+
description: "List secrets with optional filters",
|
|
95
|
+
inputSchema: {
|
|
96
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
97
|
+
category: z.string().max(100).optional().describe("Filter by category"),
|
|
98
|
+
relatedEntityId: z.string().max(100).optional().describe("Filter by related entity"),
|
|
99
|
+
isPublic: z.boolean().optional().describe("Filter by public status"),
|
|
100
|
+
knownBy: z.string().max(100).optional().describe("Only secrets known by this character ID"),
|
|
101
|
+
},
|
|
102
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
103
|
+
}, async ({ gameId, ...filter }) => {
|
|
104
|
+
const secrets = secretTools.listSecrets(gameId, filter);
|
|
105
|
+
return {
|
|
106
|
+
content: [{ type: "text", text: JSON.stringify(secrets, null, 2) }],
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// MODIFY SECRET VISIBILITY - CONSOLIDATED (replaces reveal_secret + make_secret_public)
|
|
111
|
+
// ============================================================================
|
|
112
|
+
server.registerTool("modify_secret_visibility", {
|
|
113
|
+
description: "Modify secret visibility. Reveal to specific characters and/or make public in a single call. Call this immediately when a character learns something - through dialogue, investigation, or discovery. Track what each character knows.",
|
|
114
|
+
inputSchema: {
|
|
115
|
+
secretId: z.string().max(100).describe("The secret ID"),
|
|
116
|
+
revealTo: z.array(z.string().max(100)).max(LIMITS.ARRAY_MAX).optional().describe("Character IDs to reveal to"),
|
|
117
|
+
makePublic: z.boolean().optional().describe("Set to true to make the secret public (revealed to everyone)"),
|
|
118
|
+
},
|
|
119
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
120
|
+
}, async ({ secretId, revealTo, makePublic }) => {
|
|
121
|
+
if (!revealTo?.length && !makePublic) {
|
|
122
|
+
return {
|
|
123
|
+
content: [{ type: "text", text: "No visibility changes specified (provide revealTo or makePublic)" }],
|
|
124
|
+
isError: true,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const result = secretTools.modifySecretVisibility(secretId, { revealTo, makePublic });
|
|
128
|
+
if (!result) {
|
|
129
|
+
return {
|
|
130
|
+
content: [{ type: "text", text: "Secret not found" }],
|
|
131
|
+
isError: true,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// ADD CLUE
|
|
140
|
+
// ============================================================================
|
|
141
|
+
server.registerTool("add_clue", {
|
|
142
|
+
description: "Add a clue to a secret",
|
|
143
|
+
inputSchema: {
|
|
144
|
+
secretId: z.string().max(100).describe("The secret ID"),
|
|
145
|
+
clue: z.string().max(LIMITS.DESCRIPTION_MAX).describe("The clue text"),
|
|
146
|
+
},
|
|
147
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
148
|
+
}, async ({ secretId, clue }) => {
|
|
149
|
+
const secret = secretTools.addClue(secretId, clue);
|
|
150
|
+
if (!secret) {
|
|
151
|
+
return {
|
|
152
|
+
content: [{ type: "text", text: "Secret not found" }],
|
|
153
|
+
isError: true,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
content: [{ type: "text", text: JSON.stringify(secret, null, 2) }],
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
// ============================================================================
|
|
161
|
+
// GET CHARACTER KNOWLEDGE - read-only
|
|
162
|
+
// ============================================================================
|
|
163
|
+
server.registerTool("get_character_knowledge", {
|
|
164
|
+
description: "Get all secrets and clues known by a character",
|
|
165
|
+
inputSchema: {
|
|
166
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
167
|
+
characterId: z.string().max(100).describe("The character ID"),
|
|
168
|
+
},
|
|
169
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
170
|
+
}, async ({ gameId, characterId }) => {
|
|
171
|
+
const knowledge = secretTools.getCharacterKnowledge(gameId, characterId);
|
|
172
|
+
return {
|
|
173
|
+
content: [{ type: "text", text: JSON.stringify(knowledge, null, 2) }],
|
|
174
|
+
};
|
|
175
|
+
});
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// CHECK KNOWS SECRET - read-only
|
|
178
|
+
// ============================================================================
|
|
179
|
+
server.registerTool("check_knows_secret", {
|
|
180
|
+
description: "Check if a character knows a specific secret",
|
|
181
|
+
inputSchema: {
|
|
182
|
+
secretId: z.string().max(100).describe("The secret ID"),
|
|
183
|
+
characterId: z.string().max(100).describe("The character ID"),
|
|
184
|
+
},
|
|
185
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
186
|
+
}, async ({ secretId, characterId }) => {
|
|
187
|
+
const knows = secretTools.checkKnowsSecret(secretId, characterId);
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: "text", text: JSON.stringify({ knows }, null, 2) }],
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as statusTools from "../tools/status.js";
|
|
3
|
+
import { LIMITS } from "../utils/validation.js";
|
|
4
|
+
import { ANNOTATIONS } from "../utils/tool-annotations.js";
|
|
5
|
+
export function registerStatusTools(server) {
|
|
6
|
+
server.registerTool("apply_status_effect", {
|
|
7
|
+
description: "Apply a status effect to a character (handles stacking automatically)",
|
|
8
|
+
inputSchema: {
|
|
9
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
10
|
+
targetId: z.string().max(100).describe("Character ID to apply effect to"),
|
|
11
|
+
name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Effect name (e.g., 'Poisoned', 'Blessed', 'Stunned')"),
|
|
12
|
+
description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("Description of the effect"),
|
|
13
|
+
effectType: z.enum(["buff", "debuff", "neutral"]).optional().describe("Effect category"),
|
|
14
|
+
duration: z.number().optional().describe("Duration in rounds (null for permanent)"),
|
|
15
|
+
stacks: z.number().optional().describe("Initial stack count (default: 1)"),
|
|
16
|
+
maxStacks: z.number().optional().describe("Maximum stacks allowed"),
|
|
17
|
+
effects: z.record(z.number()).optional().describe("Stat modifiers (e.g., {strength: -2, speed: +1})"),
|
|
18
|
+
sourceId: z.string().max(100).optional().describe("ID of the source (character, ability, item)"),
|
|
19
|
+
sourceType: z.string().max(100).optional().describe("Type of the source"),
|
|
20
|
+
},
|
|
21
|
+
annotations: ANNOTATIONS.CREATE,
|
|
22
|
+
}, async (params) => {
|
|
23
|
+
const effect = statusTools.applyStatusEffect(params);
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: JSON.stringify(effect, null, 2) }],
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
server.registerTool("get_status_effect", {
|
|
29
|
+
description: "Get a status effect by ID",
|
|
30
|
+
inputSchema: {
|
|
31
|
+
effectId: z.string().max(100).describe("The effect ID"),
|
|
32
|
+
},
|
|
33
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
34
|
+
}, async ({ effectId }) => {
|
|
35
|
+
const effect = statusTools.getStatusEffect(effectId);
|
|
36
|
+
if (!effect) {
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: "Effect not found" }],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: "text", text: JSON.stringify(effect, null, 2) }],
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
server.registerTool("remove_status_effect", {
|
|
47
|
+
description: "Remove a specific status effect",
|
|
48
|
+
inputSchema: {
|
|
49
|
+
effectId: z.string().max(100).describe("The effect ID"),
|
|
50
|
+
},
|
|
51
|
+
annotations: ANNOTATIONS.DESTRUCTIVE,
|
|
52
|
+
}, async ({ effectId }) => {
|
|
53
|
+
const success = statusTools.removeStatusEffect(effectId);
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: success ? "Effect removed" : "Effect not found" }],
|
|
56
|
+
isError: !success,
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
server.registerTool("list_status_effects", {
|
|
60
|
+
description: "List all status effects on a character",
|
|
61
|
+
inputSchema: {
|
|
62
|
+
targetId: z.string().max(100).describe("Character ID"),
|
|
63
|
+
effectType: z.enum(["buff", "debuff", "neutral"]).optional().describe("Filter by effect type"),
|
|
64
|
+
},
|
|
65
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
66
|
+
}, async ({ targetId, effectType }) => {
|
|
67
|
+
const effects = statusTools.listStatusEffects(targetId, effectType ? { effectType } : undefined);
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: JSON.stringify(effects, null, 2) }],
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
server.registerTool("tick_status_durations", {
|
|
73
|
+
description: "Reduce duration of all status effects (call at end of round). Returns expired and remaining effects.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
76
|
+
amount: z.number().optional().describe("Rounds to tick (default: 1)"),
|
|
77
|
+
},
|
|
78
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
79
|
+
}, async ({ gameId, amount }) => {
|
|
80
|
+
const result = statusTools.tickDurations(gameId, amount);
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
server.registerTool("modify_effect_stacks", {
|
|
86
|
+
description: "Add or remove stacks from a status effect",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
effectId: z.string().max(100).describe("The effect ID"),
|
|
89
|
+
delta: z.number().describe("Change in stacks (positive or negative)"),
|
|
90
|
+
},
|
|
91
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
92
|
+
}, async ({ effectId, delta }) => {
|
|
93
|
+
const effect = statusTools.modifyStacks(effectId, delta);
|
|
94
|
+
if (!effect) {
|
|
95
|
+
return {
|
|
96
|
+
content: [{ type: "text", text: "Effect not found" }],
|
|
97
|
+
isError: true,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
content: [{ type: "text", text: JSON.stringify(effect, null, 2) }],
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
server.registerTool("clear_status_effects", {
|
|
105
|
+
description: "Remove all status effects from a character (or filter by type/name)",
|
|
106
|
+
inputSchema: {
|
|
107
|
+
targetId: z.string().max(100).describe("Character ID"),
|
|
108
|
+
effectType: z.enum(["buff", "debuff", "neutral"]).optional().describe("Only clear this type"),
|
|
109
|
+
name: z.string().max(LIMITS.NAME_MAX).optional().describe("Only clear effects with this name"),
|
|
110
|
+
},
|
|
111
|
+
annotations: ANNOTATIONS.DESTRUCTIVE,
|
|
112
|
+
}, async ({ targetId, effectType, name }) => {
|
|
113
|
+
const count = statusTools.clearEffects(targetId, { effectType, name });
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text", text: `Cleared ${count} effect(s)` }],
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
server.registerTool("get_effective_modifiers", {
|
|
119
|
+
description: "Get the total stat modifiers from all status effects on a character",
|
|
120
|
+
inputSchema: {
|
|
121
|
+
targetId: z.string().max(100).describe("Character ID"),
|
|
122
|
+
},
|
|
123
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
124
|
+
}, async ({ targetId }) => {
|
|
125
|
+
const modifiers = statusTools.getEffectiveModifiers(targetId);
|
|
126
|
+
return {
|
|
127
|
+
content: [{ type: "text", text: JSON.stringify(modifiers, null, 2) }],
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as tableTools from "../tools/tables.js";
|
|
3
|
+
import { LIMITS } from "../utils/validation.js";
|
|
4
|
+
import { ANNOTATIONS } from "../utils/tool-annotations.js";
|
|
5
|
+
const tableEntrySchema = z.object({
|
|
6
|
+
minRoll: z.number().describe("Minimum roll to get this result"),
|
|
7
|
+
maxRoll: z.number().describe("Maximum roll to get this result"),
|
|
8
|
+
result: z.string().max(LIMITS.DESCRIPTION_MAX).describe("The result text"),
|
|
9
|
+
weight: z.number().optional().describe("Weight for weighted random selection"),
|
|
10
|
+
subtable: z.string().max(100).optional().describe("ID of subtable to roll on"),
|
|
11
|
+
metadata: z.record(z.string(), z.unknown()).optional().describe("Additional data"),
|
|
12
|
+
});
|
|
13
|
+
export function registerTableTools(server) {
|
|
14
|
+
server.registerTool("create_random_table", {
|
|
15
|
+
description: "Create a new random table for encounters, loot, weather, etc.",
|
|
16
|
+
inputSchema: {
|
|
17
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
18
|
+
name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Table name"),
|
|
19
|
+
description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("Table description"),
|
|
20
|
+
category: z.string().max(100).optional().describe("Category (e.g., 'encounter', 'loot', 'weather', 'name')"),
|
|
21
|
+
entries: z.array(tableEntrySchema).max(LIMITS.ARRAY_MAX).optional().describe("Table entries"),
|
|
22
|
+
rollExpression: z.string().max(100).optional().describe("Dice expression (default: '1d100')"),
|
|
23
|
+
},
|
|
24
|
+
annotations: ANNOTATIONS.CREATE,
|
|
25
|
+
}, async (params) => {
|
|
26
|
+
const table = tableTools.createTable(params);
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: JSON.stringify(table, null, 2) }],
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
server.registerTool("get_random_table", {
|
|
32
|
+
description: "Get a random table by ID",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
tableId: z.string().max(100).describe("The table ID"),
|
|
35
|
+
},
|
|
36
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
37
|
+
}, async ({ tableId }) => {
|
|
38
|
+
const table = tableTools.getTable(tableId);
|
|
39
|
+
if (!table) {
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: "text", text: "Table not found" }],
|
|
42
|
+
isError: true,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: "text", text: JSON.stringify(table, null, 2) }],
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
server.registerTool("update_random_table", {
|
|
50
|
+
description: "Update a random table",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
tableId: z.string().max(100).describe("The table ID"),
|
|
53
|
+
name: z.string().max(LIMITS.NAME_MAX).optional().describe("New name"),
|
|
54
|
+
description: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("New description"),
|
|
55
|
+
category: z.string().max(100).nullable().optional().describe("New category"),
|
|
56
|
+
entries: z.array(tableEntrySchema).max(LIMITS.ARRAY_MAX).optional().describe("Replace all entries"),
|
|
57
|
+
rollExpression: z.string().max(100).optional().describe("New dice expression"),
|
|
58
|
+
},
|
|
59
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
60
|
+
}, async ({ tableId, ...updates }) => {
|
|
61
|
+
const table = tableTools.updateTable(tableId, updates);
|
|
62
|
+
if (!table) {
|
|
63
|
+
return {
|
|
64
|
+
content: [{ type: "text", text: "Table not found" }],
|
|
65
|
+
isError: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
content: [{ type: "text", text: JSON.stringify(table, null, 2) }],
|
|
70
|
+
};
|
|
71
|
+
});
|
|
72
|
+
server.registerTool("delete_random_table", {
|
|
73
|
+
description: "Delete a random table",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
tableId: z.string().max(100).describe("The table ID"),
|
|
76
|
+
},
|
|
77
|
+
annotations: ANNOTATIONS.DESTRUCTIVE,
|
|
78
|
+
}, async ({ tableId }) => {
|
|
79
|
+
const success = tableTools.deleteTable(tableId);
|
|
80
|
+
return {
|
|
81
|
+
content: [{ type: "text", text: success ? "Table deleted" : "Table not found" }],
|
|
82
|
+
isError: !success,
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
server.registerTool("list_random_tables", {
|
|
86
|
+
description: "List random tables in a game",
|
|
87
|
+
inputSchema: {
|
|
88
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
89
|
+
category: z.string().max(100).optional().describe("Filter by category"),
|
|
90
|
+
},
|
|
91
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
92
|
+
}, async ({ gameId, category }) => {
|
|
93
|
+
const tables = tableTools.listTables(gameId, category);
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: "text", text: JSON.stringify(tables, null, 2) }],
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
server.registerTool("roll_on_table", {
|
|
99
|
+
description: "Roll on a random table and get a result",
|
|
100
|
+
inputSchema: {
|
|
101
|
+
tableId: z.string().max(100).describe("The table ID"),
|
|
102
|
+
modifier: z.number().optional().describe("Modifier to add to the roll"),
|
|
103
|
+
},
|
|
104
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
105
|
+
}, async ({ tableId, modifier }) => {
|
|
106
|
+
const result = tableTools.rollTable(tableId, modifier);
|
|
107
|
+
if (!result) {
|
|
108
|
+
return {
|
|
109
|
+
content: [{ type: "text", text: "Table not found or has no entries" }],
|
|
110
|
+
isError: true,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
// ============================================================================
|
|
118
|
+
// MODIFY TABLE ENTRIES - CONSOLIDATED (replaces add_table_entry + remove_table_entry)
|
|
119
|
+
// ============================================================================
|
|
120
|
+
server.registerTool("modify_table_entries", {
|
|
121
|
+
description: "Add and/or remove table entries in a single call. More efficient than separate add/remove calls.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
tableId: z.string().max(100).describe("The table ID"),
|
|
124
|
+
add: z.array(tableEntrySchema).max(LIMITS.ARRAY_MAX).optional().describe("Entries to add"),
|
|
125
|
+
remove: z.array(z.number()).max(LIMITS.ARRAY_MAX).optional().describe("Indices of entries to remove (0-based)"),
|
|
126
|
+
},
|
|
127
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
128
|
+
}, async ({ tableId, add, remove }) => {
|
|
129
|
+
if (!add?.length && !remove?.length) {
|
|
130
|
+
return {
|
|
131
|
+
content: [{ type: "text", text: "No entries to add or remove" }],
|
|
132
|
+
isError: true,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const result = tableTools.modifyTableEntries(tableId, { add, remove });
|
|
136
|
+
if (!result) {
|
|
137
|
+
return {
|
|
138
|
+
content: [{ type: "text", text: "Table not found" }],
|
|
139
|
+
isError: true,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
144
|
+
};
|
|
145
|
+
});
|
|
146
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import * as tagTools from "../tools/tags.js";
|
|
3
|
+
import { LIMITS } from "../utils/validation.js";
|
|
4
|
+
import { ANNOTATIONS } from "../utils/tool-annotations.js";
|
|
5
|
+
import { tagModifyOutputSchema } from "../utils/output-schemas.js";
|
|
6
|
+
export function registerTagTools(server) {
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// CONSOLIDATED: MODIFY TAGS (add and/or remove in a single call)
|
|
9
|
+
// ============================================================================
|
|
10
|
+
server.registerTool("modify_tags", {
|
|
11
|
+
description: "Add and/or remove tags from an entity in a single call. More efficient than separate add/remove calls.",
|
|
12
|
+
inputSchema: {
|
|
13
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
14
|
+
entityId: z.string().max(100).describe("The entity ID to modify tags on"),
|
|
15
|
+
entityType: z.string().max(100).describe("Type of entity (e.g., 'character', 'location', 'item', 'quest')"),
|
|
16
|
+
add: z.array(z.object({
|
|
17
|
+
tag: z.string().min(1).max(LIMITS.NAME_MAX),
|
|
18
|
+
color: z.string().max(50).optional(),
|
|
19
|
+
notes: z.string().max(LIMITS.DESCRIPTION_MAX).optional(),
|
|
20
|
+
})).max(LIMITS.ARRAY_MAX).optional().describe("Tags to add with optional color/notes"),
|
|
21
|
+
remove: z.array(z.string().max(LIMITS.NAME_MAX)).max(LIMITS.ARRAY_MAX).optional().describe("Tags to remove"),
|
|
22
|
+
},
|
|
23
|
+
outputSchema: tagModifyOutputSchema,
|
|
24
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
25
|
+
}, async ({ gameId, entityId, entityType, add, remove }) => {
|
|
26
|
+
if (!add?.length && !remove?.length) {
|
|
27
|
+
return {
|
|
28
|
+
content: [{ type: "text", text: "No tags to add or remove" }],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const result = tagTools.modifyTags({ gameId, entityId, entityType, add, remove });
|
|
33
|
+
const output = {
|
|
34
|
+
entityId: result.entityId,
|
|
35
|
+
entityType: result.entityType,
|
|
36
|
+
tags: result.tags,
|
|
37
|
+
action: result.added.length > 0 && result.removed.length > 0
|
|
38
|
+
? "modified"
|
|
39
|
+
: result.added.length > 0
|
|
40
|
+
? "added"
|
|
41
|
+
: "removed",
|
|
42
|
+
tag: result.added[0] || result.removed[0] || "",
|
|
43
|
+
};
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
46
|
+
structuredContent: output,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// LIST TAGS - read-only
|
|
51
|
+
// ============================================================================
|
|
52
|
+
server.registerTool("list_tags", {
|
|
53
|
+
description: "List all unique tags in a game with counts",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
56
|
+
},
|
|
57
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
58
|
+
}, async ({ gameId }) => {
|
|
59
|
+
const tags = tagTools.listTags(gameId);
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: JSON.stringify(tags, null, 2) }],
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// GET ENTITY TAGS - read-only
|
|
66
|
+
// ============================================================================
|
|
67
|
+
server.registerTool("get_entity_tags", {
|
|
68
|
+
description: "Get all tags for a specific entity",
|
|
69
|
+
inputSchema: {
|
|
70
|
+
entityId: z.string().max(100).describe("The entity ID"),
|
|
71
|
+
entityType: z.string().max(100).describe("Type of entity"),
|
|
72
|
+
},
|
|
73
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
74
|
+
}, async ({ entityId, entityType }) => {
|
|
75
|
+
const tags = tagTools.getEntityTags(entityId, entityType);
|
|
76
|
+
return {
|
|
77
|
+
content: [{ type: "text", text: JSON.stringify(tags, null, 2) }],
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// FIND BY TAG - read-only
|
|
82
|
+
// ============================================================================
|
|
83
|
+
server.registerTool("find_by_tag", {
|
|
84
|
+
description: "Find all entities with a specific tag",
|
|
85
|
+
inputSchema: {
|
|
86
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
87
|
+
tag: z.string().max(LIMITS.NAME_MAX).describe("The tag to search for"),
|
|
88
|
+
entityType: z.string().max(100).optional().describe("Filter by entity type"),
|
|
89
|
+
},
|
|
90
|
+
annotations: ANNOTATIONS.READ_ONLY,
|
|
91
|
+
}, async ({ gameId, tag, entityType }) => {
|
|
92
|
+
const entities = tagTools.findByTag(gameId, tag, entityType);
|
|
93
|
+
return {
|
|
94
|
+
content: [{ type: "text", text: JSON.stringify(entities, null, 2) }],
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// RENAME TAG - update
|
|
99
|
+
// ============================================================================
|
|
100
|
+
server.registerTool("rename_tag", {
|
|
101
|
+
description: "Rename a tag across all entities in a game",
|
|
102
|
+
inputSchema: {
|
|
103
|
+
gameId: z.string().max(100).describe("The game ID"),
|
|
104
|
+
oldTag: z.string().max(LIMITS.NAME_MAX).describe("The current tag name"),
|
|
105
|
+
newTag: z.string().min(1).max(LIMITS.NAME_MAX).describe("The new tag name"),
|
|
106
|
+
},
|
|
107
|
+
annotations: ANNOTATIONS.UPDATE,
|
|
108
|
+
}, async ({ gameId, oldTag, newTag }) => {
|
|
109
|
+
const count = tagTools.renameTag(gameId, oldTag, newTag);
|
|
110
|
+
return {
|
|
111
|
+
content: [{ type: "text", text: `Renamed ${count} tag(s)` }],
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|