hoi4-agent-tools 0.0.0-bootstrap.1 → 0.1.3
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 +50 -0
- package/LICENSE +201 -0
- package/README.md +140 -2
- package/SECURITY.md +17 -0
- package/dist/bin/http.d.ts +3 -0
- package/dist/bin/http.d.ts.map +1 -0
- package/dist/bin/http.js +27 -0
- package/dist/bin/http.js.map +1 -0
- package/dist/bin/setup.d.ts +3 -0
- package/dist/bin/setup.d.ts.map +1 -0
- package/dist/bin/setup.js +188 -0
- package/dist/bin/setup.js.map +1 -0
- package/dist/bin/stdio.d.ts +3 -0
- package/dist/bin/stdio.d.ts.map +1 -0
- package/dist/bin/stdio.js +36 -0
- package/dist/bin/stdio.js.map +1 -0
- package/dist/hoi4_agent_tools/core/artifacts.d.ts +117 -0
- package/dist/hoi4_agent_tools/core/artifacts.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/artifacts.js +1047 -0
- package/dist/hoi4_agent_tools/core/artifacts.js.map +1 -0
- package/dist/hoi4_agent_tools/core/canonical.d.ts +13 -0
- package/dist/hoi4_agent_tools/core/canonical.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/canonical.js +41 -0
- package/dist/hoi4_agent_tools/core/canonical.js.map +1 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts +137 -0
- package/dist/hoi4_agent_tools/core/configuration.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/configuration.js +383 -0
- package/dist/hoi4_agent_tools/core/configuration.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts +18 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js +45 -0
- package/dist/hoi4_agent_tools/core/diagnostic-collector.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts +26 -0
- package/dist/hoi4_agent_tools/core/diagnostics.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diagnostics.js +25 -0
- package/dist/hoi4_agent_tools/core/diagnostics.js.map +1 -0
- package/dist/hoi4_agent_tools/core/diff.d.ts +22 -0
- package/dist/hoi4_agent_tools/core/diff.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/diff.js +93 -0
- package/dist/hoi4_agent_tools/core/diff.js.map +1 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts +56 -0
- package/dist/hoi4_agent_tools/core/engine.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/engine.js +319 -0
- package/dist/hoi4_agent_tools/core/engine.js.map +1 -0
- package/dist/hoi4_agent_tools/core/image-diff.d.ts +14 -0
- package/dist/hoi4_agent_tools/core/image-diff.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/image-diff.js +121 -0
- package/dist/hoi4_agent_tools/core/image-diff.js.map +1 -0
- package/dist/hoi4_agent_tools/core/index.d.ts +64 -0
- package/dist/hoi4_agent_tools/core/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/index.js +963 -0
- package/dist/hoi4_agent_tools/core/index.js.map +1 -0
- package/dist/hoi4_agent_tools/core/index.public.d.ts +16 -0
- package/dist/hoi4_agent_tools/core/index.public.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/index.public.js +16 -0
- package/dist/hoi4_agent_tools/core/index.public.js.map +1 -0
- package/dist/hoi4_agent_tools/core/render-budget.d.ts +51 -0
- package/dist/hoi4_agent_tools/core/render-budget.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/render-budget.js +109 -0
- package/dist/hoi4_agent_tools/core/render-budget.js.map +1 -0
- package/dist/hoi4_agent_tools/core/result.d.ts +45 -0
- package/dist/hoi4_agent_tools/core/result.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/result.js +26 -0
- package/dist/hoi4_agent_tools/core/result.js.map +1 -0
- package/dist/hoi4_agent_tools/core/scanner.d.ts +28 -0
- package/dist/hoi4_agent_tools/core/scanner.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/scanner.js +136 -0
- package/dist/hoi4_agent_tools/core/scanner.js.map +1 -0
- package/dist/hoi4_agent_tools/core/server-state.d.ts +31 -0
- package/dist/hoi4_agent_tools/core/server-state.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/server-state.js +413 -0
- package/dist/hoi4_agent_tools/core/server-state.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts +10 -0
- package/dist/hoi4_agent_tools/core/source/encoding.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/encoding.js +54 -0
- package/dist/hoi4_agent_tools/core/source/encoding.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/index.d.ts +7 -0
- package/dist/hoi4_agent_tools/core/source/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/index.js +7 -0
- package/dist/hoi4_agent_tools/core/source/index.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts +30 -0
- package/dist/hoi4_agent_tools/core/source/lexer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/lexer.js +266 -0
- package/dist/hoi4_agent_tools/core/source/lexer.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/limits.d.ts +21 -0
- package/dist/hoi4_agent_tools/core/source/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/limits.js +49 -0
- package/dist/hoi4_agent_tools/core/source/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts +20 -0
- package/dist/hoi4_agent_tools/core/source/localisation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/localisation.js +163 -0
- package/dist/hoi4_agent_tools/core/source/localisation.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/parser.d.ts +44 -0
- package/dist/hoi4_agent_tools/core/source/parser.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/parser.js +334 -0
- package/dist/hoi4_agent_tools/core/source/parser.js.map +1 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts +10 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.js +28 -0
- package/dist/hoi4_agent_tools/core/source/rewrite.js.map +1 -0
- package/dist/hoi4_agent_tools/core/svg-text.d.ts +34 -0
- package/dist/hoi4_agent_tools/core/svg-text.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/svg-text.js +127 -0
- package/dist/hoi4_agent_tools/core/svg-text.js.map +1 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts +9 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js +9 -0
- package/dist/hoi4_agent_tools/core/transaction-limits.js.map +1 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts +162 -0
- package/dist/hoi4_agent_tools/core/transactions.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/transactions.js +1181 -0
- package/dist/hoi4_agent_tools/core/transactions.js.map +1 -0
- package/dist/hoi4_agent_tools/core/workspace.d.ts +68 -0
- package/dist/hoi4_agent_tools/core/workspace.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/core/workspace.js +794 -0
- package/dist/hoi4_agent_tools/core/workspace.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/compiler.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/compiler.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/compiler.js +273 -0
- package/dist/hoi4_agent_tools/focus/compiler.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts +32 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.js +179 -0
- package/dist/hoi4_agent_tools/focus/continuous-render.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/drift.d.ts +22 -0
- package/dist/hoi4_agent_tools/focus/drift.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/drift.js +124 -0
- package/dist/hoi4_agent_tools/focus/drift.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/geometry.d.ts +17 -0
- package/dist/hoi4_agent_tools/focus/geometry.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/geometry.js +35 -0
- package/dist/hoi4_agent_tools/focus/geometry.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/importer.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/importer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/importer.js +732 -0
- package/dist/hoi4_agent_tools/focus/importer.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/index.d.ts +14 -0
- package/dist/hoi4_agent_tools/focus/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/index.js +14 -0
- package/dist/hoi4_agent_tools/focus/index.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/layout.d.ts +5 -0
- package/dist/hoi4_agent_tools/focus/layout.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/layout.js +653 -0
- package/dist/hoi4_agent_tools/focus/layout.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/limits.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/limits.js +9 -0
- package/dist/hoi4_agent_tools/focus/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/lint.d.ts +14 -0
- package/dist/hoi4_agent_tools/focus/lint.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/lint.js +839 -0
- package/dist/hoi4_agent_tools/focus/lint.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/model.d.ts +303 -0
- package/dist/hoi4_agent_tools/focus/model.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/model.js +20 -0
- package/dist/hoi4_agent_tools/focus/model.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts +9 -0
- package/dist/hoi4_agent_tools/focus/native-effects.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/native-effects.js +563 -0
- package/dist/hoi4_agent_tools/focus/native-effects.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/planning.d.ts +15 -0
- package/dist/hoi4_agent_tools/focus/planning.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/planning.js +241 -0
- package/dist/hoi4_agent_tools/focus/planning.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/presentation.d.ts +24 -0
- package/dist/hoi4_agent_tools/focus/presentation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/presentation.js +204 -0
- package/dist/hoi4_agent_tools/focus/presentation.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts +35 -0
- package/dist/hoi4_agent_tools/focus/render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/render.js +491 -0
- package/dist/hoi4_agent_tools/focus/render.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/service.d.ts +82 -0
- package/dist/hoi4_agent_tools/focus/service.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/service.js +492 -0
- package/dist/hoi4_agent_tools/focus/service.js.map +1 -0
- package/dist/hoi4_agent_tools/focus/source-update.d.ts +6 -0
- package/dist/hoi4_agent_tools/focus/source-update.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/focus/source-update.js +328 -0
- package/dist/hoi4_agent_tools/focus/source-update.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts +37 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.js +66 -0
- package/dist/hoi4_agent_tools/gui/animation-manifest.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/assets.d.ts +71 -0
- package/dist/hoi4_agent_tools/gui/assets.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/assets.js +629 -0
- package/dist/hoi4_agent_tools/gui/assets.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/dds.d.ts +17 -0
- package/dist/hoi4_agent_tools/gui/dds.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/dds.js +245 -0
- package/dist/hoi4_agent_tools/gui/dds.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/helpers.d.ts +101 -0
- package/dist/hoi4_agent_tools/gui/helpers.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/helpers.js +1072 -0
- package/dist/hoi4_agent_tools/gui/helpers.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/index.d.ts +15 -0
- package/dist/hoi4_agent_tools/gui/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/index.js +15 -0
- package/dist/hoi4_agent_tools/gui/index.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/layout.d.ts +6 -0
- package/dist/hoi4_agent_tools/gui/layout.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/layout.js +753 -0
- package/dist/hoi4_agent_tools/gui/layout.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/limits.d.ts +25 -0
- package/dist/hoi4_agent_tools/gui/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/limits.js +27 -0
- package/dist/hoi4_agent_tools/gui/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/renderer.d.ts +18 -0
- package/dist/hoi4_agent_tools/gui/renderer.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/renderer.js +388 -0
- package/dist/hoi4_agent_tools/gui/renderer.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/scenario.d.ts +66 -0
- package/dist/hoi4_agent_tools/gui/scenario.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/scenario.js +191 -0
- package/dist/hoi4_agent_tools/gui/scenario.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts +7 -0
- package/dist/hoi4_agent_tools/gui/source-graph.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js +1074 -0
- package/dist/hoi4_agent_tools/gui/source-graph.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts +13 -0
- package/dist/hoi4_agent_tools/gui/source-patch.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/source-patch.js +90 -0
- package/dist/hoi4_agent_tools/gui/source-patch.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/studio.d.ts +86 -0
- package/dist/hoi4_agent_tools/gui/studio.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/studio.js +830 -0
- package/dist/hoi4_agent_tools/gui/studio.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/tga.d.ts +16 -0
- package/dist/hoi4_agent_tools/gui/tga.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/tga.js +157 -0
- package/dist/hoi4_agent_tools/gui/tga.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/types.d.ts +322 -0
- package/dist/hoi4_agent_tools/gui/types.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/types.js +19 -0
- package/dist/hoi4_agent_tools/gui/types.js.map +1 -0
- package/dist/hoi4_agent_tools/gui/validators.d.ts +8 -0
- package/dist/hoi4_agent_tools/gui/validators.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/gui/validators.js +951 -0
- package/dist/hoi4_agent_tools/gui/validators.js.map +1 -0
- package/dist/hoi4_agent_tools/map/bmp.d.ts +62 -0
- package/dist/hoi4_agent_tools/map/bmp.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/bmp.js +363 -0
- package/dist/hoi4_agent_tools/map/bmp.js.map +1 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts +5 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js +21 -0
- package/dist/hoi4_agent_tools/map/diagnostic-limit.js.map +1 -0
- package/dist/hoi4_agent_tools/map/index.d.ts +8 -0
- package/dist/hoi4_agent_tools/map/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/index.js +8 -0
- package/dist/hoi4_agent_tools/map/index.js.map +1 -0
- package/dist/hoi4_agent_tools/map/limits.d.ts +13 -0
- package/dist/hoi4_agent_tools/map/limits.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/limits.js +13 -0
- package/dist/hoi4_agent_tools/map/limits.js.map +1 -0
- package/dist/hoi4_agent_tools/map/model.d.ts +269 -0
- package/dist/hoi4_agent_tools/map/model.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/model.js +1114 -0
- package/dist/hoi4_agent_tools/map/model.js.map +1 -0
- package/dist/hoi4_agent_tools/map/operations.d.ts +401 -0
- package/dist/hoi4_agent_tools/map/operations.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/operations.js +2717 -0
- package/dist/hoi4_agent_tools/map/operations.js.map +1 -0
- package/dist/hoi4_agent_tools/map/render.d.ts +90 -0
- package/dist/hoi4_agent_tools/map/render.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/render.js +759 -0
- package/dist/hoi4_agent_tools/map/render.js.map +1 -0
- package/dist/hoi4_agent_tools/map/service.d.ts +81 -0
- package/dist/hoi4_agent_tools/map/service.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/service.js +695 -0
- package/dist/hoi4_agent_tools/map/service.js.map +1 -0
- package/dist/hoi4_agent_tools/map/validation.d.ts +23 -0
- package/dist/hoi4_agent_tools/map/validation.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/map/validation.js +1177 -0
- package/dist/hoi4_agent_tools/map/validation.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts +3 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.js +50 -0
- package/dist/hoi4_agent_tools/mcp/prompts/register.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts +9 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js +10 -0
- package/dist/hoi4_agent_tools/mcp/registry/metadata.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js +278 -0
- package/dist/hoi4_agent_tools/mcp/resources/register.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts +24 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.js +234 -0
- package/dist/hoi4_agent_tools/mcp/security/auth.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts +35 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js +835 -0
- package/dist/hoi4_agent_tools/mcp/server/base-tools.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/server/create.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js +26 -0
- package/dist/hoi4_agent_tools/mcp/server/create.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts +133 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js +127 -0
- package/dist/hoi4_agent_tools/mcp/server/output-schemas.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts +8 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js +21 -0
- package/dist/hoi4_agent_tools/mcp/server/progress.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts +232 -0
- package/dist/hoi4_agent_tools/mcp/server/result.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js +353 -0
- package/dist/hoi4_agent_tools/mcp/server/result.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.js +1059 -0
- package/dist/hoi4_agent_tools/mcp/tools/focus.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts +111 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.js +598 -0
- package/dist/hoi4_agent_tools/mcp/tools/gui.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts +5 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js +685 -0
- package/dist/hoi4_agent_tools/mcp/tools/map.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts +58 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js +181 -0
- package/dist/hoi4_agent_tools/mcp/transports/bounded-stdio.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts +26 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js +86 -0
- package/dist/hoi4_agent_tools/mcp/transports/event-store.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts +28 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js +744 -0
- package/dist/hoi4_agent_tools/mcp/transports/http.js.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts +15 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js +51 -0
- package/dist/hoi4_agent_tools/mcp/transports/protocol-gate.js.map +1 -0
- package/dist/hoi4_agent_tools/runtime.d.ts +4 -0
- package/dist/hoi4_agent_tools/runtime.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/runtime.js +27 -0
- package/dist/hoi4_agent_tools/runtime.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/common.d.ts +74 -0
- package/dist/hoi4_agent_tools/schemas/common.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/common.js +51 -0
- package/dist/hoi4_agent_tools/schemas/common.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/focus.d.ts +784 -0
- package/dist/hoi4_agent_tools/schemas/focus.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/focus.js +330 -0
- package/dist/hoi4_agent_tools/schemas/focus.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/index.d.ts +5 -0
- package/dist/hoi4_agent_tools/schemas/index.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/index.js +5 -0
- package/dist/hoi4_agent_tools/schemas/index.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/map.d.ts +654 -0
- package/dist/hoi4_agent_tools/schemas/map.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/map.js +570 -0
- package/dist/hoi4_agent_tools/schemas/map.js.map +1 -0
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts +259 -0
- package/dist/hoi4_agent_tools/schemas/transaction.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/schemas/transaction.js +104 -0
- package/dist/hoi4_agent_tools/schemas/transaction.js.map +1 -0
- package/dist/hoi4_agent_tools/version.d.ts +6 -0
- package/dist/hoi4_agent_tools/version.d.ts.map +1 -0
- package/dist/hoi4_agent_tools/version.js +7 -0
- package/dist/hoi4_agent_tools/version.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/docs/README.md +22 -0
- package/docs/adr/0001-runtime-and-language.md +16 -0
- package/docs/adr/0002-lossless-source-model.md +18 -0
- package/docs/adr/0003-image-and-font-codecs.md +18 -0
- package/docs/adr/0004-focus-layout.md +16 -0
- package/docs/adr/0005-offline-rendering.md +18 -0
- package/docs/adr/0006-mcp-sdk-and-protocol.md +16 -0
- package/docs/adr/0007-storage-and-transactions.md +24 -0
- package/docs/adr/0008-test-strategy.md +18 -0
- package/docs/adr/0009-packaging-and-registry.md +16 -0
- package/docs/adr/0010-unsupported-and-refusal-policy.md +18 -0
- package/docs/adr/0011-http-security.md +16 -0
- package/docs/adr/README.md +13 -0
- package/docs/architecture.md +60 -0
- package/docs/artifacts.md +133 -0
- package/docs/compatibility.md +33 -0
- package/docs/completion-report.md +69 -0
- package/docs/configuration.md +285 -0
- package/docs/focus-workflow.md +68 -0
- package/docs/gui-workflow.md +121 -0
- package/docs/limitations.md +30 -0
- package/docs/map-workflow.md +89 -0
- package/docs/performance.md +117 -0
- package/docs/release.md +200 -0
- package/docs/research/mcp-2026-07-10.md +46 -0
- package/docs/research/source-ledger.md +68 -0
- package/docs/security.md +139 -0
- package/docs/self-hosting.md +168 -0
- package/docs/testing.md +43 -0
- package/docs/third-party-notices.md +11 -0
- package/docs/transactions.md +162 -0
- package/docs/troubleshooting.md +46 -0
- package/package.json +98 -5
- package/schemas/configuration.schema.json +794 -0
- package/schemas/continuous-focus-palette.schema.json +325 -0
- package/schemas/focus-plan.schema.json +902 -0
- package/schemas/focus-planning-sidecar.schema.json +364 -0
- package/schemas/gui-animation-source.schema.json +176 -0
- package/schemas/gui-helper.schema.json +301 -0
- package/schemas/gui-scenario.schema.json +298 -0
- package/schemas/map-operation.schema.json +2284 -0
- package/schemas/operation-result.schema.json +344 -0
- package/schemas/transaction-manifest.schema.json +685 -0
- package/server.json +32 -0
|
@@ -0,0 +1,744 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { isIP } from 'node:net';
|
|
3
|
+
import { TextDecoder } from 'node:util';
|
|
4
|
+
import express, {} from 'express';
|
|
5
|
+
import { hostHeaderValidation } from '@modelcontextprotocol/sdk/server/middleware/hostHeaderValidation.js';
|
|
6
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
+
import { canonicalJson } from '../../core/canonical.js';
|
|
9
|
+
import { MCP_PROTOCOL_VERSION } from '../../version.js';
|
|
10
|
+
import { authenticate, bearerChallenge, HttpAuthError, protectedResourceMetadata, validateHttpDeployment, } from '../security/auth.js';
|
|
11
|
+
import { BoundedEventStore, SharedEventStoreBudget } from './event-store.js';
|
|
12
|
+
import { FinalProtocolTransport } from './protocol-gate.js';
|
|
13
|
+
const WRITE_SCOPED_TOOLS = new Set([
|
|
14
|
+
'hoi4.project_register',
|
|
15
|
+
'hoi4.focus_plan_changes',
|
|
16
|
+
'hoi4.gui_plan_changes',
|
|
17
|
+
'hoi4.map_plan',
|
|
18
|
+
'hoi4.transaction_apply',
|
|
19
|
+
'hoi4.transaction_rollback',
|
|
20
|
+
]);
|
|
21
|
+
const MCP_ALLOWED_METHODS = 'GET, POST, DELETE';
|
|
22
|
+
const SINGLETON_SECURITY_HEADERS = new Set([
|
|
23
|
+
'authorization',
|
|
24
|
+
'content-encoding',
|
|
25
|
+
'content-type',
|
|
26
|
+
'host',
|
|
27
|
+
'last-event-id',
|
|
28
|
+
'mcp-protocol-version',
|
|
29
|
+
'mcp-session-id',
|
|
30
|
+
'origin',
|
|
31
|
+
'x-forwarded-for',
|
|
32
|
+
]);
|
|
33
|
+
const HTTP_TOKEN = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/u;
|
|
34
|
+
const HTTP_QVALUE = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/u;
|
|
35
|
+
const UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
|
|
36
|
+
function isRecord(value) {
|
|
37
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
38
|
+
}
|
|
39
|
+
/** Format a configured bind host for an authority component without changing the listen host. */
|
|
40
|
+
export function httpUrlHost(host) {
|
|
41
|
+
return host.includes(':') && !(host.startsWith('[') && host.endsWith(']')) ? `[${host}]` : host;
|
|
42
|
+
}
|
|
43
|
+
/** Match the WHATWG hostname representation used by the SDK Host-header middleware. */
|
|
44
|
+
export function httpAllowedHostname(host) {
|
|
45
|
+
return new URL(`http://${httpUrlHost(host)}`).hostname;
|
|
46
|
+
}
|
|
47
|
+
function normalizeIpAddress(value) {
|
|
48
|
+
const lower = value.toLowerCase();
|
|
49
|
+
return lower.startsWith('::ffff:') && isIP(lower.slice(7)) === 4 ? lower.slice(7) : lower;
|
|
50
|
+
}
|
|
51
|
+
export function rateLimitClientAddress(request, configuration) {
|
|
52
|
+
const socketAddress = normalizeIpAddress(request.socket.remoteAddress ?? 'unknown');
|
|
53
|
+
const trusted = new Set(configuration.http.trustedProxyAddresses.map((address) => normalizeIpAddress(address)));
|
|
54
|
+
if (!trusted.has(socketAddress))
|
|
55
|
+
return socketAddress;
|
|
56
|
+
const forwarded = request.headers['x-forwarded-for'];
|
|
57
|
+
if (typeof forwarded !== 'string')
|
|
58
|
+
return undefined;
|
|
59
|
+
const client = forwarded.split(',')[0]?.trim();
|
|
60
|
+
if (client === undefined || isIP(client) === 0)
|
|
61
|
+
return undefined;
|
|
62
|
+
return normalizeIpAddress(client);
|
|
63
|
+
}
|
|
64
|
+
export function requiredScopesForMcpRequest(configuration, body) {
|
|
65
|
+
const scopes = [...(configuration.http.oauth?.requiredScopes ?? ['hoi4:read'])];
|
|
66
|
+
if (!isRecord(body) || body.method !== 'tools/call' || !isRecord(body.params)) {
|
|
67
|
+
return [...new Set(scopes)];
|
|
68
|
+
}
|
|
69
|
+
if (typeof body.params.name === 'string' && WRITE_SCOPED_TOOLS.has(body.params.name)) {
|
|
70
|
+
scopes.push('hoi4:write');
|
|
71
|
+
}
|
|
72
|
+
return [...new Set(scopes)];
|
|
73
|
+
}
|
|
74
|
+
function sessionHeader(request) {
|
|
75
|
+
const value = request.headers['mcp-session-id'];
|
|
76
|
+
return Array.isArray(value) ? undefined : value;
|
|
77
|
+
}
|
|
78
|
+
function scrubAuthorizationHeader(request) {
|
|
79
|
+
delete request.headers.authorization;
|
|
80
|
+
for (let index = request.rawHeaders.length - 2; index >= 0; index -= 2) {
|
|
81
|
+
if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {
|
|
82
|
+
request.rawHeaders.splice(index, 2);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function jsonRpcError(response, status, message, code = -32_000) {
|
|
87
|
+
response.status(status).json({
|
|
88
|
+
jsonrpc: '2.0',
|
|
89
|
+
error: { code, message },
|
|
90
|
+
id: null,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function splitHttpHeader(value, separator) {
|
|
94
|
+
const parts = [];
|
|
95
|
+
let start = 0;
|
|
96
|
+
let quoted = false;
|
|
97
|
+
let escaped = false;
|
|
98
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
99
|
+
const character = value[index];
|
|
100
|
+
const code = value.charCodeAt(index);
|
|
101
|
+
if (code === 0x0a || code === 0x0d || code === 0x00)
|
|
102
|
+
return undefined;
|
|
103
|
+
if (escaped) {
|
|
104
|
+
escaped = false;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (quoted && character === '\\') {
|
|
108
|
+
escaped = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (character === '"') {
|
|
112
|
+
quoted = !quoted;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (!quoted && character === separator) {
|
|
116
|
+
const part = value.slice(start, index).trim();
|
|
117
|
+
if (part.length === 0)
|
|
118
|
+
return undefined;
|
|
119
|
+
parts.push(part);
|
|
120
|
+
start = index + 1;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (quoted || escaped)
|
|
124
|
+
return undefined;
|
|
125
|
+
const finalPart = value.slice(start).trim();
|
|
126
|
+
if (finalPart.length === 0)
|
|
127
|
+
return undefined;
|
|
128
|
+
parts.push(finalPart);
|
|
129
|
+
return parts;
|
|
130
|
+
}
|
|
131
|
+
function decodeHttpParameterValue(raw) {
|
|
132
|
+
if (HTTP_TOKEN.test(raw))
|
|
133
|
+
return raw;
|
|
134
|
+
if (raw.length < 2 || !raw.startsWith('"') || !raw.endsWith('"'))
|
|
135
|
+
return undefined;
|
|
136
|
+
let value = '';
|
|
137
|
+
let escaped = false;
|
|
138
|
+
for (let index = 1; index < raw.length - 1; index += 1) {
|
|
139
|
+
const character = raw[index];
|
|
140
|
+
const code = raw.charCodeAt(index);
|
|
141
|
+
if (escaped) {
|
|
142
|
+
if ((code < 0x20 && code !== 0x09) || code === 0x7f)
|
|
143
|
+
return undefined;
|
|
144
|
+
value += character;
|
|
145
|
+
escaped = false;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (character === '\\') {
|
|
149
|
+
escaped = true;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (character === '"' || (code < 0x20 && code !== 0x09) || code === 0x7f)
|
|
153
|
+
return undefined;
|
|
154
|
+
value += character;
|
|
155
|
+
}
|
|
156
|
+
return escaped ? undefined : value;
|
|
157
|
+
}
|
|
158
|
+
function parseMediaType(value) {
|
|
159
|
+
const segments = splitHttpHeader(value, ';');
|
|
160
|
+
if (segments === undefined)
|
|
161
|
+
return undefined;
|
|
162
|
+
const [rawMediaType, ...rawParameters] = segments;
|
|
163
|
+
const mediaType = rawMediaType?.trim() ?? '';
|
|
164
|
+
const slash = mediaType.indexOf('/');
|
|
165
|
+
if (slash < 1 || mediaType.includes('/', slash + 1))
|
|
166
|
+
return undefined;
|
|
167
|
+
const type = mediaType.slice(0, slash);
|
|
168
|
+
const subtype = mediaType.slice(slash + 1);
|
|
169
|
+
if (!HTTP_TOKEN.test(type) || !HTTP_TOKEN.test(subtype))
|
|
170
|
+
return undefined;
|
|
171
|
+
const parameters = new Map();
|
|
172
|
+
const quotedParameters = new Set();
|
|
173
|
+
for (const rawParameter of rawParameters) {
|
|
174
|
+
const equals = rawParameter.indexOf('=');
|
|
175
|
+
if (equals < 1)
|
|
176
|
+
return undefined;
|
|
177
|
+
const rawName = rawParameter.slice(0, equals);
|
|
178
|
+
const rawValue = rawParameter.slice(equals + 1);
|
|
179
|
+
if (rawName !== rawName.trim() || rawValue !== rawValue.trim())
|
|
180
|
+
return undefined;
|
|
181
|
+
const name = rawName.toLowerCase();
|
|
182
|
+
const parameterValue = decodeHttpParameterValue(rawValue);
|
|
183
|
+
if (!HTTP_TOKEN.test(name) || parameterValue === undefined || parameters.has(name)) {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
parameters.set(name, parameterValue);
|
|
187
|
+
if (rawValue.startsWith('"'))
|
|
188
|
+
quotedParameters.add(name);
|
|
189
|
+
}
|
|
190
|
+
return {
|
|
191
|
+
mediaType: `${type.toLowerCase()}/${subtype.toLowerCase()}`,
|
|
192
|
+
parameters,
|
|
193
|
+
quotedParameters,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function acceptsExactMediaTypes(value, required) {
|
|
197
|
+
if (typeof value !== 'string')
|
|
198
|
+
return false;
|
|
199
|
+
const entries = splitHttpHeader(value, ',');
|
|
200
|
+
if (entries === undefined)
|
|
201
|
+
return false;
|
|
202
|
+
const accepted = new Set();
|
|
203
|
+
for (const entry of entries) {
|
|
204
|
+
const parsed = parseMediaType(entry);
|
|
205
|
+
if (parsed === undefined)
|
|
206
|
+
return false;
|
|
207
|
+
const quality = parsed.parameters.get('q') ?? '1';
|
|
208
|
+
if (parsed.quotedParameters.has('q') || !HTTP_QVALUE.test(quality))
|
|
209
|
+
return false;
|
|
210
|
+
const representationParameters = [...parsed.parameters.keys()].filter((name) => name !== 'q');
|
|
211
|
+
if (representationParameters.length === 0 && Number(quality) > 0) {
|
|
212
|
+
accepted.add(parsed.mediaType);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return required.every((mediaType) => accepted.has(mediaType));
|
|
216
|
+
}
|
|
217
|
+
function rejectAmbiguousSecurityHeaders(request, response, next) {
|
|
218
|
+
const counts = new Map();
|
|
219
|
+
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
220
|
+
const name = request.rawHeaders[index]?.toLowerCase();
|
|
221
|
+
if (name !== undefined && SINGLETON_SECURITY_HEADERS.has(name)) {
|
|
222
|
+
counts.set(name, (counts.get(name) ?? 0) + 1);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const invalid = [...counts].find(([, count]) => count > 1);
|
|
226
|
+
if ((counts.get('host') ?? 0) !== 1 || invalid !== undefined) {
|
|
227
|
+
jsonRpcError(response, 400, 'Ambiguous or duplicate security-sensitive HTTP header');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
next();
|
|
231
|
+
}
|
|
232
|
+
function validateRequestTargetAuthority(allowedHosts) {
|
|
233
|
+
const normalizedAllowedHosts = new Set(allowedHosts.map((host) => host.toLowerCase()));
|
|
234
|
+
return (request, response, next) => {
|
|
235
|
+
if (request.url.startsWith('/')) {
|
|
236
|
+
next();
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (!/^https?:\/\//iu.test(request.url)) {
|
|
240
|
+
jsonRpcError(response, 400, 'Unsupported HTTP request-target form');
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const target = new URL(request.url);
|
|
245
|
+
const hostHeader = request.headers.host;
|
|
246
|
+
if (typeof hostHeader !== 'string')
|
|
247
|
+
throw new Error('Host is missing');
|
|
248
|
+
const headerAuthority = new URL(`${target.protocol}//${hostHeader}`);
|
|
249
|
+
if (target.username.length > 0 ||
|
|
250
|
+
target.password.length > 0 ||
|
|
251
|
+
target.hash.length > 0 ||
|
|
252
|
+
!normalizedAllowedHosts.has(target.hostname.toLowerCase()) ||
|
|
253
|
+
target.host.toLowerCase() !== headerAuthority.host.toLowerCase()) {
|
|
254
|
+
throw new Error('Absolute request target has an untrusted authority');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
jsonRpcError(response, 400, 'Absolute request-target authority does not match Host');
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
next();
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
function rejectUnacceptableMcpAccept(request, response, required) {
|
|
265
|
+
if (acceptsExactMediaTypes(request.headers.accept, required))
|
|
266
|
+
return false;
|
|
267
|
+
jsonRpcError(response, 406, `Not Acceptable: Accept must include ${required.join(' and ')}`);
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
function requireMcpJsonContentType(request, response, next) {
|
|
271
|
+
// Validate the complete field value instead of using the SDK's substring check or a
|
|
272
|
+
// lenient MIME parser. This accepts syntactically valid parameters on application/json,
|
|
273
|
+
// but not structured suffixes, malformed parameters, duplicate field values, or a
|
|
274
|
+
// parameter whose value merely contains "application/json".
|
|
275
|
+
const contentType = request.headers['content-type'];
|
|
276
|
+
const parsed = typeof contentType === 'string' ? parseMediaType(contentType) : undefined;
|
|
277
|
+
const charset = parsed?.parameters.get('charset');
|
|
278
|
+
if (parsed?.mediaType !== 'application/json' ||
|
|
279
|
+
(charset !== undefined && charset.toLowerCase() !== 'utf-8')) {
|
|
280
|
+
jsonRpcError(response, 415, 'Unsupported Media Type: Content-Type must be UTF-8 application/json');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
next();
|
|
284
|
+
}
|
|
285
|
+
function requireIdentityContentEncoding(request, response, next) {
|
|
286
|
+
const encoding = request.headers['content-encoding'];
|
|
287
|
+
if (encoding !== undefined &&
|
|
288
|
+
(typeof encoding !== 'string' || encoding.trim().toLowerCase() !== 'identity')) {
|
|
289
|
+
jsonRpcError(response, 415, 'Unsupported Media Type: Content-Encoding must be identity');
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
next();
|
|
293
|
+
}
|
|
294
|
+
function verifyMcpUtf8(_request, _response, buffer, _encoding) {
|
|
295
|
+
UTF8_DECODER.decode(buffer);
|
|
296
|
+
}
|
|
297
|
+
function requireParsedMcpJsonBody(request, response, next) {
|
|
298
|
+
if (request.body === undefined) {
|
|
299
|
+
jsonRpcError(response, 400, 'Parse error: Invalid JSON', -32_700);
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
next();
|
|
303
|
+
}
|
|
304
|
+
function rejectInvalidProtocolHeader(request, response, required) {
|
|
305
|
+
const value = request.headers['mcp-protocol-version'];
|
|
306
|
+
if (value === MCP_PROTOCOL_VERSION)
|
|
307
|
+
return false;
|
|
308
|
+
if (value === undefined && !required)
|
|
309
|
+
return false;
|
|
310
|
+
if (value === undefined) {
|
|
311
|
+
jsonRpcError(response, 400, `Missing MCP-Protocol-Version header; this server requires ${MCP_PROTOCOL_VERSION}`);
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
jsonRpcError(response, 400, `Unsupported MCP protocol version; this server requires ${MCP_PROTOCOL_VERSION}`);
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
function sendAuthError(response, configuration, error) {
|
|
318
|
+
response.setHeader('WWW-Authenticate', bearerChallenge(configuration, error));
|
|
319
|
+
response.status(error.status).json({ error: error.code });
|
|
320
|
+
}
|
|
321
|
+
export function sessionAuthorizationMatches(session, principal) {
|
|
322
|
+
return (session.principal === principal.principal &&
|
|
323
|
+
session.clientId === principal.clientId &&
|
|
324
|
+
session.credentialId === principal.credentialId &&
|
|
325
|
+
session.scopes.every((scope) => principal.scopes.includes(scope)));
|
|
326
|
+
}
|
|
327
|
+
function boundedSessionExpiration(principal, now, sessionTtlMs) {
|
|
328
|
+
const inactivityExpiration = now + sessionTtlMs;
|
|
329
|
+
return principal.expiresAt === undefined
|
|
330
|
+
? inactivityExpiration
|
|
331
|
+
: Math.min(inactivityExpiration, principal.expiresAt * 1000);
|
|
332
|
+
}
|
|
333
|
+
export async function startHttpServer(engine, configuration, serverFactory) {
|
|
334
|
+
validateHttpDeployment(configuration);
|
|
335
|
+
const http = configuration.http;
|
|
336
|
+
const allowedHosts = [httpAllowedHostname(http.host)];
|
|
337
|
+
if (http.publicUrl !== undefined)
|
|
338
|
+
allowedHosts.push(new URL(http.publicUrl).hostname);
|
|
339
|
+
const app = express();
|
|
340
|
+
app.disable('x-powered-by');
|
|
341
|
+
app.use(rejectAmbiguousSecurityHeaders);
|
|
342
|
+
app.use(validateRequestTargetAuthority(allowedHosts));
|
|
343
|
+
app.use(hostHeaderValidation([...new Set(allowedHosts)]));
|
|
344
|
+
const allowedOrigins = new Set(http.allowedOrigins.map((origin) => new URL(origin).origin));
|
|
345
|
+
app.use((request, response, next) => {
|
|
346
|
+
const origin = request.headers.origin;
|
|
347
|
+
if (origin !== undefined && !allowedOrigins.has(origin)) {
|
|
348
|
+
response.status(403).json({ error: 'origin_not_allowed' });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
next();
|
|
352
|
+
});
|
|
353
|
+
const preAuthRates = new Map();
|
|
354
|
+
const rates = new Map();
|
|
355
|
+
let lastRatePruneMinute = -1;
|
|
356
|
+
let concurrent = 0;
|
|
357
|
+
const admissionMiddleware = (request, response, next) => {
|
|
358
|
+
if (concurrent >= http.maxConcurrentRequests) {
|
|
359
|
+
response.status(429).setHeader('Retry-After', '1').json({ error: 'concurrency_limit' });
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
concurrent += 1;
|
|
363
|
+
let released = false;
|
|
364
|
+
const release = () => {
|
|
365
|
+
if (released)
|
|
366
|
+
return;
|
|
367
|
+
released = true;
|
|
368
|
+
concurrent = Math.max(0, concurrent - 1);
|
|
369
|
+
};
|
|
370
|
+
response.once('finish', release);
|
|
371
|
+
response.once('close', release);
|
|
372
|
+
request.releaseAdmission = release;
|
|
373
|
+
const minute = Math.floor(Date.now() / 60_000);
|
|
374
|
+
if (minute !== lastRatePruneMinute) {
|
|
375
|
+
for (const windows of [preAuthRates, rates]) {
|
|
376
|
+
for (const [key, window] of windows) {
|
|
377
|
+
if (window.minute < minute)
|
|
378
|
+
windows.delete(key);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
lastRatePruneMinute = minute;
|
|
382
|
+
}
|
|
383
|
+
const remoteAddress = rateLimitClientAddress(request, configuration);
|
|
384
|
+
if (remoteAddress === undefined) {
|
|
385
|
+
response.status(400).json({ error: 'invalid_forwarded_for' });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const preAuthRate = preAuthRates.get(remoteAddress);
|
|
389
|
+
const currentPreAuthRate = preAuthRate?.minute === minute ? preAuthRate : { minute, count: 0 };
|
|
390
|
+
currentPreAuthRate.count += 1;
|
|
391
|
+
preAuthRates.set(remoteAddress, currentPreAuthRate);
|
|
392
|
+
if (currentPreAuthRate.count > http.requestsPerMinute) {
|
|
393
|
+
response.status(429).setHeader('Retry-After', '60').json({ error: 'rate_limit' });
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
next();
|
|
397
|
+
};
|
|
398
|
+
app.use(admissionMiddleware);
|
|
399
|
+
const authenticateMiddleware = async (request, response, next) => {
|
|
400
|
+
try {
|
|
401
|
+
const authorization = request.headers.authorization;
|
|
402
|
+
request.authPrincipal = await authenticate(Array.isArray(authorization) ? undefined : authorization, configuration, requiredScopesForMcpRequest(configuration, request.body));
|
|
403
|
+
// Authentication is complete; do not let the SDK copy the bearer secret
|
|
404
|
+
// into MessageExtraInfo.requestInfo for downstream MCP handlers.
|
|
405
|
+
scrubAuthorizationHeader(request);
|
|
406
|
+
const minute = Math.floor(Date.now() / 60_000);
|
|
407
|
+
const rate = rates.get(request.authPrincipal.principal);
|
|
408
|
+
const current = rate?.minute === minute ? rate : { minute, count: 0 };
|
|
409
|
+
current.count += 1;
|
|
410
|
+
rates.set(request.authPrincipal.principal, current);
|
|
411
|
+
if (current.count > http.requestsPerMinute) {
|
|
412
|
+
response.status(429).setHeader('Retry-After', '60').json({ error: 'rate_limit' });
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
next();
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
const authError = error instanceof HttpAuthError
|
|
419
|
+
? error
|
|
420
|
+
: new HttpAuthError(401, 'AUTH_INVALID', 'Authentication failed', 'invalid_token', requiredScopesForMcpRequest(configuration, request.body));
|
|
421
|
+
sendAuthError(response, configuration, authError);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
app.all('/mcp', async (request, response, next) => {
|
|
425
|
+
if (['GET', 'POST', 'DELETE'].includes(request.method)) {
|
|
426
|
+
next();
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
await authenticateMiddleware(request, response, () => {
|
|
430
|
+
response.setHeader('Allow', MCP_ALLOWED_METHODS);
|
|
431
|
+
jsonRpcError(response, 405, 'Method not allowed.');
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
// Every MCP POST is media-type gated and parsed here under the configured byte limit.
|
|
435
|
+
// Supplying a defined parsed body to handleRequest prevents the SDK from falling back to
|
|
436
|
+
// an unbounded Request.json() parse for alternate media types or empty established-session
|
|
437
|
+
// requests. GET and DELETE deliberately remain bodyless.
|
|
438
|
+
app.post('/mcp', requireMcpJsonContentType);
|
|
439
|
+
app.post('/mcp', requireIdentityContentEncoding);
|
|
440
|
+
app.post('/mcp', express.json({
|
|
441
|
+
limit: http.maxBodyBytes,
|
|
442
|
+
strict: true,
|
|
443
|
+
type: () => true,
|
|
444
|
+
inflate: false,
|
|
445
|
+
verify: verifyMcpUtf8,
|
|
446
|
+
}));
|
|
447
|
+
app.post('/mcp', requireParsedMcpJsonBody);
|
|
448
|
+
if (http.oauth !== undefined && http.publicUrl !== undefined) {
|
|
449
|
+
const metadata = protectedResourceMetadata(configuration);
|
|
450
|
+
app.get('/.well-known/oauth-protected-resource', (_request, response) => response.json(metadata));
|
|
451
|
+
app.get('/.well-known/oauth-protected-resource/mcp', (_request, response) => response.json(metadata));
|
|
452
|
+
}
|
|
453
|
+
const sessions = new Map();
|
|
454
|
+
const eventStoreBudget = new SharedEventStoreBudget(http.maxEventStoreBytes);
|
|
455
|
+
let pendingSessions = 0;
|
|
456
|
+
const pendingSessionsByPrincipal = new Map();
|
|
457
|
+
let eventStreams = 0;
|
|
458
|
+
const eventStreamsByPrincipal = new Map();
|
|
459
|
+
const sessionTtlMs = http.sessionTtlSeconds * 1000;
|
|
460
|
+
const removeSession = async (sessionId) => {
|
|
461
|
+
const session = sessions.get(sessionId);
|
|
462
|
+
if (session === undefined)
|
|
463
|
+
return;
|
|
464
|
+
sessions.delete(sessionId);
|
|
465
|
+
if (session.expiryTimer !== undefined)
|
|
466
|
+
clearTimeout(session.expiryTimer);
|
|
467
|
+
session.eventStore.clear();
|
|
468
|
+
await session.transport.close().catch(() => undefined);
|
|
469
|
+
await session.server.close().catch(() => undefined);
|
|
470
|
+
};
|
|
471
|
+
const scheduleSessionExpiry = (sessionId, session) => {
|
|
472
|
+
if (session.expiryTimer !== undefined)
|
|
473
|
+
clearTimeout(session.expiryTimer);
|
|
474
|
+
session.expiryTimer = setTimeout(() => void removeSession(sessionId), Math.max(0, session.expiresAt - Date.now()));
|
|
475
|
+
session.expiryTimer.unref();
|
|
476
|
+
};
|
|
477
|
+
const pruneExpiredSessions = async () => {
|
|
478
|
+
const now = Date.now();
|
|
479
|
+
await Promise.all([...sessions]
|
|
480
|
+
.filter(([, session]) => session.expiresAt <= now)
|
|
481
|
+
.map(([sessionId]) => removeSession(sessionId)));
|
|
482
|
+
};
|
|
483
|
+
const findSession = async (request, response) => {
|
|
484
|
+
const id = sessionHeader(request);
|
|
485
|
+
if (id === undefined) {
|
|
486
|
+
jsonRpcError(response, 400, 'Missing MCP session ID');
|
|
487
|
+
return undefined;
|
|
488
|
+
}
|
|
489
|
+
const session = sessions.get(id);
|
|
490
|
+
if (session === undefined || session.expiresAt <= Date.now()) {
|
|
491
|
+
if (session !== undefined)
|
|
492
|
+
await removeSession(id);
|
|
493
|
+
jsonRpcError(response, 404, 'Unknown or expired MCP session');
|
|
494
|
+
return undefined;
|
|
495
|
+
}
|
|
496
|
+
const principal = request.authPrincipal;
|
|
497
|
+
if (session.principal !== principal?.principal) {
|
|
498
|
+
jsonRpcError(response, 403, 'MCP session belongs to another principal');
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
if (session.clientId !== principal.clientId ||
|
|
502
|
+
session.credentialId !== principal.credentialId) {
|
|
503
|
+
jsonRpcError(response, 403, 'MCP session authorization context does not match');
|
|
504
|
+
return undefined;
|
|
505
|
+
}
|
|
506
|
+
const missingSessionScopes = session.scopes.filter((scope) => !principal.scopes.includes(scope));
|
|
507
|
+
if (missingSessionScopes.length > 0) {
|
|
508
|
+
sendAuthError(response, configuration, new HttpAuthError(403, 'AUTH_SCOPE_INSUFFICIENT', 'Bearer token cannot reduce the session authorization scope', 'insufficient_scope', session.scopes));
|
|
509
|
+
return undefined;
|
|
510
|
+
}
|
|
511
|
+
const expandedScopes = [...new Set([...session.scopes, ...principal.scopes])];
|
|
512
|
+
session.scopes = expandedScopes;
|
|
513
|
+
session.authorizationContext.scopes = expandedScopes;
|
|
514
|
+
session.expiresAt = boundedSessionExpiration(principal, Date.now(), sessionTtlMs);
|
|
515
|
+
scheduleSessionExpiry(id, session);
|
|
516
|
+
return session;
|
|
517
|
+
};
|
|
518
|
+
app.post('/mcp', authenticateMiddleware, async (request, response) => {
|
|
519
|
+
try {
|
|
520
|
+
const sessionId = sessionHeader(request);
|
|
521
|
+
if (rejectInvalidProtocolHeader(request, response, sessionId !== undefined))
|
|
522
|
+
return;
|
|
523
|
+
if (Array.isArray(request.body)) {
|
|
524
|
+
jsonRpcError(response, 400, 'JSON-RPC batching is not supported');
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
let session;
|
|
528
|
+
if (sessionId !== undefined) {
|
|
529
|
+
session = await findSession(request, response);
|
|
530
|
+
if (session === undefined)
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
else if (isInitializeRequest(request.body)) {
|
|
534
|
+
if (rejectUnacceptableMcpAccept(request, response, ['application/json', 'text/event-stream'])) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
await pruneExpiredSessions();
|
|
538
|
+
const principal = request.authPrincipal;
|
|
539
|
+
if (principal === undefined) {
|
|
540
|
+
jsonRpcError(response, 401, 'Authentication context is missing');
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const initialExpiration = boundedSessionExpiration(principal, Date.now(), sessionTtlMs);
|
|
544
|
+
if (initialExpiration <= Date.now()) {
|
|
545
|
+
sendAuthError(response, configuration, new HttpAuthError(401, 'AUTH_INVALID', 'Bearer token has expired', 'invalid_token', requiredScopesForMcpRequest(configuration, request.body)));
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (sessions.size + pendingSessions >= http.maxSessions) {
|
|
549
|
+
response.status(429).json({ error: 'session_limit' });
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const principalSessionCount = [...sessions.values()].filter((session) => session.principal === principal.principal).length;
|
|
553
|
+
const pendingForPrincipal = pendingSessionsByPrincipal.get(principal.principal) ?? 0;
|
|
554
|
+
if (principalSessionCount + pendingForPrincipal >=
|
|
555
|
+
Math.min(http.maxSessions, http.maxSessionsPerPrincipal)) {
|
|
556
|
+
response.status(429).json({ error: 'principal_session_limit' });
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
pendingSessions += 1;
|
|
560
|
+
pendingSessionsByPrincipal.set(principal.principal, pendingForPrincipal + 1);
|
|
561
|
+
const authorizationContext = {
|
|
562
|
+
principal: principal.principal,
|
|
563
|
+
scopes: [...principal.scopes],
|
|
564
|
+
};
|
|
565
|
+
const server = serverFactory(engine, authorizationContext);
|
|
566
|
+
const eventStore = new BoundedEventStore(1000, sessionTtlMs, Math.min(http.maxSessionEventBytes, http.maxEventStoreBytes), eventStoreBudget);
|
|
567
|
+
const transport = new StreamableHTTPServerTransport({
|
|
568
|
+
sessionIdGenerator: randomUUID,
|
|
569
|
+
eventStore,
|
|
570
|
+
onsessioninitialized: (newId) => {
|
|
571
|
+
const session = {
|
|
572
|
+
transport,
|
|
573
|
+
server,
|
|
574
|
+
eventStore,
|
|
575
|
+
principal: principal.principal,
|
|
576
|
+
clientId: principal.clientId,
|
|
577
|
+
credentialId: principal.credentialId,
|
|
578
|
+
scopes: [...principal.scopes],
|
|
579
|
+
authorizationContext,
|
|
580
|
+
expiresAt: Math.min(initialExpiration, boundedSessionExpiration(principal, Date.now(), sessionTtlMs)),
|
|
581
|
+
};
|
|
582
|
+
sessions.set(newId, session);
|
|
583
|
+
scheduleSessionExpiry(newId, session);
|
|
584
|
+
},
|
|
585
|
+
onsessionclosed: async (closedId) => removeSession(closedId),
|
|
586
|
+
});
|
|
587
|
+
let initializedSessionId;
|
|
588
|
+
try {
|
|
589
|
+
transport.onclose = () => {
|
|
590
|
+
const id = transport.sessionId;
|
|
591
|
+
if (id !== undefined) {
|
|
592
|
+
const closed = sessions.get(id);
|
|
593
|
+
if (closed !== undefined)
|
|
594
|
+
clearTimeout(closed.expiryTimer);
|
|
595
|
+
closed?.eventStore.clear();
|
|
596
|
+
sessions.delete(id);
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
await server.connect(new FinalProtocolTransport(transport));
|
|
600
|
+
await transport.handleRequest(request, response, request.body);
|
|
601
|
+
initializedSessionId = transport.sessionId;
|
|
602
|
+
}
|
|
603
|
+
finally {
|
|
604
|
+
if (initializedSessionId === undefined ||
|
|
605
|
+
sessions.get(initializedSessionId)?.transport !== transport) {
|
|
606
|
+
const registeredId = transport.sessionId;
|
|
607
|
+
if (registeredId !== undefined && sessions.get(registeredId)?.transport === transport) {
|
|
608
|
+
clearTimeout(sessions.get(registeredId).expiryTimer);
|
|
609
|
+
sessions.delete(registeredId);
|
|
610
|
+
}
|
|
611
|
+
eventStore.clear();
|
|
612
|
+
await transport.close().catch(() => undefined);
|
|
613
|
+
await server.close().catch(() => undefined);
|
|
614
|
+
}
|
|
615
|
+
pendingSessions = Math.max(0, pendingSessions - 1);
|
|
616
|
+
const remaining = (pendingSessionsByPrincipal.get(principal.principal) ?? 1) - 1;
|
|
617
|
+
if (remaining <= 0)
|
|
618
|
+
pendingSessionsByPrincipal.delete(principal.principal);
|
|
619
|
+
else
|
|
620
|
+
pendingSessionsByPrincipal.set(principal.principal, remaining);
|
|
621
|
+
}
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
jsonRpcError(response, 400, 'No valid session ID or initialize request');
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
if (rejectUnacceptableMcpAccept(request, response, ['application/json', 'text/event-stream'])) {
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
await session.transport.handleRequest(request, response, request.body);
|
|
632
|
+
}
|
|
633
|
+
catch {
|
|
634
|
+
if (!response.headersSent)
|
|
635
|
+
jsonRpcError(response, 500, 'Internal server error');
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
app.get('/mcp', authenticateMiddleware, async (request, response) => {
|
|
639
|
+
if (rejectInvalidProtocolHeader(request, response, true))
|
|
640
|
+
return;
|
|
641
|
+
const session = await findSession(request, response);
|
|
642
|
+
if (session === undefined)
|
|
643
|
+
return;
|
|
644
|
+
if (rejectUnacceptableMcpAccept(request, response, ['text/event-stream']))
|
|
645
|
+
return;
|
|
646
|
+
const principal = request.authPrincipal?.principal;
|
|
647
|
+
if (principal === undefined) {
|
|
648
|
+
jsonRpcError(response, 401, 'Authentication context is missing');
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
const principalStreams = eventStreamsByPrincipal.get(principal) ?? 0;
|
|
652
|
+
if (eventStreams >= http.maxEventStreams) {
|
|
653
|
+
response.status(429).setHeader('Retry-After', '1').json({ error: 'event_stream_limit' });
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (principalStreams >= http.maxEventStreamsPerPrincipal) {
|
|
657
|
+
response
|
|
658
|
+
.status(429)
|
|
659
|
+
.setHeader('Retry-After', '1')
|
|
660
|
+
.json({ error: 'principal_event_stream_limit' });
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
eventStreams += 1;
|
|
664
|
+
eventStreamsByPrincipal.set(principal, principalStreams + 1);
|
|
665
|
+
let streamReleased = false;
|
|
666
|
+
const releaseStream = () => {
|
|
667
|
+
if (streamReleased)
|
|
668
|
+
return;
|
|
669
|
+
streamReleased = true;
|
|
670
|
+
eventStreams = Math.max(0, eventStreams - 1);
|
|
671
|
+
const remaining = (eventStreamsByPrincipal.get(principal) ?? 1) - 1;
|
|
672
|
+
if (remaining <= 0)
|
|
673
|
+
eventStreamsByPrincipal.delete(principal);
|
|
674
|
+
else
|
|
675
|
+
eventStreamsByPrincipal.set(principal, remaining);
|
|
676
|
+
};
|
|
677
|
+
response.once('finish', releaseStream);
|
|
678
|
+
response.once('close', releaseStream);
|
|
679
|
+
request.releaseAdmission?.();
|
|
680
|
+
await session.transport.handleRequest(request, response);
|
|
681
|
+
});
|
|
682
|
+
app.delete('/mcp', authenticateMiddleware, async (request, response) => {
|
|
683
|
+
if (rejectInvalidProtocolHeader(request, response, true))
|
|
684
|
+
return;
|
|
685
|
+
const session = await findSession(request, response);
|
|
686
|
+
if (session !== undefined)
|
|
687
|
+
await session.transport.handleRequest(request, response);
|
|
688
|
+
});
|
|
689
|
+
app.use((error, _request, response, next) => {
|
|
690
|
+
if (response.headersSent) {
|
|
691
|
+
next(error);
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (error.type === 'entity.too.large') {
|
|
695
|
+
jsonRpcError(response, 413, 'Payload too large');
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
if (error.type === 'entity.parse.failed') {
|
|
699
|
+
jsonRpcError(response, 400, 'Parse error: Invalid JSON', -32_700);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
if (error.type === 'entity.verify.failed') {
|
|
703
|
+
jsonRpcError(response, 400, 'Parse error: Invalid UTF-8 JSON', -32_700);
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
if (error.type === 'encoding.unsupported') {
|
|
707
|
+
jsonRpcError(response, 415, 'Unsupported Media Type: Content-Encoding must be identity');
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
jsonRpcError(response, 500, 'Internal server error');
|
|
711
|
+
});
|
|
712
|
+
const server = await new Promise((resolve, reject) => {
|
|
713
|
+
const listener = app.listen(http.port, http.host, () => resolve(listener));
|
|
714
|
+
listener.headersTimeout = http.headersTimeoutMs;
|
|
715
|
+
listener.requestTimeout = http.requestTimeoutMs;
|
|
716
|
+
listener.keepAliveTimeout = http.keepAliveTimeoutMs;
|
|
717
|
+
listener.maxConnections = http.maxConnections;
|
|
718
|
+
listener.maxRequestsPerSocket = http.maxRequestsPerSocket;
|
|
719
|
+
listener.once('error', reject);
|
|
720
|
+
});
|
|
721
|
+
const address = server.address();
|
|
722
|
+
const port = typeof address === 'object' && address !== null ? address.port : http.port;
|
|
723
|
+
const url = http.publicUrl === undefined
|
|
724
|
+
? `http://${httpUrlHost(http.host)}:${port}/mcp`
|
|
725
|
+
: new URL('/mcp', http.publicUrl).href;
|
|
726
|
+
process.stderr.write(`${canonicalJson({ level: 'info', event: 'http_listening', url })}\n`);
|
|
727
|
+
const cleanup = setInterval(() => {
|
|
728
|
+
for (const [sessionId, session] of sessions) {
|
|
729
|
+
if (session.expiresAt <= Date.now())
|
|
730
|
+
void removeSession(sessionId);
|
|
731
|
+
}
|
|
732
|
+
}, Math.min(60_000, sessionTtlMs));
|
|
733
|
+
cleanup.unref();
|
|
734
|
+
return {
|
|
735
|
+
server,
|
|
736
|
+
url,
|
|
737
|
+
async close() {
|
|
738
|
+
clearInterval(cleanup);
|
|
739
|
+
await Promise.all([...sessions.keys()].map(removeSession));
|
|
740
|
+
await new Promise((resolve, reject) => server.close((error) => (error === undefined ? resolve() : reject(error))));
|
|
741
|
+
},
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
//# sourceMappingURL=http.js.map
|