warhammer-oracle 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 (78) hide show
  1. package/README.md +147 -0
  2. package/dist/data/keywords.d.ts +3 -0
  3. package/dist/data/keywords.d.ts.map +1 -0
  4. package/dist/data/keywords.js +193 -0
  5. package/dist/data/keywords.js.map +1 -0
  6. package/dist/data/kill-team-operatives.d.ts +3 -0
  7. package/dist/data/kill-team-operatives.d.ts.map +1 -0
  8. package/dist/data/kill-team-operatives.js +24956 -0
  9. package/dist/data/kill-team-operatives.js.map +1 -0
  10. package/dist/data/kill-team-rules.d.ts +5 -0
  11. package/dist/data/kill-team-rules.d.ts.map +1 -0
  12. package/dist/data/kill-team-rules.js +94 -0
  13. package/dist/data/kill-team-rules.js.map +1 -0
  14. package/dist/data/phases.d.ts +3 -0
  15. package/dist/data/phases.d.ts.map +1 -0
  16. package/dist/data/phases.js +232 -0
  17. package/dist/data/phases.js.map +1 -0
  18. package/dist/data/rules.d.ts +5 -0
  19. package/dist/data/rules.d.ts.map +1 -0
  20. package/dist/data/rules.js +138 -0
  21. package/dist/data/rules.js.map +1 -0
  22. package/dist/data/units.d.ts +3 -0
  23. package/dist/data/units.d.ts.map +1 -0
  24. package/dist/data/units.js +222240 -0
  25. package/dist/data/units.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +8 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/lib/search.d.ts +2 -0
  31. package/dist/lib/search.d.ts.map +1 -0
  32. package/dist/lib/search.js +14 -0
  33. package/dist/lib/search.js.map +1 -0
  34. package/dist/lib/xml-parser.d.ts +28 -0
  35. package/dist/lib/xml-parser.d.ts.map +1 -0
  36. package/dist/lib/xml-parser.js +411 -0
  37. package/dist/lib/xml-parser.js.map +1 -0
  38. package/dist/lib/xml-parser.test.d.ts +2 -0
  39. package/dist/lib/xml-parser.test.d.ts.map +1 -0
  40. package/dist/lib/xml-parser.test.js +202 -0
  41. package/dist/lib/xml-parser.test.js.map +1 -0
  42. package/dist/register-tools.d.ts +3 -0
  43. package/dist/register-tools.d.ts.map +1 -0
  44. package/dist/register-tools.js +15 -0
  45. package/dist/register-tools.js.map +1 -0
  46. package/dist/server.d.ts +3 -0
  47. package/dist/server.d.ts.map +1 -0
  48. package/dist/server.js +11 -0
  49. package/dist/server.js.map +1 -0
  50. package/dist/tools/compare-units.d.ts +3 -0
  51. package/dist/tools/compare-units.d.ts.map +1 -0
  52. package/dist/tools/compare-units.js +191 -0
  53. package/dist/tools/compare-units.js.map +1 -0
  54. package/dist/tools/game-flow.d.ts +3 -0
  55. package/dist/tools/game-flow.d.ts.map +1 -0
  56. package/dist/tools/game-flow.js +119 -0
  57. package/dist/tools/game-flow.js.map +1 -0
  58. package/dist/tools/lookup-keyword.d.ts +3 -0
  59. package/dist/tools/lookup-keyword.d.ts.map +1 -0
  60. package/dist/tools/lookup-keyword.js +84 -0
  61. package/dist/tools/lookup-keyword.js.map +1 -0
  62. package/dist/tools/lookup-phase.d.ts +3 -0
  63. package/dist/tools/lookup-phase.d.ts.map +1 -0
  64. package/dist/tools/lookup-phase.js +74 -0
  65. package/dist/tools/lookup-phase.js.map +1 -0
  66. package/dist/tools/lookup-unit.d.ts +3 -0
  67. package/dist/tools/lookup-unit.d.ts.map +1 -0
  68. package/dist/tools/lookup-unit.js +175 -0
  69. package/dist/tools/lookup-unit.js.map +1 -0
  70. package/dist/tools/search-units.d.ts +3 -0
  71. package/dist/tools/search-units.d.ts.map +1 -0
  72. package/dist/tools/search-units.js +108 -0
  73. package/dist/tools/search-units.js.map +1 -0
  74. package/dist/types.d.ts +92 -0
  75. package/dist/types.d.ts.map +1 -0
  76. package/dist/types.js +3 -0
  77. package/dist/types.js.map +1 -0
  78. package/package.json +39 -0
