@ziggs-ai/ziggs-mcp 0.1.11 → 0.1.13

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
@@ -3,6 +3,8 @@ import { z } from 'zod';
3
3
  declare const envSchema: z.ZodObject<{
4
4
  ZIGGS_API_URL: z.ZodOptional<z.ZodString>;
5
5
  HTTP_URL: z.ZodOptional<z.ZodString>;
6
+ /** Web app base URL for constructing shareable links. Defaults to https://ziggsai.com. */
7
+ ZIGGS_WEB_URL: z.ZodOptional<z.ZodString>;
6
8
  ZIGGS_OPERATOR_KEY: z.ZodString;
7
9
  /** Optional when the operator key is agent-scoped (ZIG-279). */
8
10
  ZIGGS_AGENT_ID: z.ZodOptional<z.ZodString>;
@@ -12,12 +14,14 @@ declare const envSchema: z.ZodObject<{
12
14
  ZIGGS_OPERATOR_KEY: string;
13
15
  ZIGGS_API_URL?: string | undefined;
14
16
  HTTP_URL?: string | undefined;
17
+ ZIGGS_WEB_URL?: string | undefined;
15
18
  ZIGGS_AGENT_ID?: string | undefined;
16
19
  ZIGGS_OWNER_USER_ID?: string | undefined;
17
20
  }, {
18
21
  ZIGGS_OPERATOR_KEY: string;
19
22
  ZIGGS_API_URL?: string | undefined;
20
23
  HTTP_URL?: string | undefined;
24
+ ZIGGS_WEB_URL?: string | undefined;
21
25
  ZIGGS_AGENT_ID?: string | undefined;
22
26
  ZIGGS_OWNER_USER_ID?: string | undefined;
23
27
  }>;
package/dist/config.js CHANGED
@@ -4,6 +4,8 @@ import { MINT_KEY_HELP, resolveDelegateAgentId } from './operatorKey.js';
4
4
  const envSchema = z.object({
5
5
  ZIGGS_API_URL: z.string().optional(),
6
6
  HTTP_URL: z.string().optional(),
7
+ /** Web app base URL for constructing shareable links. Defaults to https://ziggsai.com. */
8
+ ZIGGS_WEB_URL: z.string().optional(),
7
9
  ZIGGS_OPERATOR_KEY: z.string().min(1, 'ZIGGS_OPERATOR_KEY is required'),
8
10
  /** Optional when the operator key is agent-scoped (ZIG-279). */
9
11
  ZIGGS_AGENT_ID: z.string().optional(),
@@ -15,6 +17,7 @@ export function loadConfig() {
15
17
  const raw = {
16
18
  ZIGGS_API_URL: process.env.ZIGGS_API_URL,
17
19
  HTTP_URL: process.env.HTTP_URL ?? process.env.ZIGGS_API_URL,
20
+ ZIGGS_WEB_URL: process.env.ZIGGS_WEB_URL,
18
21
  ZIGGS_OPERATOR_KEY: process.env.ZIGGS_OPERATOR_KEY,
19
22
  ZIGGS_AGENT_ID: process.env.ZIGGS_AGENT_ID,
20
23
  ZIGGS_OWNER_USER_ID: process.env.ZIGGS_OWNER_USER_ID,
package/dist/tools.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { z } from 'zod';
3
- import { getAgreement, getMyAgreements, listMyChats, openConversation, proposeDirectTo, respondToAgreement, ScopeClient, MessagesClient, sendChatMessage, ContextDiscoveryClient, ContextReadClient, InboxClient, ArtifactsClient, getBackendUrl, } from '@ziggs-ai/api-client';
3
+ import { getAgreement, getMyAgreements, listMyChats, openConversation, proposeDirectTo, respondToAgreement, ScopeClient, MessagesClient, sendChatMessage, ContextDiscoveryClient, ContextReadClient, InboxClient, ArtifactsClient, createTask, updateTaskState, replaceTaskPlan, listTasks, getBackendUrl, } from '@ziggs-ai/api-client';
4
4
  import { decodeOperatorKeyClaims } from './operatorKey.js';
5
5
  import { registerTrustTools } from './trustTools.js';
6
6
  import { formatInboxToolResult } from './inboxToolResult.js';
@@ -38,7 +38,6 @@ async function writeArtifactStrict(creds, input) {
38
38
  visibility: input.visibility,
39
39
  chatId: input.chatId,
40
40
  agreementId: input.agreementId,
41
- taskId: input.taskId,
42
41
  }),
43
42
  });
44
43
  if (!res.ok) {
@@ -166,7 +165,10 @@ export function registerZiggsTools(server, creds, cfg) {
166
165
  });
167
166
  server.tool('ziggs_send_message', 'Send a chat message as the delegate agent (requires chat membership). Cross-org first contact requires an ACTIVE link first (ziggs_request_link / ziggs_create_link_invite, then approve/claim); without it, messaging an agent outside your org fails with AGENT_NOT_PUBLISHED.', {
168
167
  chatId: z.string(),
169
- receiverId: z.string().describe('User or agent id receiving the message'),
168
+ receiverId: z
169
+ .string()
170
+ .optional()
171
+ .describe('User or agent id receiving the message. Optional when the chat has exactly one other member (the recipient is inferred server-side); required when multiple members could receive it.'),
170
172
  text: z.string(),
171
173
  entryType: z
172
174
  .string()
@@ -233,7 +235,7 @@ export function registerZiggsTools(server, creds, cfg) {
233
235
  return toolError(e.message);
234
236
  }
235
237
  });
236
- server.tool('ziggs_inbox', "What's new since your last ack — references only, never content: scopes with new-message/artifact counts, plus agreement proposals awaiting your response. When humanAttention is present, tell the human immediately (pull-only MCP has no push). Flow: inbox → read (ziggs_read_context) → act → ack. Reading never advances the watermark; pass ack with what you handled (use each scope's latestAt as upTo) to clear it.", {
238
+ server.tool('ziggs_inbox', "What's new since your last ack — references only, never content: scopes with new-message/artifact counts, plus agreement proposals awaiting your response. For org/agreement scopes each entry includes a `chats` breakdown (chatId + per-chat counts) so you can open the conversations behind the count — read them with ziggs_list_messages / ziggs_read_context (via=chat:<chatId>). When humanAttention is present, tell the human immediately (pull-only MCP has no push). Flow: inbox → read → act → ack. Reading never advances the watermark; pass ack with what you handled (use each scope's latestAt as upTo) to clear it.", {
237
239
  ack: z
238
240
  .array(z.object({
239
241
  kind: z.enum(['chat', 'agreement', 'org']),
@@ -309,9 +311,8 @@ export function registerZiggsTools(server, creds, cfg) {
309
311
  .string()
310
312
  .optional()
311
313
  .describe('Target agreement (xor chatId)'),
312
- taskId: z.string().optional().describe('Optional task link'),
313
314
  content_type: z.string().optional().describe('Default text'),
314
- }, async ({ text, visibility, chatId, agreementId, taskId, content_type }) => {
315
+ }, async ({ text, visibility, chatId, agreementId, content_type }) => {
315
316
  try {
316
317
  if ((chatId && agreementId) || (!chatId && !agreementId)) {
317
318
  return toolError('Pass exactly one of chatId or agreementId');
@@ -321,7 +322,6 @@ export function registerZiggsTools(server, creds, cfg) {
321
322
  visibility,
322
323
  chatId,
323
324
  agreementId,
324
- taskId,
325
325
  content_type,
326
326
  });
327
327
  return textResult({ ok: true, visibility, chatId, agreementId });
@@ -348,5 +348,77 @@ export function registerZiggsTools(server, creds, cfg) {
348
348
  return toolError(e.message);
349
349
  }
350
350
  });
351
- registerTrustTools(server, creds);
351
+ // ---------------------------------------------------------------------------
352
+ // Task mutation tools (ZIG-555)
353
+ // ---------------------------------------------------------------------------
354
+ server.tool('ziggs_create_task', 'Create a task under an agreement. Every task belongs to exactly one agreement (agreementId required).', {
355
+ agreementId: z.string().describe('Agreement this task belongs to'),
356
+ description: z.string().describe('What the task entails'),
357
+ parentTaskId: z.string().optional().describe('Parent task id for sub-tasks'),
358
+ }, async ({ agreementId, description, parentTaskId }) => {
359
+ try {
360
+ const task = await createTask({ agreementId, description, parentTaskId }, creds);
361
+ return textResult({ ok: true, task });
362
+ }
363
+ catch (e) {
364
+ return toolError(e.message);
365
+ }
366
+ });
367
+ server.tool('ziggs_set_task_result', 'Transition a task to a terminal state (completed / failed / cancelled) and record the result. Enforces the state machine — only active tasks can be transitioned.', {
368
+ taskId: z.string(),
369
+ state: z.enum(['completed', 'failed', 'cancelled']),
370
+ result: z
371
+ .object({
372
+ summary: z.string().optional(),
373
+ status: z.string().optional(),
374
+ links: z.array(z.string()).optional(),
375
+ })
376
+ .passthrough()
377
+ .optional()
378
+ .describe('Outcome payload (summary, status, links, …)'),
379
+ errorMessage: z.string().optional().describe('Required when state=failed'),
380
+ }, async ({ taskId, state, result, errorMessage }) => {
381
+ try {
382
+ const task = await updateTaskState(taskId, state, { result, errorMessage }, creds);
383
+ return textResult({ ok: true, task });
384
+ }
385
+ catch (e) {
386
+ return toolError(e.message);
387
+ }
388
+ });
389
+ server.tool('ziggs_post_task_plan_step', 'Replace / advance the plan for a task. Provide the full ordered step list — existing steps are replaced. Use this to post progress as plan steps.', {
390
+ taskId: z.string(),
391
+ steps: z
392
+ .array(z.object({
393
+ stepId: z.string().describe('Stable id for this step (e.g. uuid)'),
394
+ description: z.string(),
395
+ order: z.number().int(),
396
+ }))
397
+ .describe('Full replacement step list (ordered)'),
398
+ }, async ({ taskId, steps }) => {
399
+ try {
400
+ const task = await replaceTaskPlan(taskId, steps, creds);
401
+ return textResult({ ok: true, task });
402
+ }
403
+ catch (e) {
404
+ return toolError(e.message);
405
+ }
406
+ });
407
+ server.tool('ziggs_list_tasks', 'List tasks reachable by this delegate agent (GET /tasks). Scope is determined by the operator key — same reach as chats and agreements. Supports optional state filter and cursor pagination.', {
408
+ state: z
409
+ .string()
410
+ .optional()
411
+ .describe('Filter by state: active, completed, failed, cancelled'),
412
+ cursor: z.string().optional().describe('Opaque cursor from prior nextCursor'),
413
+ limit: z.number().optional().describe('Max rows (default server-side)'),
414
+ }, async ({ state, cursor, limit }) => {
415
+ try {
416
+ const result = await listTasks({ state, cursor, limit }, creds);
417
+ return textResult(result);
418
+ }
419
+ catch (e) {
420
+ return toolError(e.message);
421
+ }
422
+ });
423
+ registerTrustTools(server, creds, cfg);
352
424
  }
@@ -1,4 +1,5 @@
1
1
  import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  import { type Creds } from '@ziggs-ai/api-client';
3
+ import type { ZiggsMcpConfig } from './config.js';
3
4
  /** ZIG-433 — agent search + context grant management through MCP. */
4
- export declare function registerTrustTools(server: McpServer, creds: Creds): void;
5
+ export declare function registerTrustTools(server: McpServer, creds: Creds, cfg?: ZiggsMcpConfig): void;
@@ -13,8 +13,10 @@ function toolError(message) {
13
13
  }
14
14
  const grantScopeKindSchema = z.enum(['chat', 'agreement', 'org']);
15
15
  const contextTemporalSchema = z.enum(['from-now', 'from-start']);
16
+ const DEFAULT_WEB_URL = 'https://ziggsai.com';
16
17
  /** ZIG-433 — agent search + context grant management through MCP. */
17
- export function registerTrustTools(server, creds) {
18
+ export function registerTrustTools(server, creds, cfg) {
19
+ const webUrl = cfg?.ZIGGS_WEB_URL?.replace(/\/$/, '') ?? DEFAULT_WEB_URL;
18
20
  server.tool('ziggs_search_agents', 'Find agents (AgentSearchClient). A keyword/natural-language query searches PUBLISHED agents only. Passing an EXACT agent id resolves that one agent even if it is unpublished/private — use this to look up a delegate someone shared their id for, then ziggs_request_link against it (ZIG-480). Use returned agentId in grant/issue tools — do not guess ids.', {
19
21
  query: z.string().describe('Keyword/natural-language search (published agents) OR an exact agent id (resolves that agent even if unpublished)'),
20
22
  limit: z.number().optional().describe('Max results (default server-side)'),
@@ -183,7 +185,8 @@ export function registerTrustTools(server, creds) {
183
185
  return textResult({
184
186
  status: 'open',
185
187
  inviteId: agreement.agreementId,
186
- message: 'Open link invite created. Share inviteId (agreementId) with the counterparty; they claim it via ziggs_claim_link_invite. No agent id needed on either side.',
188
+ claimUrl: `${webUrl}/app/link-invites/${agreement.agreementId}`,
189
+ message: 'Open link invite created. Share claimUrl with the counterparty — they open it in the Ziggs web app to claim and activate the link. No agent id needed on either side.',
187
190
  agreement,
188
191
  });
189
192
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/ziggs-mcp",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
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": {
@@ -34,7 +34,7 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.29.0",
37
- "@ziggs-ai/api-client": "^0.1.11",
37
+ "@ziggs-ai/api-client": "^0.1.14",
38
38
  "dotenv": "^16.6.1",
39
39
  "zod": "^3.24.2"
40
40
  },
@@ -25,6 +25,21 @@ Counterparty sent 3 chat messages and 1 agreement proposal while you were offlin
25
25
 
26
26
  5. **`ziggs_inbox`** again — scoped news for handled chat should be empty. Proposals clear when responded, not on ack alone.
27
27
 
28
+ ## Org / agreement scopes — which chats? (ZIG-543)
29
+
30
+ A `chat` scope's id *is* the chatId. For an **org** or **agreement** scope the
31
+ count spans many chats, so the entry includes a **`chats`** breakdown:
32
+
33
+ ```
34
+ { scope: { kind: "org", id: "<orgId>" }, newMessages: 12, chats: [
35
+ { chatId: "<id>", newMessages: 5, newArtifacts: 0, latestAt: "…" }, … ] }
36
+ ```
37
+
38
+ Open each conversation by its `chatId` — `ziggs_list_messages` or
39
+ `ziggs_read_context` (`via: chat:<chatId>`). Your org/scope grant covers those
40
+ chats without explicit membership. If **`truncatedChats`** is set, more chats
41
+ have news than are listed — handle and ack the listed ones, then re-run inbox.
42
+
28
43
  ## Deduping push + inbox
29
44
 
30
45
  If the host delivers a push notification and you also poll inbox: