hoi4-agent-tools 3.10.0 → 4.0.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 (150) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +115 -147
  3. package/dist/bin/http.js +10 -0
  4. package/dist/bin/setup.js +3 -2
  5. package/dist/bin/stdio.js +10 -0
  6. package/dist/hoi4_agent_tools/core/artifacts.js +40 -5
  7. package/dist/hoi4_agent_tools/core/canonical.js +18 -4
  8. package/dist/hoi4_agent_tools/core/configuration.js +7 -0
  9. package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +12 -0
  10. package/dist/hoi4_agent_tools/core/domain-tools.js +65 -0
  11. package/dist/hoi4_agent_tools/core/domain-validation.js +5 -2
  12. package/dist/hoi4_agent_tools/core/engine.js +29 -2
  13. package/dist/hoi4_agent_tools/core/index.js +2 -2
  14. package/dist/hoi4_agent_tools/core/job-executor.js +14 -1
  15. package/dist/hoi4_agent_tools/core/job-operation-registration.js +15 -7
  16. package/dist/hoi4_agent_tools/core/job-store.js +78 -5
  17. package/dist/hoi4_agent_tools/core/job-transactions.js +31 -2
  18. package/dist/hoi4_agent_tools/core/job-worker-host.js +215 -116
  19. package/dist/hoi4_agent_tools/core/job-worker.js +90 -36
  20. package/dist/hoi4_agent_tools/core/operation-result.js +123 -10
  21. package/dist/hoi4_agent_tools/core/operation-tasks.js +49 -89
  22. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +25 -21
  23. package/dist/hoi4_agent_tools/core/server-registry.js +22 -5
  24. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +26 -1
  25. package/dist/hoi4_agent_tools/core/source/cache.js +20 -10
  26. package/dist/hoi4_agent_tools/core/source/lexer.js +11 -3
  27. package/dist/hoi4_agent_tools/core/transactions.js +114 -23
  28. package/dist/hoi4_agent_tools/core/workspace.js +84 -8
  29. package/dist/hoi4_agent_tools/decision/ai.js +1 -1
  30. package/dist/hoi4_agent_tools/decision/service.js +28 -0
  31. package/dist/hoi4_agent_tools/decision/source-inventory.js +7 -5
  32. package/dist/hoi4_agent_tools/event/job-operations.js +9 -5
  33. package/dist/hoi4_agent_tools/event/operations.js +14 -1
  34. package/dist/hoi4_agent_tools/focus/job-operations.js +1 -2
  35. package/dist/hoi4_agent_tools/focus/tool-operations.js +1 -1
  36. package/dist/hoi4_agent_tools/gui/validators.js +8 -6
  37. package/dist/hoi4_agent_tools/map/bmp.js +1 -1
  38. package/dist/hoi4_agent_tools/map/derived.js +748 -0
  39. package/dist/hoi4_agent_tools/map/job-operations.js +7 -3
  40. package/dist/hoi4_agent_tools/map/model.js +103 -0
  41. package/dist/hoi4_agent_tools/map/operations.js +427 -58
  42. package/dist/hoi4_agent_tools/map/references.js +483 -0
  43. package/dist/hoi4_agent_tools/map/service.js +136 -61
  44. package/dist/hoi4_agent_tools/map/tool-operations.js +2 -1
  45. package/dist/hoi4_agent_tools/map/validation.js +3 -0
  46. package/dist/hoi4_agent_tools/map/world.js +1280 -0
  47. package/dist/hoi4_agent_tools/mcp/server/argument-errors.js +72 -0
  48. package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +23 -5
  49. package/dist/hoi4_agent_tools/mcp/server/create.js +7 -0
  50. package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +24 -30
  51. package/dist/hoi4_agent_tools/mcp/server/tool-listing.js +45 -0
  52. package/dist/hoi4_agent_tools/mcp/tools/analysis.js +25 -5
  53. package/dist/hoi4_agent_tools/mcp/tools/event.js +18 -29
  54. package/dist/hoi4_agent_tools/mcp/tools/focus.js +4 -12
  55. package/dist/hoi4_agent_tools/mcp/tools/gui.js +3 -3
  56. package/dist/hoi4_agent_tools/mcp/tools/job.js +2 -2
  57. package/dist/hoi4_agent_tools/mcp/tools/map.js +13 -16
  58. package/dist/hoi4_agent_tools/mcp/tools/probability.js +42 -48
  59. package/dist/hoi4_agent_tools/mcp/tools/reference.js +22 -5
  60. package/dist/hoi4_agent_tools/mcp/tools/technology.js +16 -29
  61. package/dist/hoi4_agent_tools/mcp/transports/http.js +28 -4
  62. package/dist/hoi4_agent_tools/mcp/transports/stdio-lifetime.js +31 -8
  63. package/dist/hoi4_agent_tools/probability/job-operations.js +4 -9
  64. package/dist/hoi4_agent_tools/probability/operations.js +175 -13
  65. package/dist/hoi4_agent_tools/probability/source-analysis.js +24 -1
  66. package/dist/hoi4_agent_tools/reference/error-log.js +359 -0
  67. package/dist/hoi4_agent_tools/reference/known-mistakes.js +307 -0
  68. package/dist/hoi4_agent_tools/reference/known-mistakes.json +154 -0
  69. package/dist/hoi4_agent_tools/reference/mod-index.js +101 -0
  70. package/dist/hoi4_agent_tools/reference/operations.js +88 -19
  71. package/dist/hoi4_agent_tools/reference/player-text.js +4 -3
  72. package/dist/hoi4_agent_tools/reference/script-validation.js +19 -1
  73. package/dist/hoi4_agent_tools/reference/service.js +6 -0
  74. package/dist/hoi4_agent_tools/schemas/event.js +78 -19
  75. package/dist/hoi4_agent_tools/schemas/focus-requests.js +10 -0
  76. package/dist/hoi4_agent_tools/schemas/lenient-arguments.js +266 -0
  77. package/dist/hoi4_agent_tools/schemas/map-requests.js +63 -20
  78. package/dist/hoi4_agent_tools/schemas/map.js +122 -3
  79. package/dist/hoi4_agent_tools/schemas/mod-tools.js +181 -0
  80. package/dist/hoi4_agent_tools/schemas/probability-requests.js +119 -9
  81. package/dist/hoi4_agent_tools/schemas/reference.js +13 -0
  82. package/dist/hoi4_agent_tools/schemas/scenarios.js +1 -3
  83. package/dist/hoi4_agent_tools/schemas/script-validation.js +13 -6
  84. package/dist/hoi4_agent_tools/schemas/technology.js +79 -21
  85. package/dist/hoi4_agent_tools/technology/job-operations.js +3 -4
  86. package/dist/hoi4_agent_tools/technology/operations.js +14 -1
  87. package/dist/hoi4_agent_tools/version.js +1 -1
  88. package/docs/README.md +7 -4
  89. package/docs/analysis.md +13 -0
  90. package/docs/configuration.md +281 -0
  91. package/docs/development.md +24 -2
  92. package/docs/events.md +27 -3
  93. package/docs/examples.md +6 -6
  94. package/docs/focus.md +125 -4
  95. package/docs/gui.md +98 -17
  96. package/docs/helper-expansion.md +8 -8
  97. package/docs/http.md +3 -1
  98. package/docs/jobs.md +19 -13
  99. package/docs/map.md +197 -6
  100. package/docs/mechanics.md +4 -0
  101. package/docs/probability.md +42 -15
  102. package/docs/reference.md +71 -4
  103. package/docs/setup.md +44 -11
  104. package/docs/technology.md +26 -1
  105. package/docs/third-party-notices.md +11 -0
  106. package/docs/tools.md +151 -42
  107. package/docs/troubleshooting.md +61 -0
  108. package/examples/clients/codex-config.toml +5 -2
  109. package/examples/clients/generic.json +2 -2
  110. package/examples/config/config.json +7 -0
  111. package/npm-shrinkwrap.json +151 -156
  112. package/package.json +13 -8
  113. package/schemas/configuration.schema.json +144 -128
  114. package/schemas/continuous-focus-palette.schema.json +1 -1
  115. package/schemas/custom-weighted-pool.schema.json +1 -1
  116. package/schemas/decision-inspect-input.schema.json +1 -1
  117. package/schemas/error-log-input.schema.json +55 -0
  118. package/schemas/event-feature-manifest.schema.json +1 -1
  119. package/schemas/focus-plan.schema.json +1 -1
  120. package/schemas/focus-planning-sidecar.schema.json +1 -1
  121. package/schemas/gui-animation-source.schema.json +1 -1
  122. package/schemas/gui-helper.schema.json +1 -1
  123. package/schemas/gui-scenario.schema.json +1 -1
  124. package/schemas/helper-expansion-request.schema.json +1 -1
  125. package/schemas/helper-expansion-summary.schema.json +1 -1
  126. package/schemas/impact-inspect-input.schema.json +1 -1
  127. package/schemas/map-operation.schema.json +597 -126
  128. package/schemas/mechanic-test-input.schema.json +1 -1
  129. package/schemas/mod-index-input.schema.json +51 -0
  130. package/schemas/operation-result.schema.json +2 -2
  131. package/schemas/package-check-input.schema.json +1 -1
  132. package/schemas/package-manifest.schema.json +1 -1
  133. package/schemas/probability-analysis-result.schema.json +1 -1
  134. package/schemas/probability-compare-input.schema.json +1 -1
  135. package/schemas/probability-evaluate-input.schema.json +1 -1
  136. package/schemas/probability-inspect-input.schema.json +1 -1
  137. package/schemas/probability-render-input.schema.json +1 -1
  138. package/schemas/probability-scenario-set.schema.json +1 -1
  139. package/schemas/probability-sequence-input.schema.json +1 -1
  140. package/schemas/probability-simulate-input.schema.json +1 -1
  141. package/schemas/probability-sweep-input.schema.json +1 -1
  142. package/schemas/reference-context-input.schema.json +1 -1
  143. package/schemas/reference-read-input.schema.json +1 -1
  144. package/schemas/reference-search-input.schema.json +1 -1
  145. package/schemas/scenario-suite.schema.json +3 -5
  146. package/schemas/scenario-test-input.schema.json +3 -5
  147. package/schemas/script-validate-input.schema.json +1 -1
  148. package/schemas/shared-scenario.schema.json +1 -1
  149. package/schemas/source-lookup-input.schema.json +1 -1
  150. package/server.json +3 -3
