hoi4-agent-tools 3.10.0 → 4.0.1

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 (163) hide show
  1. package/CHANGELOG.md +131 -0
  2. package/README.md +91 -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/condition-evaluator.js +18 -0
  9. package/dist/hoi4_agent_tools/core/configuration.js +7 -0
  10. package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +12 -0
  11. package/dist/hoi4_agent_tools/core/domain-tools.js +65 -0
  12. package/dist/hoi4_agent_tools/core/domain-validation.js +5 -2
  13. package/dist/hoi4_agent_tools/core/engine.js +29 -2
  14. package/dist/hoi4_agent_tools/core/index.js +2 -2
  15. package/dist/hoi4_agent_tools/core/job-executor.js +14 -1
  16. package/dist/hoi4_agent_tools/core/job-operation-registration.js +15 -7
  17. package/dist/hoi4_agent_tools/core/job-store.js +78 -5
  18. package/dist/hoi4_agent_tools/core/job-transactions.js +31 -2
  19. package/dist/hoi4_agent_tools/core/job-worker-host.js +314 -127
  20. package/dist/hoi4_agent_tools/core/job-worker.js +96 -37
  21. package/dist/hoi4_agent_tools/core/operation-result.js +123 -10
  22. package/dist/hoi4_agent_tools/core/operation-tasks.js +49 -89
  23. package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +25 -21
  24. package/dist/hoi4_agent_tools/core/server-registry.js +22 -5
  25. package/dist/hoi4_agent_tools/core/shared-request-capacity.js +26 -1
  26. package/dist/hoi4_agent_tools/core/source/cache.js +20 -10
  27. package/dist/hoi4_agent_tools/core/source/lexer.js +11 -3
  28. package/dist/hoi4_agent_tools/core/temporary-directories.js +293 -0
  29. package/dist/hoi4_agent_tools/core/transactions.js +114 -23
  30. package/dist/hoi4_agent_tools/core/worker-exit.js +80 -0
  31. package/dist/hoi4_agent_tools/core/workspace.js +84 -8
  32. package/dist/hoi4_agent_tools/decision/ai.js +1 -1
  33. package/dist/hoi4_agent_tools/decision/service.js +28 -0
  34. package/dist/hoi4_agent_tools/decision/source-inventory.js +7 -5
  35. package/dist/hoi4_agent_tools/event/job-operations.js +9 -5
  36. package/dist/hoi4_agent_tools/event/operations.js +14 -1
  37. package/dist/hoi4_agent_tools/focus/job-operations.js +1 -2
  38. package/dist/hoi4_agent_tools/focus/tool-operations.js +1 -1
  39. package/dist/hoi4_agent_tools/gui/assets.js +3 -0
  40. package/dist/hoi4_agent_tools/gui/dds.js +8 -4
  41. package/dist/hoi4_agent_tools/gui/layout.js +45 -5
  42. package/dist/hoi4_agent_tools/gui/native-state.js +244 -0
  43. package/dist/hoi4_agent_tools/gui/renderer.js +12 -7
  44. package/dist/hoi4_agent_tools/gui/source-graph.js +17 -4
  45. package/dist/hoi4_agent_tools/gui/studio.js +33 -21
  46. package/dist/hoi4_agent_tools/gui/validators.js +8 -6
  47. package/dist/hoi4_agent_tools/map/bmp.js +1 -1
  48. package/dist/hoi4_agent_tools/map/derived.js +748 -0
  49. package/dist/hoi4_agent_tools/map/job-operations.js +7 -3
  50. package/dist/hoi4_agent_tools/map/model.js +103 -0
  51. package/dist/hoi4_agent_tools/map/operations.js +427 -58
  52. package/dist/hoi4_agent_tools/map/references.js +483 -0
  53. package/dist/hoi4_agent_tools/map/service.js +136 -61
  54. package/dist/hoi4_agent_tools/map/tool-operations.js +2 -1
  55. package/dist/hoi4_agent_tools/map/validation.js +3 -0
  56. package/dist/hoi4_agent_tools/map/world.js +1280 -0
  57. package/dist/hoi4_agent_tools/mcp/prompts/probability.js +2 -2
  58. package/dist/hoi4_agent_tools/mcp/server/argument-errors.js +72 -0
  59. package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +23 -5
  60. package/dist/hoi4_agent_tools/mcp/server/create.js +7 -0
  61. package/dist/hoi4_agent_tools/mcp/server/instructions.js +1 -1
  62. package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +24 -30
  63. package/dist/hoi4_agent_tools/mcp/server/tool-listing.js +45 -0
  64. package/dist/hoi4_agent_tools/mcp/tools/analysis.js +28 -8
  65. package/dist/hoi4_agent_tools/mcp/tools/event.js +19 -30
  66. package/dist/hoi4_agent_tools/mcp/tools/focus.js +5 -13
  67. package/dist/hoi4_agent_tools/mcp/tools/gui.js +6 -6
  68. package/dist/hoi4_agent_tools/mcp/tools/job.js +4 -4
  69. package/dist/hoi4_agent_tools/mcp/tools/map.js +15 -18
  70. package/dist/hoi4_agent_tools/mcp/tools/probability.js +42 -48
  71. package/dist/hoi4_agent_tools/mcp/tools/reference.js +26 -9
  72. package/dist/hoi4_agent_tools/mcp/tools/technology.js +17 -30
  73. package/dist/hoi4_agent_tools/mcp/transports/http.js +28 -4
  74. package/dist/hoi4_agent_tools/mcp/transports/stdio-lifetime.js +31 -8
  75. package/dist/hoi4_agent_tools/probability/job-operations.js +4 -9
  76. package/dist/hoi4_agent_tools/probability/operations.js +175 -13
  77. package/dist/hoi4_agent_tools/probability/source-analysis.js +24 -1
  78. package/dist/hoi4_agent_tools/reference/error-log.js +359 -0
  79. package/dist/hoi4_agent_tools/reference/known-mistakes.js +307 -0
  80. package/dist/hoi4_agent_tools/reference/known-mistakes.json +154 -0
  81. package/dist/hoi4_agent_tools/reference/mod-index.js +101 -0
  82. package/dist/hoi4_agent_tools/reference/operations.js +88 -19
  83. package/dist/hoi4_agent_tools/reference/player-text.js +4 -3
  84. package/dist/hoi4_agent_tools/reference/script-validation.js +19 -1
  85. package/dist/hoi4_agent_tools/reference/service.js +6 -0
  86. package/dist/hoi4_agent_tools/runtime.js +14 -1
  87. package/dist/hoi4_agent_tools/schemas/event.js +78 -19
  88. package/dist/hoi4_agent_tools/schemas/focus-requests.js +10 -0
  89. package/dist/hoi4_agent_tools/schemas/lenient-arguments.js +266 -0
  90. package/dist/hoi4_agent_tools/schemas/map-requests.js +63 -20
  91. package/dist/hoi4_agent_tools/schemas/map.js +122 -3
  92. package/dist/hoi4_agent_tools/schemas/mod-tools.js +181 -0
  93. package/dist/hoi4_agent_tools/schemas/probability-requests.js +119 -9
  94. package/dist/hoi4_agent_tools/schemas/reference.js +13 -0
  95. package/dist/hoi4_agent_tools/schemas/scenarios.js +1 -3
  96. package/dist/hoi4_agent_tools/schemas/script-validation.js +13 -6
  97. package/dist/hoi4_agent_tools/schemas/technology.js +79 -21
  98. package/dist/hoi4_agent_tools/technology/job-operations.js +3 -4
  99. package/dist/hoi4_agent_tools/technology/operations.js +14 -1
  100. package/dist/hoi4_agent_tools/version.js +1 -1
  101. package/docs/README.md +7 -4
  102. package/docs/analysis.md +15 -2
  103. package/docs/configuration.md +281 -0
  104. package/docs/development.md +26 -2
  105. package/docs/events.md +29 -6
  106. package/docs/examples.md +13 -28
  107. package/docs/focus.md +127 -5
  108. package/docs/gui.md +105 -21
  109. package/docs/helper-expansion.md +9 -9
  110. package/docs/http.md +3 -1
  111. package/docs/jobs.md +23 -14
  112. package/docs/map.md +199 -7
  113. package/docs/mechanics.md +6 -2
  114. package/docs/probability.md +46 -16
  115. package/docs/reference.md +73 -5
  116. package/docs/setup.md +45 -14
  117. package/docs/technology.md +28 -4
  118. package/docs/third-party-notices.md +11 -0
  119. package/docs/tools.md +150 -42
  120. package/docs/troubleshooting.md +61 -0
  121. package/examples/clients/codex-config.toml +5 -2
  122. package/examples/clients/generic.json +2 -2
  123. package/examples/config/config.json +7 -0
  124. package/npm-shrinkwrap.json +151 -156
  125. package/package.json +13 -8
  126. package/schemas/configuration.schema.json +144 -128
  127. package/schemas/continuous-focus-palette.schema.json +1 -1
  128. package/schemas/custom-weighted-pool.schema.json +1 -1
  129. package/schemas/decision-inspect-input.schema.json +1 -1
  130. package/schemas/error-log-input.schema.json +55 -0
  131. package/schemas/event-feature-manifest.schema.json +1 -1
  132. package/schemas/focus-plan.schema.json +1 -1
  133. package/schemas/focus-planning-sidecar.schema.json +1 -1
  134. package/schemas/gui-animation-source.schema.json +1 -1
  135. package/schemas/gui-helper.schema.json +1 -1
  136. package/schemas/gui-scenario.schema.json +1 -1
  137. package/schemas/helper-expansion-request.schema.json +1 -1
  138. package/schemas/helper-expansion-summary.schema.json +1 -1
  139. package/schemas/impact-inspect-input.schema.json +1 -1
  140. package/schemas/map-operation.schema.json +597 -126
  141. package/schemas/mechanic-test-input.schema.json +1 -1
  142. package/schemas/mod-index-input.schema.json +51 -0
  143. package/schemas/operation-result.schema.json +2 -2
  144. package/schemas/package-check-input.schema.json +1 -1
  145. package/schemas/package-manifest.schema.json +1 -1
  146. package/schemas/probability-analysis-result.schema.json +1 -1
  147. package/schemas/probability-compare-input.schema.json +1 -1
  148. package/schemas/probability-evaluate-input.schema.json +1 -1
  149. package/schemas/probability-inspect-input.schema.json +1 -1
  150. package/schemas/probability-render-input.schema.json +1 -1
  151. package/schemas/probability-scenario-set.schema.json +1 -1
  152. package/schemas/probability-sequence-input.schema.json +1 -1
  153. package/schemas/probability-simulate-input.schema.json +1 -1
  154. package/schemas/probability-sweep-input.schema.json +1 -1
  155. package/schemas/reference-context-input.schema.json +1 -1
  156. package/schemas/reference-read-input.schema.json +1 -1
  157. package/schemas/reference-search-input.schema.json +1 -1
  158. package/schemas/scenario-suite.schema.json +3 -5
  159. package/schemas/scenario-test-input.schema.json +3 -5
  160. package/schemas/script-validate-input.schema.json +1 -1
  161. package/schemas/shared-scenario.schema.json +1 -1
  162. package/schemas/source-lookup-input.schema.json +1 -1
  163. package/server.json +3 -3
