@ziggs-ai/ziggs-mcp 0.16.0 → 0.17.1

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/config.d.ts CHANGED
@@ -10,6 +10,26 @@ declare const envSchema: z.ZodObject<{
10
10
  ZIGGS_AGENT_ID: z.ZodOptional<z.ZodString>;
11
11
  /** Human user id for payer-side proposals — defaults the payer on propose/publish tools. */
12
12
  ZIGGS_OWNER_USER_ID: z.ZodOptional<z.ZodString>;
13
+ /**
14
+ * The lane this server's calls belong to — a chat id, or `agrn-<agreementId>`.
15
+ * Sent as `X-Ziggs-Lane` on every fenced call. Unset means the agent acts for
16
+ * itself, which is right for a person who configured this server with their
17
+ * own key, and wrong for a delegate that has been hired: that one holds
18
+ * grants its CUSTOMER gave, and the server refuses those to a wake that
19
+ * states no lane.
20
+ */
21
+ ZIGGS_LANE_ID: z.ZodOptional<z.ZodString>;
22
+ /**
23
+ * Pins this server's inbox host identity (`X-Ziggs-Instance`).
24
+ *
25
+ * One host owns an agent's inbox and a second is refused for 330 seconds, so
26
+ * a stdio server that a SCHEDULER respawns per run — the NanoClaw
27
+ * one-minute task starting `npx @ziggs-ai/ziggs-mcp` — is a new host every
28
+ * run and is refused by the previous run's lease. Set this to the same value
29
+ * on every run and they are one host. A server that stays up needs nothing:
30
+ * its process is its host.
31
+ */
32
+ ZIGGS_INSTANCE_ID: z.ZodOptional<z.ZodString>;
13
33
  /** Set to 1/true/yes to register internal/debug-only MCP tools. */
14
34
  ZIGGS_MCP_DEBUG: z.ZodOptional<z.ZodString>;
15
35
  /**
@@ -25,6 +45,8 @@ declare const envSchema: z.ZodObject<{
25
45
  ZIGGS_WEB_URL?: string | undefined;
26
46
  ZIGGS_AGENT_ID?: string | undefined;
27
47
  ZIGGS_OWNER_USER_ID?: string | undefined;
48
+ ZIGGS_LANE_ID?: string | undefined;
49
+ ZIGGS_INSTANCE_ID?: string | undefined;
28
50
  ZIGGS_MCP_DEBUG?: string | undefined;
29
51
  ZIGGS_MCP_CORE_ONLY?: string | undefined;
30
52
  }, {
@@ -34,6 +56,8 @@ declare const envSchema: z.ZodObject<{
34
56
  ZIGGS_WEB_URL?: string | undefined;
35
57
  ZIGGS_AGENT_ID?: string | undefined;
36
58
  ZIGGS_OWNER_USER_ID?: string | undefined;
59
+ ZIGGS_LANE_ID?: string | undefined;
60
+ ZIGGS_INSTANCE_ID?: string | undefined;
37
61
  ZIGGS_MCP_DEBUG?: string | undefined;
38
62
  ZIGGS_MCP_CORE_ONLY?: string | undefined;
39
63
  }>;
package/dist/config.js CHANGED
@@ -12,6 +12,26 @@ const envSchema = z.object({
12
12
  ZIGGS_AGENT_ID: z.string().optional(),
13
13
  /** Human user id for payer-side proposals — defaults the payer on propose/publish tools. */
14
14
  ZIGGS_OWNER_USER_ID: z.string().optional(),
15
+ /**
16
+ * The lane this server's calls belong to — a chat id, or `agrn-<agreementId>`.
17
+ * Sent as `X-Ziggs-Lane` on every fenced call. Unset means the agent acts for
18
+ * itself, which is right for a person who configured this server with their
19
+ * own key, and wrong for a delegate that has been hired: that one holds
20
+ * grants its CUSTOMER gave, and the server refuses those to a wake that
21
+ * states no lane.
22
+ */
23
+ ZIGGS_LANE_ID: z.string().optional(),
24
+ /**
25
+ * Pins this server's inbox host identity (`X-Ziggs-Instance`).
26
+ *
27
+ * One host owns an agent's inbox and a second is refused for 330 seconds, so
28
+ * a stdio server that a SCHEDULER respawns per run — the NanoClaw
29
+ * one-minute task starting `npx @ziggs-ai/ziggs-mcp` — is a new host every
30
+ * run and is refused by the previous run's lease. Set this to the same value
31
+ * on every run and they are one host. A server that stays up needs nothing:
32
+ * its process is its host.
33
+ */
34
+ ZIGGS_INSTANCE_ID: z.string().optional(),
15
35
  /** Set to 1/true/yes to register internal/debug-only MCP tools. */
16
36
  ZIGGS_MCP_DEBUG: z.string().optional(),
17
37
  /**
@@ -37,6 +57,8 @@ export function loadConfig() {
37
57
  ZIGGS_OPERATOR_KEY: process.env.ZIGGS_OPERATOR_KEY,
38
58
  ZIGGS_AGENT_ID: process.env.ZIGGS_AGENT_ID,
39
59
  ZIGGS_OWNER_USER_ID: process.env.ZIGGS_OWNER_USER_ID,
60
+ ZIGGS_LANE_ID: process.env.ZIGGS_LANE_ID,
61
+ ZIGGS_INSTANCE_ID: process.env.ZIGGS_INSTANCE_ID,
40
62
  ZIGGS_MCP_DEBUG: process.env.ZIGGS_MCP_DEBUG,
41
63
  ZIGGS_MCP_CORE_ONLY: process.env.ZIGGS_MCP_CORE_ONLY,
42
64
  };
@@ -1,8 +1,16 @@
1
1
  import { type Creds } from '@ziggs-ai/api-client';
2
2
  import type { ZiggsMcpConfig } from './config.js';
3
3
  export declare function parseBearerAuthorization(header: string | string[] | undefined): string;
4
- /** Per-connection credentials from HTTP Authorization. */
5
- export declare function connectionFromBearer(bearer: string, httpBaseUrl: string, ownerUserId?: string): {
4
+ /**
5
+ * Per-connection credentials from HTTP Authorization.
6
+ *
7
+ * `laneId` is the wake's lane, read off `X-Ziggs-Lane` by the caller that
8
+ * mounts this server, exactly as `ownerUserId` is read off its own header. A
9
+ * connection that states none is fenced to the agent's own estate — right for
10
+ * a person who pasted their key, wrong for a delegate holding grants its
11
+ * customer gave.
12
+ */
13
+ export declare function connectionFromBearer(bearer: string, httpBaseUrl: string, ownerUserId?: string, laneId?: string): {
6
14
  creds: Creds;
7
15
  cfg: ZiggsMcpConfig;
8
16
  };
@@ -1,4 +1,4 @@
1
- import { configureApiClient } from '@ziggs-ai/api-client';
1
+ import { configureApiClient, mcpConnectionIdentity, } from '@ziggs-ai/api-client';
2
2
  import { MINT_KEY_HELP, decodeOperatorKeyClaims, isDirectoryBoarded, resolveDelegateAgentId, } from './operatorKey.js';
3
3
  export function parseBearerAuthorization(header) {
4
4
  const raw = Array.isArray(header) ? header[0] : header;
@@ -11,8 +11,16 @@ export function parseBearerAuthorization(header) {
11
11
  }
12
12
  return token;
13
13
  }
14
- /** Per-connection credentials from HTTP Authorization. */
15
- export function connectionFromBearer(bearer, httpBaseUrl, ownerUserId) {
14
+ /**
15
+ * Per-connection credentials from HTTP Authorization.
16
+ *
17
+ * `laneId` is the wake's lane, read off `X-Ziggs-Lane` by the caller that
18
+ * mounts this server, exactly as `ownerUserId` is read off its own header. A
19
+ * connection that states none is fenced to the agent's own estate — right for
20
+ * a person who pasted their key, wrong for a delegate holding grants its
21
+ * customer gave.
22
+ */
23
+ export function connectionFromBearer(bearer, httpBaseUrl, ownerUserId, laneId) {
16
24
  const resolvedAgentId = resolveDelegateAgentId(bearer, undefined);
17
25
  if (httpBaseUrl) {
18
26
  process.env.HTTP_URL = httpBaseUrl;
@@ -26,6 +34,7 @@ export function connectionFromBearer(bearer, httpBaseUrl, ownerUserId) {
26
34
  HTTP_URL: httpBaseUrl,
27
35
  ZIGGS_AGENT_ID: undefined,
28
36
  ZIGGS_OWNER_USER_ID: ownerUserId,
37
+ ZIGGS_LANE_ID: laneId,
29
38
  resolvedAgentId,
30
39
  debugTools: false,
31
40
  coreOnly: false,
@@ -35,8 +44,24 @@ export function connectionFromBearer(bearer, httpBaseUrl, ownerUserId) {
35
44
  // ZiggsMcpConfig.directConnection.
36
45
  directConnection: !isDirectoryBoarded(decodeOperatorKeyClaims(bearer)),
37
46
  };
47
+ /**
48
+ * The inbox host is this CONNECTION, not the process serving it.
49
+ *
50
+ * This server also runs inside the API process, which serves every connected
51
+ * assistant and gets a new identity on every deploy. Stamping that made all
52
+ * of them one host and moved it under all of them at once, so a hosted
53
+ * `ziggs_inbox` read answered 409 for up to 330 seconds after each restart.
54
+ * The key is what stays put across both. Undefined only for a credential
55
+ * carrying no `keyId`, which then falls back to the process default.
56
+ */
57
+ const instanceId = mcpConnectionIdentity(bearer);
38
58
  return {
39
- creds: { operatorKey: bearer, agentId: resolvedAgentId },
59
+ creds: {
60
+ operatorKey: bearer,
61
+ agentId: resolvedAgentId,
62
+ ...(laneId ? { laneId } : {}),
63
+ ...(instanceId ? { instanceId } : {}),
64
+ },
40
65
  cfg,
41
66
  };
42
67
  }
package/dist/creds.js CHANGED
@@ -2,5 +2,13 @@ export function credsFromConfig(cfg) {
2
2
  return {
3
3
  operatorKey: cfg.ZIGGS_OPERATOR_KEY,
4
4
  agentId: cfg.resolvedAgentId,
5
+ // The lane, so a hired delegate can spend the grants its customer gave it.
6
+ // Every client in this package reads it off `Creds`, so leaving it unset
7
+ // here silently fences all of them to the agent's own estate.
8
+ ...(cfg.ZIGGS_LANE_ID ? { laneId: cfg.ZIGGS_LANE_ID } : {}),
9
+ // Unset means this process is the host, which is right for a server that
10
+ // stays up and wrong for one a scheduler respawns per run — that one pins
11
+ // ZIGGS_INSTANCE_ID so every run is the same host.
12
+ ...(cfg.ZIGGS_INSTANCE_ID ? { instanceId: cfg.ZIGGS_INSTANCE_ID } : {}),
5
13
  };
6
14
  }
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
10
10
  import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
11
- import { ConnectionsClient, getBackendUrl, } from '@ziggs-ai/api-client';
11
+ import { ConnectionsClient, getBackendUrl, laneHeader, } from '@ziggs-ai/api-client';
12
12
  export function resolveMcpGatewayTarget(opts) {
13
13
  if (!opts.connectionId)
14
14
  throw new Error('connectionId is required');
@@ -25,7 +25,8 @@ export function resolveMcpGatewayTarget(opts) {
25
25
  authorization: `Bearer ${opts.operatorKey}`,
26
26
  'x-agent-id': opts.agentId,
27
27
  'x-grant-id': opts.grantId,
28
- ...(opts.laneId ? { 'x-ziggs-lane': opts.laneId } : {}),
28
+ // one helper for the lane, shared with every other door.
29
+ ...laneHeader(opts.laneId),
29
30
  },
30
31
  };
31
32
  }
package/dist/tools.js CHANGED
@@ -68,7 +68,7 @@ function loadSessionReads(creds) {
68
68
  // #4 — tasks and payments are independent; overlap them.
69
69
  return Promise.allSettled([
70
70
  listTasks({ state: 'active', limit: 20 }, creds),
71
- new PaymentsClient(creds.operatorKey, creds.agentId).approvals({
71
+ new PaymentsClient(creds.operatorKey, creds.agentId, undefined, creds.laneId).approvals({
72
72
  status: 'pending',
73
73
  }),
74
74
  ]);
@@ -207,7 +207,7 @@ function registerConnectionTools(server, creds, cfg) {
207
207
  ? 'How to use a row: provider "mcp" → ziggs_mcp_tools_list / ziggs_mcp_tool_call; named connectors (github, jira, …) → ziggs_connection_proxy.'
208
208
  : 'Rows are readable here but not callable over this connection: using a connection (an MCP server or a named connector such as github or jira) is only on a self-configured Ziggs MCP connection with an operator key.'), {}, readOnly('List connections you can use'), async () => {
209
209
  try {
210
- const connections = await new ConnectionsClient(creds.operatorKey, creds.agentId).listForHolder();
210
+ const connections = await new ConnectionsClient(creds.operatorKey, creds.agentId, undefined, creds.laneId).listForHolder();
211
211
  return textResult({ connections });
212
212
  }
213
213
  catch (e) {
@@ -321,7 +321,7 @@ export function registerZiggsTools(server, creds, cfg) {
321
321
  let snapshot = null;
322
322
  const firstChatId = chats[0]?.chatId;
323
323
  if (firstChatId) {
324
- const client = new ContextReadClient(creds.operatorKey, creds.agentId);
324
+ const client = new ContextReadClient(creds.operatorKey, creds.agentId, undefined, firstChatId);
325
325
  snapshot = await client.snapshot(firstChatId, { maxMessages: 5 });
326
326
  }
327
327
  return textResult({
@@ -345,7 +345,11 @@ export function registerZiggsTools(server, creds, cfg) {
345
345
  .describe('Optional grant id when reading under a context grant'),
346
346
  }, readOnly('Catch up on a chat'), async ({ chatId, maxMessages, contextGrantId }) => {
347
347
  try {
348
- const client = new ContextReadClient(creds.operatorKey, creds.agentId);
348
+ // The chat being read IS the lane: a snapshot of someone else's room
349
+ // is a call inside that engagement, and stating it is what lets a hired
350
+ // delegate read the room it was hired into. Falls back to the
351
+ // connection's lane when no chat is named.
352
+ const client = new ContextReadClient(creds.operatorKey, creds.agentId, undefined, chatId || creds.laneId);
349
353
  const result = await client.snapshot(chatId, {
350
354
  maxMessages,
351
355
  contextGrantId,
@@ -601,7 +605,9 @@ export function registerZiggsTools(server, creds, cfg) {
601
605
  .describe('Long-poll: hold up to this many seconds (server-clamped, ~110 max) and return as soon as something new arrives — same response shape, no busy re-polling. Omit for an immediate snapshot.'),
602
606
  }, readOnly('Check your inbox'), async ({ ack, handledResourceIds, waitSeconds }) => {
603
607
  try {
604
- const client = new InboxClient(creds.operatorKey, creds.agentId);
608
+ // Unset on stdio: that process IS the host, and a scheduler that
609
+ // respawns it per run pins ZIGGS_INSTANCE_ID instead.
610
+ const client = new InboxClient(creds.operatorKey, creds.agentId, undefined, creds.instanceId);
605
611
  // Ack-before-fetch is load-bearing; everything else is independent of
606
612
  // the envelope until we know whether there are deliveries to tag.
607
613
  const acked = ack
@@ -633,7 +639,7 @@ export function registerZiggsTools(server, creds, cfg) {
633
639
  // own held context grants. Best-effort — the inbox still works
634
640
  // untagged. All pages of live grants only (GET /grants also returns
635
641
  // expired/revoked and paginates).
636
- reach = await new GrantsClient(creds.operatorKey, creds.agentId).listAllGrants({
642
+ reach = await new GrantsClient(creds.operatorKey, creds.agentId, undefined, creds.laneId).listAllGrants({
637
643
  // Every context scope kind — a literal subset here is how the CLI
638
644
  // silently dropped a whole kind when `artifact` was added.
639
645
  scopeKind: [...CONTEXT_GRANT_SCOPE_KINDS],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/ziggs-mcp",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "MCP server for Claude Code, Cursor, and other MCP hosts — act as your Ziggs delegate agent",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@modelcontextprotocol/sdk": "^1.29.0",
42
- "@ziggs-ai/api-client": "0.16.0",
42
+ "@ziggs-ai/api-client": "0.17.1",
43
43
  "dotenv": "^16.6.1",
44
44
  "zod": "^3.24.2",
45
45
  "zod-to-json-schema": "^3.25.1"