@@ -0,0 +1,72 @@
1
+ import { errorResult } from '../../core/operation-result.js';
2
+ import { ServiceError } from '../../core/result.js';
3
+ /**
4
+ * Invalid arguments come back as a tool error naming each wrong field, what it expected and a
5
+ * working call, not as a protocol error: clients show a tool error's text to the model, while
6
+ * some reduce a protocol error to a generic "Invalid tool arguments".
7
+ */
8
+ export const ARGUMENT_EXAMPLES = {
9
+ 'hoi4.reference_context': '{"surface":"event"}',
10
+ 'hoi4.reference_search': '{"query":"add_timed_idea"}',
11
+ 'hoi4.reference_read': '{"id":"<id of a reference_search result>","revision":"<revision of the same result>"}',
12
+ 'hoi4.source_lookup': '{"symbol":"my_scripted_effect"} or {"path":"events/my_events.txt","line":42}',
13
+ 'hoi4.script_validate': '{"path":"events/my_events.txt"} or {"source":"add_political_power = 10","kind":"effect","scope":"country"}',
14
+ 'hoi4.error_log': '{"scope":"mod","category":"effect"}',
15
+ 'hoi4.mod_index': '{"mode":"overview"} or {"mode":"next_id","kind":"event","namespace":"my_mod"}',
16
+ 'hoi4.event_inspect': '{"mode":"lint","selector":"chaosx.nr14"} or {"mode":"trace","selector":"chaosx.nr14.1","direction":"downstream"}',
17
+ 'hoi4.event_render': '{"view":"overview","selector":"chaosx.nr14"}',
18
+ 'hoi4.focus_inspect': '{"treeId":"my_focus_tree"}',
19
+ 'hoi4.focus_render': '{"relativePath":"common/national_focus/my_tree.txt","treeId":"my_focus_tree"}',
20
+ 'hoi4.focus_rewrite': '{"relativePath":"common/national_focus/my_tree.txt","treeId":"my_focus_tree","layoutMode":"compact"}',
21
+ 'hoi4.gui_inspect': '{"windowName":"my_window"}',
22
+ 'hoi4.gui_render': '{"windowName":"my_window","scenario":{"id":"normal"}}',
23
+ 'hoi4.gui_rewrite': '{"mode":"patches","relativePath":"interface/my_window.gui","windowName":"my_window","scenario":{"id":"normal"},"expectedSourceHash":"<sha256 of the file>","patches":[{"start":120,"end":130,"expectedText":"x = 10 y","text":"x = 40 y"}]}',
24
+ 'hoi4.map_inspect': '{"query":"Brandenburg","lookupOnly":true} or {"layer":"state","tile":{"x":2600,"y":500,"width":256,"height":256}}',
25
+ 'hoi4.map_rewrite': '{"operations":[{"id":"new-state","kind":"create_state","provinceIds":[120,121],"displayName":"New State"}]}',
26
+ 'hoi4.tech_inspect': '{"mode":"scan"} or {"mode":"explain","technologyId":"my_tech"} or {"mode":"lint"}',
27
+ 'hoi4.tech_render': '{"view":"folder","folderId":"my_folder"}',
28
+ 'hoi4.probability_inspect': '{"candidatePool":["my_decision"]} or {"source":"events/my_events.txt","adapter":"event_option_ai_chance"}',
29
+ 'hoi4.probability_analyze': '{"analysis":"sweep","source":"common/decisions/my_decisions.txt","candidatePool":["my_decision"],"scenarioSet":[{"actor":"GER","state":{"variable.pressure":10}}],"sweep":{"paths":["variable.pressure"],"steps":10}}',
30
+ 'hoi4.impact_inspect': '{"symbols":[{"kind":"scripted_effect","id":"my_effect"}]} or {"changedFiles":["mod:common/scripted_effects/my_effects.txt"]}',
31
+ 'hoi4.decision_inspect': '{"mode":"inventory"} or {"mode":"inspect","id":"my_decision","scenarios":[{"id":"base","actor":"GER","state":{}}]}',
32
+ 'hoi4.mechanic_test': '{"test":{"id":"my_test","scenario":{"schemaVersion":"1.0","id":"my_test","state":{"treasury":10,"savings":2}},"steps":[{"kind":"effect","source":{"kind":"scripted_effect","id":"my_effect"}}],"assertions":[{"id":"kept","kind":"conservation","paths":[{"key":"treasury"},{"key":"savings"}]}]}}',
33
+ 'hoi4.package_check': '{"manifest":{"schemaVersion":"1.0","id":"my_package","definitions":[{"kind":"scripted_effect","id":"my_effect"}]}}',
34
+ 'hoi4.scenario_test': '{"suitePath":"tests/my_suite.json","maxCases":20}',
35
+ 'hoi4.job_inspect': '{"jobId":"job_<64 hex characters from a native task id>"}',
36
+ 'hoi4.job_cancel': '{"jobId":"job_<64 hex characters from a native task id>"}',
37
+ };
38
+ function describeIssue(issue) {
39
+ const field = issue.path.length === 0 ? '(arguments)' : issue.path.map(String).join('.');
40
+ let problem = issue.message;
41
+ if (issue.code === 'unrecognized_keys')
42
+ problem = `unknown field${issue.keys.length === 1 ? '' : 's'} ${issue.keys.join(', ')}`;
43
+ else if (issue.code === 'invalid_value')
44
+ problem = `expected one of ${issue.values.map((value) => JSON.stringify(value)).join(', ')}`;
45
+ else if (issue.code === 'invalid_union') {
46
+ // Name the alternative that came closest: the one with the fewest problems.
47
+ const closest = [...issue.errors].sort((left, right) => left.length - right.length)[0];
48
+ const first = closest?.[0];
49
+ if (first !== undefined) {
50
+ const nested = describeIssue(first);
51
+ problem = `${issue.message}; closest form: ${nested.field === '(arguments)' ? '' : `${nested.field}: `}${nested.problem}`;
52
+ }
53
+ }
54
+ return { field, problem: problem.slice(0, 300) };
55
+ }
56
+ export function invalidArgumentsError(toolName, error) {
57
+ const issues = error.issues.slice(0, 6).map(describeIssue);
58
+ const example = ARGUMENT_EXAMPLES[toolName];
59
+ const message = [
60
+ `Invalid arguments for ${toolName}: ${issues.map(({ field, problem }) => `${field}: ${problem}`).join('; ')}.`,
61
+ ...(example === undefined ? [] : [`Example: ${example}.`]),
62
+ 'The tool input schema lists every accepted field.',
63
+ ].join(' ');
64
+ return new ServiceError('INVALID_ARGUMENTS', message.slice(0, 1_500), {
65
+ tool: toolName,
66
+ issues,
67
+ ...(example === undefined ? {} : { example }),
68
+ });
69
+ }
70
+ export function invalidArgumentsResult(toolName, error, workspaceId) {
71
+ return errorResult(invalidArgumentsError(toolName, error), typeof workspaceId === 'string' ? workspaceId : 'current');
72
+ }
@@ -1,9 +1,12 @@
1
1
  import { fileURLToPath } from 'node:url';
