hoi4-agent-tools 3.9.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.
- package/CHANGELOG.md +118 -0
- package/README.md +115 -147
- package/dist/bin/http.js +12 -0
- package/dist/bin/setup.js +3 -2
- package/dist/bin/stdio.js +12 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +40 -5
- package/dist/hoi4_agent_tools/core/canonical.js +18 -4
- package/dist/hoi4_agent_tools/core/configuration.js +7 -0
- package/dist/hoi4_agent_tools/core/domain-scan-patterns.js +12 -0
- package/dist/hoi4_agent_tools/core/domain-tools.js +65 -0
- package/dist/hoi4_agent_tools/core/domain-validation.js +5 -2
- package/dist/hoi4_agent_tools/core/engine.js +56 -10
- package/dist/hoi4_agent_tools/core/index.js +45 -20
- package/dist/hoi4_agent_tools/core/job-executor.js +14 -1
- package/dist/hoi4_agent_tools/core/job-operation-registration.js +15 -7
- package/dist/hoi4_agent_tools/core/job-store.js +78 -5
- package/dist/hoi4_agent_tools/core/job-transactions.js +31 -2
- package/dist/hoi4_agent_tools/core/job-worker-host.js +215 -116
- package/dist/hoi4_agent_tools/core/job-worker.js +90 -36
- package/dist/hoi4_agent_tools/core/operation-result.js +123 -10
- package/dist/hoi4_agent_tools/core/operation-tasks.js +49 -89
- package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +25 -21
- package/dist/hoi4_agent_tools/core/server-registry.js +155 -0
- package/dist/hoi4_agent_tools/core/server-status-resource.js +22 -0
- package/dist/hoi4_agent_tools/core/shared-request-capacity.js +26 -1
- package/dist/hoi4_agent_tools/core/source/cache.js +108 -29
- package/dist/hoi4_agent_tools/core/source/lexer.js +11 -3
- package/dist/hoi4_agent_tools/core/transactions.js +114 -23
- package/dist/hoi4_agent_tools/core/workspace.js +84 -8
- package/dist/hoi4_agent_tools/decision/ai.js +1 -1
- package/dist/hoi4_agent_tools/decision/service.js +28 -0
- package/dist/hoi4_agent_tools/decision/source-inventory.js +7 -5
- package/dist/hoi4_agent_tools/event/job-operations.js +9 -5
- package/dist/hoi4_agent_tools/event/operations.js +14 -1
- package/dist/hoi4_agent_tools/event/service.js +7 -0
- package/dist/hoi4_agent_tools/focus/job-operations.js +1 -2
- package/dist/hoi4_agent_tools/focus/tool-operations.js +1 -1
- package/dist/hoi4_agent_tools/gui/scenario-generator.js +11 -3
- package/dist/hoi4_agent_tools/gui/studio.js +20 -7
- package/dist/hoi4_agent_tools/gui/validators.js +8 -6
- package/dist/hoi4_agent_tools/map/bmp.js +1 -1
- package/dist/hoi4_agent_tools/map/derived.js +748 -0
- package/dist/hoi4_agent_tools/map/job-operations.js +7 -3
- package/dist/hoi4_agent_tools/map/model.js +103 -0
- package/dist/hoi4_agent_tools/map/operations.js +427 -58
- package/dist/hoi4_agent_tools/map/references.js +483 -0
- package/dist/hoi4_agent_tools/map/service.js +136 -61
- package/dist/hoi4_agent_tools/map/tool-operations.js +2 -1
- package/dist/hoi4_agent_tools/map/validation.js +3 -0
- package/dist/hoi4_agent_tools/map/world.js +1280 -0
- package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +13 -1
- package/dist/hoi4_agent_tools/mcp/resources/register.js +6 -0
- package/dist/hoi4_agent_tools/mcp/server/argument-errors.js +72 -0
- package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +23 -5
- package/dist/hoi4_agent_tools/mcp/server/create.js +7 -0
- package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +24 -30
- package/dist/hoi4_agent_tools/mcp/server/tool-listing.js +45 -0
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +25 -5
- package/dist/hoi4_agent_tools/mcp/tools/event.js +18 -29
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +4 -12
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +3 -3
- package/dist/hoi4_agent_tools/mcp/tools/job.js +2 -2
- package/dist/hoi4_agent_tools/mcp/tools/map.js +13 -16
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +42 -48
- package/dist/hoi4_agent_tools/mcp/tools/reference.js +22 -5
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +16 -26
- package/dist/hoi4_agent_tools/mcp/transports/http.js +28 -4
- package/dist/hoi4_agent_tools/mcp/transports/stdio-lifetime.js +37 -18
- package/dist/hoi4_agent_tools/probability/job-operations.js +4 -9
- package/dist/hoi4_agent_tools/probability/operations.js +175 -13
- package/dist/hoi4_agent_tools/probability/source-analysis.js +24 -1
- package/dist/hoi4_agent_tools/reference/error-log.js +359 -0
- package/dist/hoi4_agent_tools/reference/known-mistakes.js +307 -0
- package/dist/hoi4_agent_tools/reference/known-mistakes.json +154 -0
- package/dist/hoi4_agent_tools/reference/mod-index.js +101 -0
- package/dist/hoi4_agent_tools/reference/operations.js +88 -19
- package/dist/hoi4_agent_tools/reference/player-text.js +4 -3
- package/dist/hoi4_agent_tools/reference/script-validation.js +19 -1
- package/dist/hoi4_agent_tools/reference/service.js +6 -0
- package/dist/hoi4_agent_tools/schemas/event.js +78 -19
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +10 -0
- package/dist/hoi4_agent_tools/schemas/lenient-arguments.js +266 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +63 -20
- package/dist/hoi4_agent_tools/schemas/map.js +122 -3
- package/dist/hoi4_agent_tools/schemas/mod-tools.js +181 -0
- package/dist/hoi4_agent_tools/schemas/probability-requests.js +119 -9
- package/dist/hoi4_agent_tools/schemas/reference.js +13 -0
- package/dist/hoi4_agent_tools/schemas/scenarios.js +1 -3
- package/dist/hoi4_agent_tools/schemas/script-validation.js +13 -6
- package/dist/hoi4_agent_tools/schemas/technology.js +83 -23
- package/dist/hoi4_agent_tools/technology/job-operations.js +3 -4
- package/dist/hoi4_agent_tools/technology/operations.js +14 -1
- package/dist/hoi4_agent_tools/technology/service.js +7 -0
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +7 -4
- package/docs/analysis.md +13 -0
- package/docs/configuration.md +281 -0
- package/docs/development.md +24 -2
- package/docs/events.md +27 -3
- package/docs/examples.md +6 -6
- package/docs/focus.md +125 -4
- package/docs/gui.md +98 -17
- package/docs/helper-expansion.md +8 -8
- package/docs/http.md +3 -1
- package/docs/jobs.md +19 -13
- package/docs/map.md +197 -6
- package/docs/mechanics.md +4 -0
- package/docs/probability.md +42 -15
- package/docs/reference.md +71 -4
- package/docs/setup.md +45 -11
- package/docs/technology.md +26 -1
- package/docs/third-party-notices.md +11 -0
- package/docs/tools.md +151 -42
- package/docs/troubleshooting.md +61 -0
- package/examples/clients/codex-config.toml +5 -2
- package/examples/clients/generic.json +2 -2
- package/examples/config/config.json +7 -0
- package/npm-shrinkwrap.json +161 -162
- package/package.json +13 -8
- package/schemas/configuration.schema.json +144 -128
- package/schemas/continuous-focus-palette.schema.json +1 -1
- package/schemas/custom-weighted-pool.schema.json +1 -1
- package/schemas/decision-inspect-input.schema.json +1 -1
- package/schemas/error-log-input.schema.json +55 -0
- package/schemas/event-feature-manifest.schema.json +1 -1
- package/schemas/focus-plan.schema.json +1 -1
- package/schemas/focus-planning-sidecar.schema.json +1 -1
- package/schemas/gui-animation-source.schema.json +1 -1
- package/schemas/gui-helper.schema.json +1 -1
- package/schemas/gui-scenario.schema.json +1 -1
- package/schemas/helper-expansion-request.schema.json +1 -1
- package/schemas/helper-expansion-summary.schema.json +1 -1
- package/schemas/impact-inspect-input.schema.json +1 -1
- package/schemas/map-operation.schema.json +597 -126
- package/schemas/mechanic-test-input.schema.json +1 -1
- package/schemas/mod-index-input.schema.json +51 -0
- package/schemas/operation-result.schema.json +2 -2
- package/schemas/package-check-input.schema.json +1 -1
- package/schemas/package-manifest.schema.json +1 -1
- package/schemas/probability-analysis-result.schema.json +1 -1
- package/schemas/probability-compare-input.schema.json +1 -1
- package/schemas/probability-evaluate-input.schema.json +1 -1
- package/schemas/probability-inspect-input.schema.json +1 -1
- package/schemas/probability-render-input.schema.json +1 -1
- package/schemas/probability-scenario-set.schema.json +1 -1
- package/schemas/probability-sequence-input.schema.json +1 -1
- package/schemas/probability-simulate-input.schema.json +1 -1
- package/schemas/probability-sweep-input.schema.json +1 -1
- package/schemas/reference-context-input.schema.json +1 -1
- package/schemas/reference-read-input.schema.json +1 -1
- package/schemas/reference-search-input.schema.json +1 -1
- package/schemas/scenario-suite.schema.json +3 -5
- package/schemas/scenario-test-input.schema.json +3 -5
- package/schemas/script-validate-input.schema.json +1 -1
- package/schemas/shared-scenario.schema.json +1 -1
- package/schemas/source-lookup-input.schema.json +1 -1
- package/server.json +3 -3
|
@@ -18,7 +18,7 @@ const scope = z.enum([
|
|
|
18
18
|
export const scriptValidateRequestSchema = z
|
|
19
19
|
.object({
|
|
20
20
|
workspaceId: workspaceIdSchema,
|
|
21
|
-
/** Snippet mode: an effect or trigger body checked under `kind`
|
|
21
|
+
/** Snippet mode: an effect or trigger body checked under `scope`; a missing `kind` is inferred. */
|
|
22
22
|
source: z.string().min(1).max(64_000).optional(),
|
|
23
23
|
/** File mode: a scanned mod file whose blocks are checked under the scope its structure fixes. */
|
|
24
24
|
path: z.string().trim().min(1).max(4096).optional(),
|
|
@@ -39,11 +39,11 @@ export const scriptValidateRequestSchema = z
|
|
|
39
39
|
path: ['source'],
|
|
40
40
|
message: 'Provide either source with kind and scope, or a mod file path',
|
|
41
41
|
});
|
|
42
|
-
if (value.source !== undefined &&
|
|
42
|
+
if (value.source !== undefined && value.scope === undefined)
|
|
43
43
|
context.addIssue({
|
|
44
44
|
code: 'custom',
|
|
45
|
-
path: ['
|
|
46
|
-
message: 'A snippet check requires
|
|
45
|
+
path: ['scope'],
|
|
46
|
+
message: 'A snippet check requires a scope, such as country or state',
|
|
47
47
|
});
|
|
48
48
|
if (value.path !== undefined &&
|
|
49
49
|
(value.kind !== undefined || value.scope !== undefined || value.bindings.length > 0))
|
|
@@ -56,7 +56,14 @@ export const scriptValidateRequestSchema = z
|
|
|
56
56
|
export const scriptValidateDataSchema = z
|
|
57
57
|
.object({
|
|
58
58
|
valid: z.boolean().nullable(),
|
|
59
|
-
checksPerformed: z.array(z.enum([
|
|
59
|
+
checksPerformed: z.array(z.enum([
|
|
60
|
+
'syntax',
|
|
61
|
+
'command_kind',
|
|
62
|
+
'declared_scope',
|
|
63
|
+
'player_text',
|
|
64
|
+
'event_popups',
|
|
65
|
+
'known_mistakes',
|
|
66
|
+
])),
|
|
60
67
|
parametersChecked: z.literal(false),
|
|
61
68
|
documentation: z
|
|
62
69
|
.object({
|
|
@@ -73,7 +80,7 @@ export const scriptValidateDataSchema = z
|
|
|
73
80
|
code: z.string().max(80),
|
|
74
81
|
status: z.enum(['supported', 'error', 'unresolved', 'warning']),
|
|
75
82
|
command: z.string().max(256),
|
|
76
|
-
kind: z.enum(['effect', 'trigger', 'text']),
|
|
83
|
+
kind: z.enum(['effect', 'trigger', 'text', 'definition']),
|
|
77
84
|
scope: z.string().max(64),
|
|
78
85
|
line: z.number().int().min(1),
|
|
79
86
|
column: z.number().int().min(1),
|
|
@@ -102,27 +102,6 @@ export function validateTechnologyInspectRequest(value, context) {
|
|
|
102
102
|
message: 'Impact mode requires impact',
|
|
103
103
|
});
|
|
104
104
|
}
|
|
105
|
-
export const technologyInspectRequestSchema = z
|
|
106
|
-
.object({
|
|
107
|
-
workspaceId: workspaceIdSchema,
|
|
108
|
-
mode: technologyAnalysisModeSchema,
|
|
109
|
-
folderId: technologyIdSchema.optional(),
|
|
110
|
-
technologyId: technologyIdSchema.optional(),
|
|
111
|
-
categoryId: technologyIdSchema.optional(),
|
|
112
|
-
targetKind: technologyUnlockKindSchema.optional(),
|
|
113
|
-
targetId: technologyIdSchema.optional(),
|
|
114
|
-
direction: technologyDirectionSchema.optional(),
|
|
115
|
-
maxDepth: z.number().int().min(1).max(256).optional(),
|
|
116
|
-
maxNodes: z.number().int().min(1).max(25_000).optional(),
|
|
117
|
-
includeSubTechnologies: z.boolean().optional(),
|
|
118
|
-
classifications: z.array(technologyDefectClassSchema).max(4).optional(),
|
|
119
|
-
codes: z.array(z.string().min(1).max(256)).max(100).optional(),
|
|
120
|
-
impact: technologyImpactSchema.optional(),
|
|
121
|
-
helperExpansion: helperExpansionRequestSchema.optional(),
|
|
122
|
-
refresh: z.boolean().optional(),
|
|
123
|
-
})
|
|
124
|
-
.strict()
|
|
125
|
-
.superRefine(validateTechnologyInspectRequest);
|
|
126
105
|
export const technologyRenderRequestSchema = z
|
|
127
106
|
.object({
|
|
128
107
|
workspaceId: workspaceIdSchema,
|
|
@@ -143,14 +122,16 @@ export const technologyRenderRequestSchema = z
|
|
|
143
122
|
refresh: z.boolean().optional(),
|
|
144
123
|
})
|
|
145
124
|
.strict()
|
|
146
|
-
.superRefine(
|
|
125
|
+
.superRefine(validateTechnologyRenderRequest);
|
|
126
|
+
/** Shared by the request schema and the published tool input, so clients see the same rule. */
|
|
127
|
+
export function validateTechnologyRenderRequest(value, context) {
|
|
147
128
|
if (value.view === 'folder' && value.folderId === undefined)
|
|
148
129
|
context.addIssue({
|
|
149
130
|
code: 'custom',
|
|
150
131
|
path: ['folderId'],
|
|
151
132
|
message: 'Folder view requires folderId',
|
|
152
133
|
});
|
|
153
|
-
}
|
|
134
|
+
}
|
|
154
135
|
export function validateTechnologyCompareRequest(value, context) {
|
|
155
136
|
if (value.after !== undefined && value.proposedSources !== undefined)
|
|
156
137
|
context.addIssue({
|
|
@@ -171,3 +152,82 @@ export const technologyCompareRequestSchema = z
|
|
|
171
152
|
})
|
|
172
153
|
.strict()
|
|
173
154
|
.superRefine(validateTechnologyCompareRequest);
|
|
155
|
+
export const technologyInspectToolModeSchema = z.enum([
|
|
156
|
+
...technologyAnalysisModeSchema.options,
|
|
157
|
+
'compare',
|
|
158
|
+
]);
|
|
159
|
+
const technologyCompareOnlyFields = [
|
|
160
|
+
'before',
|
|
161
|
+
'after',
|
|
162
|
+
'proposedSources',
|
|
163
|
+
'render',
|
|
164
|
+
'maxRenderNodes',
|
|
165
|
+
];
|
|
166
|
+
const technologyInspectOnlyFields = [
|
|
167
|
+
'folderId',
|
|
168
|
+
'technologyId',
|
|
169
|
+
'categoryId',
|
|
170
|
+
'targetKind',
|
|
171
|
+
'targetId',
|
|
172
|
+
'direction',
|
|
173
|
+
'maxDepth',
|
|
174
|
+
'maxNodes',
|
|
175
|
+
'includeSubTechnologies',
|
|
176
|
+
'classifications',
|
|
177
|
+
'codes',
|
|
178
|
+
'impact',
|
|
179
|
+
'helperExpansion',
|
|
180
|
+
];
|
|
181
|
+
/** Inspection and graph comparison share one tool; mode compare selects the comparison. */
|
|
182
|
+
export const technologyInspectRequestSchema = z
|
|
183
|
+
.object({
|
|
184
|
+
workspaceId: workspaceIdSchema,
|
|
185
|
+
mode: technologyInspectToolModeSchema,
|
|
186
|
+
folderId: technologyIdSchema.optional(),
|
|
187
|
+
technologyId: technologyIdSchema.optional(),
|
|
188
|
+
categoryId: technologyIdSchema.optional(),
|
|
189
|
+
targetKind: technologyUnlockKindSchema.optional(),
|
|
190
|
+
targetId: technologyIdSchema.optional(),
|
|
191
|
+
direction: technologyDirectionSchema.optional(),
|
|
192
|
+
maxDepth: z.number().int().min(1).max(256).optional(),
|
|
193
|
+
maxNodes: z.number().int().min(1).max(25_000).optional(),
|
|
194
|
+
includeSubTechnologies: z.boolean().optional(),
|
|
195
|
+
classifications: z.array(technologyDefectClassSchema).max(4).optional(),
|
|
196
|
+
codes: z.array(z.string().min(1).max(256)).max(100).optional(),
|
|
197
|
+
impact: technologyImpactSchema.optional(),
|
|
198
|
+
helperExpansion: helperExpansionRequestSchema.optional(),
|
|
199
|
+
before: technologyGraphReferenceSchema.optional(),
|
|
200
|
+
after: technologyGraphReferenceSchema.optional(),
|
|
201
|
+
proposedSources: z.array(technologyProposedSourceSchema).min(1).max(128).optional(),
|
|
202
|
+
render: z.boolean().optional(),
|
|
203
|
+
maxRenderNodes: z.number().int().min(1).max(2_000).optional(),
|
|
204
|
+
refresh: z.boolean().optional(),
|
|
205
|
+
})
|
|
206
|
+
.strict()
|
|
207
|
+
.superRefine(validateTechnologyInspectToolRequest);
|
|
208
|
+
export function validateTechnologyInspectToolRequest(value, context) {
|
|
209
|
+
const compare = value.mode === 'compare';
|
|
210
|
+
for (const field of compare ? technologyInspectOnlyFields : technologyCompareOnlyFields)
|
|
211
|
+
if (value[field] !== undefined)
|
|
212
|
+
context.addIssue({
|
|
213
|
+
code: 'custom',
|
|
214
|
+
path: [field],
|
|
215
|
+
message: compare
|
|
216
|
+
? `${field} is not used by mode compare`
|
|
217
|
+
: `${field} is used only by mode compare`,
|
|
218
|
+
});
|
|
219
|
+
if (compare)
|
|
220
|
+
validateTechnologyCompareRequest(value, context);
|
|
221
|
+
else
|
|
222
|
+
validateTechnologyInspectRequest(value, context);
|
|
223
|
+
}
|
|
224
|
+
/** Split an inspect request into a comparison or an analysis of the current graph. */
|
|
225
|
+
export function splitTechnologyInspectRequest(input) {
|
|
226
|
+
if (input.mode !== 'compare')
|
|
227
|
+
return { compare: false, request: input };
|
|
228
|
+
const request = { workspaceId: input.workspaceId };
|
|
229
|
+
for (const field of [...technologyCompareOnlyFields, 'refresh'])
|
|
230
|
+
if (input[field] !== undefined)
|
|
231
|
+
request[field] = input[field];
|
|
232
|
+
return { compare: true, request: technologyCompareRequestSchema.parse(request) };
|
|
233
|
+
}
|
|
@@ -2,8 +2,8 @@ import { z } from 'zod/v4';
|
|
|
2
2
|
import { IdleCacheLifetime } from '../core/idle-cache-lifetime.js';
|
|
3
3
|
import { errorResult, toolResult } from '../core/operation-result.js';
|
|
4
4
|
import { ServiceError } from '../core/result.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { technologyInspectRequestSchema, technologyRenderRequestSchema, } from '../schemas/technology.js';
|
|
6
|
+
import { inspectOrCompareTechnologies, normalizeTechnologyInspectionRequest, normalizeTechnologyRenderRequest, renderTechnologies, } from './operations.js';
|
|
7
7
|
import { TechnologyTreeViewer } from './service.js';
|
|
8
8
|
const wireResult = z.record(z.string(), z.json());
|
|
9
9
|
/** Registers typed technology operations without invoking transport handlers. */
|
|
@@ -54,7 +54,6 @@ export function registerTechnologyJobs(operations, engine) {
|
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
register('hoi4.tech_inspect', technologyInspectRequestSchema, normalizeTechnologyInspectionRequest,
|
|
57
|
+
register('hoi4.tech_inspect', technologyInspectRequestSchema, normalizeTechnologyInspectionRequest, inspectOrCompareTechnologies, 'Analyzing technology trees');
|
|
58
58
|
register('hoi4.tech_render', technologyRenderRequestSchema, normalizeTechnologyRenderRequest, renderTechnologies, 'Rendering technology-tree view');
|
|
59
|
-
register('hoi4.tech_compare', technologyCompareRequestSchema, normalizeTechnologyCompareRequest, compareTechnologies, 'Comparing technology-tree graphs');
|
|
60
59
|
}
|
|
@@ -3,11 +3,24 @@ import { helperExpansionValidation } from '../core/helper-expansion.js';
|
|
|
3
3
|
import { compareCodeUnits, hashCanonical } from '../core/canonical.js';
|
|
4
4
|
import { emptyServiceResult } from '../core/result.js';
|
|
5
5
|
import { setInlineFilesScanned } from '../core/operation-result.js';
|
|
6
|
-
import { technologyCompareRequestSchema, technologyInspectRequestSchema, technologyRenderRequestSchema, } from '../schemas/technology.js';
|
|
6
|
+
import { technologyCompareRequestSchema, technologyInspectRequestSchema, splitTechnologyInspectRequest, technologyRenderRequestSchema, } from '../schemas/technology.js';
|
|
7
7
|
import { technologyDiagnostics } from './service.js';
|
|
8
8
|
export function normalizeTechnologyInspectionRequest(input) {
|
|
9
9
|
return JSON.parse(JSON.stringify(technologyInspectRequestSchema.parse(input)));
|
|
10
10
|
}
|
|
11
|
+
/** Route mode compare to the graph comparison and every other mode to analysis. */
|
|
12
|
+
export function inspectOrCompareTechnologies(viewer, input) {
|
|
13
|
+
const split = splitTechnologyInspectRequest(input);
|
|
14
|
+
if (!split.compare)
|
|
15
|
+
return inspectTechnologies(viewer, input);
|
|
16
|
+
const { signal, principal } = input;
|
|
17
|
+
return compareTechnologies(viewer, {
|
|
18
|
+
...JSON.parse(JSON.stringify(split.request)),
|
|
19
|
+
workspaceId: input.workspaceId,
|
|
20
|
+
...(principal === undefined ? {} : { principal }),
|
|
21
|
+
...(signal === undefined ? {} : { signal }),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
11
24
|
export function normalizeTechnologyRenderRequest(input) {
|
|
12
25
|
return JSON.parse(JSON.stringify(technologyRenderRequestSchema.parse(input)));
|
|
13
26
|
}
|
|
@@ -880,6 +880,13 @@ export class TechnologyTreeViewer {
|
|
|
880
880
|
input.signal?.throwIfAborted();
|
|
881
881
|
if (input.proposedSources !== undefined)
|
|
882
882
|
assertProposedBounds(input.proposedSources);
|
|
883
|
+
// Without references the comparison uses the previous cached revision; with none cached,
|
|
884
|
+
// say so before scanning the workspace.
|
|
885
|
+
if (input.before === undefined &&
|
|
886
|
+
input.after === undefined &&
|
|
887
|
+
input.proposedSources === undefined &&
|
|
888
|
+
(this.#state.history.get(input.workspaceId)?.size ?? 0) === 0)
|
|
889
|
+
throw new ServiceError('TECH_COMPARISON_BASELINE_REQUIRED', 'Provide a cached revision, graph artifact, or proposed source overlay');
|
|
883
890
|
const current = await this.scan(input.workspaceId, {
|
|
884
891
|
refresh: input.refresh ?? input.proposedSources !== undefined,
|
|
885
892
|
...(input.principal === undefined ? {} : { principal: input.principal }),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
export const PACKAGE_NAME = 'hoi4-agent-tools';
|
|
3
|
-
export const PACKAGE_VERSION = '
|
|
3
|
+
export const PACKAGE_VERSION = '4.0.0';
|
|
4
4
|
export const MCP_PROTOCOL_VERSION = LATEST_PROTOCOL_VERSION;
|
|
5
5
|
export const CONFIG_VERSION = 1;
|
|
6
6
|
export const TRANSACTION_VERSION = 1;
|
package/docs/README.md
CHANGED
|
@@ -16,21 +16,24 @@ You can ask your coding agent for these tasks in ordinary language; you do not n
|
|
|
16
16
|
| Inspect or edit the map | [Maps](map.md) |
|
|
17
17
|
| Compare weights, chances or timing | [AI and probability](probability.md) |
|
|
18
18
|
| Test a mechanic or check a content package | [Mechanic tests](mechanics.md) |
|
|
19
|
+
| Read the game's error log or pick a free event ID | [Local references](reference.md) |
|
|
19
20
|
|
|
20
21
|
## See the results
|
|
21
22
|
|
|
22
23
|
- [Examples](examples.md): practical prompts with real renders.
|
|
23
24
|
- [Latest release](https://github.com/klimPaskov/hoi4-agent-tools/releases/latest): a short introduction to the published server.
|
|
24
|
-
- [Focus and technology comparisons](visual-comparisons.md): supplied game captures beside source renders.
|
|
25
|
-
- [Interface comparisons](gui-comparisons.md): vanilla and
|
|
25
|
+
- [Focus and technology comparisons](https://github.com/klimPaskov/hoi4-agent-tools/blob/main/docs/visual-comparisons.md): supplied game captures beside source renders.
|
|
26
|
+
- [Interface comparisons](https://github.com/klimPaskov/hoi4-agent-tools/blob/main/docs/gui-comparisons.md): vanilla and mod windows.
|
|
26
27
|
|
|
27
28
|
## Detailed reference
|
|
28
29
|
|
|
29
|
-
- [Tool reference](tools.md): tool names
|
|
30
|
+
- [Tool reference](tools.md): tool names, what each one does, and an example call for each.
|
|
31
|
+
- [Troubleshooting](troubleshooting.md): error codes and what to do about them.
|
|
32
|
+
- [Configuration](configuration.md): every setting, default location and environment variable.
|
|
30
33
|
- [Background jobs](jobs.md): inspect, cancel or retrieve long operations.
|
|
31
34
|
- [Large event and technology queries](helper-expansion.md): follow bounded results without losing coverage.
|
|
32
35
|
- [Remote hosting](http.md): share the server over HTTP.
|
|
33
36
|
- [Development](development.md): build, test and contribute.
|
|
34
|
-
- [Architecture decisions](adr/README.md): implementation rationale for contributors.
|
|
37
|
+
- [Architecture decisions](https://github.com/klimPaskov/hoi4-agent-tools/blob/main/docs/adr/README.md): implementation rationale for contributors.
|
|
35
38
|
|
|
36
39
|
For vulnerability reporting, use the [security policy](../SECURITY.md).
|
package/docs/analysis.md
CHANGED
|
@@ -18,6 +18,19 @@ Use a symbol when you know the definition you plan to change:
|
|
|
18
18
|
|
|
19
19
|
Use `changedFiles` with scanned display paths such as `mod:common/ideas/rationing.txt` to seed every active definition in a file.
|
|
20
20
|
Use `proposedSources` to add, replace, or remove files in memory under the authorized mod root; a `null` content removes a source from the proposed snapshot.
|
|
21
|
+
Each entry is an object with `relativePath` and `content`, and a request takes between 1 and 64 entries of at most 8 MiB each:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"proposedSources": [
|
|
26
|
+
{ "relativePath": "common/ideas/my_ideas.txt", "content": "ideas = { ... }" },
|
|
27
|
+
{ "relativePath": "common/ideas/my_old_ideas.txt", "content": null }
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Impact and decision proposals name the text field `content`.
|
|
33
|
+
Event and technology proposals for `hoi4.event_inspect` and `hoi4.tech_inspect` with `mode: compare` name the same field `source` and also accept an optional `expectedSourceHash`.
|
|
21
34
|
When `changedFiles` is omitted, each proposed file seeds all its active definitions alongside any selected symbols.
|
|
22
35
|
The report compares the original and proposed consumer graph, including added and removed consumers and affected files.
|
|
23
36
|
Moving a source location within one file does not count an unchanged reference as a new or removed consumer.
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Most users need no configuration file: start the server inside a mod folder and it configures itself for that mod.
|
|
4
|
+
When you want fixed game and mod paths or several mods, `hoi4-agent-tools-setup --init` writes a file for you, as described in [Setup](setup.md).
|
|
5
|
+
|
|
6
|
+
## Where the file lives
|
|
7
|
+
|
|
8
|
+
The server reads one JSON file, chosen in this order:
|
|
9
|
+
|
|
10
|
+
1. the path after `--config`, for example `hoi4-agent-tools --config /srv/hoi4/config.json`;
|
|
11
|
+
2. the path in the `HOI4_AGENT_CONFIG` environment variable;
|
|
12
|
+
3. the default path for your system.
|
|
13
|
+
|
|
14
|
+
| System | Default configuration path |
|
|
15
|
+
| --------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
16
|
+
| Windows | `%APPDATA%\hoi4-agent-tools\config.json` |
|
|
17
|
+
| Linux and macOS | `$XDG_CONFIG_HOME/hoi4-agent-tools/config.json`, or `~/.config/hoi4-agent-tools/config.json` when `XDG_CONFIG_HOME` is unset |
|
|
18
|
+
|
|
19
|
+
`APPDATA` takes precedence over `XDG_CONFIG_HOME` when both are set.
|
|
20
|
+
|
|
21
|
+
The server builds an automatic configuration only when all of these hold: `HOI4_AGENT_CONFIG` is unset, `--config` is absent or names the default path, and no file exists at the default path.
|
|
22
|
+
In every other case a missing or unreadable file stops startup with `CONFIG_READ_FAILED`, and a file that fails validation stops it with `CONFIG_INVALID`.
|
|
23
|
+
|
|
24
|
+
The automatic configuration works like this:
|
|
25
|
+
|
|
26
|
+
- Starting from the server's working directory and moving up through its parents, the server takes the first folder that looks like a mod: it has `descriptor.mod`, a standard `common/` subfolder such as `common/national_focus`, or a standard content folder such as `events/` or `localisation/` with matching files.
|
|
27
|
+
The search stops at a game installation (a folder with `hoi4.exe`, `run_hoi4` or `steam_appid.txt`), and startup fails with `AUTO_MOD_ROOT_NOT_FOUND` when nothing is found.
|
|
28
|
+
- That folder becomes the only workspace, with the ID `auto_<folder name>`.
|
|
29
|
+
- The game folder is the first existing folder among `HOI4_GAME_ROOT` and the standard Steam locations on Windows, Linux and macOS.
|
|
30
|
+
- Server state and generated files use the default storage locations below.
|
|
31
|
+
|
|
32
|
+
| Storage | Default location |
|
|
33
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
34
|
+
| `serverStateRoot` | `$XDG_STATE_HOME/hoi4-agent-tools/state`, else `%LOCALAPPDATA%\hoi4-agent-tools\state`, else `~/.local/state/hoi4-agent-tools/state` |
|
|
35
|
+
| `workspaceStorageRoot` | `$XDG_DATA_HOME/hoi4-agent-tools/workspaces`, else `%LOCALAPPDATA%\hoi4-agent-tools\workspaces`, else `~/.local/share/hoi4-agent-tools/workspaces` |
|
|
36
|
+
|
|
37
|
+
`hoi4-agent-tools-setup --init` writes these same two locations into the file it creates.
|
|
38
|
+
A hand-written file has no storage defaults: set `serverStateRoot` yourself, and leave out `workspaceStorageRoot` only if generated files may live inside each mod.
|
|
39
|
+
|
|
40
|
+
What each location holds:
|
|
41
|
+
|
|
42
|
+
- `serverStateRoot` holds the persistent index and parsed-source cache (`analysis-cache/`), background jobs (`jobs/`, `job-workers/`), the records of running server processes (`servers/`) and the key that authenticates rewrite journals.
|
|
43
|
+
- `workspaceStorageRoot` holds one folder per workspace ID, with `artifacts/` for rendered images and other generated results and `cache/` for rewrite transactions and locks.
|
|
44
|
+
- Without `workspaceStorageRoot`, a mod workspace keeps these in `.hoi4-agent/artifacts` and `.hoi4-agent/cache` inside the mod folder.
|
|
45
|
+
|
|
46
|
+
## A minimal file
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"version": 1,
|
|
51
|
+
"serverStateRoot": "D:/hoi4-agent-tools/state",
|
|
52
|
+
"modRoots": ["D:/hoi4-mods"],
|
|
53
|
+
"gameRoot": "C:/Program Files (x86)/Steam/steamapps/common/Hearts of Iron IV",
|
|
54
|
+
"workspaceStorageRoot": "D:/hoi4-agent-tools/workspaces"
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Every subfolder of a `modRoots` entry becomes a mod workspace, and every key not shown takes its default.
|
|
59
|
+
Unknown keys are rejected at every level, so a misspelled key stops startup instead of being ignored.
|
|
60
|
+
The full machine-readable definition is the [configuration JSON schema](../schemas/configuration.schema.json), and [config.json](../examples/config/config.json) is the same example with Linux paths.
|
|
61
|
+
|
|
62
|
+
## How the server picks your mod
|
|
63
|
+
|
|
64
|
+
Every tool takes a `workspaceId` argument, which defaults to `"current"`.
|
|
65
|
+
|
|
66
|
+
For `"current"`, the server asks the MCP client for its workspace roots when the client supports them:
|
|
67
|
+
|
|
68
|
+
- It selects the one mod workspace that contains a client root or lies inside one.
|
|
69
|
+
- No match fails with `WORKSPACE_CONTEXT_NOT_FOUND`, and more than one match fails with `WORKSPACE_CONTEXT_AMBIGUOUS`.
|
|
70
|
+
|
|
71
|
+
When the client does not supply roots, the server uses its own working directory:
|
|
72
|
+
|
|
73
|
+
- It selects the one mod workspace that contains the working directory.
|
|
74
|
+
- If the working directory is in no mod and exactly one mod workspace exists, it selects that mod.
|
|
75
|
+
- If the working directory is in no mod and several mod workspaces exist, the call fails with `WORKSPACE_CONTEXT_REQUIRED`, and the error lists the available workspace IDs.
|
|
76
|
+
- With no mod workspace at all, the call fails with `WORKSPACE_CONTEXT_NOT_FOUND`.
|
|
77
|
+
|
|
78
|
+
When a tool reads a named source file with `"current"` and the selected mod lacks that file, the server uses the one other mod that has it, and fails with `WORKSPACE_SOURCE_AMBIGUOUS` if several do.
|
|
79
|
+
|
|
80
|
+
To choose a mod explicitly, pass its workspace ID.
|
|
81
|
+
A configured workspace keeps the `id` you gave it.
|
|
82
|
+
A mod found under `modRoots` gets an ID of the form `mod_<folder name>_<12 hex characters>`.
|
|
83
|
+
For a mod workspace, the server also accepts a short form: the mod's folder name or workspace name in lower case with other characters replaced by `_`, optionally prefixed with `auto_` or `mod_`.
|
|
84
|
+
A `mod_<folder name>_<hash>` ID whose hash no longer matches resolves by its folder name in the same way.
|
|
85
|
+
A short form that matches several mods fails with `WORKSPACE_CONTEXT_AMBIGUOUS`, and an unknown ID fails with `WORKSPACE_NOT_REGISTERED`.
|
|
86
|
+
Over HTTP, a principal sees only the workspaces granted to it, and any other ID fails with `WORKSPACE_INACCESSIBLE`.
|
|
87
|
+
|
|
88
|
+
## Paths
|
|
89
|
+
|
|
90
|
+
| Key | Type | Range | Default | Purpose |
|
|
91
|
+
| ---------------------- | ---------------- | ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `version` | number | `1` | none | Configuration format version; required. |
|
|
93
|
+
| `serverStateRoot` | absolute path | | unset | Private folder for indexes, jobs and server records. Required whenever `modRoots` or a `mod` workspace is configured. |
|
|
94
|
+
| `modRoots` | array of paths | up to 16 | `[]` | Parent folders whose direct subfolders each become a writable mod workspace. Hidden and linked subfolders are skipped, and a subfolder already registered under `workspaces` keeps that registration. |
|
|
95
|
+
| `gameRoot` | path | | unset | The installed game, used by every `mod` workspace that sets no `gameRoot` of its own. |
|
|
96
|
+
| `gameUserRoot` | path | | unset | The game's user-data folder holding `logs/error.log`, read by `hoi4.error_log`. When unset, the server tries the folder containing a mod's parent `mod/` folder, then `Documents/Paradox Interactive/Hearts of Iron IV` (also under `%OneDrive%` on Windows), or `~/.local/share/Paradox Interactive/Hearts of Iron IV` on Linux. |
|
|
97
|
+
| `workspaceStorageRoot` | path | | unset | Base folder for generated files: each workspace stores `artifacts/` and `cache/` in `<workspaceStorageRoot>/<workspace ID>/`. When unset, mods use `.hoi4-agent/` inside the mod. |
|
|
98
|
+
| `storageRoots` | array of paths | up to 16 | `[]` | Additional folders in which an explicit `artifactRoot` or `cacheRoot` may be placed. |
|
|
99
|
+
| `workspaces` | array of objects | up to 1,000 | `[]` | Explicit workspace registrations, described in the next section. |
|
|
100
|
+
|
|
101
|
+
## Workspaces
|
|
102
|
+
|
|
103
|
+
Each entry in `workspaces` registers one source folder.
|
|
104
|
+
|
|
105
|
+
| Key | Type | Range | Default | Purpose |
|
|
106
|
+
| ----------------- | --------------------------- | ---------------------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `id` | string | lower-case letter, then up to 63 of `a-z0-9_-` | none | Workspace ID used in tool calls and grants; required. `current` is reserved. |
|
|
108
|
+
| `name` | string | 1–200 characters | none | Display name; required. |
|
|
109
|
+
| `root` | path | | none | The workspace folder; required and must exist. |
|
|
110
|
+
| `kind` | `mod`, `game`, `dependency` | | `mod` | `mod` is writable source. `game` registers an installed game as read-only source with its DLC layered after it. `dependency` is read-only source. |
|
|
111
|
+
| `gameRoot` | path | | top-level `gameRoot` for `mod` | The installed game loaded beneath this workspace. |
|
|
112
|
+
| `includeGameDlc` | boolean | | `true` | Layer the game's `dlc/` and `integrated_dlc/` folders between the game and the mod. Set `false` to analyze the base game alone. |
|
|
113
|
+
| `dependencyRoots` | array of paths | up to 16 | `[]` | Read-only mods loaded after the game and DLC and before this workspace, in list order. |
|
|
114
|
+
| `dependencies` | array of objects | up to 16 | `[]` | The same as `dependencyRoots`, with a `replacePaths` list per dependency: `{ "root": "...", "replacePaths": ["..."] }`. Use either this or `dependencyRoots`, not both. |
|
|
115
|
+
| `replacePaths` | array of relative paths | up to 1,000 | `[]` | Folders whose earlier-loaded files this workspace unloads, as `replace_path` does. Entries from the folder's `descriptor.mod` are added automatically. Ignored for `kind: "game"`. |
|
|
116
|
+
| `wikiRoot` | absolute path | | `paradox_wiki/` in the mod | Folder of the offline wiki snapshot used by the reference tools. |
|
|
117
|
+
| `scriptDocsRoot` | absolute path | | `script_docs/` in the mod | Folder of a generated script documentation dump used by the reference tools. |
|
|
118
|
+
| `roots` | object | | see below | Folders, relative to each source layer, that the domain tools scan. |
|
|
119
|
+
| `artifactRoot` | path | | see below | Where rendered images and other generated results are stored. |
|
|
120
|
+
| `cacheRoot` | path | | see below | Where rewrite transactions and locks are stored. |
|
|
121
|
+
| `fixtureRoot` | path | | unset | A read-only folder of extra source files scanned with the workspace at the base game's load position and treated as workspace-owned source. |
|
|
122
|
+
|
|
123
|
+
`artifactRoot` and `cacheRoot` default to `<workspaceStorageRoot>/<id>/artifacts` and `<workspaceStorageRoot>/<id>/cache`, or, without `workspaceStorageRoot`, to `.hoi4-agent/artifacts` and `.hoi4-agent/cache` inside a `mod` workspace.
|
|
124
|
+
A `game` or `dependency` workspace needs both, either explicitly or through `workspaceStorageRoot`.
|
|
125
|
+
|
|
126
|
+
Relative paths in `replacePaths` and `roots` must be plain relative folders of up to 1,024 characters: no leading `/`, drive letter, `:`, `..` segment or wildcard character (`* ? [ ] { } !`).
|
|
127
|
+
|
|
128
|
+
| `roots` key | Default | Scanned for |
|
|
129
|
+
| -------------- | ----------------------------------------- | ----------------------------- |
|
|
130
|
+
| `localisation` | `["localisation", "localisation_synced"]` | Localisation files |
|
|
131
|
+
| `interface` | `["interface"]` | `.gui` and `.gfx` files |
|
|
132
|
+
| `gfx` | `["gfx"]` | Sprite definitions and images |
|
|
133
|
+
| `map` | `["map"]` | Map data |
|
|
134
|
+
| `focus` | `["common/national_focus"]` | National focus trees |
|
|
135
|
+
| `scriptedGui` | `["common/scripted_guis"]` | Scripted GUI definitions |
|
|
136
|
+
| `states` | `["history/states"]` | State history |
|
|
137
|
+
|
|
138
|
+
Each `roots` key takes up to 16 folders, and setting `roots` replaces only the keys you list.
|
|
139
|
+
|
|
140
|
+
## Scan and storage limits
|
|
141
|
+
|
|
142
|
+
| Key | Type | Range | Default | Purpose |
|
|
143
|
+
| ------------------------ | ------- | -------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
144
|
+
| `scanMaxFiles` | integer | 1–1,000,000 | 100,000 | Most files one source scan may enumerate across all layers; more fails with `SCAN_FILE_LIMIT`. |
|
|
145
|
+
| `scanMaxBytes` | integer | 1 MiB or more | 268,435,456 (256 MiB) | Most bytes one source scan may read; more fails with `SCAN_BYTE_LIMIT`. |
|
|
146
|
+
| `scanMaxFileBytes` | integer | 64 KiB or more | 67,108,864 (64 MiB) | Largest single file a scan may read. |
|
|
147
|
+
| `artifactMaxBytes` | integer | 1 MiB or more | 536,870,912 (512 MiB) | Total size of one workspace's artifact store. When a new artifact would exceed it, older unpinned artifacts are removed down to three quarters of the limit. |
|
|
148
|
+
| `artifactMaxEntries` | integer | 1–100,000 | 5,000 | Number of artifacts one workspace keeps, pruned the same way. |
|
|
149
|
+
| `artifactMaxSingleBytes` | integer | 1 MiB or more | 134,217,728 (128 MiB) | Largest single artifact. |
|
|
150
|
+
|
|
151
|
+
## Tools, jobs and lifetime
|
|
152
|
+
|
|
153
|
+
| Key | Type | Range | Default | Purpose |
|
|
154
|
+
| ----------------------- | ------- | ---------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
155
|
+
| `maxConcurrentTools` | integer | 1–32 | 2 | Expensive tool calls one server process runs at once. |
|
|
156
|
+
| `maxSharedTools` | integer | 1–128 | 4 | Expensive calls and job workers that run at once across every server process sharing the same `serverStateRoot`. |
|
|
157
|
+
| `jobDeadlineSeconds` | integer | 1–86,400 | 1,800 | A read-only job still running this long after dispatch is stopped and fails with `JOB_DEADLINE_EXCEEDED`. Rewrite jobs are never stopped this way. |
|
|
158
|
+
| `jobCancelGraceSeconds` | integer | 1–600 | 10 | A read-only job still running this long after cancellation is stopped and settles as cancelled. |
|
|
159
|
+
| `jobWorkerMaxHeapMiB` | integer | 256–65,536 | unset | Memory ceiling for each job worker's JavaScript heap, in MiB. Unset keeps Node's default. |
|
|
160
|
+
| `jobWorkerIdleSeconds` | integer | 0–3,600 | 180 | How long a job worker stays warm after a job so the next job reuses its loaded indexes. `0` starts a fresh worker for every job. |
|
|
161
|
+
| `stdioIdleExitMinutes` | integer | 0–10,080 | 0 | A stdio server with no client traffic and no open request for this many minutes exits. `0` never exits. `HOI4_AGENT_STDIO_IDLE_EXIT_MINUTES` overrides it per client. |
|
|
162
|
+
|
|
163
|
+
See [Persistent jobs and MCP tasks](jobs.md) for how jobs, cancellation and recovery behave.
|
|
164
|
+
|
|
165
|
+
## HTTP
|
|
166
|
+
|
|
167
|
+
These keys sit inside the `http` object and are used only by `hoi4-agent-tools-http`.
|
|
168
|
+
|
|
169
|
+
| Key | Type | Range | Default | Purpose |
|
|
170
|
+
| ----------------------------- | --------------------- | ------------------ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
171
|
+
| `host` | string | | `127.0.0.1` | Listening address, also accepted in the `Host` header. Anything other than `127.0.0.1`, `localhost` or `::1` counts as public. |
|
|
172
|
+
| `port` | integer | 0–65535 | 3210 | Listening port; `0` lets the system choose one. |
|
|
173
|
+
| `publicUrl` | URL | | unset | The address clients use: an HTTP(S) origin or its exact `/mcp` endpoint, without credentials, query or fragment. Its host name is also accepted in the `Host` header. Required with `oauth`. |
|
|
174
|
+
| `allowedOrigins` | array of URLs | | `[]` | Exact browser origins allowed to call the server. A request carrying any other `Origin` header is refused. |
|
|
175
|
+
| `trustedProxyAddresses` | array of IP addresses | | `[]` | Reverse proxies whose `X-Forwarded-For` header identifies the real client for rate limiting. |
|
|
176
|
+
| `tokens` | array of objects | up to 1,000 | `[]` | Static bearer tokens for loopback use; see below. |
|
|
177
|
+
| `principals` | array of objects | up to 1,000 | `[]` | OAuth subjects allowed to connect; see below. |
|
|
178
|
+
| `oauth` | object | | unset | OAuth or OpenID Connect token verification; see below. |
|
|
179
|
+
| `maxBodyBytes` | integer | 1,024–67,108,864 | 67,108,864 (64 MiB) | Largest request body. |
|
|
180
|
+
| `headersTimeoutMs` | integer | 1,000–120,000 | 10,000 | Time allowed to receive request headers. |
|
|
181
|
+
| `requestTimeoutMs` | integer | 1,000–300,000 | 30,000 | Time allowed to receive a complete request. |
|
|
182
|
+
| `keepAliveTimeoutMs` | integer | 1,000–120,000 | 5,000 | How long an idle connection stays open. |
|
|
183
|
+
| `maxConnections` | integer | 1–100,000 | 512 | Open connections. |
|
|
184
|
+
| `maxRequestsPerSocket` | integer | 1–100,000 | 100 | Requests served on one connection before it closes. |
|
|
185
|
+
| `maxConcurrentRequests` | integer | 1–1,024 | 128 | Requests handled at once. Up to 16 more small requests, such as cancellations, are still admitted when it is full. |
|
|
186
|
+
| `maxSessions` | integer | 1–10,000 | 128 | Open MCP sessions. |
|
|
187
|
+
| `maxSessionsPerPrincipal` | integer | 1–10,000 | 32 | Open MCP sessions per principal. |
|
|
188
|
+
| `maxEventStreams` | integer | 1–10,000 | 128 | Open server-to-client event streams. |
|
|
189
|
+
| `maxEventStreamsPerPrincipal` | integer | 1–10,000 | 32 | Open event streams per principal. |
|
|
190
|
+
| `maxSessionEventBytes` | integer | 65,536–67,108,864 | 2,097,152 (2 MiB) | Resumable event history kept for one session. |
|
|
191
|
+
| `maxEventStoreBytes` | integer | 65,536–268,435,456 | 16,777,216 (16 MiB) | Resumable event history kept for all sessions together. |
|
|
192
|
+
| `requestsPerMinute` | integer | 1–100,000 | 6,000 | Requests per minute from one client address before authentication, and again per principal after it. |
|
|
193
|
+
| `sessionTtlSeconds` | integer | 60–86,400 | 3,600 | An idle session with no active request expires after this many seconds. A session never outlives its credential. |
|
|
194
|
+
|
|
195
|
+
Each `tokens` entry:
|
|
196
|
+
|
|
197
|
+
| Key | Type | Range | Default | Purpose |
|
|
198
|
+
| --------------------- | ---------------- | -------------------------------------------- | ------- | --------------------------------------------------------------------------------------------- |
|
|
199
|
+
| `principal` | string | 1–128 of `A-Z a-z 0-9 . _ @ -` | none | Name of the client; required. |
|
|
200
|
+
| `tokenEnv` | string | upper-case letter, then 1–127 of `A-Z 0-9 _` | none | Environment variable holding the secret; required. The secret must be at least 32 characters. |
|
|
201
|
+
| `workspaceIds` | array of strings | up to 1,000 | `[]` | IDs of configured `workspaces` this client may use. |
|
|
202
|
+
| `allowDiscoveredMods` | boolean | | `false` | Also grant every mod found under `modRoots`. |
|
|
203
|
+
|
|
204
|
+
A static token always carries both the `hoi4:read` and `hoi4:write` scopes, so it can also run rewrite tools.
|
|
205
|
+
|
|
206
|
+
Each `principals` entry:
|
|
207
|
+
|
|
208
|
+
| Key | Type | Range | Default | Purpose |
|
|
209
|
+
| --------------------- | ---------------- | -------------------------------- | ------- | ---------------------------------------------------- |
|
|
210
|
+
| `principal` | string | 1–256 of `A-Z a-z 0-9 . _ @ : -` | none | The token subject (`sub` claim) to allow; required. |
|
|
211
|
+
| `workspaceIds` | array of strings | up to 1,000 | `[]` | IDs of configured `workspaces` this subject may use. |
|
|
212
|
+
| `allowDiscoveredMods` | boolean | | `false` | Also grant every mod found under `modRoots`. |
|
|
213
|
+
|
|
214
|
+
The `oauth` object:
|
|
215
|
+
|
|
216
|
+
| Key | Type | Range | Default | Purpose |
|
|
217
|
+
| ---------------------- | ---------------- | ------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------------ |
|
|
218
|
+
| `issuer` | URL | | none | Expected `iss` claim; required. |
|
|
219
|
+
| `jwksUri` | URL | | none | Where the signing keys are fetched; required. |
|
|
220
|
+
| `audience` | string | 1 character or more | none | Expected `aud` claim; required. |
|
|
221
|
+
| `authorizationServers` | array of URLs | 1 or more | none | Authorization servers advertised in the protected resource metadata; required. |
|
|
222
|
+
| `requiredScopes` | array of strings | | `["hoi4:read"]` | Scopes every request needs. Rewrite tools also need `hoi4:write`. |
|
|
223
|
+
| `algorithms` | array | `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`, `EdDSA` | `["RS256"]` | Accepted token signature algorithms. |
|
|
224
|
+
|
|
225
|
+
An OAuth principal receives exactly the scopes in its token, and a token must carry an expiry.
|
|
226
|
+
With `oauth` and `publicUrl` set, the server publishes protected resource metadata at `/.well-known/oauth-protected-resource/mcp`.
|
|
227
|
+
|
|
228
|
+
## Rules checked at startup
|
|
229
|
+
|
|
230
|
+
The server refuses to start when the file breaks any of these rules.
|
|
231
|
+
|
|
232
|
+
File contents:
|
|
233
|
+
|
|
234
|
+
- `version` is `1`, and no level contains an unknown key.
|
|
235
|
+
- `serverStateRoot` is an absolute path, and it is set whenever `modRoots` is non-empty or any workspace has `kind: "mod"`.
|
|
236
|
+
- `scanMaxFileBytes` is not larger than `scanMaxBytes`, and `artifactMaxSingleBytes` is not larger than `artifactMaxBytes`.
|
|
237
|
+
- `http.maxSessionEventBytes` is not larger than `http.maxEventStoreBytes`.
|
|
238
|
+
- `http.headersTimeoutMs` is not longer than `http.requestTimeoutMs`, and `http.keepAliveTimeoutMs` is shorter than `http.headersTimeoutMs`.
|
|
239
|
+
- A workspace uses `dependencyRoots` or `dependencies`, not both.
|
|
240
|
+
- Workspace IDs are unique, and no workspace uses the ID `current`.
|
|
241
|
+
- `http.oauth` and `http.tokens` are not both set.
|
|
242
|
+
- Token principals are unique, token environment variable names are unique, OAuth principals are unique, and no name appears both as a token principal and as an OAuth principal.
|
|
243
|
+
- Every `workspaceIds` entry in `tokens` and `principals` names a workspace listed under `workspaces`.
|
|
244
|
+
|
|
245
|
+
Folders:
|
|
246
|
+
|
|
247
|
+
- Every `modRoots` entry is an existing real folder, not a link, and no two of them overlap.
|
|
248
|
+
- `workspaceStorageRoot` does not overlap any `modRoots` entry.
|
|
249
|
+
- `serverStateRoot` does not overlap any mod, game, dependency, fixture, wiki, documentation, storage, artifact or cache folder.
|
|
250
|
+
- Each workspace's `root`, `gameRoot`, dependency folders and `fixtureRoot` exist, and its game, dependency and fixture folders neither overlap each other nor the workspace folder.
|
|
251
|
+
- `artifactRoot` and `cacheRoot` lie inside the mod's own `.hoi4-agent/artifacts` and `.hoi4-agent/cache` folders, `workspaceStorageRoot` or a `storageRoots` entry; they do not overlap each other or any source folder.
|
|
252
|
+
- A workspace's own folders do not overlap another workspace's folders, and its read-only folders do not overlap another workspace's mod, artifact or cache folder.
|
|
253
|
+
- At most 1,000 workspaces exist in total, counting mods found under `modRoots`, and a game folder lists at most 256 DLC folders.
|
|
254
|
+
|
|
255
|
+
HTTP server only:
|
|
256
|
+
|
|
257
|
+
- `http.tokens` or `http.oauth` is configured.
|
|
258
|
+
- With `oauth`, `publicUrl` is set and `principals` is not empty.
|
|
259
|
+
- Every static token's environment variable holds at least 32 characters, and no two tokens share a secret.
|
|
260
|
+
- When the server is public, because `host` or the `publicUrl` host is not `127.0.0.1`, `localhost` or `::1`, it must use `oauth`, `publicUrl` must use HTTPS, the issuer, signing-key and authorization server URLs must use HTTPS, and `allowedOrigins` must not be empty.
|
|
261
|
+
|
|
262
|
+
## Environment variables
|
|
263
|
+
|
|
264
|
+
| Variable | Read by | Purpose |
|
|
265
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
266
|
+
| `HOI4_AGENT_CONFIG` | `hoi4-agent-tools`, `hoi4-agent-tools-http`, setup `--diagnose` and `--print-client-config` | Configuration file path when `--config` is not given. `--init` writes to `--config` or the default path and ignores this variable. |
|
|
267
|
+
| `HOI4_GAME_ROOT` | automatic configuration, setup `--init` and `--discover` | First game folder to try, before the standard Steam locations. |
|
|
268
|
+
| `HOI4_MOD_ROOTS` | setup `--init` and `--discover` | Extra mod parent folders to discover, separated by `;` on Windows and `:` elsewhere. `--init` uses discovered folders only when no `--mod-root` is given. |
|
|
269
|
+
| `HOI4_AGENT_STDIO_IDLE_EXIT_MINUTES` | `hoi4-agent-tools` | Overrides `stdioIdleExitMinutes` for one client registration. It must be a whole number from 0 to 10,080; an empty value keeps the configured setting, and any other value stops startup. |
|
|
270
|
+
| `HOI4_AGENT_TOOLS_CHAOSX` | `hoi4-agent-tools` | `1` exposes the optional Chaos Redux workflow tools listed in [Tools](tools.md). The HTTP server never exposes them. |
|
|
271
|
+
| the variable named by `tokenEnv` | `hoi4-agent-tools-http` | Secret for a static bearer token, at least 32 characters. |
|
|
272
|
+
| `APPDATA`, `XDG_CONFIG_HOME` | every command | Location of the default configuration file. |
|
|
273
|
+
| `XDG_STATE_HOME`, `XDG_DATA_HOME`, `LOCALAPPDATA` | automatic configuration, setup `--init` | Default `serverStateRoot` and `workspaceStorageRoot`. |
|
|
274
|
+
| `ProgramFiles(x86)` | automatic configuration, setup `--init` and `--discover` | Base of the standard Windows Steam location. |
|
|
275
|
+
| `OneDrive`, `OneDriveConsumer`, `OneDriveCommercial` | setup `--init` and `--discover`; `OneDrive` also `hoi4.error_log` | Additional `Documents` folders searched for mods and, for `OneDrive`, for the game's user-data folder. |
|
|
276
|
+
|
|
277
|
+
## Related
|
|
278
|
+
|
|
279
|
+
- [Setup](setup.md) for installation, client registration and `hoi4-agent-tools-setup`.
|
|
280
|
+
- [HTTP](http.md) for loopback and remote deployments.
|
|
281
|
+
- [Persistent jobs and MCP tasks](jobs.md) for background work, cancellation and retention.
|