toolgate 1.0.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/LICENSE +21 -0
- package/README.md +342 -0
- package/dist/index.d.mts +165 -0
- package/dist/index.d.ts +165 -0
- package/dist/index.js +616 -0
- package/dist/index.mjs +574 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# 🛡️ ToolGate
|
|
2
|
+
|
|
3
|
+
**Optimistic execution middleware for autonomous agents.**
|
|
4
|
+
|
|
5
|
+
Let your agent run freely — reads execute instantly, every other action (writes, deletes, sends, deploys) is silently intercepted, recorded, and held for human approval. The agent never knows the difference.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Agent calls tool ──▶ ToolGate classifies ──▶ READ? ──▶ Execute for real
|
|
9
|
+
│
|
|
10
|
+
└──▶ ACTION? ──▶ Return phantom result
|
|
11
|
+
Save params to queue
|
|
12
|
+
┊
|
|
13
|
+
Agent finishes ──▶ Human reviews all actions
|
|
14
|
+
┊
|
|
15
|
+
Approve ──▶ Execute for real
|
|
16
|
+
Reject ──▶ Discard
|
|
17
|
+
Edit ──▶ Execute with new params
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install toolgate
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Quick Start — 5 Lines
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { ToolGate, cliApproval } from "toolgate";
|
|
30
|
+
|
|
31
|
+
// Your real tool executor (what the agent normally calls)
|
|
32
|
+
async function execute(tool: string, params: Record<string, unknown>) {
|
|
33
|
+
// call your APIs, MCP servers, SDKs, whatever
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const gate = new ToolGate(execute, {
|
|
37
|
+
agentName: "my-agent",
|
|
38
|
+
onApprovalNeeded: cliApproval, // interactive terminal approval
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Give your agent gate.proxy instead of the real executor
|
|
42
|
+
await myAgent.run({
|
|
43
|
+
tools: gate.proxy,
|
|
44
|
+
task: "Organize my inbox and draft replies",
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// When done, finalize — triggers the approval flow
|
|
48
|
+
await gate.finalize();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
That's it. Your agent runs as normal. Reads pass through. Actions are queued. You approve at the end.
|
|
52
|
+
|
|
53
|
+
## How Classification Works
|
|
54
|
+
|
|
55
|
+
ToolGate uses a multi-layer classifier to determine if a tool call is a **read** (passthrough) or an **action** (intercept):
|
|
56
|
+
|
|
57
|
+
1. **Explicit overrides** — `readTools: ["getUser"]` / `actionTools: ["deleteUser"]`
|
|
58
|
+
2. **Custom classifier** — provide your own function
|
|
59
|
+
3. **Name pattern matching** — `get*`, `list*`, `search*` → read. `create*`, `send*`, `delete*` → action
|
|
60
|
+
4. **MCP description analysis** — parses tool descriptions for read vs. mutate signals
|
|
61
|
+
5. **HTTP method hints** — if params contain `method: "GET"` → read, `method: "POST"` → action
|
|
62
|
+
6. **Safe default** — unknown tools are intercepted (never accidentally executes a write)
|
|
63
|
+
|
|
64
|
+
## Explicit Tool Lists
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
const gate = new ToolGate(execute, {
|
|
68
|
+
readTools: ["getEmails", "searchContacts", "listFiles"],
|
|
69
|
+
actionTools: ["sendEmail", "deleteFile", "createIssue"],
|
|
70
|
+
});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Custom Classifier
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
const gate = new ToolGate(execute, {
|
|
77
|
+
classifier: (toolName, params) => {
|
|
78
|
+
if (toolName.startsWith("db.query")) {
|
|
79
|
+
return { intent: "read", isPassthrough: true, confidence: 1, reason: "DB query" };
|
|
80
|
+
}
|
|
81
|
+
// fall through to default
|
|
82
|
+
return classifyTool(toolName, params);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## MCP Integration
|
|
88
|
+
|
|
89
|
+
ToolGate auto-classifies MCP tools by analyzing their names and descriptions:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { mcpToolGate, autoConfigFromMCP } from "toolgate";
|
|
93
|
+
|
|
94
|
+
// Option 1: Provide tool definitions upfront
|
|
95
|
+
const gate = mcpToolGate(mcpExecutor, {
|
|
96
|
+
mcpServers: [
|
|
97
|
+
{
|
|
98
|
+
name: "gmail",
|
|
99
|
+
url: "https://gmail.mcp.example.com/sse",
|
|
100
|
+
tools: [
|
|
101
|
+
{ name: "gmail_listMessages", description: "List messages in the inbox" },
|
|
102
|
+
{ name: "gmail_sendMessage", description: "Send a new email message" },
|
|
103
|
+
{ name: "gmail_trashMessage", description: "Move a message to trash" },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Option 2: Auto-discover from running MCP servers
|
|
110
|
+
const config = await autoConfigFromMCP([
|
|
111
|
+
{ name: "gmail", url: "https://gmail.mcp.example.com/sse" },
|
|
112
|
+
{ name: "github", url: "https://github.mcp.example.com/sse" },
|
|
113
|
+
]);
|
|
114
|
+
const gate = new ToolGate(mcpExecutor, config);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Wrapping an Existing Tool Map
|
|
118
|
+
|
|
119
|
+
If your agent uses a `{ toolName: function }` map:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
const realTools = {
|
|
123
|
+
getUser: async (p) => db.users.find(p.id),
|
|
124
|
+
createUser: async (p) => db.users.insert(p),
|
|
125
|
+
sendEmail: async (p) => mailer.send(p),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const gate = new ToolGate(
|
|
129
|
+
async (name, params) => realTools[name](params),
|
|
130
|
+
{ agentName: "user-manager" }
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Or use the convenience wrapper:
|
|
134
|
+
const proxiedTools = gate.wrapTools(realTools);
|
|
135
|
+
await agent.run({ tools: proxiedTools });
|
|
136
|
+
await gate.finalize();
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Phantom Responses
|
|
140
|
+
|
|
141
|
+
When an action is intercepted, the agent receives a convincing phantom response so it continues working normally:
|
|
142
|
+
|
|
143
|
+
| Intent | Default Phantom Response |
|
|
144
|
+
|----------|-------------------------------------------------------|
|
|
145
|
+
| `create` | `{ success: true, id: "phantom_abc123" }` |
|
|
146
|
+
| `update` | `{ success: true, message: "Updated successfully" }` |
|
|
147
|
+
| `delete` | `{ success: true, message: "Deleted successfully" }` |
|
|
148
|
+
| `send` | `{ success: true, messageId: "msg_xyz789" }` |
|
|
149
|
+
|
|
150
|
+
Custom phantom responses:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const gate = new ToolGate(execute, {
|
|
154
|
+
phantomResponse: (tool) => {
|
|
155
|
+
if (tool.name === "createIssue") {
|
|
156
|
+
return { id: 99999, url: "https://github.com/org/repo/issues/99999", title: tool.params.title };
|
|
157
|
+
}
|
|
158
|
+
return { ok: true };
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Approval Methods
|
|
164
|
+
|
|
165
|
+
### 1. CLI (built-in)
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { cliApproval } from "toolgate";
|
|
169
|
+
const gate = new ToolGate(execute, { onApprovalNeeded: cliApproval });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 2. Programmatic
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
const gate = new ToolGate(execute);
|
|
176
|
+
await agent.run({ tools: gate.proxy });
|
|
177
|
+
const request = await gate.finalize();
|
|
178
|
+
|
|
179
|
+
// Inspect what the agent wants to do
|
|
180
|
+
console.log(gate.summary());
|
|
181
|
+
|
|
182
|
+
// Approve everything
|
|
183
|
+
await gate.approveAll();
|
|
184
|
+
|
|
185
|
+
// Or reject everything
|
|
186
|
+
await gate.rejectAll();
|
|
187
|
+
|
|
188
|
+
// Or decide per-action
|
|
189
|
+
await gate.executeApproval({
|
|
190
|
+
sessionId: gate.sessionId,
|
|
191
|
+
approvedAt: Date.now(),
|
|
192
|
+
decisions: new Map([
|
|
193
|
+
["action-id-1", { action: "approve" }],
|
|
194
|
+
["action-id-2", { action: "reject" }],
|
|
195
|
+
["action-id-3", { action: "edit", actionId: "action-id-3", newParams: { to: "corrected@example.com" } }],
|
|
196
|
+
]),
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### 3. Dashboard (Next.js + Supabase)
|
|
201
|
+
|
|
202
|
+
See the **Dashboard** section below.
|
|
203
|
+
|
|
204
|
+
## Supabase Persistence
|
|
205
|
+
|
|
206
|
+
Pass your Supabase credentials and sessions are automatically persisted:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const gate = new ToolGate(execute, {
|
|
210
|
+
supabaseUrl: process.env.SUPABASE_URL,
|
|
211
|
+
supabaseKey: process.env.SUPABASE_ANON_KEY,
|
|
212
|
+
agentName: "inbox-agent",
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Inspection
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
gate.reads; // all reads that were executed
|
|
220
|
+
gate.pending; // all intercepted actions
|
|
221
|
+
gate.currentSession; // full session snapshot
|
|
222
|
+
gate.summary(); // pretty-printed CLI summary
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
# Dashboard Setup
|
|
228
|
+
|
|
229
|
+
The dashboard is a Next.js app that shows all ToolGate sessions in real time.
|
|
230
|
+
|
|
231
|
+
## 1. Supabase Setup
|
|
232
|
+
|
|
233
|
+
1. Create a project at [supabase.com](https://supabase.com)
|
|
234
|
+
2. Go to **SQL Editor** and run the contents of `sql/schema.sql` (included in this repo)
|
|
235
|
+
3. Copy your **Project URL** and **anon public key** from Settings → API
|
|
236
|
+
|
|
237
|
+
## 2. Dashboard Setup
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
cd dashboard
|
|
241
|
+
cp .env.example .env.local
|
|
242
|
+
# Edit .env.local with your Supabase URL and key
|
|
243
|
+
npm install
|
|
244
|
+
npm run dev
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Open `http://localhost:3000`. Sessions appear in real time as your agents run.
|
|
248
|
+
|
|
249
|
+
## 3. Use Together
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
import { ToolGate } from "toolgate";
|
|
253
|
+
|
|
254
|
+
const gate = new ToolGate(execute, {
|
|
255
|
+
agentName: "deploy-bot",
|
|
256
|
+
supabaseUrl: "https://xxx.supabase.co",
|
|
257
|
+
supabaseKey: "eyJ...",
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
gate.describe("Deploy v2.3.1 to production");
|
|
261
|
+
|
|
262
|
+
await agent.run({ tools: gate.proxy });
|
|
263
|
+
await gate.finalize();
|
|
264
|
+
|
|
265
|
+
// Now open the dashboard — the session is there with approve/reject/edit buttons
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
# Full Example: Autonomous Email Agent
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
import { ToolGate, cliApproval } from "toolgate";
|
|
274
|
+
|
|
275
|
+
// Simulated tool executor
|
|
276
|
+
async function execute(tool: string, params: Record<string, unknown>) {
|
|
277
|
+
switch (tool) {
|
|
278
|
+
case "listEmails": return [{ id: 1, from: "boss@co.com", subject: "Q3 Report" }];
|
|
279
|
+
case "readEmail": return { id: 1, body: "Please review the Q3 numbers." };
|
|
280
|
+
case "sendEmail": return { messageId: "real_123" };
|
|
281
|
+
case "archiveEmail": return { success: true };
|
|
282
|
+
default: return { error: "unknown tool" };
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const gate = new ToolGate(execute, {
|
|
287
|
+
agentName: "email-assistant",
|
|
288
|
+
onApprovalNeeded: cliApproval,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
gate.describe("Read inbox, draft replies, archive processed emails");
|
|
292
|
+
|
|
293
|
+
// Simulate what an autonomous agent would do:
|
|
294
|
+
const emails = await gate.proxy("listEmails", {}); // ✅ READ — executes
|
|
295
|
+
const detail = await gate.proxy("readEmail", { id: 1 }); // ✅ READ — executes
|
|
296
|
+
const sent = await gate.proxy("sendEmail", { // 🛑 SEND — intercepted
|
|
297
|
+
to: "boss@co.com",
|
|
298
|
+
subject: "Re: Q3 Report",
|
|
299
|
+
body: "Reviewed — numbers look solid. Ship it.",
|
|
300
|
+
});
|
|
301
|
+
const archived = await gate.proxy("archiveEmail", { id: 1 }); // 🛑 DELETE — intercepted
|
|
302
|
+
|
|
303
|
+
// Agent thinks both worked. Now finalize:
|
|
304
|
+
await gate.finalize();
|
|
305
|
+
// → CLI shows: 2 reads executed, 2 actions pending approval
|
|
306
|
+
// → You approve/reject/edit each one
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
# API Reference
|
|
312
|
+
|
|
313
|
+
### `new ToolGate(executor, config?)`
|
|
314
|
+
|
|
315
|
+
| Config Field | Type | Description |
|
|
316
|
+
|-------------------|----------------------------------------------|------------------------------------------|
|
|
317
|
+
| `classifier` | `(name, params) => ToolClassification` | Custom classification function |
|
|
318
|
+
| `readTools` | `string[]` | Always-passthrough tool names |
|
|
319
|
+
| `actionTools` | `string[]` | Always-intercept tool names |
|
|
320
|
+
| `phantomResponse` | `(tool: ToolCall) => unknown` | Custom phantom result generator |
|
|
321
|
+
| `onApprovalNeeded`| `(req: ApprovalRequest) => Promise<Result>` | Callback when agent finishes |
|
|
322
|
+
| `supabaseUrl` | `string` | Supabase project URL |
|
|
323
|
+
| `supabaseKey` | `string` | Supabase anon key |
|
|
324
|
+
| `agentName` | `string` | Display name for the agent |
|
|
325
|
+
| `mcpServers` | `MCPServerDef[]` | MCP server + tool definitions |
|
|
326
|
+
|
|
327
|
+
### Instance Methods
|
|
328
|
+
|
|
329
|
+
| Method | Returns | Description |
|
|
330
|
+
|----------------------|----------------------------|----------------------------------------------|
|
|
331
|
+
| `.proxy` | `RealExecutor` | The proxied executor to give to your agent |
|
|
332
|
+
| `.wrapTools(map)` | Same tool map shape | Wraps a `{ name: fn }` tool map |
|
|
333
|
+
| `.describe(text)` | `this` | Set task description |
|
|
334
|
+
| `.finalize()` | `Promise<ApprovalRequest>` | End session, trigger approval |
|
|
335
|
+
| `.approveAll()` | `Promise<Map>` | Approve + execute all pending actions |
|
|
336
|
+
| `.rejectAll()` | `void` | Reject all pending actions |
|
|
337
|
+
| `.executeApproval()` | `Promise<Map>` | Execute with per-action decisions |
|
|
338
|
+
| `.summary()` | `string` | Pretty-printed session summary |
|
|
339
|
+
|
|
340
|
+
## License
|
|
341
|
+
|
|
342
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
type ToolIntent = "read" | "write" | "delete" | "create" | "update" | "execute" | "send" | "unknown";
|
|
2
|
+
interface ToolClassification {
|
|
3
|
+
intent: ToolIntent;
|
|
4
|
+
isPassthrough: boolean;
|
|
5
|
+
confidence: number;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
interface ToolCall {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
params: Record<string, unknown>;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
classification: ToolClassification;
|
|
14
|
+
}
|
|
15
|
+
interface ExecutedRead extends ToolCall {
|
|
16
|
+
result: unknown;
|
|
17
|
+
}
|
|
18
|
+
interface PendingAction extends ToolCall {
|
|
19
|
+
phantomResult: unknown;
|
|
20
|
+
approved?: boolean;
|
|
21
|
+
editedParams?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
type SessionStatus = "running" | "pending_approval" | "approved" | "rejected" | "partial";
|
|
24
|
+
interface Session {
|
|
25
|
+
id: string;
|
|
26
|
+
agentName: string;
|
|
27
|
+
taskDescription: string;
|
|
28
|
+
startedAt: number;
|
|
29
|
+
completedAt?: number;
|
|
30
|
+
status: SessionStatus;
|
|
31
|
+
reads: ExecutedRead[];
|
|
32
|
+
pendingActions: PendingAction[];
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
interface ApprovalRequest {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
agentName: string;
|
|
38
|
+
taskDescription: string;
|
|
39
|
+
reads: ExecutedRead[];
|
|
40
|
+
pendingActions: PendingAction[];
|
|
41
|
+
createdAt: number;
|
|
42
|
+
}
|
|
43
|
+
type ActionDecision = {
|
|
44
|
+
action: "approve";
|
|
45
|
+
} | {
|
|
46
|
+
action: "reject";
|
|
47
|
+
} | {
|
|
48
|
+
action: "edit";
|
|
49
|
+
actionId: string;
|
|
50
|
+
newParams: Record<string, unknown>;
|
|
51
|
+
};
|
|
52
|
+
interface ApprovalResult {
|
|
53
|
+
sessionId: string;
|
|
54
|
+
decisions: Map<string, ActionDecision>;
|
|
55
|
+
approvedAt: number;
|
|
56
|
+
}
|
|
57
|
+
interface ToolGateConfig {
|
|
58
|
+
/** Custom classifier — override the built-in heuristic */
|
|
59
|
+
classifier?: (toolName: string, params: Record<string, unknown>) => ToolClassification;
|
|
60
|
+
/** Explicit read tool names (always passthrough) */
|
|
61
|
+
readTools?: string[];
|
|
62
|
+
/** Explicit write/action tool names (always intercept) */
|
|
63
|
+
actionTools?: string[];
|
|
64
|
+
/** What to return to the agent when an action is intercepted */
|
|
65
|
+
phantomResponse?: (tool: ToolCall) => unknown;
|
|
66
|
+
/** Called when session completes and needs approval */
|
|
67
|
+
onApprovalNeeded?: (request: ApprovalRequest) => Promise<ApprovalResult>;
|
|
68
|
+
/** Supabase URL for dashboard persistence (optional) */
|
|
69
|
+
supabaseUrl?: string;
|
|
70
|
+
/** Supabase anon key (optional) */
|
|
71
|
+
supabaseKey?: string;
|
|
72
|
+
/** Agent display name */
|
|
73
|
+
agentName?: string;
|
|
74
|
+
/** MCP server definitions for auto-classification */
|
|
75
|
+
mcpServers?: MCPServerDef[];
|
|
76
|
+
}
|
|
77
|
+
interface MCPToolDef {
|
|
78
|
+
name: string;
|
|
79
|
+
description: string;
|
|
80
|
+
inputSchema?: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
interface MCPServerDef {
|
|
83
|
+
name: string;
|
|
84
|
+
url: string;
|
|
85
|
+
tools?: MCPToolDef[];
|
|
86
|
+
}
|
|
87
|
+
type RealExecutor = (toolName: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
88
|
+
|
|
89
|
+
declare class ToolGate {
|
|
90
|
+
private config;
|
|
91
|
+
private executor;
|
|
92
|
+
private session;
|
|
93
|
+
private mcpToolIndex;
|
|
94
|
+
private readSet;
|
|
95
|
+
private actionSet;
|
|
96
|
+
private persistence;
|
|
97
|
+
constructor(executor: RealExecutor, config?: ToolGateConfig);
|
|
98
|
+
/** Set a human-readable description for this task/session */
|
|
99
|
+
describe(taskDescription: string): this;
|
|
100
|
+
/** The proxy function you hand to the agent instead of the real executor */
|
|
101
|
+
get proxy(): RealExecutor;
|
|
102
|
+
/** Wrap an existing tool map (name → function) into a proxied tool map */
|
|
103
|
+
wrapTools<T extends Record<string, (...args: any[]) => Promise<unknown>>>(tools: T): T;
|
|
104
|
+
/** Core: classify and either passthrough or intercept */
|
|
105
|
+
private handle;
|
|
106
|
+
private classify;
|
|
107
|
+
private defaultPhantom;
|
|
108
|
+
/** Call when agent is done. Returns the approval request. */
|
|
109
|
+
finalize(): Promise<ApprovalRequest>;
|
|
110
|
+
/** Execute approved actions for real */
|
|
111
|
+
executeApproval(result: ApprovalResult): Promise<Map<string, unknown>>;
|
|
112
|
+
/** Approve all pending actions at once */
|
|
113
|
+
approveAll(): Promise<Map<string, unknown>>;
|
|
114
|
+
/** Reject all pending actions */
|
|
115
|
+
rejectAll(): Promise<void>;
|
|
116
|
+
get sessionId(): string;
|
|
117
|
+
get reads(): ExecutedRead[];
|
|
118
|
+
get pending(): PendingAction[];
|
|
119
|
+
get currentSession(): Session;
|
|
120
|
+
/** Pretty-print a summary for CLI / logging */
|
|
121
|
+
summary(): string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Interactive CLI approval flow.
|
|
126
|
+
* Pass this as `onApprovalNeeded` in ToolGateConfig to get a
|
|
127
|
+
* terminal-based approval UI.
|
|
128
|
+
*/
|
|
129
|
+
declare function cliApproval(request: ApprovalRequest): Promise<ApprovalResult>;
|
|
130
|
+
|
|
131
|
+
declare function classifyTool(toolName: string, params: Record<string, unknown>, mcpToolDef?: MCPToolDef): ToolClassification;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create a ToolGate specifically for MCP-based agents.
|
|
135
|
+
*
|
|
136
|
+
* Pass your MCP server definitions (with tool lists) and ToolGate will
|
|
137
|
+
* auto-classify every tool by analyzing its name + description.
|
|
138
|
+
*
|
|
139
|
+
* Usage:
|
|
140
|
+
* const gate = mcpToolGate(mcpExecutor, {
|
|
141
|
+
* mcpServers: [
|
|
142
|
+
* { name: "gmail", url: "https://gmail.mcp.example.com/sse", tools: [...] },
|
|
143
|
+
* { name: "github", url: "https://github.mcp.example.com/sse", tools: [...] },
|
|
144
|
+
* ],
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* // Hand gate.proxy to your agent as the tool executor
|
|
148
|
+
* await agent.run({ execute: gate.proxy });
|
|
149
|
+
* const approval = await gate.finalize();
|
|
150
|
+
*/
|
|
151
|
+
declare function mcpToolGate(executor: RealExecutor, config?: ToolGateConfig): ToolGate;
|
|
152
|
+
/**
|
|
153
|
+
* Fetch MCP tool definitions from a running MCP server.
|
|
154
|
+
* Calls the standard `tools/list` JSON-RPC method.
|
|
155
|
+
*/
|
|
156
|
+
declare function discoverMCPTools(serverUrl: string): Promise<MCPToolDef[]>;
|
|
157
|
+
/**
|
|
158
|
+
* Auto-discover tools from all MCP servers and return a fully configured ToolGateConfig.
|
|
159
|
+
*/
|
|
160
|
+
declare function autoConfigFromMCP(servers: Array<{
|
|
161
|
+
name: string;
|
|
162
|
+
url: string;
|
|
163
|
+
}>, overrides?: Partial<ToolGateConfig>): Promise<ToolGateConfig>;
|
|
164
|
+
|
|
165
|
+
export { type ActionDecision, type ApprovalRequest, type ApprovalResult, type ExecutedRead, type MCPServerDef, type MCPToolDef, type PendingAction, type RealExecutor, type Session, type SessionStatus, type ToolCall, type ToolClassification, ToolGate, type ToolGateConfig, type ToolIntent, autoConfigFromMCP, classifyTool, cliApproval, discoverMCPTools, mcpToolGate };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
type ToolIntent = "read" | "write" | "delete" | "create" | "update" | "execute" | "send" | "unknown";
|
|
2
|
+
interface ToolClassification {
|
|
3
|
+
intent: ToolIntent;
|
|
4
|
+
isPassthrough: boolean;
|
|
5
|
+
confidence: number;
|
|
6
|
+
reason: string;
|
|
7
|
+
}
|
|
8
|
+
interface ToolCall {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
params: Record<string, unknown>;
|
|
12
|
+
timestamp: number;
|
|
13
|
+
classification: ToolClassification;
|
|
14
|
+
}
|
|
15
|
+
interface ExecutedRead extends ToolCall {
|
|
16
|
+
result: unknown;
|
|
17
|
+
}
|
|
18
|
+
interface PendingAction extends ToolCall {
|
|
19
|
+
phantomResult: unknown;
|
|
20
|
+
approved?: boolean;
|
|
21
|
+
editedParams?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
type SessionStatus = "running" | "pending_approval" | "approved" | "rejected" | "partial";
|
|
24
|
+
interface Session {
|
|
25
|
+
id: string;
|
|
26
|
+
agentName: string;
|
|
27
|
+
taskDescription: string;
|
|
28
|
+
startedAt: number;
|
|
29
|
+
completedAt?: number;
|
|
30
|
+
status: SessionStatus;
|
|
31
|
+
reads: ExecutedRead[];
|
|
32
|
+
pendingActions: PendingAction[];
|
|
33
|
+
metadata?: Record<string, unknown>;
|
|
34
|
+
}
|
|
35
|
+
interface ApprovalRequest {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
agentName: string;
|
|
38
|
+
taskDescription: string;
|
|
39
|
+
reads: ExecutedRead[];
|
|
40
|
+
pendingActions: PendingAction[];
|
|
41
|
+
createdAt: number;
|
|
42
|
+
}
|
|
43
|
+
type ActionDecision = {
|
|
44
|
+
action: "approve";
|
|
45
|
+
} | {
|
|
46
|
+
action: "reject";
|
|
47
|
+
} | {
|
|
48
|
+
action: "edit";
|
|
49
|
+
actionId: string;
|
|
50
|
+
newParams: Record<string, unknown>;
|
|
51
|
+
};
|
|
52
|
+
interface ApprovalResult {
|
|
53
|
+
sessionId: string;
|
|
54
|
+
decisions: Map<string, ActionDecision>;
|
|
55
|
+
approvedAt: number;
|
|
56
|
+
}
|
|
57
|
+
interface ToolGateConfig {
|
|
58
|
+
/** Custom classifier — override the built-in heuristic */
|
|
59
|
+
classifier?: (toolName: string, params: Record<string, unknown>) => ToolClassification;
|
|
60
|
+
/** Explicit read tool names (always passthrough) */
|
|
61
|
+
readTools?: string[];
|
|
62
|
+
/** Explicit write/action tool names (always intercept) */
|
|
63
|
+
actionTools?: string[];
|
|
64
|
+
/** What to return to the agent when an action is intercepted */
|
|
65
|
+
phantomResponse?: (tool: ToolCall) => unknown;
|
|
66
|
+
/** Called when session completes and needs approval */
|
|
67
|
+
onApprovalNeeded?: (request: ApprovalRequest) => Promise<ApprovalResult>;
|
|
68
|
+
/** Supabase URL for dashboard persistence (optional) */
|
|
69
|
+
supabaseUrl?: string;
|
|
70
|
+
/** Supabase anon key (optional) */
|
|
71
|
+
supabaseKey?: string;
|
|
72
|
+
/** Agent display name */
|
|
73
|
+
agentName?: string;
|
|
74
|
+
/** MCP server definitions for auto-classification */
|
|
75
|
+
mcpServers?: MCPServerDef[];
|
|
76
|
+
}
|
|
77
|
+
interface MCPToolDef {
|
|
78
|
+
name: string;
|
|
79
|
+
description: string;
|
|
80
|
+
inputSchema?: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
interface MCPServerDef {
|
|
83
|
+
name: string;
|
|
84
|
+
url: string;
|
|
85
|
+
tools?: MCPToolDef[];
|
|
86
|
+
}
|
|
87
|
+
type RealExecutor = (toolName: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
88
|
+
|
|
89
|
+
declare class ToolGate {
|
|
90
|
+
private config;
|
|
91
|
+
private executor;
|
|
92
|
+
private session;
|
|
93
|
+
private mcpToolIndex;
|
|
94
|
+
private readSet;
|
|
95
|
+
private actionSet;
|
|
96
|
+
private persistence;
|
|
97
|
+
constructor(executor: RealExecutor, config?: ToolGateConfig);
|
|
98
|
+
/** Set a human-readable description for this task/session */
|
|
99
|
+
describe(taskDescription: string): this;
|
|
100
|
+
/** The proxy function you hand to the agent instead of the real executor */
|
|
101
|
+
get proxy(): RealExecutor;
|
|
102
|
+
/** Wrap an existing tool map (name → function) into a proxied tool map */
|
|
103
|
+
wrapTools<T extends Record<string, (...args: any[]) => Promise<unknown>>>(tools: T): T;
|
|
104
|
+
/** Core: classify and either passthrough or intercept */
|
|
105
|
+
private handle;
|
|
106
|
+
private classify;
|
|
107
|
+
private defaultPhantom;
|
|
108
|
+
/** Call when agent is done. Returns the approval request. */
|
|
109
|
+
finalize(): Promise<ApprovalRequest>;
|
|
110
|
+
/** Execute approved actions for real */
|
|
111
|
+
executeApproval(result: ApprovalResult): Promise<Map<string, unknown>>;
|
|
112
|
+
/** Approve all pending actions at once */
|
|
113
|
+
approveAll(): Promise<Map<string, unknown>>;
|
|
114
|
+
/** Reject all pending actions */
|
|
115
|
+
rejectAll(): Promise<void>;
|
|
116
|
+
get sessionId(): string;
|
|
117
|
+
get reads(): ExecutedRead[];
|
|
118
|
+
get pending(): PendingAction[];
|
|
119
|
+
get currentSession(): Session;
|
|
120
|
+
/** Pretty-print a summary for CLI / logging */
|
|
121
|
+
summary(): string;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Interactive CLI approval flow.
|
|
126
|
+
* Pass this as `onApprovalNeeded` in ToolGateConfig to get a
|
|
127
|
+
* terminal-based approval UI.
|
|
128
|
+
*/
|
|
129
|
+
declare function cliApproval(request: ApprovalRequest): Promise<ApprovalResult>;
|
|
130
|
+
|
|
131
|
+
declare function classifyTool(toolName: string, params: Record<string, unknown>, mcpToolDef?: MCPToolDef): ToolClassification;
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Create a ToolGate specifically for MCP-based agents.
|
|
135
|
+
*
|
|
136
|
+
* Pass your MCP server definitions (with tool lists) and ToolGate will
|
|
137
|
+
* auto-classify every tool by analyzing its name + description.
|
|
138
|
+
*
|
|
139
|
+
* Usage:
|
|
140
|
+
* const gate = mcpToolGate(mcpExecutor, {
|
|
141
|
+
* mcpServers: [
|
|
142
|
+
* { name: "gmail", url: "https://gmail.mcp.example.com/sse", tools: [...] },
|
|
143
|
+
* { name: "github", url: "https://github.mcp.example.com/sse", tools: [...] },
|
|
144
|
+
* ],
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* // Hand gate.proxy to your agent as the tool executor
|
|
148
|
+
* await agent.run({ execute: gate.proxy });
|
|
149
|
+
* const approval = await gate.finalize();
|
|
150
|
+
*/
|
|
151
|
+
declare function mcpToolGate(executor: RealExecutor, config?: ToolGateConfig): ToolGate;
|
|
152
|
+
/**
|
|
153
|
+
* Fetch MCP tool definitions from a running MCP server.
|
|
154
|
+
* Calls the standard `tools/list` JSON-RPC method.
|
|
155
|
+
*/
|
|
156
|
+
declare function discoverMCPTools(serverUrl: string): Promise<MCPToolDef[]>;
|
|
157
|
+
/**
|
|
158
|
+
* Auto-discover tools from all MCP servers and return a fully configured ToolGateConfig.
|
|
159
|
+
*/
|
|
160
|
+
declare function autoConfigFromMCP(servers: Array<{
|
|
161
|
+
name: string;
|
|
162
|
+
url: string;
|
|
163
|
+
}>, overrides?: Partial<ToolGateConfig>): Promise<ToolGateConfig>;
|
|
164
|
+
|
|
165
|
+
export { type ActionDecision, type ApprovalRequest, type ApprovalResult, type ExecutedRead, type MCPServerDef, type MCPToolDef, type PendingAction, type RealExecutor, type Session, type SessionStatus, type ToolCall, type ToolClassification, ToolGate, type ToolGateConfig, type ToolIntent, autoConfigFromMCP, classifyTool, cliApproval, discoverMCPTools, mcpToolGate };
|