memory-journal-mcp 9.0.0 → 9.1.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/README.md +165 -124
- package/dist/audit/interceptor.d.ts.map +1 -1
- package/dist/auth/authorization-server-discovery.d.ts.map +1 -1
- package/dist/auth/middleware.d.ts +11 -22
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/scope-map.d.ts.map +1 -1
- package/dist/auth/scopes.d.ts.map +1 -1
- package/dist/auth/token-validator.d.ts.map +1 -1
- package/dist/auth/transport-agnostic.d.ts +4 -0
- package/dist/auth/transport-agnostic.d.ts.map +1 -1
- package/dist/auth/validation.d.ts +1 -0
- package/dist/auth/validation.d.ts.map +1 -1
- package/dist/{chunk-6RCZ4IYV.js → chunk-4FZCHFB5.js} +28 -5
- package/dist/{chunk-AIWHVEOW.js → chunk-D2FY6XS5.js} +99 -7
- package/dist/chunk-FZLB6NAV.js +746 -0
- package/dist/{chunk-NVXCRDCO.js → chunk-LE6ZWUPK.js} +216 -138
- package/dist/chunk-SGJU2DHU.js +2102 -0
- package/dist/{chunk-2UIMA6JF.js → chunk-T7SROBBV.js} +32375 -29351
- package/dist/{chunk-IYZWMAA4.js → chunk-Y6MMKR2Z.js} +957 -1333
- package/dist/cli/commands/admin.d.ts +1 -1
- package/dist/cli/commands/admin.d.ts.map +1 -1
- package/dist/cli/commands/entry-payload.d.ts +38 -0
- package/dist/cli/commands/entry-payload.d.ts.map +1 -0
- package/dist/cli/commands/entry.d.ts +10 -0
- package/dist/cli/commands/entry.d.ts.map +1 -1
- package/dist/cli/commands/exec.d.ts.map +1 -1
- package/dist/cli/commands/io.d.ts +1 -0
- package/dist/cli/commands/io.d.ts.map +1 -1
- package/dist/cli/commands/visualize.d.ts.map +1 -1
- package/dist/cli/index.js +7 -10
- package/dist/cli/schemas.d.ts +14 -1
- package/dist/cli/schemas.d.ts.map +1 -1
- package/dist/cli/server-options-builder.d.ts.map +1 -1
- package/dist/cli.js +7 -10
- package/dist/codemode/api/journal-api.d.ts +5 -23
- package/dist/codemode/api/journal-api.d.ts.map +1 -1
- package/dist/codemode/api/key-normalizer.d.ts +8 -0
- package/dist/codemode/api/key-normalizer.d.ts.map +1 -0
- package/dist/codemode/api/param-normalizer.d.ts +2 -4
- package/dist/codemode/api/param-normalizer.d.ts.map +1 -1
- package/dist/codemode/api/project-registry-ops.d.ts +26 -0
- package/dist/codemode/api/project-registry-ops.d.ts.map +1 -0
- package/dist/codemode/api/sandbox-bindings.d.ts +25 -0
- package/dist/codemode/api/sandbox-bindings.d.ts.map +1 -0
- package/dist/codemode/api/types.d.ts.map +1 -1
- package/dist/codemode/api-constants.d.ts +3 -39
- package/dist/codemode/api-constants.d.ts.map +1 -1
- package/dist/codemode/constants/examples-and-params.d.ts +31 -0
- package/dist/codemode/constants/examples-and-params.d.ts.map +1 -0
- package/dist/codemode/constants/method-aliases.d.ts +8 -0
- package/dist/codemode/constants/method-aliases.d.ts.map +1 -0
- package/dist/codemode/normalizers/domain-tool-interceptions.d.ts +6 -0
- package/dist/codemode/normalizers/domain-tool-interceptions.d.ts.map +1 -0
- package/dist/codemode/normalizers/entry-and-search-interceptions.d.ts +6 -0
- package/dist/codemode/normalizers/entry-and-search-interceptions.d.ts.map +1 -0
- package/dist/codemode/normalizers/generic-interceptions.d.ts +6 -0
- package/dist/codemode/normalizers/generic-interceptions.d.ts.map +1 -0
- package/dist/codemode/normalizers/github-and-backup-interceptions.d.ts +6 -0
- package/dist/codemode/normalizers/github-and-backup-interceptions.d.ts.map +1 -0
- package/dist/codemode/normalizers/shared-utils.d.ts +31 -0
- package/dist/codemode/normalizers/shared-utils.d.ts.map +1 -0
- package/dist/codemode/param-normalizer.d.ts +5 -1
- package/dist/codemode/param-normalizer.d.ts.map +1 -1
- package/dist/codemode/proxy-helpers.d.ts +34 -0
- package/dist/codemode/proxy-helpers.d.ts.map +1 -0
- package/dist/codemode/proxy-namespaces.d.ts +30 -0
- package/dist/codemode/proxy-namespaces.d.ts.map +1 -0
- package/dist/codemode/security.d.ts +11 -0
- package/dist/codemode/security.d.ts.map +1 -1
- package/dist/codemode/type-generator.d.ts.map +1 -1
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/worker/mj-shim-domains.d.ts +15 -0
- package/dist/codemode/worker/mj-shim-domains.d.ts.map +1 -0
- package/dist/codemode/worker/mj-shim-proxy.d.ts +18 -0
- package/dist/codemode/worker/mj-shim-proxy.d.ts.map +1 -0
- package/dist/codemode/worker/namespace-shims.d.ts +32 -0
- package/dist/codemode/worker/namespace-shims.d.ts.map +1 -0
- package/dist/codemode/worker/node-shims.d.ts +65 -0
- package/dist/codemode/worker/node-shims.d.ts.map +1 -0
- package/dist/codemode/worker/prototype-hardening.d.ts +18 -0
- package/dist/codemode/worker/prototype-hardening.d.ts.map +1 -0
- package/dist/codemode/worker/result-compactor.d.ts +20 -0
- package/dist/codemode/worker/result-compactor.d.ts.map +1 -0
- package/dist/codemode/worker/sandbox-globals.d.ts +19 -0
- package/dist/codemode/worker/sandbox-globals.d.ts.map +1 -0
- package/dist/codemode/worker-api-proxy.d.ts +1 -6
- package/dist/codemode/worker-api-proxy.d.ts.map +1 -1
- package/dist/codemode/worker-result-proxy.d.ts.map +1 -1
- package/dist/codemode/worker-sandbox.d.ts.map +1 -1
- package/dist/constants/instructions/admin.d.ts +1 -1
- package/dist/constants/instructions/admin.d.ts.map +1 -1
- package/dist/constants/instructions/base.d.ts +2 -2
- package/dist/constants/instructions/base.d.ts.map +1 -1
- package/dist/constants/instructions/codemode.d.ts +1 -1
- package/dist/constants/instructions/codemode.d.ts.map +1 -1
- package/dist/constants/instructions/core.d.ts +1 -1
- package/dist/constants/instructions/core.d.ts.map +1 -1
- package/dist/constants/instructions/github.d.ts +1 -1
- package/dist/constants/instructions/github.d.ts.map +1 -1
- package/dist/constants/instructions/gotchas.d.ts +1 -1
- package/dist/constants/instructions/gotchas.d.ts.map +1 -1
- package/dist/constants/instructions/io.d.ts +1 -1
- package/dist/constants/instructions/io.d.ts.map +1 -1
- package/dist/constants/instructions/templates.d.ts +1 -1
- package/dist/constants/instructions/templates.d.ts.map +1 -1
- package/dist/constants/server-instructions.d.ts +2 -2
- package/dist/constants/server-instructions.d.ts.map +1 -1
- package/dist/database/core/interfaces.d.ts +6 -6
- package/dist/database/core/interfaces.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/adapter-delegates.d.ts +161 -0
- package/dist/database/sqlite-adapter/adapter-delegates.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/backup.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/database-adapter.d.ts +17 -147
- package/dist/database/sqlite-adapter/database-adapter.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/entries/crud-read.d.ts +16 -0
- package/dist/database/sqlite-adapter/entries/crud-read.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/entries/crud-write.d.ts +62 -0
- package/dist/database/sqlite-adapter/entries/crud-write.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/entries/crud.d.ts +3 -92
- package/dist/database/sqlite-adapter/entries/crud.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/entries/importance.d.ts +7 -0
- package/dist/database/sqlite-adapter/entries/importance.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/entries/index.d.ts +9 -4
- package/dist/database/sqlite-adapter/entries/index.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/entries/revisions.d.ts +16 -0
- package/dist/database/sqlite-adapter/entries/revisions.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/entries/search/date-and-pagination.d.ts +37 -0
- package/dist/database/sqlite-adapter/entries/search/date-and-pagination.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/entries/search/entity-lookups.d.ts +31 -0
- package/dist/database/sqlite-adapter/entries/search/entity-lookups.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/entries/search/fts-and-hybrid.d.ts +44 -0
- package/dist/database/sqlite-adapter/entries/search/fts-and-hybrid.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/entries/search/index.d.ts +1 -1
- package/dist/database/sqlite-adapter/entries/search/index.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/entries/search/query.d.ts +3 -82
- package/dist/database/sqlite-adapter/entries/search/query.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/entries/statistics.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/insights/cross-project.d.ts +42 -0
- package/dist/database/sqlite-adapter/insights/cross-project.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/insights/entry-digest-queries.d.ts +17 -0
- package/dist/database/sqlite-adapter/insights/entry-digest-queries.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/insights/team-collaboration.d.ts +29 -0
- package/dist/database/sqlite-adapter/insights/team-collaboration.d.ts.map +1 -0
- package/dist/database/sqlite-adapter/insights-operations.d.ts +11 -50
- package/dist/database/sqlite-adapter/insights-operations.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/lifecycle-operations.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/members.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/native-connection.d.ts +5 -0
- package/dist/database/sqlite-adapter/native-connection.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/relationships.d.ts +1 -2
- package/dist/database/sqlite-adapter/relationships.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/tags.d.ts.map +1 -1
- package/dist/database/sqlite-adapter/vector-operations.d.ts.map +1 -1
- package/dist/filtering/tool-constants.d.ts +1 -1
- package/dist/{fts-T466O2JP.js → fts-YHUVPPYF.js} +2 -2
- package/dist/github/commit-references/enricher.d.ts.map +1 -1
- package/dist/github/github-integration/projects.d.ts.map +1 -1
- package/dist/handlers/prompts/adversarial-helpers.d.ts +75 -0
- package/dist/handlers/prompts/adversarial-helpers.d.ts.map +1 -0
- package/dist/handlers/prompts/adversarial.d.ts +3 -5
- package/dist/handlers/prompts/adversarial.d.ts.map +1 -1
- package/dist/handlers/prompts/cms-supervisor.d.ts +8 -0
- package/dist/handlers/prompts/cms-supervisor.d.ts.map +1 -0
- package/dist/handlers/prompts/index.d.ts.map +1 -1
- package/dist/handlers/prompts/optimizer.d.ts.map +1 -1
- package/dist/handlers/prompts/workflow.d.ts.map +1 -1
- package/dist/handlers/resources/core/briefing/context-section.d.ts.map +1 -1
- package/dist/handlers/resources/core/briefing/github-repo-and-kanban.d.ts +57 -0
- package/dist/handlers/resources/core/briefing/github-repo-and-kanban.d.ts.map +1 -0
- package/dist/handlers/resources/core/briefing/github-section.d.ts +23 -20
- package/dist/handlers/resources/core/briefing/github-section.d.ts.map +1 -1
- package/dist/handlers/resources/core/briefing/system-section.d.ts.map +1 -1
- package/dist/handlers/resources/core/briefing/user-message.d.ts.map +1 -1
- package/dist/handlers/resources/help.d.ts.map +1 -1
- package/dist/handlers/resources/index.d.ts.map +1 -1
- package/dist/handlers/resources/shared.d.ts +1 -0
- package/dist/handlers/resources/shared.d.ts.map +1 -1
- package/dist/handlers/resources/staleness.d.ts.map +1 -1
- package/dist/handlers/tools/admin/batch-tools.d.ts.map +1 -1
- package/dist/handlers/tools/admin/core-tools.d.ts.map +1 -1
- package/dist/handlers/tools/admin/index.d.ts.map +1 -1
- package/dist/handlers/tools/admin/lifecycle-tools.d.ts.map +1 -1
- package/dist/handlers/tools/admin/project-tools.d.ts +1 -1
- package/dist/handlers/tools/admin/project-tools.d.ts.map +1 -1
- package/dist/handlers/tools/admin/schemas/crud-batch-schemas.d.ts +609 -0
- package/dist/handlers/tools/admin/schemas/crud-batch-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/admin/schemas/project-and-ops-schemas.d.ts +136 -0
- package/dist/handlers/tools/admin/schemas/project-and-ops-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/admin/schemas.d.ts +2 -733
- package/dist/handlers/tools/admin/schemas.d.ts.map +1 -1
- package/dist/handlers/tools/admin/update_configuration.d.ts +14 -0
- package/dist/handlers/tools/admin/update_configuration.d.ts.map +1 -1
- package/dist/handlers/tools/admin/vector-tools.d.ts.map +1 -1
- package/dist/handlers/tools/analytics.d.ts.map +1 -1
- package/dist/handlers/tools/arg-interceptions.d.ts +51 -0
- package/dist/handlers/tools/arg-interceptions.d.ts.map +1 -0
- package/dist/handlers/tools/codemode/code-healer.d.ts +11 -0
- package/dist/handlers/tools/codemode/code-healer.d.ts.map +1 -0
- package/dist/handlers/tools/codemode/handler.d.ts +4 -23
- package/dist/handlers/tools/codemode/handler.d.ts.map +1 -1
- package/dist/handlers/tools/codemode/json-entry-recovery.d.ts +13 -0
- package/dist/handlers/tools/codemode/json-entry-recovery.d.ts.map +1 -0
- package/dist/handlers/tools/codemode/payload-synthesizer.d.ts +17 -0
- package/dist/handlers/tools/codemode/payload-synthesizer.d.ts.map +1 -0
- package/dist/handlers/tools/codemode/secure-dispatcher.d.ts +25 -0
- package/dist/handlers/tools/codemode/secure-dispatcher.d.ts.map +1 -0
- package/dist/handlers/tools/codemode/sql-recovery.d.ts +14 -0
- package/dist/handlers/tools/codemode/sql-recovery.d.ts.map +1 -0
- package/dist/handlers/tools/core/dummy-healing/alias-catalog.d.ts +21 -0
- package/dist/handlers/tools/core/dummy-healing/alias-catalog.d.ts.map +1 -0
- package/dist/handlers/tools/core/dummy-healing/entry-fallbacks.d.ts +4 -0
- package/dist/handlers/tools/core/dummy-healing/entry-fallbacks.d.ts.map +1 -0
- package/dist/handlers/tools/core/dummy-healing/fs-fallbacks.d.ts +19 -0
- package/dist/handlers/tools/core/dummy-healing/fs-fallbacks.d.ts.map +1 -0
- package/dist/handlers/tools/core/dummy-healing/index.d.ts +7 -0
- package/dist/handlers/tools/core/dummy-healing/index.d.ts.map +1 -0
- package/dist/handlers/tools/core/dummy-healing/mcp-fallbacks.d.ts +12 -0
- package/dist/handlers/tools/core/dummy-healing/mcp-fallbacks.d.ts.map +1 -0
- package/dist/handlers/tools/core/dummy-healing.d.ts +1 -29
- package/dist/handlers/tools/core/dummy-healing.d.ts.map +1 -1
- package/dist/handlers/tools/core/index.d.ts +1 -1
- package/dist/handlers/tools/core/index.d.ts.map +1 -1
- package/dist/handlers/tools/core/schemas.d.ts +33 -9
- package/dist/handlers/tools/core/schemas.d.ts.map +1 -1
- package/dist/handlers/tools/core/staleness-tools.d.ts +17 -0
- package/dist/handlers/tools/core/staleness-tools.d.ts.map +1 -1
- package/dist/handlers/tools/core/tools.d.ts.map +1 -1
- package/dist/handlers/tools/git.d.ts +5 -11
- package/dist/handlers/tools/git.d.ts.map +1 -1
- package/dist/handlers/tools/github/close-issue-tool.d.ts +6 -0
- package/dist/handlers/tools/github/close-issue-tool.d.ts.map +1 -0
- package/dist/handlers/tools/github/create-issue-tool.d.ts +6 -0
- package/dist/handlers/tools/github/create-issue-tool.d.ts.map +1 -0
- package/dist/handlers/tools/github/issue-tools.d.ts +3 -0
- package/dist/handlers/tools/github/issue-tools.d.ts.map +1 -1
- package/dist/handlers/tools/github/kanban-tools.d.ts.map +1 -1
- package/dist/handlers/tools/github/milestone-tools.d.ts.map +1 -1
- package/dist/handlers/tools/index.d.ts +2 -2
- package/dist/handlers/tools/index.d.ts.map +1 -1
- package/dist/handlers/tools/io/schemas.d.ts +5 -2
- package/dist/handlers/tools/io/schemas.d.ts.map +1 -1
- package/dist/handlers/tools/relationships.d.ts.map +1 -1
- package/dist/handlers/tools/schemas/constants-and-primitives.d.ts +53 -0
- package/dist/handlers/tools/schemas/constants-and-primitives.d.ts.map +1 -0
- package/dist/handlers/tools/schemas/hallucination-healer.d.ts +8 -0
- package/dist/handlers/tools/schemas/hallucination-healer.d.ts.map +1 -0
- package/dist/handlers/tools/schemas/output-schemas.d.ts +247 -0
- package/dist/handlers/tools/schemas/output-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/schemas.d.ts +4 -306
- package/dist/handlers/tools/schemas.d.ts.map +1 -1
- package/dist/handlers/tools/search/cross-team.d.ts +14 -0
- package/dist/handlers/tools/search/cross-team.d.ts.map +1 -1
- package/dist/handlers/tools/search/helpers.d.ts.map +1 -1
- package/dist/handlers/tools/search/hybrid.d.ts.map +1 -1
- package/dist/handlers/tools/search/misc-tools.d.ts.map +1 -1
- package/dist/handlers/tools/search/schemas.d.ts +6 -3
- package/dist/handlers/tools/search/schemas.d.ts.map +1 -1
- package/dist/handlers/tools/search/search-entries.d.ts.map +1 -1
- package/dist/handlers/tools/search/semantic-search.d.ts.map +1 -1
- package/dist/handlers/tools/team/admin-tools/batch.d.ts.map +1 -1
- package/dist/handlers/tools/team/admin-tools/crud.d.ts.map +1 -1
- package/dist/handlers/tools/team/backup-tools.d.ts.map +1 -1
- package/dist/handlers/tools/team/core-tools.d.ts.map +1 -1
- package/dist/handlers/tools/team/helpers.d.ts.map +1 -1
- package/dist/handlers/tools/team/io-tools.d.ts.map +1 -1
- package/dist/handlers/tools/team/member-tools.d.ts.map +1 -1
- package/dist/handlers/tools/team/rbac.d.ts.map +1 -1
- package/dist/handlers/tools/team/relationship-tools.d.ts.map +1 -1
- package/dist/handlers/tools/team/schemas/admin-batch-schemas.d.ts +618 -0
- package/dist/handlers/tools/team/schemas/admin-batch-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/team/schemas/analytics-vector-schemas.d.ts +464 -0
- package/dist/handlers/tools/team/schemas/analytics-vector-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/team/schemas/core-search-schemas.d.ts +525 -0
- package/dist/handlers/tools/team/schemas/core-search-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/team/schemas/io-backup-schemas.d.ts +419 -0
- package/dist/handlers/tools/team/schemas/io-backup-schemas.d.ts.map +1 -0
- package/dist/handlers/tools/team/schemas.d.ts +5 -2002
- package/dist/handlers/tools/team/schemas.d.ts.map +1 -1
- package/dist/handlers/tools/team/search-tools.d.ts.map +1 -1
- package/dist/handlers/tools/team/vector-tools.d.ts.map +1 -1
- package/dist/index.js +6 -9
- package/dist/markdown/exporter.d.ts.map +1 -1
- package/dist/markdown/importer.d.ts.map +1 -1
- package/dist/observability/latency-tracker.d.ts.map +1 -1
- package/dist/observability/metrics-server.d.ts +1 -1
- package/dist/observability/metrics-server.d.ts.map +1 -1
- package/dist/observability/token-estimator.d.ts.map +1 -1
- package/dist/{security-utils-HNTROJAG.js → security-utils-P4P6A2DG.js} +1 -1
- package/dist/server/digest-reporter.d.ts +1 -0
- package/dist/server/digest-reporter.d.ts.map +1 -1
- package/dist/server/mcp-server/core.d.ts.map +1 -1
- package/dist/server/mcp-server/tool-registration/call-interceptor.d.ts +6 -0
- package/dist/server/mcp-server/tool-registration/call-interceptor.d.ts.map +1 -0
- package/dist/server/mcp-server/tool-registration/index.d.ts +4 -0
- package/dist/server/mcp-server/tool-registration/index.d.ts.map +1 -0
- package/dist/server/mcp-server/tool-registration/list-interceptor.d.ts +4 -0
- package/dist/server/mcp-server/tool-registration/list-interceptor.d.ts.map +1 -0
- package/dist/server/mcp-server/tool-registration/register-tools.d.ts +18 -0
- package/dist/server/mcp-server/tool-registration/register-tools.d.ts.map +1 -0
- package/dist/server/mcp-server/tool-registration.d.ts +1 -17
- package/dist/server/mcp-server/tool-registration.d.ts.map +1 -1
- package/dist/server/registration.d.ts +2 -0
- package/dist/server/registration.d.ts.map +1 -1
- package/dist/server/scheduler.d.ts +1 -0
- package/dist/server/scheduler.d.ts.map +1 -1
- package/dist/server/subscription-manager.d.ts +2 -0
- package/dist/server/subscription-manager.d.ts.map +1 -1
- package/dist/tools-BOQV7QD3.js +6 -0
- package/dist/transports/http/security.d.ts.map +1 -1
- package/dist/transports/http/server/http-transport.d.ts +25 -0
- package/dist/transports/http/server/http-transport.d.ts.map +1 -0
- package/dist/transports/http/server/index.d.ts +3 -30
- package/dist/transports/http/server/index.d.ts.map +1 -1
- package/dist/transports/http/server/legacy-sse.d.ts.map +1 -1
- package/dist/transports/http/server/protocol-handlers.d.ts +9 -0
- package/dist/transports/http/server/protocol-handlers.d.ts.map +1 -0
- package/dist/types/entities.d.ts +41 -1
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/types/index.d.ts +11 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/config-manager.d.ts +2 -0
- package/dist/utils/config-manager.d.ts.map +1 -1
- package/dist/utils/error-helpers.d.ts.map +1 -1
- package/dist/utils/issue-logger.d.ts.map +1 -1
- package/dist/utils/mcp-logger.d.ts +14 -2
- package/dist/utils/mcp-logger.d.ts.map +1 -1
- package/dist/utils/security-utils.d.ts.map +1 -1
- package/dist/utils/vector-index-helpers.d.ts +1 -1
- package/dist/utils/vector-index-helpers.d.ts.map +1 -1
- package/dist/utils/workspace-resolver.d.ts.map +1 -1
- package/dist/vector/vector-search-manager/manager.d.ts +2 -2
- package/dist/vector/vector-search-manager/manager.d.ts.map +1 -1
- package/dist/vector/vector-search-manager/rebuild.d.ts.map +1 -1
- package/dist/vendor/lib-agent-exec/interceptors/git-interceptor.d.ts.map +1 -1
- package/dist/vendor/lib-agent-exec/schema.d.ts +18 -18
- package/dist/vendor/lib-agent-exec/schema.d.ts.map +1 -1
- package/dist/vendor/lib-agent-exec/stream-manager.d.ts.map +1 -1
- package/dist/worker-script.js +2662 -2645
- package/package.json +3 -4
- package/skills/memory-journal-mcp/SKILL.md +8 -8
- package/dist/auth/context.d.ts +0 -7
- package/dist/auth/context.d.ts.map +0 -1
- package/dist/chunk-ELMJYRTT.js +0 -29
- package/dist/chunk-F6WSLZVN.js +0 -675
- package/dist/chunk-JVGY2GHK.js +0 -776
- package/dist/chunk-KWSBCSFM.js +0 -411
- package/dist/chunk-SP5D3XYY.js +0 -1550
- package/dist/chunk-XJMEPP36.js +0 -57
- package/dist/database/core/entry-columns.d.ts +0 -8
- package/dist/database/core/entry-columns.d.ts.map +0 -1
- package/dist/database/core/schema.d.ts +0 -69
- package/dist/database/core/schema.d.ts.map +0 -1
- package/dist/error-helpers-STR7IPUR.js +0 -2
- package/dist/git-runner-6OE7PVKA.js +0 -1
- package/dist/journal-api-UG43ZD7Y.js +0 -1289
- package/dist/tools-RBNFDWIE.js +0 -9
|
@@ -1,155 +1,24 @@
|
|
|
1
|
-
import { logger, DatabaseAdapterFactory, runAutoPrune, AuditLogger, createAuditInterceptor, getGitHubIntegration, parseToolFilter, getFilterSummary, getToolFilterFromEnv, getAllToolNames, DEFAULT_BRIEFING_CONFIG, getTools, getRequiredScope, getResources, getPrompts, callTool, requestContextStorage, MetricsAccumulator, formatPrometheusMetrics, SUPPORTED_SCOPES,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { runWithAuthContext } from './chunk-6RCZ4IYV.js';
|
|
1
|
+
import { DUMMY_GREP_SEARCH_TOOL_NAMES, DUMMY_FILE_VIEW_TOOL_NAMES, DUMMY_DIR_LIST_TOOL_NAMES, TOOL_GROUPS, logger, DatabaseAdapterFactory, runAutoPrune, AuditLogger, createAuditInterceptor, getGitHubIntegration, parseToolFilter, getFilterSummary, getToolFilterFromEnv, getAllToolNames, DEFAULT_BRIEFING_CONFIG, getTools, globalSubscriptionManager, getRequiredScope, getResources, getPrompts, callTool, sendProgress, requestContextStorage, MetricsAccumulator, formatPrometheusMetrics, SUPPORTED_SCOPES, logAgentIssue, validateResourceSubscription, isValidScope, getEnabledGroups, generateCodeModeInstructions, generateStandardInstructions, enforceAccessBoundary, auditOperation, parseScopes, BASE_SCOPES, getPrompt, handlePrompt, handleReadResource, handleListResources, handleListPrompts, handleGetPrompt, DUMMY_SERVER_INFO_TOOL_NAMES, handleServerInfo, handleListAdapters, handleGrepSearch, handleViewFile, handleListDir, DUMMY_RESOURCE_TOOL_NAMES, DUMMY_LIST_RESOURCE_TOOL_NAMES, DUMMY_LIST_PROMPT_TOOL_NAMES, DUMMY_LIST_TOOL_NAMES, DUMMY_GET_PROMPT_TOOL_NAMES, DUMMY_PROJECT_TOOL_NAMES, DUMMY_ADAPTER_TOOL_NAMES, readResource } from './chunk-T7SROBBV.js';
|
|
2
|
+
import { VERSION, applyArgInterceptions } from './chunk-SGJU2DHU.js';
|
|
3
|
+
import { runWithAuthContext, getAuthContext } from './chunk-4FZCHFB5.js';
|
|
5
4
|
import { MemoryJournalMcpError, ConfigurationError } from './chunk-E6IQP6PE.js';
|
|
6
5
|
import { StdioServerTransport } from '@modelcontextprotocol/server/stdio';
|
|
7
|
-
import { toNodeHandler, NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
|
|
8
|
-
import { createMcpHandler, McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
|
|
9
|
-
import { randomUUID, createHash, timingSafeEqual } from 'crypto';
|
|
10
6
|
import express from 'express';
|
|
7
|
+
import { createHash, timingSafeEqual, randomUUID } from 'crypto';
|
|
11
8
|
import 'https';
|
|
12
9
|
import * as jose from 'jose';
|
|
13
10
|
import { localhostHostValidation, hostHeaderValidation } from '@modelcontextprotocol/express';
|
|
11
|
+
import { toNodeHandler, NodeStreamableHTTPServerTransport } from '@modelcontextprotocol/node';
|
|
12
|
+
import { McpServer, createMcpHandler, ResourceTemplate } from '@modelcontextprotocol/server';
|
|
14
13
|
import * as fs from 'fs';
|
|
15
14
|
import * as path from 'path';
|
|
16
15
|
import { loadSkills } from '@modelcontextprotocol/server/ext/skills/node';
|
|
16
|
+
import * as dns from 'dns';
|
|
17
17
|
import { EventEmitter } from 'events';
|
|
18
18
|
import { parse, stringify } from 'yaml';
|
|
19
19
|
import { z } from 'zod';
|
|
20
20
|
import * as http from 'http';
|
|
21
21
|
|
|
22
|
-
// src/server/subscription-manager.ts
|
|
23
|
-
var SubscriptionManager = class {
|
|
24
|
-
// Map of Session ID (or 'stdio') -> McpServer instance
|
|
25
|
-
servers = /* @__PURE__ */ new Map();
|
|
26
|
-
// Map of URI string -> Set of Session IDs
|
|
27
|
-
subscriptions = /* @__PURE__ */ new Map();
|
|
28
|
-
/**
|
|
29
|
-
* Register a server instance for a session to allow sending notifications.
|
|
30
|
-
*/
|
|
31
|
-
registerServer(sessionId, server) {
|
|
32
|
-
this.servers.set(sessionId, server);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Unregister a server instance and clean up its subscriptions.
|
|
36
|
-
*/
|
|
37
|
-
unregisterServer(sessionId) {
|
|
38
|
-
this.servers.delete(sessionId);
|
|
39
|
-
this.unsubscribeSession(sessionId);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Subscribe a session to a resource URI.
|
|
43
|
-
*/
|
|
44
|
-
subscribe(uri, sessionId) {
|
|
45
|
-
if (!sessionId) {
|
|
46
|
-
sessionId = "stdio";
|
|
47
|
-
}
|
|
48
|
-
let sessions = this.subscriptions.get(uri);
|
|
49
|
-
if (!sessions) {
|
|
50
|
-
sessions = /* @__PURE__ */ new Set();
|
|
51
|
-
this.subscriptions.set(uri, sessions);
|
|
52
|
-
}
|
|
53
|
-
if (!sessions.has(sessionId)) {
|
|
54
|
-
sessions.add(sessionId);
|
|
55
|
-
logger.debug(`Session ${sessionId} subscribed to resource ${uri}`, {
|
|
56
|
-
module: "SubscriptionManager"
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Unsubscribe a session from a resource URI.
|
|
62
|
-
*/
|
|
63
|
-
unsubscribe(uri, sessionId) {
|
|
64
|
-
if (!sessionId) {
|
|
65
|
-
sessionId = "stdio";
|
|
66
|
-
}
|
|
67
|
-
const sessions = this.subscriptions.get(uri);
|
|
68
|
-
if (sessions?.has(sessionId)) {
|
|
69
|
-
sessions.delete(sessionId);
|
|
70
|
-
logger.debug(`Session ${sessionId} unsubscribed from resource ${uri}`, {
|
|
71
|
-
module: "SubscriptionManager"
|
|
72
|
-
});
|
|
73
|
-
if (sessions.size === 0) {
|
|
74
|
-
this.subscriptions.delete(uri);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Unsubscribe a session from all resources (useful on transport disconnect).
|
|
80
|
-
*/
|
|
81
|
-
unsubscribeSession(sessionId) {
|
|
82
|
-
let count = 0;
|
|
83
|
-
for (const [uri, sessions] of this.subscriptions.entries()) {
|
|
84
|
-
if (sessions.has(sessionId)) {
|
|
85
|
-
sessions.delete(sessionId);
|
|
86
|
-
count++;
|
|
87
|
-
if (sessions.size === 0) {
|
|
88
|
-
this.subscriptions.delete(uri);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (count > 0) {
|
|
93
|
-
logger.debug(
|
|
94
|
-
`Session ${sessionId} disconnected, removed ${count} subscriptions`,
|
|
95
|
-
{ module: "SubscriptionManager" }
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Notifies all subscribed clients that a resource has changed.
|
|
101
|
-
*/
|
|
102
|
-
async notifyResourceUpdated(uri) {
|
|
103
|
-
const sessions = this.subscriptions.get(uri);
|
|
104
|
-
if (!sessions || sessions.size === 0) {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const promises = [];
|
|
108
|
-
for (const sessionId of sessions) {
|
|
109
|
-
const server = this.servers.get(sessionId);
|
|
110
|
-
if (server) {
|
|
111
|
-
promises.push(
|
|
112
|
-
server.server.sendResourceUpdated({ uri }).catch((error) => {
|
|
113
|
-
logger.error(`Failed to notify session ${sessionId} for ${uri}`, {
|
|
114
|
-
module: "SubscriptionManager",
|
|
115
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
116
|
-
});
|
|
117
|
-
})
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
await Promise.allSettled(promises);
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Broadcasts a generic notification to all connected clients.
|
|
125
|
-
*/
|
|
126
|
-
async broadcastNotification(method) {
|
|
127
|
-
if (this.servers.size === 0) {
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
|
-
const promises = [];
|
|
131
|
-
for (const [sessionId, server] of this.servers.entries()) {
|
|
132
|
-
promises.push(
|
|
133
|
-
server.server.notification({ method }).catch((error) => {
|
|
134
|
-
logger.debug(`Failed to broadcast ${method} to session ${sessionId}`, {
|
|
135
|
-
module: "SubscriptionManager",
|
|
136
|
-
error: error instanceof Error ? error.message : String(error)
|
|
137
|
-
});
|
|
138
|
-
})
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
await Promise.allSettled(promises);
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Check if there are any active subscribers for a specific URI.
|
|
145
|
-
*/
|
|
146
|
-
hasSubscribers(uri) {
|
|
147
|
-
const sessions = this.subscriptions.get(uri);
|
|
148
|
-
return sessions ? sessions.size > 0 : false;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
var globalSubscriptionManager = new SubscriptionManager();
|
|
152
|
-
|
|
153
22
|
// src/transports/http/types.ts
|
|
154
23
|
var HTTP_REQUEST_TIMEOUT_MS = 12e4;
|
|
155
24
|
var HTTP_KEEP_ALIVE_TIMEOUT_MS = 65e3;
|
|
@@ -159,6 +28,8 @@ var DEFAULT_RATE_LIMIT_MAX_REQUESTS = 100;
|
|
|
159
28
|
var DEFAULT_MAX_BODY_BYTES = 1048576;
|
|
160
29
|
var DEFAULT_HSTS_MAX_AGE = 31536e3;
|
|
161
30
|
var CORS_PREFLIGHT_MAX_AGE_SECONDS = 86400;
|
|
31
|
+
var SESSION_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
32
|
+
var SESSION_SWEEP_INTERVAL_MS = 1 * 60 * 1e3;
|
|
162
33
|
function getClientIp(req) {
|
|
163
34
|
return req.ip ?? req.socket.remoteAddress ?? "unknown";
|
|
164
35
|
}
|
|
@@ -167,7 +38,7 @@ function checkRateLimit(req, config, rateLimitMap) {
|
|
|
167
38
|
return { allowed: true };
|
|
168
39
|
}
|
|
169
40
|
const authReq = req;
|
|
170
|
-
const subject = authReq.auth?.sub ?? authReq.auth?.subject;
|
|
41
|
+
const subject = authReq.tokenClaims?.sub ?? authReq.tokenClaims?.client_id ?? authReq.auth?.sub ?? authReq.auth?.subject ?? authReq.auth?.clientId;
|
|
171
42
|
const ip = getClientIp(req);
|
|
172
43
|
const rawIdentity = typeof subject === "string" && subject ? subject : ip;
|
|
173
44
|
const clientIdentity = createHash("sha256").update(rawIdentity).digest("hex");
|
|
@@ -584,6 +455,7 @@ var TokenValidator = class {
|
|
|
584
455
|
issuer: this.issuer,
|
|
585
456
|
audience: this.audience,
|
|
586
457
|
clockTolerance: this.clockTolerance,
|
|
458
|
+
algorithms: ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "EdDSA"],
|
|
587
459
|
// jose only checks `exp` when it is present. Require it so a validly signed token
|
|
588
460
|
// without an expiry cannot become a permanent credential.
|
|
589
461
|
requiredClaims: ["exp"]
|
|
@@ -806,23 +678,19 @@ function createTokenValidator(config) {
|
|
|
806
678
|
return new TokenValidator(config);
|
|
807
679
|
}
|
|
808
680
|
|
|
809
|
-
// src/auth/
|
|
681
|
+
// src/auth/transport-agnostic.ts
|
|
810
682
|
function extractBearerToken(authHeader) {
|
|
811
|
-
if (!authHeader)
|
|
812
|
-
return null;
|
|
813
|
-
}
|
|
683
|
+
if (!authHeader) return null;
|
|
814
684
|
const parts = authHeader.split(" ");
|
|
815
685
|
const scheme = parts[0];
|
|
816
686
|
const tokenPart = parts[1];
|
|
817
|
-
if (parts.length !== 2 || scheme?.toLowerCase() !== "bearer")
|
|
818
|
-
|
|
819
|
-
}
|
|
820
|
-
if (tokenPart === void 0) {
|
|
821
|
-
return null;
|
|
822
|
-
}
|
|
687
|
+
if (parts.length !== 2 || scheme?.toLowerCase() !== "bearer") return null;
|
|
688
|
+
if (tokenPart === void 0) return null;
|
|
823
689
|
const token = tokenPart.trim();
|
|
824
690
|
return token.length > 0 ? token : null;
|
|
825
691
|
}
|
|
692
|
+
|
|
693
|
+
// src/auth/middleware.ts
|
|
826
694
|
function isPublicPath(path2, publicPaths) {
|
|
827
695
|
if (path2 === "/.well-known/oauth-authorization-server" || path2 === "/.well-known/jwks.json") {
|
|
828
696
|
return true;
|
|
@@ -959,6 +827,10 @@ data: ${JSON.stringify(message)}
|
|
|
959
827
|
* Handles the initial GET request to establish the SSE stream.
|
|
960
828
|
*/
|
|
961
829
|
handleSseRequest(req, res) {
|
|
830
|
+
if (this.res !== null) {
|
|
831
|
+
res.status(409).json({ error: { message: "SSE stream already connected for this session" } });
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
962
834
|
this.res = res;
|
|
963
835
|
res.writeHead(200, {
|
|
964
836
|
"Content-Type": "text/event-stream",
|
|
@@ -994,15 +866,223 @@ data: ${endpointUrl}
|
|
|
994
866
|
}
|
|
995
867
|
};
|
|
996
868
|
|
|
997
|
-
// src/transports/http/server/
|
|
869
|
+
// src/transports/http/server/protocol-handlers.ts
|
|
870
|
+
var MAX_ACTIVE_SESSIONS = 100;
|
|
871
|
+
function resolveRequestSubject() {
|
|
872
|
+
const claims = getAuthContext()?.claims;
|
|
873
|
+
const sub = claims?.sub ?? claims?.email ?? claims?.client_id ?? claims?.subject ?? claims?.clientId;
|
|
874
|
+
return typeof sub === "string" && sub.length > 0 ? sub : void 0;
|
|
875
|
+
}
|
|
876
|
+
function registerMcpProtocolRoutes(app, config, serverFactory, activeServers, sseTransports) {
|
|
877
|
+
const statelessMcpHandler = toNodeHandler(
|
|
878
|
+
createMcpHandler(
|
|
879
|
+
() => {
|
|
880
|
+
const server = serverFactory();
|
|
881
|
+
activeServers.add(server);
|
|
882
|
+
const originalClose = server.close.bind(server);
|
|
883
|
+
server.close = async () => {
|
|
884
|
+
activeServers.delete(server);
|
|
885
|
+
return originalClose();
|
|
886
|
+
};
|
|
887
|
+
return server;
|
|
888
|
+
},
|
|
889
|
+
{ legacy: "reject" }
|
|
890
|
+
)
|
|
891
|
+
);
|
|
892
|
+
const sessionOwners = /* @__PURE__ */ new Map();
|
|
893
|
+
const sessionLastActivity = /* @__PURE__ */ new Map();
|
|
894
|
+
const sweepTimer = setInterval(() => {
|
|
895
|
+
const now = Date.now();
|
|
896
|
+
for (const [sid, lastActive] of sessionLastActivity.entries()) {
|
|
897
|
+
if (now - lastActive > SESSION_TIMEOUT_MS) {
|
|
898
|
+
const transport = sseTransports.get(sid);
|
|
899
|
+
if (transport !== void 0) {
|
|
900
|
+
void transport.close();
|
|
901
|
+
sseTransports.delete(sid);
|
|
902
|
+
}
|
|
903
|
+
sessionOwners.delete(sid);
|
|
904
|
+
sessionLastActivity.delete(sid);
|
|
905
|
+
globalSubscriptionManager.unregisterServer(sid);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}, SESSION_SWEEP_INTERVAL_MS);
|
|
909
|
+
sweepTimer.unref();
|
|
910
|
+
const verifySessionOwner = (res, sid) => {
|
|
911
|
+
const expectedOwner = sessionOwners.get(sid);
|
|
912
|
+
const callerSubject = resolveRequestSubject();
|
|
913
|
+
if (expectedOwner !== void 0 && expectedOwner !== callerSubject) {
|
|
914
|
+
res.status(403).json({ error: { message: "Forbidden: Session belongs to another principal" } });
|
|
915
|
+
return false;
|
|
916
|
+
}
|
|
917
|
+
sessionLastActivity.set(sid, Date.now());
|
|
918
|
+
return true;
|
|
919
|
+
};
|
|
920
|
+
const createSessionAndHandle = (req, res, next, _sid, isLegacySse = false) => {
|
|
921
|
+
if (sseTransports.size >= MAX_ACTIVE_SESSIONS) {
|
|
922
|
+
res.status(503).json({
|
|
923
|
+
error: { message: "Service Unavailable: Maximum active stateful sessions reached" }
|
|
924
|
+
});
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const sid = _sid ?? randomUUID();
|
|
928
|
+
const transport = isLegacySse ? new LegacySSEServerTransport("/messages", sid) : new NodeStreamableHTTPServerTransport({
|
|
929
|
+
sessionIdGenerator: () => sid
|
|
930
|
+
});
|
|
931
|
+
const sessionId = sid;
|
|
932
|
+
const server = serverFactory();
|
|
933
|
+
const callerSubject = resolveRequestSubject();
|
|
934
|
+
if (callerSubject !== void 0) {
|
|
935
|
+
sessionOwners.set(sessionId, callerSubject);
|
|
936
|
+
}
|
|
937
|
+
sessionLastActivity.set(sessionId, Date.now());
|
|
938
|
+
const authLog = callerSubject !== void 0 ? ` as ${callerSubject}` : "";
|
|
939
|
+
logger.info(`New MCP stateful session initialized${authLog}`, {
|
|
940
|
+
module: "HTTP",
|
|
941
|
+
sessionId,
|
|
942
|
+
...callerSubject !== void 0 ? { subject: callerSubject } : {}
|
|
943
|
+
});
|
|
944
|
+
activeServers.add(server);
|
|
945
|
+
globalSubscriptionManager.registerServer(sessionId, server);
|
|
946
|
+
const originalClose = server.close.bind(server);
|
|
947
|
+
server.close = async () => {
|
|
948
|
+
activeServers.delete(server);
|
|
949
|
+
sseTransports.delete(sessionId);
|
|
950
|
+
sessionOwners.delete(sessionId);
|
|
951
|
+
sessionLastActivity.delete(sessionId);
|
|
952
|
+
globalSubscriptionManager.unregisterServer(sessionId);
|
|
953
|
+
return originalClose();
|
|
954
|
+
};
|
|
955
|
+
sseTransports.set(sessionId, transport);
|
|
956
|
+
transport.onclose = () => {
|
|
957
|
+
server.close().catch(
|
|
958
|
+
(err) => logger.error("Error closing server on transport close", {
|
|
959
|
+
error: String(err)
|
|
960
|
+
})
|
|
961
|
+
);
|
|
962
|
+
};
|
|
963
|
+
server.connect(transport).catch(
|
|
964
|
+
(err) => logger.error("Error connecting server to transport", {
|
|
965
|
+
error: String(err)
|
|
966
|
+
})
|
|
967
|
+
);
|
|
968
|
+
if (transport instanceof LegacySSEServerTransport) {
|
|
969
|
+
transport.handleSseRequest(req, res);
|
|
970
|
+
} else {
|
|
971
|
+
transport.handleRequest(req, res, req.body).catch((err) => {
|
|
972
|
+
logger.error("Error handling transport request", {
|
|
973
|
+
error: String(err),
|
|
974
|
+
...err instanceof Error && err.stack !== void 0 ? { stack: err.stack } : {}
|
|
975
|
+
});
|
|
976
|
+
next(err);
|
|
977
|
+
});
|
|
978
|
+
}
|
|
979
|
+
};
|
|
980
|
+
const handleStatefulGet = (req, res, next) => {
|
|
981
|
+
if (config.stateless) {
|
|
982
|
+
if (req.path === "/sse" || req.path === "/sse/") {
|
|
983
|
+
next();
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
res.status(405).json({
|
|
987
|
+
error: { message: "SSE streaming not available in stateless mode" }
|
|
988
|
+
});
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const sid = req.query?.["sessionId"] ?? req.headers?.["mcp-session-id"];
|
|
992
|
+
if (sid !== void 0 && sid !== "") {
|
|
993
|
+
const transport = sseTransports.get(sid);
|
|
994
|
+
if (transport === void 0) {
|
|
995
|
+
res.status(400).json({ error: { message: "Session not found" } });
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
if (!verifySessionOwner(res, sid)) return;
|
|
999
|
+
if (transport instanceof LegacySSEServerTransport) {
|
|
1000
|
+
transport.handleSseRequest(req, res);
|
|
1001
|
+
} else {
|
|
1002
|
+
transport.handleRequest(req, res).catch((err) => {
|
|
1003
|
+
next(err);
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
} else {
|
|
1007
|
+
if (req.path === "/sse" || req.path === "/sse/") {
|
|
1008
|
+
createSessionAndHandle(req, res, next, randomUUID(), true);
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
res.status(405).json({
|
|
1012
|
+
error: "Method Not Allowed (Streamable HTTP requires POST initialization)"
|
|
1013
|
+
});
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
app.get("/mcp", handleStatefulGet);
|
|
1018
|
+
app.get("/sse", handleStatefulGet);
|
|
1019
|
+
const handlePost = (req, res, next) => {
|
|
1020
|
+
const sid = req.query?.["sessionId"] ?? req.headers?.["mcp-session-id"];
|
|
1021
|
+
if (sid !== void 0 && sid !== "") {
|
|
1022
|
+
const transport = sseTransports.get(sid);
|
|
1023
|
+
if (transport === void 0) {
|
|
1024
|
+
res.status(400).json({ error: { message: "Session not found" } });
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
if (!verifySessionOwner(res, sid)) return;
|
|
1028
|
+
if (transport instanceof LegacySSEServerTransport) {
|
|
1029
|
+
transport.handlePostMessage(req, res);
|
|
1030
|
+
} else {
|
|
1031
|
+
transport.handleRequest(req, res, req.body).catch((err) => {
|
|
1032
|
+
next(err);
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
} else {
|
|
1036
|
+
const acceptsSse = req.headers.accept?.includes("text/event-stream") ?? false;
|
|
1037
|
+
const isLegacyMessages = req.path === "/messages" || req.path === "/messages/";
|
|
1038
|
+
if (isLegacyMessages) {
|
|
1039
|
+
res.status(400).json({ error: { message: "Missing sessionId parameter" } });
|
|
1040
|
+
} else if (acceptsSse && !config.stateless) {
|
|
1041
|
+
createSessionAndHandle(req, res, next, randomUUID(), false);
|
|
1042
|
+
} else {
|
|
1043
|
+
logger.debug("Routing to statelessMcpHandler", {
|
|
1044
|
+
method: req.method,
|
|
1045
|
+
path: req.path
|
|
1046
|
+
});
|
|
1047
|
+
statelessMcpHandler(req, res, req.body).catch((err) => {
|
|
1048
|
+
next(err);
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
app.post("/mcp", handlePost);
|
|
1054
|
+
app.post("/sse", handlePost);
|
|
1055
|
+
app.post("/messages", handlePost);
|
|
1056
|
+
app.delete("/mcp", (req, res) => {
|
|
1057
|
+
if (config.stateless) {
|
|
1058
|
+
res.status(204).end();
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
const sid = req.query?.["sessionId"] ?? req.headers?.["mcp-session-id"];
|
|
1062
|
+
if (sid === void 0 || sid === "") {
|
|
1063
|
+
res.status(400).json({ error: { message: "Missing sessionId parameter" } });
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const transport = sseTransports.get(sid);
|
|
1067
|
+
if (transport !== void 0) {
|
|
1068
|
+
if (!verifySessionOwner(res, sid)) return;
|
|
1069
|
+
void transport.close();
|
|
1070
|
+
sseTransports.delete(sid);
|
|
1071
|
+
sessionOwners.delete(sid);
|
|
1072
|
+
sessionLastActivity.delete(sid);
|
|
1073
|
+
globalSubscriptionManager.unregisterServer(sid);
|
|
1074
|
+
}
|
|
1075
|
+
res.status(204).end();
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// src/transports/http/server/http-transport.ts
|
|
998
1080
|
var HttpTransport = class {
|
|
999
1081
|
app;
|
|
1000
1082
|
config;
|
|
1001
1083
|
activeServers = /* @__PURE__ */ new Set();
|
|
1002
1084
|
httpServer = null;
|
|
1003
|
-
// Map to keep track of stateful SSE transports
|
|
1004
1085
|
sseTransports = /* @__PURE__ */ new Map();
|
|
1005
|
-
// Rate limiting state
|
|
1006
1086
|
rateLimitMap = /* @__PURE__ */ new Map();
|
|
1007
1087
|
rateLimitCleanupTimer = null;
|
|
1008
1088
|
constructor(config) {
|
|
@@ -1011,13 +1091,10 @@ var HttpTransport = class {
|
|
|
1011
1091
|
enableRateLimit: config.enableRateLimit ?? true
|
|
1012
1092
|
};
|
|
1013
1093
|
this.app = express();
|
|
1014
|
-
if (this.config.trustProxy) {
|
|
1094
|
+
if (this.config.trustProxy === true) {
|
|
1015
1095
|
this.app.set("trust proxy", "loopback");
|
|
1016
1096
|
}
|
|
1017
1097
|
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Initialize and start the HTTP transport
|
|
1020
|
-
*/
|
|
1021
1098
|
async start(serverFactory, scheduler, runtime) {
|
|
1022
1099
|
const { port, host, authToken, corsOrigins } = this.config;
|
|
1023
1100
|
const isLocalhost = host === "localhost" || host === "127.0.0.1" || host === "::1";
|
|
@@ -1077,23 +1154,19 @@ var HttpTransport = class {
|
|
|
1077
1154
|
const maxBody = this.config.maxBodySize ?? DEFAULT_MAX_BODY_BYTES;
|
|
1078
1155
|
const jsonParser = express.json({ limit: maxBody });
|
|
1079
1156
|
this.app.use((req, res, next) => {
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
return;
|
|
1092
|
-
}
|
|
1093
|
-
next();
|
|
1094
|
-
} else {
|
|
1095
|
-
jsonParser(req, res, next);
|
|
1157
|
+
const contentLength = parseInt(req.headers["content-length"] ?? "0", 10);
|
|
1158
|
+
if (contentLength > maxBody) {
|
|
1159
|
+
res.status(413).json({
|
|
1160
|
+
jsonrpc: "2.0",
|
|
1161
|
+
error: {
|
|
1162
|
+
code: -32e3,
|
|
1163
|
+
message: "Payload Too Large: Maximum request body size exceeded"
|
|
1164
|
+
},
|
|
1165
|
+
id: null
|
|
1166
|
+
});
|
|
1167
|
+
return;
|
|
1096
1168
|
}
|
|
1169
|
+
jsonParser(req, res, next);
|
|
1097
1170
|
});
|
|
1098
1171
|
this.app.use((err, _req, res, next) => {
|
|
1099
1172
|
if (err !== null && typeof err === "object" && "type" in err) {
|
|
@@ -1150,18 +1223,13 @@ var HttpTransport = class {
|
|
|
1150
1223
|
}
|
|
1151
1224
|
}, 6e4);
|
|
1152
1225
|
this.rateLimitCleanupTimer.unref();
|
|
1153
|
-
logger.info("Rate limiting enabled (using IP)", {
|
|
1154
|
-
module: "HTTP"
|
|
1155
|
-
});
|
|
1226
|
+
logger.info("Rate limiting enabled (using IP)", { module: "HTTP" });
|
|
1156
1227
|
}
|
|
1157
|
-
if (this.config.oauthEnabled && this.config.oauthIssuer && this.config.oauthAudience) {
|
|
1228
|
+
if (this.config.oauthEnabled === true && this.config.oauthIssuer !== void 0 && this.config.oauthAudience !== void 0) {
|
|
1158
1229
|
if (this.config.oauthIssuer.startsWith("http://")) {
|
|
1159
|
-
if (
|
|
1230
|
+
if (this.config.allowPlaintextLoopbackOAuth !== true) {
|
|
1160
1231
|
const errorMsg = `FATAL: OAuth issuer targets a plaintext protocol. You MUST deliberately set 'allowPlaintextLoopbackOAuth: true' in config to bypass strict discovery bound.`;
|
|
1161
|
-
logger.error(errorMsg, {
|
|
1162
|
-
module: "HTTP",
|
|
1163
|
-
operation: "oauth-config"
|
|
1164
|
-
});
|
|
1232
|
+
logger.error(errorMsg, { module: "HTTP", operation: "oauth-config" });
|
|
1165
1233
|
throw new Error(errorMsg);
|
|
1166
1234
|
}
|
|
1167
1235
|
try {
|
|
@@ -1171,14 +1239,11 @@ var HttpTransport = class {
|
|
|
1171
1239
|
}
|
|
1172
1240
|
} catch {
|
|
1173
1241
|
const errorMsg = `FATAL: Plaintext OAuth bypass is ONLY permitted for loopback hosts (localhost, 127.0.0.1, [::1]). The configured issuer is not allowed.`;
|
|
1174
|
-
logger.error(errorMsg, {
|
|
1175
|
-
module: "HTTP",
|
|
1176
|
-
operation: "oauth-config"
|
|
1177
|
-
});
|
|
1242
|
+
logger.error(errorMsg, { module: "HTTP", operation: "oauth-config" });
|
|
1178
1243
|
throw new Error(errorMsg);
|
|
1179
1244
|
}
|
|
1180
1245
|
}
|
|
1181
|
-
if (!isLocalhost &&
|
|
1246
|
+
if (!isLocalhost && (this.config.publicOrigin === void 0 || this.config.publicOrigin === "")) {
|
|
1182
1247
|
const errorMsg = `FATAL: OAuth is enabled on a non-loopback interface ('${host}'), but no 'publicOrigin' was provided. You MUST specify an explicit public origin (e.g. 'https://api.example.com') for secure OAuth token audience binding.`;
|
|
1183
1248
|
logger.error(errorMsg, { module: "HTTP" });
|
|
1184
1249
|
throw new Error(errorMsg);
|
|
@@ -1210,14 +1275,29 @@ var HttpTransport = class {
|
|
|
1210
1275
|
resource: resourceServer.getResourceUri(),
|
|
1211
1276
|
audience: this.config.oauthAudience
|
|
1212
1277
|
});
|
|
1213
|
-
} else if (authToken) {
|
|
1278
|
+
} else if (authToken !== void 0 && authToken !== "") {
|
|
1214
1279
|
this.app.use(createAuthMiddleware(authToken));
|
|
1215
1280
|
}
|
|
1216
1281
|
const propagateContextMiddleware = (req, _res, next) => {
|
|
1217
1282
|
const reqRecord = req;
|
|
1218
|
-
const
|
|
1219
|
-
|
|
1220
|
-
|
|
1283
|
+
const tokenClaims = reqRecord["tokenClaims"];
|
|
1284
|
+
const authInfo = reqRecord["auth"];
|
|
1285
|
+
if (tokenClaims !== void 0 && tokenClaims !== null) {
|
|
1286
|
+
runWithAuthContext(
|
|
1287
|
+
{ authenticated: true, claims: tokenClaims, scopes: tokenClaims.scopes },
|
|
1288
|
+
next
|
|
1289
|
+
);
|
|
1290
|
+
} else if (authInfo !== void 0 && authInfo !== null) {
|
|
1291
|
+
const sub = authInfo.sub ?? (typeof authInfo["subject"] === "string" ? authInfo["subject"] : void 0) ?? authInfo.clientId ?? "unknown";
|
|
1292
|
+
const normalizedClaims = {
|
|
1293
|
+
...authInfo,
|
|
1294
|
+
sub,
|
|
1295
|
+
scopes: Array.isArray(authInfo.scopes) ? authInfo.scopes : []
|
|
1296
|
+
};
|
|
1297
|
+
runWithAuthContext(
|
|
1298
|
+
{ authenticated: true, claims: normalizedClaims, scopes: normalizedClaims.scopes },
|
|
1299
|
+
next
|
|
1300
|
+
);
|
|
1221
1301
|
} else {
|
|
1222
1302
|
next();
|
|
1223
1303
|
}
|
|
@@ -1229,153 +1309,22 @@ var HttpTransport = class {
|
|
|
1229
1309
|
logger.info("Prometheus metrics export enabled on /metrics", { module: "HTTP" });
|
|
1230
1310
|
}
|
|
1231
1311
|
this.app.get("/", handleRootInfo);
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
this.
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
};
|
|
1240
|
-
return server;
|
|
1241
|
-
}, { legacy: "reject" }));
|
|
1242
|
-
const createSessionAndHandle = (req, res, next, _sid, isLegacySse = false) => {
|
|
1243
|
-
const sid = _sid ?? randomUUID();
|
|
1244
|
-
const transport = isLegacySse ? new LegacySSEServerTransport("/messages", sid) : new NodeStreamableHTTPServerTransport({
|
|
1245
|
-
sessionIdGenerator: () => sid
|
|
1246
|
-
});
|
|
1247
|
-
const sessionId = sid;
|
|
1248
|
-
const server = serverFactory();
|
|
1249
|
-
const authObj = req.auth;
|
|
1250
|
-
const rawSubject = authObj ? authObj["subject"] ?? authObj["clientId"] ?? authObj["client_id"] ?? "unknown" : "";
|
|
1251
|
-
const authSubject = typeof rawSubject === "string" || typeof rawSubject === "number" ? String(rawSubject) : "unknown";
|
|
1252
|
-
const authLog = authObj ? ` as ${authSubject}` : "";
|
|
1253
|
-
logger.info(`New MCP stateful session initialized${authLog}`, {
|
|
1254
|
-
module: "HTTP",
|
|
1255
|
-
sessionId,
|
|
1256
|
-
...req.auth && { auth: req.auth }
|
|
1257
|
-
});
|
|
1258
|
-
this.activeServers.add(server);
|
|
1259
|
-
globalSubscriptionManager.registerServer(sessionId, server);
|
|
1260
|
-
const originalClose = server.close.bind(server);
|
|
1261
|
-
server.close = async () => {
|
|
1262
|
-
this.activeServers.delete(server);
|
|
1263
|
-
this.sseTransports.delete(sessionId);
|
|
1264
|
-
globalSubscriptionManager.unregisterServer(sessionId);
|
|
1265
|
-
return originalClose();
|
|
1266
|
-
};
|
|
1267
|
-
this.sseTransports.set(sessionId, transport);
|
|
1268
|
-
transport.onclose = () => {
|
|
1269
|
-
server.close().catch((err) => logger.error("Error closing server on transport close", { error: String(err) }));
|
|
1270
|
-
};
|
|
1271
|
-
server.connect(transport).catch((err) => logger.error("Error connecting server to transport", { error: String(err) }));
|
|
1272
|
-
if (transport instanceof LegacySSEServerTransport) {
|
|
1273
|
-
transport.handleSseRequest(req, res);
|
|
1274
|
-
} else {
|
|
1275
|
-
transport.handleRequest(req, res, req.body).catch((err) => {
|
|
1276
|
-
logger.error("Error handling transport request", {
|
|
1277
|
-
error: String(err),
|
|
1278
|
-
stack: err instanceof Error ? err.stack : void 0
|
|
1279
|
-
});
|
|
1280
|
-
next(err);
|
|
1281
|
-
});
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
1284
|
-
const handleStatefulGet = (req, res, next) => {
|
|
1285
|
-
if (this.config.stateless) {
|
|
1286
|
-
if (req.path === "/sse" || req.path === "/sse/") {
|
|
1287
|
-
next();
|
|
1288
|
-
return;
|
|
1289
|
-
}
|
|
1290
|
-
res.status(405).json({ error: { message: "SSE streaming not available in stateless mode" } });
|
|
1291
|
-
return;
|
|
1292
|
-
}
|
|
1293
|
-
const sid = req.query?.["sessionId"] ?? req.headers?.["mcp-session-id"];
|
|
1294
|
-
if (sid !== void 0 && sid !== "") {
|
|
1295
|
-
const transport = this.sseTransports.get(sid);
|
|
1296
|
-
if (!transport) {
|
|
1297
|
-
res.status(400).json({ error: { message: "Session not found" } });
|
|
1298
|
-
return;
|
|
1299
|
-
}
|
|
1300
|
-
if (transport instanceof LegacySSEServerTransport) {
|
|
1301
|
-
transport.handleSseRequest(req, res);
|
|
1302
|
-
} else {
|
|
1303
|
-
transport.handleRequest(req, res).catch((err) => {
|
|
1304
|
-
next(err);
|
|
1305
|
-
});
|
|
1306
|
-
}
|
|
1307
|
-
} else {
|
|
1308
|
-
if (this.config.stateless) {
|
|
1309
|
-
res.status(405).json({ error: "Method Not Allowed (Stateful SSE is disabled in stateless mode)" });
|
|
1310
|
-
return;
|
|
1311
|
-
}
|
|
1312
|
-
if (req.path === "/sse" || req.path === "/sse/") {
|
|
1313
|
-
createSessionAndHandle(req, res, next, randomUUID(), true);
|
|
1314
|
-
return;
|
|
1315
|
-
}
|
|
1316
|
-
res.status(405).json({ error: "Method Not Allowed (Streamable HTTP requires POST initialization)" });
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
};
|
|
1320
|
-
this.app.get("/mcp", handleStatefulGet);
|
|
1321
|
-
this.app.get("/sse", handleStatefulGet);
|
|
1322
|
-
const handlePost = (req, res, next) => {
|
|
1323
|
-
const sid = req.query?.["sessionId"] ?? req.headers?.["mcp-session-id"];
|
|
1324
|
-
if (sid !== void 0 && sid !== "") {
|
|
1325
|
-
const transport = this.sseTransports.get(sid);
|
|
1326
|
-
if (!transport) {
|
|
1327
|
-
res.status(400).json({ error: { message: "Session not found" } });
|
|
1328
|
-
return;
|
|
1329
|
-
}
|
|
1330
|
-
if (transport instanceof LegacySSEServerTransport) {
|
|
1331
|
-
transport.handlePostMessage(req, res);
|
|
1332
|
-
} else {
|
|
1333
|
-
transport.handleRequest(req, res, req.body).catch((err) => {
|
|
1334
|
-
next(err);
|
|
1335
|
-
});
|
|
1336
|
-
}
|
|
1337
|
-
} else {
|
|
1338
|
-
const acceptsSse = req.headers.accept?.includes("text/event-stream") ?? false;
|
|
1339
|
-
const isLegacyMessages = req.path === "/messages" || req.path === "/messages/";
|
|
1340
|
-
if (isLegacyMessages) {
|
|
1341
|
-
res.status(400).json({ error: { message: "Missing sessionId parameter" } });
|
|
1342
|
-
} else if (acceptsSse && !this.config.stateless) {
|
|
1343
|
-
createSessionAndHandle(req, res, next, randomUUID(), false);
|
|
1344
|
-
} else {
|
|
1345
|
-
logger.info("Routing to statelessMcpHandler", { method: req.method, path: req.path, headers: req.headers, body: req.body });
|
|
1346
|
-
statelessMcpHandler(req, res, req.body).catch((err) => {
|
|
1347
|
-
next(err);
|
|
1348
|
-
});
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
};
|
|
1352
|
-
this.app.post("/mcp", handlePost);
|
|
1353
|
-
this.app.post("/sse", handlePost);
|
|
1354
|
-
this.app.post("/messages", handlePost);
|
|
1355
|
-
this.app.delete("/mcp", (req, res) => {
|
|
1356
|
-
if (this.config.stateless) {
|
|
1357
|
-
res.status(204).end();
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
const sid = req.query?.["sessionId"] ?? req.headers?.["mcp-session-id"];
|
|
1361
|
-
if (!sid) {
|
|
1362
|
-
res.status(400).json({ error: { message: "Missing sessionId parameter" } });
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
|
-
const transport = this.sseTransports.get(sid);
|
|
1366
|
-
if (transport) {
|
|
1367
|
-
void transport.close();
|
|
1368
|
-
this.sseTransports.delete(sid);
|
|
1369
|
-
globalSubscriptionManager.unregisterServer(sid);
|
|
1370
|
-
}
|
|
1371
|
-
res.status(204).end();
|
|
1372
|
-
});
|
|
1312
|
+
registerMcpProtocolRoutes(
|
|
1313
|
+
this.app,
|
|
1314
|
+
this.config,
|
|
1315
|
+
serverFactory,
|
|
1316
|
+
this.activeServers,
|
|
1317
|
+
this.sseTransports
|
|
1318
|
+
);
|
|
1373
1319
|
this.app.use((req, res) => {
|
|
1374
|
-
logger.debug("404 Not Found", {
|
|
1320
|
+
logger.debug("404 Not Found", {
|
|
1321
|
+
method: req.method,
|
|
1322
|
+
path: req.path
|
|
1323
|
+
});
|
|
1375
1324
|
res.status(404).json({ error: "Not found" });
|
|
1376
1325
|
});
|
|
1377
1326
|
this.httpServer = this.app.listen(port, host, () => {
|
|
1378
|
-
if (this.httpServer) {
|
|
1327
|
+
if (this.httpServer !== null) {
|
|
1379
1328
|
this.httpServer.setTimeout(HTTP_REQUEST_TIMEOUT_MS);
|
|
1380
1329
|
this.httpServer.keepAliveTimeout = HTTP_KEEP_ALIVE_TIMEOUT_MS;
|
|
1381
1330
|
this.httpServer.headersTimeout = HTTP_HEADERS_TIMEOUT_MS;
|
|
@@ -1395,13 +1344,10 @@ var HttpTransport = class {
|
|
|
1395
1344
|
logger.info("HTTP server closed", { module: "HTTP" });
|
|
1396
1345
|
});
|
|
1397
1346
|
}
|
|
1398
|
-
/**
|
|
1399
|
-
* Graceful shutdown
|
|
1400
|
-
*/
|
|
1401
1347
|
async stop(scheduler) {
|
|
1402
1348
|
logger.info("Shutting down HTTP server...", { module: "HTTP" });
|
|
1403
1349
|
scheduler?.stop();
|
|
1404
|
-
if (this.rateLimitCleanupTimer) {
|
|
1350
|
+
if (this.rateLimitCleanupTimer !== null) {
|
|
1405
1351
|
clearInterval(this.rateLimitCleanupTimer);
|
|
1406
1352
|
this.rateLimitCleanupTimer = null;
|
|
1407
1353
|
}
|
|
@@ -1487,7 +1433,8 @@ async function rebuildIndex(manager, dbAdapter, progress, options) {
|
|
|
1487
1433
|
}
|
|
1488
1434
|
}
|
|
1489
1435
|
try {
|
|
1490
|
-
const
|
|
1436
|
+
const tagsText = entry.tags !== void 0 && entry.tags !== null && entry.tags.length > 0 ? ` [Tags: ${entry.tags.join(", ")}]` : "";
|
|
1437
|
+
const text = `[${entry.entryType}]${tagsText} ${entry.content}`;
|
|
1491
1438
|
const embedding = await manager.generateEmbedding(text);
|
|
1492
1439
|
vectorsToUpsert.push({ entryId: entry.id, embedding });
|
|
1493
1440
|
} catch (err) {
|
|
@@ -1539,7 +1486,7 @@ async function rebuildIndex(manager, dbAdapter, progress, options) {
|
|
|
1539
1486
|
}
|
|
1540
1487
|
|
|
1541
1488
|
// src/vector/vector-search-manager/manager.ts
|
|
1542
|
-
var VectorSearchManager = class {
|
|
1489
|
+
var VectorSearchManager = class _VectorSearchManager {
|
|
1543
1490
|
dbAdapter;
|
|
1544
1491
|
modelName;
|
|
1545
1492
|
dimensions = null;
|
|
@@ -1567,7 +1514,7 @@ var VectorSearchManager = class {
|
|
|
1567
1514
|
* Check if the vector search manager is initialized
|
|
1568
1515
|
*/
|
|
1569
1516
|
isInitialized() {
|
|
1570
|
-
return this.initializationPromise !== null && this.pipeline !== null;
|
|
1517
|
+
return this.initializationPromise !== null && (this.pipeline !== null || this.dimensions !== null);
|
|
1571
1518
|
}
|
|
1572
1519
|
/**
|
|
1573
1520
|
* Initialize the vector search manager.
|
|
@@ -1580,29 +1527,45 @@ var VectorSearchManager = class {
|
|
|
1580
1527
|
if (this.initializationPromise) {
|
|
1581
1528
|
return this.initializationPromise;
|
|
1582
1529
|
}
|
|
1583
|
-
this.initializationPromise = this._initialize()
|
|
1530
|
+
this.initializationPromise = this._initialize().catch((error) => {
|
|
1531
|
+
this.initializationPromise = null;
|
|
1532
|
+
throw error;
|
|
1533
|
+
});
|
|
1584
1534
|
return this.initializationPromise;
|
|
1585
1535
|
}
|
|
1586
1536
|
async _initialize() {
|
|
1587
1537
|
try {
|
|
1588
1538
|
logger.info("Initializing vector search...");
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1539
|
+
const hasCustomEmbeddingFn = this.generateEmbedding !== _VectorSearchManager.prototype.generateEmbedding;
|
|
1540
|
+
if (hasCustomEmbeddingFn) {
|
|
1541
|
+
logger.info("Detecting embedding dimensions via custom embedding generator...");
|
|
1542
|
+
const testEmbedding = await this.generateEmbedding("test");
|
|
1543
|
+
this.dimensions = testEmbedding.length;
|
|
1544
|
+
this.pipeline = true;
|
|
1545
|
+
logger.info(`Detected ${this.dimensions} dimensions for ${this.modelName}`);
|
|
1546
|
+
} else {
|
|
1547
|
+
if (this.transformers === null) {
|
|
1548
|
+
const transformersModule2 = await import('@huggingface/transformers');
|
|
1549
|
+
this.transformers = transformersModule2;
|
|
1550
|
+
transformersModule2.env.allowRemoteModels = true;
|
|
1551
|
+
transformersModule2.env.localModelPath = ".cache/huggingface";
|
|
1552
|
+
}
|
|
1553
|
+
logger.info(`Loading embedding model: ${this.modelName}`);
|
|
1554
|
+
const transformersModule = this.transformers;
|
|
1555
|
+
this.pipeline = await transformersModule.pipeline(
|
|
1556
|
+
"feature-extraction",
|
|
1557
|
+
this.modelName,
|
|
1558
|
+
{
|
|
1559
|
+
dtype: "fp32"
|
|
1560
|
+
}
|
|
1561
|
+
);
|
|
1562
|
+
logger.info("Embedding model loaded");
|
|
1563
|
+
logger.info("Detecting embedding dimensions...");
|
|
1564
|
+
const pipelineFunc = this.pipeline;
|
|
1565
|
+
const testResult = await pipelineFunc("test", { pooling: "mean", normalize: true });
|
|
1566
|
+
this.dimensions = testResult.data.length;
|
|
1567
|
+
logger.info(`Detected ${this.dimensions} dimensions for ${this.modelName}`);
|
|
1594
1568
|
}
|
|
1595
|
-
logger.info(`Loading embedding model: ${this.modelName}`);
|
|
1596
|
-
const transformersModule = this.transformers;
|
|
1597
|
-
this.pipeline = await transformersModule.pipeline("feature-extraction", this.modelName, {
|
|
1598
|
-
dtype: "fp32"
|
|
1599
|
-
});
|
|
1600
|
-
logger.info("Embedding model loaded");
|
|
1601
|
-
logger.info("Detecting embedding dimensions...");
|
|
1602
|
-
const pipelineFunc = this.pipeline;
|
|
1603
|
-
const testResult = await pipelineFunc("test", { pooling: "mean", normalize: true });
|
|
1604
|
-
this.dimensions = testResult.data.length;
|
|
1605
|
-
logger.info(`Detected ${this.dimensions} dimensions for ${this.modelName}`);
|
|
1606
1569
|
this.verifyAndCreateVecTable();
|
|
1607
1570
|
logger.info("Vector search initialized successfully");
|
|
1608
1571
|
} catch (error) {
|
|
@@ -1699,25 +1662,38 @@ var VectorSearchManager = class {
|
|
|
1699
1662
|
/**
|
|
1700
1663
|
* Search for similar entries by text query
|
|
1701
1664
|
*/
|
|
1702
|
-
async search(query, limit = 10, threshold = 0) {
|
|
1665
|
+
async search(query, limit = 10, threshold = 0, workspaceId) {
|
|
1703
1666
|
await this.initialize();
|
|
1704
1667
|
try {
|
|
1705
1668
|
const queryEmbedding = await this.generateEmbedding(query);
|
|
1706
1669
|
const results = this.dbAdapter.searchVectors(queryEmbedding, limit);
|
|
1707
|
-
const
|
|
1670
|
+
const candidates = [];
|
|
1708
1671
|
for (const result of results) {
|
|
1709
|
-
const
|
|
1710
|
-
if (
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1672
|
+
const similarityScore = Math.max(-1, 1 - result.distance * result.distance / 2);
|
|
1673
|
+
if (similarityScore >= threshold) {
|
|
1674
|
+
candidates.push({
|
|
1675
|
+
entryId: result.entry_id,
|
|
1676
|
+
distance: result.distance,
|
|
1677
|
+
similarityScore
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
if (candidates.length === 0) {
|
|
1682
|
+
return [];
|
|
1683
|
+
}
|
|
1684
|
+
const targetWs = workspaceId ?? "*";
|
|
1685
|
+
const entriesMap = typeof this.dbAdapter.getEntriesByIds === "function" ? this.dbAdapter.getEntriesByIds(candidates.map((c) => c.entryId)) : null;
|
|
1686
|
+
const searchResults = [];
|
|
1687
|
+
for (const candidate of candidates) {
|
|
1688
|
+
const entry = entriesMap ? entriesMap.get(candidate.entryId) ?? null : this.dbAdapter.getEntryById(candidate.entryId, targetWs);
|
|
1689
|
+
if (entry && (targetWs === "*" || entry.workspaceId === void 0 || entry.workspaceId === targetWs)) {
|
|
1690
|
+
searchResults.push({
|
|
1691
|
+
entryId: candidate.entryId,
|
|
1692
|
+
entry,
|
|
1693
|
+
distance: candidate.distance,
|
|
1694
|
+
score: candidate.similarityScore,
|
|
1695
|
+
similarityScore: candidate.similarityScore
|
|
1696
|
+
});
|
|
1721
1697
|
}
|
|
1722
1698
|
if (searchResults.length >= limit) break;
|
|
1723
1699
|
}
|
|
@@ -1733,36 +1709,50 @@ var VectorSearchManager = class {
|
|
|
1733
1709
|
/**
|
|
1734
1710
|
* Search for similar entries using an existing entry as the query
|
|
1735
1711
|
*/
|
|
1736
|
-
async searchByEntryId(entryId, limit = 10, threshold = 0) {
|
|
1712
|
+
async searchByEntryId(entryId, limit = 10, threshold = 0, workspaceId) {
|
|
1737
1713
|
await this.initialize();
|
|
1738
1714
|
try {
|
|
1715
|
+
const sourceEntry = this.dbAdapter.getEntryById(entryId, workspaceId ?? "*");
|
|
1716
|
+
if (!sourceEntry) {
|
|
1717
|
+
throw new Error(`Entry ${entryId} not found`);
|
|
1718
|
+
}
|
|
1739
1719
|
let queryEmbedding = this.dbAdapter.getVector(entryId);
|
|
1740
1720
|
if (!queryEmbedding) {
|
|
1741
|
-
const
|
|
1742
|
-
|
|
1743
|
-
throw new Error(`Entry ${entryId} not found`);
|
|
1744
|
-
}
|
|
1745
|
-
const tagsText = entry.tags !== void 0 && entry.tags !== null && entry.tags.length > 0 ? ` [Tags: ${entry.tags.join(", ")}]` : "";
|
|
1746
|
-
const text = `[${entry.entryType}]${tagsText} ${entry.content}`;
|
|
1721
|
+
const tagsText = sourceEntry.tags !== void 0 && sourceEntry.tags !== null && sourceEntry.tags.length > 0 ? ` [Tags: ${sourceEntry.tags.join(", ")}]` : "";
|
|
1722
|
+
const text = `[${sourceEntry.entryType}]${tagsText} ${sourceEntry.content}`;
|
|
1747
1723
|
queryEmbedding = await this.generateEmbedding(text);
|
|
1748
1724
|
this.dbAdapter.upsertVector(entryId, queryEmbedding);
|
|
1749
1725
|
}
|
|
1750
1726
|
const results = this.dbAdapter.searchVectors(queryEmbedding, limit + 1);
|
|
1751
|
-
const
|
|
1727
|
+
const candidates = [];
|
|
1752
1728
|
for (const result of results) {
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1729
|
+
const candidateId = result.entry_id;
|
|
1730
|
+
if (candidateId === entryId) continue;
|
|
1731
|
+
const similarityScore = Math.max(-1, 1 - result.distance * result.distance / 2);
|
|
1732
|
+
if (similarityScore >= threshold) {
|
|
1733
|
+
candidates.push({
|
|
1734
|
+
entryId: candidateId,
|
|
1735
|
+
distance: result.distance,
|
|
1736
|
+
similarityScore
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
if (candidates.length === 0) {
|
|
1741
|
+
return [];
|
|
1742
|
+
}
|
|
1743
|
+
const targetWs = workspaceId ?? "*";
|
|
1744
|
+
const entriesMap = typeof this.dbAdapter.getEntriesByIds === "function" ? this.dbAdapter.getEntriesByIds(candidates.map((c) => c.entryId)) : null;
|
|
1745
|
+
const searchResults = [];
|
|
1746
|
+
for (const candidate of candidates) {
|
|
1747
|
+
const entry = entriesMap ? entriesMap.get(candidate.entryId) ?? null : this.dbAdapter.getEntryById(candidate.entryId, targetWs);
|
|
1748
|
+
if (entry && (targetWs === "*" || entry.workspaceId === void 0 || entry.workspaceId === targetWs)) {
|
|
1749
|
+
searchResults.push({
|
|
1750
|
+
entryId: candidate.entryId,
|
|
1751
|
+
entry,
|
|
1752
|
+
distance: candidate.distance,
|
|
1753
|
+
score: candidate.similarityScore,
|
|
1754
|
+
similarityScore: candidate.similarityScore
|
|
1755
|
+
});
|
|
1766
1756
|
}
|
|
1767
1757
|
if (searchResults.length >= limit) break;
|
|
1768
1758
|
}
|
|
@@ -1815,8 +1805,44 @@ var VectorSearchManagerProxy = {
|
|
|
1815
1805
|
});
|
|
1816
1806
|
}
|
|
1817
1807
|
};
|
|
1818
|
-
|
|
1819
|
-
|
|
1808
|
+
function isBlockedWebhookIp(ip) {
|
|
1809
|
+
const normalized = ip.toLowerCase();
|
|
1810
|
+
if (normalized === "localhost" || normalized === "::1" || normalized === "0.0.0.0" || normalized === "::" || normalized.startsWith("127.") || normalized.startsWith("10.") || normalized.startsWith("192.168.") || normalized.startsWith("169.254.") || normalized.startsWith("fe80:") || normalized.startsWith("fc00:") || normalized.startsWith("fd00:") || normalized.startsWith("::ffff:")) {
|
|
1811
|
+
return true;
|
|
1812
|
+
}
|
|
1813
|
+
const octets = normalized.split(".");
|
|
1814
|
+
if (octets.length === 4 && octets[0] === "172") {
|
|
1815
|
+
const second = Number(octets[1]);
|
|
1816
|
+
if (second >= 16 && second <= 31) {
|
|
1817
|
+
return true;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
return false;
|
|
1821
|
+
}
|
|
1822
|
+
async function validateWebhookUrl(webhookUrl) {
|
|
1823
|
+
let parsed;
|
|
1824
|
+
try {
|
|
1825
|
+
parsed = new URL(webhookUrl);
|
|
1826
|
+
} catch {
|
|
1827
|
+
throw new Error("Invalid webhook URL format");
|
|
1828
|
+
}
|
|
1829
|
+
const allowInsecureHttp = process.env["ALLOW_INSECURE_WEBHOOK_HTTP"] === "true";
|
|
1830
|
+
if (parsed.protocol !== "https:" && !(allowInsecureHttp && parsed.protocol === "http:")) {
|
|
1831
|
+
throw new Error(`Disallowed webhook protocol: ${parsed.protocol} (https: required)`);
|
|
1832
|
+
}
|
|
1833
|
+
const hostname = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
|
|
1834
|
+
if (hostname === "localhost" || hostname.endsWith(".localhost") || hostname.endsWith(".local") || hostname.endsWith(".internal") || isBlockedWebhookIp(hostname)) {
|
|
1835
|
+
throw new Error(`Webhook hostname resolves to restricted network address: ${hostname}`);
|
|
1836
|
+
}
|
|
1837
|
+
if (!process.env["VITEST"]) {
|
|
1838
|
+
const records = await dns.promises.lookup(hostname, { all: true });
|
|
1839
|
+
for (const rec of records) {
|
|
1840
|
+
if (isBlockedWebhookIp(rec.address)) {
|
|
1841
|
+
throw new Error(`Webhook DNS resolved to private/loopback IP: ${rec.address}`);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1820
1846
|
function formatDigestReport(snapshot, serverVersion) {
|
|
1821
1847
|
const lines = [];
|
|
1822
1848
|
lines.push(`# Memory Journal Digest`);
|
|
@@ -1853,6 +1879,15 @@ function formatDigestReport(snapshot, serverVersion) {
|
|
|
1853
1879
|
}
|
|
1854
1880
|
async function deliverReport(report, snapshot, options) {
|
|
1855
1881
|
const { webhookUrl, webhookAuth, contentType = "slack" } = options;
|
|
1882
|
+
try {
|
|
1883
|
+
await validateWebhookUrl(webhookUrl);
|
|
1884
|
+
} catch (err) {
|
|
1885
|
+
logger.error("Refusing to deliver digest report to unsafe webhook URL", {
|
|
1886
|
+
module: "DigestReporter",
|
|
1887
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1888
|
+
});
|
|
1889
|
+
return false;
|
|
1890
|
+
}
|
|
1856
1891
|
let bodyPayload;
|
|
1857
1892
|
if (contentType === "discord") {
|
|
1858
1893
|
bodyPayload = JSON.stringify({ content: report });
|
|
@@ -1880,7 +1915,8 @@ async function deliverReport(report, snapshot, options) {
|
|
|
1880
1915
|
method: "POST",
|
|
1881
1916
|
headers,
|
|
1882
1917
|
body: bodyPayload,
|
|
1883
|
-
signal: controller.signal
|
|
1918
|
+
signal: controller.signal,
|
|
1919
|
+
redirect: "error"
|
|
1884
1920
|
});
|
|
1885
1921
|
clearTimeout(timeoutId);
|
|
1886
1922
|
if (response.ok) {
|
|
@@ -1930,6 +1966,7 @@ var Scheduler = class {
|
|
|
1930
1966
|
vectorManager;
|
|
1931
1967
|
timers = [];
|
|
1932
1968
|
started = false;
|
|
1969
|
+
sigtermHandler = null;
|
|
1933
1970
|
runtime;
|
|
1934
1971
|
constructor(options, db, vectorManager, runtime) {
|
|
1935
1972
|
this.options = options;
|
|
@@ -1949,7 +1986,10 @@ var Scheduler = class {
|
|
|
1949
1986
|
return;
|
|
1950
1987
|
}
|
|
1951
1988
|
this.started = true;
|
|
1952
|
-
|
|
1989
|
+
this.sigtermHandler = () => {
|
|
1990
|
+
this.stop();
|
|
1991
|
+
};
|
|
1992
|
+
process.on("SIGTERM", this.sigtermHandler);
|
|
1953
1993
|
const { backupIntervalMinutes, vacuumIntervalMinutes, rebuildIndexIntervalMinutes } = this.options;
|
|
1954
1994
|
if (backupIntervalMinutes > 0) {
|
|
1955
1995
|
this.scheduleJob("backup", backupIntervalMinutes, () => this.runBackup());
|
|
@@ -2011,6 +2051,10 @@ var Scheduler = class {
|
|
|
2011
2051
|
module: "Scheduler"
|
|
2012
2052
|
});
|
|
2013
2053
|
}
|
|
2054
|
+
if (this.sigtermHandler !== null) {
|
|
2055
|
+
process.off("SIGTERM", this.sigtermHandler);
|
|
2056
|
+
this.sigtermHandler = null;
|
|
2057
|
+
}
|
|
2014
2058
|
this.timers.length = 0;
|
|
2015
2059
|
this.started = false;
|
|
2016
2060
|
}
|
|
@@ -2322,11 +2366,14 @@ var ServerRuntime = class {
|
|
|
2322
2366
|
};
|
|
2323
2367
|
var ConfigManager = class extends EventEmitter {
|
|
2324
2368
|
config;
|
|
2369
|
+
initialCodemodeFullAccess;
|
|
2325
2370
|
configFilePath;
|
|
2326
2371
|
watcher;
|
|
2372
|
+
isWriting = false;
|
|
2327
2373
|
constructor(initialConfig, configFilePath) {
|
|
2328
2374
|
super();
|
|
2329
2375
|
this.config = { ...initialConfig };
|
|
2376
|
+
this.initialCodemodeFullAccess = Boolean(initialConfig.codemodeInternalFullAccess);
|
|
2330
2377
|
this.configFilePath = configFilePath;
|
|
2331
2378
|
if (this.configFilePath && fs.existsSync(this.configFilePath)) {
|
|
2332
2379
|
this.watchFile();
|
|
@@ -2350,6 +2397,7 @@ var ConfigManager = class extends EventEmitter {
|
|
|
2350
2397
|
}
|
|
2351
2398
|
persistToFile() {
|
|
2352
2399
|
if (!this.configFilePath) return;
|
|
2400
|
+
this.isWriting = true;
|
|
2353
2401
|
try {
|
|
2354
2402
|
const content = fs.readFileSync(this.configFilePath, "utf-8");
|
|
2355
2403
|
let parsed = {};
|
|
@@ -2383,15 +2431,16 @@ var ConfigManager = class extends EventEmitter {
|
|
|
2383
2431
|
}
|
|
2384
2432
|
} catch (e) {
|
|
2385
2433
|
logger.error("Failed to persist configuration to file", { module: "ConfigManager", error: e instanceof Error ? e.message : String(e) });
|
|
2434
|
+
} finally {
|
|
2435
|
+
this.isWriting = false;
|
|
2386
2436
|
}
|
|
2387
2437
|
}
|
|
2388
2438
|
watchFile() {
|
|
2389
2439
|
if (!this.configFilePath) return;
|
|
2390
2440
|
let debounceTimer;
|
|
2391
|
-
const isWriting = false;
|
|
2392
2441
|
try {
|
|
2393
2442
|
this.watcher = fs.watch(this.configFilePath, (eventType) => {
|
|
2394
|
-
if (eventType === "change" && !isWriting) {
|
|
2443
|
+
if (eventType === "change" && !this.isWriting) {
|
|
2395
2444
|
clearTimeout(debounceTimer);
|
|
2396
2445
|
debounceTimer = setTimeout(() => {
|
|
2397
2446
|
try {
|
|
@@ -2402,6 +2451,13 @@ var ConfigManager = class extends EventEmitter {
|
|
|
2402
2451
|
}, 500);
|
|
2403
2452
|
}
|
|
2404
2453
|
});
|
|
2454
|
+
this.watcher.on("error", (err) => {
|
|
2455
|
+
logger.warning("Config file watcher error", {
|
|
2456
|
+
module: "ConfigManager",
|
|
2457
|
+
path: this.configFilePath,
|
|
2458
|
+
error: err instanceof Error ? err.message : String(err)
|
|
2459
|
+
});
|
|
2460
|
+
});
|
|
2405
2461
|
logger.info("Watching configuration file for changes", { module: "ConfigManager", path: this.configFilePath });
|
|
2406
2462
|
} catch (error) {
|
|
2407
2463
|
logger.warning("Failed to start configuration file watcher", { module: "ConfigManager", error: error instanceof Error ? error.message : String(error) });
|
|
@@ -2422,7 +2478,10 @@ var ConfigManager = class extends EventEmitter {
|
|
|
2422
2478
|
updates.rawToolFilter = typeof parsed["toolFilter"] === "string" ? parsed["toolFilter"] : void 0;
|
|
2423
2479
|
}
|
|
2424
2480
|
if ("codemodeInternalFullAccess" in parsed) {
|
|
2425
|
-
|
|
2481
|
+
const requested = typeof parsed["codemodeInternalFullAccess"] === "boolean" ? parsed["codemodeInternalFullAccess"] : void 0;
|
|
2482
|
+
if (requested === false || requested === true && this.initialCodemodeFullAccess) {
|
|
2483
|
+
updates.codemodeInternalFullAccess = requested;
|
|
2484
|
+
}
|
|
2426
2485
|
}
|
|
2427
2486
|
const bc = { ...this.config.briefingConfig };
|
|
2428
2487
|
let bcChanged = false;
|
|
@@ -2843,34 +2902,457 @@ function registerPrompts(server, prompts, db, teamDb, runtime, github, briefingC
|
|
|
2843
2902
|
}
|
|
2844
2903
|
}
|
|
2845
2904
|
|
|
2846
|
-
// src/server/mcp-server/tool-registration.ts
|
|
2905
|
+
// src/server/mcp-server/tool-registration/list-interceptor.ts
|
|
2906
|
+
function attachListToolsInterceptor(requestHandlers, configManager, allToolNames) {
|
|
2907
|
+
const listToolsHandler = requestHandlers?.get("tools/list");
|
|
2908
|
+
if (listToolsHandler === void 0 || requestHandlers === void 0) {
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
requestHandlers.set("tools/list", async (request2, extra) => {
|
|
2912
|
+
const currentConfig = configManager.getConfig();
|
|
2913
|
+
const enabledSet = currentConfig.toolFilterConfig?.enabledTools ?? allToolNames;
|
|
2914
|
+
const result = await listToolsHandler(request2, extra);
|
|
2915
|
+
const res = result;
|
|
2916
|
+
if (Array.isArray(res?.tools)) {
|
|
2917
|
+
res.tools = res.tools.filter((t) => enabledSet.has(t.name));
|
|
2918
|
+
}
|
|
2919
|
+
return result;
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
// src/server/mcp-server/tool-registration/call-interceptor.ts
|
|
2924
|
+
var ALIASED_EXECUTE_CODE_NAMES = /* @__PURE__ */ new Set([
|
|
2925
|
+
"mysql_execute_code",
|
|
2926
|
+
"mysqlExecuteCode",
|
|
2927
|
+
"execute_code",
|
|
2928
|
+
"executeCode",
|
|
2929
|
+
"mjExecuteCode",
|
|
2930
|
+
"mj_eval",
|
|
2931
|
+
"eval_code",
|
|
2932
|
+
"run_code",
|
|
2933
|
+
"exec_code",
|
|
2934
|
+
"code_execute"
|
|
2935
|
+
]);
|
|
2936
|
+
var ALIASED_LIST_PROJECTS_NAMES = /* @__PURE__ */ new Set([
|
|
2937
|
+
"listProjects",
|
|
2938
|
+
"list-projects",
|
|
2939
|
+
"get_projects",
|
|
2940
|
+
"getProjects",
|
|
2941
|
+
"get-projects",
|
|
2942
|
+
"mj_list_projects",
|
|
2943
|
+
"mjListProjects",
|
|
2944
|
+
"mj_get_projects",
|
|
2945
|
+
"mjGetProjects",
|
|
2946
|
+
"mj_admin_list_projects",
|
|
2947
|
+
"mj_admin_get_projects"
|
|
2948
|
+
]);
|
|
2949
|
+
var ALIASED_GET_PROJECT_NAMES = /* @__PURE__ */ new Set([
|
|
2950
|
+
"getProject",
|
|
2951
|
+
"get-project",
|
|
2952
|
+
"mj_get_project",
|
|
2953
|
+
"mjGetProject",
|
|
2954
|
+
"mj_admin_get_project"
|
|
2955
|
+
]);
|
|
2956
|
+
var ALIASED_GREP_SEARCH_NAMES = new Set(DUMMY_GREP_SEARCH_TOOL_NAMES);
|
|
2957
|
+
var ALIASED_VIEW_FILE_NAMES = new Set(
|
|
2958
|
+
DUMMY_FILE_VIEW_TOOL_NAMES.filter((name) => name !== "cat")
|
|
2959
|
+
);
|
|
2960
|
+
var ALIASED_LIST_DIR_NAMES = new Set(
|
|
2961
|
+
DUMMY_DIR_LIST_TOOL_NAMES.filter((name) => name !== "ls" && name !== "dir")
|
|
2962
|
+
);
|
|
2963
|
+
var FORBIDDEN_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
2964
|
+
var ALL_KNOWN_GROUP_TOOLS = new Set(Object.values(TOOL_GROUPS).flat());
|
|
2965
|
+
var UPPERCASE_RE = /[A-Z]/;
|
|
2966
|
+
function deepCamelToSnake(obj, depth = 0) {
|
|
2967
|
+
if (depth > 12) return obj;
|
|
2968
|
+
if (Array.isArray(obj)) return obj.map((item) => deepCamelToSnake(item, depth + 1));
|
|
2969
|
+
if (obj !== null && typeof obj === "object") {
|
|
2970
|
+
const newObj = {};
|
|
2971
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
2972
|
+
if (FORBIDDEN_KEYS.has(k)) continue;
|
|
2973
|
+
const snakeKey = UPPERCASE_RE.test(k) ? k.replace(/^[A-Z]/, (letter) => letter.toLowerCase()).replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`) : k;
|
|
2974
|
+
if (FORBIDDEN_KEYS.has(snakeKey)) continue;
|
|
2975
|
+
newObj[snakeKey] = deepCamelToSnake(v, depth + 1);
|
|
2976
|
+
}
|
|
2977
|
+
return newObj;
|
|
2978
|
+
}
|
|
2979
|
+
return obj;
|
|
2980
|
+
}
|
|
2981
|
+
function formatSuccessPayload(payload) {
|
|
2982
|
+
const enriched = JSON.stringify(payload, null, 2);
|
|
2983
|
+
return {
|
|
2984
|
+
content: [{ type: "text", text: enriched }],
|
|
2985
|
+
structuredContent: payload
|
|
2986
|
+
};
|
|
2987
|
+
}
|
|
2988
|
+
function formatTokenEstimatedError(errorResult, isError) {
|
|
2989
|
+
const enriched = JSON.stringify({ ...errorResult, _meta: { tokenEstimate: 0 } });
|
|
2990
|
+
const tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
|
|
2991
|
+
const finalText = enriched.replace('"tokenEstimate":0', `"tokenEstimate":${tokenEstimate}`);
|
|
2992
|
+
return {
|
|
2993
|
+
content: [{ type: "text", text: finalText }],
|
|
2994
|
+
structuredContent: errorResult,
|
|
2995
|
+
...isError ? { isError: true } : {}
|
|
2996
|
+
};
|
|
2997
|
+
}
|
|
2998
|
+
function parseZodErrorsFromMessage(rawText, prefixToRemove) {
|
|
2999
|
+
let errorMessage = prefixToRemove !== void 0 ? rawText.replace(prefixToRemove, "") : rawText;
|
|
3000
|
+
try {
|
|
3001
|
+
const parts = rawText.split(": [");
|
|
3002
|
+
if (parts.length > 1) {
|
|
3003
|
+
const zodErrors = JSON.parse("[" + parts[1]);
|
|
3004
|
+
errorMessage = zodErrors.map((e) => `${e.path && e.path.length > 0 ? e.path.join(".") : "unknown"}: ${e.message ?? "invalid"}`).join("; ");
|
|
3005
|
+
}
|
|
3006
|
+
} catch {
|
|
3007
|
+
}
|
|
3008
|
+
return errorMessage;
|
|
3009
|
+
}
|
|
3010
|
+
function isSuccessObject(res) {
|
|
3011
|
+
return typeof res === "object" && res !== null && res["success"] === true;
|
|
3012
|
+
}
|
|
3013
|
+
function matchesSuffixes(name, list, suffixes) {
|
|
3014
|
+
return list.includes(name) || suffixes.some((s) => name.endsWith(s));
|
|
3015
|
+
}
|
|
3016
|
+
async function tryAutonomousUnknownToolFallback(requestedTool, toolArgs, matchingPrompt, context, allTools, allToolNames, customToolHandlerConfig) {
|
|
3017
|
+
const { configManager, db, teamDb, vectorManager, teamVectorManager, github, runtime } = context;
|
|
3018
|
+
const currentConfig = configManager.getConfig();
|
|
3019
|
+
const toolContext = {
|
|
3020
|
+
db,
|
|
3021
|
+
...teamDb !== void 0 ? { teamDb } : {},
|
|
3022
|
+
vectorManager,
|
|
3023
|
+
...teamVectorManager !== void 0 ? { teamVectorManager } : {},
|
|
3024
|
+
github,
|
|
3025
|
+
config: {
|
|
3026
|
+
...customToolHandlerConfig,
|
|
3027
|
+
filterConfig: currentConfig.toolFilterConfig,
|
|
3028
|
+
briefingConfig: currentConfig.briefingConfig,
|
|
3029
|
+
runtime
|
|
3030
|
+
}
|
|
3031
|
+
};
|
|
3032
|
+
if (matchingPrompt !== void 0) {
|
|
3033
|
+
enforceAccessBoundary(matchingPrompt.name, "prompt", void 0, runtime.auditLogger);
|
|
3034
|
+
const res = await handlePrompt(toolArgs, requestedTool, toolContext);
|
|
3035
|
+
if (isSuccessObject(res)) return formatSuccessPayload(res);
|
|
3036
|
+
}
|
|
3037
|
+
if (matchesSuffixes(requestedTool, DUMMY_RESOURCE_TOOL_NAMES, ["_read_resource", "ReadResource"]) || requestedTool === "mjReadResource" || requestedTool.startsWith("memory://")) {
|
|
3038
|
+
enforceAccessBoundary("read_resource", "tool", void 0, runtime.auditLogger);
|
|
3039
|
+
const res = await handleReadResource(toolArgs, requestedTool, toolContext);
|
|
3040
|
+
if (isSuccessObject(res)) return formatSuccessPayload(res);
|
|
3041
|
+
}
|
|
3042
|
+
if (matchesSuffixes(requestedTool, DUMMY_LIST_RESOURCE_TOOL_NAMES, ["_list_resources", "ListResources"])) {
|
|
3043
|
+
enforceAccessBoundary("list_resources", "tool", void 0, runtime.auditLogger);
|
|
3044
|
+
return formatSuccessPayload(handleListResources(requestedTool));
|
|
3045
|
+
}
|
|
3046
|
+
if (matchesSuffixes(requestedTool, DUMMY_LIST_PROMPT_TOOL_NAMES, ["_list_prompts", "ListPrompts"])) {
|
|
3047
|
+
enforceAccessBoundary("list_prompts", "tool", void 0, runtime.auditLogger);
|
|
3048
|
+
return formatSuccessPayload(handleListPrompts(requestedTool));
|
|
3049
|
+
}
|
|
3050
|
+
if (matchesSuffixes(requestedTool, DUMMY_LIST_TOOL_NAMES, ["_list_tools", "ListTools"])) {
|
|
3051
|
+
enforceAccessBoundary("list_tools", "tool", void 0, runtime.auditLogger);
|
|
3052
|
+
const enabledSet = currentConfig.toolFilterConfig?.enabledTools ?? allToolNames;
|
|
3053
|
+
const toolsList = allTools.filter((t) => enabledSet.has(t.name)).map((t) => ({ name: t.name, title: t.title, description: t.description, annotations: t.annotations }));
|
|
3054
|
+
return formatSuccessPayload({
|
|
3055
|
+
success: true,
|
|
3056
|
+
tools: toolsList,
|
|
3057
|
+
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool instead of an MCP protocol request (tools/list). The available tools were returned successfully.`
|
|
3058
|
+
});
|
|
3059
|
+
}
|
|
3060
|
+
if (matchesSuffixes(requestedTool, DUMMY_GET_PROMPT_TOOL_NAMES, ["_get_prompt", "GetPrompt"])) {
|
|
3061
|
+
enforceAccessBoundary("get_prompt", "tool", void 0, runtime.auditLogger);
|
|
3062
|
+
const res = await handleGetPrompt(toolArgs, requestedTool, toolContext);
|
|
3063
|
+
if (isSuccessObject(res)) return formatSuccessPayload(res);
|
|
3064
|
+
}
|
|
3065
|
+
if (DUMMY_SERVER_INFO_TOOL_NAMES.includes(requestedTool)) {
|
|
3066
|
+
enforceAccessBoundary("server_info", "tool", void 0, runtime.auditLogger);
|
|
3067
|
+
return formatSuccessPayload(await handleServerInfo(toolArgs, requestedTool, toolContext));
|
|
3068
|
+
}
|
|
3069
|
+
if (matchesSuffixes(requestedTool, DUMMY_PROJECT_TOOL_NAMES, ["list_projects", "listProjects", "ListProjects"]) || requestedTool === "projects") {
|
|
3070
|
+
enforceAccessBoundary("list_projects", "tool", void 0, runtime.auditLogger);
|
|
3071
|
+
const cfg = currentConfig;
|
|
3072
|
+
return formatSuccessPayload({
|
|
3073
|
+
success: true,
|
|
3074
|
+
projects: cfg.projectRegistry ?? {},
|
|
3075
|
+
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool. Returned registered projects from projectRegistry successfully.`
|
|
3076
|
+
});
|
|
3077
|
+
}
|
|
3078
|
+
if (matchesSuffixes(requestedTool, DUMMY_ADAPTER_TOOL_NAMES, ["list_adapters", "listAdapters", "ListAdapters"])) {
|
|
3079
|
+
enforceAccessBoundary("list_adapters", "tool", void 0, runtime.auditLogger);
|
|
3080
|
+
return formatSuccessPayload(handleListAdapters(requestedTool, toolContext));
|
|
3081
|
+
}
|
|
3082
|
+
if (matchesSuffixes(requestedTool, DUMMY_GREP_SEARCH_TOOL_NAMES, ["grep_search", "grepSearch"])) {
|
|
3083
|
+
enforceAccessBoundary("grep_search", "tool", void 0, runtime.auditLogger);
|
|
3084
|
+
return formatSuccessPayload(await handleGrepSearch(toolArgs, requestedTool, toolContext));
|
|
3085
|
+
}
|
|
3086
|
+
if (DUMMY_FILE_VIEW_TOOL_NAMES.includes(requestedTool)) {
|
|
3087
|
+
enforceAccessBoundary("view_file", "tool", void 0, runtime.auditLogger);
|
|
3088
|
+
return formatSuccessPayload(await handleViewFile(toolArgs, requestedTool, toolContext));
|
|
3089
|
+
}
|
|
3090
|
+
if (DUMMY_DIR_LIST_TOOL_NAMES.includes(requestedTool)) {
|
|
3091
|
+
enforceAccessBoundary("list_dir", "tool", void 0, runtime.auditLogger);
|
|
3092
|
+
return formatSuccessPayload(await handleListDir(toolArgs, requestedTool, toolContext));
|
|
3093
|
+
}
|
|
3094
|
+
return void 0;
|
|
3095
|
+
}
|
|
3096
|
+
function normalizeAndAliasToolCall(reqParams, options) {
|
|
3097
|
+
reqParams.arguments = deepCamelToSnake(reqParams.arguments);
|
|
3098
|
+
const toolArgs = reqParams.arguments;
|
|
3099
|
+
if (toolArgs["project_number"] === void 0) {
|
|
3100
|
+
const repoStr = typeof toolArgs["repo"] === "string" ? toolArgs["repo"] : typeof toolArgs["project"] === "string" ? toolArgs["project"] : void 0;
|
|
3101
|
+
if (repoStr !== void 0 && repoStr !== "") toolArgs["project_number"] = repoStr;
|
|
3102
|
+
}
|
|
3103
|
+
if (typeof toolArgs["project_number"] === "string") {
|
|
3104
|
+
const strVal = toolArgs["project_number"];
|
|
3105
|
+
const parsed = Number(strVal);
|
|
3106
|
+
if (!Number.isNaN(parsed)) {
|
|
3107
|
+
toolArgs["project_number"] = parsed;
|
|
3108
|
+
} else if (options.projectRegistry !== void 0) {
|
|
3109
|
+
let entry = options.projectRegistry[strVal];
|
|
3110
|
+
if (entry === void 0 && strVal.includes("/")) {
|
|
3111
|
+
const repoName = strVal.split("/").pop();
|
|
3112
|
+
if (repoName !== void 0 && repoName !== "") entry = options.projectRegistry[repoName];
|
|
3113
|
+
}
|
|
3114
|
+
if (entry?.project_number !== void 0) toolArgs["project_number"] = entry.project_number;
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
const currentName = reqParams.name;
|
|
3118
|
+
if (currentName !== void 0 && currentName !== "") {
|
|
3119
|
+
if (ALIASED_EXECUTE_CODE_NAMES.has(currentName)) {
|
|
3120
|
+
reqParams.name = "mj_execute_code";
|
|
3121
|
+
} else if (currentName === "create_entry_minimal") {
|
|
3122
|
+
reqParams.name = "create_entry";
|
|
3123
|
+
if (toolArgs["entry_type"] === void 0) toolArgs["entry_type"] = "personal_reflection";
|
|
3124
|
+
} else if (ALIASED_LIST_PROJECTS_NAMES.has(currentName)) {
|
|
3125
|
+
reqParams.name = "list_projects";
|
|
3126
|
+
} else if (ALIASED_GET_PROJECT_NAMES.has(currentName)) {
|
|
3127
|
+
reqParams.name = "get_project";
|
|
3128
|
+
} else if (ALIASED_GREP_SEARCH_NAMES.has(currentName)) {
|
|
3129
|
+
reqParams.name = "grep_search";
|
|
3130
|
+
} else if (ALIASED_VIEW_FILE_NAMES.has(currentName)) {
|
|
3131
|
+
reqParams.name = "view_file";
|
|
3132
|
+
} else if (ALIASED_LIST_DIR_NAMES.has(currentName)) {
|
|
3133
|
+
reqParams.name = "list_dir";
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
function attachCallToolInterceptor(requestHandlers, context, options, prompts, allTools, allToolNames, customToolHandlerConfig) {
|
|
3138
|
+
const originalHandler = requestHandlers?.get("tools/call");
|
|
3139
|
+
if (originalHandler === void 0 || requestHandlers === void 0) return;
|
|
3140
|
+
requestHandlers.set("tools/call", async (request2, extra) => {
|
|
3141
|
+
try {
|
|
3142
|
+
const req = request2;
|
|
3143
|
+
const healingEnabled = options.agentHealing !== false && customToolHandlerConfig.agentHealing !== false;
|
|
3144
|
+
if (req.params?.arguments !== void 0 && healingEnabled) {
|
|
3145
|
+
normalizeAndAliasToolCall(req.params, options);
|
|
3146
|
+
const toolArgs = req.params.arguments;
|
|
3147
|
+
if (req.params.name !== void 0 && req.params.name !== "") {
|
|
3148
|
+
const intercept = applyArgInterceptions(req.params.name, [toolArgs]);
|
|
3149
|
+
if ("error" in intercept) {
|
|
3150
|
+
const errorResult = intercept.error;
|
|
3151
|
+
logAgentIssue({
|
|
3152
|
+
source: "MCP",
|
|
3153
|
+
action: req.params.name,
|
|
3154
|
+
input: toolArgs,
|
|
3155
|
+
error: errorResult["error"] ?? "Validation Error"
|
|
3156
|
+
});
|
|
3157
|
+
return formatTokenEstimatedError(errorResult, false);
|
|
3158
|
+
}
|
|
3159
|
+
const normalizedArgs = intercept.args?.[0];
|
|
3160
|
+
if (normalizedArgs !== null && normalizedArgs !== void 0 && typeof normalizedArgs === "object" && !Array.isArray(normalizedArgs)) {
|
|
3161
|
+
req.params.arguments = normalizedArgs;
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
if (req.params?.name !== void 0 && req.params.name !== "" && !allToolNames.has(req.params.name)) {
|
|
3166
|
+
const requestedTool = req.params.name;
|
|
3167
|
+
const toolArgs = req.params.arguments ?? {};
|
|
3168
|
+
const normalized = requestedTool.replace(/^mj_/, "").replace(/_/g, "-");
|
|
3169
|
+
const matchingPrompt = prompts.find(
|
|
3170
|
+
(p) => p.name === normalized || p.name === requestedTool || p.name === requestedTool.replace(/^mj_/, "")
|
|
3171
|
+
);
|
|
3172
|
+
if (healingEnabled) {
|
|
3173
|
+
const fallbackResponse = await tryAutonomousUnknownToolFallback(
|
|
3174
|
+
requestedTool,
|
|
3175
|
+
toolArgs,
|
|
3176
|
+
matchingPrompt,
|
|
3177
|
+
context,
|
|
3178
|
+
allTools,
|
|
3179
|
+
allToolNames,
|
|
3180
|
+
customToolHandlerConfig
|
|
3181
|
+
);
|
|
3182
|
+
if (fallbackResponse !== void 0) return fallbackResponse;
|
|
3183
|
+
}
|
|
3184
|
+
let suggestion = `Tool '${requestedTool}' does not exist. Use tools/list to see available tools.`;
|
|
3185
|
+
const isFilteredTool = ALL_KNOWN_GROUP_TOOLS.has(requestedTool);
|
|
3186
|
+
if (isFilteredTool) {
|
|
3187
|
+
suggestion = `'${requestedTool}' is a valid tool but it is not currently exposed because of your active --tool-filter or codemode restrictions. If you are in Code Mode, invoke it via 'mj_execute_code' (e.g. 'await mj.core.${requestedTool}(...)').`;
|
|
3188
|
+
} else if (matchingPrompt !== void 0) {
|
|
3189
|
+
suggestion = `'${requestedTool}' is an MCP Prompt, not an MCP Tool. Please use the 'get_prompt' tool (or your native MCP Prompts integration) to evaluate it, then use 'create_entry' to save the result.`;
|
|
3190
|
+
} else if (requestedTool.startsWith("mj_")) {
|
|
3191
|
+
suggestion = `'${requestedTool}' is not a valid root tool. If you are trying to use Code Mode, you must invoke the 'mj_execute_code' tool and write javascript (e.g. 'await mj.core.searchEntries(...)').`;
|
|
3192
|
+
}
|
|
3193
|
+
const errorResult = {
|
|
3194
|
+
success: false,
|
|
3195
|
+
error: `MCP error -32602: Tool ${requestedTool} not found`,
|
|
3196
|
+
code: "TOOL_NOT_FOUND",
|
|
3197
|
+
category: "hallucination",
|
|
3198
|
+
suggestion,
|
|
3199
|
+
recoverable: false
|
|
3200
|
+
};
|
|
3201
|
+
if (healingEnabled) {
|
|
3202
|
+
logAgentIssue({
|
|
3203
|
+
source: "MCP",
|
|
3204
|
+
action: requestedTool,
|
|
3205
|
+
input: toolArgs,
|
|
3206
|
+
error: errorResult.error
|
|
3207
|
+
});
|
|
3208
|
+
}
|
|
3209
|
+
return formatTokenEstimatedError(errorResult, true);
|
|
3210
|
+
}
|
|
3211
|
+
const result = await originalHandler(request2, extra);
|
|
3212
|
+
if (result !== null && result !== void 0 && typeof result === "object" && "isError" in result && result.isError === true) {
|
|
3213
|
+
const resObj = result;
|
|
3214
|
+
if (Array.isArray(resObj.content) && resObj.content.length > 0) {
|
|
3215
|
+
const text = resObj.content[0]?.text;
|
|
3216
|
+
if (typeof text === "string" && text.includes("MCP error -32602")) {
|
|
3217
|
+
const errorMessage = parseZodErrorsFromMessage(
|
|
3218
|
+
text,
|
|
3219
|
+
"MCP error -32602: Input validation error: "
|
|
3220
|
+
);
|
|
3221
|
+
const errorResult = {
|
|
3222
|
+
success: false,
|
|
3223
|
+
error: errorMessage,
|
|
3224
|
+
code: "VALIDATION_ERROR",
|
|
3225
|
+
category: "validation",
|
|
3226
|
+
suggestion: "Check input parameters against the tool schema",
|
|
3227
|
+
recoverable: false
|
|
3228
|
+
};
|
|
3229
|
+
logAgentIssue({
|
|
3230
|
+
source: "MCP",
|
|
3231
|
+
action: req.params?.name ?? "unknown",
|
|
3232
|
+
input: req.params?.arguments ?? {},
|
|
3233
|
+
error: errorMessage
|
|
3234
|
+
});
|
|
3235
|
+
return formatTokenEstimatedError(errorResult, false);
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
return result;
|
|
3240
|
+
} catch (error) {
|
|
3241
|
+
const err = error;
|
|
3242
|
+
if (err.code === -32602) {
|
|
3243
|
+
let errorMessage = err.message ?? "Validation Error";
|
|
3244
|
+
if (typeof errorMessage === "string" && errorMessage.includes("Invalid arguments for tool")) {
|
|
3245
|
+
errorMessage = parseZodErrorsFromMessage(errorMessage);
|
|
3246
|
+
}
|
|
3247
|
+
const errorResult = {
|
|
3248
|
+
success: false,
|
|
3249
|
+
error: errorMessage,
|
|
3250
|
+
code: "VALIDATION_ERROR",
|
|
3251
|
+
category: "validation",
|
|
3252
|
+
suggestion: "Check input parameters against the tool schema",
|
|
3253
|
+
recoverable: false
|
|
3254
|
+
};
|
|
3255
|
+
const fallbackReq = request2;
|
|
3256
|
+
logAgentIssue({
|
|
3257
|
+
source: "MCP",
|
|
3258
|
+
action: fallbackReq.params?.name ?? "unknown",
|
|
3259
|
+
input: fallbackReq.params?.arguments ?? {},
|
|
3260
|
+
error: errorMessage
|
|
3261
|
+
});
|
|
3262
|
+
return formatTokenEstimatedError(errorResult, true);
|
|
3263
|
+
}
|
|
3264
|
+
throw error;
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
}
|
|
3268
|
+
|
|
3269
|
+
// src/server/mcp-server/tool-registration/register-tools.ts
|
|
2847
3270
|
var DEFAULT_MAX_TOOL_INPUT_ELEMENTS = 25e3;
|
|
2848
3271
|
function resolveEffectiveMaxToolInputElements(configured) {
|
|
2849
|
-
if (configured === void 0)
|
|
2850
|
-
|
|
3272
|
+
if (configured === void 0) return DEFAULT_MAX_TOOL_INPUT_ELEMENTS;
|
|
3273
|
+
return configured === 0 || configured === Infinity ? Infinity : configured;
|
|
3274
|
+
}
|
|
3275
|
+
var MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
3276
|
+
"create_entry",
|
|
3277
|
+
"update_entry",
|
|
3278
|
+
"delete_entry",
|
|
3279
|
+
"restore_entry",
|
|
3280
|
+
"batch_create_entries",
|
|
3281
|
+
"batch_update_entries",
|
|
3282
|
+
"batch_delete_entries",
|
|
3283
|
+
"team_add_member",
|
|
3284
|
+
"team_update_member_role",
|
|
3285
|
+
"team_remove_member"
|
|
3286
|
+
]);
|
|
3287
|
+
var toolRegistrationOptionsCache = /* @__PURE__ */ new WeakMap();
|
|
3288
|
+
function buildToolRegistrationOptions(tool) {
|
|
3289
|
+
const cached = toolRegistrationOptionsCache.get(tool);
|
|
3290
|
+
if (cached !== void 0) return cached;
|
|
3291
|
+
const toolOptions = { description: tool.description };
|
|
3292
|
+
if (tool.title !== void 0 && tool.title !== "") toolOptions.title = tool.title;
|
|
3293
|
+
if (tool.inputSchema !== void 0) {
|
|
3294
|
+
const schema = tool.inputSchema;
|
|
3295
|
+
const fallbackInputSchema = schema;
|
|
3296
|
+
if (typeof schema === "object" && schema !== null && "partial" in schema && typeof schema.partial === "function") {
|
|
3297
|
+
try {
|
|
3298
|
+
const relaxed = schema.partial();
|
|
3299
|
+
toolOptions.inputSchema = typeof relaxed.passthrough === "function" ? relaxed.passthrough() : fallbackInputSchema;
|
|
3300
|
+
} catch {
|
|
3301
|
+
toolOptions.inputSchema = fallbackInputSchema;
|
|
3302
|
+
}
|
|
3303
|
+
} else {
|
|
3304
|
+
toolOptions.inputSchema = fallbackInputSchema;
|
|
3305
|
+
}
|
|
2851
3306
|
}
|
|
2852
|
-
if (
|
|
2853
|
-
|
|
3307
|
+
if (tool.outputSchema !== void 0) {
|
|
3308
|
+
const outSchema = tool.outputSchema;
|
|
3309
|
+
const fallbackOutputSchema = outSchema;
|
|
3310
|
+
if (typeof outSchema === "object" && outSchema !== null && "passthrough" in outSchema && typeof outSchema.passthrough === "function") {
|
|
3311
|
+
try {
|
|
3312
|
+
toolOptions.outputSchema = outSchema.passthrough();
|
|
3313
|
+
} catch {
|
|
3314
|
+
toolOptions.outputSchema = fallbackOutputSchema;
|
|
3315
|
+
}
|
|
3316
|
+
} else {
|
|
3317
|
+
toolOptions.outputSchema = fallbackOutputSchema;
|
|
3318
|
+
}
|
|
2854
3319
|
}
|
|
2855
|
-
|
|
3320
|
+
if (tool.annotations !== void 0) toolOptions.annotations = tool.annotations;
|
|
3321
|
+
if (tool.icons !== void 0) toolOptions.icons = [{ src: tool.icons.iconUrl }];
|
|
3322
|
+
toolRegistrationOptionsCache.set(tool, toolOptions);
|
|
3323
|
+
return toolOptions;
|
|
2856
3324
|
}
|
|
2857
3325
|
function createServerInstance(context, options, prompts, resources, allTools, allToolNames, customToolHandlerConfig) {
|
|
2858
|
-
const {
|
|
3326
|
+
const {
|
|
3327
|
+
configManager,
|
|
3328
|
+
db,
|
|
3329
|
+
teamDb,
|
|
3330
|
+
vectorManager,
|
|
3331
|
+
teamVectorManager,
|
|
3332
|
+
github,
|
|
3333
|
+
scheduler,
|
|
3334
|
+
allowedIoRoots,
|
|
3335
|
+
runtime
|
|
3336
|
+
} = context;
|
|
2859
3337
|
const { defaultProjectNumber } = options;
|
|
2860
3338
|
const getInstructions = () => {
|
|
2861
3339
|
const currentConfig = configManager.getConfig();
|
|
2862
3340
|
const enabledSet = currentConfig.toolFilterConfig?.enabledTools ?? allToolNames;
|
|
2863
3341
|
const groups = currentConfig.toolFilterConfig ? getEnabledGroups(currentConfig.toolFilterConfig.enabledTools) : void 0;
|
|
2864
3342
|
const isCodeModeOnly = enabledSet.size === 1 && enabledSet.has("mj_execute_code");
|
|
3343
|
+
const mappedPrompts = prompts.map((p) => {
|
|
3344
|
+
const prompt = p;
|
|
3345
|
+
return {
|
|
3346
|
+
name: prompt.name,
|
|
3347
|
+
...prompt.description !== void 0 ? { description: prompt.description } : {}
|
|
3348
|
+
};
|
|
3349
|
+
});
|
|
2865
3350
|
if (isCodeModeOnly || currentConfig.toolFilterConfig?.raw === "codemode") {
|
|
2866
3351
|
const finalGroups = currentConfig.codemodeInternalFullAccess === true ? new Set(Object.keys(TOOL_GROUPS)) : groups;
|
|
2867
3352
|
const finalEnabledTools = currentConfig.codemodeInternalFullAccess === true ? allToolNames : enabledSet;
|
|
2868
3353
|
return generateCodeModeInstructions(
|
|
2869
3354
|
finalEnabledTools,
|
|
2870
|
-
|
|
2871
|
-
const prompt = p;
|
|
2872
|
-
return { name: prompt.name, description: prompt.description };
|
|
2873
|
-
}),
|
|
3355
|
+
mappedPrompts,
|
|
2874
3356
|
void 0,
|
|
2875
3357
|
finalGroups,
|
|
2876
3358
|
currentConfig.briefingConfig
|
|
@@ -2878,85 +3360,35 @@ function createServerInstance(context, options, prompts, resources, allTools, al
|
|
|
2878
3360
|
}
|
|
2879
3361
|
return generateStandardInstructions(
|
|
2880
3362
|
enabledSet,
|
|
2881
|
-
|
|
2882
|
-
const prompt = p;
|
|
2883
|
-
return { name: prompt.name, description: prompt.description };
|
|
2884
|
-
}),
|
|
3363
|
+
mappedPrompts,
|
|
2885
3364
|
void 0,
|
|
2886
3365
|
groups,
|
|
2887
3366
|
currentConfig.briefingConfig
|
|
2888
3367
|
);
|
|
2889
3368
|
};
|
|
2890
3369
|
const server = new McpServer(
|
|
2891
|
-
{
|
|
2892
|
-
name: "memory-journal-mcp",
|
|
2893
|
-
version: VERSION
|
|
2894
|
-
},
|
|
3370
|
+
{ name: "memory-journal-mcp", version: VERSION },
|
|
2895
3371
|
{
|
|
2896
3372
|
capabilities: {
|
|
2897
3373
|
logging: {},
|
|
2898
3374
|
prompts: { listChanged: true },
|
|
2899
3375
|
tools: { listChanged: true },
|
|
2900
|
-
resources: {
|
|
2901
|
-
|
|
2902
|
-
listChanged: true
|
|
2903
|
-
},
|
|
2904
|
-
experimental: {
|
|
2905
|
-
progress: {}
|
|
2906
|
-
}
|
|
3376
|
+
resources: { subscribe: true, listChanged: true },
|
|
3377
|
+
experimental: { progress: {} }
|
|
2907
3378
|
},
|
|
2908
3379
|
instructions: getInstructions(),
|
|
2909
3380
|
maxToolInputElements: resolveEffectiveMaxToolInputElements(options.maxToolInputElements)
|
|
2910
3381
|
}
|
|
2911
3382
|
);
|
|
2912
|
-
if (context.skillCatalog && typeof server.isConnected === "function") {
|
|
3383
|
+
if (context.skillCatalog !== void 0 && typeof server.isConnected === "function") {
|
|
2913
3384
|
context.skillCatalog.attach(server);
|
|
2914
3385
|
}
|
|
2915
3386
|
for (const tool of allTools) {
|
|
2916
|
-
const toolOptions =
|
|
2917
|
-
description: tool.description
|
|
2918
|
-
};
|
|
2919
|
-
if (tool.title) {
|
|
2920
|
-
toolOptions.title = tool.title;
|
|
2921
|
-
}
|
|
2922
|
-
if (tool.inputSchema !== void 0) {
|
|
2923
|
-
const schema = tool.inputSchema;
|
|
2924
|
-
if (typeof schema === "object" && schema !== null && "partial" in schema && typeof schema.partial === "function") {
|
|
2925
|
-
try {
|
|
2926
|
-
const relaxed = schema.partial();
|
|
2927
|
-
toolOptions.inputSchema = typeof relaxed.passthrough === "function" ? relaxed.passthrough() : schema;
|
|
2928
|
-
} catch {
|
|
2929
|
-
toolOptions.inputSchema = schema;
|
|
2930
|
-
}
|
|
2931
|
-
} else {
|
|
2932
|
-
toolOptions.inputSchema = schema;
|
|
2933
|
-
}
|
|
2934
|
-
}
|
|
2935
|
-
if (tool.outputSchema !== void 0) {
|
|
2936
|
-
const outSchema = tool.outputSchema;
|
|
2937
|
-
if (typeof outSchema === "object" && outSchema !== null && "passthrough" in outSchema && typeof outSchema.passthrough === "function") {
|
|
2938
|
-
try {
|
|
2939
|
-
toolOptions.outputSchema = outSchema.passthrough();
|
|
2940
|
-
} catch {
|
|
2941
|
-
toolOptions.outputSchema = outSchema;
|
|
2942
|
-
}
|
|
2943
|
-
} else {
|
|
2944
|
-
toolOptions.outputSchema = outSchema;
|
|
2945
|
-
}
|
|
2946
|
-
}
|
|
2947
|
-
if (tool.annotations !== void 0) {
|
|
2948
|
-
toolOptions.annotations = tool.annotations;
|
|
2949
|
-
}
|
|
2950
|
-
if (tool.icons) {
|
|
2951
|
-
toolOptions.icons = [{
|
|
2952
|
-
src: tool.icons.iconUrl
|
|
2953
|
-
}];
|
|
2954
|
-
}
|
|
3387
|
+
const toolOptions = buildToolRegistrationOptions(tool);
|
|
2955
3388
|
const hasOutputSchema = Boolean(tool.outputSchema);
|
|
2956
3389
|
server.registerTool(
|
|
2957
3390
|
tool.name,
|
|
2958
3391
|
toolOptions,
|
|
2959
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2960
3392
|
async (args, extra) => {
|
|
2961
3393
|
try {
|
|
2962
3394
|
const extraMeta = extra;
|
|
@@ -2965,7 +3397,7 @@ function createServerInstance(context, options, prompts, resources, allTools, al
|
|
|
2965
3397
|
const currentConfig = configManager.getConfig();
|
|
2966
3398
|
customToolHandlerConfig.filterConfig = currentConfig.toolFilterConfig;
|
|
2967
3399
|
customToolHandlerConfig.briefingConfig = currentConfig.briefingConfig;
|
|
2968
|
-
const toolArgs = args;
|
|
3400
|
+
const toolArgs = typeof args === "object" && args !== null ? args : {};
|
|
2969
3401
|
const result = await callTool(
|
|
2970
3402
|
tool.name,
|
|
2971
3403
|
toolArgs,
|
|
@@ -2979,49 +3411,31 @@ function createServerInstance(context, options, prompts, resources, allTools, al
|
|
|
2979
3411
|
);
|
|
2980
3412
|
const isSuccess = typeof result === "object" && result !== null && result["success"] === true;
|
|
2981
3413
|
if (isSuccess) {
|
|
2982
|
-
const
|
|
2983
|
-
"create_entry",
|
|
2984
|
-
"update_entry",
|
|
2985
|
-
"delete_entry",
|
|
2986
|
-
"restore_entry",
|
|
2987
|
-
"batch_create_entries",
|
|
2988
|
-
"batch_update_entries",
|
|
2989
|
-
"batch_delete_entries",
|
|
2990
|
-
"add_team_member",
|
|
2991
|
-
"update_team_member",
|
|
2992
|
-
"remove_team_member"
|
|
2993
|
-
];
|
|
2994
|
-
const isMutation = mutatingTools.includes(tool.name) || tool.name.startsWith("team_") && tool.name !== "team_search" && tool.name !== "get_team_entry_by_id";
|
|
3414
|
+
const isMutation = MUTATING_TOOLS.has(tool.name) || tool.name.startsWith("team_") && tool.annotations.readOnlyHint !== true;
|
|
2995
3415
|
if (isMutation) {
|
|
2996
3416
|
void globalSubscriptionManager.notifyResourceUpdated("memory://briefing");
|
|
2997
3417
|
void globalSubscriptionManager.notifyResourceUpdated("memory://recent");
|
|
2998
|
-
void globalSubscriptionManager.notifyResourceUpdated(
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3418
|
+
void globalSubscriptionManager.notifyResourceUpdated(
|
|
3419
|
+
"memory://statistics"
|
|
3420
|
+
);
|
|
3421
|
+
if (tool.name.startsWith("team_")) {
|
|
3422
|
+
void globalSubscriptionManager.notifyResourceUpdated(
|
|
3423
|
+
"memory://team/recent"
|
|
3424
|
+
);
|
|
3425
|
+
void globalSubscriptionManager.notifyResourceUpdated(
|
|
3426
|
+
"memory://team/statistics"
|
|
3427
|
+
);
|
|
3002
3428
|
}
|
|
3003
3429
|
}
|
|
3004
3430
|
}
|
|
3005
3431
|
const textContent = typeof result === "string" ? result : JSON.stringify(result, null, 2);
|
|
3006
3432
|
if (hasOutputSchema) {
|
|
3007
3433
|
return {
|
|
3008
|
-
content: [
|
|
3009
|
-
{
|
|
3010
|
-
type: "text",
|
|
3011
|
-
text: textContent
|
|
3012
|
-
}
|
|
3013
|
-
],
|
|
3434
|
+
content: [{ type: "text", text: textContent }],
|
|
3014
3435
|
structuredContent: result
|
|
3015
3436
|
};
|
|
3016
3437
|
}
|
|
3017
|
-
return {
|
|
3018
|
-
content: [
|
|
3019
|
-
{
|
|
3020
|
-
type: "text",
|
|
3021
|
-
text: textContent
|
|
3022
|
-
}
|
|
3023
|
-
]
|
|
3024
|
-
};
|
|
3438
|
+
return { content: [{ type: "text", text: textContent }] };
|
|
3025
3439
|
} catch (error) {
|
|
3026
3440
|
let errorResult;
|
|
3027
3441
|
if (error instanceof Error && "toResponse" in error && typeof error.toResponse === "function") {
|
|
@@ -3058,12 +3472,11 @@ function createServerInstance(context, options, prompts, resources, allTools, al
|
|
|
3058
3472
|
}
|
|
3059
3473
|
const handleResourceRead = async (uri, mimeType) => {
|
|
3060
3474
|
const currentConfig = configManager.getConfig();
|
|
3475
|
+
const resolvedDefaultProj = currentConfig.briefingConfig.defaultProjectNumber ?? defaultProjectNumber;
|
|
3061
3476
|
const activeBriefingConfig = {
|
|
3062
3477
|
...currentConfig.briefingConfig,
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
defaultProjectNumber: currentConfig.briefingConfig.defaultProjectNumber ?? defaultProjectNumber,
|
|
3066
|
-
projectRegistry: options.projectRegistry,
|
|
3478
|
+
...resolvedDefaultProj !== void 0 ? { defaultProjectNumber: resolvedDefaultProj } : {},
|
|
3479
|
+
...options.projectRegistry !== void 0 ? { projectRegistry: options.projectRegistry } : {},
|
|
3067
3480
|
allowedIoRoots
|
|
3068
3481
|
};
|
|
3069
3482
|
try {
|
|
@@ -3086,7 +3499,7 @@ function createServerInstance(context, options, prompts, resources, allTools, al
|
|
|
3086
3499
|
uri: uri.href,
|
|
3087
3500
|
mimeType,
|
|
3088
3501
|
text: dataStr,
|
|
3089
|
-
...result.annotations ? { annotations: result.annotations } : {}
|
|
3502
|
+
...result.annotations !== void 0 ? { annotations: result.annotations } : {}
|
|
3090
3503
|
}
|
|
3091
3504
|
]
|
|
3092
3505
|
};
|
|
@@ -3097,845 +3510,46 @@ function createServerInstance(context, options, prompts, resources, allTools, al
|
|
|
3097
3510
|
};
|
|
3098
3511
|
registerResources(server, resources, handleResourceRead, runtime);
|
|
3099
3512
|
server.server.setRequestHandler("resources/subscribe", (request2, extra) => {
|
|
3100
|
-
const
|
|
3513
|
+
const sessionId = extra?.sessionId;
|
|
3514
|
+
const transportSessionId = sessionId !== void 0 && sessionId !== "" ? sessionId : "stdio";
|
|
3101
3515
|
validateResourceSubscription(request2.params.uri, runtime);
|
|
3102
3516
|
globalSubscriptionManager.registerServer(transportSessionId, server);
|
|
3103
3517
|
globalSubscriptionManager.subscribe(request2.params.uri, transportSessionId);
|
|
3104
3518
|
return {};
|
|
3105
3519
|
});
|
|
3106
3520
|
server.server.setRequestHandler("resources/unsubscribe", (request2, extra) => {
|
|
3107
|
-
const
|
|
3521
|
+
const sessionId = extra?.sessionId;
|
|
3522
|
+
const transportSessionId = sessionId !== void 0 && sessionId !== "" ? sessionId : "stdio";
|
|
3108
3523
|
globalSubscriptionManager.unsubscribe(request2.params.uri, transportSessionId);
|
|
3109
3524
|
return {};
|
|
3110
3525
|
});
|
|
3111
3526
|
const briefingConfig = configManager.getConfig().briefingConfig;
|
|
3112
|
-
registerPrompts(
|
|
3527
|
+
registerPrompts(
|
|
3528
|
+
server,
|
|
3529
|
+
prompts,
|
|
3530
|
+
db,
|
|
3531
|
+
teamDb ?? void 0,
|
|
3532
|
+
runtime,
|
|
3533
|
+
context.github,
|
|
3534
|
+
briefingConfig
|
|
3535
|
+
);
|
|
3113
3536
|
const serverObj = server.server;
|
|
3114
3537
|
const requestHandlers = serverObj._requestHandlers;
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
requestHandlers
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
return result;
|
|
3126
|
-
});
|
|
3127
|
-
}
|
|
3128
|
-
const originalHandler = requestHandlers?.get("tools/call");
|
|
3129
|
-
if (originalHandler) {
|
|
3130
|
-
requestHandlers.set("tools/call", async (request2, extra) => {
|
|
3131
|
-
try {
|
|
3132
|
-
const req = request2;
|
|
3133
|
-
const healingEnabled = options.agentHealing !== false && customToolHandlerConfig.agentHealing !== false;
|
|
3134
|
-
if (req?.params?.arguments && healingEnabled) {
|
|
3135
|
-
const deepCamelToSnake = (obj) => {
|
|
3136
|
-
if (Array.isArray(obj)) return obj.map(deepCamelToSnake);
|
|
3137
|
-
if (obj !== null && typeof obj === "object") {
|
|
3138
|
-
const newObj = {};
|
|
3139
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
3140
|
-
const snakeKey = k.replace(/^[A-Z]/, (letter) => letter.toLowerCase()).replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
3141
|
-
newObj[snakeKey] = deepCamelToSnake(v);
|
|
3142
|
-
}
|
|
3143
|
-
return newObj;
|
|
3144
|
-
}
|
|
3145
|
-
return obj;
|
|
3146
|
-
};
|
|
3147
|
-
req.params.arguments = deepCamelToSnake(req.params.arguments);
|
|
3148
|
-
const toolArgs = req.params.arguments;
|
|
3149
|
-
if (typeof toolArgs["project_number"] === "undefined") {
|
|
3150
|
-
let repoStr;
|
|
3151
|
-
if (typeof toolArgs["repo"] === "string") repoStr = toolArgs["repo"];
|
|
3152
|
-
else if (typeof toolArgs["project"] === "string") repoStr = toolArgs["project"];
|
|
3153
|
-
if (repoStr) {
|
|
3154
|
-
toolArgs["project_number"] = repoStr;
|
|
3155
|
-
}
|
|
3156
|
-
}
|
|
3157
|
-
if (typeof toolArgs["project_number"] === "string") {
|
|
3158
|
-
const strVal = toolArgs["project_number"];
|
|
3159
|
-
const parsed = Number(strVal);
|
|
3160
|
-
if (!Number.isNaN(parsed)) {
|
|
3161
|
-
toolArgs["project_number"] = parsed;
|
|
3162
|
-
} else if (options.projectRegistry) {
|
|
3163
|
-
let entry = options.projectRegistry[strVal];
|
|
3164
|
-
if (!entry && strVal.includes("/")) {
|
|
3165
|
-
const repoName = strVal.split("/").pop();
|
|
3166
|
-
if (repoName) {
|
|
3167
|
-
entry = options.projectRegistry[repoName];
|
|
3168
|
-
}
|
|
3169
|
-
}
|
|
3170
|
-
if (entry?.project_number !== void 0) {
|
|
3171
|
-
toolArgs["project_number"] = entry.project_number;
|
|
3172
|
-
}
|
|
3173
|
-
}
|
|
3174
|
-
}
|
|
3175
|
-
const ALIASED_EXECUTE_CODE_NAMES = /* @__PURE__ */ new Set([
|
|
3176
|
-
"mysql_execute_code",
|
|
3177
|
-
"mysqlExecuteCode",
|
|
3178
|
-
"execute_code",
|
|
3179
|
-
"executeCode",
|
|
3180
|
-
"mjExecuteCode",
|
|
3181
|
-
"mj_eval",
|
|
3182
|
-
"eval_code",
|
|
3183
|
-
"run_code",
|
|
3184
|
-
"exec_code",
|
|
3185
|
-
"code_execute"
|
|
3186
|
-
]);
|
|
3187
|
-
if (req.params.name && ALIASED_EXECUTE_CODE_NAMES.has(req.params.name)) {
|
|
3188
|
-
req.params.name = "mj_execute_code";
|
|
3189
|
-
}
|
|
3190
|
-
if (req.params.name === "create_entry_minimal") {
|
|
3191
|
-
req.params.name = "create_entry";
|
|
3192
|
-
if (typeof toolArgs["entry_type"] === "undefined") {
|
|
3193
|
-
toolArgs["entry_type"] = "personal_reflection";
|
|
3194
|
-
}
|
|
3195
|
-
}
|
|
3196
|
-
const ALIASED_LIST_PROJECTS_NAMES = /* @__PURE__ */ new Set([
|
|
3197
|
-
"listProjects",
|
|
3198
|
-
"list-projects",
|
|
3199
|
-
"get_projects",
|
|
3200
|
-
"getProjects",
|
|
3201
|
-
"get-projects",
|
|
3202
|
-
"mj_list_projects",
|
|
3203
|
-
"mjListProjects",
|
|
3204
|
-
"mj_get_projects",
|
|
3205
|
-
"mjGetProjects",
|
|
3206
|
-
"mj_admin_list_projects",
|
|
3207
|
-
"mj_admin_get_projects"
|
|
3208
|
-
]);
|
|
3209
|
-
if (req.params.name && ALIASED_LIST_PROJECTS_NAMES.has(req.params.name)) {
|
|
3210
|
-
req.params.name = "list_projects";
|
|
3211
|
-
}
|
|
3212
|
-
const ALIASED_GET_PROJECT_NAMES = /* @__PURE__ */ new Set([
|
|
3213
|
-
"getProject",
|
|
3214
|
-
"get-project",
|
|
3215
|
-
"mj_get_project",
|
|
3216
|
-
"mjGetProject",
|
|
3217
|
-
"mj_admin_get_project"
|
|
3218
|
-
]);
|
|
3219
|
-
if (req.params.name && ALIASED_GET_PROJECT_NAMES.has(req.params.name)) {
|
|
3220
|
-
req.params.name = "get_project";
|
|
3221
|
-
}
|
|
3222
|
-
const ALIASED_GREP_SEARCH_NAMES = /* @__PURE__ */ new Set([
|
|
3223
|
-
"grep_search",
|
|
3224
|
-
"grepSearch",
|
|
3225
|
-
"grep",
|
|
3226
|
-
"file_search",
|
|
3227
|
-
"fileSearch",
|
|
3228
|
-
"find_in_files",
|
|
3229
|
-
"findInFiles",
|
|
3230
|
-
"search_files",
|
|
3231
|
-
"searchFiles"
|
|
3232
|
-
]);
|
|
3233
|
-
if (req.params.name && ALIASED_GREP_SEARCH_NAMES.has(req.params.name)) {
|
|
3234
|
-
req.params.name = "grep_search";
|
|
3235
|
-
}
|
|
3236
|
-
const ALIASED_VIEW_FILE_NAMES = /* @__PURE__ */ new Set([
|
|
3237
|
-
"view_file",
|
|
3238
|
-
"viewFile",
|
|
3239
|
-
"read_file",
|
|
3240
|
-
"readFile",
|
|
3241
|
-
"get_file",
|
|
3242
|
-
"getFile"
|
|
3243
|
-
]);
|
|
3244
|
-
if (req.params.name && ALIASED_VIEW_FILE_NAMES.has(req.params.name)) {
|
|
3245
|
-
req.params.name = "view_file";
|
|
3246
|
-
}
|
|
3247
|
-
const ALIASED_LIST_DIR_NAMES = /* @__PURE__ */ new Set([
|
|
3248
|
-
"list_dir",
|
|
3249
|
-
"listDir",
|
|
3250
|
-
"read_dir",
|
|
3251
|
-
"readDir"
|
|
3252
|
-
]);
|
|
3253
|
-
if (req.params.name && ALIASED_LIST_DIR_NAMES.has(req.params.name)) {
|
|
3254
|
-
req.params.name = "list_dir";
|
|
3255
|
-
}
|
|
3256
|
-
if (req.params.name) {
|
|
3257
|
-
const intercept = applyArgInterceptions(req.params.name, [toolArgs]);
|
|
3258
|
-
if ("error" in intercept) {
|
|
3259
|
-
const errorResult = intercept.error;
|
|
3260
|
-
logAgentIssue({
|
|
3261
|
-
source: "MCP",
|
|
3262
|
-
action: req.params.name,
|
|
3263
|
-
input: toolArgs,
|
|
3264
|
-
error: errorResult["error"] ?? "Validation Error"
|
|
3265
|
-
});
|
|
3266
|
-
const enriched = JSON.stringify({
|
|
3267
|
-
...errorResult,
|
|
3268
|
-
_meta: { tokenEstimate: 0 }
|
|
3269
|
-
});
|
|
3270
|
-
const tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
|
|
3271
|
-
const finalText = enriched.replace('"tokenEstimate":0', `"tokenEstimate":${tokenEstimate}`);
|
|
3272
|
-
return {
|
|
3273
|
-
content: [
|
|
3274
|
-
{
|
|
3275
|
-
type: "text",
|
|
3276
|
-
text: finalText
|
|
3277
|
-
}
|
|
3278
|
-
],
|
|
3279
|
-
structuredContent: errorResult
|
|
3280
|
-
};
|
|
3281
|
-
} else {
|
|
3282
|
-
const normalizedArgs = intercept.args?.[0];
|
|
3283
|
-
if (normalizedArgs !== null && typeof normalizedArgs === "object" && !Array.isArray(normalizedArgs)) {
|
|
3284
|
-
req.params.arguments = normalizedArgs;
|
|
3285
|
-
}
|
|
3286
|
-
}
|
|
3287
|
-
}
|
|
3288
|
-
}
|
|
3289
|
-
if (req?.params?.name && !allToolNames.has(req.params.name)) {
|
|
3290
|
-
const requestedTool = req.params.name;
|
|
3291
|
-
const toolArgs = req.params.arguments ?? {};
|
|
3292
|
-
const normalized = requestedTool.replace(/^mj_/, "").replace(/_/g, "-");
|
|
3293
|
-
const matchingPrompt = prompts.find((p) => p.name === normalized || p.name === requestedTool || p.name === requestedTool.replace(/^mj_/, ""));
|
|
3294
|
-
if (healingEnabled) {
|
|
3295
|
-
if (matchingPrompt) {
|
|
3296
|
-
try {
|
|
3297
|
-
const stringArgs = {};
|
|
3298
|
-
for (const [k, v] of Object.entries(toolArgs)) {
|
|
3299
|
-
if (typeof v === "string") stringArgs[k] = v;
|
|
3300
|
-
else if (typeof v === "number" || typeof v === "boolean") stringArgs[k] = String(v);
|
|
3301
|
-
}
|
|
3302
|
-
if ((matchingPrompt.name === "load-briefing" || matchingPrompt.name === "briefing") && !stringArgs["projects"]) {
|
|
3303
|
-
const fallbackProject = stringArgs["project"] ?? stringArgs["name"] ?? stringArgs["target"] ?? stringArgs["repo"] ?? stringArgs["workspace"] ?? "";
|
|
3304
|
-
stringArgs["projects"] = fallbackProject;
|
|
3305
|
-
}
|
|
3306
|
-
const currentConfig = configManager.getConfig();
|
|
3307
|
-
const promptResult = await getPrompt(
|
|
3308
|
-
matchingPrompt.name,
|
|
3309
|
-
stringArgs,
|
|
3310
|
-
db,
|
|
3311
|
-
teamDb ?? void 0,
|
|
3312
|
-
github ?? void 0,
|
|
3313
|
-
currentConfig.briefingConfig
|
|
3314
|
-
);
|
|
3315
|
-
const successPayload = {
|
|
3316
|
-
success: true,
|
|
3317
|
-
data: promptResult,
|
|
3318
|
-
messages: promptResult.messages,
|
|
3319
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' is an MCP Prompt, but was invoked directly as an MCP tool. The prompt was evaluated and returned successfully.`
|
|
3320
|
-
};
|
|
3321
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3322
|
-
return {
|
|
3323
|
-
content: [
|
|
3324
|
-
{
|
|
3325
|
-
type: "text",
|
|
3326
|
-
text: enriched2
|
|
3327
|
-
}
|
|
3328
|
-
],
|
|
3329
|
-
structuredContent: successPayload
|
|
3330
|
-
};
|
|
3331
|
-
} catch {
|
|
3332
|
-
}
|
|
3333
|
-
}
|
|
3334
|
-
const isResource = requestedTool === "read_resource" || requestedTool === "readResource" || requestedTool === "read-resource" || requestedTool === "mj_read_resource" || requestedTool === "mjReadResource" || requestedTool === "memory_read_resource" || requestedTool === "memoryReadResource" || requestedTool === "memory_journal_read_resource" || requestedTool === "get_resource" || requestedTool === "getResource" || requestedTool === "get-resource" || requestedTool === "memory_get_resource" || requestedTool === "memoryGetResource" || requestedTool === "mj_get_resource" || requestedTool === "mjGetResource" || requestedTool === "read_memory_resource" || requestedTool.endsWith("_read_resource") || requestedTool.endsWith("ReadResource") || requestedTool.startsWith("memory://");
|
|
3335
|
-
if (isResource) {
|
|
3336
|
-
try {
|
|
3337
|
-
let uri = typeof toolArgs["uri"] === "string" ? toolArgs["uri"] : typeof toolArgs["Uri"] === "string" ? toolArgs["Uri"] : typeof toolArgs["_uri"] === "string" ? toolArgs["_uri"] : void 0;
|
|
3338
|
-
if (!uri) {
|
|
3339
|
-
for (const key of ["resourceUri", "resource_uri", "resource", "Resource", "url", "Url", "path", "Path", "name", "Name", "target", "Target", "ServerName", "server_name"]) {
|
|
3340
|
-
const rawVal = toolArgs[key];
|
|
3341
|
-
if (typeof rawVal === "string" && rawVal.trim()) {
|
|
3342
|
-
const val = rawVal.trim();
|
|
3343
|
-
if (key === "ServerName" || key === "server_name") {
|
|
3344
|
-
if (val === "memory-journal-mcp" || val === "memory-journal") continue;
|
|
3345
|
-
}
|
|
3346
|
-
uri = val;
|
|
3347
|
-
break;
|
|
3348
|
-
}
|
|
3349
|
-
}
|
|
3350
|
-
}
|
|
3351
|
-
if (!uri && requestedTool.startsWith("memory://")) {
|
|
3352
|
-
uri = requestedTool;
|
|
3353
|
-
}
|
|
3354
|
-
if (uri && !uri.includes("://")) {
|
|
3355
|
-
if (uri.startsWith("briefing") || uri.startsWith("recent") || uri.startsWith("agent-") || uri.startsWith("statistics") || uri.startsWith("insights") || uri.startsWith("help") || uri.startsWith("health") || uri.startsWith("team") || uri.startsWith("git") || uri.startsWith("tools")) {
|
|
3356
|
-
uri = `memory://${uri}`;
|
|
3357
|
-
} else {
|
|
3358
|
-
uri = `memory://briefing/${uri}`;
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
if (!uri) {
|
|
3362
|
-
uri = "memory://briefing";
|
|
3363
|
-
}
|
|
3364
|
-
const currentConfig = configManager.getConfig();
|
|
3365
|
-
const res = await readResource(
|
|
3366
|
-
uri,
|
|
3367
|
-
db,
|
|
3368
|
-
vectorManager ?? void 0,
|
|
3369
|
-
currentConfig.toolFilterConfig,
|
|
3370
|
-
github ?? void 0,
|
|
3371
|
-
void 0,
|
|
3372
|
-
teamDb ?? void 0,
|
|
3373
|
-
currentConfig.briefingConfig,
|
|
3374
|
-
runtime
|
|
3375
|
-
);
|
|
3376
|
-
const successPayload = {
|
|
3377
|
-
success: true,
|
|
3378
|
-
data: res.data,
|
|
3379
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool instead of an MCP resource read. The resource '${uri}' was resolved successfully.`
|
|
3380
|
-
};
|
|
3381
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3382
|
-
return {
|
|
3383
|
-
content: [
|
|
3384
|
-
{
|
|
3385
|
-
type: "text",
|
|
3386
|
-
text: enriched2
|
|
3387
|
-
}
|
|
3388
|
-
],
|
|
3389
|
-
structuredContent: successPayload
|
|
3390
|
-
};
|
|
3391
|
-
} catch {
|
|
3392
|
-
}
|
|
3393
|
-
}
|
|
3394
|
-
const isListResources = requestedTool === "list_resources" || requestedTool === "listResources" || requestedTool === "list-resources" || requestedTool === "mj_list_resources" || requestedTool === "mjListResources" || requestedTool === "memory_list_resources" || requestedTool === "memoryListResources" || requestedTool === "memory_journal_list_resources" || requestedTool === "get_resources" || requestedTool === "getResources" || requestedTool === "get-resources" || requestedTool === "memory_get_resources" || requestedTool === "memoryGetResources" || requestedTool.endsWith("_list_resources") || requestedTool.endsWith("ListResources");
|
|
3395
|
-
if (isListResources) {
|
|
3396
|
-
try {
|
|
3397
|
-
const resourceList = getResources();
|
|
3398
|
-
const successPayload = {
|
|
3399
|
-
success: true,
|
|
3400
|
-
resources: resourceList,
|
|
3401
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool instead of an MCP protocol request (resources/list). The available resources were returned successfully.`
|
|
3402
|
-
};
|
|
3403
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3404
|
-
return {
|
|
3405
|
-
content: [
|
|
3406
|
-
{
|
|
3407
|
-
type: "text",
|
|
3408
|
-
text: enriched2
|
|
3409
|
-
}
|
|
3410
|
-
],
|
|
3411
|
-
structuredContent: successPayload
|
|
3412
|
-
};
|
|
3413
|
-
} catch {
|
|
3414
|
-
}
|
|
3415
|
-
}
|
|
3416
|
-
const isListPrompts = requestedTool === "list_prompts" || requestedTool === "listPrompts" || requestedTool === "list-prompts" || requestedTool === "mj_list_prompts" || requestedTool === "mjListPrompts" || requestedTool === "memory_list_prompts" || requestedTool === "memoryListPrompts" || requestedTool === "memory_journal_list_prompts" || requestedTool === "get_prompts" || requestedTool === "getPrompts" || requestedTool.endsWith("_list_prompts") || requestedTool.endsWith("ListPrompts");
|
|
3417
|
-
if (isListPrompts) {
|
|
3418
|
-
try {
|
|
3419
|
-
const promptList = getPrompts();
|
|
3420
|
-
const successPayload = {
|
|
3421
|
-
success: true,
|
|
3422
|
-
prompts: promptList,
|
|
3423
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool instead of an MCP protocol request (prompts/list). The available prompts were returned successfully.`
|
|
3424
|
-
};
|
|
3425
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3426
|
-
return {
|
|
3427
|
-
content: [
|
|
3428
|
-
{
|
|
3429
|
-
type: "text",
|
|
3430
|
-
text: enriched2
|
|
3431
|
-
}
|
|
3432
|
-
],
|
|
3433
|
-
structuredContent: successPayload
|
|
3434
|
-
};
|
|
3435
|
-
} catch {
|
|
3436
|
-
}
|
|
3437
|
-
}
|
|
3438
|
-
const isListTools = requestedTool === "list_tools" || requestedTool === "listTools" || requestedTool === "list-tools" || requestedTool === "mj_list_tools" || requestedTool === "mjListTools" || requestedTool === "memory_list_tools" || requestedTool === "memoryListTools" || requestedTool === "memory_journal_list_tools" || requestedTool === "get_tools" || requestedTool === "getTools" || requestedTool.endsWith("_list_tools") || requestedTool.endsWith("ListTools");
|
|
3439
|
-
if (isListTools) {
|
|
3440
|
-
try {
|
|
3441
|
-
const currentConfig = configManager.getConfig();
|
|
3442
|
-
const enabledSet = currentConfig.toolFilterConfig?.enabledTools ?? allToolNames;
|
|
3443
|
-
const toolsList = allTools.filter((t) => enabledSet.has(t.name)).map((t) => ({
|
|
3444
|
-
name: t.name,
|
|
3445
|
-
title: t.title,
|
|
3446
|
-
description: t.description,
|
|
3447
|
-
annotations: t.annotations
|
|
3448
|
-
}));
|
|
3449
|
-
const successPayload = {
|
|
3450
|
-
success: true,
|
|
3451
|
-
tools: toolsList,
|
|
3452
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool instead of an MCP protocol request (tools/list). The available tools were returned successfully.`
|
|
3453
|
-
};
|
|
3454
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3455
|
-
return {
|
|
3456
|
-
content: [
|
|
3457
|
-
{
|
|
3458
|
-
type: "text",
|
|
3459
|
-
text: enriched2
|
|
3460
|
-
}
|
|
3461
|
-
],
|
|
3462
|
-
structuredContent: successPayload
|
|
3463
|
-
};
|
|
3464
|
-
} catch {
|
|
3465
|
-
}
|
|
3466
|
-
}
|
|
3467
|
-
const isGetPrompt = requestedTool === "get_prompt" || requestedTool === "getPrompt" || requestedTool === "mj_get_prompt" || requestedTool === "mjGetPrompt" || requestedTool === "memory_get_prompt" || requestedTool === "memoryGetPrompt" || requestedTool === "memory_journal_get_prompt" || requestedTool === "read_prompt" || requestedTool === "readPrompt" || requestedTool.endsWith("_get_prompt") || requestedTool.endsWith("GetPrompt");
|
|
3468
|
-
if (isGetPrompt) {
|
|
3469
|
-
try {
|
|
3470
|
-
const targetPromptName = typeof toolArgs["name"] === "string" ? toolArgs["name"] : typeof toolArgs["prompt"] === "string" ? toolArgs["prompt"] : typeof toolArgs["prompt_name"] === "string" ? toolArgs["prompt_name"] : "load-briefing";
|
|
3471
|
-
const nestedArgs = typeof toolArgs["arguments"] === "object" && toolArgs["arguments"] !== null ? toolArgs["arguments"] : toolArgs;
|
|
3472
|
-
const stringArgs = {};
|
|
3473
|
-
for (const [k, v] of Object.entries(nestedArgs)) {
|
|
3474
|
-
if (k !== "name" && k !== "prompt" && k !== "prompt_name" && k !== "arguments") {
|
|
3475
|
-
if (typeof v === "string") stringArgs[k] = v;
|
|
3476
|
-
else if (typeof v === "number" || typeof v === "boolean") stringArgs[k] = String(v);
|
|
3477
|
-
}
|
|
3478
|
-
}
|
|
3479
|
-
const currentConfig = configManager.getConfig();
|
|
3480
|
-
const promptResult = await getPrompt(
|
|
3481
|
-
targetPromptName,
|
|
3482
|
-
stringArgs,
|
|
3483
|
-
db,
|
|
3484
|
-
teamDb ?? void 0,
|
|
3485
|
-
github ?? void 0,
|
|
3486
|
-
currentConfig.briefingConfig
|
|
3487
|
-
);
|
|
3488
|
-
const successPayload = {
|
|
3489
|
-
success: true,
|
|
3490
|
-
data: promptResult,
|
|
3491
|
-
messages: promptResult.messages,
|
|
3492
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool instead of an MCP protocol request (prompts/get). The prompt '${targetPromptName}' was evaluated successfully.`
|
|
3493
|
-
};
|
|
3494
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3495
|
-
return {
|
|
3496
|
-
content: [
|
|
3497
|
-
{
|
|
3498
|
-
type: "text",
|
|
3499
|
-
text: enriched2
|
|
3500
|
-
}
|
|
3501
|
-
],
|
|
3502
|
-
structuredContent: successPayload
|
|
3503
|
-
};
|
|
3504
|
-
} catch {
|
|
3505
|
-
}
|
|
3506
|
-
}
|
|
3507
|
-
const isServerInfo = [
|
|
3508
|
-
"server_info",
|
|
3509
|
-
"serverInfo",
|
|
3510
|
-
"server-info",
|
|
3511
|
-
"mj_server_info",
|
|
3512
|
-
"get_server_info",
|
|
3513
|
-
"getServerInfo",
|
|
3514
|
-
"server_health",
|
|
3515
|
-
"serverHealth",
|
|
3516
|
-
"server-health",
|
|
3517
|
-
"mj_server_health",
|
|
3518
|
-
"get_server_health",
|
|
3519
|
-
"getServerHealth",
|
|
3520
|
-
"health_check",
|
|
3521
|
-
"healthCheck",
|
|
3522
|
-
"health-check",
|
|
3523
|
-
"server_status",
|
|
3524
|
-
"serverStatus",
|
|
3525
|
-
"server-status",
|
|
3526
|
-
"status",
|
|
3527
|
-
"health"
|
|
3528
|
-
].includes(requestedTool);
|
|
3529
|
-
if (isServerInfo) {
|
|
3530
|
-
try {
|
|
3531
|
-
let healthData;
|
|
3532
|
-
try {
|
|
3533
|
-
const res = await readResource(
|
|
3534
|
-
"memory://health",
|
|
3535
|
-
db,
|
|
3536
|
-
vectorManager ?? void 0,
|
|
3537
|
-
configManager.getConfig().toolFilterConfig ?? void 0,
|
|
3538
|
-
github ?? void 0,
|
|
3539
|
-
void 0,
|
|
3540
|
-
teamDb ?? void 0,
|
|
3541
|
-
configManager.getConfig().briefingConfig,
|
|
3542
|
-
runtime
|
|
3543
|
-
);
|
|
3544
|
-
if (typeof res.data === "object" && res.data !== null) {
|
|
3545
|
-
healthData = res.data;
|
|
3546
|
-
}
|
|
3547
|
-
} catch {
|
|
3548
|
-
}
|
|
3549
|
-
const dbHealth = healthData ?? db.getHealthStatus();
|
|
3550
|
-
const successPayload = {
|
|
3551
|
-
success: true,
|
|
3552
|
-
status: "healthy",
|
|
3553
|
-
server: {
|
|
3554
|
-
name: "memory-journal-mcp",
|
|
3555
|
-
version: VERSION,
|
|
3556
|
-
description: "Memory Journal MCP Server with Code Mode & Semantic Search"
|
|
3557
|
-
},
|
|
3558
|
-
runtime: {
|
|
3559
|
-
nodeVersion: process.version,
|
|
3560
|
-
platform: process.platform,
|
|
3561
|
-
uptimeSeconds: Math.floor(process.uptime())
|
|
3562
|
-
},
|
|
3563
|
-
...healthData !== void 0 ? { health: healthData } : {},
|
|
3564
|
-
...dbHealth !== void 0 ? { database: dbHealth } : {},
|
|
3565
|
-
capabilities: {
|
|
3566
|
-
codemode: true,
|
|
3567
|
-
resources: true,
|
|
3568
|
-
prompts: true,
|
|
3569
|
-
tools: true
|
|
3570
|
-
},
|
|
3571
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool. Returned server diagnostic information and health status successfully.`
|
|
3572
|
-
};
|
|
3573
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3574
|
-
return {
|
|
3575
|
-
content: [
|
|
3576
|
-
{
|
|
3577
|
-
type: "text",
|
|
3578
|
-
text: enriched2
|
|
3579
|
-
}
|
|
3580
|
-
],
|
|
3581
|
-
structuredContent: successPayload
|
|
3582
|
-
};
|
|
3583
|
-
} catch {
|
|
3584
|
-
}
|
|
3585
|
-
}
|
|
3586
|
-
const isListProjects = [
|
|
3587
|
-
"list_projects",
|
|
3588
|
-
"listProjects",
|
|
3589
|
-
"list-projects",
|
|
3590
|
-
"get_projects",
|
|
3591
|
-
"getProjects",
|
|
3592
|
-
"get-projects",
|
|
3593
|
-
"mj_list_projects",
|
|
3594
|
-
"mjListProjects",
|
|
3595
|
-
"mj_get_projects",
|
|
3596
|
-
"mjGetProjects",
|
|
3597
|
-
"mj_admin_list_projects",
|
|
3598
|
-
"mj_admin_get_projects",
|
|
3599
|
-
"projects"
|
|
3600
|
-
].includes(requestedTool) || requestedTool.endsWith("list_projects") || requestedTool.endsWith("listProjects") || requestedTool.endsWith("ListProjects");
|
|
3601
|
-
if (isListProjects) {
|
|
3602
|
-
try {
|
|
3603
|
-
const currentConfig = configManager.getConfig();
|
|
3604
|
-
const registry = currentConfig.projectRegistry ?? {};
|
|
3605
|
-
const successPayload = {
|
|
3606
|
-
success: true,
|
|
3607
|
-
projects: registry,
|
|
3608
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool. Returned registered projects from projectRegistry successfully.`
|
|
3609
|
-
};
|
|
3610
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3611
|
-
return {
|
|
3612
|
-
content: [
|
|
3613
|
-
{
|
|
3614
|
-
type: "text",
|
|
3615
|
-
text: enriched2
|
|
3616
|
-
}
|
|
3617
|
-
],
|
|
3618
|
-
structuredContent: successPayload
|
|
3619
|
-
};
|
|
3620
|
-
} catch {
|
|
3621
|
-
}
|
|
3622
|
-
}
|
|
3623
|
-
const isListAdapters = [
|
|
3624
|
-
"list_adapters",
|
|
3625
|
-
"listAdapters",
|
|
3626
|
-
"list-adapters",
|
|
3627
|
-
"mj_list_adapters",
|
|
3628
|
-
"mjListAdapters",
|
|
3629
|
-
"get_adapters",
|
|
3630
|
-
"getAdapters",
|
|
3631
|
-
"get-adapters",
|
|
3632
|
-
"adapters"
|
|
3633
|
-
].includes(requestedTool) || requestedTool.endsWith("list_adapters") || requestedTool.endsWith("listAdapters") || requestedTool.endsWith("ListAdapters");
|
|
3634
|
-
if (isListAdapters) {
|
|
3635
|
-
try {
|
|
3636
|
-
const adapters = [
|
|
3637
|
-
{
|
|
3638
|
-
name: "sqlite",
|
|
3639
|
-
type: "database",
|
|
3640
|
-
driver: "better-sqlite3",
|
|
3641
|
-
status: "connected",
|
|
3642
|
-
primary: true,
|
|
3643
|
-
description: "Primary SQLite storage engine for journal entries, tags, and relationships"
|
|
3644
|
-
},
|
|
3645
|
-
{
|
|
3646
|
-
name: "sqlite-vec",
|
|
3647
|
-
type: "vector",
|
|
3648
|
-
driver: "sqlite-vec",
|
|
3649
|
-
status: vectorManager !== void 0 && vectorManager !== null ? "connected" : "ready",
|
|
3650
|
-
description: "Native vector embeddings and semantic search engine"
|
|
3651
|
-
},
|
|
3652
|
-
{
|
|
3653
|
-
name: "team",
|
|
3654
|
-
type: "database",
|
|
3655
|
-
driver: "better-sqlite3",
|
|
3656
|
-
status: teamDb !== void 0 && teamDb !== null ? "connected" : "disabled",
|
|
3657
|
-
description: "Collaborative team journal database adapter"
|
|
3658
|
-
},
|
|
3659
|
-
{
|
|
3660
|
-
name: "github",
|
|
3661
|
-
type: "integration",
|
|
3662
|
-
driver: "octokit / gh",
|
|
3663
|
-
status: github !== void 0 && github !== null ? "connected" : "disabled",
|
|
3664
|
-
description: "GitHub synchronization and issue tracking adapter"
|
|
3665
|
-
}
|
|
3666
|
-
];
|
|
3667
|
-
const successPayload = {
|
|
3668
|
-
success: true,
|
|
3669
|
-
adapters,
|
|
3670
|
-
default_adapter: "sqlite",
|
|
3671
|
-
_healing_note: `\u{1F6E0}\uFE0F AUTONOMOUS HEALING: '${requestedTool}' was called as an MCP tool. Returned registered database and service adapters successfully.`
|
|
3672
|
-
};
|
|
3673
|
-
const enriched2 = JSON.stringify(successPayload, null, 2);
|
|
3674
|
-
return {
|
|
3675
|
-
content: [
|
|
3676
|
-
{
|
|
3677
|
-
type: "text",
|
|
3678
|
-
text: enriched2
|
|
3679
|
-
}
|
|
3680
|
-
],
|
|
3681
|
-
structuredContent: successPayload
|
|
3682
|
-
};
|
|
3683
|
-
} catch {
|
|
3684
|
-
}
|
|
3685
|
-
}
|
|
3686
|
-
const isGrepSearch = [
|
|
3687
|
-
"grep_search",
|
|
3688
|
-
"grepSearch",
|
|
3689
|
-
"grep",
|
|
3690
|
-
"file_search",
|
|
3691
|
-
"fileSearch",
|
|
3692
|
-
"find_in_files",
|
|
3693
|
-
"findInFiles",
|
|
3694
|
-
"search_files",
|
|
3695
|
-
"searchFiles"
|
|
3696
|
-
].includes(requestedTool) || requestedTool.endsWith("grep_search") || requestedTool.endsWith("grepSearch");
|
|
3697
|
-
if (isGrepSearch) {
|
|
3698
|
-
try {
|
|
3699
|
-
const dummyTools = getDummyHealingTools({ db, teamDb: teamDb ?? void 0, vectorManager: vectorManager ?? void 0, teamVectorManager: teamVectorManager ?? void 0, github: github ?? void 0, config: customToolHandlerConfig });
|
|
3700
|
-
const grepTool = dummyTools.find((t) => t.name === "grep_search");
|
|
3701
|
-
if (grepTool) {
|
|
3702
|
-
const res = await Promise.resolve(grepTool.handler(toolArgs));
|
|
3703
|
-
const enriched2 = JSON.stringify(res, null, 2);
|
|
3704
|
-
return {
|
|
3705
|
-
content: [
|
|
3706
|
-
{
|
|
3707
|
-
type: "text",
|
|
3708
|
-
text: enriched2
|
|
3709
|
-
}
|
|
3710
|
-
],
|
|
3711
|
-
structuredContent: res
|
|
3712
|
-
};
|
|
3713
|
-
}
|
|
3714
|
-
} catch {
|
|
3715
|
-
}
|
|
3716
|
-
}
|
|
3717
|
-
const isViewFile = [
|
|
3718
|
-
"view_file",
|
|
3719
|
-
"viewFile",
|
|
3720
|
-
"read_file",
|
|
3721
|
-
"readFile",
|
|
3722
|
-
"get_file",
|
|
3723
|
-
"getFile",
|
|
3724
|
-
"cat"
|
|
3725
|
-
].includes(requestedTool);
|
|
3726
|
-
if (isViewFile) {
|
|
3727
|
-
try {
|
|
3728
|
-
const dummyTools = getDummyHealingTools({ db, teamDb: teamDb ?? void 0, vectorManager: vectorManager ?? void 0, teamVectorManager: teamVectorManager ?? void 0, github: github ?? void 0, config: customToolHandlerConfig });
|
|
3729
|
-
const viewTool = dummyTools.find((t) => t.name === "view_file");
|
|
3730
|
-
if (viewTool) {
|
|
3731
|
-
const res = await Promise.resolve(viewTool.handler(toolArgs));
|
|
3732
|
-
const enriched2 = JSON.stringify(res, null, 2);
|
|
3733
|
-
return {
|
|
3734
|
-
content: [
|
|
3735
|
-
{
|
|
3736
|
-
type: "text",
|
|
3737
|
-
text: enriched2
|
|
3738
|
-
}
|
|
3739
|
-
],
|
|
3740
|
-
structuredContent: res
|
|
3741
|
-
};
|
|
3742
|
-
}
|
|
3743
|
-
} catch {
|
|
3744
|
-
}
|
|
3745
|
-
}
|
|
3746
|
-
const isListDir = [
|
|
3747
|
-
"list_dir",
|
|
3748
|
-
"listDir",
|
|
3749
|
-
"read_dir",
|
|
3750
|
-
"readDir",
|
|
3751
|
-
"ls",
|
|
3752
|
-
"dir"
|
|
3753
|
-
].includes(requestedTool);
|
|
3754
|
-
if (isListDir) {
|
|
3755
|
-
try {
|
|
3756
|
-
const dummyTools = getDummyHealingTools({ db, teamDb: teamDb ?? void 0, vectorManager: vectorManager ?? void 0, teamVectorManager: teamVectorManager ?? void 0, github: github ?? void 0, config: customToolHandlerConfig });
|
|
3757
|
-
const dirTool = dummyTools.find((t) => t.name === "list_dir");
|
|
3758
|
-
if (dirTool) {
|
|
3759
|
-
const res = await Promise.resolve(dirTool.handler(toolArgs));
|
|
3760
|
-
const enriched2 = JSON.stringify(res, null, 2);
|
|
3761
|
-
return {
|
|
3762
|
-
content: [
|
|
3763
|
-
{
|
|
3764
|
-
type: "text",
|
|
3765
|
-
text: enriched2
|
|
3766
|
-
}
|
|
3767
|
-
],
|
|
3768
|
-
structuredContent: res
|
|
3769
|
-
};
|
|
3770
|
-
}
|
|
3771
|
-
} catch {
|
|
3772
|
-
}
|
|
3773
|
-
}
|
|
3774
|
-
}
|
|
3775
|
-
let suggestion = `Tool '${requestedTool}' does not exist. Use tools/list to see available tools.`;
|
|
3776
|
-
const isFilteredTool = Object.values(TOOL_GROUPS).flat().includes(requestedTool);
|
|
3777
|
-
if (isFilteredTool) {
|
|
3778
|
-
suggestion = `'${requestedTool}' is a valid tool but it is not currently exposed because of your active --tool-filter or codemode restrictions. If you are in Code Mode, invoke it via 'mj_execute_code' (e.g. 'await mj.core.${requestedTool}(...)').`;
|
|
3779
|
-
} else if (matchingPrompt !== void 0) {
|
|
3780
|
-
suggestion = `'${requestedTool}' is an MCP Prompt, not an MCP Tool. Please use the 'get_prompt' tool (or your native MCP Prompts integration) to evaluate it, then use 'create_entry' to save the result.`;
|
|
3781
|
-
} else if (requestedTool.startsWith("mj_")) {
|
|
3782
|
-
suggestion = `'${requestedTool}' is not a valid root tool. If you are trying to use Code Mode, you must invoke the 'mj_execute_code' tool and write javascript (e.g. 'await mj.core.searchEntries(...)').`;
|
|
3783
|
-
}
|
|
3784
|
-
const errorResult = {
|
|
3785
|
-
success: false,
|
|
3786
|
-
error: `MCP error -32602: Tool ${requestedTool} not found`,
|
|
3787
|
-
code: "TOOL_NOT_FOUND",
|
|
3788
|
-
category: "hallucination",
|
|
3789
|
-
suggestion,
|
|
3790
|
-
recoverable: false
|
|
3791
|
-
};
|
|
3792
|
-
if (healingEnabled) {
|
|
3793
|
-
logAgentIssue({
|
|
3794
|
-
source: "MCP",
|
|
3795
|
-
action: requestedTool,
|
|
3796
|
-
input: toolArgs,
|
|
3797
|
-
error: errorResult.error
|
|
3798
|
-
});
|
|
3799
|
-
}
|
|
3800
|
-
const enriched = JSON.stringify({
|
|
3801
|
-
...errorResult,
|
|
3802
|
-
_meta: { tokenEstimate: 0 }
|
|
3803
|
-
});
|
|
3804
|
-
const tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
|
|
3805
|
-
const finalText = enriched.replace('"tokenEstimate":0', `"tokenEstimate":${tokenEstimate}`);
|
|
3806
|
-
return {
|
|
3807
|
-
content: [
|
|
3808
|
-
{
|
|
3809
|
-
type: "text",
|
|
3810
|
-
text: finalText
|
|
3811
|
-
}
|
|
3812
|
-
],
|
|
3813
|
-
structuredContent: errorResult,
|
|
3814
|
-
isError: true
|
|
3815
|
-
};
|
|
3816
|
-
}
|
|
3817
|
-
const result = await originalHandler(request2, extra);
|
|
3818
|
-
if (result !== null && result !== void 0 && typeof result === "object" && "isError" in result && result.isError === true) {
|
|
3819
|
-
const resObj = result;
|
|
3820
|
-
if (Array.isArray(resObj.content) && resObj.content.length > 0) {
|
|
3821
|
-
const text = resObj.content[0]?.text;
|
|
3822
|
-
if (typeof text === "string" && text.includes("MCP error -32602")) {
|
|
3823
|
-
let errorMessage = text.replace(
|
|
3824
|
-
"MCP error -32602: Input validation error: ",
|
|
3825
|
-
""
|
|
3826
|
-
);
|
|
3827
|
-
try {
|
|
3828
|
-
const parts = text.split(": [");
|
|
3829
|
-
if (parts.length > 1) {
|
|
3830
|
-
const zodErrors = JSON.parse("[" + parts[1]);
|
|
3831
|
-
const formattedErrors = zodErrors.map((e) => {
|
|
3832
|
-
const path2 = e.path && e.path.length > 0 ? e.path.join(".") : "unknown";
|
|
3833
|
-
return `${path2}: ${e.message || "invalid"}`;
|
|
3834
|
-
}).join("; ");
|
|
3835
|
-
errorMessage = formattedErrors;
|
|
3836
|
-
}
|
|
3837
|
-
} catch {
|
|
3838
|
-
}
|
|
3839
|
-
const errorResult = {
|
|
3840
|
-
success: false,
|
|
3841
|
-
error: errorMessage,
|
|
3842
|
-
code: "VALIDATION_ERROR",
|
|
3843
|
-
category: "validation",
|
|
3844
|
-
suggestion: "Check input parameters against the tool schema",
|
|
3845
|
-
recoverable: false
|
|
3846
|
-
};
|
|
3847
|
-
logAgentIssue({
|
|
3848
|
-
source: "MCP",
|
|
3849
|
-
action: req.params?.name ?? "unknown",
|
|
3850
|
-
input: req.params?.arguments ?? {},
|
|
3851
|
-
error: errorMessage
|
|
3852
|
-
});
|
|
3853
|
-
const enriched = JSON.stringify({
|
|
3854
|
-
...errorResult,
|
|
3855
|
-
_meta: { tokenEstimate: 0 }
|
|
3856
|
-
});
|
|
3857
|
-
const tokenEstimate = Math.ceil(
|
|
3858
|
-
Buffer.byteLength(enriched, "utf8") / 4
|
|
3859
|
-
);
|
|
3860
|
-
const finalText = enriched.replace(
|
|
3861
|
-
'"tokenEstimate":0',
|
|
3862
|
-
`"tokenEstimate":${tokenEstimate}`
|
|
3863
|
-
);
|
|
3864
|
-
return {
|
|
3865
|
-
content: [
|
|
3866
|
-
{
|
|
3867
|
-
type: "text",
|
|
3868
|
-
text: finalText
|
|
3869
|
-
}
|
|
3870
|
-
],
|
|
3871
|
-
structuredContent: errorResult
|
|
3872
|
-
};
|
|
3873
|
-
}
|
|
3874
|
-
}
|
|
3875
|
-
}
|
|
3876
|
-
return result;
|
|
3877
|
-
} catch (error) {
|
|
3878
|
-
const err = error;
|
|
3879
|
-
if (err?.code === -32602) {
|
|
3880
|
-
let errorMessage = err.message || "Validation Error";
|
|
3881
|
-
if (typeof errorMessage === "string" && errorMessage.includes("Invalid arguments for tool")) {
|
|
3882
|
-
try {
|
|
3883
|
-
const parts = errorMessage.split(": [");
|
|
3884
|
-
if (parts.length > 1) {
|
|
3885
|
-
const zodErrors = JSON.parse("[" + parts[1]);
|
|
3886
|
-
const formattedErrors = zodErrors.map((e) => {
|
|
3887
|
-
const path2 = e.path && e.path.length > 0 ? e.path.join(".") : "unknown";
|
|
3888
|
-
return `${path2}: ${e.message || "invalid"}`;
|
|
3889
|
-
}).join("; ");
|
|
3890
|
-
errorMessage = formattedErrors;
|
|
3891
|
-
}
|
|
3892
|
-
} catch {
|
|
3893
|
-
}
|
|
3894
|
-
}
|
|
3895
|
-
const errorResult = {
|
|
3896
|
-
success: false,
|
|
3897
|
-
error: errorMessage,
|
|
3898
|
-
code: "VALIDATION_ERROR",
|
|
3899
|
-
category: "validation",
|
|
3900
|
-
suggestion: "Check input parameters against the tool schema",
|
|
3901
|
-
recoverable: false
|
|
3902
|
-
};
|
|
3903
|
-
const fallbackReq = request2;
|
|
3904
|
-
logAgentIssue({
|
|
3905
|
-
source: "MCP",
|
|
3906
|
-
action: fallbackReq?.params?.name ?? "unknown",
|
|
3907
|
-
input: fallbackReq?.params?.arguments ?? {},
|
|
3908
|
-
error: errorMessage
|
|
3909
|
-
});
|
|
3910
|
-
const enriched = JSON.stringify({
|
|
3911
|
-
...errorResult,
|
|
3912
|
-
_meta: { tokenEstimate: 0 }
|
|
3913
|
-
});
|
|
3914
|
-
const tokenEstimate = Math.ceil(Buffer.byteLength(enriched, "utf8") / 4);
|
|
3915
|
-
const finalText = enriched.replace(
|
|
3916
|
-
'"tokenEstimate":0',
|
|
3917
|
-
`"tokenEstimate":${tokenEstimate}`
|
|
3918
|
-
);
|
|
3919
|
-
return {
|
|
3920
|
-
content: [
|
|
3921
|
-
{
|
|
3922
|
-
type: "text",
|
|
3923
|
-
text: finalText
|
|
3924
|
-
}
|
|
3925
|
-
],
|
|
3926
|
-
structuredContent: errorResult,
|
|
3927
|
-
isError: true
|
|
3928
|
-
};
|
|
3929
|
-
}
|
|
3930
|
-
throw error;
|
|
3931
|
-
}
|
|
3932
|
-
});
|
|
3933
|
-
}
|
|
3538
|
+
attachListToolsInterceptor(requestHandlers, configManager, allToolNames);
|
|
3539
|
+
attachCallToolInterceptor(
|
|
3540
|
+
requestHandlers,
|
|
3541
|
+
context,
|
|
3542
|
+
options,
|
|
3543
|
+
prompts,
|
|
3544
|
+
allTools,
|
|
3545
|
+
allToolNames,
|
|
3546
|
+
customToolHandlerConfig
|
|
3547
|
+
);
|
|
3934
3548
|
return server;
|
|
3935
3549
|
}
|
|
3936
|
-
function startMetricsServer(runtime) {
|
|
3550
|
+
function startMetricsServer(runtime, metricsPort) {
|
|
3937
3551
|
const portStr = process.env["MJ_METRICS_PORT"];
|
|
3938
|
-
const port = portStr ? parseInt(portStr, 10) : 9465;
|
|
3552
|
+
const port = metricsPort ?? (portStr !== void 0 && portStr !== "" ? parseInt(portStr, 10) : 9465);
|
|
3939
3553
|
const server = http.createServer((req, res) => {
|
|
3940
3554
|
if (req.url === "/metrics" && req.method === "GET") {
|
|
3941
3555
|
try {
|
|
@@ -3963,8 +3577,10 @@ function startMetricsServer(runtime) {
|
|
|
3963
3577
|
logger.error("Metrics server error", { error: error instanceof Error ? error.message : String(error), module: "MetricsServer" });
|
|
3964
3578
|
}
|
|
3965
3579
|
});
|
|
3966
|
-
|
|
3967
|
-
|
|
3580
|
+
const rawHost = process.env["MJ_METRICS_HOST"]?.trim();
|
|
3581
|
+
const host = rawHost !== void 0 && rawHost !== "" ? rawHost : "127.0.0.1";
|
|
3582
|
+
server.listen(port, host, () => {
|
|
3583
|
+
logger.info(`Standalone metrics server listening on ${host}:${port}`, { module: "MetricsServer" });
|
|
3968
3584
|
});
|
|
3969
3585
|
return () => {
|
|
3970
3586
|
return new Promise((resolve, reject) => {
|
|
@@ -4002,7 +3618,11 @@ async function createServer2(options) {
|
|
|
4002
3618
|
teamDefaultRole: options.teamDefaultRole,
|
|
4003
3619
|
teamAdmin: options.teamAdmin,
|
|
4004
3620
|
runtime,
|
|
4005
|
-
subscriptionManager: globalSubscriptionManager
|
|
3621
|
+
subscriptionManager: globalSubscriptionManager,
|
|
3622
|
+
archiveOlderThanDays: options.archiveOlderThanDays,
|
|
3623
|
+
pruneOlderThanDays: options.pruneOlderThanDays,
|
|
3624
|
+
hardDeleteOlderThanDays: options.hardDeleteOlderThanDays,
|
|
3625
|
+
pruneImportanceThreshold: options.pruneImportanceThreshold
|
|
4006
3626
|
};
|
|
4007
3627
|
const dispatch = (name, args) => callTool(
|
|
4008
3628
|
name,
|
|
@@ -4031,7 +3651,11 @@ async function createServer2(options) {
|
|
|
4031
3651
|
}
|
|
4032
3652
|
const resources = getResources();
|
|
4033
3653
|
const prompts = getPrompts();
|
|
4034
|
-
configManager.on("changed", () => {
|
|
3654
|
+
configManager.on("changed", (newConfig) => {
|
|
3655
|
+
baseConfig.filterConfig = newConfig.toolFilterConfig;
|
|
3656
|
+
baseConfig.briefingConfig = newConfig.briefingConfig;
|
|
3657
|
+
customToolHandlerConfig.filterConfig = newConfig.toolFilterConfig;
|
|
3658
|
+
customToolHandlerConfig.briefingConfig = newConfig.briefingConfig;
|
|
4035
3659
|
void globalSubscriptionManager.notifyResourceUpdated("memory://briefing");
|
|
4036
3660
|
try {
|
|
4037
3661
|
void globalSubscriptionManager.broadcastNotification("notifications/tools/list_changed");
|
|
@@ -4054,7 +3678,7 @@ async function createServer2(options) {
|
|
|
4054
3678
|
);
|
|
4055
3679
|
let shutdownMetrics;
|
|
4056
3680
|
if (options.metricsExport === "prometheus") {
|
|
4057
|
-
shutdownMetrics = startMetricsServer(runtime);
|
|
3681
|
+
shutdownMetrics = startMetricsServer(runtime, options.metricsPort);
|
|
4058
3682
|
}
|
|
4059
3683
|
if (transport === "stdio") {
|
|
4060
3684
|
const stdioTransport = new StdioServerTransport();
|
|
@@ -4139,4 +3763,4 @@ async function createServer2(options) {
|
|
|
4139
3763
|
}
|
|
4140
3764
|
}
|
|
4141
3765
|
|
|
4142
|
-
export { VectorSearchManager, createServer2 as createServer,
|
|
3766
|
+
export { VectorSearchManager, createServer2 as createServer, initializeDependencies };
|