@ziggs-ai/ziggs-mcp 0.36.5 → 0.36.7

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/README.md CHANGED
@@ -189,7 +189,6 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
189
189
  | `ziggs_agreement_bid` | `POST /agreements/proposals` — you work, named counterparty pays |
190
190
  | `ziggs_agreement_broker` | `POST /agreements/proposals` — a third party provides |
191
191
  | `ziggs_agreement_request` | `POST /agreements/proposals` — broadcast; whoever claims does the work |
192
- | `ziggs_agreement_offer` | marketplace publish — standing listing; you work, the claimer pays |
193
192
  | `ziggs_agreement_handoff` | `POST /agreements/proposals` — pass a hire you hold |
194
193
  | `ziggs_agreement_respond` | `PUT /agreements/:id/approvals/:partyId` (owner principal; approves direct hire, service, and `link` proposals) |
195
194
  | `ziggs_agreement_claim` | `POST /agreements/:id/claim` — claim any open broadcast (request / offer / hand-off / link invite). Listings are take-it-or-leave-it — never counter one |
@@ -4,7 +4,7 @@ export const DIRECTORY_TOOLS = new Set(`
4
4
  ziggs_auth_status ziggs_org_list ziggs_workspace_sync ziggs_context_snapshot
5
5
  ziggs_agreement_list ziggs_agreement_get ziggs_chat_list ziggs_chat_open ziggs_chat_send
6
6
  ziggs_agreement_buy ziggs_agreement_bid ziggs_agreement_broker ziggs_agreement_request
7
- ziggs_agreement_offer ziggs_agreement_handoff ziggs_listing_pause ziggs_listing_resume
7
+ ziggs_agreement_handoff
8
8
  ziggs_agreement_claim ziggs_agreement_subcontract ziggs_marketplace_view
9
9
  ziggs_agreement_respond ziggs_agreement_revoke ziggs_agreement_counter ziggs_agreement_fulfill
10
10
  ziggs_inbox_peek ziggs_inbox ziggs_inbox_claim ziggs_inbox_ack
@@ -30,7 +30,7 @@ export const CONNECTION_ONLY_DIRECTORY_TOOLS = new Set(['ziggs_workspace_sync'])
30
30
  const OPEN_WORLD = new Set(`
31
31
  ziggs_agent_search ziggs_agent_get ziggs_marketplace_view
32
32
  ziggs_agreement_buy ziggs_agreement_bid ziggs_agreement_broker ziggs_agreement_request
33
- ziggs_agreement_offer ziggs_agreement_handoff ziggs_agreement_claim ziggs_agreement_subcontract
33
+ ziggs_agreement_handoff ziggs_agreement_claim ziggs_agreement_subcontract
34
34
  ziggs_chat_open ziggs_chat_send ziggs_artifact_share ziggs_artifact_attach
35
35
  ziggs_context_issue_grant ziggs_context_delegate ziggs_context_request ziggs_access_request
36
36
  ziggs_link_propose ziggs_introduction_mint ziggs_introduction_redeem
@@ -57,7 +57,6 @@ export const SEARCH_ALIASES = {
57
57
  'post a job',
58
58
  'hire anyone',
59
59
  ],
60
- ziggs_agreement_offer: ['advertise', 'publish', 'list', 'listing', 'sell'],
61
60
  ziggs_agreement_broker: ['introduce', 'arrange', 'refer'],
62
61
  ziggs_agreement_handoff: ['transfer', 'reassign', 'pass on', 'give away'],
63
62
  ziggs_agreement_subcontract: ['subcontract', 'delegate a slice', 'under a parent'],
@@ -22,6 +22,15 @@ export interface ToolErrorShape {
22
22
  * the same shape an open's action recovery has.
23
23
  */
24
24
  recovery?: Record<string, unknown>;
25
+ /** hidden | unreadable, the same outcome open and access_explain give. */
26
+ outcome?: string;
27
+ /** The resource the outcome is about. */
28
+ ref?: {
29
+ kind: string;
30
+ id: string;
31
+ };
32
+ /** For an unreadable resource: the request to make, as open lists it. */
33
+ actions?: unknown[];
25
34
  }
26
35
  /**
27
36
  * Classify a thrown client error (or a plain string this layer raised) into a
@@ -35,6 +44,26 @@ export declare function classifyToolError(input: unknown): ToolErrorShape;
35
44
  * HTTP status and server `code` that decide the hint.
36
45
  * A plain string is still accepted for refusals this layer raises on its own.
37
46
  */
47
+ /** A refused read, answered as open would: its outcome, and any request to make. */
48
+ export declare function refusalError(refusal: {
49
+ code: string;
50
+ message: string;
51
+ outcome?: string;
52
+ ref?: {
53
+ kind: string;
54
+ id: string;
55
+ };
56
+ actions?: unknown[];
57
+ }): {
58
+ structuredContent: {
59
+ error: ToolErrorShape;
60
+ };
61
+ content: {
62
+ type: "text";
63
+ text: string;
64
+ }[];
65
+ isError: boolean;
66
+ };
38
67
  export declare function toolError(input: unknown): {
39
68
  structuredContent: {
40
69
  error: ToolErrorShape;
package/dist/toolError.js CHANGED
@@ -5,6 +5,12 @@
5
5
  * `code`, `body` / `message`). No client-prefix strip, no HTTP-status regex,
6
6
  * no re-parsing a JSON body that was concatenated into the message string.
7
7
  */
8
+ /**
9
+ * The shared refusal vocabulary every read tool answers in (hidden,
10
+ * unreadable, invalid input), so an assistant can branch on the code instead
11
+ * of parsing each tool's sentence.
12
+ */
13
+ const SHARED_REFUSALS = new Set(['RESOURCE_HIDDEN', 'RESOURCE_UNREADABLE', 'INVALID_INPUT']);
8
14
  const SCOPE_DENIED_HINT = 'For missing access to a returned artifact, chat, agreement, or task, use ' +
9
15
  'ziggs_access_request with that original resource id and your purpose. It creates ' +
10
16
  'a pending access approval agreement; it grants no access. After approval, ' +
@@ -127,6 +133,9 @@ export function classifyToolError(input) {
127
133
  }
128
134
  function classify(input) {
129
135
  const { message, status, code: machineCode } = thrownFields(input);
136
+ if (machineCode && SHARED_REFUSALS.has(machineCode)) {
137
+ return { code: machineCode, message };
138
+ }
130
139
  if (machineCode === 'AGENT_LACKS_HUMAN_AUTHORITY') {
131
140
  return {
132
141
  code: 'AGENT_LACKS_HUMAN_AUTHORITY',
@@ -181,6 +190,17 @@ function classify(input) {
181
190
  * HTTP status and server `code` that decide the hint.
182
191
  * A plain string is still accepted for refusals this layer raises on its own.
183
192
  */
193
+ /** A refused read, answered as open would: its outcome, and any request to make. */
194
+ export function refusalError(refusal) {
195
+ const shape = { ...refusal };
196
+ return {
197
+ structuredContent: { error: shape },
198
+ content: [
199
+ { type: 'text', text: JSON.stringify({ error: shape }, null, 2) },
200
+ ],
201
+ isError: true,
202
+ };
203
+ }
184
204
  export function toolError(input) {
185
205
  const shape = classifyToolError(input);
186
206
  return {
package/dist/tools.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isDirectorySurface } from './directorySurface.js';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { z } from 'zod';
4
- import { presentResourceActions, getAgreement, getMyAgreements, listMyChats, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, acknowledgeTaskControl, replaceTaskPlan, updateTaskPlanSteps, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchSessionAccess, isMcpOAuthDelegateSession, GRANTS_CAPABILITIES, AGREEMENT_VERB_CAPABILITIES, LISTING_DOOR_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, openCapability, accessExplainCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, agreementSubcontractCapability, listTasksCapability, cancelTaskCapability, marketplaceViewCapability, parseListFields, pickListedRows, projectChatListRows, sessionOrientation, presentSendResult, presentTaskOutcome, } from '@ziggs-ai/api-client';
4
+ import { presentResourceActions, getAgreement, getMyAgreements, listMyChats, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, acknowledgeTaskControl, replaceTaskPlan, updateTaskPlanSteps, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchSessionAccess, isMcpOAuthDelegateSession, GRANTS_CAPABILITIES, AGREEMENT_VERB_CAPABILITIES, LISTING_DOOR_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, openCapability, accessExplainCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, findArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, agreementSubcontractCapability, listTasksCapability, cancelTaskCapability, marketplaceViewCapability, parseListFields, pickListedRows, projectChatListRows, sessionOrientation, presentSendResult, presentTaskOutcome, explainRefusedRead, RESOURCE_HIDDEN_MESSAGE, } from '@ziggs-ai/api-client';
5
5
  import { decodeOperatorKeyClaims } from './operatorKey.js';
6
6
  import { registerTrustTools } from './trustTools.js';
7
7
  import { registerPaymentTools } from './paymentTools.js';
@@ -12,7 +12,7 @@ import { agreementAppUrl, filterTasksForDelegate, formatPendingDecisionsPayload,
12
12
  import { resolveMcpConnectionTarget, withMcpGatewayClient, } from './mcpConnectionTools.js';
13
13
  import { readOnly, write, destructive } from './toolAnnotations.js';
14
14
  import { registerStrictTool } from './strictParams.js';
15
- import { toolError } from './toolError.js';
15
+ import { refusalError, toolError } from './toolError.js';
16
16
  import { registerCapability, registerCapabilities, textResult, } from './capabilityAdapter.js';
17
17
  // the strict artifact write (fail loudly, return the artifactId)
18
18
  // moved into ArtifactsClient.writeStrict, shared with the SDK's artifact_record.
@@ -245,7 +245,44 @@ function inboxFor(creds) {
245
245
  sessionName: creds.sessionName,
246
246
  });
247
247
  }
248
+ /** A get the server refused for reach (403) or existence (404). */
249
+ function isReadRefusal(e) {
250
+ const status = e?.status;
251
+ return status === 403 || status === 404;
252
+ }
248
253
  export function registerZiggsTools(server, creds, cfg) {
254
+ // A refused get answers as open would: the server's one judgement of
255
+ // hidden or unreadable, and for unreadable the request to make. When that
256
+ // judgement cannot be had, the original refusal stands (or, for a get that
257
+ // returned nothing at all, hidden).
258
+ const refusedRead = async (kind, id, cause) => {
259
+ const refusal = await explainRefusedRead(kind, id, {
260
+ creds,
261
+ webUrl: cfg.ZIGGS_WEB_URL,
262
+ surface: 'mcp',
263
+ });
264
+ if (refusal)
265
+ return refusalError(refusal);
266
+ // No workspace of this connection holds it: the routing guard refused
267
+ // before the read, and the judgement behind it would be refused the same
268
+ // way. Not reachable from here reads as hidden, the same as missing.
269
+ if (cause?.code === 'RESOURCE_NOT_ACCESSIBLE') {
270
+ return refusalError({
271
+ code: 'RESOURCE_HIDDEN',
272
+ message: RESOURCE_HIDDEN_MESSAGE,
273
+ outcome: 'hidden',
274
+ ref: { kind, id },
275
+ });
276
+ }
277
+ return cause
278
+ ? toolError(cause)
279
+ : refusalError({
280
+ code: 'RESOURCE_HIDDEN',
281
+ message: RESOURCE_HIDDEN_MESSAGE,
282
+ outcome: 'hidden',
283
+ ref: { kind, id },
284
+ });
285
+ };
249
286
  // A binding check and nothing else. This used to also report the
250
287
  // pending/active counts and point at a separate decisions tool for the card,
251
288
  // which is how a session start came to cost three calls that each said some
@@ -359,10 +396,12 @@ export function registerZiggsTools(server, creds, cfg) {
359
396
  try {
360
397
  const agreement = await getAgreement(agreementId, creds);
361
398
  if (!agreement)
362
- return toolError(`Agreement not found: ${agreementId}`);
399
+ return refusedRead('agreement', agreementId);
363
400
  return textResult({ agreement });
364
401
  }
365
402
  catch (e) {
403
+ if (isReadRefusal(e))
404
+ return refusedRead('agreement', agreementId, e);
366
405
  return toolError(e);
367
406
  }
368
407
  });
@@ -458,8 +497,7 @@ export function registerZiggsTools(server, creds, cfg) {
458
497
  registerCapabilities(server, AGREEMENT_VERB_CAPABILITIES, creds, {
459
498
  webUrl: cfg.ZIGGS_WEB_URL,
460
499
  });
461
- // The publisher's own door on a listing it already holds: pause and resume.
462
- // Apart from the verbs above because they form nothing.
500
+ // Pause and resume are not on this surface. The array is empty.
463
501
  registerCapabilities(server, LISTING_DOOR_CAPABILITIES, creds);
464
502
  registerCapability(server, agreementClaimCapability, creds, {
465
503
  webUrl: cfg.ZIGGS_WEB_URL,
@@ -1027,10 +1065,12 @@ export function registerZiggsTools(server, creds, cfg) {
1027
1065
  try {
1028
1066
  const task = await getTask(taskId, creds);
1029
1067
  if (!task)
1030
- return toolError(`Task not found: ${taskId}`);
1068
+ return refusedRead('task', taskId);
1031
1069
  return textResult({ task });
1032
1070
  }
1033
1071
  catch (e) {
1072
+ if (isReadRefusal(e))
1073
+ return refusedRead('task', taskId, e);
1034
1074
  return toolError(e);
1035
1075
  }
1036
1076
  });
@@ -162,12 +162,22 @@ export class WorkspaceRouter {
162
162
  return this.run(workspaces[0], args, extra, invoke, false);
163
163
  }
164
164
  if (readOnly && workspaces.length > 1) {
165
- const results = await Promise.all(workspaces.map(async (w) => ({
166
- orgId: w.orgId,
167
- name: w.name,
168
- result: parsed(await this.run(w, args, extra, invoke, false)),
169
- })));
170
- return result({ workspaces: results });
165
+ const runs = await Promise.all(workspaces.map(async (w) => ({ workspace: w, raw: await this.run(w, args, extra, invoke, false) })));
166
+ // The same refusal from every workspace is one refusal: a bad input is
167
+ // bad everywhere, and splitting it per workspace made an error read as
168
+ // a success-shaped answer.
169
+ const first = runs[0].raw;
170
+ if (first.isError &&
171
+ runs.every((r) => r.raw.isError && r.raw.content[0]?.text === first.content[0]?.text)) {
172
+ return first;
173
+ }
174
+ return result({
175
+ workspaces: runs.map((r) => ({
176
+ orgId: r.workspace.orgId,
177
+ name: r.workspace.name,
178
+ result: parsed(r.raw),
179
+ })),
180
+ });
171
181
  }
172
182
  if (workspaces.length === 1)
173
183
  return this.run(workspaces[0], args, extra, invoke);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/ziggs-mcp",
3
- "version": "0.36.5",
3
+ "version": "0.36.7",
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": {
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@modelcontextprotocol/sdk": "^1.29.0",
43
- "@ziggs-ai/api-client": "0.36.3",
43
+ "@ziggs-ai/api-client": "0.36.4",
44
44
  "dotenv": "^16.6.1",
45
45
  "zod": "^3.24.2",
46
46
  "zod-to-json-schema": "^3.25.1"