@ziggs-ai/ziggs-mcp 0.7.2 → 0.7.4

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.
@@ -29,7 +29,7 @@ export interface ReadPlanResult {
29
29
  * the caller already received, restated as runnable calls.
30
30
  *
31
31
  * Mapping honours how reads resolve server-side: messages read only via chat,
32
- * artifacts via chat or agreement.
32
+ * artifacts via the chat, agreement, or task they landed on.
33
33
  */
34
34
  export declare function buildReadPlan(inbox: InboxEnvelope, grantsByScope?: Map<string, ScopeGrantTag>): ReadPlanResult;
35
35
  /**
@@ -23,7 +23,7 @@ function readContextCall(type, kind, id, grantId) {
23
23
  * the caller already received, restated as runnable calls.
24
24
  *
25
25
  * Mapping honours how reads resolve server-side: messages read only via chat,
26
- * artifacts via chat or agreement.
26
+ * artifacts via the chat, agreement, or task they landed on.
27
27
  */
28
28
  export function buildReadPlan(inbox, grantsByScope) {
29
29
  const proposals = inbox.proposalsAwaitingMe ?? [];
@@ -63,7 +63,13 @@ export function buildReadPlan(inbox, grantsByScope) {
63
63
  const grantFor = (kind, id) => grantsByScope?.get(`${kind}:${id}`)?.grantId;
64
64
  const read = (type, viaKind, viaId) => add(`read:${type}:${viaKind}:${viaId}`, readContextCall(type, viaKind, viaId, grantFor(viaKind, viaId)));
65
65
  // Reads — one call per place mail actually landed. The delivery names the
66
- // chat or agreement directly, so nothing has to be inferred from a scope.
66
+ // chat, agreement or task directly, so nothing has to be inferred from a scope.
67
+ //
68
+ // The task arm is not optional: a deliverable recorded against a task alone is
69
+ // the shape the protocol asks for, and it lands in no chat and no agreement.
70
+ // While this only looked at chatId/agreementId, such a delivery produced an
71
+ // envelope whose plan was the ack and nothing else — an agent following the
72
+ // plan acked the deliverable without ever reading it.
67
73
  for (const d of deliveries) {
68
74
  if (d.kind === 'message' && d.chatId)
69
75
  read('messages', 'chat', d.chatId);
@@ -72,6 +78,8 @@ export function buildReadPlan(inbox, grantsByScope) {
72
78
  read('artifacts', 'chat', d.chatId);
73
79
  else if (d.agreementId)
74
80
  read('artifacts', 'agreement', d.agreementId);
81
+ else if (d.taskId)
82
+ read('artifacts', 'task', d.taskId);
75
83
  }
76
84
  }
77
85
  // ZIG-660: reserve a slot for the ack before capping, so the pre-filled ack
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, proposeUnified, delegateAgreement, provisionRelayWorkers, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, PaymentsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, replaceTaskPlan, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchDelegateAccess, GRANTS_CAPABILITIES, contextReadCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, marketplaceViewCapability, linkSummary, } from '@ziggs-ai/api-client';
3
+ import { getAgreement, getMyAgreements, listMyChats, proposeUnified, delegateAgreement, provisionRelayWorkers, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, PaymentsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, replaceTaskPlan, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchDelegateAccess, GRANTS_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, marketplaceViewCapability, linkSummary, } from '@ziggs-ai/api-client';
4
4
  import { decodeOperatorKeyClaims } from './operatorKey.js';
5
5
  import { registerTrustTools } from './trustTools.js';
6
6
  import { registerPaymentTools } from './paymentTools.js';
@@ -43,7 +43,13 @@ const ZIGGS_SEND_MESSAGE_DESCRIPTION = 'Send a chat message as the delegate agen
43
43
  // result slot on the artifact_record description and success path so an agent
44
44
  // finds the right move unaided. Reporting rule sourced from the shared const
45
45
  // (ZIG-557).
46
- const ZIGGS_RECORD_ARTIFACT_DESCRIPTION = 'Write an artifact to a chat or agreement scope. Set visibility explicitly. ' +
46
+ // ZIG-1037: scope is optional. Say so first the old wording ("write to a chat
47
+ // or agreement scope") is what pushed a model into guessing a container, and
48
+ // guessing wrong used to fail the write outright.
49
+ const ZIGGS_RECORD_ARTIFACT_DESCRIPTION = 'Write an artifact. Scope is optional — pass agreementId or chatId to record it into that ' +
50
+ 'scope, pass taskId alone to bind a deliverable to its task, or pass no scope at all for a ' +
51
+ 'free-standing artifact that is yours until you attach or share it. Never guess a scope: ' +
52
+ 'recording with none always succeeds. Set visibility explicitly. ' +
47
53
  'For a finished deliverable, set content_type=result and pass taskId to bind it to the task. ' +
48
54
  PROTOCOL.reporting;
49
55
  // ZIG-899 — the strict artifact write (fail loudly, return the artifactId)
@@ -633,7 +639,9 @@ export function registerZiggsTools(server, creds, cfg) {
633
639
  let reach = [];
634
640
  try {
635
641
  reach = await new GrantsClient(creds.operatorKey, creds.agentId).listAllGrants({
636
- scopeKind: ['chat', 'agreement', 'org'],
642
+ // Every context scope kind — a literal subset here is how the CLI
643
+ // silently dropped a whole kind when `artifact` was added.
644
+ scopeKind: [...CONTEXT_GRANT_SCOPE_KINDS],
637
645
  health: 'active',
638
646
  });
639
647
  }
@@ -664,6 +672,12 @@ export function registerZiggsTools(server, creds, cfg) {
664
672
  registerCapability(server, recordArtifactCapability, creds, {
665
673
  description: ZIGGS_RECORD_ARTIFACT_DESCRIPTION,
666
674
  });
675
+ // ZIG-1037 — find what you recorded free-standing, and hand one artifact to one
676
+ // agent. Descriptions come from the shared capability (no PROTOCOL override
677
+ // needed; neither is a reporting surface).
678
+ registerCapability(server, listArtifactsCapability, creds);
679
+ registerCapability(server, shareArtifactCapability, creds);
680
+ registerCapability(server, attachArtifactCapability, creds);
667
681
  // ---------------------------------------------------------------------------
668
682
  // Task mutation tools (ZIG-555)
669
683
  // ---------------------------------------------------------------------------
@@ -3,7 +3,9 @@ import { ContextGrantsClient, addChatMember, contextBounds, resolveOrgScopeId, L
3
3
  import { WRITE, DESTRUCTIVE } from './toolAnnotations.js';
4
4
  import { toolError } from './toolError.js';
5
5
  import { registerCapabilities, registerCapability, textResult } from './capabilityAdapter.js';
6
- const grantScopeKindSchema = z.enum(['chat', 'agreement', 'org']);
6
+ // ZIG-1037: `artifact` is the narrowest context scope — one specific artifact,
7
+ // shared without sharing any container it sits in.
8
+ const grantScopeKindSchema = z.enum(['chat', 'agreement', 'org', 'artifact']);
7
9
  const contextTemporalSchema = z.enum(['from-now', 'from-start']);
8
10
  const DEFAULT_WEB_URL = 'https://ziggsai.com';
9
11
  /** ZIG-433 / ZIG-956 — agent search + context grant management through MCP.
@@ -13,20 +15,27 @@ const DEFAULT_WEB_URL = 'https://ziggsai.com';
13
15
  export function registerTrustTools(server, creds, cfg) {
14
16
  const webUrl = cfg?.ZIGGS_WEB_URL?.replace(/\/$/, '') ?? DEFAULT_WEB_URL;
15
17
  registerCapabilities(server, DISCOVERY_CAPABILITIES, creds);
16
- server.tool('ziggs_context_issue_grant', 'Issue bounded context access. Chat scope: admits agent via POST /chats/:id/members (agent-invite → pending_approval until humans consent) — this works for you as a delegate. Agreement/org scope: issuing a NEW root grant is a human-authority action; if you are acting for a principal you are denied (AGENT_LACKS_HUMAN_AUTHORITY) — instead use ziggs_context_delegate to hand a peer a narrower slice of a grant you already hold, or ask your human to issue it. Defaults: from-now, narrow scope.', {
18
+ server.tool('ziggs_context_issue_grant', 'Issue bounded context access. Chat scope: admits agent via POST /chats/:id/members (agent-invite → pending_approval until humans consent) — this works for you as a delegate. Agreement/org scope: issuing a NEW root grant is a human-authority action; if you are acting for a principal you are denied (AGENT_LACKS_HUMAN_AUTHORITY) — instead use ziggs_context_delegate to hand a peer a narrower slice of a grant you already hold, or ask your human to issue it. Artifact scope: hands over one specific artifact and nothing else; it is always from-start (the artifact already exists), and if YOU authored it use ziggs_artifact_share instead — that needs no human authority. Defaults: from-now, narrow scope.', {
17
19
  holderId: z.string().describe('Bare agent id receiving the grant'),
18
20
  scopeKind: grantScopeKindSchema,
19
- scopeId: z.string().describe('chatId, agreementId, or orgId'),
21
+ scopeId: z.string().describe('chatId, agreementId, orgId, or artifactId'),
20
22
  temporal: contextTemporalSchema
21
23
  .optional()
22
- .describe('from-now (default) or from-start (approval-gated on chat)'),
24
+ .describe('from-now (default) or from-start (approval-gated on chat). Artifact ' +
25
+ 'scope defaults to from-start and rejects from-now.'),
23
26
  expiresAt: z
24
27
  .string()
25
28
  .optional()
26
29
  .nullable()
27
30
  .describe('ISO-8601 expiry; omit for platform default TTL'),
28
31
  }, WRITE, async ({ holderId, scopeKind, scopeId, temporal, expiresAt }) => {
29
- const resolvedTemporal = temporal ?? 'from-now';
32
+ // An artifact predates any watermark you could set, so from-now would
33
+ // validate and then read empty — the server refuses it outright. Defaulting
34
+ // artifact scope to from-now here made the tool's own default invocation
35
+ // fail, while the description promised it was always from-start. An
36
+ // EXPLICIT from-now still goes through and is refused loudly: silently
37
+ // rewriting what the caller asked for would hide the mistake.
38
+ const resolvedTemporal = temporal ?? (scopeKind === 'artifact' ? 'from-start' : 'from-now');
30
39
  try {
31
40
  if (scopeKind === 'chat') {
32
41
  const result = await addChatMember({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/ziggs-mcp",
3
- "version": "0.7.2",
3
+ "version": "0.7.4",
4
4
  "description": "MCP server for Claude Code, Cursor, and other MCP hosts \u2014 act as your Ziggs delegate agent",
5
5
  "type": "module",
6
6
  "bin": {