@ziggs-ai/agent-sdk 0.1.9 → 0.1.10
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/package.json
CHANGED
|
@@ -8,7 +8,110 @@ import type { Workflow } from '../types.js';
|
|
|
8
8
|
|
|
9
9
|
export type McpTransportConfig =
|
|
10
10
|
| { type: 'stdio'; command: string; args?: string[]; env?: Record<string, string> }
|
|
11
|
-
| { type: 'http'; url: string }
|
|
11
|
+
| { type: 'http'; url: string }
|
|
12
|
+
| {
|
|
13
|
+
/**
|
|
14
|
+
* Brokered connection through the Ziggs MCP gateway: the agent speaks
|
|
15
|
+
* StreamableHTTP to `/connections/:id/mcp` and Ziggs injects the
|
|
16
|
+
* connection owner's OAuth token upstream. The agent never holds the
|
|
17
|
+
* token; every tools/call is checked against the grant's caveats.
|
|
18
|
+
*/
|
|
19
|
+
type: 'ziggs';
|
|
20
|
+
/** Connection id (`con_…`) of the user's MCP server connection. */
|
|
21
|
+
connectionId: string;
|
|
22
|
+
/** ConnectionGrant id (`cgr_…`) issued to this agent. */
|
|
23
|
+
grantId: string;
|
|
24
|
+
/** Operator key — defaults to ZIGGS_OPERATOR_KEY. */
|
|
25
|
+
operatorKey?: string;
|
|
26
|
+
/** Ziggs backend base URL — defaults to BACKEND_URL / ZIGGS_BACKEND_URL. */
|
|
27
|
+
baseUrl?: string;
|
|
28
|
+
/** Agent to impersonate (X-Agent-Id) — defaults to the host agentId. */
|
|
29
|
+
agentId?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Typed failure from the Ziggs MCP gateway (ZIG-702). `code` is the stable
|
|
34
|
+
* machine-readable reason — e.g. `GRANT_REVOKED`, `GRANT_EXPIRED`,
|
|
35
|
+
* `GRANT_HOLDER_MISMATCH`, `GRANT_CONNECTION_MISMATCH` — so agent code can
|
|
36
|
+
* branch ("revoked → tell the user and stop retrying" vs "network blip →
|
|
37
|
+
* retry") instead of parsing transport-error prose.
|
|
38
|
+
*/
|
|
39
|
+
export class ZiggsGatewayError extends Error {
|
|
40
|
+
readonly code: string;
|
|
41
|
+
readonly status: number;
|
|
42
|
+
|
|
43
|
+
constructor(code: string, message: string, status: number) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = 'ZiggsGatewayError';
|
|
46
|
+
this.code = code;
|
|
47
|
+
this.status = status;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Wrap fetch so structured gateway denials (401/403 with a `code` body)
|
|
53
|
+
* surface as ZiggsGatewayError instead of a generic transport failure.
|
|
54
|
+
*/
|
|
55
|
+
export function wrapGatewayFetch(
|
|
56
|
+
baseFetch: typeof fetch = fetch,
|
|
57
|
+
): typeof fetch {
|
|
58
|
+
return (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
59
|
+
const response = await baseFetch(input, init);
|
|
60
|
+
if (response.status === 401 || response.status === 403) {
|
|
61
|
+
const body = (await response
|
|
62
|
+
.clone()
|
|
63
|
+
.json()
|
|
64
|
+
.catch(() => null)) as { code?: unknown; message?: unknown } | null;
|
|
65
|
+
if (body && typeof body.code === 'string') {
|
|
66
|
+
throw new ZiggsGatewayError(
|
|
67
|
+
body.code,
|
|
68
|
+
typeof body.message === 'string' ? body.message : body.code,
|
|
69
|
+
response.status,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return response;
|
|
74
|
+
}) as typeof fetch;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Resolve the gateway URL + auth headers for a `type: 'ziggs'` transport. */
|
|
78
|
+
export function resolveZiggsGatewayTarget(
|
|
79
|
+
config: Extract<McpTransportConfig, { type: 'ziggs' }>,
|
|
80
|
+
defaults: { agentId?: string } = {},
|
|
81
|
+
): { url: URL; headers: Record<string, string> } {
|
|
82
|
+
if (!config.connectionId) {
|
|
83
|
+
throw new Error('ziggs MCP transport requires a connectionId');
|
|
84
|
+
}
|
|
85
|
+
if (!config.grantId) {
|
|
86
|
+
throw new Error('ziggs MCP transport requires a grantId');
|
|
87
|
+
}
|
|
88
|
+
const operatorKey = config.operatorKey || process.env.ZIGGS_OPERATOR_KEY;
|
|
89
|
+
if (!operatorKey) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
'ziggs MCP transport requires an operatorKey (or ZIGGS_OPERATOR_KEY)',
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const agentId = config.agentId || defaults.agentId;
|
|
95
|
+
if (!agentId) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
'ziggs MCP transport requires an agentId to impersonate (X-Agent-Id)',
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
const baseUrl = (
|
|
101
|
+
config.baseUrl ||
|
|
102
|
+
process.env.BACKEND_URL ||
|
|
103
|
+
process.env.ZIGGS_BACKEND_URL ||
|
|
104
|
+
'http://localhost:3000'
|
|
105
|
+
).replace(/\/$/, '');
|
|
106
|
+
return {
|
|
107
|
+
url: new URL(`${baseUrl}/connections/${encodeURIComponent(config.connectionId)}/mcp`),
|
|
108
|
+
headers: {
|
|
109
|
+
authorization: `Bearer ${operatorKey}`,
|
|
110
|
+
'x-agent-id': agentId,
|
|
111
|
+
'x-grant-id': config.grantId,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
12
115
|
|
|
13
116
|
export interface CreateMcpAgentOptions extends Omit<AgentHostOptions, 'tools' | 'workflow' | 'states' | 'initial'> {
|
|
14
117
|
/** MCP server connection — stdio (local process) or HTTP (remote). */
|
|
@@ -27,7 +130,7 @@ export interface McpAgentHandle {
|
|
|
27
130
|
toolNames: string[];
|
|
28
131
|
}
|
|
29
132
|
|
|
30
|
-
function buildTransport(config: McpTransportConfig) {
|
|
133
|
+
function buildTransport(config: McpTransportConfig, defaults: { agentId?: string }) {
|
|
31
134
|
if (config.type === 'stdio') {
|
|
32
135
|
return new StdioClientTransport({
|
|
33
136
|
command: config.command,
|
|
@@ -35,6 +138,13 @@ function buildTransport(config: McpTransportConfig) {
|
|
|
35
138
|
env: config.env,
|
|
36
139
|
});
|
|
37
140
|
}
|
|
141
|
+
if (config.type === 'ziggs') {
|
|
142
|
+
const target = resolveZiggsGatewayTarget(config, defaults);
|
|
143
|
+
return new StreamableHTTPClientTransport(target.url, {
|
|
144
|
+
requestInit: { headers: target.headers },
|
|
145
|
+
fetch: wrapGatewayFetch(),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
38
148
|
return new StreamableHTTPClientTransport(new URL(config.url));
|
|
39
149
|
}
|
|
40
150
|
|
|
@@ -81,28 +191,6 @@ function mapMcpTools(client: Client, tools: McpTool[]): ToolDefinition[] {
|
|
|
81
191
|
}));
|
|
82
192
|
}
|
|
83
193
|
|
|
84
|
-
/**
|
|
85
|
-
* Wraps any MCP server as a Ziggs AgentHost.
|
|
86
|
-
*
|
|
87
|
-
* 1. Opens a connection to the MCP server (stdio or HTTP).
|
|
88
|
-
* 2. Calls tools/list and maps each tool to a Ziggs ToolDefinition.
|
|
89
|
-
* 3. Returns an AgentHost pre-loaded with those tools, ready to connectAsync().
|
|
90
|
-
*
|
|
91
|
-
* Usage:
|
|
92
|
-
* const { host, closeMcp } = await createMcpAgent({
|
|
93
|
-
* transport: { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-github'] },
|
|
94
|
-
* agentId: 'my-github-agent',
|
|
95
|
-
* name: 'GitHub Agent',
|
|
96
|
-
* description: 'Wraps the GitHub MCP server',
|
|
97
|
-
* });
|
|
98
|
-
* await host.connectAsync();
|
|
99
|
-
*
|
|
100
|
-
* process.on('SIGTERM', async () => {
|
|
101
|
-
* host.disconnect();
|
|
102
|
-
* await host.drain();
|
|
103
|
-
* await closeMcp();
|
|
104
|
-
* });
|
|
105
|
-
*/
|
|
106
194
|
function buildDefaultWorkflow(description: string, mcpTools: McpTool[]): Workflow {
|
|
107
195
|
const dflt = thinkingDefaults({ initial: 'idle' });
|
|
108
196
|
// Bind each MCP tool as an explicit action so runTurn includes them in the
|
|
@@ -157,11 +245,11 @@ function buildDefaultWorkflow(description: string, mcpTools: McpTool[]): Workflo
|
|
|
157
245
|
/**
|
|
158
246
|
* Wraps any MCP server as a Ziggs AgentHost.
|
|
159
247
|
*
|
|
160
|
-
* 1. Opens a connection to the MCP server (stdio or
|
|
248
|
+
* 1. Opens a connection to the MCP server (stdio, HTTP, or the Ziggs gateway).
|
|
161
249
|
* 2. Calls tools/list and maps each tool to a Ziggs ToolDefinition.
|
|
162
250
|
* 3. Returns an AgentHost pre-loaded with those tools, ready to connectAsync().
|
|
163
251
|
*
|
|
164
|
-
* Usage:
|
|
252
|
+
* Usage (local stdio server):
|
|
165
253
|
* const { host, closeMcp } = await createMcpAgent({
|
|
166
254
|
* transport: { type: 'stdio', command: 'npx', args: ['-y', '@modelcontextprotocol/server-github'] },
|
|
167
255
|
* agentId: 'my-github-agent',
|
|
@@ -170,6 +258,17 @@ function buildDefaultWorkflow(description: string, mcpTools: McpTool[]): Workflo
|
|
|
170
258
|
* });
|
|
171
259
|
* await host.connectAsync();
|
|
172
260
|
*
|
|
261
|
+
* Usage (OAuth-protected remote server, brokered through Ziggs — the user
|
|
262
|
+
* connected the server and issued this agent a grant; the agent never sees
|
|
263
|
+
* the user's token):
|
|
264
|
+
* const { host, closeMcp } = await createMcpAgent({
|
|
265
|
+
* transport: { type: 'ziggs', connectionId: 'con_abc', grantId: 'cgr_xyz' },
|
|
266
|
+
* agentId: 'my-notion-agent',
|
|
267
|
+
* name: 'Notion Agent',
|
|
268
|
+
* description: 'Works in the owner\'s Notion via their MCP connection',
|
|
269
|
+
* });
|
|
270
|
+
* await host.connectAsync();
|
|
271
|
+
*
|
|
173
272
|
* process.on('SIGTERM', async () => {
|
|
174
273
|
* host.disconnect();
|
|
175
274
|
* await host.drain();
|
|
@@ -179,7 +278,7 @@ function buildDefaultWorkflow(description: string, mcpTools: McpTool[]): Workflo
|
|
|
179
278
|
export async function createMcpAgent(options: CreateMcpAgentOptions): Promise<McpAgentHandle> {
|
|
180
279
|
const { transport: transportConfig, extraTools = [], workflow, ...hostOptions } = options;
|
|
181
280
|
|
|
182
|
-
const transport = buildTransport(transportConfig);
|
|
281
|
+
const transport = buildTransport(transportConfig, { agentId: hostOptions.agentId });
|
|
183
282
|
const client = new Client({ name: 'ziggs-mcp-agent', version: '1.0.0' });
|
|
184
283
|
|
|
185
284
|
await client.connect(transport);
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export { defineAgent } from './defineAgent.js';
|
|
12
|
-
export { createMcpAgent } from './adapters/McpAdapter.js';
|
|
12
|
+
export { createMcpAgent, resolveZiggsGatewayTarget, wrapGatewayFetch, ZiggsGatewayError } from './adapters/McpAdapter.js';
|
|
13
13
|
export type { CreateMcpAgentOptions, McpAgentHandle, McpTransportConfig } from './adapters/McpAdapter.js';
|
|
14
14
|
export { AgentHost, createAgent, createAgentPool } from './AgentHost.js';
|
|
15
15
|
export { Agent } from './agent/Agent.js';
|
|
@@ -32,7 +32,7 @@ export {
|
|
|
32
32
|
PAYMENT_TOOLS,
|
|
33
33
|
paymentBalanceTool, paymentTransferTool, paymentWaitForApprovalTool,
|
|
34
34
|
paymentHoldTool, paymentReleaseTool, paymentResolveWalletTool,
|
|
35
|
-
|
|
35
|
+
paymentIssueGrantTool, paymentAttenuateGrantTool, paymentRevokeGrantTool, paymentListGrantsTool,
|
|
36
36
|
} from './tier2/paymentTools.js';
|
|
37
37
|
export {
|
|
38
38
|
CONNECTION_TOOLS,
|
|
@@ -121,14 +121,8 @@ export const paymentIssueGrantTool: ToolDefinition = defineTool('payment_issue_g
|
|
|
121
121
|
issueGrantHandler,
|
|
122
122
|
);
|
|
123
123
|
|
|
124
|
-
/** @deprecated Use payment_issue_grant */
|
|
125
|
-
export const paymentIssueTokenTool: ToolDefinition = defineTool('payment_issue_token',
|
|
126
|
-
{ holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' },
|
|
127
|
-
issueGrantHandler,
|
|
128
|
-
);
|
|
129
|
-
|
|
130
124
|
const attenuateGrantHandler = async (args: AnyObj, ctx: Ctx) => {
|
|
131
|
-
const grantId =
|
|
125
|
+
const grantId = args['grantId'] as string;
|
|
132
126
|
if (!grantId) throw new Error('grantId is required');
|
|
133
127
|
if (!args['holderId']) throw new Error('holderId is required');
|
|
134
128
|
try {
|
|
@@ -144,14 +138,8 @@ export const paymentAttenuateGrantTool: ToolDefinition = defineTool('payment_att
|
|
|
144
138
|
attenuateGrantHandler,
|
|
145
139
|
);
|
|
146
140
|
|
|
147
|
-
/** @deprecated Use payment_attenuate_grant */
|
|
148
|
-
export const paymentAttenuateTokenTool: ToolDefinition = defineTool('payment_attenuate_token',
|
|
149
|
-
{ tokenId: { type: 'string', required: true }, holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' },
|
|
150
|
-
attenuateGrantHandler,
|
|
151
|
-
);
|
|
152
|
-
|
|
153
141
|
const revokeGrantHandler = async (args: AnyObj, ctx: Ctx) => {
|
|
154
|
-
const grantId =
|
|
142
|
+
const grantId = args['grantId'] as string;
|
|
155
143
|
if (!grantId) throw new Error('grantId is required');
|
|
156
144
|
try {
|
|
157
145
|
const result = await clientFor(ctx).revokeGrant(grantId) as AnyObj;
|
|
@@ -164,12 +152,6 @@ export const paymentRevokeGrantTool: ToolDefinition = defineTool('payment_revoke
|
|
|
164
152
|
revokeGrantHandler,
|
|
165
153
|
);
|
|
166
154
|
|
|
167
|
-
/** @deprecated Use payment_revoke_grant */
|
|
168
|
-
export const paymentRevokeTokenTool: ToolDefinition = defineTool('payment_revoke_token',
|
|
169
|
-
{ tokenId: { type: 'string', required: true } },
|
|
170
|
-
revokeGrantHandler,
|
|
171
|
-
);
|
|
172
|
-
|
|
173
155
|
const listGrantsHandler = async (_args: AnyObj, ctx: Ctx) => {
|
|
174
156
|
try {
|
|
175
157
|
const result = await clientFor(ctx).listGrants();
|
|
@@ -181,11 +163,6 @@ export const paymentListGrantsTool: ToolDefinition = defineTool('payment_list_gr
|
|
|
181
163
|
listGrantsHandler,
|
|
182
164
|
);
|
|
183
165
|
|
|
184
|
-
/** @deprecated Use payment_list_grants */
|
|
185
|
-
export const paymentListTokensTool: ToolDefinition = defineTool('payment_list_tokens', {},
|
|
186
|
-
listGrantsHandler,
|
|
187
|
-
);
|
|
188
|
-
|
|
189
166
|
export const PAYMENT_TOOLS: ToolDefinition[] = [
|
|
190
167
|
paymentBalanceTool, paymentTransferTool, paymentWaitForApprovalTool,
|
|
191
168
|
paymentHoldTool, paymentReleaseTool, paymentResolveWalletTool,
|