2
+ import { normalizeToolArguments } from '../../schemas/lenient-arguments.js';
3
+ import { invalidArgumentsResult } from './argument-errors.js';
4
+ import { slimListedTool } from './tool-listing.js';
2
5
  import { CallToolResultSchema, ClientCapabilitiesSchema, ListToolsResultSchema, } from '@modelcontextprotocol/core';
3
6
  import { CLIENT_CAPABILITIES_META_KEY, inputRequired, ProtocolError, ProtocolErrorCode, } from '@modelcontextprotocol/server';
4
7
  import { z } from 'zod/v4';
5
8
  import { isJobControlTool, JobControlService } from '../../core/job-controls.js';
6
- import { OperationTaskService } from '../../core/operation-tasks.js';
9
+ import { OperationTaskService, releaseIdleWorkers } from '../../core/operation-tasks.js';
7
10
  import { errorResult } from '../../core/operation-result.js';
8
11
  import { PACKAGE_NAME, PACKAGE_VERSION } from '../../version.js';
9
12
  import { MODERN_TASK_ROUTES, ModernTaskRoutingServer } from '../transports/modern-task-routing.js';
@@ -62,10 +65,16 @@ export function createModernOperationServer(engine, context = {}, tasks = new Op
62
65
  },
63
66
  instructions: SERVER_INSTRUCTIONS,
64
67
  });
68
+ // Warm job workers outlive single calls, not the client session that used them.
69
+ const previousOnClose = server.onclose;
70
+ server.onclose = () => {
71
+ previousOnClose?.();
72
+ void releaseIdleWorkers(engine);
73
+ };
65
74
  registerModernResources(server, engine, context);
66
75
  registerModernPrompts(server);
