@ziggs-ai/ziggs-mcp 0.3.2 → 0.5.0
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/capabilityAdapter.d.ts +25 -0
- package/dist/capabilityAdapter.js +75 -0
- package/dist/inboxToolResult.d.ts +8 -8
- package/dist/inboxToolResult.js +35 -78
- package/dist/paymentTools.d.ts +9 -8
- package/dist/paymentTools.js +12 -248
- package/dist/tools.js +54 -279
- package/dist/trustTools.d.ts +4 -1
- package/dist/trustTools.js +14 -272
- package/package.json +3 -3
- package/dist/orgs.d.ts +0 -28
- package/dist/orgs.js +0 -44
package/dist/tools.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { getAgreement, getMyAgreements, listMyChats,
|
|
3
|
+
import { getAgreement, getMyAgreements, listMyChats, proposeDirectTo, proposeBroadcast, publishOffer, claimOffer, 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, } from '@ziggs-ai/api-client';
|
|
4
4
|
import { decodeOperatorKeyClaims } from './operatorKey.js';
|
|
5
|
-
import { fetchMyOrgs } from './orgs.js';
|
|
6
5
|
import { registerTrustTools } from './trustTools.js';
|
|
7
6
|
import { registerPaymentTools } from './paymentTools.js';
|
|
8
7
|
import { formatInboxToolResult, buildReadContextReadPlan, } from './inboxToolResult.js';
|
|
@@ -19,6 +18,7 @@ function buildRelayCoordinatorTaskBody(opts) {
|
|
|
19
18
|
}
|
|
20
19
|
import { READ_ONLY, WRITE, DESTRUCTIVE } from './toolAnnotations.js';
|
|
21
20
|
import { toolError } from './toolError.js';
|
|
21
|
+
import { registerCapability, registerCapabilities, textResult, } from './capabilityAdapter.js';
|
|
22
22
|
// ZIG-557: the protocol sentences (loop / ack / humanAttention) are sourced
|
|
23
23
|
// from the shared const so this description can't drift from SKILL / server
|
|
24
24
|
// instructions / .cursorrules.
|
|
@@ -47,75 +47,12 @@ const ZIGGS_SEND_MESSAGE_DESCRIPTION = 'Send a chat message as the delegate agen
|
|
|
47
47
|
const ZIGGS_RECORD_ARTIFACT_DESCRIPTION = 'Write an artifact to a chat or agreement scope. Set visibility explicitly. ' +
|
|
48
48
|
'For a finished deliverable, set content_type=result and pass taskId to bind it to the task. ' +
|
|
49
49
|
PROTOCOL.reporting;
|
|
50
|
-
/** Teach the result slot on the record_artifact success path (ZIG-560). */
|
|
51
|
-
function recordArtifactReportingHint(contentType, taskId) {
|
|
52
|
-
if (contentType === 'result') {
|
|
53
|
-
return taskId
|
|
54
|
-
? 'Recorded as a task-bound result artifact. Close the task by setting its terminal result with ziggs_set_task_result ({ summary, status, links }).'
|
|
55
|
-
: 'Recorded as a result artifact, but not bound to a task — pass taskId to bind it, then close the task with ziggs_set_task_result ({ summary, status, links }).';
|
|
56
|
-
}
|
|
57
|
-
return 'Reporting finished work? Record it with content_type=result bound to the task (taskId), then ziggs_set_task_result — chat messages are conversation only.';
|
|
58
|
-
}
|
|
59
|
-
function textResult(data) {
|
|
60
|
-
return {
|
|
61
|
-
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
const contextReadTypeSchema = z.enum([
|
|
65
|
-
'messages',
|
|
66
|
-
'artifacts',
|
|
67
|
-
'agreements',
|
|
68
|
-
'tasks',
|
|
69
|
-
]);
|
|
70
|
-
const artifactVisibilitySchema = z.enum(['chat', 'agent-private']);
|
|
71
50
|
// ZIG-899 — the strict artifact write (fail loudly, return the artifactId)
|
|
72
51
|
// moved into ArtifactsClient.writeStrict, shared with the SDK's record_artifact.
|
|
73
|
-
// ZIG-894 — the leak-guard
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const url = `${getBackendUrl()}/agents/claude-delegate/access`;
|
|
78
|
-
const res = await fetch(url, {
|
|
79
|
-
method: 'GET',
|
|
80
|
-
headers: {
|
|
81
|
-
Authorization: `Bearer ${creds.operatorKey}`,
|
|
82
|
-
'X-Agent-Id': creds.agentId,
|
|
83
|
-
},
|
|
84
|
-
});
|
|
85
|
-
const body = await res.text().catch(() => '');
|
|
86
|
-
if (!res.ok) {
|
|
87
|
-
throw new Error(`GET /agents/claude-delegate/access ${res.status} ${body.slice(0, 200)}`);
|
|
88
|
-
}
|
|
89
|
-
return body ? JSON.parse(body) : {};
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* ZIG-641 / ZIG-648 — cross-connection discovery over the unified GET /grants:
|
|
93
|
-
* every connection grant this agent holds, grouped by connection so
|
|
94
|
-
* ziggs_connection_proxy's connectionId/grantId no longer has to arrive out of
|
|
95
|
-
* band. `provider` comes from the grant's resolved scope label. The response is
|
|
96
|
-
* scanned defensively for leaked secrets, as ConnectionsClient.proxy does.
|
|
97
|
-
*/
|
|
98
|
-
async function listConnectionsForHolder(creds) {
|
|
99
|
-
const client = new GrantsClient(creds.operatorKey, creds.agentId);
|
|
100
|
-
// All pages of the agent's live connection grants (not just the first page).
|
|
101
|
-
const items = await client.listAllGrants({
|
|
102
|
-
scopeKind: 'connection',
|
|
103
|
-
health: 'active',
|
|
104
|
-
});
|
|
105
|
-
const byConnection = new Map();
|
|
106
|
-
for (const g of items) {
|
|
107
|
-
const connectionId = g.scope.id;
|
|
108
|
-
let group = byConnection.get(connectionId);
|
|
109
|
-
if (!group) {
|
|
110
|
-
group = { connectionId, provider: g.scope.label ?? null, grants: [] };
|
|
111
|
-
byConnection.set(connectionId, group);
|
|
112
|
-
}
|
|
113
|
-
group.grants.push(g);
|
|
114
|
-
}
|
|
115
|
-
const result = [...byConnection.values()];
|
|
116
|
-
assertNoLeakedConnectionSecret(JSON.stringify(result));
|
|
117
|
-
return result;
|
|
118
|
-
}
|
|
52
|
+
// ZIG-894 / ZIG-956 — the leak-guard, the connections proxy/request calls, the
|
|
53
|
+
// grouped connection lister (ConnectionsClient.listForHolder), the org lookups
|
|
54
|
+
// (fetchMyOrgs / fetchDelegateAccess), and the whole SDK-twin tool definitions
|
|
55
|
+
// all live in @ziggs-ai/api-client now (shared with the agent SDK).
|
|
119
56
|
/**
|
|
120
57
|
* Ids this delegate answers for: its own agent id plus its principal's user
|
|
121
58
|
* id (operator-key ownerId / ZIGGS_OWNER_USER_ID). Used to decide which
|
|
@@ -283,67 +220,19 @@ function registerMarketplaceTools(server, creds) {
|
|
|
283
220
|
});
|
|
284
221
|
}
|
|
285
222
|
function registerConnectionTools(server, creds) {
|
|
286
|
-
server
|
|
287
|
-
'Calls the backend connections proxy with a grant the owner issued to this agent: the proxy enforces the grant, decrypts the token server-side, makes the upstream provider call, and returns the result (token-leak guarded on both sides). ' +
|
|
288
|
-
'Provide connectionId, grantId, the provider action (e.g. repo:read), and an optional action-specific payload. ' +
|
|
289
|
-
"Don't know connectionId/grantId yet? Call ziggs_list_my_connections first.", {
|
|
290
|
-
connectionId: z.string().describe('Connection to act on'),
|
|
291
|
-
grantId: z
|
|
292
|
-
.string()
|
|
293
|
-
.describe('Grant the owner issued to this agent for the connection'),
|
|
294
|
-
action: z.string().describe('Provider action, e.g. repo:read'),
|
|
295
|
-
payload: z
|
|
296
|
-
.record(z.unknown())
|
|
297
|
-
.optional()
|
|
298
|
-
.describe('Action-specific arguments (provider-defined)'),
|
|
299
|
-
}, WRITE, async ({ connectionId, grantId, action, payload }) => {
|
|
300
|
-
try {
|
|
301
|
-
const result = await new ConnectionsClient(creds.operatorKey, creds.agentId).proxy({ connectionId, grantId, action, payload });
|
|
302
|
-
return textResult({ ok: true, action, result });
|
|
303
|
-
}
|
|
304
|
-
catch (e) {
|
|
305
|
-
return toolError(e.message);
|
|
306
|
-
}
|
|
307
|
-
});
|
|
223
|
+
registerCapability(server, connectionProxyCapability, creds);
|
|
308
224
|
server.tool('ziggs_list_my_connections', 'Discover the third-party connections (credentials like GitHub/Jira, NOT agent-to-agent Links — see ziggs_list_links for that) you hold grants for (e.g. "is GitHub connected?") without the owner sharing connectionId/grantId out of band. ' +
|
|
309
225
|
'Returns, per connection: connectionId, provider, and the grant(s) you hold — each as the canonical grant shape (grantId, scope, caveats, and grant health active/expired/revoked). ' +
|
|
310
226
|
'Read-only — never returns credential material. Feed the connectionId + a grantId with health "active" into ziggs_connection_proxy to actually use it.', {}, READ_ONLY, async () => {
|
|
311
227
|
try {
|
|
312
|
-
const connections = await
|
|
228
|
+
const connections = await new ConnectionsClient(creds.operatorKey, creds.agentId).listForHolder();
|
|
313
229
|
return textResult({ connections });
|
|
314
230
|
}
|
|
315
231
|
catch (e) {
|
|
316
232
|
return toolError(e.message);
|
|
317
233
|
}
|
|
318
234
|
});
|
|
319
|
-
server
|
|
320
|
-
'Opens a connection-consent agreement as an approvable card in the chat you pass — the human approves it there like any other agreement (there is no MCP tool to approve it, so tell them to approve it in the chat). ' +
|
|
321
|
-
'On approval the server is connected (browser OAuth if needed) and you are granted the tools; the result shows up in ziggs_list_my_connections for use with ziggs_connection_proxy.', {
|
|
322
|
-
chatId: z
|
|
323
|
-
.string()
|
|
324
|
-
.describe('The chat you are working in — the consent card is opened there'),
|
|
325
|
-
serverUrl: z.string().describe('Remote MCP server URL (https)'),
|
|
326
|
-
tools: z
|
|
327
|
-
.array(z.string())
|
|
328
|
-
.describe("Tool names you want — become the grant's allowed_actions caveats"),
|
|
329
|
-
reason: z
|
|
330
|
-
.string()
|
|
331
|
-
.optional()
|
|
332
|
-
.describe('Plain-language reason shown to the human deciding'),
|
|
333
|
-
}, WRITE, async ({ chatId, serverUrl, tools, reason }) => {
|
|
334
|
-
try {
|
|
335
|
-
const result = await new ConnectionsClient(creds.operatorKey, creds.agentId).requestMcpConnection({ chatId, serverUrl, tools, reason });
|
|
336
|
-
return textResult({
|
|
337
|
-
ok: true,
|
|
338
|
-
...result,
|
|
339
|
-
note: 'A connection-consent card is now in the chat awaiting your principal. Tell the human now (pull-only MCP has no push) — they approve it right in the chat. ' +
|
|
340
|
-
'Once approved, the connection + grant appear in ziggs_list_my_connections for ziggs_connection_proxy.',
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
catch (e) {
|
|
344
|
-
return toolError(e.message);
|
|
345
|
-
}
|
|
346
|
-
});
|
|
235
|
+
registerCapability(server, requestConnectionCapability, creds);
|
|
347
236
|
}
|
|
348
237
|
export function registerZiggsTools(server, creds, cfg) {
|
|
349
238
|
server.tool('ziggs_auth_status', 'Verify MCP OAuth binding: delegate agent id, owner user id, and org scope. Call after connect before inbox/chats. Includes pendingDecisions summary when approve/reject is waiting. (Renamed from ziggs_connection_status — "connection" now refers only to third-party credential connections, see ziggs_connection_proxy.)', {}, READ_ONLY, async () => {
|
|
@@ -527,17 +416,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
527
416
|
return toolError(e.message);
|
|
528
417
|
}
|
|
529
418
|
});
|
|
530
|
-
server
|
|
531
|
-
participantId: z.string().describe('User or agent id to converse with'),
|
|
532
|
-
}, WRITE, async ({ participantId }) => {
|
|
533
|
-
try {
|
|
534
|
-
const out = await openConversation(participantId, creds);
|
|
535
|
-
return textResult(out);
|
|
536
|
-
}
|
|
537
|
-
catch (e) {
|
|
538
|
-
return toolError(e.message);
|
|
539
|
-
}
|
|
540
|
-
});
|
|
419
|
+
registerCapability(server, openConversationCapability, creds);
|
|
541
420
|
server.tool('ziggs_send_message', ZIGGS_SEND_MESSAGE_DESCRIPTION, {
|
|
542
421
|
chatId: z.string(),
|
|
543
422
|
receiverId: z
|
|
@@ -574,7 +453,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
574
453
|
return toolError(e.message);
|
|
575
454
|
}
|
|
576
455
|
});
|
|
577
|
-
server.tool('ziggs_propose_agreement', 'Propose a direct agreement to one counterparty (proposedTo) in a chat. The payer is always derived server-side as the non-providing side — there is no payer input. Omit providerId (or set it to proposedTo) to commission the recipient (they work, your side pays). Set providerId to your own agent id to offer (you work, proposedTo pays). Set providerId to a third-party agent id to broker (they work, proposedTo pays) — that provider must have an active published offer whose terms match this proposal (price, lifecycle, engagementKind, etc.) or the call fails naming the mismatched field. engagementKind "service" (default) = one deliverable; "hire" = ongoing engagement. price is recorded on the agreement but does not itself trigger a transfer.', {
|
|
456
|
+
server.tool('ziggs_propose_agreement', 'Propose a direct agreement to one counterparty (proposedTo) in a chat. The payer is always derived server-side as the non-providing side — there is no payer input. Omit providerId (or set it to proposedTo) to commission the recipient (they work, your side pays). Set providerId to your own agent id to offer (you work, proposedTo pays). Set providerId to a third-party agent id to broker (they work, proposedTo pays) — that provider must have an active published offer whose terms match this proposal (price, lifecycle, engagementKind, etc.) or the call fails naming the mismatched field. engagementKind "service" (default) = one deliverable; "hire" = ongoing engagement. Agreements are STANDING by default (lifecycle "open": no expiry, unlimited tasks) — hire once, then keep spawning tasks under the same agreement; set expiresAt (time-bound) or maxExecutions (count-bound) only when the engagement should end on its own. price is recorded on the agreement but does not itself trigger a transfer.', {
|
|
578
457
|
proposedTo: z.string(),
|
|
579
458
|
chatId: z.string(),
|
|
580
459
|
description: z.string(),
|
|
@@ -587,7 +466,25 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
587
466
|
.enum(['hire', 'service'])
|
|
588
467
|
.optional()
|
|
589
468
|
.describe("'service' (default) = one-off deliverable; 'hire' = ongoing engagement"),
|
|
590
|
-
|
|
469
|
+
expiresAt: z
|
|
470
|
+
.string()
|
|
471
|
+
.optional()
|
|
472
|
+
.describe('ISO date: the agreement ends (is cancelled, tasks and all) at this time. Omit for a standing agreement.'),
|
|
473
|
+
maxExecutions: z
|
|
474
|
+
.number()
|
|
475
|
+
.int()
|
|
476
|
+
.positive()
|
|
477
|
+
.optional()
|
|
478
|
+
.describe('The agreement auto-fulfills after this many completed tasks. Omit for unlimited tasks.'),
|
|
479
|
+
lifecycle: z
|
|
480
|
+
.enum(['open', 'time-bound', 'count-bound'])
|
|
481
|
+
.optional()
|
|
482
|
+
.describe("Usually inferred: expiresAt → 'time-bound', maxExecutions → 'count-bound', neither → 'open' (standing)."),
|
|
483
|
+
billing: z
|
|
484
|
+
.enum(['total', 'per_task'])
|
|
485
|
+
.optional()
|
|
486
|
+
.describe("How price reads. 'total' (default) = one price for the whole engagement, escrowed now and paid at the end. 'per_task' = a RATE charged for each completed task, paid as work lands — requires a standing (open) agreement, and is the default for a hire. Never send 'per_task' for a one-off price or the payer is charged it once per task."),
|
|
487
|
+
}, WRITE, async ({ proposedTo, chatId, description, providerId, price, engagementKind, expiresAt, maxExecutions, lifecycle, billing, }) => {
|
|
591
488
|
try {
|
|
592
489
|
const agreement = await proposeDirectTo({
|
|
593
490
|
proposedTo,
|
|
@@ -596,6 +493,10 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
596
493
|
providerId: providerId?.trim() || proposedTo,
|
|
597
494
|
price,
|
|
598
495
|
engagementKind: engagementKind ?? 'service',
|
|
496
|
+
expiresAt,
|
|
497
|
+
maxExecutions,
|
|
498
|
+
lifecycle,
|
|
499
|
+
billing,
|
|
599
500
|
}, creds);
|
|
600
501
|
return textResult({ agreement });
|
|
601
502
|
}
|
|
@@ -665,7 +566,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
665
566
|
return toolError(e.message);
|
|
666
567
|
}
|
|
667
568
|
});
|
|
668
|
-
server.tool('ziggs_fulfill_agreement', '
|
|
569
|
+
server.tool('ziggs_fulfill_agreement', 'END an agreement you PROVIDE — permanently (POST /agreements/:id/fulfill). Fulfilling terminates the whole relationship, not one deliverable: every grant the agreement conferred (context, connection, payment) is revoked, its shared space is torn down, and it cannot be reopened — the counterparty would have to re-hire you from scratch. Finished WORK is reported with ziggs_set_task_result, which closes the task and leaves the agreement standing for the next one. Only fulfill a count/time-bound engagement whose full scope is delivered and where nothing more is expected — never a standing hire that just finished a task. Party-gated server-side: only the providing side can fulfill.', {
|
|
669
570
|
agreementId: z.string().describe('The agreement you provide, to mark fulfilled'),
|
|
670
571
|
}, WRITE, async ({ agreementId }) => {
|
|
671
572
|
try {
|
|
@@ -678,13 +579,9 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
678
579
|
});
|
|
679
580
|
server.tool('ziggs_inbox', ZIGGS_INBOX_DESCRIPTION, {
|
|
680
581
|
ack: z
|
|
681
|
-
.
|
|
682
|
-
kind: z.enum(['chat', 'agreement', 'org']),
|
|
683
|
-
id: z.string(),
|
|
684
|
-
upTo: z.string().describe('ISO timestamp handled up to (inclusive)'),
|
|
685
|
-
}))
|
|
582
|
+
.string()
|
|
686
583
|
.optional()
|
|
687
|
-
.describe('
|
|
584
|
+
.describe("The envelope's `ackTo` from a previous call, once you have handled everything it carried — acked before fetching, monotonic (an older value is a no-op)."),
|
|
688
585
|
waitSeconds: z
|
|
689
586
|
.number()
|
|
690
587
|
.optional()
|
|
@@ -692,7 +589,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
692
589
|
}, READ_ONLY, async ({ ack, waitSeconds }) => {
|
|
693
590
|
try {
|
|
694
591
|
const client = new InboxClient(creds.operatorKey, creds.agentId);
|
|
695
|
-
const acked = ack
|
|
592
|
+
const acked = ack ? await client.ack(ack) : null;
|
|
696
593
|
const inbox = await client.getInbox(waitSeconds != null ? { waitSeconds } : {});
|
|
697
594
|
let activeTasks = [];
|
|
698
595
|
let activeTasksError;
|
|
@@ -724,145 +621,23 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
724
621
|
return toolError(e.message);
|
|
725
622
|
}
|
|
726
623
|
});
|
|
727
|
-
server
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
.describe('Opaque cursor from a prior nextCursor'),
|
|
740
|
-
limit: z.number().optional().describe('Page size (default server-side)'),
|
|
741
|
-
}, READ_ONLY, async ({ scopeKind, health, cursor, limit }) => {
|
|
742
|
-
try {
|
|
743
|
-
const client = new GrantsClient(creds.operatorKey, creds.agentId);
|
|
744
|
-
const kinds = scopeKind && scopeKind.length
|
|
745
|
-
? scopeKind
|
|
746
|
-
: undefined;
|
|
747
|
-
const { items, nextCursor } = await client.listGrants({
|
|
748
|
-
scopeKind: kinds,
|
|
749
|
-
// Reach = live grants only by default; pass health for expired/revoked.
|
|
750
|
-
health: health ?? 'active',
|
|
751
|
-
cursor,
|
|
752
|
-
limit,
|
|
753
|
-
});
|
|
754
|
-
const unreadable = unreadableGrantRails(creds.operatorKey, kinds);
|
|
755
|
-
return textResult({
|
|
756
|
-
count: items.length,
|
|
757
|
-
grants: items,
|
|
758
|
-
nextCursor,
|
|
759
|
-
...(unreadable && unreadable.length
|
|
760
|
-
? { unreadableRails: unreadable }
|
|
761
|
-
: {}),
|
|
762
|
-
});
|
|
763
|
-
}
|
|
764
|
-
catch (e) {
|
|
765
|
-
return toolError(e.message);
|
|
766
|
-
}
|
|
767
|
-
});
|
|
768
|
-
server.tool('ziggs_expand_context', 'Expand a grant you hold into the chat/agreement ids inside its scope, so you can actually read through it. ziggs_list_grants tells you that you hold e.g. org:acme or agreement:x; this returns the { chats, agreements } (ids + labels only, never content) that scope covers — feed an id to ziggs_read_context (via=chat:<id> / agreement:<id>). Org scope is capped: truncatedChats/truncatedAgreements say how many were left off. Holder-only, grant-fenced.', {
|
|
769
|
-
grantId: z
|
|
770
|
-
.string()
|
|
771
|
-
.describe('A grant you hold (grantId from ziggs_list_grants) to expand'),
|
|
772
|
-
}, READ_ONLY, async ({ grantId }) => {
|
|
773
|
-
try {
|
|
774
|
-
const client = new ContextGrantsClient(creds.operatorKey, creds.agentId);
|
|
775
|
-
return textResult(await client.getReach(grantId));
|
|
776
|
-
}
|
|
777
|
-
catch (e) {
|
|
778
|
-
return toolError(e.message);
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
server.tool('ziggs_discover_grantable', 'See what context EXISTS in your orgs that you CANNOT read yet — so you can ask for it instead of failing blind. Covers chats, agreements, and connections (type is "chat" | "agreement" | "connection"; connection labels are the provider name only). Returns labels only: { type, label, scopeRef, orgId } per item, never content, member names, tokens, or money. Bounded to orgs you have an active agreement in. To act on one, ask your human to grant it, or (if you hold a broader grant of your own) delegate via ziggs_delegate_grant using the scopeRef. Use ziggs_list_grants for what you already hold; this is what you lack.', {}, READ_ONLY, async () => {
|
|
782
|
-
try {
|
|
783
|
-
const client = new ContextDiscoveryClient(creds.operatorKey, creds.agentId);
|
|
784
|
-
const items = await client.discoverGrantable();
|
|
785
|
-
return textResult({ count: items.length, items });
|
|
786
|
-
}
|
|
787
|
-
catch (e) {
|
|
788
|
-
return toolError(e.message);
|
|
789
|
-
}
|
|
790
|
-
});
|
|
791
|
-
server.tool('ziggs_read_context', 'Read the contents of a scope you already hold: messages | artifacts | agreements | tasks (the type param), under via=chat:<id>, agreement:<id>, or task:<id>. Forward-delta with after+direction=forward; cursor pagination; contextGrantId pins a grant. The response carries a `readPlan` with the next page and/or forward-delta call pre-filled (after=this page\'s latestSequence), so you can keep reading without rebuilding args. This is the single read path for all four types — to discover which scopes exist (your chats / tasks / agreements / grants / links), use the ziggs_list_* tools.', {
|
|
792
|
-
type: contextReadTypeSchema.describe('Resource type to read'),
|
|
793
|
-
via: z
|
|
794
|
-
.string()
|
|
795
|
-
.describe('Scope entry, e.g. chat:<id>, agreement:<id>, task:<id>'),
|
|
796
|
-
cursor: z.string().optional().describe('Opaque cursor from prior nextCursor'),
|
|
797
|
-
after: z
|
|
798
|
-
.string()
|
|
799
|
-
.optional()
|
|
800
|
-
.describe('ISO timestamp for forward-delta (messages/artifacts)'),
|
|
801
|
-
direction: z
|
|
802
|
-
.enum(['forward'])
|
|
803
|
-
.optional()
|
|
804
|
-
.describe('Use forward with after for message forward-delta'),
|
|
805
|
-
limit: z.number().optional().describe('Page size (default server-side)'),
|
|
806
|
-
state: z.string().optional().describe('Task state filter (tasks only)'),
|
|
807
|
-
contextGrantId: z
|
|
808
|
-
.string()
|
|
809
|
-
.optional()
|
|
810
|
-
.describe('Pin a specific grant when holding several'),
|
|
811
|
-
}, READ_ONLY, async ({ type, via, cursor, after, direction, limit, state, contextGrantId }) => {
|
|
812
|
-
try {
|
|
813
|
-
const client = new ContextReadClient(creds.operatorKey, creds.agentId);
|
|
814
|
-
const result = await client.read(type, {
|
|
815
|
-
via,
|
|
816
|
-
cursor,
|
|
817
|
-
after,
|
|
818
|
-
direction,
|
|
819
|
-
limit,
|
|
820
|
-
state,
|
|
821
|
-
contextGrantId,
|
|
822
|
-
});
|
|
823
|
-
const readPlan = buildReadContextReadPlan(result, type, via, contextGrantId);
|
|
824
|
-
return textResult(readPlan.length ? { ...result, readPlan } : result);
|
|
825
|
-
}
|
|
826
|
-
catch (e) {
|
|
827
|
-
return toolError(e.message);
|
|
828
|
-
}
|
|
624
|
+
registerCapabilities(server, GRANTS_CAPABILITIES, creds);
|
|
625
|
+
registerCapability(server, contextExpandReachCapability, creds);
|
|
626
|
+
registerCapability(server, contextDiscoverGrantableCapability, creds);
|
|
627
|
+
// The read-plan is MCP-local decoration (its next-call tool names and the
|
|
628
|
+
// inbox loop it feeds are this surface's); schema + handler stay shared.
|
|
629
|
+
registerCapability(server, contextReadCapability, creds, {
|
|
630
|
+
transformResult: (result, args) => {
|
|
631
|
+
const readPlan = buildReadContextReadPlan(result, args['type'], args['via'], args['contextGrantId']);
|
|
632
|
+
return readPlan.length
|
|
633
|
+
? { ...result, readPlan }
|
|
634
|
+
: result;
|
|
635
|
+
},
|
|
829
636
|
});
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
agreementId: z
|
|
835
|
-
.string()
|
|
836
|
-
.optional()
|
|
837
|
-
.describe('Target agreement (xor chatId)'),
|
|
838
|
-
taskId: z
|
|
839
|
-
.string()
|
|
840
|
-
.optional()
|
|
841
|
-
.describe('Optional task — creates a TaskArtifactLink alongside the primary scope link'),
|
|
842
|
-
content_type: z.string().optional().describe('Default text'),
|
|
843
|
-
idempotencyKey: z
|
|
844
|
-
.string()
|
|
845
|
-
.optional()
|
|
846
|
-
.describe('Optional dedup key: a redelivered record with the same key no-ops and returns the original artifact. Derive it deterministically (e.g. from the source event + step) — not a random value — so a crash-replay reproduces it.'),
|
|
847
|
-
}, WRITE, async ({ text, visibility, chatId, agreementId, taskId, content_type, idempotencyKey }) => {
|
|
848
|
-
try {
|
|
849
|
-
if ((chatId && agreementId) || (!chatId && !agreementId)) {
|
|
850
|
-
return toolError('Pass exactly one of chatId or agreementId');
|
|
851
|
-
}
|
|
852
|
-
const { artifactId } = await new ArtifactsClient(creds.operatorKey, creds.agentId).writeStrict({ text, visibility, chatId, agreementId, taskId, content_type, idempotencyKey });
|
|
853
|
-
return textResult({
|
|
854
|
-
ok: true,
|
|
855
|
-
artifactId,
|
|
856
|
-
visibility,
|
|
857
|
-
chatId,
|
|
858
|
-
agreementId,
|
|
859
|
-
taskId,
|
|
860
|
-
reportingHint: recordArtifactReportingHint(content_type, taskId),
|
|
861
|
-
});
|
|
862
|
-
}
|
|
863
|
-
catch (e) {
|
|
864
|
-
return toolError(e.message);
|
|
865
|
-
}
|
|
637
|
+
// Description override keeps the reporting rule sourced from the shared
|
|
638
|
+
// PROTOCOL const (ZIG-557) so it can't drift from SKILL/server instructions.
|
|
639
|
+
registerCapability(server, recordArtifactCapability, creds, {
|
|
640
|
+
description: ZIGGS_RECORD_ARTIFACT_DESCRIPTION,
|
|
866
641
|
});
|
|
867
642
|
// ---------------------------------------------------------------------------
|
|
868
643
|
// Task mutation tools (ZIG-555)
|
package/dist/trustTools.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { type Creds } from '@ziggs-ai/api-client';
|
|
3
3
|
import type { ZiggsMcpConfig } from './config.js';
|
|
4
|
-
/** ZIG-433 — agent search + context grant management through MCP.
|
|
4
|
+
/** ZIG-433 / ZIG-956 — agent search + context grant management through MCP.
|
|
5
|
+
* The SDK-twin tools (search/get, links, delegate) come from the shared
|
|
6
|
+
* capability layer; only the human-authority grant tools (issue/revoke) stay
|
|
7
|
+
* MCP-local. */
|
|
5
8
|
export declare function registerTrustTools(server: McpServer, creds: Creds, cfg?: ZiggsMcpConfig): void;
|