run-dmcp 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +51 -13
  2. package/dist/bin/run-dmcp.js +1 -1
  3. package/dist/db/schema.js +170 -18
  4. package/dist/http/server.js +22 -1
  5. package/dist/index.d.ts +34 -2
  6. package/dist/index.js +86 -2
  7. package/dist/mcp-server.d.ts +1 -1
  8. package/dist/mcp-server.js +1 -1
  9. package/dist/register/resources.js +2 -2
  10. package/dist/rpg/index.d.ts +0 -16
  11. package/dist/rpg/index.js +4 -22
  12. package/dist/rpg/server.d.ts +16 -0
  13. package/dist/rpg/server.js +22 -0
  14. package/dist/schemas/index.d.ts +62 -62
  15. package/dist/server.d.ts +1 -0
  16. package/dist/server.js +20 -0
  17. package/dist/timeline/changes.d.ts +8 -0
  18. package/dist/timeline/changes.js +8 -0
  19. package/dist/timeline/export.d.ts +10 -0
  20. package/dist/timeline/export.js +10 -0
  21. package/dist/timeline/irreversible.d.ts +2 -0
  22. package/dist/timeline/replay.d.ts +22 -0
  23. package/dist/timeline/replay.js +22 -0
  24. package/dist/timeline/schema.js +2 -0
  25. package/dist/tools/audio.js +13 -9
  26. package/dist/tools/game.js +33 -1
  27. package/dist/tools/images.js +17 -10
  28. package/dist/tools/resource.d.ts +2 -2
  29. package/dist/tools/time.js +18 -3
  30. package/dist/types/index.d.ts +1 -1
  31. package/dist/utils/media-path.d.ts +52 -0
  32. package/dist/utils/media-path.js +106 -0
  33. package/dist/utils/output-schemas.d.ts +63 -63
  34. package/dist/utils/output-schemas.js +1 -1
  35. package/package.json +14 -2
  36. package/dist/register/abilities.d.ts +0 -2
  37. package/dist/register/abilities.js +0 -165
  38. package/dist/register/combat.d.ts +0 -2
  39. package/dist/register/combat.js +0 -207
  40. package/dist/register/mcp-prompts.d.ts +0 -2
  41. package/dist/register/mcp-prompts.js +0 -684
  42. package/dist/register/quests.d.ts +0 -2
  43. package/dist/register/quests.js +0 -118
  44. package/dist/register/status.d.ts +0 -2
  45. package/dist/register/status.js +0 -130
  46. package/dist/register/tables.d.ts +0 -2
  47. package/dist/register/tables.js +0 -146
  48. package/dist/tools/ability.d.ts +0 -48
  49. package/dist/tools/ability.js +0 -238
  50. package/dist/tools/combat.d.ts +0 -13
  51. package/dist/tools/combat.js +0 -195
  52. package/dist/tools/dice.d.ts +0 -23
  53. package/dist/tools/dice.js +0 -111
  54. package/dist/tools/quest.d.ts +0 -34
  55. package/dist/tools/quest.js +0 -164
  56. package/dist/tools/status.d.ts +0 -36
  57. package/dist/tools/status.js +0 -218
  58. package/dist/tools/tables.d.ts +0 -33
  59. package/dist/tools/tables.js +0 -209
