@mappies/miniwat-mcp-core 0.11.0 → 0.13.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/dist/index.cjs CHANGED
@@ -123,7 +123,7 @@ var ToolDispatcher = class {
123
123
  return tool?.getInputText?.(input) || "";
124
124
  }
125
125
  // Evaluates the dispatch-boundary input guardrail against the concrete invocation. Returns
126
- // undefined when execution may proceed; a ToolAuthorizationResult (defer/deny) when it is
126
+ // undefined when execution may proceed; a ToolAuthorizationBlock (defer/deny) when it is
127
127
  // blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the
128
128
  // deferred result is returned (persist-before-respond); if a tool can defer but no sink is
129
129
  // supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/entities/toolDispatcher.ts"],"sourcesContent":["export { ToolDispatcher } from './entities/toolDispatcher';\nexport { McpConfig } from './interfaces/mcpConfig';\nexport { McpServer } from './interfaces/mcpServer';\nexport { McpToolImport } from './interfaces/mcpToolImport';","import { Tool, McpServer, ToolInputGuardrail, ToolInputGuardrailFactory, ToolOutputGuardrail, ToolOutputGuardrailFactory, ToolOutputGuardrailResult, DefaultOutputGuardrail, ToolAuthorizationOptions, ToolAuthorizationResult, PendingToolCall } from \"@mappies/miniwat-agent-core\";\nimport { randomUUID } from \"crypto\";\nimport { McpConfig } from \"../interfaces/mcpConfig\";\n\nexport class ToolDispatcher\n{\n private tools:Tool[]\n private servers:McpServer[]\n\n constructor(private mcp?:McpConfig)\n {\n this.tools = []\n this.servers = []\n }\n\n async init()\n {\n if(this.tools.length !== 0) return;\n\n this.tools = [];\n\n if(this.mcp === undefined) return;\n\n for(let server of Object.keys(this.mcp.mcpServers))\n {\n let service = this.mcp.mcpServers[server]\n\n this.servers.push(new McpServer({name: server, ...service}))\n \n const pck = await import(service.package);\n for(let t of service.tools)\n {\n // Host intent: a tool explicitly disabled in config is excluded here, during\n // discovery, so the planner never sees it.\n if(t.enabled === false) continue;\n\n let definition = pck[t.import];\n let tool = new Tool({ \n name: definition.name, \n displayName: definition.displayName,\n description: definition.description, \n schema: definition.inputSchema, \n func: pck[definition.name],\n service: server,\n getInputText: pck[`${definition.name}InputText`],\n readOnly: definition.readOnly === true,\n requiresApproval: definition.requiresApproval === true,\n inputGuardrail: this.resolveInputGuardrail(server, definition, pck),\n outputGuardrail: this.resolveOutputGuardrail(server, definition, pck)\n })\n \n this.tools.push(tool)\n }\n }\n }\n\n // Resolves a tool definition's named inputGuardrail from the OWNING package's INPUT_GUARDRAILS\n // factory export. Validation is loud and happens at init — never deferred to tool invocation —\n // so the app cannot reach agent.stream() with a partially resolved tool.\n private resolveInputGuardrail(server:string, definition:any, pck:any): ToolInputGuardrail | undefined\n {\n const name:string | undefined = definition.inputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.INPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' but its package exports no INPUT_GUARDRAILS.`);\n }\n\n const factory:ToolInputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' not found in the package's INPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolInputGuardrail.`);\n }\n\n return guardrail;\n }\n\n // Resolves a tool definition's named outputGuardrail from the OWNING package's OUTPUT_GUARDRAILS\n // factory export. Validated loudly at init, mirroring resolveInputGuardrail.\n private resolveOutputGuardrail(server:string, definition:any, pck:any): ToolOutputGuardrail | undefined\n {\n const name:string | undefined = definition.outputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.OUTPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' but its package exports no OUTPUT_GUARDRAILS.`);\n }\n\n const factory:ToolOutputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' not found in the package's OUTPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolOutputGuardrail.`);\n }\n\n return guardrail;\n }\n\n async list(): Promise<Tool[]>\n {\n await this.init()\n return this.tools;\n }\n\n async listServers(): Promise<McpServer[]>\n {\n await this.init()\n return this.servers;\n }\n\n async get(name:string): Promise<Tool|undefined>\n {\n await this.init()\n return this.tools.find(tool => tool.name === name);\n }\n\n async getInputText(name:string, input?:object): Promise<string>\n {\n const tool = await this.get(name);\n\n return tool?.getInputText?.(input) || '';\n }\n\n // Evaluates the dispatch-boundary input guardrail against the concrete invocation. Returns\n // undefined when execution may proceed; a ToolAuthorizationResult (defer/deny) when it is\n // blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the\n // deferred result is returned (persist-before-respond); if a tool can defer but no sink is\n // supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.\n private async authorize(name:string, input:object | undefined, tool:Tool, auth?:ToolAuthorizationOptions): Promise<ToolAuthorizationResult | undefined>\n {\n if(!auth)\n {\n return undefined;\n }\n\n const decision = auth.guardrail.evaluate({ toolName: name, arguments: input }, auth.context);\n\n if(decision.status === \"allow\")\n {\n return undefined;\n }\n\n if(decision.status === \"deny\")\n {\n return { authorization: \"deny\", succeeded: false, text: decision.reason };\n }\n\n // defer: preserve the invocation for a later approval turn.\n const pending:PendingToolCall = { id: randomUUID(), name, arguments: (input ?? {}) as Record<string, unknown>, status: \"pending\" };\n\n if(!auth.onDeferred)\n {\n // Fail closed: a deferral we cannot durably record must not surface as a usable\n // approval-required result.\n return { authorization: \"deny\", succeeded: false, text: \"Authorization is unavailable for this operation.\" };\n }\n\n // Persist-before-respond: the pending call is durably recorded before the deferred result\n // (and the approval request derived from it) can reach the user. A rejection propagates.\n await auth.onDeferred(pending);\n\n return { authorization: \"defer\", succeeded: false, text: decision.reason, pending };\n }\n\n async execute(name:string, input?:object, auth?:ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationResult>\n {\n const tool = await this.get(name);\n \n if(!tool)\n {\n throw new Error(`Tool ${name} could not be found.`);\n }\n\n if(!tool.func)\n {\n throw new Error(`Tool ${name} does not have a function.`);\n }\n\n // Dispatch-boundary authorization runs BEFORE the tool, so an unauthorized side effect never\n // executes — regardless of which planner (or no planner) proposed it. The guardrail is the\n // existing allow/defer/deny input guardrail evaluated against the CONCRETE invocation.\n const authorization = await this.authorize(name, input, tool, auth);\n\n if(authorization)\n {\n return authorization;\n }\n\n let result;\n\n try\n {\n result = await tool.func(input);\n }\n catch(e:any)\n {\n throw new Error(`Failed to run ${name} tool: ${e?.message}`);\n }\n\n // The output guardrail is the SINGLE post-execution authority. It runs here, at the\n // provider-agnostic chokepoint, so the real result is classified BEFORE any provider relays\n // it. Its verdict carries both the user-facing text and the semantic status to the caller;\n // a `blocked` verdict withholds the real result in favor of a replacement. A tool without an\n // explicit guardrail gets the framework default (a normal return is a success).\n const guardrail = tool.outputGuardrail ?? new DefaultOutputGuardrail();\n\n return guardrail.evaluate(result);\n }\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/entities/toolDispatcher.ts"],"sourcesContent":["export { ToolDispatcher } from './entities/toolDispatcher';\nexport { McpConfig } from './interfaces/mcpConfig';\nexport { McpServer } from './interfaces/mcpServer';\nexport { McpToolImport } from './interfaces/mcpToolImport';","import { Tool, McpServer, ToolInputGuardrail, ToolInputGuardrailFactory, ToolOutputGuardrail, ToolOutputGuardrailFactory, ToolOutputGuardrailResult, DefaultOutputGuardrail, ToolAuthorizationOptions, ToolAuthorizationBlock, PendingToolCall } from \"@mappies/miniwat-agent-core\";\nimport { randomUUID } from \"crypto\";\nimport { McpConfig } from \"../interfaces/mcpConfig\";\n\nexport class ToolDispatcher\n{\n private tools:Tool[]\n private servers:McpServer[]\n\n constructor(private mcp?:McpConfig)\n {\n this.tools = []\n this.servers = []\n }\n\n async init()\n {\n if(this.tools.length !== 0) return;\n\n this.tools = [];\n\n if(this.mcp === undefined) return;\n\n for(let server of Object.keys(this.mcp.mcpServers))\n {\n let service = this.mcp.mcpServers[server]\n\n this.servers.push(new McpServer({name: server, ...service}))\n \n const pck = await import(service.package);\n for(let t of service.tools)\n {\n // Host intent: a tool explicitly disabled in config is excluded here, during\n // discovery, so the planner never sees it.\n if(t.enabled === false) continue;\n\n let definition = pck[t.import];\n let tool = new Tool({ \n name: definition.name, \n displayName: definition.displayName,\n description: definition.description, \n schema: definition.inputSchema, \n func: pck[definition.name],\n service: server,\n getInputText: pck[`${definition.name}InputText`],\n readOnly: definition.readOnly === true,\n requiresApproval: definition.requiresApproval === true,\n inputGuardrail: this.resolveInputGuardrail(server, definition, pck),\n outputGuardrail: this.resolveOutputGuardrail(server, definition, pck)\n })\n \n this.tools.push(tool)\n }\n }\n }\n\n // Resolves a tool definition's named inputGuardrail from the OWNING package's INPUT_GUARDRAILS\n // factory export. Validation is loud and happens at init — never deferred to tool invocation —\n // so the app cannot reach agent.stream() with a partially resolved tool.\n private resolveInputGuardrail(server:string, definition:any, pck:any): ToolInputGuardrail | undefined\n {\n const name:string | undefined = definition.inputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.INPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' but its package exports no INPUT_GUARDRAILS.`);\n }\n\n const factory:ToolInputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' not found in the package's INPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolInputGuardrail.`);\n }\n\n return guardrail;\n }\n\n // Resolves a tool definition's named outputGuardrail from the OWNING package's OUTPUT_GUARDRAILS\n // factory export. Validated loudly at init, mirroring resolveInputGuardrail.\n private resolveOutputGuardrail(server:string, definition:any, pck:any): ToolOutputGuardrail | undefined\n {\n const name:string | undefined = definition.outputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.OUTPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' but its package exports no OUTPUT_GUARDRAILS.`);\n }\n\n const factory:ToolOutputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' not found in the package's OUTPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolOutputGuardrail.`);\n }\n\n return guardrail;\n }\n\n async list(): Promise<Tool[]>\n {\n await this.init()\n return this.tools;\n }\n\n async listServers(): Promise<McpServer[]>\n {\n await this.init()\n return this.servers;\n }\n\n async get(name:string): Promise<Tool|undefined>\n {\n await this.init()\n return this.tools.find(tool => tool.name === name);\n }\n\n async getInputText(name:string, input?:object): Promise<string>\n {\n const tool = await this.get(name);\n\n return tool?.getInputText?.(input) || '';\n }\n\n // Evaluates the dispatch-boundary input guardrail against the concrete invocation. Returns\n // undefined when execution may proceed; a ToolAuthorizationBlock (defer/deny) when it is\n // blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the\n // deferred result is returned (persist-before-respond); if a tool can defer but no sink is\n // supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.\n private async authorize(name:string, input:object | undefined, tool:Tool, auth?:ToolAuthorizationOptions): Promise<ToolAuthorizationBlock | undefined>\n {\n if(!auth)\n {\n return undefined;\n }\n\n const decision = auth.guardrail.evaluate({ toolName: name, arguments: input }, auth.context);\n\n if(decision.status === \"allow\")\n {\n return undefined;\n }\n\n if(decision.status === \"deny\")\n {\n return { authorization: \"deny\", succeeded: false, text: decision.reason };\n }\n\n // defer: preserve the invocation for a later approval turn.\n const pending:PendingToolCall = { id: randomUUID(), name, arguments: (input ?? {}) as Record<string, unknown>, status: \"pending\" };\n\n if(!auth.onDeferred)\n {\n // Fail closed: a deferral we cannot durably record must not surface as a usable\n // approval-required result.\n return { authorization: \"deny\", succeeded: false, text: \"Authorization is unavailable for this operation.\" };\n }\n\n // Persist-before-respond: the pending call is durably recorded before the deferred result\n // (and the approval request derived from it) can reach the user. A rejection propagates.\n await auth.onDeferred(pending);\n\n return { authorization: \"defer\", succeeded: false, text: decision.reason, pending };\n }\n\n async execute(name:string, input?:object, auth?:ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationBlock>\n {\n const tool = await this.get(name);\n \n if(!tool)\n {\n throw new Error(`Tool ${name} could not be found.`);\n }\n\n if(!tool.func)\n {\n throw new Error(`Tool ${name} does not have a function.`);\n }\n\n // Dispatch-boundary authorization runs BEFORE the tool, so an unauthorized side effect never\n // executes — regardless of which planner (or no planner) proposed it. The guardrail is the\n // existing allow/defer/deny input guardrail evaluated against the CONCRETE invocation.\n const authorization = await this.authorize(name, input, tool, auth);\n\n if(authorization)\n {\n return authorization;\n }\n\n let result;\n\n try\n {\n result = await tool.func(input);\n }\n catch(e:any)\n {\n throw new Error(`Failed to run ${name} tool: ${e?.message}`);\n }\n\n // The output guardrail is the SINGLE post-execution authority. It runs here, at the\n // provider-agnostic chokepoint, so the real result is classified BEFORE any provider relays\n // it. Its verdict carries both the user-facing text and the semantic status to the caller;\n // a `blocked` verdict withholds the real result in favor of a replacement. A tool without an\n // explicit guardrail gets the framework default (a normal return is a success).\n const guardrail = tool.outputGuardrail ?? new DefaultOutputGuardrail();\n\n return guardrail.evaluate(result);\n }\n}"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { Tool, McpServer as McpServer$1, ToolAuthorizationOptions, ToolOutputGuardrailResult, ToolAuthorizationResult } from '@mappies/miniwat-agent-core';