67
76
  server.setRequestHandler('tools/list', () => ListToolsResultSchema.parse({
68
- tools: definitions.map(({ name, inputSchema, outputSchema, ...metadata }) => ({
77
+ tools: definitions.map(({ name, inputSchema, outputSchema, ...metadata }) => slimListedTool({
69
78
  ...metadata,
70
79
  name,
71
80
  inputSchema: {
@@ -86,9 +95,18 @@ export function createModernOperationServer(engine, context = {}, tasks = new Op
86
95
  const definition = definitions.find(({ name }) => name === request.params.name);
87
96
  if (definition === undefined)
88
97
  throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Unknown tool');
89
- const input = definition.inputSchema.safeParse(request.params.arguments ?? {});
90
- if (!input.success)
91
- throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Invalid tool arguments');
98
+ const input = definition.inputSchema.safeParse(normalizeToolArguments(definition.name, request.params.arguments ?? {}));
99
+ if (!input.success) {
100
+ // A tool error, not a protocol error, so the model sees which field to correct.
101
+ const workspaceId = request.params.arguments
102
+ ?.workspaceId;
103
+ return CallToolResultSchema.parse({
104
+ ...invalidArgumentsResult(definition.name, input.error, workspaceId),
105
+ resultType: 'complete',
106
+ ttlMs: 0,
107
+ cacheScope: 'private',
108
+ });
109
+ }
92
110
  const capabilities = clientCapabilities(extra.mcpReq.envelope);
93
111
  const arguments_ = z.record(z.string(), z.json()).parse(input.data);
94
112
  let operationContext = context;
@@ -14,6 +14,7 @@ import { registerMcpResources } from '../resources/register.js';
14
14
  import { installRequestLifecycle } from './request-lifecycle.js';
15
15
  import { PersistentJobTaskStore } from './job-task-store.js';
16
16
  import { SERVER_INSTRUCTIONS } from './instructions.js';
17
+ import { releaseIdleWorkers } from '../../core/operation-tasks.js';
17
18
  export { SERVER_INSTRUCTIONS } from './instructions.js';
18
19
  export function createMcpServer(engine, context = {}) {
19
20
  let serverContext = context;
@@ -31,6 +32,12 @@ export function createMcpServer(engine, context = {}) {
31
32
  taskStore,
32
33
  });
33
34
  installRequestLifecycle(server, engine);
35
+ // Warm job workers outlive single calls, not the client session that used them.
36
+ const previousOnClose = server.server.onclose;
37
+ server.server.onclose = () => {
38
+ previousOnClose?.();
39
+ void releaseIdleWorkers(engine);
40
+ };
34
41
  serverContext = {
35
42
  ...context,
36
43
  resolveCurrentWorkspaceId: async (signal) => {
@@ -1,47 +1,41 @@
1
1
  import { CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
2
+ import { DOMAIN_TOOL_NAMES } from '../../core/domain-tools.js';
3
+ import { normalizeToolArguments } from '../../schemas/lenient-arguments.js';
4
+ import { referenceTools } from '../tools/reference.js';
5
+ import { jobControlTools } from '../tools/job.js';
6
+ import { invalidArgumentsResult } from './argument-errors.js';
7
+ import { taskToolCatalog } from './task-tool-catalog.js';
2
8
  import { progressReporter, withProgressHeartbeat } from './progress.js';
3
9
  import { withTaskRequestSignal } from './task-request-context.js';
10
+ import { slimToolList } from './tool-listing.js';
4
11
  // Durable task compatibility and cancellation are control traffic. They must
5
12
  // remain responsive while every domain-execution slot is occupied.
6
13
  const controlTools = new Set(['hoi4.job_inspect', 'hoi4.job_cancel']);
7
- const backgroundTools = new Set([
8
- 'hoi4.impact_inspect',
9
- 'hoi4.decision_inspect',
10
- 'hoi4.mechanic_test',
11
- 'hoi4.package_check',
12
- 'hoi4.scenario_test',
13
- 'hoi4.event_inspect',
14
- 'hoi4.event_render',
15
- 'hoi4.event_compare',
16
- 'hoi4.tech_inspect',
17
- 'hoi4.tech_render',
18
- 'hoi4.tech_compare',
19
- 'hoi4.probability_inspect',
20
- 'hoi4.probability_evaluate',
21
- 'hoi4.probability_sweep',
22
- 'hoi4.probability_simulate',
23
- 'hoi4.probability_sequence',
24
- 'hoi4.probability_compare',
25
- 'hoi4.probability_render',
26
- 'hoi4.map_inspect',
27
- 'hoi4.map_render',
28
- 'hoi4.map_rewrite',
29
- 'hoi4.gui_inspect',
30
- 'hoi4.gui_render',
31
- 'hoi4.gui_rewrite',
32
- 'hoi4.focus_inspect',
33
- 'hoi4.focus_render',
34
- 'hoi4.focus_raster',
35
- 'hoi4.focus_rewrite',
36
- ]);
14
+ const backgroundTools = new Set(DOMAIN_TOOL_NAMES);
15
+ let inputSchemas;
16
+ function inputSchema(name) {
17
+ inputSchemas ??= new Map([...taskToolCatalog, ...referenceTools, ...jobControlTools].map(({ name: toolName, inputSchema: schema }) => [toolName, schema]));
18
+ return inputSchemas.get(name);
19
+ }
37
20
  /** Wrap the public SDK handler registration so optional tools receive the same lifecycle. */
38
21
  export function installRequestLifecycle(server, engine) {
39
22
  const register = server.server.setRequestHandler.bind(server.server);
40
23
  const owner = {};
41
24
  server.server.setRequestHandler = (schema, handler) => {
42
25
  register(schema, (request, extra) => {
26
+ if (request.method === 'tools/list')
27
+ return Promise.resolve(handler(request, extra)).then(slimToolList);
43
28
  if (request.method !== 'tools/call')
44
29
  return handler(request, extra);
30
+ const params = request.params;
31
+ if (params !== undefined && typeof params.name === 'string') {
32
+ params.arguments = normalizeToolArguments(params.name, params.arguments);
33
+ // Answer invalid arguments with a tool error that names the fields and a working call.
34
+ const schema = params.task === undefined ? inputSchema(params.name) : undefined;
35
+ const parsed = schema?.safeParse(params.arguments ?? {});
36
+ if (parsed !== undefined && !parsed.success)
37
+ return Promise.resolve(invalidArgumentsResult(params.name, parsed.error, params.arguments?.workspaceId));
38
+ }
45
39
  const progress = progressReporter(extra);
46
40
  const taskCall = CallToolRequestSchema.safeParse(request);
47
41
  if (taskCall.success && controlTools.has(taskCall.data.params.name))
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Every tool schema is sent to the client's model on each session, so listed schemas carry no
3
+ * bytes that change nothing: the per-schema `$schema` dialect declaration (the schemas use no
4
+ * dialect-specific keywords, so the MCP default dialect reads them identically) and the
5
+ * safe-integer bounds that every unbounded integer receives.
6
+ */
7
+ const SAFE_INTEGER_BOUNDS = new Map([
8
+ ['maximum', Number.MAX_SAFE_INTEGER],
9
+ ['minimum', Number.MIN_SAFE_INTEGER],
10
+ ]);
11
+ function slimSchema(value) {
12
+ if (Array.isArray(value))
13
+ return value.map(slimSchema);
14
+ if (value === null || typeof value !== 'object')
15
+ return value;
16
+ const result = {};
17
+ for (const [key, child] of Object.entries(value)) {
18
+ if (key === '$schema')
19
+ continue;
20
+ if (SAFE_INTEGER_BOUNDS.get(key) === child)
21
+ continue;
22
+ result[key] = slimSchema(child);
23
+ }
24
+ return result;
25
+ }
26
+ /** Remove redundant schema bytes from one listed tool, leaving everything else untouched. */
27
+ export function slimListedTool(tool) {
28
+ return {
29
+ ...tool,
30
+ ...(tool.inputSchema === undefined ? {} : { inputSchema: slimSchema(tool.inputSchema) }),
31
+ ...(tool.outputSchema === undefined ? {} : { outputSchema: slimSchema(tool.outputSchema) }),
32
+ };
33
+ }
34
+ /** Apply {@link slimListedTool} to a `tools/list` result. */
35
+ export function slimToolList(result) {
36
+ if (result === null || typeof result !== 'object' || !('tools' in result))
37
+ return result;
38
+ const { tools } = result;
39
+ if (!Array.isArray(tools))
40
+ return result;
41
+ return {
42
+ ...result,
43
+ tools: tools.map((tool) => slimListedTool(tool)),
44
+ };
45
+ }
@@ -36,6 +36,26 @@ const decisionOutputSchema = strictOperationResultSchema(z
36
36
  overrides: nonNegativeIntegerSchema,
37
37
  scenarios: nonNegativeIntegerSchema,
38
38
  unresolvedDefinitions: nonNegativeIntegerSchema,
39
+ unresolvedSample: z
40
+ .array(z.object({ id: z.string(), path: z.string(), reason: z.string() }).strict())
41
+ .max(8)
42
+ .optional(),
43
+ outcomes: z
44
+ .array(z
45
+ .object({
46
+ scenarioId: z.string(),
47
+ found: z.boolean(),
48
+ eligible: z.string().optional(),
49
+ visible: z.string().nullable().optional(),
50
+ available: z.string().optional(),
51
+ unresolved: z
52
+ .array(z.object({ code: z.string(), message: z.string() }).strict())
53
+ .max(5)
54
+ .optional(),
55
+ })
56
+ .strict())
57
+ .max(16)
58
+ .optional(),
39
59
  })
40
60
  .strict());
41
61
  const mechanicOutputSchema = strictOperationResultSchema(z
@@ -112,7 +132,7 @@ export const analysisTaskTools = [
112
132
  {
113
133
  name: 'hoi4.impact_inspect',
114
134
  title: 'Inspect cross-system impact',
115
- description: 'Trace definitions and consumers of symbols or changed source across HOI4 systems. Proposed sources are analyzed in memory; full revision-bound evidence is linked.',
135
+ description: 'Trace where symbols are defined and consumed across focuses, events, decisions, ideas, GUI and helpers, or what a changed file affects; proposed sources are analyzed in memory. Example: {"symbols":[{"kind":"scripted_effect","id":"my_effect"}]}. Returns consumer counts inline; the revision-bound graph is a linked resource.',
116
136
  inputSchema: impactInspectRequestSchema,
117
137
  outputSchema: impactOutputSchema,
118
138
  annotations: readOnlyAnalysisTool,
@@ -120,7 +140,7 @@ export const analysisTaskTools = [
120
140
  {
121
141
  name: 'hoi4.decision_inspect',
122
142
  title: 'Inspect decisions and missions',
123
- description: 'Inventory decisions or evaluate declared actor and target scenarios for gates, payment, lifecycle, and proposed-source differences. Full evidence is linked.',
143
+ description: 'Inventory decisions and missions, or evaluate one under declared actor and target scenarios: visibility, availability, payment, lifecycle and proposed-source differences. Example: {"mode":"inspect","id":"my_decision","scenarios":[{"id":"base","actor":"GER","state":{}}]}. Returns each scenario outcome inline; full evidence is linked.',
124
144
  inputSchema: decisionInspectRequestSchema,
125
145
  outputSchema: decisionOutputSchema,
126
146
  annotations: readOnlyAnalysisTool,
@@ -128,7 +148,7 @@ export const analysisTaskTools = [
128
148
  {
129
149
  name: 'hoi4.mechanic_test',
130
150
  title: 'Test declared mechanic steps',
131
- description: 'Interpret bounded, source-backed effect steps on isolated declared state and check typed invariants. Unsupported effects remain unresolved; no game or mod source is changed.',
151
+ description: 'Interpret source-backed effect steps (scripted effect, event option, decision, focus reward) on a declared state and check typed assertions; unsupported effects stay unresolved and no source changes. Example: {"test":{"id":"t","scenario":{"schemaVersion":"1.0","id":"t","state":{"treasury":10}},"steps":[...],"assertions":[...]}}. See docs/mechanics.md.',
132
152
  inputSchema: mechanicInputSchema,
133
153
  outputSchema: mechanicOutputSchema,
134
154
  annotations: readOnlyAnalysisTool,
@@ -136,7 +156,7 @@ export const analysisTaskTools = [
136
156
  {
137
157
  name: 'hoi4.package_check',
138
158
  title: 'Check package connections',
139
- description: 'Check a declarative package manifest against active definitions, references, registrations, localisation, assets, and required suite cases.',
159
+ description: 'Check a declarative package manifest against active definitions, calls, registrations, localisation, assets and required suite cases. Example: {"manifest":{"schemaVersion":"1.0","id":"my_package","definitions":[{"kind":"scripted_effect","id":"my_effect"}]}}. Returns present, absent, shadowed and unresolved counts inline.',
140
160
  inputSchema: packageInputSchema,
141
161
  outputSchema: packageOutputSchema,
142
162
  annotations: readOnlyAnalysisTool,
@@ -144,7 +164,7 @@ export const analysisTaskTools = [
144
164
  {
145
165
  name: 'hoi4.scenario_test',
146
166
  title: 'Run scenario suite',
147
- description: 'Run named inline or workspace-relative cases through bounded typed read-only domain services. Large suites return an authenticated continuation.',
167
+ description: 'Run a suite of named mechanic, package or read-only tool cases, inline or from a workspace file, in resumable revision-bound batches. Example: {"suitePath":"tests/my_suite.json","maxCases":20}. Returns completed, failed, unresolved and pending counts, and a continuation while cases remain.',
148
168
  inputSchema: scenarioInputSchema,
149
169
  outputSchema: scenarioOutputSchema,
150
170
  annotations: readOnlyAnalysisTool,
@@ -1,11 +1,14 @@
1
1
  import { registerLegacyTaskTools } from '../server/legacy-task-tools.js';
2
2
  import { z } from 'zod/v4';
3
3
  import { helperExpansionRequestSchema, helperExpansionSummarySchema, } from '../../schemas/helper-expansion.js';
4
- import { eventDirectionSchema, eventGraphReferenceSchema, eventImpactSubjectSchema, eventInspectModeSchema, eventProposedSourceSchema, eventRenderViewSchema, eventSelectorSchema, eventInspectRequestSchema, eventRenderRequestSchema, eventCompareRequestSchema, validateEventCompareRequest, validateEventInspectRequest, eventStateSubjectSchema, } from '../../schemas/event.js';
4
+ import { eventDirectionSchema, eventGraphReferenceSchema, eventImpactSubjectSchema, eventInspectModeSchema, eventProposedSourceSchema, eventRenderViewSchema, eventSelectorSchema, eventInspectRequestSchema, eventRenderRequestSchema, validateEventInspectToolRequest, eventStateSubjectSchema, } from '../../schemas/event.js';
5
5
  import { compactValidatedInputSchema } from '../server/context-schemas.js';
6
6
  import { nonNegativeIntegerSchema, sha256Schema } from '../server/output-schemas.js';
7
7
  import { strictOperationResultSchema } from '../server/result.js';
8
- const nestedSelectorSchema = compactValidatedInputSchema(eventSelectorSchema, 'Event selector; see docs/events.md.');
8
+ // The selector is published in full: it is the field agents most often get wrong. A plain
9
+ // string ("my_ns.12", "my_ns", "events/file.txt") is also accepted and normalized.
10
+ const nestedSelectorSchema = eventSelectorSchema;
11
+ const nestedEndpointSchema = compactValidatedInputSchema(eventSelectorSchema, 'Same shape as selector, or a plain event ID string.');
9
12
  const nestedStateSubjectSchema = compactValidatedInputSchema(eventStateSubjectSchema, 'State kind and name.');
10
13
  const nestedImpactSubjectSchema = compactValidatedInputSchema(eventImpactSubjectSchema, 'Impact kind and name.');
11
14
  const nestedGraphReferenceSchema = compactValidatedInputSchema(eventGraphReferenceSchema, 'Cached revision or graph artifact URI.');
@@ -14,30 +17,23 @@ const eventInspectInputSchema = z
14
17
  .object({
15
18
  ...eventInspectRequestSchema.shape,
16
19
  selector: nestedSelectorSchema.optional(),
17
- from: nestedSelectorSchema.optional(),
18
- to: nestedSelectorSchema.optional(),
20
+ from: nestedEndpointSchema.optional(),
21
+ to: nestedEndpointSchema.optional(),
19
22
  stateSubject: nestedStateSubjectSchema.optional(),
20
23
  impactSubject: nestedImpactSubjectSchema.optional(),
21
24
  helperExpansion: compactValidatedInputSchema(helperExpansionRequestSchema, 'Bounded helper paths; opaque continuationUri resumes the same source and roots. See docs/events.md.').optional(),
25
+ before: nestedGraphReferenceSchema.optional(),
26
+ after: nestedGraphReferenceSchema.optional(),
27
+ proposedSources: z.array(nestedProposedSourceSchema).min(1).max(64).optional(),
22
28
  })
23
29
  .strict()
24
- .superRefine(validateEventInspectRequest);
30
+ .superRefine(validateEventInspectToolRequest);
25
31
  const eventRenderInputSchema = z
26
32
  .object({
27
33
  ...eventRenderRequestSchema.shape,
28
34
  selector: nestedSelectorSchema.optional(),
29
35
  })
30
36
  .strict();
31
- const eventCompareInputSchema = z
32
- .object({
33
- ...eventCompareRequestSchema.shape,
34
- selector: nestedSelectorSchema.optional(),
35
- before: nestedGraphReferenceSchema.optional(),
36
- after: nestedGraphReferenceSchema.optional(),
37
- proposedSources: z.array(nestedProposedSourceSchema).min(1).max(64).optional(),
38
- })
39
- .strict()
40
- .superRefine(validateEventCompareRequest);
41
37
  const eventGraphCountsSchema = z
42
38
  .object({
43
39
  events: nonNegativeIntegerSchema,
@@ -74,7 +70,7 @@ const renderHashSchema = z
74
70
  html: sha256Schema.optional(),
75
71
  })
76
72
  .strict();
77
- const eventInspectOutputSchema = strictOperationResultSchema(z
73
+ const eventInspectDataSchema = z
78
74
  .object({
79
75
  mode: eventInspectModeSchema,
80
76
  analysisMode: z.enum(['full', 'focused']),
@@ -84,7 +80,7 @@ const eventInspectOutputSchema = strictOperationResultSchema(z
84
80
  helperExpansion: helperExpansionSummarySchema.optional(),
85
81
  boundary: inspectBoundarySchema,
86
82
  })
87
- .strict());
83
+ .strict();
88
84
  const eventRenderOutputSchema = strictOperationResultSchema(z
89
85
  .object({
90
86
  view: eventRenderViewSchema,
@@ -110,7 +106,7 @@ const eventRenderOutputSchema = strictOperationResultSchema(z
110
106
  .strict(),
111
107
  })
112
108
  .strict());
113
- const eventCompareOutputSchema = strictOperationResultSchema(z
109
+ const eventCompareDataSchema = z
114
110
  .object({
115
111
  beforeRevision: sha256Schema,
116
112
  afterRevision: sha256Schema,
@@ -161,7 +157,8 @@ const eventCompareOutputSchema = strictOperationResultSchema(z
161
157
  })
162
158
  .strict(),
163
159
  })
164
- .strict());
160
+ .strict();
161
+ const eventInspectOutputSchema = strictOperationResultSchema(z.union([eventInspectDataSchema, eventCompareDataSchema]));
165
162
  const readOnlyEventTool = {
166
163
  readOnlyHint: true,
167
164
  destructiveHint: false,
@@ -172,7 +169,7 @@ export const eventTaskTools = [
172
169
  {
173
170
  name: 'hoi4.event_inspect',
174
171
  title: 'Inspect event chains',
175
- description: 'Scan, find roots, trace, explain paths, inspect state flow, lint, or assess impact. Full source-linked reports are resources.',
172
+ description: 'Scan, find roots, trace, explain paths, inspect state flow, lint, assess impact or compare (mode compare) event graphs. Example: {"mode":"trace","selector":"my_mod.1","direction":"downstream"}; a selector is an event ID, a namespace, my_mod.* or a file. Returns counts and the main findings inline; the full source-linked report is a resource.',
176
173
  inputSchema: eventInspectInputSchema,
177
174
  outputSchema: eventInspectOutputSchema,
178
175
  annotations: readOnlyEventTool,
@@ -180,19 +177,11 @@ export const eventTaskTools = [
180
177
  {
181
178
  name: 'hoi4.event_render',
182
179
  title: 'Render event chains',
183
- description: 'Render deterministic JSON, SVG, PNG, and optional HTML for an event-chain view. Complete artifacts retain source links.',
180
+ description: 'Render a source-linked event view (overview, neighborhood, options, timing, state, scope, unresolved and more) as JSON, SVG and PNG. Example: {"view":"overview","selector":"my_mod"}. Images are linked artifacts; calls that cannot be resolved statically stay explicit unresolved edges.',
184
181
  inputSchema: eventRenderInputSchema,
185
182
  outputSchema: eventRenderOutputSchema,
186
183
  annotations: readOnlyEventTool,
187
184
  },
188
- {
189
- name: 'hoi4.event_compare',
190
- title: 'Compare event chains',
191
- description: 'Compare cached, artifact-backed, current, or in-memory proposed event graphs, optionally selecting one downstream chain. Full changes and explicit selection coverage are resources.',
192
- inputSchema: eventCompareInputSchema,
193
- outputSchema: eventCompareOutputSchema,
194
- annotations: readOnlyEventTool,
195
- },
196
185
  ];
197
186
  export function registerEventTools(server) {
198
187
  registerLegacyTaskTools(server, eventTaskTools);
@@ -135,23 +135,15 @@ export const focusTaskTools = [
135
135
  {
136
136
  name: 'hoi4.focus_inspect',
137
137
  title: 'Inspect focus trees',
138
- description: 'Inspect focus trees and continuous palettes, including source references, diagnostics, and layout.',
138
+ description: 'Read national focus trees and continuous-focus palettes: structure, layout, source references and diagnostics. Example: {"treeId":"my_focus_tree"}. Returns counts and the main diagnostics inline; the full report is a linked resource.',
139
139
  inputSchema: focusInspectRequestSchema,
140
140
  outputSchema: focusInspectOutputSchema,
141
141
  annotations: artifactProducing,
142
142
  },
143
143
  {
144
144
  name: 'hoi4.focus_render',
145
- title: 'Render focus review artifacts',
146
- description: 'Render focus HTML, SVG, JSON, and source maps. Use hoi4.focus_raster for decoded icons and PNG.',
147
- inputSchema: focusRenderRequestSchema,
148
- outputSchema: focusRenderOutputSchema,
149
- annotations: artifactProducing,
150
- },
151
- {
152
- name: 'hoi4.focus_raster',
153
- title: 'Rasterize focus review artifacts',
154
- description: 'Render focus PNG with decoded source icons; also returns HTML, SVG, and JSON.',
145
+ title: 'Render a focus tree or continuous palette',
146
+ description: 'Render a focus tree or palette as a PNG with decoded icons plus HTML, SVG and JSON source maps (png:false for vector only; cropFocusIds adds close-ups). Example: {"relativePath":"common/national_focus/my_tree.txt","treeId":"my_focus_tree"}. Images are linked artifacts, not inline.',
155
147
  inputSchema: focusRenderRequestSchema,
156
148
  outputSchema: focusRenderOutputSchema,
157
149
  annotations: artifactProducing,
@@ -159,7 +151,7 @@ export const focusTaskTools = [
159
151
  {
160
152
  name: 'hoi4.focus_rewrite',
161
153
  title: 'Create or clean up focus content',
162
- description: 'Create or compact a focus tree or palette from a complete plan. Use treeId for existing trees and createIfMissing for new files.',
154
+ description: 'Create a focus tree or palette from a complete plan (createIfMissing), or re-lay out an existing tree without a plan. Example: {"relativePath":"common/national_focus/my_tree.txt","treeId":"my_focus_tree","layoutMode":"compact"}. Validates, writes atomically and returns the changed files with a rollback-able transaction.',
163
155
  inputSchema: focusRewriteRequestSchema,
164
156
  outputSchema: focusPlanOutputSchema,
165
157
  annotations: {
@@ -72,7 +72,7 @@ export const guiTaskTools = [
72
72
  {
73
73
  name: 'hoi4.gui_inspect',
74
74
  title: 'Inspect scripted GUI',
75
- description: 'Inspect GUI sources and scenarios.',
75
+ description: 'Inspect a scripted GUI window with its linked .gui, .gfx, scripted-GUI, localisation and font sources, and layout diagnostics. Example: {"windowName":"my_window"}. Returns element counts and fidelity inline; the full scene is a linked resource.',
76
76
  inputSchema: guiInspectRequestSchema,
77
77
  outputSchema: guiScanOutputSchema,
78
78
  annotations: artifactProducing,
@@ -80,7 +80,7 @@ export const guiTaskTools = [
80
80
  {
81
81
  name: 'hoi4.gui_render',
82
82
  title: 'Render scripted GUI artifacts',
83
- description: 'Render GUI states, resolutions, scenarios, hierarchy, comparisons, and diagnostics.',
83
+ description: 'Render a GUI window under a scenario at several states and resolutions, with click regions, hierarchy and a diagnostics report. Example: {"windowName":"my_window","scenario":{"id":"normal","flags":{"my_flag":true}}}. Returns a gallery of about 25 linked artifacts; undecided visibility stays marked unresolved.',
84
84
  inputSchema: guiRenderRequestSchema,
85
85
  outputSchema: guiRenderOutputSchema,
86
86
  annotations: artifactProducing,
@@ -88,7 +88,7 @@ export const guiTaskTools = [
88
88
  {
89
89
  name: 'hoi4.gui_rewrite',
90
90
  title: 'Create or clean up scripted GUI',
91
- description: 'Apply one validated source, helper, or exact-patch GUI package. Text dependencies use additionalFiles; binary art stays workspace-referenced.',
91
+ description: 'Apply one validated GUI change as complete source, structured helpers or exact patches, plus extra text files (additionalFiles); binary art must already be in the mod. Example: {"mode":"helpers","relativePath":"interface/my_window.gui","windowName":"my_window","scenario":{"id":"normal"},"helper":{...}}. Writes atomically; returns changed files and a rollback-able transaction.',
92
92
  inputSchema: guiRewriteRequestSchema,
93
93
  outputSchema: guiPlanOutputSchema,
94
94
  annotations: {
@@ -5,7 +5,7 @@ export const jobControlTools = [
5
5
  {
6
6
  name: 'hoi4.job_inspect',
7
7
  title: 'Inspect persistent job',
8
- description: 'Inspect a durable background operation. A completed job returns the original tool result; active and terminal non-result states return bounded status evidence.',
8
+ description: 'Inspect a background operation by jobId (from a native task id): status while it runs, the original tool result once it completed. Example: {"jobId":"job_<64 hex characters>"}. Returns bounded status evidence.',
9
9
  inputSchema: jobReferenceSchema,
10
10
  annotations: {
11
11
  readOnlyHint: true,
@@ -17,7 +17,7 @@ export const jobControlTools = [
17
17
  {
18
18
  name: 'hoi4.job_cancel',
19
19
  title: 'Cancel persistent job',
20
- description: 'Durably request cancellation of an authorized background operation. Completed results remain completed.',
20
+ description: 'Durably request cancellation of a background operation by jobId. A completed result stays completed. Example: {"jobId":"job_<64 hex characters>"}.',
21
21
  inputSchema: jobReferenceSchema,
22
22
  outputSchema: jobStatusOutputSchema,
23
23
  annotations: {
@@ -1,6 +1,6 @@
1
1
  import { registerLegacyTaskTools } from '../server/legacy-task-tools.js';
2
2
  import { z } from 'zod/v4';
3
- import { mapInspectRequestSchema, mapRenderRequestSchema, mapRewriteRequestSchema, } from '../../schemas/map-requests.js';
3
+ import { mapInspectRequestSchema, mapRewriteRequestSchema } from '../../schemas/map-requests.js';
4
4
  import { allocationEvidenceSchema, bitmapRenderHashesSchema, nonNegativeIntegerSchema, operationBlockerDataSchema, sha256Schema, } from '../server/output-schemas.js';
5
5
  import { strictOperationResultSchema } from '../server/result.js';
6
6
  const changedBoundsSchema = z
@@ -55,7 +55,7 @@ const mapSemanticDiffSchema = z
55
55
  normalAdjacenciesChanged: z.boolean(),
56
56
  })
57
57
  .strict();
58
- const mapWorkspaceInspectOutputSchema = strictOperationResultSchema(z
58
+ const mapWorkspaceInspectDataSchema = z
59
59
  .object({
60
60
  revision: sha256Schema,
61
61
  sharedRevision: sha256Schema,
@@ -78,9 +78,13 @@ const mapWorkspaceInspectOutputSchema = strictOperationResultSchema(z
78
78
  lookupOnly: z.boolean().optional(),
79
79
  unknownProvinceIds: z.array(nonNegativeIntegerSchema).max(512),
80
80
  missingGeometryProvinceIds: z.array(nonNegativeIntegerSchema).max(512),
81
+ scriptReferences: z
82
+ .object({ references: nonNegativeIntegerSchema, missing: nonNegativeIntegerSchema })
83
+ .strict()
84
+ .optional(),
81
85
  })
82
- .strict());
83
- const mapRenderOutputSchema = strictOperationResultSchema(z
86
+ .strict();
87
+ const mapRenderDataSchema = z
84
88
  .object({
85
89
  revision: sha256Schema,
86
90
  width: nonNegativeIntegerSchema,
@@ -97,7 +101,8 @@ const mapRenderOutputSchema = strictOperationResultSchema(z
97
101
  .strict()
98
102
  .optional(),
99
103
  })
100
- .strict());
104
+ .strict();
105
+ const mapInspectOutputSchema = strictOperationResultSchema(z.union([mapWorkspaceInspectDataSchema, mapRenderDataSchema]));
101
106
  const mapPlanOutputSchema = strictOperationResultSchema(z
102
107
  .object({
103
108
  execution: z.enum(['applied', 'blocked', 'unchanged']),
@@ -123,23 +128,15 @@ export const mapTaskTools = [
123
128
  {
124
129
  name: 'hoi4.map_inspect',
125
130
  title: 'Inspect HOI4 map',
126
- description: 'Validate and inspect the complete map, or use lookupOnly for bounded coordinate and entity lookups.',
131
+ description: 'Validate the map with a searchable navigator, look up provinces, states, regions, names or coordinates (lookupOnly for speed), render a layer, tile or area, or check script references (references:true). Example: {"query":"Brandenburg","lookupOnly":true}. Returns counts and matches inline; PNG, HTML and the catalog are artifacts.',
127
132
  inputSchema: mapInspectRequestSchema,
128
- outputSchema: mapWorkspaceInspectOutputSchema,
129
- annotations: artifactProducing,
130
- },
131
- {
132
- name: 'hoi4.map_render',
133
- title: 'Render map inspection artifacts',
134
- description: 'Render searchable full-map PNG, JSON, and HTML, or a reusable source-coordinate tile.',
135
- inputSchema: mapRenderRequestSchema,
136
- outputSchema: mapRenderOutputSchema,
133
+ outputSchema: mapInspectOutputSchema,
137
134
  annotations: artifactProducing,
138
135
  },
139
136
  {
140
137
  name: 'hoi4.map_rewrite',
141
138
  title: 'Create or clean up map content',
142
- description: 'Apply map creation, edits, and ID swaps with visual and semantic evidence.',
139
+ description: 'Create a world, or create, split, merge, move and renumber states and provinces, regenerate positions and supply, and remap script references. Example: {"operations":[{"id":"s1","kind":"create_state","provinceIds":[120,121],"displayName":"New State"}]}. All or nothing: a failed operation leaves no partial files. Returns changed IDs, semantic diff and a render.',
143
140
  inputSchema: mapRewriteRequestSchema,
144
141
  outputSchema: mapPlanOutputSchema,
145
142
  annotations: {