@@ -1,2 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerQuestTools(server: McpServer): void;
@@ -1,118 +0,0 @@
1
- import { z } from "zod";
2
- import * as questTools from "../tools/quest.js";
3
- import { LIMITS } from "../utils/validation.js";
4
- import { ANNOTATIONS } from "../utils/tool-annotations.js";
5
- export function registerQuestTools(server) {
6
- server.registerTool("create_quest", {
7
- description: "Create a new quest. Call this whenever the player receives a mission, task, or goal - whether from an NPC, discovered through exploration, or self-initiated. Include clear objectives that can be tracked.",
8
- inputSchema: {
9
- gameId: z.string().max(100).describe("The game ID"),
10
- name: z.string().min(1).max(LIMITS.NAME_MAX).describe("Quest name"),
11
- description: z.string().max(LIMITS.DESCRIPTION_MAX).describe("Quest description"),
12
- objectives: z.array(z.object({
13
- description: z.string().max(LIMITS.DESCRIPTION_MAX),
14
- completed: z.boolean().optional(),
15
- optional: z.boolean().optional(),
16
- })).max(LIMITS.ARRAY_MAX).describe("Quest objectives"),
17
- rewards: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("Quest rewards"),
18
- },
19
- annotations: ANNOTATIONS.CREATE,
20
- }, async (params) => {
21
- const quest = questTools.createQuest({
22
- ...params,
23
- objectives: params.objectives.map((obj) => ({
24
- description: obj.description,
25
- completed: obj.completed ?? false,
26
- optional: obj.optional,
27
- })),
28
- });
29
- return {
30
- content: [{ type: "text", text: JSON.stringify(quest, null, 2) }],
31
- };
32
- });
33
- server.registerTool("get_quest", {
34
- description: "Get quest details",
35
- inputSchema: {
36
- questId: z.string().max(100).describe("The quest ID"),
37
- },
38
- annotations: ANNOTATIONS.READ_ONLY,
39
- }, async ({ questId }) => {
40
- const quest = questTools.getQuest(questId);
41
- if (!quest) {
42
- return {
43
- content: [{ type: "text", text: "Quest not found" }],
44
- isError: true,
45
- };
46
- }
47
- return {
48
- content: [{ type: "text", text: JSON.stringify(quest, null, 2) }],
49
- };
50
- });
51
- server.registerTool("update_quest", {
52
- description: "Update a quest",
53
- inputSchema: {
54
- questId: z.string().max(100).describe("The quest 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
- status: z.enum(["active", "completed", "failed", "abandoned"]).optional().describe("New status"),
58
- rewards: z.string().max(LIMITS.DESCRIPTION_MAX).optional().describe("Updated rewards"),
59
- },
60
- annotations: ANNOTATIONS.UPDATE,
61
- }, async ({ questId, ...updates }) => {
62
- const quest = questTools.updateQuest(questId, updates);
63
- if (!quest) {
64
- return {
65
- content: [{ type: "text", text: "Quest not found" }],
66
- isError: true,
67
- };
68
- }
69
- return {
70
- content: [{ type: "text", text: JSON.stringify(quest, null, 2) }],
71
- };
72
- });
73
- // ============================================================================
74
- // MODIFY OBJECTIVES - CONSOLIDATED (replaces add_objective + complete_objective)
75
- // ============================================================================
76
- server.registerTool("modify_objectives", {
77
- description: "Add new objectives and/or mark existing objectives as completed in a single call. Call this immediately when: (1) player completes a quest step, (2) new sub-tasks are discovered, (3) objectives change based on player choices.",
78
- inputSchema: {
79
- questId: z.string().max(100).describe("The quest ID"),
80
- add: z.array(z.object({
81
- description: z.string().max(LIMITS.DESCRIPTION_MAX).describe("Objective description"),
82
- optional: z.boolean().optional().describe("Is this objective optional?"),
83
- })).max(LIMITS.ARRAY_MAX).optional().describe("New objectives to add"),
84
- complete: z.array(z.string().max(100)).max(LIMITS.ARRAY_MAX).optional().describe("Objective IDs to mark as completed"),
85
- },
86
- annotations: ANNOTATIONS.UPDATE,
87
- }, async ({ questId, add, complete }) => {
88
- if (!add?.length && !complete?.length) {
89
- return {
90
- content: [{ type: "text", text: "No objectives to add or complete" }],
91
- isError: true,
92
- };
93
- }
94
- const result = questTools.modifyObjectives(questId, { add, complete });
95
- if (!result) {
96
- return {
97
- content: [{ type: "text", text: "Quest not found" }],
98
- isError: true,
99
- };
100
- }
101
- return {
102
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
103
- };
104
- });
105
- server.registerTool("list_quests", {
106
- description: "List quests in a game",
107
- inputSchema: {
108
- gameId: z.string().max(100).describe("The game ID"),
109
- status: z.enum(["active", "completed", "failed", "abandoned"]).optional().describe("Filter by status"),
110
- },
111
- annotations: ANNOTATIONS.READ_ONLY,
112
- }, async ({ gameId, status }) => {
113
- const quests = questTools.listQuests(gameId, status ? { status } : undefined);
114
- return {
115
- content: [{ type: "text", text: JSON.stringify(quests, null, 2) }],
116
- };
117
- });
118
- }
@@ -1,2 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerStatusTools(server: McpServer): void;
@@ -1,130 +0,0 @@
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
- }
@@ -1,2 +0,0 @@
1
- import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
- export declare function registerTableTools(server: McpServer): void;
@@ -1,146 +0,0 @@
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
- }
@@ -1,48 +0,0 @@
1
- import type { Ability } from "../types/index.js";
2
- export declare function createAbility(params: {
3
- gameId: string;
4
- ownerType: "template" | "character";
5
- ownerId?: string;
6
- name: string;
7
- description?: string;
8
- category?: string;
9
- cost?: Record<string, number>;
10
- cooldown?: number;
11
- effects?: string[];
12
- requirements?: Record<string, number>;
13
- tags?: string[];
14
- }): Ability;
15
- export declare function getAbility(id: string): Ability | null;
16
- export declare function updateAbility(id: string, updates: {
17
- name?: string;
18
- description?: string;
19
- category?: string | null;
20
- cost?: Record<string, number>;
21
- cooldown?: number | null;
22
- effects?: string[];
23
- requirements?: Record<string, number>;
24
- tags?: string[];
25
- }): Ability | null;
26
- export declare function deleteAbility(id: string): boolean;
27
- export declare function listAbilities(gameId: string, filter?: {
28
- ownerType?: "template" | "character";
29
- ownerId?: string;
30
- category?: string;
31
- }): Ability[];
32
- export declare function learnAbility(templateId: string, characterId: string): Ability | null;
33
- export interface UseAbilityResult {
34
- success: boolean;
35
- ability: Ability;
36
- reason?: string;
37
- costsPaid?: Record<string, number>;
38
- }
39
- export declare function useAbility(abilityId: string, _characterId: string): UseAbilityResult;
40
- export declare function tickCooldowns(gameId: string, amount?: number): Ability[];
41
- export interface RequirementCheck {
42
- meetsRequirements: boolean;
43
- missing: Record<string, {
44
- required: number;
45
- actual: number;
46
- }>;
47
- }
48
- export declare function checkRequirements(abilityId: string, characterId: string): RequirementCheck | null;