@@ -0,0 +1,84 @@
1
+ import { z } from "zod";
2
+ import { KEYWORDS } from "../data/keywords.js";
3
+ import { SHARED_RULES } from "../data/rules.js";
4
+ import { fuzzySearch } from "../lib/search.js";
5
+ function formatCuratedKeyword(kw) {
6
+ const sections = [];
7
+ sections.push(`# ${kw.name}`);
8
+ sections.push(`**Game Modes:** ${kw.gameModes.join(", ")}`);
9
+ sections.push(`## Official Definition\n\n${kw.description}`);
10
+ sections.push(`## Plain English\n\n${kw.plainEnglish}`);
11
+ if (kw.examples && kw.examples.length > 0) {
12
+ const exampleLines = kw.examples.map((e) => `- ${e}`);
13
+ sections.push(`## Examples\n\n${exampleLines.join("\n")}`);
14
+ }
15
+ return sections.join("\n\n");
16
+ }
17
+ function formatSharedRule(rule) {
18
+ const sections = [];
19
+ sections.push(`# ${rule.name}`);
20
+ sections.push(`*Source: rules data (BSData). A curated plain-English explanation is not yet available for this keyword.*`);
21
+ sections.push(`## Definition\n\n${rule.description}`);
22
+ return sections.join("\n\n");
23
+ }
24
+ export function registerLookupKeyword(server) {
25
+ server.tool("lookup_keyword", "Look up a Warhammer 40K keyword or rule. Returns the official definition, a plain English explanation, examples, and applicable game modes.", {
26
+ keyword: z
27
+ .string()
28
+ .describe("Name or partial name of the keyword to look up (e.g. 'Devastating Wounds', 'Feel No Pain')"),
29
+ game_mode: z
30
+ .enum(["40k", "combat_patrol", "kill_team"])
31
+ .optional()
32
+ .describe("Optional game mode filter — only show keywords applicable to this mode"),
33
+ }, async ({ keyword, game_mode }) => {
34
+ // 1. Search curated KEYWORDS first (fuzzy match on name)
35
+ const allCuratedMatches = fuzzySearch(KEYWORDS, keyword, ["name"]);
36
+ if (allCuratedMatches.length > 0) {
37
+ // Apply game_mode filter if provided
38
+ const filtered = game_mode
39
+ ? allCuratedMatches.filter((kw) => kw.gameModes.includes(game_mode))
40
+ : allCuratedMatches;
41
+ if (filtered.length > 0) {
42
+ return {
43
+ content: [
44
+ {
45
+ type: "text",
46
+ text: formatCuratedKeyword(filtered[0]),
47
+ },
48
+ ],
49
+ };
50
+ }
51
+ // Keyword exists in curated list but not for this game mode
52
+ return {
53
+ content: [
54
+ {
55
+ type: "text",
56
+ text: `Keyword "${keyword}" was found but is not applicable to ${game_mode}.\n\nIt applies to: ${allCuratedMatches[0].gameModes.join(", ")}.`,
57
+ },
58
+ ],
59
+ };
60
+ }
61
+ // 2. Fall back to SHARED_RULES (no game_mode filter — rules data doesn't have mode info)
62
+ const ruleMatches = fuzzySearch(SHARED_RULES, keyword, ["name"]);
63
+ if (ruleMatches.length > 0) {
64
+ return {
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: formatSharedRule(ruleMatches[0]),
69
+ },
70
+ ],
71
+ };
72
+ }
73
+ // 3. Not found anywhere
74
+ return {
75
+ content: [
76
+ {
77
+ type: "text",
78
+ text: `Keyword "${keyword}" not found in the curated keywords or rules data.\n\nTry a different spelling or a partial name. Some keywords include: Devastating Wounds, Lethal Hits, Feel No Pain, Deep Strike, Invulnerable Save.`,
79
+ },
80
+ ],
81
+ };
82
+ });
83
+ }
84
+ //# sourceMappingURL=lookup-keyword.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-keyword.js","sourceRoot":"","sources":["../../src/tools/lookup-keyword.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerLookupPhase(server: McpServer): void;
3
+ //# sourceMappingURL=lookup-phase.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-phase.d.ts","sourceRoot":"","sources":["../../src/tools/lookup-phase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqDpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0C3D"}
@@ -0,0 +1,74 @@
1
+ import { z } from "zod";
2
+ import { TURN_SEQUENCES } from "../data/phases.js";
3
+ function findPhase(phaseName, gameMode) {
4
+ const sequence = TURN_SEQUENCES.find((s) => s.gameMode === gameMode);
5
+ if (!sequence)
6
+ return undefined;
7
+ const lowerName = phaseName.toLowerCase();
8
+ // Exact match first (case insensitive)
9
+ const exact = sequence.phases.find((p) => p.name.toLowerCase() === lowerName);
10
+ if (exact)
11
+ return exact;
12
+ // Substring match
13
+ const substring = sequence.phases.find((p) => p.name.toLowerCase().includes(lowerName));
14
+ if (substring)
15
+ return substring;
16
+ // Reverse substring — query contains the phase name
17
+ const reverseSubstring = sequence.phases.find((p) => lowerName.includes(p.name.toLowerCase()));
18
+ return reverseSubstring;
19
+ }
20
+ function getAvailablePhases(gameMode) {
21
+ const sequence = TURN_SEQUENCES.find((s) => s.gameMode === gameMode);
22
+ if (!sequence)
23
+ return [];
24
+ return sequence.phases.map((p) => p.name);
25
+ }
26
+ function formatPhase(phase) {
27
+ const sections = [];
28
+ sections.push(`# ${phase.name} Phase`);
29
+ sections.push(`**Game Mode:** ${phase.gameMode}`);
30
+ // Numbered steps
31
+ const steps = phase.steps.map((s, i) => `${i + 1}. ${s}`);
32
+ sections.push(`## Steps\n\n${steps.join("\n")}`);
33
+ // Tips
34
+ if (phase.tips.length > 0) {
35
+ const tips = phase.tips.map((t) => `- ${t}`);
36
+ sections.push(`## Tips\n\n${tips.join("\n")}`);
37
+ }
38
+ return sections.join("\n\n");
39
+ }
40
+ export function registerLookupPhase(server) {
41
+ server.tool("lookup_phase", "Look up a game phase by name. Returns step-by-step instructions and tips for the phase in the specified game mode.", {
42
+ phase_name: z
43
+ .string()
44
+ .describe("Name or partial name of the phase to look up (e.g. 'Shooting', 'Command', 'Firefight')"),
45
+ game_mode: z
46
+ .enum(["40k", "combat_patrol", "kill_team"])
47
+ .optional()
48
+ .default("40k")
49
+ .describe("Game mode (default: 40k). Use kill_team for Kill Team phases."),
50
+ }, async ({ phase_name, game_mode }) => {
51
+ const mode = game_mode;
52
+ const phase = findPhase(phase_name, mode);
53
+ if (!phase) {
54
+ const available = getAvailablePhases(mode);
55
+ return {
56
+ content: [
57
+ {
58
+ type: "text",
59
+ text: `Phase "${phase_name}" not found for ${mode}.\n\nAvailable phases: ${available.join(", ")}.`,
60
+ },
61
+ ],
62
+ };
63
+ }
64
+ return {
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: formatPhase(phase),
69
+ },
70
+ ],
71
+ };
72
+ });
73
+ }
74
+ //# sourceMappingURL=lookup-phase.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-phase.js","sourceRoot":"","sources":["../../src/tools/lookup-phase.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerLookupUnit(server: McpServer): void;
3
+ //# sourceMappingURL=lookup-unit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-unit.d.ts","sourceRoot":"","sources":["../../src/tools/lookup-unit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAoJpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmF1D"}
@@ -0,0 +1,175 @@
1
+ import { z } from "zod";
2
+ import { UNITS } from "../data/units.js";
3
+ import { KILL_TEAM_OPERATIVES } from "../data/kill-team-operatives.js";
4
+ import { fuzzySearch } from "../lib/search.js";
5
+ // === 40K Formatters ===
6
+ function formatStatsTable(profiles) {
7
+ const header = "| Name | M | T | SV | W | LD | OC |";
8
+ const separator = "|---|---|---|---|---|---|---|";
9
+ const rows = profiles.map((p) => `| ${p.name} | ${p.movement} | ${p.toughness} | ${p.save} | ${p.wounds} | ${p.leadership} | ${p.objectiveControl} |`);
10
+ return [header, separator, ...rows].join("\n");
11
+ }
12
+ function formatRangedWeapons(weapons) {
13
+ if (weapons.length === 0)
14
+ return "";
15
+ const header = "| Weapon | Range | A | BS | S | AP | D | Keywords |";
16
+ const separator = "|---|---|---|---|---|---|---|---|";
17
+ const rows = weapons.map((w) => `| ${w.name} | ${w.range} | ${w.attacks} | ${w.ballisticSkill} | ${w.strength} | ${w.armourPenetration} | ${w.damage} | ${w.keywords.join(", ") || "-"} |`);
18
+ return `### Ranged Weapons\n\n${[header, separator, ...rows].join("\n")}`;
19
+ }
20
+ function formatMeleeWeapons(weapons) {
21
+ if (weapons.length === 0)
22
+ return "";
23
+ const header = "| Weapon | Range | A | WS | S | AP | D | Keywords |";
24
+ const separator = "|---|---|---|---|---|---|---|---|";
25
+ const rows = weapons.map((w) => `| ${w.name} | ${w.range} | ${w.attacks} | ${w.weaponSkill} | ${w.strength} | ${w.armourPenetration} | ${w.damage} | ${w.keywords.join(", ") || "-"} |`);
26
+ return `### Melee Weapons\n\n${[header, separator, ...rows].join("\n")}`;
27
+ }
28
+ function formatAbilities(abilities) {
29
+ if (abilities.length === 0)
30
+ return "";
31
+ const lines = abilities.map((a) => `- **${a.name}**: ${a.description}`);
32
+ return `### Abilities\n\n${lines.join("\n")}`;
33
+ }
34
+ function formatUnit(unit) {
35
+ const sections = [];
36
+ // Header
37
+ const pointsStr = unit.points !== null ? ` — ${unit.points} pts` : "";
38
+ sections.push(`# ${unit.name}\n\n**Faction:** ${unit.faction}${pointsStr}`);
39
+ // Stats
40
+ sections.push(`### Unit Profiles\n\n${formatStatsTable(unit.profiles)}`);
41
+ // Weapons
42
+ const ranged = formatRangedWeapons(unit.rangedWeapons);
43
+ if (ranged)
44
+ sections.push(ranged);
45
+ const melee = formatMeleeWeapons(unit.meleeWeapons);
46
+ if (melee)
47
+ sections.push(melee);
48
+ // Abilities
49
+ const abilities = formatAbilities(unit.abilities);
50
+ if (abilities)
51
+ sections.push(abilities);
52
+ // Keywords
53
+ if (unit.keywords.length > 0) {
54
+ sections.push(`### Keywords\n\n${unit.keywords.join(", ")}`);
55
+ }
56
+ return sections.join("\n\n");
57
+ }
58
+ // === Kill Team Formatters ===
59
+ function formatKTProfile(profile) {
60
+ const header = "| Name | APL | Move | Save | Wounds |";
61
+ const separator = "|---|---|---|---|---|";
62
+ const row = `| ${profile.name} | ${profile.apl} | ${profile.movement} | ${profile.save} | ${profile.wounds} |`;
63
+ return [header, separator, row].join("\n");
64
+ }
65
+ function formatKTWeapons(weapons) {
66
+ if (weapons.length === 0)
67
+ return "";
68
+ const ranged = weapons.filter((w) => w.type === "ranged");
69
+ const melee = weapons.filter((w) => w.type === "melee");
70
+ const sections = [];
71
+ if (ranged.length > 0) {
72
+ const header = "| Weapon | ATK | HIT | DMG | WR |";
73
+ const separator = "|---|---|---|---|---|";
74
+ const rows = ranged.map((w) => `| ${w.name} | ${w.attacks} | ${w.hit} | ${w.damage} | ${w.weaponRules || "-"} |`);
75
+ sections.push(`### Ranged Weapons\n\n${[header, separator, ...rows].join("\n")}`);
76
+ }
77
+ if (melee.length > 0) {
78
+ const header = "| Weapon | ATK | HIT | DMG | WR |";
79
+ const separator = "|---|---|---|---|---|";
80
+ const rows = melee.map((w) => `| ${w.name} | ${w.attacks} | ${w.hit} | ${w.damage} | ${w.weaponRules || "-"} |`);
81
+ sections.push(`### Melee Weapons\n\n${[header, separator, ...rows].join("\n")}`);
82
+ }
83
+ return sections.join("\n\n");
84
+ }
85
+ function formatKTOperative(op) {
86
+ const sections = [];
87
+ sections.push(`# ${op.name}\n\n**Faction:** ${op.faction} | **Game:** Kill Team`);
88
+ sections.push(`### Operative Profile\n\n${formatKTProfile(op.profile)}`);
89
+ const weapons = formatKTWeapons(op.weapons);
90
+ if (weapons)
91
+ sections.push(weapons);
92
+ const abilities = formatAbilities(op.abilities);
93
+ if (abilities)
94
+ sections.push(abilities);
95
+ if (op.uniqueActions.length > 0) {
96
+ const lines = op.uniqueActions.map((a) => `- **${a.name}**: ${a.description}`);
97
+ sections.push(`### Unique Actions\n\n${lines.join("\n")}`);
98
+ }
99
+ if (op.keywords.length > 0) {
100
+ sections.push(`### Keywords\n\n${op.keywords.join(", ")}`);
101
+ }
102
+ return sections.join("\n\n");
103
+ }
104
+ // === Tool Registration ===
105
+ export function registerLookupUnit(server) {
106
+ server.tool("lookup_unit", "Look up a Warhammer 40K unit or Kill Team operative datasheet by name. Returns stats, weapons, abilities, and keywords.", {
107
+ unit_name: z.string().describe("Name or partial name of the unit to search for"),
108
+ faction: z
109
+ .string()
110
+ .optional()
111
+ .describe("Optional faction name to narrow results (e.g. 'Chaos Space Marines', 'Astartes')"),
112
+ game_mode: z
113
+ .enum(["40k", "combat_patrol", "kill_team"])
114
+ .optional()
115
+ .describe("Game mode: '40k' (default) searches 40K units, 'kill_team' searches Kill Team operatives"),
116
+ }, async ({ unit_name, faction, game_mode }) => {
117
+ if (game_mode === "kill_team") {
118
+ let candidates = [...KILL_TEAM_OPERATIVES];
119
+ if (faction) {
120
+ candidates = fuzzySearch(candidates, faction, ["faction"]);
121
+ }
122
+ const matches = fuzzySearch(candidates, unit_name, ["name"]);
123
+ if (matches.length === 0) {
124
+ const suggestion = faction
125
+ ? `Try a different faction or check the spelling.`
126
+ : `Try a partial name or check the spelling.`;
127
+ return {
128
+ content: [
129
+ {
130
+ type: "text",
131
+ text: `No Kill Team operative found matching "${unit_name}".${faction ? ` (faction filter: "${faction}")` : ""}\n\n${suggestion}`,
132
+ },
133
+ ],
134
+ };
135
+ }
136
+ return {
137
+ content: [
138
+ {
139
+ type: "text",
140
+ text: formatKTOperative(matches[0]),
141
+ },
142
+ ],
143
+ };
144
+ }
145
+ // Default: 40K units
146
+ let candidates = [...UNITS];
147
+ if (faction) {
148
+ candidates = fuzzySearch(candidates, faction, ["faction"]);
149
+ }
150
+ const matches = fuzzySearch(candidates, unit_name, ["name"]);
151
+ if (matches.length === 0) {
152
+ const suggestion = faction
153
+ ? `Try a different faction or check the spelling.`
154
+ : `Try a partial name or check the spelling.`;
155
+ return {
156
+ content: [
157
+ {
158
+ type: "text",
159
+ text: `No unit found matching "${unit_name}".${faction ? ` (faction filter: "${faction}")` : ""}\n\n${suggestion}`,
160
+ },
161
+ ],
162
+ };
163
+ }
164
+ const unit = matches[0];
165
+ return {
166
+ content: [
167
+ {
168
+ type: "text",
169
+ text: formatUnit(unit),
170
+ },
171
+ ],
172
+ };
173
+ });
174
+ }
175
+ //# sourceMappingURL=lookup-unit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-unit.js","sourceRoot":"","sources":["../../src/tools/lookup-unit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSearchUnits(server: McpServer): void;
3
+ //# sourceMappingURL=search-units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-units.d.ts","sourceRoot":"","sources":["../../src/tools/search-units.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAqBpE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA6G3D"}
@@ -0,0 +1,108 @@
1
+ import { z } from "zod";
2
+ import { UNITS } from "../data/units.js";
3
+ import { KILL_TEAM_OPERATIVES } from "../data/kill-team-operatives.js";
4
+ import { fuzzySearch } from "../lib/search.js";
5
+ const MAX_RESULTS = 10;
6
+ function formatCompactUnit(unit) {
7
+ const pointsStr = unit.points !== null ? `${unit.points}pts` : "pts N/A";
8
+ const keywords = unit.keywords.length > 0 ? unit.keywords.join(", ") : "None";
9
+ return `**${unit.name}** (${unit.faction}) — ${pointsStr} — Keywords: ${keywords}`;
10
+ }
11
+ function formatCompactOperative(op) {
12
+ const stats = `APL:${op.profile.apl} Move:${op.profile.movement} Save:${op.profile.save} W:${op.profile.wounds}`;
13
+ const keywords = op.keywords.length > 0 ? op.keywords.join(", ") : "None";
14
+ return `**${op.name}** (${op.faction}) — ${stats} — Keywords: ${keywords}`;
15
+ }
16
+ export function registerSearchUnits(server) {
17
+ server.tool("search_units", "Search Warhammer 40K units or Kill Team operatives by name, faction, or keywords. Returns a compact list of matching results (max 10).", {
18
+ query: z.string().describe("Search query — matches against name, faction, and keywords"),
19
+ faction: z
20
+ .string()
21
+ .optional()
22
+ .describe("Optional faction filter to narrow results (e.g. 'Necrons', 'Aeldari')"),
23
+ max_points: z
24
+ .number()
25
+ .optional()
26
+ .describe("Optional max points filter — only return units costing this many points or fewer (40K only)"),
27
+ game_mode: z
28
+ .enum(["40k", "combat_patrol", "kill_team"])
29
+ .optional()
30
+ .describe("Game mode: '40k' (default) searches 40K units, 'kill_team' searches Kill Team operatives"),
31
+ }, async ({ query, faction, max_points, game_mode }) => {
32
+ if (game_mode === "kill_team") {
33
+ let candidates = [...KILL_TEAM_OPERATIVES];
34
+ if (faction) {
35
+ candidates = fuzzySearch(candidates, faction, ["faction"]);
36
+ }
37
+ let matches = fuzzySearch(candidates, query, ["name", "faction", "keywords"]);
38
+ if (matches.length === 0) {
39
+ const filters = [];
40
+ if (faction)
41
+ filters.push(`faction: "${faction}"`);
42
+ const filterStr = filters.length > 0 ? ` (filters: ${filters.join(", ")})` : "";
43
+ return {
44
+ content: [
45
+ {
46
+ type: "text",
47
+ text: `No Kill Team operatives found matching "${query}"${filterStr}. Try a broader search term or different filters.`,
48
+ },
49
+ ],
50
+ };
51
+ }
52
+ const total = matches.length;
53
+ const displayed = matches.slice(0, MAX_RESULTS);
54
+ const lines = displayed.map(formatCompactOperative);
55
+ if (total > MAX_RESULTS) {
56
+ lines.push(`\n_Showing ${MAX_RESULTS} of ${total} results. Narrow your search for more specific results._`);
57
+ }
58
+ return {
59
+ content: [
60
+ {
61
+ type: "text",
62
+ text: lines.join("\n"),
63
+ },
64
+ ],
65
+ };
66
+ }
67
+ // Default: 40K units
68
+ let candidates = [...UNITS];
69
+ if (faction) {
70
+ candidates = fuzzySearch(candidates, faction, ["faction"]);
71
+ }
72
+ let matches = fuzzySearch(candidates, query, ["name", "faction", "keywords"]);
73
+ if (max_points !== undefined) {
74
+ matches = matches.filter((u) => u.points !== null && u.points <= max_points);
75
+ }
76
+ if (matches.length === 0) {
77
+ const filters = [];
78
+ if (faction)
79
+ filters.push(`faction: "${faction}"`);
80
+ if (max_points !== undefined)
81
+ filters.push(`max points: ${max_points}`);
82
+ const filterStr = filters.length > 0 ? ` (filters: ${filters.join(", ")})` : "";
83
+ return {
84
+ content: [
85
+ {
86
+ type: "text",
87
+ text: `No units found matching "${query}"${filterStr}. Try a broader search term or different filters.`,
88
+ },
89
+ ],
90
+ };
91
+ }
92
+ const total = matches.length;
93
+ const displayed = matches.slice(0, MAX_RESULTS);
94
+ const lines = displayed.map(formatCompactUnit);
95
+ if (total > MAX_RESULTS) {
96
+ lines.push(`\n_Showing ${MAX_RESULTS} of ${total} results. Narrow your search for more specific results._`);
97
+ }
98
+ return {
99
+ content: [
100
+ {
101
+ type: "text",
102
+ text: lines.join("\n"),
103
+ },
104
+ ],
105
+ };
106
+ });
107
+ }
108
+ //# sourceMappingURL=search-units.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-units.js","sourceRoot":"","sources":["../../src/tools/search-units.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ export type GameMode = "40k" | "combat_patrol" | "kill_team";
2
+ export type GameSystem = "wh40k-10e" | "wh40k-killteam";
3
+ export type UnitProfile = {
4
+ name: string;
5
+ movement: string;
6
+ toughness: string;
7
+ save: string;
8
+ wounds: string;
9
+ leadership: string;
10
+ objectiveControl: string;
11
+ };
12
+ export type KillTeamOperativeProfile = {
13
+ name: string;
14
+ apl: string;
15
+ movement: string;
16
+ save: string;
17
+ wounds: string;
18
+ };
19
+ export type RangedWeapon = {
20
+ name: string;
21
+ range: string;
22
+ attacks: string;
23
+ ballisticSkill: string;
24
+ strength: string;
25
+ armourPenetration: string;
26
+ damage: string;
27
+ keywords: string[];
28
+ };
29
+ export type MeleeWeapon = {
30
+ name: string;
31
+ range: string;
32
+ attacks: string;
33
+ weaponSkill: string;
34
+ strength: string;
35
+ armourPenetration: string;
36
+ damage: string;
37
+ keywords: string[];
38
+ };
39
+ export type KillTeamWeapon = {
40
+ name: string;
41
+ attacks: string;
42
+ hit: string;
43
+ damage: string;
44
+ weaponRules: string;
45
+ type: "ranged" | "melee";
46
+ };
47
+ export type Ability = {
48
+ name: string;
49
+ description: string;
50
+ };
51
+ export type Unit = {
52
+ id: string;
53
+ name: string;
54
+ faction: string;
55
+ keywords: string[];
56
+ profiles: UnitProfile[];
57
+ rangedWeapons: RangedWeapon[];
58
+ meleeWeapons: MeleeWeapon[];
59
+ abilities: Ability[];
60
+ points: number | null;
61
+ gameSystem: GameSystem;
62
+ };
63
+ export type KillTeamOperative = {
64
+ id: string;
65
+ name: string;
66
+ faction: string;
67
+ keywords: string[];
68
+ profile: KillTeamOperativeProfile;
69
+ weapons: KillTeamWeapon[];
70
+ abilities: Ability[];
71
+ uniqueActions: Ability[];
72
+ gameSystem: GameSystem;
73
+ };
74
+ export type KeywordDefinition = {
75
+ name: string;
76
+ description: string;
77
+ plainEnglish: string;
78
+ gameModes: GameMode[];
79
+ examples?: string[];
80
+ };
81
+ export type Phase = {
82
+ name: string;
83
+ order: number;
84
+ steps: string[];
85
+ tips: string[];
86
+ gameMode: GameMode;
87
+ };
88
+ export type TurnSequence = {
89
+ gameMode: GameMode;
90
+ phases: Phase[];
91
+ };
92
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // === Game Modes ===
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,qBAAqB"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "warhammer-oracle",
3
+ "version": "0.1.0",
4
+ "description": "Warhammer 40K and Kill Team rules, stats, and game flow MCP server",
5
+ "type": "module",
6
+ "bin": {
7
+ "warhammer-oracle": "dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc && chmod 755 dist/index.js",
16
+ "dev": "tsx src/index.ts",
17
+ "lint": "tsc --noEmit",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "fetch-data": "tsx scripts/fetch-data.ts",
21
+ "inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
22
+ "prepublishOnly": "npm run build && npm test"
23
+ },
24
+ "dependencies": {
25
+ "@modelcontextprotocol/sdk": "^1.12",
26
+ "fast-xml-parser": "^5.2",
27
+ "zod": "^3.25"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "tsx": "^4.0.0",
32
+ "typescript": "^5.7.0",
33
+ "vitest": "^3.0.0"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "license": "MIT"
39
+ }