hoi4-agent-tools 3.0.9 → 3.2.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 +32 -1
- package/README.md +10 -2
- package/dist/bin/stdio.js +23 -12
- package/dist/hoi4_agent_tools/core/analysis-checkpoints.js +82 -0
- package/dist/hoi4_agent_tools/core/analysis-evidence.js +9 -0
- package/dist/hoi4_agent_tools/core/analysis-job-operations.js +38 -0
- package/dist/hoi4_agent_tools/core/artifact-resource.js +173 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +84 -2
- package/dist/hoi4_agent_tools/core/condition-evaluator.js +16 -0
- package/dist/hoi4_agent_tools/core/dependency-pages.js +216 -0
- package/dist/hoi4_agent_tools/core/dependency-walk.js +266 -0
- package/dist/hoi4_agent_tools/core/domain-validation.js +188 -0
- package/dist/hoi4_agent_tools/core/engine.js +48 -5
- package/dist/hoi4_agent_tools/core/helper-expansion.js +186 -0
- package/dist/hoi4_agent_tools/core/impact-comparison.js +49 -0
- package/dist/hoi4_agent_tools/core/impact-graph.js +301 -0
- package/dist/hoi4_agent_tools/core/impact-semantic.js +256 -0
- package/dist/hoi4_agent_tools/core/index-segments.js +137 -0
- package/dist/hoi4_agent_tools/core/index.js +47 -4
- package/dist/hoi4_agent_tools/core/job-controls.js +109 -0
- package/dist/hoi4_agent_tools/core/job-executor.js +190 -0
- package/dist/hoi4_agent_tools/core/job-service.js +112 -0
- package/dist/hoi4_agent_tools/core/job-store.js +641 -0
- package/dist/hoi4_agent_tools/core/job-transactions.js +126 -0
- package/dist/hoi4_agent_tools/core/job-worker-host.js +202 -0
- package/dist/hoi4_agent_tools/core/job-worker.js +64 -0
- package/dist/hoi4_agent_tools/core/operation-context.js +31 -0
- package/dist/hoi4_agent_tools/core/operation-result.js +412 -0
- package/dist/hoi4_agent_tools/core/operation-tasks.js +387 -0
- package/dist/hoi4_agent_tools/core/persistent-analysis-cache.js +383 -0
- package/dist/hoi4_agent_tools/core/result.js +1 -0
- package/dist/hoi4_agent_tools/core/scanner.js +15 -75
- package/dist/hoi4_agent_tools/core/semantic-dependencies.js +233 -0
- package/dist/hoi4_agent_tools/core/shared-request-capacity.js +43 -11
- package/dist/hoi4_agent_tools/core/source/cache.js +107 -0
- package/dist/hoi4_agent_tools/core/source/parser.js +11 -1
- package/dist/hoi4_agent_tools/core/source-overlay.js +125 -0
- package/dist/hoi4_agent_tools/core/transaction-execution.js +134 -0
- package/dist/hoi4_agent_tools/core/transactions.js +80 -27
- package/dist/hoi4_agent_tools/decision/ai.js +104 -0
- package/dist/hoi4_agent_tools/decision/comparison.js +106 -0
- package/dist/hoi4_agent_tools/decision/cost.js +177 -0
- package/dist/hoi4_agent_tools/decision/evaluation.js +256 -0
- package/dist/hoi4_agent_tools/decision/lifecycle.js +114 -0
- package/dist/hoi4_agent_tools/decision/service.js +172 -0
- package/dist/hoi4_agent_tools/decision/source-inventory.js +182 -0
- package/dist/hoi4_agent_tools/event/graph.js +214 -290
- package/dist/hoi4_agent_tools/event/helper-expansion.js +33 -0
- package/dist/hoi4_agent_tools/event/job-operations.js +51 -0
- package/dist/hoi4_agent_tools/event/operations.js +179 -0
- package/dist/hoi4_agent_tools/event/service.js +67 -15
- package/dist/hoi4_agent_tools/event/source-analysis.js +5 -4
- package/dist/hoi4_agent_tools/focus/job-operations.js +77 -0
- package/dist/hoi4_agent_tools/focus/tool-operations.js +669 -0
- package/dist/hoi4_agent_tools/focus/tool-support.js +123 -0
- package/dist/hoi4_agent_tools/gui/job-operations.js +81 -0
- package/dist/hoi4_agent_tools/gui/tool-operations.js +270 -0
- package/dist/hoi4_agent_tools/impact/scenario-suites.js +74 -0
- package/dist/hoi4_agent_tools/impact/service.js +127 -0
- package/dist/hoi4_agent_tools/map/job-operations.js +76 -0
- package/dist/hoi4_agent_tools/map/tool-operations.js +493 -0
- package/dist/hoi4_agent_tools/mcp/prompts/probability.js +25 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register-modern.js +29 -0
- package/dist/hoi4_agent_tools/mcp/resources/register-modern.js +34 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js +4 -150
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +3 -219
- package/dist/hoi4_agent_tools/mcp/server/create-modern-operations.js +188 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +18 -6
- package/dist/hoi4_agent_tools/mcp/server/instructions.js +2 -0
- package/dist/hoi4_agent_tools/mcp/server/job-task-store.js +67 -0
- package/dist/hoi4_agent_tools/mcp/server/legacy-task-tools.js +20 -0
- package/dist/hoi4_agent_tools/mcp/server/modern-tasks.js +168 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js +17 -15
- package/dist/hoi4_agent_tools/mcp/server/request-lifecycle.js +45 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js +2 -412
- package/dist/hoi4_agent_tools/mcp/server/task-request-context.js +9 -0
- package/dist/hoi4_agent_tools/mcp/server/task-tool-catalog.js +16 -0
- package/dist/hoi4_agent_tools/mcp/server/task-tool-definition.js +1 -0
- package/dist/hoi4_agent_tools/mcp/server/transaction-execution.js +1 -123
- package/dist/hoi4_agent_tools/mcp/tools/analysis.js +65 -0
- package/dist/hoi4_agent_tools/mcp/tools/chaosx.js +38 -21
- package/dist/hoi4_agent_tools/mcp/tools/event.js +24 -315
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +34 -1039
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +19 -504
- package/dist/hoi4_agent_tools/mcp/tools/job.js +46 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +21 -600
- package/dist/hoi4_agent_tools/mcp/tools/probability.js +39 -463
- package/dist/hoi4_agent_tools/mcp/tools/technology.js +40 -304
- package/dist/hoi4_agent_tools/mcp/transports/http.js +58 -1
- package/dist/hoi4_agent_tools/mcp/transports/modern-task-routing.js +109 -0
- package/dist/hoi4_agent_tools/mcp/transports/negotiated-stdio.js +121 -0
- package/dist/hoi4_agent_tools/probability/job-operations.js +58 -0
- package/dist/hoi4_agent_tools/probability/operations.js +183 -0
- package/dist/hoi4_agent_tools/probability/service.js +24 -3
- package/dist/hoi4_agent_tools/schemas/analysis.js +228 -0
- package/dist/hoi4_agent_tools/schemas/common.js +2 -0
- package/dist/hoi4_agent_tools/schemas/event.js +72 -1
- package/dist/hoi4_agent_tools/schemas/focus-requests.js +189 -0
- package/dist/hoi4_agent_tools/schemas/gui-requests.js +193 -0
- package/dist/hoi4_agent_tools/schemas/helper-expansion.js +58 -0
- package/dist/hoi4_agent_tools/schemas/map-requests.js +85 -0
- package/dist/hoi4_agent_tools/schemas/probability-requests.js +168 -0
- package/dist/hoi4_agent_tools/schemas/technology.js +90 -1
- package/dist/hoi4_agent_tools/schemas/transaction.js +1 -0
- package/dist/hoi4_agent_tools/technology/graph.js +83 -78
- package/dist/hoi4_agent_tools/technology/helper-expansion.js +33 -0
- package/dist/hoi4_agent_tools/technology/job-operations.js +60 -0
- package/dist/hoi4_agent_tools/technology/operations.js +125 -0
- package/dist/hoi4_agent_tools/technology/service.js +89 -15
- package/dist/hoi4_agent_tools/technology/source-analysis.js +2 -1
- package/dist/hoi4_agent_tools/version.js +1 -1
- package/docs/README.md +3 -0
- package/docs/analysis.md +100 -0
- package/docs/development.md +2 -0
- package/docs/events.md +7 -2
- package/docs/helper-expansion.md +78 -0
- package/docs/http.md +2 -0
- package/docs/jobs.md +86 -0
- package/docs/technology.md +6 -2
- package/package.json +15 -6
- package/schemas/configuration.schema.json +1 -1
- 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 +290 -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 +44 -0
- package/schemas/helper-expansion-summary.schema.json +70 -0
- package/schemas/impact-inspect-input.schema.json +203 -0
- package/schemas/map-operation.schema.json +1 -1
- package/schemas/operation-result.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/server.json +3 -3
|
@@ -1,153 +1,7 @@
|
|
|
1
1
|
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
const artifactResourceLimit = 1_048_576;
|
|
5
|
-
const artifactChunkMetadataKey = 'io.github.klimpaskov/hoi4-agent-tools.artifact-byte-range';
|
|
6
|
-
const fatalUtf8Decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
|
|
7
|
-
const artifactResourceMetadata = {
|
|
8
|
-
title: 'HOI4 review artifact',
|
|
9
|
-
description: 'Complete plans, diagnostics, layouts, previews, event graphs, routes, state and scope reports, comparisons, and diffs. Reads accept zero-based byte offset and positive byte length query selectors; each response reports its exact byte range and continuation URI in _meta.',
|
|
10
|
-
_meta: {
|
|
11
|
-
[artifactChunkMetadataKey]: {
|
|
12
|
-
version: 1,
|
|
13
|
-
unit: 'byte',
|
|
14
|
-
maxChunkSize: artifactResourceLimit,
|
|
15
|
-
selectors: {
|
|
16
|
-
offset: { type: 'integer', minimum: 0, default: 0 },
|
|
17
|
-
length: {
|
|
18
|
-
type: 'integer',
|
|
19
|
-
minimum: 1,
|
|
20
|
-
maximum: artifactResourceLimit,
|
|
21
|
-
default: artifactResourceLimit,
|
|
22
|
-
},
|
|
23
|
-
metadata: { type: 'string', enum: ['manifest'], optional: true },
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
function assertResourceSelectors(uri, allowed) {
|
|
29
|
-
if (uri.hash !== '' || uri.username !== '' || uri.password !== '' || uri.port !== '') {
|
|
30
|
-
throw new ServiceError('RESOURCE_URI_INVALID', 'Resource URI is not canonical');
|
|
31
|
-
}
|
|
32
|
-
const allowedNames = new Set(allowed);
|
|
33
|
-
const seen = new Set();
|
|
34
|
-
for (const name of uri.searchParams.keys()) {
|
|
35
|
-
if (!allowedNames.has(name)) {
|
|
36
|
-
throw new ServiceError('RESOURCE_SELECTOR_INVALID', 'Resource selector is not supported');
|
|
37
|
-
}
|
|
38
|
-
if (seen.has(name)) {
|
|
39
|
-
throw new ServiceError('RESOURCE_SELECTOR_INVALID', 'Resource selectors may not be repeated');
|
|
40
|
-
}
|
|
41
|
-
seen.add(name);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function contentArtifactUri(uri) {
|
|
45
|
-
const contentUri = new URL(uri);
|
|
46
|
-
contentUri.search = '';
|
|
47
|
-
contentUri.hash = '';
|
|
48
|
-
return contentUri.href;
|
|
49
|
-
}
|
|
50
|
-
function exactUtf8Text(bytes) {
|
|
51
|
-
try {
|
|
52
|
-
const text = fatalUtf8Decoder.decode(bytes);
|
|
53
|
-
return Buffer.from(text, 'utf8').equals(bytes) ? text : undefined;
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function artifactRangeParameter(uri, name, fallback, minimum, maximum = Number.MAX_SAFE_INTEGER) {
|
|
60
|
-
const raw = uri.searchParams.get(name);
|
|
61
|
-
if (raw === null)
|
|
62
|
-
return fallback;
|
|
63
|
-
if (!/^\d+$/u.test(raw)) {
|
|
64
|
-
throw new ServiceError('ARTIFACT_RANGE_INVALID', 'Artifact byte range is invalid');
|
|
65
|
-
}
|
|
66
|
-
const value = Number(raw);
|
|
67
|
-
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
68
|
-
throw new ServiceError('ARTIFACT_RANGE_INVALID', 'Artifact byte range is invalid');
|
|
69
|
-
}
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
function artifactChunkMetadata(uri, metadata, totalSize, offset, returnedLength, requestedLength) {
|
|
73
|
-
const endExclusive = offset + returnedLength;
|
|
74
|
-
let continuationUri = null;
|
|
75
|
-
if (endExclusive < totalSize) {
|
|
76
|
-
const continuation = new URL(contentArtifactUri(uri));
|
|
77
|
-
continuation.searchParams.set('offset', String(endExclusive));
|
|
78
|
-
continuation.searchParams.set('length', String(requestedLength));
|
|
79
|
-
if (metadata !== null)
|
|
80
|
-
continuation.searchParams.set('metadata', metadata);
|
|
81
|
-
continuationUri = continuation.href;
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
[artifactChunkMetadataKey]: {
|
|
85
|
-
version: 1,
|
|
86
|
-
unit: 'byte',
|
|
87
|
-
totalSize,
|
|
88
|
-
returnedRange: { offset, length: returnedLength, endExclusive },
|
|
89
|
-
complete: offset === 0 && returnedLength === totalSize,
|
|
90
|
-
continuationUri,
|
|
91
|
-
},
|
|
92
|
-
};
|
|
93
|
-
}
|
|
2
|
+
import { artifactResourceDefinition, readArtifactResource } from '../../core/artifact-resource.js';
|
|
3
|
+
/** SDK-v1 registration only; ownership and byte-range behavior live in the shared core. */
|
|
94
4
|
export function registerMcpResources(server, engine, context) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
assertResourceSelectors(uri, ['offset', 'length', 'metadata']);
|
|
98
|
-
const workspace = engine.resolver.get(String(workspaceId), context.principal);
|
|
99
|
-
const offset = artifactRangeParameter(uri, 'offset', 0, 0);
|
|
100
|
-
const length = artifactRangeParameter(uri, 'length', artifactResourceLimit, 1, artifactResourceLimit);
|
|
101
|
-
const metadata = uri.searchParams.get('metadata');
|
|
102
|
-
if (metadata !== null && metadata !== 'manifest') {
|
|
103
|
-
throw new ServiceError('ARTIFACT_METADATA_INVALID', 'Artifact metadata selector is invalid');
|
|
104
|
-
}
|
|
105
|
-
if (metadata === 'manifest') {
|
|
106
|
-
const described = await engine.artifacts.describe(workspace, contentArtifactUri(uri), extra.signal);
|
|
107
|
-
const { path: artifactPath, ...publicManifest } = described;
|
|
108
|
-
void artifactPath;
|
|
109
|
-
const manifestBytes = Buffer.from(`${canonicalJson(publicManifest)}\n`, 'utf8');
|
|
110
|
-
const start = Math.min(offset, manifestBytes.length);
|
|
111
|
-
const selected = manifestBytes.subarray(start, Math.min(manifestBytes.length, start + length));
|
|
112
|
-
const complete = start === 0 && selected.length === manifestBytes.length;
|
|
113
|
-
const _meta = artifactChunkMetadata(uri, metadata, manifestBytes.length, start, selected.length, length);
|
|
114
|
-
return {
|
|
115
|
-
contents: [
|
|
116
|
-
complete
|
|
117
|
-
? {
|
|
118
|
-
uri: uri.href,
|
|
119
|
-
mimeType: 'application/json',
|
|
120
|
-
text: selected.toString('utf8'),
|
|
121
|
-
_meta,
|
|
122
|
-
}
|
|
123
|
-
: {
|
|
124
|
-
uri: uri.href,
|
|
125
|
-
mimeType: 'application/json',
|
|
126
|
-
blob: selected.toString('base64'),
|
|
127
|
-
_meta,
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
const artifact = await engine.artifacts.read(workspace, contentArtifactUri(uri), { offset, length }, extra.signal);
|
|
133
|
-
const textual = artifact.mimeType.startsWith('text/') ||
|
|
134
|
-
artifact.mimeType === 'application/json' ||
|
|
135
|
-
artifact.mimeType === 'image/svg+xml';
|
|
136
|
-
const start = Math.min(offset, artifact.totalSize);
|
|
137
|
-
const complete = start === 0 && artifact.bytes.length === artifact.totalSize;
|
|
138
|
-
const text = textual && complete ? exactUtf8Text(artifact.bytes) : undefined;
|
|
139
|
-
const _meta = artifactChunkMetadata(uri, metadata, artifact.totalSize, start, artifact.bytes.length, length);
|
|
140
|
-
return {
|
|
141
|
-
contents: [
|
|
142
|
-
text !== undefined
|
|
143
|
-
? { uri: uri.href, mimeType: artifact.mimeType, text, _meta }
|
|
144
|
-
: {
|
|
145
|
-
uri: uri.href,
|
|
146
|
-
mimeType: artifact.mimeType,
|
|
147
|
-
blob: artifact.bytes.toString('base64'),
|
|
148
|
-
_meta,
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
};
|
|
152
|
-
});
|
|
5
|
+
const { name, uriTemplate, ...metadata } = artifactResourceDefinition;
|
|
6
|
+
server.registerResource(name, new ResourceTemplate(uriTemplate, { list: undefined }), metadata, (uri, _variables, extra) => readArtifactResource(engine, uri.href, context, extra.signal));
|
|
153
7
|
}
|
|
@@ -1,219 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { exactChangedFilePatterns, focusDomainScanPatterns, } from '../../core/domain-scan-patterns.js';
|
|
5
|
-
import { ServiceError } from '../../core/result.js';
|
|
6
|
-
import { parseClausewitz } from '../../core/source/index.js';
|
|
7
|
-
import { TRANSACTION_MAX_DIAGNOSTICS } from '../../core/transaction-limits.js';
|
|
8
|
-
import { importContinuousFocusPalettes, importFocusTrees, layoutFocusTreeAsync, lintContinuousFocusPalette, lintFocusTree, } from '../../focus/index.js';
|
|
9
|
-
import { ScriptedGuiStudio } from '../../gui/index.js';
|
|
10
|
-
import { AgentNudger, attributeMapValidationDiagnostics } from '../../map/index.js';
|
|
11
|
-
import { PACKAGE_VERSION } from '../../version.js';
|
|
12
|
-
export async function resolveServerWorkspaceId(engine, context, workspaceId, signal) {
|
|
13
|
-
if (workspaceId === 'current' && context.resolveCurrentWorkspaceId !== undefined) {
|
|
14
|
-
return context.resolveCurrentWorkspaceId(signal);
|
|
15
|
-
}
|
|
16
|
-
return engine.resolver.resolveWorkspaceId(workspaceId, context.principal);
|
|
17
|
-
}
|
|
18
|
-
export async function resolveServerWorkspaceForSource(engine, context, workspaceId, relativePath, signal) {
|
|
19
|
-
const resolvedWorkspaceId = await resolveServerWorkspaceId(engine, context, workspaceId, signal);
|
|
20
|
-
if (workspaceId !== 'current' || relativePath === undefined)
|
|
21
|
-
return resolvedWorkspaceId;
|
|
22
|
-
try {
|
|
23
|
-
const resolved = await engine.resolver.resolvePath(resolvedWorkspaceId, relativePath, 'read', ['mod'], context.principal);
|
|
24
|
-
signal?.throwIfAborted();
|
|
25
|
-
if ((await lstat(resolved.path)).isFile())
|
|
26
|
-
return resolvedWorkspaceId;
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
if (error.name === 'AbortError')
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
return ((await engine.resolver.resolveUniqueModSourceWorkspaceId(relativePath, context.principal, signal)) ?? resolvedWorkspaceId);
|
|
33
|
-
}
|
|
34
|
-
export function requireServerScope(context, scope) {
|
|
35
|
-
if (context.scopes !== undefined && !context.scopes.includes(scope)) {
|
|
36
|
-
throw new ServiceError('AUTH_SCOPE_REQUIRED', `This operation requires the ${scope} scope`, {
|
|
37
|
-
requiredScope: scope,
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function sourcePathComparisonKey(value) {
|
|
42
|
-
const normalized = value.replaceAll('\\', '/');
|
|
43
|
-
return process.platform === 'win32' ? normalized.toLocaleLowerCase('en-US') : normalized;
|
|
44
|
-
}
|
|
45
|
-
function focusReferences(engineSnapshot) {
|
|
46
|
-
const active = (kind) => engineSnapshot.index
|
|
47
|
-
.findAll(kind)
|
|
48
|
-
.filter(({ overridden }) => !overridden)
|
|
49
|
-
.map(({ id }) => id);
|
|
50
|
-
return {
|
|
51
|
-
decision: active('decision'),
|
|
52
|
-
decision_category: active('decision_category'),
|
|
53
|
-
event: active('event'),
|
|
54
|
-
idea: active('idea'),
|
|
55
|
-
leader: active('leader'),
|
|
56
|
-
formable: active('formable'),
|
|
57
|
-
helper: active('scripted_effect'),
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
export async function postValidateTransaction(engine, transaction, principal, signal) {
|
|
61
|
-
engine.invalidate(transaction.workspaceId);
|
|
62
|
-
const workspace = engine.resolver.get(transaction.workspaceId, principal);
|
|
63
|
-
const focusTransaction = transaction.operationKind === 'focus-plan-changes' ||
|
|
64
|
-
transaction.operationKind === 'continuous-focus-plan-changes';
|
|
65
|
-
const snapshot = await engine.scan(transaction.workspaceId, {
|
|
66
|
-
patterns: focusTransaction
|
|
67
|
-
? focusDomainScanPatterns(workspace)
|
|
68
|
-
: exactChangedFilePatterns(transaction.files.map(({ relativePath }) => relativePath)),
|
|
69
|
-
}, principal, signal);
|
|
70
|
-
const changed = new Set(transaction.files.map(({ relativePath }) => sourcePathComparisonKey(relativePath)));
|
|
71
|
-
const diagnostics = snapshot.diagnostics.filter(({ location }) => {
|
|
72
|
-
if (location === undefined)
|
|
73
|
-
return false;
|
|
74
|
-
const sourcePath = sourcePathComparisonKey(location.path);
|
|
75
|
-
return [...changed].some((relativePath) => sourcePath.endsWith(`:${relativePath}`));
|
|
76
|
-
});
|
|
77
|
-
const checks = [];
|
|
78
|
-
if (transaction.operationKind === 'focus-plan-changes') {
|
|
79
|
-
const catalog = focusReferences(snapshot);
|
|
80
|
-
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined &&
|
|
81
|
-
relativePath.toLowerCase().endsWith('.txt') &&
|
|
82
|
-
changed.has(sourcePathComparisonKey(relativePath)))) {
|
|
83
|
-
const document = parseClausewitz(file.bytes, file.displayPath);
|
|
84
|
-
const imported = importFocusTrees(document, { references: catalog });
|
|
85
|
-
diagnostics.push(...imported.diagnostics);
|
|
86
|
-
for (const plan of imported.plans) {
|
|
87
|
-
const layout = await layoutFocusTreeAsync(plan, signal === undefined ? {} : { signal });
|
|
88
|
-
diagnostics.push(...lintFocusTree(plan, {
|
|
89
|
-
index: snapshot.index,
|
|
90
|
-
references: catalog,
|
|
91
|
-
layout,
|
|
92
|
-
}));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
const passed = !diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
96
|
-
checks.push({
|
|
97
|
-
id: 'post-write-focus',
|
|
98
|
-
passed,
|
|
99
|
-
message: passed
|
|
100
|
-
? 'The focus result re-imported, laid out, and linted successfully'
|
|
101
|
-
: 'The focus result failed post-write import or lint',
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
else if (transaction.operationKind === 'continuous-focus-plan-changes') {
|
|
105
|
-
for (const file of snapshot.files.filter(({ shadowedBy, relativePath }) => shadowedBy === undefined &&
|
|
106
|
-
relativePath.toLowerCase().endsWith('.txt') &&
|
|
107
|
-
changed.has(sourcePathComparisonKey(relativePath)))) {
|
|
108
|
-
const imported = importContinuousFocusPalettes(parseClausewitz(file.bytes, file.displayPath));
|
|
109
|
-
diagnostics.push(...imported.diagnostics);
|
|
110
|
-
for (const plan of imported.continuousFocusPalettes) {
|
|
111
|
-
diagnostics.push(...lintContinuousFocusPalette(plan));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
const passed = !diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
115
|
-
checks.push({
|
|
116
|
-
id: 'post-write-continuous-focus',
|
|
117
|
-
passed,
|
|
118
|
-
message: passed
|
|
119
|
-
? 'The continuous focus result re-imported and linted successfully'
|
|
120
|
-
: 'The continuous focus result failed post-write import or lint',
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
else if (transaction.operationKind === 'gui-source-change' ||
|
|
124
|
-
transaction.operationKind === 'gui-helper-compilation') {
|
|
125
|
-
const studio = new ScriptedGuiStudio(engine);
|
|
126
|
-
const scanned = await studio.scan(transaction.workspaceId, principal, signal);
|
|
127
|
-
diagnostics.push(...scanned.graph.diagnostics);
|
|
128
|
-
const passed = !scanned.graph.diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
129
|
-
checks.push({
|
|
130
|
-
id: 'post-write-gui-graph',
|
|
131
|
-
passed,
|
|
132
|
-
message: passed
|
|
133
|
-
? 'The GUI graph rebuilt successfully'
|
|
134
|
-
: 'The GUI graph has blocking diagnostics',
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
else if (transaction.operationKind === 'agent-nudger-map-changes') {
|
|
138
|
-
const nudger = new AgentNudger(engine);
|
|
139
|
-
const validated = await nudger.validate(transaction.workspaceId, principal, signal);
|
|
140
|
-
const mapDiagnostics = attributeMapValidationDiagnostics({
|
|
141
|
-
diagnostics: [...diagnostics, ...validated.validation.diagnostics],
|
|
142
|
-
operations: transaction.operations,
|
|
143
|
-
changes: transaction.files,
|
|
144
|
-
});
|
|
145
|
-
diagnostics.length = 0;
|
|
146
|
-
diagnostics.push(...mapDiagnostics);
|
|
147
|
-
checks.push(...validated.validation.checks);
|
|
148
|
-
checks.push({
|
|
149
|
-
id: 'post-write-map',
|
|
150
|
-
passed: validated.validation.passed,
|
|
151
|
-
message: validated.validation.passed
|
|
152
|
-
? 'The map result rescanned and validated successfully'
|
|
153
|
-
: 'The map result failed post-write validation',
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
const passed = !diagnostics.some(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
157
|
-
checks.unshift({
|
|
158
|
-
id: 'post-write-shared-index',
|
|
159
|
-
passed,
|
|
160
|
-
message: passed
|
|
161
|
-
? 'Changed files reparsed and reindexed successfully'
|
|
162
|
-
: 'Changed files have blocking post-write diagnostics',
|
|
163
|
-
});
|
|
164
|
-
const availableDiagnostics = Math.max(0, TRANSACTION_MAX_DIAGNOSTICS - transaction.diagnostics.length);
|
|
165
|
-
if (diagnostics.length > availableDiagnostics) {
|
|
166
|
-
const workspace = engine.resolver.get(transaction.workspaceId, principal);
|
|
167
|
-
const sourceEvidence = boundedSourceHashEvidence(Object.fromEntries(transaction.files.flatMap(({ relativePath, afterSha256 }) => afterSha256 === null ? [] : [[relativePath, afterSha256]])));
|
|
168
|
-
const artifact = await engine.artifacts.putChunked(workspace, `${transaction.operationKind}.post-validation.json`, 'application/json', `${canonicalJson({
|
|
169
|
-
schemaVersion: 1,
|
|
170
|
-
operationKind: transaction.operationKind,
|
|
171
|
-
diagnosticCount: diagnostics.length,
|
|
172
|
-
checks,
|
|
173
|
-
diagnostics,
|
|
174
|
-
})}\n`, {
|
|
175
|
-
kind: 'rewrite-post-validation',
|
|
176
|
-
toolVersion: PACKAGE_VERSION,
|
|
177
|
-
schemaVersion: 'rewrite-post-validation.v1',
|
|
178
|
-
sourceHashes: sourceEvidence.sourceHashes,
|
|
179
|
-
metadata: { sourceHashInventory: sourceEvidence.inventory },
|
|
180
|
-
}, 'Complete diagnostics from post-write validation', signal);
|
|
181
|
-
const retainedLimit = Math.max(0, availableDiagnostics - 1);
|
|
182
|
-
const hard = diagnostics.filter(({ severity }) => severity === 'error' || severity === 'blocker');
|
|
183
|
-
const retainedHard = hard.slice(0, retainedLimit);
|
|
184
|
-
const retainedSoft = diagnostics
|
|
185
|
-
.filter(({ severity }) => severity !== 'error' && severity !== 'blocker')
|
|
186
|
-
.slice(0, retainedLimit - retainedHard.length);
|
|
187
|
-
const omittedHard = hard.length - retainedHard.length;
|
|
188
|
-
const artifactLink = publicArtifactLink(artifact);
|
|
189
|
-
checks.push({
|
|
190
|
-
id: 'post-write-diagnostics-artifact',
|
|
191
|
-
passed: omittedHard === 0,
|
|
192
|
-
message: `Complete post-write diagnostics are stored at ${artifactLink.uri}`,
|
|
193
|
-
});
|
|
194
|
-
return {
|
|
195
|
-
diagnostics: availableDiagnostics === 0
|
|
196
|
-
? []
|
|
197
|
-
: [
|
|
198
|
-
...retainedHard,
|
|
199
|
-
...retainedSoft,
|
|
200
|
-
{
|
|
201
|
-
code: 'POST_VALIDATION_DIAGNOSTICS_IN_ARTIFACT',
|
|
202
|
-
severity: omittedHard > 0 ? 'blocker' : 'warning',
|
|
203
|
-
category: 'validation',
|
|
204
|
-
message: `Complete post-write validation contains ${diagnostics.length} diagnostics and is stored as an artifact`,
|
|
205
|
-
details: {
|
|
206
|
-
total: diagnostics.length,
|
|
207
|
-
returned: availableDiagnostics,
|
|
208
|
-
omitted: diagnostics.length - retainedLimit,
|
|
209
|
-
omittedHard,
|
|
210
|
-
artifact: artifactLink,
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
],
|
|
214
|
-
checks,
|
|
215
|
-
artifacts: [artifactLink],
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
return { diagnostics, checks, artifacts: [] };
|
|
219
|
-
}
|
|
1
|
+
/** Compatibility names for the protocol-independent execution authorization boundary. */
|
|
2
|
+
export { requireOperationScope as requireServerScope, resolveOperationWorkspaceForSource as resolveServerWorkspaceForSource, resolveOperationWorkspaceId as resolveServerWorkspaceId, } from '../../core/operation-context.js';
|
|
3
|
+
export { postValidateTransaction } from '../../core/domain-validation.js';
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
import { CallToolResultSchema, ClientCapabilitiesSchema, ListToolsResultSchema, } from '@modelcontextprotocol/core';
|
|
3
|
+
import { CLIENT_CAPABILITIES_META_KEY, inputRequired, ProtocolError, ProtocolErrorCode, } from '@modelcontextprotocol/server';
|
|
4
|
+
import { z } from 'zod/v4';
|
|
5
|
+
import { isJobControlTool, JobControlService } from '../../core/job-controls.js';
|
|
6
|
+
import { OperationTaskService } from '../../core/operation-tasks.js';
|
|
7
|
+
import { errorResult } from '../../core/operation-result.js';
|
|
8
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from '../../version.js';
|
|
9
|
+
import { MODERN_TASK_ROUTES, ModernTaskRoutingServer } from '../transports/modern-task-routing.js';
|
|
10
|
+
import { registerModernResources } from '../resources/register-modern.js';
|
|
11
|
+
import { registerModernPrompts } from '../prompts/register-modern.js';
|
|
12
|
+
import { jobControlTools } from '../tools/job.js';
|
|
13
|
+
import { ModernTaskAdapter, TASKS_EXTENSION } from './modern-tasks.js';
|
|
14
|
+
import { SERVER_INSTRUCTIONS } from './instructions.js';
|
|
15
|
+
import { modernProgressReporter, withProgressHeartbeat } from './progress.js';
|
|
16
|
+
import { taskToolCatalog } from './task-tool-catalog.js';
|
|
17
|
+
const rootsResponseSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
roots: z
|
|
20
|
+
.array(z.object({ uri: z.string().max(8192), name: z.string().optional() }).loose())
|
|
21
|
+
.max(256),
|
|
22
|
+
})
|
|
23
|
+
.loose();
|
|
24
|
+
const metadataParamsSchema = z.record(z.string(), z.unknown());
|
|
25
|
+
const workspaceRootsKey = 'hoi4-workspace-roots';
|
|
26
|
+
function clientCapabilities(envelope) {
|
|
27
|
+
// SDK 2.0.0 publishes RequestMetaEnvelope as {}, although it retains these keys at runtime.
|
|
28
|
+
const parsed = z
|
|
29
|
+
.object({ [CLIENT_CAPABILITIES_META_KEY]: ClientCapabilitiesSchema })
|
|
30
|
+
.loose()
|
|
31
|
+
.safeParse(envelope);
|
|
32
|
+
return parsed.success ? parsed.data[CLIENT_CAPABILITIES_META_KEY] : undefined;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Modern operation/task factory, shared by the connection and HTTP serving entries.
|
|
36
|
+
* The stdio entry explicitly opts into the two private ChaosX tools when configured.
|
|
37
|
+
* HTTP remains mod-independent and never enables that private extension.
|
|
38
|
+
*/
|
|
39
|
+
export function createModernOperationServer(engine, context = {}, tasks = new OperationTaskService(engine), options = {}) {
|
|
40
|
+
const adapter = new ModernTaskAdapter(tasks);
|
|
41
|
+
const controls = new JobControlService(engine);
|
|
42
|
+
const privateOperations = options.createPrivateTools?.(engine, context);
|
|
43
|
+
const definitions = [
|
|
44
|
+
...taskToolCatalog,
|
|
45
|
+
...jobControlTools,
|
|
46
|
+
...(privateOperations === undefined
|
|
47
|
+
? []
|
|
48
|
+
: [privateOperations.visual.definition, privateOperations.country.definition]),
|
|
49
|
+
];
|
|
50
|
+
const server = new ModernTaskRoutingServer({ name: PACKAGE_NAME, version: PACKAGE_VERSION }, {
|
|
51
|
+
capabilities: {
|
|
52
|
+
tools: {},
|
|
53
|
+
resources: {},
|
|
54
|
+
prompts: {},
|
|
55
|
+
extensions: { [TASKS_EXTENSION]: {} },
|
|
56
|
+
},
|
|
57
|
+
instructions: SERVER_INSTRUCTIONS,
|
|
58
|
+
});
|
|
59
|
+
registerModernResources(server, engine, context);
|
|
60
|
+
registerModernPrompts(server);
|
|
61
|
+
server.setRequestHandler('tools/list', () => ListToolsResultSchema.parse({
|
|
62
|
+
tools: definitions.map(({ name, inputSchema, outputSchema, ...metadata }) => ({
|
|
63
|
+
...metadata,
|
|
64
|
+
name,
|
|
65
|
+
inputSchema: {
|
|
66
|
+
...z.toJSONSchema(inputSchema, { target: 'draft-7', io: 'input' }),
|
|
67
|
+
type: 'object',
|
|
68
|
+
},
|
|
69
|
+
...(outputSchema === undefined
|
|
70
|
+
? {}
|
|
71
|
+
: {
|
|
72
|
+
outputSchema: {
|
|
73
|
+
...z.toJSONSchema(outputSchema, { target: 'draft-7', io: 'input' }),
|
|
74
|
+
type: 'object',
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
})),
|
|
78
|
+
}));
|
|
79
|
+
server.setRequestHandler('tools/call', async (request, extra) => {
|
|
80
|
+
const definition = definitions.find(({ name }) => name === request.params.name);
|
|
81
|
+
if (definition === undefined)
|
|
82
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Unknown tool');
|
|
83
|
+
const input = definition.inputSchema.safeParse(request.params.arguments ?? {});
|
|
84
|
+
if (!input.success)
|
|
85
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Invalid tool arguments');
|
|
86
|
+
const capabilities = clientCapabilities(extra.mcpReq.envelope);
|
|
87
|
+
const arguments_ = z.record(z.string(), z.json()).parse(input.data);
|
|
88
|
+
let operationContext = context;
|
|
89
|
+
if ((arguments_.workspaceId === undefined || arguments_.workspaceId === 'current') &&
|
|
90
|
+
capabilities?.roots !== undefined &&
|
|
91
|
+
context.resolveCurrentWorkspaceId === undefined) {
|
|
92
|
+
const response = extra.mcpReq.inputResponses?.[workspaceRootsKey];
|
|
93
|
+
if (response === undefined)
|
|
94
|
+
return inputRequired({ inputRequests: { [workspaceRootsKey]: inputRequired.listRoots() } });
|
|
95
|
+
const roots = rootsResponseSchema.safeParse(response);
|
|
96
|
+
if (!roots.success)
|
|
97
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Invalid workspace roots response');
|
|
98
|
+
const paths = roots.data.roots.flatMap(({ uri }) => {
|
|
99
|
+
try {
|
|
100
|
+
return new URL(uri).protocol === 'file:' ? [fileURLToPath(uri)] : [];
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
operationContext = {
|
|
107
|
+
...context,
|
|
108
|
+
resolveCurrentWorkspaceId: async () => engine.resolver.resolveClientWorkspaceId(paths, context.principal),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
if (privateOperations !== undefined) {
|
|
113
|
+
const operations = operationContext === context || options.createPrivateTools === undefined
|
|
114
|
+
? privateOperations
|
|
115
|
+
: options.createPrivateTools(engine, operationContext);
|
|
116
|
+
const progress = modernProgressReporter(extra);
|
|
117
|
+
if (definition.name === operations.visual.definition.name) {
|
|
118
|
+
const result = await operations.visual.call(operations.visual.definition.inputSchema.parse(arguments_), progress);
|
|
119
|
+
if (result.isError !== true)
|
|
120
|
+
operations.visual.definition.outputSchema.parse(result.structuredContent);
|
|
121
|
+
return CallToolResultSchema.parse({
|
|
122
|
+
...result,
|
|
123
|
+
resultType: 'complete',
|
|
124
|
+
ttlMs: 0,
|
|
125
|
+
cacheScope: 'private',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
if (definition.name === operations.country.definition.name) {
|
|
129
|
+
const result = await operations.country.call(operations.country.definition.inputSchema.parse(arguments_), progress);
|
|
130
|
+
if (result.isError !== true)
|
|
131
|
+
operations.country.definition.outputSchema.parse(result.structuredContent);
|
|
132
|
+
return CallToolResultSchema.parse({
|
|
133
|
+
...result,
|
|
134
|
+
resultType: 'complete',
|
|
135
|
+
ttlMs: 0,
|
|
136
|
+
cacheScope: 'private',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (isJobControlTool(definition.name)) {
|
|
141
|
+
const result = await controls.call(definition.name, arguments_, operationContext, extra.mcpReq.signal);
|
|
142
|
+
const completed = CallToolResultSchema.parse({
|
|
143
|
+
...result,
|
|
144
|
+
resultType: 'complete',
|
|
145
|
+
ttlMs: 0,
|
|
146
|
+
cacheScope: 'private',
|
|
147
|
+
});
|
|
148
|
+
if (definition.outputSchema !== undefined)
|
|
149
|
+
definition.outputSchema.parse(completed.structuredContent);
|
|
150
|
+
return completed;
|
|
151
|
+
}
|
|
152
|
+
const progress = modernProgressReporter(extra);
|
|
153
|
+
const result = await withProgressHeartbeat(async () => {
|
|
154
|
+
const completed = await adapter.call({ name: definition.name, arguments: arguments_ }, capabilities, operationContext, {
|
|
155
|
+
requestId: extra.mcpReq.id,
|
|
156
|
+
signal: extra.mcpReq.signal,
|
|
157
|
+
...(extra.sessionId === undefined ? {} : { sessionId: extra.sessionId }),
|
|
158
|
+
});
|
|
159
|
+
// A native task response ends this request, not the durable operation.
|
|
160
|
+
// Subsequent state and progress come from authenticated tasks/get polling.
|
|
161
|
+
if (completed.resultType !== 'task') {
|
|
162
|
+
await progress.report(2, 3, 'Retrieving persistent operation result');
|
|
163
|
+
await progress.report(3, 3, 'Persistent operation complete');
|
|
164
|
+
}
|
|
165
|
+
return completed;
|
|
166
|
+
}, progress);
|
|
167
|
+
return CallToolResultSchema.parse({ content: [], ...result });
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
if (error instanceof ProtocolError || extra.mcpReq.signal.aborted)
|
|
171
|
+
throw error;
|
|
172
|
+
return errorResult(error, typeof arguments_.workspaceId === 'string' ? arguments_.workspaceId : 'current');
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
for (const [method, alias] of MODERN_TASK_ROUTES) {
|
|
176
|
+
server.setRequestHandler(alias, { params: metadataParamsSchema }, async (params, extra) => {
|
|
177
|
+
if (extra.http?.req !== undefined &&
|
|
178
|
+
(extra.http.req.headers.get('mcp-method') !== method ||
|
|
179
|
+
extra.http.req.headers.get('mcp-name') !== params.taskId))
|
|
180
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidParams, 'Task routing headers do not match the request');
|
|
181
|
+
const { _meta: _metadata, ...payload } = params;
|
|
182
|
+
if (method === 'tasks/update')
|
|
183
|
+
payload.inputResponses = extra.mcpReq.inputResponses ?? payload.inputResponses;
|
|
184
|
+
return adapter.handle(method, payload, clientCapabilities(extra.mcpReq.envelope), context, extra.mcpReq.signal);
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return server;
|
|
188
|
+
}
|
|
@@ -5,22 +5,32 @@ import { registerFocusTools } from '../tools/focus.js';
|
|
|
5
5
|
import { registerGuiTools } from '../tools/gui.js';
|
|
6
6
|
import { registerMapTools } from '../tools/map.js';
|
|
7
7
|
import { registerEventTools } from '../tools/event.js';
|
|
8
|
+
import { registerAnalysisTools } from '../tools/analysis.js';
|
|
8
9
|
import { registerTechnologyTools } from '../tools/technology.js';
|
|
9
10
|
import { registerProbabilityTools } from '../tools/probability.js';
|
|
11
|
+
import { registerJobTools } from '../tools/job.js';
|
|
10
12
|
import { registerMcpResources } from '../resources/register.js';
|
|
11
13
|
import { installRequestLifecycle } from './request-lifecycle.js';
|
|
12
|
-
|
|
14
|
+
import { PersistentJobTaskStore } from './job-task-store.js';
|
|
15
|
+
import { SERVER_INSTRUCTIONS } from './instructions.js';
|
|
16
|
+
export { SERVER_INSTRUCTIONS } from './instructions.js';
|
|
13
17
|
export function createMcpServer(engine, context = {}) {
|
|
18
|
+
let serverContext = context;
|
|
19
|
+
const taskStore = new PersistentJobTaskStore(engine, () => serverContext);
|
|
14
20
|
const server = new McpServer({
|
|
15
21
|
name: PACKAGE_NAME,
|
|
16
22
|
version: PACKAGE_VERSION,
|
|
17
23
|
websiteUrl: 'https://github.com/klimPaskov/hoi4-agent-tools',
|
|
18
24
|
}, {
|
|
19
|
-
capabilities: {
|
|
25
|
+
capabilities: {
|
|
26
|
+
logging: {},
|
|
27
|
+
tasks: { list: {}, cancel: {}, requests: { tools: { call: {} } } },
|
|
28
|
+
},
|
|
20
29
|
instructions: SERVER_INSTRUCTIONS,
|
|
30
|
+
taskStore,
|
|
21
31
|
});
|
|
22
32
|
installRequestLifecycle(server, engine);
|
|
23
|
-
|
|
33
|
+
serverContext = {
|
|
24
34
|
...context,
|
|
25
35
|
resolveCurrentWorkspaceId: async (signal) => {
|
|
26
36
|
const capabilities = server.server.getClientCapabilities();
|
|
@@ -43,9 +53,11 @@ export function createMcpServer(engine, context = {}) {
|
|
|
43
53
|
registerFocusTools(server, engine, serverContext);
|
|
44
54
|
registerGuiTools(server, engine, serverContext);
|
|
45
55
|
registerMapTools(server, engine, serverContext);
|
|
46
|
-
registerEventTools(server
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
registerEventTools(server);
|
|
57
|
+
registerAnalysisTools(server);
|
|
58
|
+
registerTechnologyTools(server);
|
|
59
|
+
registerProbabilityTools(server);
|
|
60
|
+
registerJobTools(server, engine, serverContext);
|
|
49
61
|
registerMcpResources(server, engine, serverContext);
|
|
50
62
|
return server;
|
|
51
63
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/** Shared agent-facing guidance for both MCP protocol generations. */
|
|
2
|
+
export const SERVER_INSTRUCTIONS = 'Use focus tools for focus trees, GUI tools for interfaces, and map tools for map data. Start unfamiliar event chains with hoi4.event_inspect. Event tools are read-only. Start cross-system source review with hoi4.impact_inspect and decision or mission scenarios with hoi4.decision_inspect. Impact and decision tools are read-only. Start technology and doctrine work with hoi4.tech_inspect. Technology tools are read-only. Start weighted AI, MTTH, random, and declared-pool analysis with hoi4.probability_inspect. Probability tools are read-only. Concurrent calls queue automatically and emit periodic progress when the client requests it. Long operations support negotiated MCP tasks; native background rewrites require a stable requestKey. Large evidence is linked as resources.';
|