@@ -5,8 +5,8 @@ export const probabilityPromptArguments = z.object({
5
5
  });
6
6
  export const probabilityPromptDefinition = {
7
7
  name: 'hoi4.probability_analysis',
8
- title: 'Analyze HOI4 weighted logic',
9
- description: 'Plan one source-linked AI, MTTH, random, or declared-pool analysis and return to the owning modding workflow.',
8
+ title: 'Analyze AI choices and event timing',
9
+ description: 'Outline an analysis of AI choices, event timing, random chances or a custom weighted pool.',
10
10
  };
11
11
  /** One prompt body for both protocol generations; it does not run an analysis. */
12
12
  export function probabilityPrompt(input) {
@@ -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,2 +1,2 @@
1
1
  /** Shared agent-facing guidance for both MCP protocol generations. */
2
- export const SERVER_INSTRUCTIONS = 'Start with the matching inspect tool; use hoi4.probability_inspect for weights and timing. Find local documentation with hoi4.reference_context, hoi4.reference_search and hoi4.reference_read, and source definitions with hoi4.source_lookup. Inspection and rendering are read-only; rewrite tools edit mod source. Results describe offline source and scenario models, not observed game behavior. Unresolved states are not confirmed coverage. Read linked artifacts when summaries omit detail. Long operations support negotiated MCP tasks and hoi4.job_inspect/hoi4.job_cancel; background rewrites need a stable requestKey.';
2
+ export const SERVER_INSTRUCTIONS = 'Start with the matching inspect tool; use hoi4.probability_inspect for weights and timing. Find local documentation with hoi4.reference_context, hoi4.reference_search and hoi4.reference_read, and source definitions with hoi4.source_lookup. Rewrite tools edit mod source. Results describe offline source and scenario models, not observed game behavior. Unresolved states are not confirmed coverage. Read linked artifacts when summaries omit detail. Long operations support negotiated MCP tasks and hoi4.job_inspect/hoi4.job_cancel; background rewrites need a stable requestKey.';
@@ -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
@@ -92,7 +112,7 @@ const packageInputSchema = z
92
112
  const scenarioInputSchema = z
93
113
  .object({
94
114
  ...scenarioTestRequestSchema.shape,
95
- suite: compactValidatedInputSchema(scenarioSuiteSchema, 'Named mechanic, package, or fixed read-only tool cases. See docs/mechanics.md.').optional(),
115
+ suite: compactValidatedInputSchema(scenarioSuiteSchema, 'Named mechanic, package or inspection cases. See docs/mechanics.md.').optional(),
96
116
  })
97
117
  .strict()
98
118
  .superRefine((input, context) => {
@@ -111,8 +131,8 @@ const readOnlyAnalysisTool = {
111
131
  export const analysisTaskTools = [
112
132
  {
113
133
  name: 'hoi4.impact_inspect',
114
- 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.',
134
+ title: 'Find what a script change affects',
135
+ description: 'Find what a script change affects across focuses, events, decisions, ideas, windows and helpers. Trace definitions and their uses, including proposed source. 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,15 +140,15 @@ 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: 'Explain why a decision or mission is hidden, unavailable or unaffordable. Inspect conditions, costs and lifecycle for declared actors and targets, or compare proposed source. 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,
127
147
  },
128
148
  {
129
149
  name: 'hoi4.mechanic_test',
130
- 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.',
150
+ title: 'Test scripted effects',
151
+ description: 'Test what an effect, event option, decision or focus reward does to a declared state. Run steps and check assertions; unsupported effects stay unresolved. See docs/mechanics.md. Example: {"test":{"id":"t","scenario":{"schemaVersion":"1.0","id":"t","state":{"treasury":10}},"steps":[...],"assertions":[...]}}.',
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: 'Find missing pieces of a feature by checking its package manifest against definitions, calls, registrations, localisation, assets and required test 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 named mechanic, package or inspection cases from a suite or workspace file. Continue unfinished batches and report failures and unresolved cases. 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,27 +169,19 @@ 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: 'Follow event chains and explain their options, conditions, timing and state changes. Find broken calls, inspect affected events or compare revisions with mode compare. 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,
179
176
  },
180
177
  {
181
178
  name: 'hoi4.event_render',
182
- title: 'Render event chains',
183
- description: 'Render deterministic JSON, SVG, PNG, and optional HTML for an event-chain view. Complete artifacts retain source links.',
179
+ title: 'Preview event chains',
180
+ description: 'Preview event routes, options, timing, state and scope 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,31 +135,23 @@ 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: 'Find focus branches, prerequisites, layout problems and missing references in national trees and continuous-focus palettes. 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: 'Preview a focus tree or continuous palette',
146
+ description: 'Preview a focus tree or palette with its icons. Export PNG, HTML, SVG and JSON source maps; png:false skips the PNG and 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,
158
150
  },
159
151
  {
160
152
  name: 'hoi4.focus_rewrite',
161
- 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.',
153
+ title: 'Build or arrange a focus tree',
154
+ description: 'Create focus trees and continuous-focus palettes, or rearrange existing branches. Use createIfMissing with a complete plan to create content, or layoutMode to arrange an existing tree. Example: {"relativePath":"common/national_focus/my_tree.txt","treeId":"my_focus_tree","layoutMode":"compact"}. Returns the changed files.',
163
155
  inputSchema: focusRewriteRequestSchema,
164
156
  outputSchema: focusPlanOutputSchema,
165
157
  annotations: {
@@ -71,24 +71,24 @@ const artifactProducing = {
71
71
  export const guiTaskTools = [
72
72
  {
73
73
  name: 'hoi4.gui_inspect',
74
- title: 'Inspect scripted GUI',
75
- description: 'Inspect GUI sources and scenarios.',
74
+ title: 'Inspect a window',
75
+ description: 'Find the elements, assets and script behind a window, including layout problems, localisation and fonts. 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,
79
79
  },
80
80
  {
81
81
  name: 'hoi4.gui_render',
82
- title: 'Render scripted GUI artifacts',
83
- description: 'Render GUI states, resolutions, scenarios, hierarchy, comparisons, and diagnostics.',
82
+ title: 'Preview a window',
83
+ description: 'Preview a window at different resolutions and states. Shows control regions, element hierarchy and layout problems; unknown visibility stays unresolved. Example: {"windowName":"my_window","scenario":{"id":"normal","flags":{"my_flag":true}}}. Images and reports are linked resources.',
84
84
  inputSchema: guiRenderRequestSchema,
85
85
  outputSchema: guiRenderOutputSchema,
86
86
  annotations: artifactProducing,
87
87
  },
88
88
  {
89
89
  name: 'hoi4.gui_rewrite',
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.',
90
+ title: 'Create or edit a window',
91
+ description: 'Create or edit a window using complete source, structured helpers or exact patches. Include related text files with additionalFiles; binary art must already be in the mod. Example: {"mode":"helpers","relativePath":"interface/my_window.gui","windowName":"my_window","scenario":{"id":"normal"},"helper":{...}}. Returns the changed files.',
92
92
  inputSchema: guiRewriteRequestSchema,
93
93
  outputSchema: guiPlanOutputSchema,
94
94
  annotations: {
@@ -4,8 +4,8 @@ import { errorResult } from '../../core/operation-result.js';
4
4
  export const jobControlTools = [
5
5
  {
6
6
  name: 'hoi4.job_inspect',
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.',
7
+ title: 'Check a background task',
8
+ description: 'Check progress on a background task or retrieve its completed result. Pass jobId from the task handle. Example: {"jobId":"job_<64 hex characters>"}. Returns task status or the completed result.',
9
9
  inputSchema: jobReferenceSchema,
10
10
  annotations: {
11
11
  readOnlyHint: true,
@@ -16,8 +16,8 @@ export const jobControlTools = [
16
16
  },
17
17
  {
18
18
  name: 'hoi4.job_cancel',
19
- title: 'Cancel persistent job',
20
- description: 'Durably request cancellation of an authorized background operation. Completed results remain completed.',
19
+ title: 'Stop a background task',
20
+ description: 'Stop a background task by jobId. A result that has already completed stays available. Example: {"jobId":"job_<64 hex characters>"}.',
21
21
  inputSchema: jobReferenceSchema,
22
22
  outputSchema: jobStatusOutputSchema,
23
23
  annotations: {