wingfoil 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +334 -0
- package/dist/cli/error.d.ts +11 -0
- package/dist/cli/error.d.ts.map +1 -0
- package/dist/cli/error.js +27 -0
- package/dist/cli/error.js.map +1 -0
- package/dist/cli/exit.d.ts +19 -0
- package/dist/cli/exit.d.ts.map +1 -0
- package/dist/cli/exit.js +14 -0
- package/dist/cli/exit.js.map +1 -0
- package/dist/cli/index.d.ts +28 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +36 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-command.d.ts +34 -0
- package/dist/cli/init-command.d.ts.map +1 -0
- package/dist/cli/init-command.js +100 -0
- package/dist/cli/init-command.js.map +1 -0
- package/dist/cli/mcp-command.d.ts +24 -0
- package/dist/cli/mcp-command.d.ts.map +1 -0
- package/dist/cli/mcp-command.js +42 -0
- package/dist/cli/mcp-command.js.map +1 -0
- package/dist/cli/output.d.ts +13 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +28 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/program.d.ts +12 -0
- package/dist/cli/program.d.ts.map +1 -0
- package/dist/cli/program.js +279 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/registrar.d.ts +69 -0
- package/dist/cli/registrar.d.ts.map +1 -0
- package/dist/cli/registrar.js +82 -0
- package/dist/cli/registrar.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +45 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/approval-authority.d.ts +83 -0
- package/dist/core/approval-authority.d.ts.map +1 -0
- package/dist/core/approval-authority.js +112 -0
- package/dist/core/approval-authority.js.map +1 -0
- package/dist/core/builtin-asset.d.ts +71 -0
- package/dist/core/builtin-asset.d.ts.map +1 -0
- package/dist/core/builtin-asset.js +137 -0
- package/dist/core/builtin-asset.js.map +1 -0
- package/dist/core/builtin-integrity.d.ts +32 -0
- package/dist/core/builtin-integrity.d.ts.map +1 -0
- package/dist/core/builtin-integrity.js +148 -0
- package/dist/core/builtin-integrity.js.map +1 -0
- package/dist/core/confinement.d.ts +57 -0
- package/dist/core/confinement.d.ts.map +1 -0
- package/dist/core/confinement.js +108 -0
- package/dist/core/confinement.js.map +1 -0
- package/dist/core/context.d.ts +187 -0
- package/dist/core/context.d.ts.map +1 -0
- package/dist/core/context.js +192 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/directive-assign.d.ts +131 -0
- package/dist/core/directive-assign.d.ts.map +1 -0
- package/dist/core/directive-assign.js +352 -0
- package/dist/core/directive-assign.js.map +1 -0
- package/dist/core/directives-list.d.ts +80 -0
- package/dist/core/directives-list.d.ts.map +1 -0
- package/dist/core/directives-list.js +148 -0
- package/dist/core/directives-list.js.map +1 -0
- package/dist/core/exit-code.d.ts +85 -0
- package/dist/core/exit-code.d.ts.map +1 -0
- package/dist/core/exit-code.js +163 -0
- package/dist/core/exit-code.js.map +1 -0
- package/dist/core/git-identity.d.ts +32 -0
- package/dist/core/git-identity.d.ts.map +1 -0
- package/dist/core/git-identity.js +72 -0
- package/dist/core/git-identity.js.map +1 -0
- package/dist/core/index.d.ts +388 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1331 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/init.d.ts +78 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +167 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/loaders.d.ts +174 -0
- package/dist/core/loaders.d.ts.map +1 -0
- package/dist/core/loaders.js +364 -0
- package/dist/core/loaders.js.map +1 -0
- package/dist/core/memory-add-type.d.ts +43 -0
- package/dist/core/memory-add-type.d.ts.map +1 -0
- package/dist/core/memory-add-type.js +151 -0
- package/dist/core/memory-add-type.js.map +1 -0
- package/dist/core/memory-transition.d.ts +111 -0
- package/dist/core/memory-transition.d.ts.map +1 -0
- package/dist/core/memory-transition.js +269 -0
- package/dist/core/memory-transition.js.map +1 -0
- package/dist/core/registry.d.ts +189 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +66 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/relevance.d.ts +86 -0
- package/dist/core/relevance.d.ts.map +1 -0
- package/dist/core/relevance.js +258 -0
- package/dist/core/relevance.js.map +1 -0
- package/dist/core/require-reason.d.ts +27 -0
- package/dist/core/require-reason.d.ts.map +1 -0
- package/dist/core/require-reason.js +95 -0
- package/dist/core/require-reason.js.map +1 -0
- package/dist/core/types.d.ts +45 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +22 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usage-error.d.ts +20 -0
- package/dist/core/usage-error.d.ts.map +1 -0
- package/dist/core/usage-error.js +29 -0
- package/dist/core/usage-error.js.map +1 -0
- package/dist/core/write-guard.d.ts +99 -0
- package/dist/core/write-guard.d.ts.map +1 -0
- package/dist/core/write-guard.js +247 -0
- package/dist/core/write-guard.js.map +1 -0
- package/dist/directives/create.d.ts +47 -0
- package/dist/directives/create.d.ts.map +1 -0
- package/dist/directives/create.js +82 -0
- package/dist/directives/create.js.map +1 -0
- package/dist/directives/index.d.ts +20 -0
- package/dist/directives/index.d.ts.map +1 -0
- package/dist/directives/index.js +32 -0
- package/dist/directives/index.js.map +1 -0
- package/dist/directives/roles-edit.d.ts +53 -0
- package/dist/directives/roles-edit.d.ts.map +1 -0
- package/dist/directives/roles-edit.js +286 -0
- package/dist/directives/roles-edit.js.map +1 -0
- package/dist/directives/schema.d.ts +65 -0
- package/dist/directives/schema.d.ts.map +1 -0
- package/dist/directives/schema.js +68 -0
- package/dist/directives/schema.js.map +1 -0
- package/dist/dna/edit.d.ts +61 -0
- package/dist/dna/edit.d.ts.map +1 -0
- package/dist/dna/edit.js +429 -0
- package/dist/dna/edit.js.map +1 -0
- package/dist/dna/index.d.ts +24 -0
- package/dist/dna/index.d.ts.map +1 -0
- package/dist/dna/index.js +59 -0
- package/dist/dna/index.js.map +1 -0
- package/dist/dna/mutate.d.ts +74 -0
- package/dist/dna/mutate.d.ts.map +1 -0
- package/dist/dna/mutate.js +318 -0
- package/dist/dna/mutate.js.map +1 -0
- package/dist/dna/path.d.ts +175 -0
- package/dist/dna/path.d.ts.map +1 -0
- package/dist/dna/path.js +289 -0
- package/dist/dna/path.js.map +1 -0
- package/dist/dna/roles.d.ts +57 -0
- package/dist/dna/roles.d.ts.map +1 -0
- package/dist/dna/roles.js +50 -0
- package/dist/dna/roles.js.map +1 -0
- package/dist/dna/schema.d.ts +172 -0
- package/dist/dna/schema.d.ts.map +1 -0
- package/dist/dna/schema.js +182 -0
- package/dist/dna/schema.js.map +1 -0
- package/dist/dna/set.d.ts +145 -0
- package/dist/dna/set.d.ts.map +1 -0
- package/dist/dna/set.js +428 -0
- package/dist/dna/set.js.map +1 -0
- package/dist/mcp/dna-resource.d.ts +16 -0
- package/dist/mcp/dna-resource.d.ts.map +1 -0
- package/dist/mcp/dna-resource.js +56 -0
- package/dist/mcp/dna-resource.js.map +1 -0
- package/dist/mcp/index.d.ts +49 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +60 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/memory-resource.d.ts +17 -0
- package/dist/mcp/memory-resource.d.ts.map +1 -0
- package/dist/mcp/memory-resource.js +133 -0
- package/dist/mcp/memory-resource.js.map +1 -0
- package/dist/mcp/prompt.d.ts +48 -0
- package/dist/mcp/prompt.d.ts.map +1 -0
- package/dist/mcp/prompt.js +176 -0
- package/dist/mcp/prompt.js.map +1 -0
- package/dist/mcp/read-only.d.ts +42 -0
- package/dist/mcp/read-only.d.ts.map +1 -0
- package/dist/mcp/read-only.js +93 -0
- package/dist/mcp/read-only.js.map +1 -0
- package/dist/mcp/registrar.d.ts +54 -0
- package/dist/mcp/registrar.d.ts.map +1 -0
- package/dist/mcp/registrar.js +72 -0
- package/dist/mcp/registrar.js.map +1 -0
- package/dist/mcp/server.d.ts +58 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +64 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/workflow-resource.d.ts +17 -0
- package/dist/mcp/workflow-resource.d.ts.map +1 -0
- package/dist/mcp/workflow-resource.js +63 -0
- package/dist/mcp/workflow-resource.js.map +1 -0
- package/dist/memory/add.d.ts +50 -0
- package/dist/memory/add.d.ts.map +1 -0
- package/dist/memory/add.js +131 -0
- package/dist/memory/add.js.map +1 -0
- package/dist/memory/audit.d.ts +177 -0
- package/dist/memory/audit.d.ts.map +1 -0
- package/dist/memory/audit.js +287 -0
- package/dist/memory/audit.js.map +1 -0
- package/dist/memory/commit-message.d.ts +146 -0
- package/dist/memory/commit-message.d.ts.map +1 -0
- package/dist/memory/commit-message.js +203 -0
- package/dist/memory/commit-message.js.map +1 -0
- package/dist/memory/entry.d.ts +56 -0
- package/dist/memory/entry.d.ts.map +1 -0
- package/dist/memory/entry.js +28 -0
- package/dist/memory/entry.js.map +1 -0
- package/dist/memory/frontmatter-edit.d.ts +68 -0
- package/dist/memory/frontmatter-edit.d.ts.map +1 -0
- package/dist/memory/frontmatter-edit.js +333 -0
- package/dist/memory/frontmatter-edit.js.map +1 -0
- package/dist/memory/git-log.d.ts +32 -0
- package/dist/memory/git-log.d.ts.map +1 -0
- package/dist/memory/git-log.js +100 -0
- package/dist/memory/git-log.js.map +1 -0
- package/dist/memory/history.d.ts +142 -0
- package/dist/memory/history.d.ts.map +1 -0
- package/dist/memory/history.js +250 -0
- package/dist/memory/history.js.map +1 -0
- package/dist/memory/index.d.ts +58 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +107 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/query.d.ts +150 -0
- package/dist/memory/query.d.ts.map +1 -0
- package/dist/memory/query.js +307 -0
- package/dist/memory/query.js.map +1 -0
- package/dist/memory/reject.d.ts +11 -0
- package/dist/memory/reject.d.ts.map +1 -0
- package/dist/memory/reject.js +32 -0
- package/dist/memory/reject.js.map +1 -0
- package/dist/memory/schema.d.ts +104 -0
- package/dist/memory/schema.d.ts.map +1 -0
- package/dist/memory/schema.js +161 -0
- package/dist/memory/schema.js.map +1 -0
- package/dist/memory/state-machine.d.ts +166 -0
- package/dist/memory/state-machine.d.ts.map +1 -0
- package/dist/memory/state-machine.js +431 -0
- package/dist/memory/state-machine.js.map +1 -0
- package/dist/memory/submit.d.ts +11 -0
- package/dist/memory/submit.d.ts.map +1 -0
- package/dist/memory/submit.js +44 -0
- package/dist/memory/submit.js.map +1 -0
- package/dist/storage/builtin-directives.d.ts +54 -0
- package/dist/storage/builtin-directives.d.ts.map +1 -0
- package/dist/storage/builtin-directives.js +152 -0
- package/dist/storage/builtin-directives.js.map +1 -0
- package/dist/storage/commit.d.ts +92 -0
- package/dist/storage/commit.d.ts.map +1 -0
- package/dist/storage/commit.js +201 -0
- package/dist/storage/commit.js.map +1 -0
- package/dist/storage/confinement.d.ts +93 -0
- package/dist/storage/confinement.d.ts.map +1 -0
- package/dist/storage/confinement.js +164 -0
- package/dist/storage/confinement.js.map +1 -0
- package/dist/storage/document.d.ts +27 -0
- package/dist/storage/document.d.ts.map +1 -0
- package/dist/storage/document.js +52 -0
- package/dist/storage/document.js.map +1 -0
- package/dist/storage/errors.d.ts +36 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +46 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/frontmatter.d.ts +27 -0
- package/dist/storage/frontmatter.d.ts.map +1 -0
- package/dist/storage/frontmatter.js +37 -0
- package/dist/storage/frontmatter.js.map +1 -0
- package/dist/storage/git-root.d.ts +17 -0
- package/dist/storage/git-root.d.ts.map +1 -0
- package/dist/storage/git-root.js +49 -0
- package/dist/storage/git-root.js.map +1 -0
- package/dist/storage/index.d.ts +29 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +66 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/init-state.d.ts +9 -0
- package/dist/storage/init-state.d.ts.map +1 -0
- package/dist/storage/init-state.js +24 -0
- package/dist/storage/init-state.js.map +1 -0
- package/dist/storage/layout.d.ts +51 -0
- package/dist/storage/layout.d.ts.map +1 -0
- package/dist/storage/layout.js +88 -0
- package/dist/storage/layout.js.map +1 -0
- package/dist/storage/memory-path.d.ts +72 -0
- package/dist/storage/memory-path.d.ts.map +1 -0
- package/dist/storage/memory-path.js +127 -0
- package/dist/storage/memory-path.js.map +1 -0
- package/dist/storage/snapshot.d.ts +16 -0
- package/dist/storage/snapshot.d.ts.map +1 -0
- package/dist/storage/snapshot.js +72 -0
- package/dist/storage/snapshot.js.map +1 -0
- package/dist/storage/templates.d.ts +88 -0
- package/dist/storage/templates.d.ts.map +1 -0
- package/dist/storage/templates.js +422 -0
- package/dist/storage/templates.js.map +1 -0
- package/dist/validation/error-mapper.d.ts +16 -0
- package/dist/validation/error-mapper.d.ts.map +1 -0
- package/dist/validation/error-mapper.js +18 -0
- package/dist/validation/error-mapper.js.map +1 -0
- package/dist/validation/errors.d.ts +69 -0
- package/dist/validation/errors.d.ts.map +1 -0
- package/dist/validation/errors.js +69 -0
- package/dist/validation/errors.js.map +1 -0
- package/dist/validation/id.d.ts +16 -0
- package/dist/validation/id.d.ts.map +1 -0
- package/dist/validation/id.js +177 -0
- package/dist/validation/id.js.map +1 -0
- package/dist/validation/index.d.ts +18 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +37 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/secret-scan.d.ts +129 -0
- package/dist/validation/secret-scan.d.ts.map +1 -0
- package/dist/validation/secret-scan.js +367 -0
- package/dist/validation/secret-scan.js.map +1 -0
- package/dist/validation/two-pass.d.ts +24 -0
- package/dist/validation/two-pass.d.ts.map +1 -0
- package/dist/validation/two-pass.js +24 -0
- package/dist/validation/two-pass.js.map +1 -0
- package/dist/validation/warning.d.ts +31 -0
- package/dist/validation/warning.d.ts.map +1 -0
- package/dist/validation/warning.js +81 -0
- package/dist/validation/warning.js.map +1 -0
- package/dist/validation/yaml.d.ts +9 -0
- package/dist/validation/yaml.d.ts.map +1 -0
- package/dist/validation/yaml.js +30 -0
- package/dist/validation/yaml.js.map +1 -0
- package/dist/workflow/index.d.ts +10 -0
- package/dist/workflow/index.d.ts.map +1 -0
- package/dist/workflow/index.js +16 -0
- package/dist/workflow/index.js.map +1 -0
- package/dist/workflow/schema.d.ts +89 -0
- package/dist/workflow/schema.d.ts.map +1 -0
- package/dist/workflow/schema.js +73 -0
- package/dist/workflow/schema.js.map +1 -0
- package/package.json +78 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.startMcpServer = exports.createMcpServer = exports.roleSessionPromptName = exports.registerRolePrompts = exports.WRITE_INTENT_META_KEY = exports.WRITE_REFUSAL_MESSAGE = exports.resourceNotFoundError = exports.refuseIfWriteIntent = exports.registerWriteRefusalHandler = exports.WORKFLOW_DOCUMENT_URI_TEMPLATE = exports.WORKFLOWS_COLLECTION_URI = exports.registerWorkflowResources = exports.DNA_SECTION_URI_TEMPLATE = exports.DNA_WHOLE_URI = exports.registerDnaResources = exports.MEMORY_DOCUMENT_URI_TEMPLATE = exports.MEMORY_COLLECTION_URI_TEMPLATE = exports.registerMemoryResources = exports.deriveMcpResourceUri = exports.deriveMcpToolName = exports.registerCoreModules = exports.MODULE_NAME = void 0;
|
|
4
|
+
exports.registerReadOnlyResources = registerReadOnlyResources;
|
|
5
|
+
/** This module's `dna.yaml` name (`mcp-server`) — the stable identifier surfaces and tests key it by. */
|
|
6
|
+
exports.MODULE_NAME = 'mcp-server';
|
|
7
|
+
var registrar_1 = require("./registrar");
|
|
8
|
+
Object.defineProperty(exports, "registerCoreModules", { enumerable: true, get: function () { return registrar_1.registerCoreModules; } });
|
|
9
|
+
Object.defineProperty(exports, "deriveMcpToolName", { enumerable: true, get: function () { return registrar_1.deriveMcpToolName; } });
|
|
10
|
+
Object.defineProperty(exports, "deriveMcpResourceUri", { enumerable: true, get: function () { return registrar_1.deriveMcpResourceUri; } });
|
|
11
|
+
var memory_resource_1 = require("./memory-resource");
|
|
12
|
+
Object.defineProperty(exports, "registerMemoryResources", { enumerable: true, get: function () { return memory_resource_1.registerMemoryResources; } });
|
|
13
|
+
Object.defineProperty(exports, "MEMORY_COLLECTION_URI_TEMPLATE", { enumerable: true, get: function () { return memory_resource_1.MEMORY_COLLECTION_URI_TEMPLATE; } });
|
|
14
|
+
Object.defineProperty(exports, "MEMORY_DOCUMENT_URI_TEMPLATE", { enumerable: true, get: function () { return memory_resource_1.MEMORY_DOCUMENT_URI_TEMPLATE; } });
|
|
15
|
+
var dna_resource_1 = require("./dna-resource");
|
|
16
|
+
Object.defineProperty(exports, "registerDnaResources", { enumerable: true, get: function () { return dna_resource_1.registerDnaResources; } });
|
|
17
|
+
Object.defineProperty(exports, "DNA_WHOLE_URI", { enumerable: true, get: function () { return dna_resource_1.DNA_WHOLE_URI; } });
|
|
18
|
+
Object.defineProperty(exports, "DNA_SECTION_URI_TEMPLATE", { enumerable: true, get: function () { return dna_resource_1.DNA_SECTION_URI_TEMPLATE; } });
|
|
19
|
+
var workflow_resource_1 = require("./workflow-resource");
|
|
20
|
+
Object.defineProperty(exports, "registerWorkflowResources", { enumerable: true, get: function () { return workflow_resource_1.registerWorkflowResources; } });
|
|
21
|
+
Object.defineProperty(exports, "WORKFLOWS_COLLECTION_URI", { enumerable: true, get: function () { return workflow_resource_1.WORKFLOWS_COLLECTION_URI; } });
|
|
22
|
+
Object.defineProperty(exports, "WORKFLOW_DOCUMENT_URI_TEMPLATE", { enumerable: true, get: function () { return workflow_resource_1.WORKFLOW_DOCUMENT_URI_TEMPLATE; } });
|
|
23
|
+
var read_only_1 = require("./read-only");
|
|
24
|
+
Object.defineProperty(exports, "registerWriteRefusalHandler", { enumerable: true, get: function () { return read_only_1.registerWriteRefusalHandler; } });
|
|
25
|
+
Object.defineProperty(exports, "refuseIfWriteIntent", { enumerable: true, get: function () { return read_only_1.refuseIfWriteIntent; } });
|
|
26
|
+
Object.defineProperty(exports, "resourceNotFoundError", { enumerable: true, get: function () { return read_only_1.resourceNotFoundError; } });
|
|
27
|
+
Object.defineProperty(exports, "WRITE_REFUSAL_MESSAGE", { enumerable: true, get: function () { return read_only_1.WRITE_REFUSAL_MESSAGE; } });
|
|
28
|
+
Object.defineProperty(exports, "WRITE_INTENT_META_KEY", { enumerable: true, get: function () { return read_only_1.WRITE_INTENT_META_KEY; } });
|
|
29
|
+
// task-039-mcp-prompts-role-based-infra adds the spec-004 §3 Prompts channel (REQ-INT-02): one
|
|
30
|
+
// `{role}-session` prompt per `dna.yaml` role, embedding that role's directives, resolved per
|
|
31
|
+
// request. Deliberately NOT folded into `registerReadOnlyResources` below (that call is spec-004 §2's
|
|
32
|
+
// Resources bundle); `createMcpServer` registers it alongside that bundle since
|
|
33
|
+
// `task-058-mcp-prompts-role-based` (P5.2.2, spec-014 §3). See `./prompt.ts`.
|
|
34
|
+
var prompt_1 = require("./prompt");
|
|
35
|
+
Object.defineProperty(exports, "registerRolePrompts", { enumerable: true, get: function () { return prompt_1.registerRolePrompts; } });
|
|
36
|
+
Object.defineProperty(exports, "roleSessionPromptName", { enumerable: true, get: function () { return prompt_1.roleSessionPromptName; } });
|
|
37
|
+
const dna_resource_2 = require("./dna-resource");
|
|
38
|
+
const memory_resource_2 = require("./memory-resource");
|
|
39
|
+
const read_only_2 = require("./read-only");
|
|
40
|
+
const workflow_resource_2 = require("./workflow-resource");
|
|
41
|
+
/**
|
|
42
|
+
* Wire the full spec-004 §2 read-only Resources channel onto `server` in one call: the Memory, DNA,
|
|
43
|
+
* and Workflow Resources plus the structural `resources/write` refusal. Deliberately does not touch
|
|
44
|
+
* `registerCoreModules`/`CORE_MODULES` — a caller building a real server still calls that separately
|
|
45
|
+
* for the (today, zero) Tools/legacy-shaped Resources it derives from `src/core`.
|
|
46
|
+
*/
|
|
47
|
+
function registerReadOnlyResources(server, options) {
|
|
48
|
+
(0, read_only_2.registerWriteRefusalHandler)(server);
|
|
49
|
+
(0, memory_resource_2.registerMemoryResources)(server, options);
|
|
50
|
+
(0, dna_resource_2.registerDnaResources)(server, options);
|
|
51
|
+
(0, workflow_resource_2.registerWorkflowResources)(server, options);
|
|
52
|
+
}
|
|
53
|
+
// The production stdio entry point (task-030-implement-mcp-resources, spec-014-mcp-server-entry-point):
|
|
54
|
+
// `createMcpServer` builds a real `McpServer` with the read-only Resources and Prompts channels above wired on,
|
|
55
|
+
// `startMcpServer` connects it over `StdioServerTransport`. Exported here so the `bin`/`wingfoil mcp`
|
|
56
|
+
// seam imports the whole `src/mcp` surface from one module.
|
|
57
|
+
var server_1 = require("./server");
|
|
58
|
+
Object.defineProperty(exports, "createMcpServer", { enumerable: true, get: function () { return server_1.createMcpServer; } });
|
|
59
|
+
Object.defineProperty(exports, "startMcpServer", { enumerable: true, get: function () { return server_1.startMcpServer; } });
|
|
60
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";;;AA8EA,8DAKC;AA7DD,yGAAyG;AAC5F,QAAA,WAAW,GAAG,YAAqB,CAAC;AAEjD,yCAA2F;AAAlF,gHAAA,mBAAmB,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAGrE,qDAI2B;AAHzB,0HAAA,uBAAuB,OAAA;AACvB,iIAAA,8BAA8B,OAAA;AAC9B,+HAAA,4BAA4B,OAAA;AAI9B,+CAA+F;AAAtF,oHAAA,oBAAoB,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAGtE,yDAI6B;AAH3B,8HAAA,yBAAyB,OAAA;AACzB,6HAAA,wBAAwB,OAAA;AACxB,mIAAA,8BAA8B,OAAA;AAIhC,yCAMqB;AALnB,wHAAA,2BAA2B,OAAA;AAC3B,gHAAA,mBAAmB,OAAA;AACnB,kHAAA,qBAAqB,OAAA;AACrB,kHAAA,qBAAqB,OAAA;AACrB,kHAAA,qBAAqB,OAAA;AAGvB,+FAA+F;AAC/F,8FAA8F;AAC9F,sGAAsG;AACtG,gFAAgF;AAChF,8EAA8E;AAC9E,mCAAsE;AAA7D,6GAAA,mBAAmB,OAAA;AAAE,+GAAA,qBAAqB,OAAA;AAGnD,iDAAsD;AACtD,uDAA4D;AAC5D,2CAA0D;AAC1D,2DAAgE;AAQhE;;;;;GAKG;AACH,SAAgB,yBAAyB,CAAC,MAAiB,EAAE,OAAyC;IACpG,IAAA,uCAA2B,EAAC,MAAM,CAAC,CAAC;IACpC,IAAA,yCAAuB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,IAAA,mCAAoB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,IAAA,6CAAyB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC7C,CAAC;AAED,wGAAwG;AACxG,gHAAgH;AAChH,sGAAsG;AACtG,4DAA4D;AAC5D,mCAA2D;AAAlD,yGAAA,eAAe,OAAA;AAAE,wGAAA,cAAc,OAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/** Options for {@link registerMemoryResources}. */
|
|
3
|
+
export interface RegisterMemoryResourcesOptions {
|
|
4
|
+
/** Resolves the project root each Memory Resource read is served from (an ambient concern, not a request parameter). */
|
|
5
|
+
readonly resolveRoot: () => string;
|
|
6
|
+
}
|
|
7
|
+
/** spec-004 §2.1 — collection listing (frontmatter only, no `{id}`). */
|
|
8
|
+
export declare const MEMORY_COLLECTION_URI_TEMPLATE = "wingfoil://memory/{type}";
|
|
9
|
+
/** spec-004 §2.1 — a single Memory document (full frontmatter + body). */
|
|
10
|
+
export declare const MEMORY_DOCUMENT_URI_TEMPLATE = "wingfoil://memory/{type}/{id}";
|
|
11
|
+
/**
|
|
12
|
+
* Register both Memory Resources on `server`. Reads `.wingfoil/memory.yaml` fresh on every request
|
|
13
|
+
* (no server-lifetime cache) — same "no cold-start cost, no stale-config either" contract the rest of
|
|
14
|
+
* `src/mcp`'s Resources already follow.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerMemoryResources(server: McpServer, options: RegisterMemoryResourcesOptions): void;
|
|
17
|
+
//# sourceMappingURL=memory-resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-resource.d.ts","sourceRoot":"","sources":["../../src/mcp/memory-resource.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASzE,mDAAmD;AACnD,MAAM,WAAW,8BAA8B;IAC7C,wHAAwH;IACxH,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;CACpC;AAED,wEAAwE;AACxE,eAAO,MAAM,8BAA8B,6BAA6B,CAAC;AACzE,0EAA0E;AAC1E,eAAO,MAAM,4BAA4B,kCAAkC,CAAC;AAE5E;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,8BAA8B,GAAG,IAAI,CAyFxG"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MEMORY_DOCUMENT_URI_TEMPLATE = exports.MEMORY_COLLECTION_URI_TEMPLATE = void 0;
|
|
4
|
+
exports.registerMemoryResources = registerMemoryResources;
|
|
5
|
+
/**
|
|
6
|
+
* `wingfoil://memory/{type}` (collection) and `wingfoil://memory/{type}/{id}` (single document)
|
|
7
|
+
* Resources — task-011-mcp-resources-read-only, REQ-INT-01, spec-004-mcp-surface-contract §2.1/§2.2.
|
|
8
|
+
* This REPLACES task-009-mcp-resource-fetch-latency's placeholder `wingfoil://memory/{id}` adapter
|
|
9
|
+
* (that module's own doc-comment flagged itself as non-conformant and due for replacement, not
|
|
10
|
+
* extension, precisely because its single `{id}` segment collides with spec-004's `{type}` segment —
|
|
11
|
+
* see `src/memory/query.ts`'s `findMemoryDocumentById` doc comment for the full history). REQ-PERF-04
|
|
12
|
+
* coverage is ported forward onto this conformant URI, not dropped (`test/mcp/resource-latency.test.ts`).
|
|
13
|
+
*
|
|
14
|
+
* The two handlers differ deliberately on **archived content** (REQ-STATE-06, the set ratified by
|
|
15
|
+
* `dl-028-archived-states-excluded-from-context`, fixed by
|
|
16
|
+
* `task-069-fix-archived-excluded-from-agent-context` for `bug-010-deprecated-reaches-agent-context`).
|
|
17
|
+
* The **collection** withholds documents whose `status` is archived (`{deprecated, superseded}`), so
|
|
18
|
+
* an agent browsing a type is never handed a replaced or retired decision as if it were current — the
|
|
19
|
+
* same guarantee `wingfoil://memory/search` and default `memory search` already give. The
|
|
20
|
+
* **single-document** Resource does **not** filter: addressing a document by its id is explicit
|
|
21
|
+
* retrieval, which REQ-STATE-06 preserves ("remaining present on disk and in git history"). It is the
|
|
22
|
+
* MCP counterpart of `memory search --status deprecated` on the CLI, whose own opt-in
|
|
23
|
+
* (`memorySearchFn`, `src/core/index.ts`) is gated on the same `isArchivedStatus`. `draft` is not
|
|
24
|
+
* archived and is withheld by neither handler; only the *context* path (`src/core/relevance.ts`)
|
|
25
|
+
* excludes it as well.
|
|
26
|
+
*
|
|
27
|
+
* Both handlers are thin adapters over `src/memory/query.ts`'s primitives
|
|
28
|
+
* (`listMemoryDocumentsByType`, `findMemoryDocumentByTypeAndId`, both new in this task) — no scanning
|
|
29
|
+
* logic lives here, only URI-variable extraction, the read-only guard (`./read-only`), and MCP
|
|
30
|
+
* envelope shaping. Registered directly via `McpServer.registerResource` rather than through
|
|
31
|
+
* `registerCoreModules`'s `CoreModule[]` registry (`src/core/registry.ts`): no `memoryShow`/`memoryList`
|
|
32
|
+
* `CoreOperation` exists in `src/core`'s production `CORE_MODULES` yet (see `src/core/index.ts`'s SCOPE
|
|
33
|
+
* note). This is the same spec-006-core-domain-api §4 (Parity rule) deviation task-009 already
|
|
34
|
+
* documented for this surface — acceptable because it is read-only and has no CLI/Tool counterpart to
|
|
35
|
+
* keep in parity with, to be reconciled (or re-confirmed as permanent) by
|
|
36
|
+
* task-030-implement-mcp-resources, which also wires a real `StdioServerTransport`/`wingfoil mcp`
|
|
37
|
+
* entry point around this registrar (out of this task's scope — see this task's Execution Notes for
|
|
38
|
+
* the task-011-vs-task-030 boundary).
|
|
39
|
+
*/
|
|
40
|
+
const path_1 = require("path");
|
|
41
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
42
|
+
const core_1 = require("../core");
|
|
43
|
+
const query_1 = require("../memory/query");
|
|
44
|
+
const state_machine_1 = require("../memory/state-machine");
|
|
45
|
+
const storage_1 = require("../storage");
|
|
46
|
+
const read_only_1 = require("./read-only");
|
|
47
|
+
/** spec-004 §2.1 — collection listing (frontmatter only, no `{id}`). */
|
|
48
|
+
exports.MEMORY_COLLECTION_URI_TEMPLATE = 'wingfoil://memory/{type}';
|
|
49
|
+
/** spec-004 §2.1 — a single Memory document (full frontmatter + body). */
|
|
50
|
+
exports.MEMORY_DOCUMENT_URI_TEMPLATE = 'wingfoil://memory/{type}/{id}';
|
|
51
|
+
/**
|
|
52
|
+
* Register both Memory Resources on `server`. Reads `.wingfoil/memory.yaml` fresh on every request
|
|
53
|
+
* (no server-lifetime cache) — same "no cold-start cost, no stale-config either" contract the rest of
|
|
54
|
+
* `src/mcp`'s Resources already follow.
|
|
55
|
+
*/
|
|
56
|
+
function registerMemoryResources(server, options) {
|
|
57
|
+
const collectionTemplate = new mcp_js_1.ResourceTemplate(exports.MEMORY_COLLECTION_URI_TEMPLATE, { list: undefined });
|
|
58
|
+
server.registerResource('memory.list', collectionTemplate, { description: 'Memory documents of one type, frontmatter only (read-only)' }, async (uri, variables, extra) => {
|
|
59
|
+
(0, read_only_1.refuseIfWriteIntent)(extra._meta);
|
|
60
|
+
const root = options.resolveRoot();
|
|
61
|
+
const memoryYaml = (0, core_1.loadMemoryYaml)(root);
|
|
62
|
+
// `variables.type` is typed `string | string[]` (the SDK's generic `Variables` shape), but
|
|
63
|
+
// this template's `{type}` variable has no `*`/`+` explode modifier, so a match always binds
|
|
64
|
+
// it to a single string (see the SDK's `UriTemplate.match`) — the `string[]` case is
|
|
65
|
+
// unreachable here (mirrors task-009's own `{id}` reasoning for this same SDK behaviour).
|
|
66
|
+
const type = variables.type;
|
|
67
|
+
if (!(type in memoryYaml.types))
|
|
68
|
+
throw (0, read_only_1.resourceNotFoundError)(`memory/${type}`);
|
|
69
|
+
// REQ-STATE-06 / `dl-028-archived-states-excluded-from-context` (`bug-010`): this collection is
|
|
70
|
+
// an agent-facing read path, so archived documents — `{deprecated, superseded}`, per the shared
|
|
71
|
+
// `isArchivedStatus` — are withheld here. The filter sits at this call site rather than inside
|
|
72
|
+
// `listMemoryDocumentsByType` because that primitive also serves the single-document Resource's
|
|
73
|
+
// consumers, which must still see archived content; the *policy* belongs to the surface, while
|
|
74
|
+
// the *definition* of "archived" stays single, as dl-028 requires. `status` is already
|
|
75
|
+
// projected to `string | undefined` by the primitive — the shape `isArchivedStatus` takes — so
|
|
76
|
+
// a non-string frontmatter `status` is never archived. `filter` preserves the primitive's
|
|
77
|
+
// id-ascending order (REQ-SYS-07).
|
|
78
|
+
const summaries = (0, query_1.listMemoryDocumentsByType)(root, memoryYaml, type)
|
|
79
|
+
.filter(({ status }) => !(0, state_machine_1.isArchivedStatus)(status))
|
|
80
|
+
.map(({ id, title, status, tags }) => ({
|
|
81
|
+
id,
|
|
82
|
+
title,
|
|
83
|
+
status,
|
|
84
|
+
tags,
|
|
85
|
+
}));
|
|
86
|
+
return (0, read_only_1.jsonResourceResult)(uri, summaries);
|
|
87
|
+
});
|
|
88
|
+
const documentTemplate = new mcp_js_1.ResourceTemplate(exports.MEMORY_DOCUMENT_URI_TEMPLATE, { list: undefined });
|
|
89
|
+
server.registerResource('memory.show', documentTemplate, { description: 'one Memory document, full content + metadata (read-only)' }, async (uri, variables, extra) => {
|
|
90
|
+
(0, read_only_1.refuseIfWriteIntent)(extra._meta);
|
|
91
|
+
const root = options.resolveRoot();
|
|
92
|
+
const memoryYaml = (0, core_1.loadMemoryYaml)(root);
|
|
93
|
+
const type = variables.type;
|
|
94
|
+
const id = variables.id;
|
|
95
|
+
if (!(type in memoryYaml.types))
|
|
96
|
+
throw (0, read_only_1.resourceNotFoundError)(`memory/${type}/${id}`);
|
|
97
|
+
const doc = (0, query_1.findMemoryDocumentByTypeAndId)(root, memoryYaml, type, id);
|
|
98
|
+
if (!doc)
|
|
99
|
+
throw (0, read_only_1.resourceNotFoundError)(`memory/${type}/${id}`);
|
|
100
|
+
// spec-004 §2.2 wants the *full file content* (frontmatter + body), not the re-serialized
|
|
101
|
+
// parsed frontmatter `loadMemoryDocumentSummary` already split apart for the scan — so this
|
|
102
|
+
// re-reads the resolved path's raw bytes via `storage.readDocument` (still a read primitive
|
|
103
|
+
// reuse, not a reimplemented scan: the scan itself already happened inside
|
|
104
|
+
// `findMemoryDocumentByTypeAndId`).
|
|
105
|
+
const fullText = (0, storage_1.readDocument)((0, path_1.join)(root, doc.path));
|
|
106
|
+
// spec-004 §2.2 depicts `metadata` as a sibling of `uri`/`mimeType`/`text`, i.e. flat on the
|
|
107
|
+
// result — not nested inside a `contents[]` entry (whose shape the real MCP wire schema fixes
|
|
108
|
+
// to `{uri, mimeType, text|blob, _meta?}`, `ReadResourceResultSchema`'s `contents` union in the
|
|
109
|
+
// SDK's `types.d.ts`). The top-level `ReadResourceResultSchema` object itself is passthrough
|
|
110
|
+
// (`z.core.$loose`), so this extra top-level `metadata` key is preserved end to end (server ->
|
|
111
|
+
// wire -> client parse) without needing a non-standard content shape. Built via an
|
|
112
|
+
// untyped-at-the-literal `result` variable (assigned, not returned directly) so TypeScript's
|
|
113
|
+
// excess-property check — which only applies to object literals in a contextually-typed
|
|
114
|
+
// position — never triggers on this extra key.
|
|
115
|
+
const result = {
|
|
116
|
+
contents: [
|
|
117
|
+
{
|
|
118
|
+
uri: uri.toString(),
|
|
119
|
+
mimeType: 'text/markdown',
|
|
120
|
+
text: fullText,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
metadata: {
|
|
124
|
+
id: doc.frontmatter.id,
|
|
125
|
+
type: doc.frontmatter.type,
|
|
126
|
+
status: doc.frontmatter.status,
|
|
127
|
+
title: doc.frontmatter.title,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
return result;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=memory-resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-resource.js","sourceRoot":"","sources":["../../src/mcp/memory-resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/** Options for {@link registerRolePrompts}. */
|
|
3
|
+
export interface RegisterRolePromptsOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the project root each `prompts/get` is served from — invoked per request by the prompt
|
|
6
|
+
* handlers, and once by {@link registerRolePrompts} itself to read the DNA role catalogue (see
|
|
7
|
+
* that function's note on the two different timings spec-004 §3.1 and §3.2 mandate).
|
|
8
|
+
*/
|
|
9
|
+
readonly resolveRoot: () => string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `{role}-session` — spec-004 §3.1's prompt naming convention, as a function so the wire-visible name
|
|
13
|
+
* is derived in exactly one place (the registrar here, and any later consumer such as
|
|
14
|
+
* `task-058-mcp-prompts-role-based`) rather than spelled out per call site.
|
|
15
|
+
*/
|
|
16
|
+
export declare function roleSessionPromptName(role: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Register the role-scoped Prompts channel: one `{role}-session` Prompt per role in `dna.yaml`'s
|
|
19
|
+
* `team.roles` catalogue (spec-004 §3.1), each embedding that role's directives (§3.2), and a refusal
|
|
20
|
+
* for a session prompt naming a role DNA does not declare (P5.2.2's undefined-role scenario).
|
|
21
|
+
*
|
|
22
|
+
* **Nothing is read here — every read happens per request** (task-058 second pass, rejection `ff13321`).
|
|
23
|
+
* spec-014 §2 requires `createMcpServer` to perform "no I/O at construction time beyond wiring
|
|
24
|
+
* handlers", so the DNA role catalogue is loaded inside the `prompts/list` / `prompts/get` handlers,
|
|
25
|
+
* next to the per-request directive resolution spec-004 §3.2 already mandated (REQ-INT-02's "a newly
|
|
26
|
+
* assigned directive appears on the next session start"). Two consequences, both deliberate:
|
|
27
|
+
*
|
|
28
|
+
* - `wingfoil mcp` starts in a git root with no `.wingfoil/dna.yaml` (e.g. a repository that has not
|
|
29
|
+
* run `wingfoil init`) exactly as the Resources channel always has; the missing DNA surfaces as the
|
|
30
|
+
* error of each Prompts request instead of aborting the process start.
|
|
31
|
+
* - spec-004 §3.1's "fixed set derived from DNA at server start" is served as the DNA role set **at
|
|
32
|
+
* request time**: a role added to `dna.yaml` while the server runs is listed on the next
|
|
33
|
+
* `prompts/list`. That tension between spec-014 §2 and spec-004 §3.1 is open for the approver.
|
|
34
|
+
*
|
|
35
|
+
* **Why the low-level handlers, not `McpServer.registerPrompt`.** The high-level API answers an
|
|
36
|
+
* unregistered name with its own `Prompt <name> not found` before any WingFoil code runs, so the BDD's
|
|
37
|
+
* `no prompt for undefined role 'wizard'` cannot be expressed through it (and it would need the role
|
|
38
|
+
* set at registration time). This function therefore owns `prompts/list` and `prompts/get` on
|
|
39
|
+
* `server.server` — the same pattern `./read-only.ts`'s `registerWriteRefusalHandler` uses — and a
|
|
40
|
+
* later `registerPrompt` on the same server fails loudly ("A request handler for prompts/list already
|
|
41
|
+
* exists") instead of being silently shadowed. A requested name that is not `{role}-session`-shaped
|
|
42
|
+
* keeps the SDK-equivalent `Prompt <name> not found` wording: it names no role, so it is not an
|
|
43
|
+
* undefined-role request, and it is answered without reading anything.
|
|
44
|
+
*
|
|
45
|
+
* Call before the server is connected (MCP capabilities cannot be registered after connecting).
|
|
46
|
+
*/
|
|
47
|
+
export declare function registerRolePrompts(server: McpServer, options: RegisterRolePromptsOptions): void;
|
|
48
|
+
//# sourceMappingURL=prompt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../../src/mcp/prompt.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,+CAA+C;AAC/C,MAAM,WAAW,0BAA0B;IACzC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,MAAM,CAAC;CACpC;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE1D;AAgGD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAkBhG"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.roleSessionPromptName = roleSessionPromptName;
|
|
4
|
+
exports.registerRolePrompts = registerRolePrompts;
|
|
5
|
+
/**
|
|
6
|
+
* The role-scoped MCP **Prompts** channel — `{role}-session` prompts that embed the role's assigned
|
|
7
|
+
* directives (task-039-mcp-prompts-role-based-infra, **REQ-INT-02**,
|
|
8
|
+
* `spec-004-mcp-surface-contract` §3). This is the third and last channel type spec-004 §1 names,
|
|
9
|
+
* alongside the Resources channel (`./index.ts`'s `registerReadOnlyResources`, task-011/task-030) and
|
|
10
|
+
* the still-unshipped Tools channel (P5.2.3, v0.4).
|
|
11
|
+
*
|
|
12
|
+
* **Wired into the production server by `task-058-mcp-prompts-role-based` (P5.2.2).** task-039
|
|
13
|
+
* shipped this registrar as infra, exactly like `task-011-mcp-resources-read-only` did for Resources;
|
|
14
|
+
* task-058 adds it to `createMcpServer` (`./server.ts`) per `spec-014-mcp-server-entry-point` §3
|
|
15
|
+
* ("when Prompts ship (P5.2.2, v0.2), it adds their registrar") and implements the undefined-role
|
|
16
|
+
* refusal of `p5-interaction/P5.2.2-mcp-prompts.feature` (`no prompt for undefined role '<role>'`).
|
|
17
|
+
*
|
|
18
|
+
* **Read-only (spec-004 §3.3).** A prompt handler reads `dna.yaml`, `roles.yaml` and the directive
|
|
19
|
+
* files and returns text. There is no code path in this module that writes, and it registers no Tool —
|
|
20
|
+
* so Prompts cannot become the agent write path REQ-SEC-05 reserves for validated Tools
|
|
21
|
+
* (`test/mcp/role-prompts.test.ts` asserts both: files byte-for-byte unchanged after a round trip, and
|
|
22
|
+
* `tools/list` still unroutable on a Prompts-only server).
|
|
23
|
+
*
|
|
24
|
+
* **Nothing is re-implemented here.** Role catalogue via `loadDnaYaml`, bindings via `loadRolesYaml`,
|
|
25
|
+
* directive files via `loadDirectives`, and the role → directive resolution itself via
|
|
26
|
+
* `resolveRoleDirectives` (`src/core/context.ts`, task-037) — the same canonical resolver
|
|
27
|
+
* `assembleExecutionContext` uses, so REQ-INT-02's "100% of R's currently assigned directives" and
|
|
28
|
+
* REQ-STATE-05's "100% of the role's directives and 0 of another role's" stay one guarantee rather than
|
|
29
|
+
* two that can drift apart. The only thing that resolver does not supply is a directive's **body**
|
|
30
|
+
* (`loadDirectives` returns `{path, frontmatter}`), which {@link readDirectiveBody} reads with the
|
|
31
|
+
* `storage` primitives `./memory-resource.ts` already uses for the same reason.
|
|
32
|
+
*/
|
|
33
|
+
const path_1 = require("path");
|
|
34
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
35
|
+
const core_1 = require("../core");
|
|
36
|
+
const storage_1 = require("../storage");
|
|
37
|
+
/** spec-004 §3.1's prompt-name suffix: one prompt per role, named `{role}-session`. Module-local —
|
|
38
|
+
* {@link roleSessionPromptName} is the only naming API this channel exposes. */
|
|
39
|
+
const ROLE_PROMPT_NAME_SUFFIX = '-session';
|
|
40
|
+
/**
|
|
41
|
+
* `{role}-session` — spec-004 §3.1's prompt naming convention, as a function so the wire-visible name
|
|
42
|
+
* is derived in exactly one place (the registrar here, and any later consumer such as
|
|
43
|
+
* `task-058-mcp-prompts-role-based`) rather than spelled out per call site.
|
|
44
|
+
*/
|
|
45
|
+
function roleSessionPromptName(role) {
|
|
46
|
+
return `${role}${ROLE_PROMPT_NAME_SUFFIX}`;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A `prompts/get` refusal whose JSON-RPC error carries `message` **verbatim** with code `InvalidParams`
|
|
50
|
+
* (-32602, the code the MCP SDK itself uses for an unknown prompt name).
|
|
51
|
+
*
|
|
52
|
+
* Deliberately a plain `Error` with a `code`, not the SDK's `McpError`: `McpError`'s constructor
|
|
53
|
+
* prefixes its own message with `MCP error <code>: `, and the SDK serializes `error.message` as-is, so a
|
|
54
|
+
* client — which adds that prefix again when it rebuilds the error — would read the prefix twice.
|
|
55
|
+
*/
|
|
56
|
+
function promptRequestError(message) {
|
|
57
|
+
return Object.assign(new Error(message), { code: types_js_1.ErrorCode.InvalidParams });
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The P5.2.2 undefined-role refusal, verbatim from `p5-interaction/P5.2.2-mcp-prompts.feature`
|
|
61
|
+
* (Scenario "Error - requesting a prompt for an undefined role"): `no prompt for undefined role '<role>'`.
|
|
62
|
+
*/
|
|
63
|
+
function undefinedRolePromptError(role) {
|
|
64
|
+
return promptRequestError(`no prompt for undefined role '${role}'`);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Read one directive file's body — everything after its YAML frontmatter block, trimmed of
|
|
68
|
+
* surrounding blank lines so the composed prompt is a pure function of the file's content and not of
|
|
69
|
+
* how many trailing newlines its author happened to leave (REQ-SYS-07).
|
|
70
|
+
*
|
|
71
|
+
* `file.path` is root-relative to `.wingfoil/` (`loadDirectives` stores it as `directives/<...>.md`),
|
|
72
|
+
* so it is resolved against `<root>/.wingfoil` here. A file with no frontmatter block cannot reach
|
|
73
|
+
* this function — `loadDirectives` rejects that case with `E_MISSING_FRONTMATTER` before any
|
|
74
|
+
* directive is returned — but `splitFrontmatter` degrades to "whole document is body" anyway.
|
|
75
|
+
*/
|
|
76
|
+
function readDirectiveBody(root, file) {
|
|
77
|
+
return (0, storage_1.splitFrontmatter)((0, storage_1.readDocument)((0, path_1.join)(root, storage_1.WINGFOIL_DIR, file.path))).body.trim();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Compose the prompt text for `role` from its already-resolved directives — spec-004 §3.2's shape: a
|
|
81
|
+
* `# Role: {role}` header followed by one `## Directive: {id}` block per directive, each carrying that
|
|
82
|
+
* directive's full body.
|
|
83
|
+
*
|
|
84
|
+
* The heading uses `frontmatter.id`, not `name`: spec-004 §3.2's own example headings read
|
|
85
|
+
* `## Directive: code-quality` / `security-secrets`, which are the `id` values of the real directive
|
|
86
|
+
* files (`code-quality.md` carries `id: code-quality`, `name: "Code Quality"`), and `roles.yaml` binds
|
|
87
|
+
* by id.
|
|
88
|
+
*
|
|
89
|
+
* Block order is `resolveRoleDirectives`' — deduplicated by id and sorted id-ascending per
|
|
90
|
+
* `spec-012-context-loader-relevance-filtering` §5 / REQ-SYS-07 — rather than the "own directives,
|
|
91
|
+
* then globals" grouping spec-004 §3.2's illustrative example happens to print. §3.2 states the
|
|
92
|
+
* resolution itself as a *set* union (`roles.yaml[R].directives` ∪ `global`), which fixes membership,
|
|
93
|
+
* not sequence; an explicit total order is what makes two runs byte-identical.
|
|
94
|
+
*/
|
|
95
|
+
function composeRolePromptText(role, directives) {
|
|
96
|
+
const blocks = directives.map(({ id, body }) => `## Directive: ${id}\n${body}`);
|
|
97
|
+
return [`# Role: ${role}`, ...blocks].join('\n\n');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Build the `prompts/get` result for `role`: resolve its directives against the project **now**
|
|
101
|
+
* (spec-004 §3.2 — per request, never cached from server boot) and compose them into one message.
|
|
102
|
+
*
|
|
103
|
+
* `resolveRoleDirectives` also returns operator diagnostics (`warnings`, e.g. dl-029's "no directives
|
|
104
|
+
* assigned to role 'intern'"); they are **not** embedded in the prompt. spec-004 §3.2 defines the
|
|
105
|
+
* prompt payload as role header + directive blocks, and folding a diagnostic into agent-facing
|
|
106
|
+
* instruction text would put an unratified string into the payload — the same reasoning
|
|
107
|
+
* `src/core/context.ts` gives for keeping its own warnings out of the serialized context envelope.
|
|
108
|
+
*/
|
|
109
|
+
function buildRolePrompt(root, role) {
|
|
110
|
+
const { directives } = (0, core_1.resolveRoleDirectives)((0, core_1.loadDirectives)(root), (0, core_1.loadRolesYaml)(root), role);
|
|
111
|
+
const text = composeRolePromptText(role, directives.map((file) => ({ id: file.frontmatter.id, body: readDirectiveBody(root, file) })));
|
|
112
|
+
// MCP's `PromptMessage.role` is the two-value enum `"user" | "assistant"` (the SDK's
|
|
113
|
+
// `PromptMessageSchema`), so spec-004 §3.2's illustrative `role: "system"` is not representable on
|
|
114
|
+
// this protocol by any conformant server; `user` is the only wire role that can deliver
|
|
115
|
+
// instructional content. Open as `dl-039-spec-004-prompt-example-corrections`.
|
|
116
|
+
return { messages: [{ role: 'user', content: { type: 'text', text } }] };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The DNA role catalogue as `dna.yaml` declares it **now**, in declaration order. Read per request (see
|
|
120
|
+
* {@link registerRolePrompts}); a missing or invalid `dna.yaml` throws the loader's own error, which the
|
|
121
|
+
* SDK returns to the client as that request's error — the same way `wingfoil://dna` reports it.
|
|
122
|
+
*/
|
|
123
|
+
function loadRoleNames(root) {
|
|
124
|
+
return (0, core_1.loadDnaYaml)(root).team.roles.map(({ name }) => name);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Register the role-scoped Prompts channel: one `{role}-session` Prompt per role in `dna.yaml`'s
|
|
128
|
+
* `team.roles` catalogue (spec-004 §3.1), each embedding that role's directives (§3.2), and a refusal
|
|
129
|
+
* for a session prompt naming a role DNA does not declare (P5.2.2's undefined-role scenario).
|
|
130
|
+
*
|
|
131
|
+
* **Nothing is read here — every read happens per request** (task-058 second pass, rejection `ff13321`).
|
|
132
|
+
* spec-014 §2 requires `createMcpServer` to perform "no I/O at construction time beyond wiring
|
|
133
|
+
* handlers", so the DNA role catalogue is loaded inside the `prompts/list` / `prompts/get` handlers,
|
|
134
|
+
* next to the per-request directive resolution spec-004 §3.2 already mandated (REQ-INT-02's "a newly
|
|
135
|
+
* assigned directive appears on the next session start"). Two consequences, both deliberate:
|
|
136
|
+
*
|
|
137
|
+
* - `wingfoil mcp` starts in a git root with no `.wingfoil/dna.yaml` (e.g. a repository that has not
|
|
138
|
+
* run `wingfoil init`) exactly as the Resources channel always has; the missing DNA surfaces as the
|
|
139
|
+
* error of each Prompts request instead of aborting the process start.
|
|
140
|
+
* - spec-004 §3.1's "fixed set derived from DNA at server start" is served as the DNA role set **at
|
|
141
|
+
* request time**: a role added to `dna.yaml` while the server runs is listed on the next
|
|
142
|
+
* `prompts/list`. That tension between spec-014 §2 and spec-004 §3.1 is open for the approver.
|
|
143
|
+
*
|
|
144
|
+
* **Why the low-level handlers, not `McpServer.registerPrompt`.** The high-level API answers an
|
|
145
|
+
* unregistered name with its own `Prompt <name> not found` before any WingFoil code runs, so the BDD's
|
|
146
|
+
* `no prompt for undefined role 'wizard'` cannot be expressed through it (and it would need the role
|
|
147
|
+
* set at registration time). This function therefore owns `prompts/list` and `prompts/get` on
|
|
148
|
+
* `server.server` — the same pattern `./read-only.ts`'s `registerWriteRefusalHandler` uses — and a
|
|
149
|
+
* later `registerPrompt` on the same server fails loudly ("A request handler for prompts/list already
|
|
150
|
+
* exists") instead of being silently shadowed. A requested name that is not `{role}-session`-shaped
|
|
151
|
+
* keeps the SDK-equivalent `Prompt <name> not found` wording: it names no role, so it is not an
|
|
152
|
+
* undefined-role request, and it is answered without reading anything.
|
|
153
|
+
*
|
|
154
|
+
* Call before the server is connected (MCP capabilities cannot be registered after connecting).
|
|
155
|
+
*/
|
|
156
|
+
function registerRolePrompts(server, options) {
|
|
157
|
+
server.server.registerCapabilities({ prompts: {} });
|
|
158
|
+
server.server.setRequestHandler(types_js_1.ListPromptsRequestSchema, () => ({
|
|
159
|
+
prompts: loadRoleNames(options.resolveRoot()).map((role) => ({
|
|
160
|
+
name: roleSessionPromptName(role),
|
|
161
|
+
description: `session instructions for the '${role}' role, embedding its assigned directives (read-only)`,
|
|
162
|
+
})),
|
|
163
|
+
}));
|
|
164
|
+
server.server.setRequestHandler(types_js_1.GetPromptRequestSchema, (request) => {
|
|
165
|
+
const { name } = request.params;
|
|
166
|
+
if (!name.endsWith(ROLE_PROMPT_NAME_SUFFIX)) {
|
|
167
|
+
throw promptRequestError(`Prompt ${name} not found`);
|
|
168
|
+
}
|
|
169
|
+
const role = name.slice(0, -ROLE_PROMPT_NAME_SUFFIX.length);
|
|
170
|
+
const root = options.resolveRoot();
|
|
171
|
+
if (!loadRoleNames(root).includes(role))
|
|
172
|
+
throw undefinedRolePromptError(role);
|
|
173
|
+
return buildRolePrompt(root, role);
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt.js","sourceRoot":"","sources":["../../src/mcp/prompt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/** spec-004 §2.3's exact, verbatim write-refusal string. */
|
|
3
|
+
export declare const WRITE_REFUSAL_MESSAGE = "resources are read-only";
|
|
4
|
+
/**
|
|
5
|
+
* The `_meta` key WingFoil's own bespoke Resource handlers treat as "this `resources/read` call is
|
|
6
|
+
* carrying a write intent/payload" — not an MCP-defined field (see module doc). Any truthy value
|
|
7
|
+
* under this key refuses the read identically to an unsupported `resources/write` call.
|
|
8
|
+
*/
|
|
9
|
+
export declare const WRITE_INTENT_META_KEY = "wingfoil/write-intent";
|
|
10
|
+
/**
|
|
11
|
+
* Refuse a read carrying a write intent (see module doc, case 2). Called first, before any other
|
|
12
|
+
* work, by every bespoke Resource handler this task registers — so the refusal is unconditional and
|
|
13
|
+
* precedes any read/scan/file-access, never a check bolted on after a read already started.
|
|
14
|
+
*/
|
|
15
|
+
export declare function refuseIfWriteIntent(meta: Record<string, unknown> | undefined): void;
|
|
16
|
+
/** spec-004 §2.2's "resource not found" shape — distinct wording from the write refusal above, per
|
|
17
|
+
* the BDD scenario (`P5.2.1-mcp-resources.feature`) `resource not found: {identifier}` format. */
|
|
18
|
+
export declare function resourceNotFoundError(identifier: string): Error;
|
|
19
|
+
/**
|
|
20
|
+
* The `ReadResourceResult` shape every JSON-bodied Resource in `memory-resource.ts` (collection
|
|
21
|
+
* listing), `dna-resource.ts`, and `workflow-resource.ts` returns — one `contents[]` entry,
|
|
22
|
+
* `mimeType: 'application/json'`, `value` serialized as its `text`. Factored here purely to avoid
|
|
23
|
+
* re-deriving the same three-line envelope at each of those five call sites; `memory-resource.ts`'s
|
|
24
|
+
* single-document read builds its own `text/markdown` + `metadata` result directly, since that shape
|
|
25
|
+
* differs (spec-004 §2.2).
|
|
26
|
+
*/
|
|
27
|
+
export declare function jsonResourceResult(uri: URL, value: unknown): {
|
|
28
|
+
contents: [{
|
|
29
|
+
uri: string;
|
|
30
|
+
mimeType: string;
|
|
31
|
+
text: string;
|
|
32
|
+
}];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Install the `resources/write` refusal (see module doc, case 1). MCP's own protocol does not define
|
|
36
|
+
* `resources/write` at all — no client capability or built-in handler exists for it — so this method
|
|
37
|
+
* name has no default routing on the server; without this handler such a call would fail with a
|
|
38
|
+
* generic JSON-RPC "Method not found" rather than spec-004 §2.3's specific refusal text. There is no
|
|
39
|
+
* write path here to gate: the handler always throws, unconditionally.
|
|
40
|
+
*/
|
|
41
|
+
export declare function registerWriteRefusalHandler(server: McpServer): void;
|
|
42
|
+
//# sourceMappingURL=read-only.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-only.d.ts","sourceRoot":"","sources":["../../src/mcp/read-only.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,4DAA4D;AAC5D,eAAO,MAAM,qBAAqB,4BAA4B,CAAC;AAE/D;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,IAAI,CAInF;AAED;kGACkG;AAClG,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,KAAK,CAE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,GAAG;IAAE,QAAQ,EAAE,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAU5H;AAUD;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAInE"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WRITE_INTENT_META_KEY = exports.WRITE_REFUSAL_MESSAGE = void 0;
|
|
4
|
+
exports.refuseIfWriteIntent = refuseIfWriteIntent;
|
|
5
|
+
exports.resourceNotFoundError = resourceNotFoundError;
|
|
6
|
+
exports.jsonResourceResult = jsonResourceResult;
|
|
7
|
+
exports.registerWriteRefusalHandler = registerWriteRefusalHandler;
|
|
8
|
+
/**
|
|
9
|
+
* Shared conventions for every bespoke Resource this task registers (`memory-resource`,
|
|
10
|
+
* `dna-resource`, `workflow-resource`): the read-only-channel guarantee — spec-004-mcp-surface-contract
|
|
11
|
+
* §2.3's verbatim write-refusal string, plus the standard MCP "resource not found" shape (§2.2) — and
|
|
12
|
+
* the common JSON-bodied result envelope, factored once so every module refuses/reports/shapes
|
|
13
|
+
* identically rather than re-deriving the same strings/shape per handler
|
|
14
|
+
* (task-011-mcp-resources-read-only, REQ-INT-01 / REQ-SEC-05).
|
|
15
|
+
*
|
|
16
|
+
* Two distinct write-attempt shapes must be refused (spec-004 §2.3):
|
|
17
|
+
*
|
|
18
|
+
* 1. An unsupported `resources/write` JSON-RPC call — {@link registerWriteRefusalHandler} installs a
|
|
19
|
+
* handler for that literal method name (which the MCP protocol itself does not define at all — see
|
|
20
|
+
* the SDK's `types.d.ts`: there is no `ResourceWrite*` schema) that unconditionally refuses. This
|
|
21
|
+
* is structural, not a gated check: the handler has no branch that could ever succeed.
|
|
22
|
+
* 2. A `resources/read` request "carrying a write intent/payload". The real wire schema
|
|
23
|
+
* (`ReadResourceRequestSchema`) strips any unknown key placed directly on `params` before a
|
|
24
|
+
* handler ever sees it (Zod `$strip` mode) — the one part of that schema that is *not* stripped is
|
|
25
|
+
* the nested `_meta` sub-object, which the SDK itself models as passthrough (`$loose`) for
|
|
26
|
+
* out-of-band caller metadata. {@link WRITE_INTENT_META_KEY} is WingFoil's own convention (MCP
|
|
27
|
+
* defines no write-intent field) for exercising that one channel a misbehaving/malicious client
|
|
28
|
+
* could still smuggle a write payload through; {@link refuseIfWriteIntent} is called by every
|
|
29
|
+
* bespoke read handler before it does anything else.
|
|
30
|
+
*/
|
|
31
|
+
const zod_1 = require("zod");
|
|
32
|
+
/** spec-004 §2.3's exact, verbatim write-refusal string. */
|
|
33
|
+
exports.WRITE_REFUSAL_MESSAGE = 'resources are read-only';
|
|
34
|
+
/**
|
|
35
|
+
* The `_meta` key WingFoil's own bespoke Resource handlers treat as "this `resources/read` call is
|
|
36
|
+
* carrying a write intent/payload" — not an MCP-defined field (see module doc). Any truthy value
|
|
37
|
+
* under this key refuses the read identically to an unsupported `resources/write` call.
|
|
38
|
+
*/
|
|
39
|
+
exports.WRITE_INTENT_META_KEY = 'wingfoil/write-intent';
|
|
40
|
+
/**
|
|
41
|
+
* Refuse a read carrying a write intent (see module doc, case 2). Called first, before any other
|
|
42
|
+
* work, by every bespoke Resource handler this task registers — so the refusal is unconditional and
|
|
43
|
+
* precedes any read/scan/file-access, never a check bolted on after a read already started.
|
|
44
|
+
*/
|
|
45
|
+
function refuseIfWriteIntent(meta) {
|
|
46
|
+
if (meta && exports.WRITE_INTENT_META_KEY in meta) {
|
|
47
|
+
throw new Error(exports.WRITE_REFUSAL_MESSAGE);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** spec-004 §2.2's "resource not found" shape — distinct wording from the write refusal above, per
|
|
51
|
+
* the BDD scenario (`P5.2.1-mcp-resources.feature`) `resource not found: {identifier}` format. */
|
|
52
|
+
function resourceNotFoundError(identifier) {
|
|
53
|
+
return new Error(`resource not found: ${identifier}`);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The `ReadResourceResult` shape every JSON-bodied Resource in `memory-resource.ts` (collection
|
|
57
|
+
* listing), `dna-resource.ts`, and `workflow-resource.ts` returns — one `contents[]` entry,
|
|
58
|
+
* `mimeType: 'application/json'`, `value` serialized as its `text`. Factored here purely to avoid
|
|
59
|
+
* re-deriving the same three-line envelope at each of those five call sites; `memory-resource.ts`'s
|
|
60
|
+
* single-document read builds its own `text/markdown` + `metadata` result directly, since that shape
|
|
61
|
+
* differs (spec-004 §2.2).
|
|
62
|
+
*/
|
|
63
|
+
function jsonResourceResult(uri, value) {
|
|
64
|
+
return {
|
|
65
|
+
contents: [
|
|
66
|
+
{
|
|
67
|
+
uri: uri.toString(),
|
|
68
|
+
mimeType: 'application/json',
|
|
69
|
+
text: JSON.stringify(value),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** The raw JSON-RPC shape of an (unsupported) `resources/write` request — just enough to extract the
|
|
75
|
+
* method literal `setRequestHandler` needs; `params` is never inspected, since every call is refused
|
|
76
|
+
* unconditionally regardless of its payload. */
|
|
77
|
+
const ResourcesWriteRequestSchema = zod_1.z.object({
|
|
78
|
+
method: zod_1.z.literal('resources/write'),
|
|
79
|
+
params: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Install the `resources/write` refusal (see module doc, case 1). MCP's own protocol does not define
|
|
83
|
+
* `resources/write` at all — no client capability or built-in handler exists for it — so this method
|
|
84
|
+
* name has no default routing on the server; without this handler such a call would fail with a
|
|
85
|
+
* generic JSON-RPC "Method not found" rather than spec-004 §2.3's specific refusal text. There is no
|
|
86
|
+
* write path here to gate: the handler always throws, unconditionally.
|
|
87
|
+
*/
|
|
88
|
+
function registerWriteRefusalHandler(server) {
|
|
89
|
+
server.server.setRequestHandler(ResourcesWriteRequestSchema, async () => {
|
|
90
|
+
throw new Error(exports.WRITE_REFUSAL_MESSAGE);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=read-only.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-only.js","sourceRoot":"","sources":["../../src/mcp/read-only.ts"],"names":[],"mappings":";;;AA0CA,kDAIC;AAID,sDAEC;AAUD,gDAUC;AAiBD,kEAIC;AA7FD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,6BAAwB;AAIxB,4DAA4D;AAC/C,QAAA,qBAAqB,GAAG,yBAAyB,CAAC;AAE/D;;;;GAIG;AACU,QAAA,qBAAqB,GAAG,uBAAuB,CAAC;AAE7D;;;;GAIG;AACH,SAAgB,mBAAmB,CAAC,IAAyC;IAC3E,IAAI,IAAI,IAAI,6BAAqB,IAAI,IAAI,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,6BAAqB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC;AAED;kGACkG;AAClG,SAAgB,qBAAqB,CAAC,UAAkB;IACtD,OAAO,IAAI,KAAK,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;AACxD,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,kBAAkB,CAAC,GAAQ,EAAE,KAAc;IACzD,OAAO;QACL,QAAQ,EAAE;YACR;gBACE,GAAG,EAAE,GAAG,CAAC,QAAQ,EAAE;gBACnB,QAAQ,EAAE,kBAAkB;gBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;aAC5B;SACF;KACF,CAAC;AACJ,CAAC;AAED;;gDAEgD;AAChD,MAAM,2BAA2B,GAAG,OAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,EAAE,OAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC;IACpC,MAAM,EAAE,OAAC,CAAC,MAAM,CAAC,OAAC,CAAC,MAAM,EAAE,EAAE,OAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrD,CAAC,CAAC;AAEH;;;;;;GAMG;AACH,SAAgB,2BAA2B,CAAC,MAAiB;IAC3D,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,IAAI,KAAK,CAAC,6BAAqB,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC"}
|