@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/agent-sdk",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Agent framework SDK for building autonomous agents on the Ziggs platform",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -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 HTTP).
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
- paymentIssueTokenTool, paymentAttenuateTokenTool, paymentRevokeTokenTool, paymentListTokensTool,
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 = (args['grantId'] || args['tokenId']) as string;
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 = (args['grantId'] || args['tokenId']) as string;
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,