@mappies/miniwat-mcp-core 0.9.0 → 0.11.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 +85 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -6
- package/dist/index.d.ts +12 -6
- package/dist/index.js +86 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
26
26
|
|
|
27
27
|
// src/entities/toolDispatcher.ts
|
|
28
28
|
var import_miniwat_agent_core = require("@mappies/miniwat-agent-core");
|
|
29
|
+
var import_crypto = require("crypto");
|
|
29
30
|
var ToolDispatcher = class {
|
|
30
31
|
constructor(mcp) {
|
|
31
32
|
this.mcp = mcp;
|
|
@@ -41,19 +42,70 @@ var ToolDispatcher = class {
|
|
|
41
42
|
this.servers.push(new import_miniwat_agent_core.McpServer({ name: server, ...service }));
|
|
42
43
|
const pck = await import(service.package);
|
|
43
44
|
for (let t of service.tools) {
|
|
45
|
+
if (t.enabled === false) continue;
|
|
44
46
|
let definition = pck[t.import];
|
|
45
47
|
let tool = new import_miniwat_agent_core.Tool({
|
|
46
48
|
name: definition.name,
|
|
49
|
+
displayName: definition.displayName,
|
|
47
50
|
description: definition.description,
|
|
48
51
|
schema: definition.inputSchema,
|
|
49
52
|
func: pck[definition.name],
|
|
50
53
|
service: server,
|
|
51
|
-
getInputText: pck[`${definition.name}InputText`]
|
|
54
|
+
getInputText: pck[`${definition.name}InputText`],
|
|
55
|
+
readOnly: definition.readOnly === true,
|
|
56
|
+
requiresApproval: definition.requiresApproval === true,
|
|
57
|
+
inputGuardrail: this.resolveInputGuardrail(server, definition, pck),
|
|
58
|
+
outputGuardrail: this.resolveOutputGuardrail(server, definition, pck)
|
|
52
59
|
});
|
|
53
60
|
this.tools.push(tool);
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
}
|
|
64
|
+
// Resolves a tool definition's named inputGuardrail from the OWNING package's INPUT_GUARDRAILS
|
|
65
|
+
// factory export. Validation is loud and happens at init — never deferred to tool invocation —
|
|
66
|
+
// so the app cannot reach agent.stream() with a partially resolved tool.
|
|
67
|
+
resolveInputGuardrail(server, definition, pck) {
|
|
68
|
+
const name = definition.inputGuardrail;
|
|
69
|
+
if (name === void 0) return void 0;
|
|
70
|
+
const factories = pck.INPUT_GUARDRAILS;
|
|
71
|
+
if (factories === void 0 || typeof factories !== "object") {
|
|
72
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' but its package exports no INPUT_GUARDRAILS.`);
|
|
73
|
+
}
|
|
74
|
+
const factory = factories[name];
|
|
75
|
+
if (factory === void 0) {
|
|
76
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' not found in the package's INPUT_GUARDRAILS.`);
|
|
77
|
+
}
|
|
78
|
+
if (typeof factory !== "function") {
|
|
79
|
+
throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);
|
|
80
|
+
}
|
|
81
|
+
const guardrail = factory();
|
|
82
|
+
if (guardrail === void 0 || typeof guardrail.evaluate !== "function") {
|
|
83
|
+
throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolInputGuardrail.`);
|
|
84
|
+
}
|
|
85
|
+
return guardrail;
|
|
86
|
+
}
|
|
87
|
+
// Resolves a tool definition's named outputGuardrail from the OWNING package's OUTPUT_GUARDRAILS
|
|
88
|
+
// factory export. Validated loudly at init, mirroring resolveInputGuardrail.
|
|
89
|
+
resolveOutputGuardrail(server, definition, pck) {
|
|
90
|
+
const name = definition.outputGuardrail;
|
|
91
|
+
if (name === void 0) return void 0;
|
|
92
|
+
const factories = pck.OUTPUT_GUARDRAILS;
|
|
93
|
+
if (factories === void 0 || typeof factories !== "object") {
|
|
94
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' but its package exports no OUTPUT_GUARDRAILS.`);
|
|
95
|
+
}
|
|
96
|
+
const factory = factories[name];
|
|
97
|
+
if (factory === void 0) {
|
|
98
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' not found in the package's OUTPUT_GUARDRAILS.`);
|
|
99
|
+
}
|
|
100
|
+
if (typeof factory !== "function") {
|
|
101
|
+
throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);
|
|
102
|
+
}
|
|
103
|
+
const guardrail = factory();
|
|
104
|
+
if (guardrail === void 0 || typeof guardrail.evaluate !== "function") {
|
|
105
|
+
throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolOutputGuardrail.`);
|
|
106
|
+
}
|
|
107
|
+
return guardrail;
|
|
108
|
+
}
|
|
57
109
|
async list() {
|
|
58
110
|
await this.init();
|
|
59
111
|
return this.tools;
|
|
@@ -70,7 +122,30 @@ var ToolDispatcher = class {
|
|
|
70
122
|
const tool = await this.get(name);
|
|
71
123
|
return tool?.getInputText?.(input) || "";
|
|
72
124
|
}
|
|
73
|
-
|
|
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
|
|
127
|
+
// blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the
|
|
128
|
+
// deferred result is returned (persist-before-respond); if a tool can defer but no sink is
|
|
129
|
+
// supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.
|
|
130
|
+
async authorize(name, input, tool, auth) {
|
|
131
|
+
if (!auth) {
|
|
132
|
+
return void 0;
|
|
133
|
+
}
|
|
134
|
+
const decision = auth.guardrail.evaluate({ toolName: name, arguments: input }, auth.context);
|
|
135
|
+
if (decision.status === "allow") {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
if (decision.status === "deny") {
|
|
139
|
+
return { authorization: "deny", succeeded: false, text: decision.reason };
|
|
140
|
+
}
|
|
141
|
+
const pending = { id: (0, import_crypto.randomUUID)(), name, arguments: input ?? {}, status: "pending" };
|
|
142
|
+
if (!auth.onDeferred) {
|
|
143
|
+
return { authorization: "deny", succeeded: false, text: "Authorization is unavailable for this operation." };
|
|
144
|
+
}
|
|
145
|
+
await auth.onDeferred(pending);
|
|
146
|
+
return { authorization: "defer", succeeded: false, text: decision.reason, pending };
|
|
147
|
+
}
|
|
148
|
+
async execute(name, input, auth) {
|
|
74
149
|
const tool = await this.get(name);
|
|
75
150
|
if (!tool) {
|
|
76
151
|
throw new Error(`Tool ${name} could not be found.`);
|
|
@@ -78,12 +153,18 @@ var ToolDispatcher = class {
|
|
|
78
153
|
if (!tool.func) {
|
|
79
154
|
throw new Error(`Tool ${name} does not have a function.`);
|
|
80
155
|
}
|
|
156
|
+
const authorization = await this.authorize(name, input, tool, auth);
|
|
157
|
+
if (authorization) {
|
|
158
|
+
return authorization;
|
|
159
|
+
}
|
|
160
|
+
let result;
|
|
81
161
|
try {
|
|
82
|
-
|
|
83
|
-
return result;
|
|
162
|
+
result = await tool.func(input);
|
|
84
163
|
} catch (e) {
|
|
85
164
|
throw new Error(`Failed to run ${name} tool: ${e?.message}`);
|
|
86
165
|
}
|
|
166
|
+
const guardrail = tool.outputGuardrail ?? new import_miniwat_agent_core.DefaultOutputGuardrail();
|
|
167
|
+
return guardrail.evaluate(result);
|
|
87
168
|
}
|
|
88
169
|
};
|
|
89
170
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/entities/toolDispatcher.ts"],"sourcesContent":["export { ToolDispatcher } from './entities/toolDispatcher';\nexport { McpConfig, McpServer, McpToolImport } from './interfaces/mcpConfig';","import { Tool, McpServer } from \"@mappies/miniwat-agent-core\";\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 let definition = pck[t.import];\n let tool = new Tool({ \n name: definition.name, \n description: definition.description, \n schema: definition.inputSchema, \n func: pck[definition.name],\n service: server,\n getInputText: pck[`${definition.name}InputText`]\n })\n \n this.tools.push(tool)\n }\n }\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 async execute(name:string, input?:object): Promise<string>\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 try\n {\n let result = await tool.func(input);\n return result;\n }\n catch(e:any)\n {\n throw new Error(`Failed to run ${name} tool: ${e?.message}`);\n }\n }\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,gCAAgC;AAGzB,IAAM,iBAAN,MACP;AAAA,EAII,YAAoB,KACpB;AADoB;AAEhB,SAAK,QAAQ,CAAC;AACd,SAAK,UAAU,CAAC;AAAA,EACpB;AAAA,EAEA,MAAM,OACN;AACI,QAAG,KAAK,MAAM,WAAW,EAAG;AAE5B,SAAK,QAAQ,CAAC;AAEd,QAAG,KAAK,QAAQ,OAAW;AAE3B,aAAQ,UAAU,OAAO,KAAK,KAAK,IAAI,UAAU,GACjD;AACI,UAAI,UAAU,KAAK,IAAI,WAAW,MAAM;AAExC,WAAK,QAAQ,KAAK,IAAI,oCAAU,EAAC,MAAM,QAAQ,GAAG,QAAO,CAAC,CAAC;AAE3D,YAAM,MAAM,MAAM,OAAO,QAAQ;AACjC,eAAQ,KAAK,QAAQ,OACrB;AACI,YAAI,aAAa,IAAI,EAAE,MAAM;AAC7B,YAAI,OAAO,IAAI,+BAAK;AAAA,UACI,MAAM,WAAW;AAAA,UACjB,aAAa,WAAW;AAAA,UACxB,QAAQ,WAAW;AAAA,UACnB,MAAM,IAAI,WAAW,IAAI;AAAA,UACzB,SAAS;AAAA,UACT,cAAc,IAAI,GAAG,WAAW,IAAI,WAAW;AAAA,QACnD,CAAC;AAErB,aAAK,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,OACN;AACI,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,cACN;AACI,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,IAAI,MACV;AACI,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK,MAAM,KAAK,UAAQ,KAAK,SAAS,IAAI;AAAA,EACrD;AAAA,EAEA,MAAM,aAAa,MAAa,OAChC;AACI,UAAM,OAAO,MAAM,KAAK,IAAI,IAAI;AAEhC,WAAO,MAAM,eAAe,KAAK,KAAK;AAAA,EAC1C;AAAA,EAEA,MAAM,QAAQ,MAAa,OAC3B;AACI,UAAM,OAAO,MAAM,KAAK,IAAI,IAAI;AAEhC,QAAG,CAAC,MACJ;AACI,YAAM,IAAI,MAAM,QAAQ,IAAI,sBAAsB;AAAA,IACtD;AAEA,QAAG,CAAC,KAAK,MACT;AACI,YAAM,IAAI,MAAM,QAAQ,IAAI,4BAA4B;AAAA,IAC5D;AAEA,QACA;AACI,UAAI,SAAS,MAAM,KAAK,KAAK,KAAK;AAClC,aAAO;AAAA,IACX,SACM,GACN;AACI,YAAM,IAAI,MAAM,iBAAiB,IAAI,UAAU,GAAG,OAAO,EAAE;AAAA,IAC/D;AAAA,EACJ;AACJ;","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, 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":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { Tool, McpServer as McpServer$1 } from '@mappies/miniwat-agent-core';
|
|
1
|
+
import { Tool, McpServer as McpServer$1, ToolAuthorizationOptions, ToolOutputGuardrailResult, ToolAuthorizationResult } from '@mappies/miniwat-agent-core';
|
|
2
2
|
|
|
3
|
-
interface
|
|
4
|
-
|
|
3
|
+
interface McpToolImport {
|
|
4
|
+
import: string;
|
|
5
|
+
enabled?: boolean;
|
|
5
6
|
}
|
|
7
|
+
|
|
6
8
|
interface McpServer {
|
|
7
9
|
package: string;
|
|
8
10
|
description?: string;
|
|
9
11
|
secrets?: string[];
|
|
10
12
|
tools: McpToolImport[];
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
interface McpConfig {
|
|
16
|
+
mcpServers: Record<string, McpServer>;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
declare class ToolDispatcher {
|
|
@@ -19,11 +22,14 @@ declare class ToolDispatcher {
|
|
|
19
22
|
private servers;
|
|
20
23
|
constructor(mcp?: McpConfig | undefined);
|
|
21
24
|
init(): Promise<void>;
|
|
25
|
+
private resolveInputGuardrail;
|
|
26
|
+
private resolveOutputGuardrail;
|
|
22
27
|
list(): Promise<Tool[]>;
|
|
23
28
|
listServers(): Promise<McpServer$1[]>;
|
|
24
29
|
get(name: string): Promise<Tool | undefined>;
|
|
25
30
|
getInputText(name: string, input?: object): Promise<string>;
|
|
26
|
-
|
|
31
|
+
private authorize;
|
|
32
|
+
execute(name: string, input?: object, auth?: ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationResult>;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
export { type McpConfig, type McpServer, type McpToolImport, ToolDispatcher };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { Tool, McpServer as McpServer$1 } from '@mappies/miniwat-agent-core';
|
|
1
|
+
import { Tool, McpServer as McpServer$1, ToolAuthorizationOptions, ToolOutputGuardrailResult, ToolAuthorizationResult } from '@mappies/miniwat-agent-core';
|
|
2
2
|
|
|
3
|
-
interface
|
|
4
|
-
|
|
3
|
+
interface McpToolImport {
|
|
4
|
+
import: string;
|
|
5
|
+
enabled?: boolean;
|
|
5
6
|
}
|
|
7
|
+
|
|
6
8
|
interface McpServer {
|
|
7
9
|
package: string;
|
|
8
10
|
description?: string;
|
|
9
11
|
secrets?: string[];
|
|
10
12
|
tools: McpToolImport[];
|
|
11
13
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
interface McpConfig {
|
|
16
|
+
mcpServers: Record<string, McpServer>;
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
declare class ToolDispatcher {
|
|
@@ -19,11 +22,14 @@ declare class ToolDispatcher {
|
|
|
19
22
|
private servers;
|
|
20
23
|
constructor(mcp?: McpConfig | undefined);
|
|
21
24
|
init(): Promise<void>;
|
|
25
|
+
private resolveInputGuardrail;
|
|
26
|
+
private resolveOutputGuardrail;
|
|
22
27
|
list(): Promise<Tool[]>;
|
|
23
28
|
listServers(): Promise<McpServer$1[]>;
|
|
24
29
|
get(name: string): Promise<Tool | undefined>;
|
|
25
30
|
getInputText(name: string, input?: object): Promise<string>;
|
|
26
|
-
|
|
31
|
+
private authorize;
|
|
32
|
+
execute(name: string, input?: object, auth?: ToolAuthorizationOptions): Promise<ToolOutputGuardrailResult | ToolAuthorizationResult>;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
export { type McpConfig, type McpServer, type McpToolImport, ToolDispatcher };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// src/entities/toolDispatcher.ts
|
|
2
|
-
import { Tool, McpServer } from "@mappies/miniwat-agent-core";
|
|
2
|
+
import { Tool, McpServer, DefaultOutputGuardrail } from "@mappies/miniwat-agent-core";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
3
4
|
var ToolDispatcher = class {
|
|
4
5
|
constructor(mcp) {
|
|
5
6
|
this.mcp = mcp;
|
|
@@ -15,19 +16,70 @@ var ToolDispatcher = class {
|
|
|
15
16
|
this.servers.push(new McpServer({ name: server, ...service }));
|
|
16
17
|
const pck = await import(service.package);
|
|
17
18
|
for (let t of service.tools) {
|
|
19
|
+
if (t.enabled === false) continue;
|
|
18
20
|
let definition = pck[t.import];
|
|
19
21
|
let tool = new Tool({
|
|
20
22
|
name: definition.name,
|
|
23
|
+
displayName: definition.displayName,
|
|
21
24
|
description: definition.description,
|
|
22
25
|
schema: definition.inputSchema,
|
|
23
26
|
func: pck[definition.name],
|
|
24
27
|
service: server,
|
|
25
|
-
getInputText: pck[`${definition.name}InputText`]
|
|
28
|
+
getInputText: pck[`${definition.name}InputText`],
|
|
29
|
+
readOnly: definition.readOnly === true,
|
|
30
|
+
requiresApproval: definition.requiresApproval === true,
|
|
31
|
+
inputGuardrail: this.resolveInputGuardrail(server, definition, pck),
|
|
32
|
+
outputGuardrail: this.resolveOutputGuardrail(server, definition, pck)
|
|
26
33
|
});
|
|
27
34
|
this.tools.push(tool);
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
}
|
|
38
|
+
// Resolves a tool definition's named inputGuardrail from the OWNING package's INPUT_GUARDRAILS
|
|
39
|
+
// factory export. Validation is loud and happens at init — never deferred to tool invocation —
|
|
40
|
+
// so the app cannot reach agent.stream() with a partially resolved tool.
|
|
41
|
+
resolveInputGuardrail(server, definition, pck) {
|
|
42
|
+
const name = definition.inputGuardrail;
|
|
43
|
+
if (name === void 0) return void 0;
|
|
44
|
+
const factories = pck.INPUT_GUARDRAILS;
|
|
45
|
+
if (factories === void 0 || typeof factories !== "object") {
|
|
46
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' but its package exports no INPUT_GUARDRAILS.`);
|
|
47
|
+
}
|
|
48
|
+
const factory = factories[name];
|
|
49
|
+
if (factory === void 0) {
|
|
50
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names inputGuardrail '${name}' not found in the package's INPUT_GUARDRAILS.`);
|
|
51
|
+
}
|
|
52
|
+
if (typeof factory !== "function") {
|
|
53
|
+
throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);
|
|
54
|
+
}
|
|
55
|
+
const guardrail = factory();
|
|
56
|
+
if (guardrail === void 0 || typeof guardrail.evaluate !== "function") {
|
|
57
|
+
throw new Error(`INPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolInputGuardrail.`);
|
|
58
|
+
}
|
|
59
|
+
return guardrail;
|
|
60
|
+
}
|
|
61
|
+
// Resolves a tool definition's named outputGuardrail from the OWNING package's OUTPUT_GUARDRAILS
|
|
62
|
+
// factory export. Validated loudly at init, mirroring resolveInputGuardrail.
|
|
63
|
+
resolveOutputGuardrail(server, definition, pck) {
|
|
64
|
+
const name = definition.outputGuardrail;
|
|
65
|
+
if (name === void 0) return void 0;
|
|
66
|
+
const factories = pck.OUTPUT_GUARDRAILS;
|
|
67
|
+
if (factories === void 0 || typeof factories !== "object") {
|
|
68
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' but its package exports no OUTPUT_GUARDRAILS.`);
|
|
69
|
+
}
|
|
70
|
+
const factory = factories[name];
|
|
71
|
+
if (factory === void 0) {
|
|
72
|
+
throw new Error(`Tool '${definition.name}' on server '${server}' names outputGuardrail '${name}' not found in the package's OUTPUT_GUARDRAILS.`);
|
|
73
|
+
}
|
|
74
|
+
if (typeof factory !== "function") {
|
|
75
|
+
throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' is not a factory function.`);
|
|
76
|
+
}
|
|
77
|
+
const guardrail = factory();
|
|
78
|
+
if (guardrail === void 0 || typeof guardrail.evaluate !== "function") {
|
|
79
|
+
throw new Error(`OUTPUT_GUARDRAILS['${name}'] on server '${server}' did not produce a valid ToolOutputGuardrail.`);
|
|
80
|
+
}
|
|
81
|
+
return guardrail;
|
|
82
|
+
}
|
|
31
83
|
async list() {
|
|
32
84
|
await this.init();
|
|
33
85
|
return this.tools;
|
|
@@ -44,7 +96,30 @@ var ToolDispatcher = class {
|
|
|
44
96
|
const tool = await this.get(name);
|
|
45
97
|
return tool?.getInputText?.(input) || "";
|
|
46
98
|
}
|
|
47
|
-
|
|
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
|
|
101
|
+
// blocked. On defer the pending call is recorded via the awaited onDeferred sink BEFORE the
|
|
102
|
+
// deferred result is returned (persist-before-respond); if a tool can defer but no sink is
|
|
103
|
+
// supplied, it FAILS CLOSED (denied) — a deferral must never be reported as unrecoverable.
|
|
104
|
+
async authorize(name, input, tool, auth) {
|
|
105
|
+
if (!auth) {
|
|
106
|
+
return void 0;
|
|
107
|
+
}
|
|
108
|
+
const decision = auth.guardrail.evaluate({ toolName: name, arguments: input }, auth.context);
|
|
109
|
+
if (decision.status === "allow") {
|
|
110
|
+
return void 0;
|
|
111
|
+
}
|
|
112
|
+
if (decision.status === "deny") {
|
|
113
|
+
return { authorization: "deny", succeeded: false, text: decision.reason };
|
|
114
|
+
}
|
|
115
|
+
const pending = { id: randomUUID(), name, arguments: input ?? {}, status: "pending" };
|
|
116
|
+
if (!auth.onDeferred) {
|
|
117
|
+
return { authorization: "deny", succeeded: false, text: "Authorization is unavailable for this operation." };
|
|
118
|
+
}
|
|
119
|
+
await auth.onDeferred(pending);
|
|
120
|
+
return { authorization: "defer", succeeded: false, text: decision.reason, pending };
|
|
121
|
+
}
|
|
122
|
+
async execute(name, input, auth) {
|
|
48
123
|
const tool = await this.get(name);
|
|
49
124
|
if (!tool) {
|
|
50
125
|
throw new Error(`Tool ${name} could not be found.`);
|
|
@@ -52,12 +127,18 @@ var ToolDispatcher = class {
|
|
|
52
127
|
if (!tool.func) {
|
|
53
128
|
throw new Error(`Tool ${name} does not have a function.`);
|
|
54
129
|
}
|
|
130
|
+
const authorization = await this.authorize(name, input, tool, auth);
|
|
131
|
+
if (authorization) {
|
|
132
|
+
return authorization;
|
|
133
|
+
}
|
|
134
|
+
let result;
|
|
55
135
|
try {
|
|
56
|
-
|
|
57
|
-
return result;
|
|
136
|
+
result = await tool.func(input);
|
|
58
137
|
} catch (e) {
|
|
59
138
|
throw new Error(`Failed to run ${name} tool: ${e?.message}`);
|
|
60
139
|
}
|
|
140
|
+
const guardrail = tool.outputGuardrail ?? new DefaultOutputGuardrail();
|
|
141
|
+
return guardrail.evaluate(result);
|
|
61
142
|
}
|
|
62
143
|
};
|
|
63
144
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/entities/toolDispatcher.ts"],"sourcesContent":["import { Tool, McpServer } from \"@mappies/miniwat-agent-core\";\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 let definition = pck[t.import];\n let tool = new Tool({ \n name: definition.name, \n description: definition.description, \n schema: definition.inputSchema, \n func: pck[definition.name],\n service: server,\n getInputText: pck[`${definition.name}InputText`]\n })\n \n this.tools.push(tool)\n }\n }\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 async execute(name:string, input?:object): Promise<string>\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 try\n {\n let result = await tool.func(input);\n return result;\n }\n catch(e:any)\n {\n throw new Error(`Failed to run ${name} tool: ${e?.message}`);\n }\n }\n}"],"mappings":";AAAA,SAAS,MAAM,iBAAiB;AAGzB,IAAM,iBAAN,MACP;AAAA,EAII,YAAoB,KACpB;AADoB;AAEhB,SAAK,QAAQ,CAAC;AACd,SAAK,UAAU,CAAC;AAAA,EACpB;AAAA,EAEA,MAAM,OACN;AACI,QAAG,KAAK,MAAM,WAAW,EAAG;AAE5B,SAAK,QAAQ,CAAC;AAEd,QAAG,KAAK,QAAQ,OAAW;AAE3B,aAAQ,UAAU,OAAO,KAAK,KAAK,IAAI,UAAU,GACjD;AACI,UAAI,UAAU,KAAK,IAAI,WAAW,MAAM;AAExC,WAAK,QAAQ,KAAK,IAAI,UAAU,EAAC,MAAM,QAAQ,GAAG,QAAO,CAAC,CAAC;AAE3D,YAAM,MAAM,MAAM,OAAO,QAAQ;AACjC,eAAQ,KAAK,QAAQ,OACrB;AACI,YAAI,aAAa,IAAI,EAAE,MAAM;AAC7B,YAAI,OAAO,IAAI,KAAK;AAAA,UACI,MAAM,WAAW;AAAA,UACjB,aAAa,WAAW;AAAA,UACxB,QAAQ,WAAW;AAAA,UACnB,MAAM,IAAI,WAAW,IAAI;AAAA,UACzB,SAAS;AAAA,UACT,cAAc,IAAI,GAAG,WAAW,IAAI,WAAW;AAAA,QACnD,CAAC;AAErB,aAAK,MAAM,KAAK,IAAI;AAAA,MACxB;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,OACN;AACI,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,cACN;AACI,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK;AAAA,EAChB;AAAA,EAEA,MAAM,IAAI,MACV;AACI,UAAM,KAAK,KAAK;AAChB,WAAO,KAAK,MAAM,KAAK,UAAQ,KAAK,SAAS,IAAI;AAAA,EACrD;AAAA,EAEA,MAAM,aAAa,MAAa,OAChC;AACI,UAAM,OAAO,MAAM,KAAK,IAAI,IAAI;AAEhC,WAAO,MAAM,eAAe,KAAK,KAAK;AAAA,EAC1C;AAAA,EAEA,MAAM,QAAQ,MAAa,OAC3B;AACI,UAAM,OAAO,MAAM,KAAK,IAAI,IAAI;AAEhC,QAAG,CAAC,MACJ;AACI,YAAM,IAAI,MAAM,QAAQ,IAAI,sBAAsB;AAAA,IACtD;AAEA,QAAG,CAAC,KAAK,MACT;AACI,YAAM,IAAI,MAAM,QAAQ,IAAI,4BAA4B;AAAA,IAC5D;AAEA,QACA;AACI,UAAI,SAAS,MAAM,KAAK,KAAK,KAAK;AAClC,aAAO;AAAA,IACX,SACM,GACN;AACI,YAAM,IAAI,MAAM,iBAAiB,IAAI,UAAU,GAAG,OAAO,EAAE;AAAA,IAC/D;AAAA,EACJ;AACJ;","names":[]}
|
|
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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mappies/miniwat-mcp-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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.
|
|
46
|
+
"@mappies/miniwat-agent-core": "^0.21.0"
|
|
47
47
|
}
|
|
48
48
|
}
|