1
+ import { Tool, McpServer as McpServer$1, ToolAuthorizationOptions, ToolOutputGuardrailResult, ToolAuthorizationBlock } from '@mappies/miniwat-agent-core';
2
2
 
3
3
  interface McpToolImport {
4
4
  import: string;
@@ -29,7 +29,7 @@ declare class ToolDispatcher {
29
29
  get(name: string): Promise<Tool | undefined>;
30
30
  getInputText(name: string, input?: object): Promise<string>;
31
31
  private authorize;
32
- execute(name: string, input?: object, auth?: ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationResult>;
32
+ execute(name: string, input?: object, auth?: ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationBlock>;
33
33
  }
34
34
 
35
35
  export { type McpConfig, type McpServer, type McpToolImport, ToolDispatcher };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Tool, McpServer as McpServer$1, ToolAuthorizationOptions, ToolOutputGuardrailResult, ToolAuthorizationResult } from '@mappies/miniwat-agent-core';
1
+ import { Tool, McpServer as McpServer$1, ToolAuthorizationOptions, ToolOutputGuardrailResult, ToolAuthorizationBlock } from '@mappies/miniwat-agent-core';
2
2
 
3
3
  interface McpToolImport {
4
4
  import: string;
@@ -29,7 +29,7 @@ declare class ToolDispatcher {
29
29
  get(name: string): Promise<Tool | undefined>;
30
30
  getInputText(name: string, input?: object): Promise<string>;
31
31
  private authorize;
32
- execute(name: string, input?: object, auth?: ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationResult>;
32
+ execute(name: string, input?: object, auth?: ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationBlock>;
33
33
  }
34
34
 
35
35
  export { type McpConfig, type McpServer, type McpToolImport, ToolDispatcher };
package/dist/index.js CHANGED
@@ -97,7 +97,7 @@ var ToolDispatcher = class {
97
97
  return tool?.getInputText?.(input) || "";
98
98
  }
99
99
  // Evaluates the dispatch-boundary input guardrail against the concrete invocation. Returns
100
- // undefined when execution may proceed; a ToolAuthorizationResult (defer/deny) when it is
100
+ // undefined when execution may proceed; a ToolAuthorizationBlock (defer/deny) when it is
101
101
  // blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the
102
102
  // deferred result is returned (persist-before-respond); if a tool can defer but no sink is
103
103
  // supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/entities/toolDispatcher.ts"],"sourcesContent":["import { Tool, McpServer, ToolInputGuardrail, ToolInputGuardrailFactory, ToolOutputGuardrail, ToolOutputGuardrailFactory, ToolOutputGuardrailResult, DefaultOutputGuardrail, ToolAuthorizationOptions, ToolAuthorizationResult, PendingToolCall } from \"@mappies/miniwat-agent-core\";\nimport { randomUUID } from \"crypto\";\nimport { McpConfig } from \"../interfaces/mcpConfig\";\n\nexport class ToolDispatcher\n{\n private tools:Tool[]\n private servers:McpServer[]\n\n constructor(private mcp?:McpConfig)\n {\n this.tools = []\n this.servers = []\n }\n\n async init()\n {\n if(this.tools.length !== 0) return;\n\n this.tools = [];\n\n if(this.mcp === undefined) return;\n\n for(let server of Object.keys(this.mcp.mcpServers))\n {\n let service = this.mcp.mcpServers[server]\n\n this.servers.push(new McpServer({name: server, ...service}))\n \n const pck = await import(service.package);\n for(let t of service.tools)\n {\n // Host intent: a tool explicitly disabled in config is excluded here, during\n // discovery, so the planner never sees it.\n if(t.enabled === false) continue;\n\n let definition = pck[t.import];\n let tool = new Tool({ \n name: definition.name, \n displayName: definition.displayName,\n description: definition.description, \n schema: definition.inputSchema, \n func: pck[definition.name],\n service: server,\n getInputText: pck[`${definition.name}InputText`],\n readOnly: definition.readOnly === true,\n requiresApproval: definition.requiresApproval === true,\n inputGuardrail: this.resolveInputGuardrail(server, definition, pck),\n outputGuardrail: this.resolveOutputGuardrail(server, definition, pck)\n })\n \n this.tools.push(tool)\n }\n }\n }\n\n // Resolves a tool definition's named inputGuardrail from the OWNING package's INPUT_GUARDRAILS\n // factory export. Validation is loud and happens at init — never deferred to tool invocation —\n // so the app cannot reach agent.stream() with a partially resolved tool.\n private resolveInputGuardrail(server:string, definition:any, pck:any): ToolInputGuardrail | undefined\n {\n const name:string | undefined = definition.inputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.INPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' but its package exports no INPUT_GUARDRAILS.`);\n }\n\n const factory:ToolInputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' not found in the package's INPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolInputGuardrail.`);\n }\n\n return guardrail;\n }\n\n // Resolves a tool definition's named outputGuardrail from the OWNING package's OUTPUT_GUARDRAILS\n // factory export. Validated loudly at init, mirroring resolveInputGuardrail.\n private resolveOutputGuardrail(server:string, definition:any, pck:any): ToolOutputGuardrail | undefined\n {\n const name:string | undefined = definition.outputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.OUTPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' but its package exports no OUTPUT_GUARDRAILS.`);\n }\n\n const factory:ToolOutputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' not found in the package's OUTPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolOutputGuardrail.`);\n }\n\n return guardrail;\n }\n\n async list(): Promise<Tool[]>\n {\n await this.init()\n return this.tools;\n }\n\n async listServers(): Promise<McpServer[]>\n {\n await this.init()\n return this.servers;\n }\n\n async get(name:string): Promise<Tool|undefined>\n {\n await this.init()\n return this.tools.find(tool => tool.name === name);\n }\n\n async getInputText(name:string, input?:object): Promise<string>\n {\n const tool = await this.get(name);\n\n return tool?.getInputText?.(input) || '';\n }\n\n // Evaluates the dispatch-boundary input guardrail against the concrete invocation. Returns\n // undefined when execution may proceed; a ToolAuthorizationResult (defer/deny) when it is\n // blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the\n // deferred result is returned (persist-before-respond); if a tool can defer but no sink is\n // supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.\n private async authorize(name:string, input:object | undefined, tool:Tool, auth?:ToolAuthorizationOptions): Promise<ToolAuthorizationResult | undefined>\n {\n if(!auth)\n {\n return undefined;\n }\n\n const decision = auth.guardrail.evaluate({ toolName: name, arguments: input }, auth.context);\n\n if(decision.status === \"allow\")\n {\n return undefined;\n }\n\n if(decision.status === \"deny\")\n {\n return { authorization: \"deny\", succeeded: false, text: decision.reason };\n }\n\n // defer: preserve the invocation for a later approval turn.\n const pending:PendingToolCall = { id: randomUUID(), name, arguments: (input ?? {}) as Record<string, unknown>, status: \"pending\" };\n\n if(!auth.onDeferred)\n {\n // Fail closed: a deferral we cannot durably record must not surface as a usable\n // approval-required result.\n return { authorization: \"deny\", succeeded: false, text: \"Authorization is unavailable for this operation.\" };\n }\n\n // Persist-before-respond: the pending call is durably recorded before the deferred result\n // (and the approval request derived from it) can reach the user. A rejection propagates.\n await auth.onDeferred(pending);\n\n return { authorization: \"defer\", succeeded: false, text: decision.reason, pending };\n }\n\n async execute(name:string, input?:object, auth?:ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationResult>\n {\n const tool = await this.get(name);\n \n if(!tool)\n {\n throw new Error(`Tool ${name} could not be found.`);\n }\n\n if(!tool.func)\n {\n throw new Error(`Tool ${name} does not have a function.`);\n }\n\n // Dispatch-boundary authorization runs BEFORE the tool, so an unauthorized side effect never\n // executes — regardless of which planner (or no planner) proposed it. The guardrail is the\n // existing allow/defer/deny input guardrail evaluated against the CONCRETE invocation.\n const authorization = await this.authorize(name, input, tool, auth);\n\n if(authorization)\n {\n return authorization;\n }\n\n let result;\n\n try\n {\n result = await tool.func(input);\n }\n catch(e:any)\n {\n throw new Error(`Failed to run ${name} tool: ${e?.message}`);\n }\n\n // The output guardrail is the SINGLE post-execution authority. It runs here, at the\n // provider-agnostic chokepoint, so the real result is classified BEFORE any provider relays\n // it. Its verdict carries both the user-facing text and the semantic status to the caller;\n // a `blocked` verdict withholds the real result in favor of a replacement. A tool without an\n // explicit guardrail gets the framework default (a normal return is a success).\n const guardrail = tool.outputGuardrail ?? new DefaultOutputGuardrail();\n\n return guardrail.evaluate(result);\n }\n}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/entities/toolDispatcher.ts"],"sourcesContent":["import { Tool, McpServer, ToolInputGuardrail, ToolInputGuardrailFactory, ToolOutputGuardrail, ToolOutputGuardrailFactory, ToolOutputGuardrailResult, DefaultOutputGuardrail, ToolAuthorizationOptions, ToolAuthorizationBlock, PendingToolCall } from \"@mappies/miniwat-agent-core\";\nimport { randomUUID } from \"crypto\";\nimport { McpConfig } from \"../interfaces/mcpConfig\";\n\nexport class ToolDispatcher\n{\n private tools:Tool[]\n private servers:McpServer[]\n\n constructor(private mcp?:McpConfig)\n {\n this.tools = []\n this.servers = []\n }\n\n async init()\n {\n if(this.tools.length !== 0) return;\n\n this.tools = [];\n\n if(this.mcp === undefined) return;\n\n for(let server of Object.keys(this.mcp.mcpServers))\n {\n let service = this.mcp.mcpServers[server]\n\n this.servers.push(new McpServer({name: server, ...service}))\n \n const pck = await import(service.package);\n for(let t of service.tools)\n {\n // Host intent: a tool explicitly disabled in config is excluded here, during\n // discovery, so the planner never sees it.\n if(t.enabled === false) continue;\n\n let definition = pck[t.import];\n let tool = new Tool({ \n name: definition.name, \n displayName: definition.displayName,\n description: definition.description, \n schema: definition.inputSchema, \n func: pck[definition.name],\n service: server,\n getInputText: pck[`${definition.name}InputText`],\n readOnly: definition.readOnly === true,\n requiresApproval: definition.requiresApproval === true,\n inputGuardrail: this.resolveInputGuardrail(server, definition, pck),\n outputGuardrail: this.resolveOutputGuardrail(server, definition, pck)\n })\n \n this.tools.push(tool)\n }\n }\n }\n\n // Resolves a tool definition's named inputGuardrail from the OWNING package's INPUT_GUARDRAILS\n // factory export. Validation is loud and happens at init — never deferred to tool invocation —\n // so the app cannot reach agent.stream() with a partially resolved tool.\n private resolveInputGuardrail(server:string, definition:any, pck:any): ToolInputGuardrail | undefined\n {\n const name:string | undefined = definition.inputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.INPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' but its package exports no INPUT_GUARDRAILS.`);\n }\n\n const factory:ToolInputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' not found in the package's INPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolInputGuardrail.`);\n }\n\n return guardrail;\n }\n\n // Resolves a tool definition's named outputGuardrail from the OWNING package's OUTPUT_GUARDRAILS\n // factory export. Validated loudly at init, mirroring resolveInputGuardrail.\n private resolveOutputGuardrail(server:string, definition:any, pck:any): ToolOutputGuardrail | undefined\n {\n const name:string | undefined = definition.outputGuardrail;\n\n if(name === undefined) return undefined;\n\n const factories = pck.OUTPUT_GUARDRAILS;\n\n if(factories === undefined || typeof factories !== \"object\")\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' but its package exports no OUTPUT_GUARDRAILS.`);\n }\n\n const factory:ToolOutputGuardrailFactory | undefined = factories[name];\n\n if(factory === undefined)\n {\n throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' not found in the package's OUTPUT_GUARDRAILS.`);\n }\n\n if(typeof factory !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);\n }\n\n const guardrail = factory();\n\n if(guardrail === undefined || typeof guardrail.evaluate !== \"function\")\n {\n throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolOutputGuardrail.`);\n }\n\n return guardrail;\n }\n\n async list(): Promise<Tool[]>\n {\n await this.init()\n return this.tools;\n }\n\n async listServers(): Promise<McpServer[]>\n {\n await this.init()\n return this.servers;\n }\n\n async get(name:string): Promise<Tool|undefined>\n {\n await this.init()\n return this.tools.find(tool => tool.name === name);\n }\n\n async getInputText(name:string, input?:object): Promise<string>\n {\n const tool = await this.get(name);\n\n return tool?.getInputText?.(input) || '';\n }\n\n // Evaluates the dispatch-boundary input guardrail against the concrete invocation. Returns\n // undefined when execution may proceed; a ToolAuthorizationBlock (defer/deny) when it is\n // blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the\n // deferred result is returned (persist-before-respond); if a tool can defer but no sink is\n // supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.\n private async authorize(name:string, input:object | undefined, tool:Tool, auth?:ToolAuthorizationOptions): Promise<ToolAuthorizationBlock | undefined>\n {\n if(!auth)\n {\n return undefined;\n }\n\n const decision = auth.guardrail.evaluate({ toolName: name, arguments: input }, auth.context);\n\n if(decision.status === \"allow\")\n {\n return undefined;\n }\n\n if(decision.status === \"deny\")\n {\n return { authorization: \"deny\", succeeded: false, text: decision.reason };\n }\n\n // defer: preserve the invocation for a later approval turn.\n const pending:PendingToolCall = { id: randomUUID(), name, arguments: (input ?? {}) as Record<string, unknown>, status: \"pending\" };\n\n if(!auth.onDeferred)\n {\n // Fail closed: a deferral we cannot durably record must not surface as a usable\n // approval-required result.\n return { authorization: \"deny\", succeeded: false, text: \"Authorization is unavailable for this operation.\" };\n }\n\n // Persist-before-respond: the pending call is durably recorded before the deferred result\n // (and the approval request derived from it) can reach the user. A rejection propagates.\n await auth.onDeferred(pending);\n\n return { authorization: \"defer\", succeeded: false, text: decision.reason, pending };\n }\n\n async execute(name:string, input?:object, auth?:ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationBlock>\n {\n const tool = await this.get(name);\n \n if(!tool)\n {\n throw new Error(`Tool ${name} could not be found.`);\n }\n\n if(!tool.func)\n {\n throw new Error(`Tool ${name} does not have a function.`);\n }\n\n // Dispatch-boundary authorization runs BEFORE the tool, so an unauthorized side effect never\n // executes — regardless of which planner (or no planner) proposed it. The guardrail is the\n // existing allow/defer/deny input guardrail evaluated against the CONCRETE invocation.\n const authorization = await this.authorize(name, input, tool, auth);\n\n if(authorization)\n {\n return authorization;\n }\n\n let result;\n\n try\n {\n result = await tool.func(input);\n }\n catch(e:any)\n {\n throw new Error(`Failed to run ${name} tool: ${e?.message}`);\n }\n\n // The output guardrail is the SINGLE post-execution authority. It runs here, at the\n // provider-agnostic chokepoint, so the real result is classified BEFORE any provider relays\n // it. Its verdict carries both the user-facing text and the semantic status to the caller;\n // a `blocked` verdict withholds the real result in favor of a replacement. A tool without an\n // explicit guardrail gets the framework default (a normal return is a success).\n const guardrail = tool.outputGuardrail ?? new DefaultOutputGuardrail();\n\n return guardrail.evaluate(result);\n }\n}"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mappies/miniwat-mcp-core",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Shared entities and executor for Miniwat MCP",
5
5
  "homepage": "https://gitlab.com/mappies/miniwat-mcp-core#readme",
6
6
  "bugs": {
@@ -43,6 +43,6 @@
43
43
  "vitest": "^5.0.0"
44
44
  },
45
45
  "dependencies": {
46
- "@mappies/miniwat-agent-core": "^0.21.0"
46
+ "@mappies/miniwat-agent-core": "^0.23.0"
47
47
  }
48
48
  }