@ziggs-ai/ziggs-mcp 0.1.19 → 0.1.22
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/inboxToolResult.d.ts +61 -12
- package/dist/inboxToolResult.js +155 -38
- package/dist/tools.js +17 -5
- package/dist/trustTools.js +2 -2
- package/package.json +1 -1
|
@@ -1,18 +1,67 @@
|
|
|
1
|
-
import type { InboxAckResult, InboxEnvelope, Task } from '@ziggs-ai/api-client';
|
|
1
|
+
import type { ContextReachDescriptor, ContextReadType, InboxAckResult, InboxEnvelope, Task } from '@ziggs-ai/api-client';
|
|
2
2
|
/**
|
|
3
|
-
* ZIG-
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
3
|
+
* ZIG-634 (Step 1): a pre-filled next call. The agent can run it verbatim
|
|
4
|
+
* instead of assembling args from the ids scattered through the response.
|
|
5
|
+
* `tool` + `args` mirror the MCP tool signature; `why` is a one-line reason.
|
|
6
|
+
*/
|
|
7
|
+
export interface ReadPlanCall {
|
|
8
|
+
tool: string;
|
|
9
|
+
args: Record<string, unknown>;
|
|
10
|
+
why: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* ZIG-634: replace the free-text `nextActions` hints with typed `readPlan`
|
|
14
|
+
* call objects — tool name + pre-filled args — so the most common loop
|
|
15
|
+
* (inbox → read each chat with news → ack) needs no guesswork.
|
|
16
|
+
*
|
|
17
|
+
* Safe by construction: every entry is synthesized purely from fields already
|
|
18
|
+
* on the envelope (scope kinds, ids, per-chat counts, latestAt). No new data
|
|
19
|
+
* is read and no new permission check runs — this is the same information the
|
|
20
|
+
* caller already received, restated as runnable calls.
|
|
21
|
+
*
|
|
22
|
+
* Mapping honours how reads resolve server-side: messages read only via chat,
|
|
23
|
+
* artifacts via chat or agreement. For multi-chat scopes (org / agreement) we
|
|
24
|
+
* use the per-chat breakdown (ZIG-543) to name the chatIds.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildReadPlan(inbox: InboxEnvelope): ReadPlanCall[];
|
|
27
|
+
/**
|
|
28
|
+
* ZIG-634: forward-continuation for a read_context page. Built only from fields
|
|
29
|
+
* already on the page (via, hasMore/nextCursor, latestSequence) plus the grant
|
|
30
|
+
* id the caller presented — echoed back, never discovered. Nothing new is read.
|
|
7
31
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
32
|
+
* - When the page has more rows in this window: a cursor call for the next page.
|
|
33
|
+
* - When the page reports a high-water sequence: a forward-delta call that
|
|
34
|
+
* returns only items created after this page (after = latestSequence).
|
|
11
35
|
*/
|
|
12
|
-
export declare function
|
|
36
|
+
export declare function buildReadContextReadPlan(page: {
|
|
37
|
+
hasMore?: boolean;
|
|
38
|
+
nextCursor?: string | null;
|
|
39
|
+
latestSequence?: string | null;
|
|
40
|
+
}, type: ContextReadType, via: string, presentedGrantId?: string): ReadPlanCall[];
|
|
41
|
+
/**
|
|
42
|
+
* ZIG-635 (Step 2): the covering grant attached to a scope so the agent can
|
|
43
|
+
* pin the right X-Context-Grant-Id without a separate discover_context call.
|
|
44
|
+
* Grant metadata only — never a resource-derived field.
|
|
45
|
+
*/
|
|
46
|
+
export interface ScopeGrantTag {
|
|
47
|
+
grantId: string;
|
|
48
|
+
temporal: 'from-now' | 'from-start';
|
|
49
|
+
watermarkAt: string;
|
|
50
|
+
expiresAt: string | null;
|
|
51
|
+
parentGrantId: string | null;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* ZIG-635: index the caller's own live reach descriptors by scope. The reach
|
|
55
|
+
* list is already the caller's non-expired grants (holderId == principalId),
|
|
56
|
+
* so this is grant metadata the caller already holds — no protected content.
|
|
57
|
+
*/
|
|
58
|
+
export declare function indexReachByScope(reach: ContextReachDescriptor[]): Map<string, ScopeGrantTag>;
|
|
13
59
|
/**
|
|
14
60
|
* Put humanAttention first so MCP hosts surface it before counts (ZIG-482),
|
|
15
|
-
* and append
|
|
16
|
-
*
|
|
61
|
+
* and append readPlan last so each inbox call self-narrates the follow-up
|
|
62
|
+
* calls (ZIG-634) without disturbing the leading humanAttention key.
|
|
63
|
+
*
|
|
64
|
+
* When `reach` (the caller's own live grants) is passed, each scope is tagged
|
|
65
|
+
* with its covering grant (ZIG-635) so the agent can pin X-Context-Grant-Id.
|
|
17
66
|
*/
|
|
18
|
-
export declare function formatInboxToolResult(inbox: InboxEnvelope, ack?: InboxAckResult | null, webOrigin?: string, activeTasks?: Task[]): Record<string, unknown>;
|
|
67
|
+
export declare function formatInboxToolResult(inbox: InboxEnvelope, ack?: InboxAckResult | null, webOrigin?: string, activeTasks?: Task[], reach?: ContextReachDescriptor[]): Record<string, unknown>;
|
package/dist/inboxToolResult.js
CHANGED
|
@@ -1,77 +1,194 @@
|
|
|
1
1
|
import { formatPendingDecisionsPayload, resolveWebAppOrigin, } from './pendingDecisions.js';
|
|
2
|
-
/** Keep the
|
|
3
|
-
const
|
|
4
|
-
function
|
|
5
|
-
return
|
|
2
|
+
/** Keep the plan bounded; the full scopes array still carries everything. */
|
|
3
|
+
const MAX_READ_PLAN = 12;
|
|
4
|
+
function readContextCall(type, kind, id) {
|
|
5
|
+
return {
|
|
6
|
+
tool: 'ziggs_read_context',
|
|
7
|
+
args: { type, via: `${kind}:${id}` },
|
|
8
|
+
why: `open the ${type} behind the count on ${kind}:${id}`,
|
|
9
|
+
};
|
|
6
10
|
}
|
|
7
11
|
/**
|
|
8
|
-
* ZIG-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* upfront prompt. Thin protocol up front, heavy guidance in the response.
|
|
12
|
+
* ZIG-634: replace the free-text `nextActions` hints with typed `readPlan`
|
|
13
|
+
* call objects — tool name + pre-filled args — so the most common loop
|
|
14
|
+
* (inbox → read each chat with news → ack) needs no guesswork.
|
|
12
15
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
+
* Safe by construction: every entry is synthesized purely from fields already
|
|
17
|
+
* on the envelope (scope kinds, ids, per-chat counts, latestAt). No new data
|
|
18
|
+
* is read and no new permission check runs — this is the same information the
|
|
19
|
+
* caller already received, restated as runnable calls.
|
|
20
|
+
*
|
|
21
|
+
* Mapping honours how reads resolve server-side: messages read only via chat,
|
|
22
|
+
* artifacts via chat or agreement. For multi-chat scopes (org / agreement) we
|
|
23
|
+
* use the per-chat breakdown (ZIG-543) to name the chatIds.
|
|
16
24
|
*/
|
|
17
|
-
export function
|
|
18
|
-
const
|
|
25
|
+
export function buildReadPlan(inbox) {
|
|
26
|
+
const plan = [];
|
|
19
27
|
const proposals = inbox.proposalsAwaitingMe ?? [];
|
|
20
28
|
const connectionRequests = inbox.connectionRequestsAwaitingMe ?? [];
|
|
21
29
|
const scopes = inbox.scopes ?? [];
|
|
22
30
|
// Decisions first — these also drive humanAttention (pull-only: no push).
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
// The decision (approve/reject) is the human's; we only pre-fill the target.
|
|
32
|
+
for (const p of proposals) {
|
|
33
|
+
if (plan.length >= MAX_READ_PLAN)
|
|
34
|
+
break;
|
|
35
|
+
plan.push({
|
|
36
|
+
tool: 'ziggs_respond_to_agreement',
|
|
37
|
+
args: { agreementId: p.agreementId },
|
|
38
|
+
why: 'agreement proposal awaiting your response — wait for the human to approve/reject',
|
|
39
|
+
});
|
|
26
40
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
for (const c of connectionRequests) {
|
|
42
|
+
if (plan.length >= MAX_READ_PLAN)
|
|
43
|
+
break;
|
|
44
|
+
plan.push({
|
|
45
|
+
tool: 'ziggs_respond_to_agreement',
|
|
46
|
+
args: { agreementId: c.requestId },
|
|
47
|
+
why: 'connection request awaiting your response — wait for the human to approve/reject',
|
|
48
|
+
});
|
|
30
49
|
}
|
|
31
50
|
// Reads — point each scope's news at the call that opens it.
|
|
32
51
|
for (const s of scopes) {
|
|
33
|
-
if (
|
|
52
|
+
if (plan.length >= MAX_READ_PLAN)
|
|
34
53
|
break;
|
|
35
54
|
const { kind, id } = s.scope;
|
|
36
55
|
if (kind === 'chat') {
|
|
37
56
|
if (s.newMessages)
|
|
38
|
-
|
|
57
|
+
plan.push(readContextCall('messages', 'chat', id));
|
|
39
58
|
if (s.newArtifacts)
|
|
40
|
-
|
|
59
|
+
plan.push(readContextCall('artifacts', 'chat', id));
|
|
41
60
|
}
|
|
42
61
|
else if (kind === 'agreement') {
|
|
43
62
|
// Messages resolve only via chat — name the chats from the breakdown.
|
|
44
63
|
for (const c of s.chats ?? []) {
|
|
45
64
|
if (c.newMessages)
|
|
46
|
-
|
|
65
|
+
plan.push(readContextCall('messages', 'chat', c.chatId));
|
|
47
66
|
}
|
|
48
67
|
// Artifacts (incl. task-result artifacts) read directly via the agreement.
|
|
49
68
|
if (s.newArtifacts)
|
|
50
|
-
|
|
69
|
+
plan.push(readContextCall('artifacts', 'agreement', id));
|
|
51
70
|
}
|
|
52
71
|
else {
|
|
53
72
|
// org: both messages and artifacts resolve per chat only.
|
|
54
73
|
for (const c of s.chats ?? []) {
|
|
55
74
|
if (c.newMessages)
|
|
56
|
-
|
|
75
|
+
plan.push(readContextCall('messages', 'chat', c.chatId));
|
|
57
76
|
if (c.newArtifacts)
|
|
58
|
-
|
|
77
|
+
plan.push(readContextCall('artifacts', 'chat', c.chatId));
|
|
59
78
|
}
|
|
60
79
|
}
|
|
61
80
|
}
|
|
62
|
-
// Close the loop: reading never clears the inbox — ack
|
|
63
|
-
|
|
64
|
-
|
|
81
|
+
// Close the loop: reading never clears the inbox — pre-fill the ack call with
|
|
82
|
+
// each scope's own latestAt (only scopes that actually have a high-water mark).
|
|
83
|
+
const ackTargets = scopes
|
|
84
|
+
.filter((s) => s.latestAt)
|
|
85
|
+
.map((s) => ({ kind: s.scope.kind, id: s.scope.id, upTo: s.latestAt }));
|
|
86
|
+
if (ackTargets.length && plan.length < MAX_READ_PLAN) {
|
|
87
|
+
plan.push({
|
|
88
|
+
tool: 'ziggs_inbox',
|
|
89
|
+
args: { ack: ackTargets },
|
|
90
|
+
why: 'reading does not clear the inbox — ack the scopes you handled (drop any you did not)',
|
|
91
|
+
});
|
|
65
92
|
}
|
|
66
|
-
return
|
|
93
|
+
return plan.slice(0, MAX_READ_PLAN);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* ZIG-634: forward-continuation for a read_context page. Built only from fields
|
|
97
|
+
* already on the page (via, hasMore/nextCursor, latestSequence) plus the grant
|
|
98
|
+
* id the caller presented — echoed back, never discovered. Nothing new is read.
|
|
99
|
+
*
|
|
100
|
+
* - When the page has more rows in this window: a cursor call for the next page.
|
|
101
|
+
* - When the page reports a high-water sequence: a forward-delta call that
|
|
102
|
+
* returns only items created after this page (after = latestSequence).
|
|
103
|
+
*/
|
|
104
|
+
export function buildReadContextReadPlan(page, type, via, presentedGrantId) {
|
|
105
|
+
const plan = [];
|
|
106
|
+
// Only echo a grant the caller already presented — never one we discovered.
|
|
107
|
+
const grant = presentedGrantId ? { contextGrantId: presentedGrantId } : {};
|
|
108
|
+
if (page.hasMore && page.nextCursor) {
|
|
109
|
+
plan.push({
|
|
110
|
+
tool: 'ziggs_read_context',
|
|
111
|
+
args: { type, via, cursor: page.nextCursor, ...grant },
|
|
112
|
+
why: 'more rows in this window — next page',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (page.latestSequence) {
|
|
116
|
+
plan.push({
|
|
117
|
+
tool: 'ziggs_read_context',
|
|
118
|
+
args: {
|
|
119
|
+
type,
|
|
120
|
+
via,
|
|
121
|
+
direction: 'forward',
|
|
122
|
+
after: page.latestSequence,
|
|
123
|
+
...grant,
|
|
124
|
+
},
|
|
125
|
+
why: 'forward-delta — only items created after this page',
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return plan;
|
|
129
|
+
}
|
|
130
|
+
function toScopeGrantTag(d) {
|
|
131
|
+
return {
|
|
132
|
+
grantId: d.grantId,
|
|
133
|
+
temporal: d.temporal,
|
|
134
|
+
watermarkAt: d.watermarkAt,
|
|
135
|
+
expiresAt: d.expiresAt,
|
|
136
|
+
parentGrantId: d.parentGrantId,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* When several live grants cover the same scope, pick the one the agent should
|
|
141
|
+
* pin: the broadest history first (from-start over from-now, then the earliest
|
|
142
|
+
* watermark), so a read against it returns the most the caller is entitled to.
|
|
143
|
+
*/
|
|
144
|
+
function isBroaderGrant(a, b) {
|
|
145
|
+
if (a.temporal !== b.temporal)
|
|
146
|
+
return a.temporal === 'from-start';
|
|
147
|
+
return a.watermarkAt < b.watermarkAt;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* ZIG-635: index the caller's own live reach descriptors by scope. The reach
|
|
151
|
+
* list is already the caller's non-expired grants (holderId == principalId),
|
|
152
|
+
* so this is grant metadata the caller already holds — no protected content.
|
|
153
|
+
*/
|
|
154
|
+
export function indexReachByScope(reach) {
|
|
155
|
+
const byScope = new Map();
|
|
156
|
+
for (const d of reach) {
|
|
157
|
+
if (!d?.scope)
|
|
158
|
+
continue;
|
|
159
|
+
const key = `${d.scope.kind}:${d.scope.id}`;
|
|
160
|
+
const tag = toScopeGrantTag(d);
|
|
161
|
+
const existing = byScope.get(key);
|
|
162
|
+
if (!existing || isBroaderGrant(tag, existing))
|
|
163
|
+
byScope.set(key, tag);
|
|
164
|
+
}
|
|
165
|
+
return byScope;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* ZIG-635: tag each inbox scope with its covering grant. Scopes with no
|
|
169
|
+
* matching live grant (e.g. reachable via membership, not a grant) are left
|
|
170
|
+
* untagged — the agent keeps navigating by id, never a fabricated grant.
|
|
171
|
+
*/
|
|
172
|
+
function tagScopesWithGrants(scopes, reach) {
|
|
173
|
+
if (!reach?.length)
|
|
174
|
+
return scopes;
|
|
175
|
+
const byScope = indexReachByScope(reach);
|
|
176
|
+
return scopes.map((s) => {
|
|
177
|
+
const tag = byScope.get(`${s.scope.kind}:${s.scope.id}`);
|
|
178
|
+
return tag ? { ...s, grant: tag } : s;
|
|
179
|
+
});
|
|
67
180
|
}
|
|
68
181
|
/**
|
|
69
182
|
* Put humanAttention first so MCP hosts surface it before counts (ZIG-482),
|
|
70
|
-
* and append
|
|
71
|
-
*
|
|
183
|
+
* and append readPlan last so each inbox call self-narrates the follow-up
|
|
184
|
+
* calls (ZIG-634) without disturbing the leading humanAttention key.
|
|
185
|
+
*
|
|
186
|
+
* When `reach` (the caller's own live grants) is passed, each scope is tagged
|
|
187
|
+
* with its covering grant (ZIG-635) so the agent can pin X-Context-Grant-Id.
|
|
72
188
|
*/
|
|
73
|
-
export function formatInboxToolResult(inbox, ack, webOrigin, activeTasks) {
|
|
74
|
-
const
|
|
189
|
+
export function formatInboxToolResult(inbox, ack, webOrigin, activeTasks, reach) {
|
|
190
|
+
const readPlan = buildReadPlan(inbox);
|
|
191
|
+
const scopes = tagScopesWithGrants(inbox.scopes ?? [], reach);
|
|
75
192
|
const origin = resolveWebAppOrigin(webOrigin);
|
|
76
193
|
const pending = formatPendingDecisionsPayload(inbox, origin, { activeTasks });
|
|
77
194
|
const pendingTail = pending.hasActionable === true
|
|
@@ -84,14 +201,14 @@ export function formatInboxToolResult(inbox, ack, webOrigin, activeTasks) {
|
|
|
84
201
|
...(pending.workChatCard ? { workChatCard: pending.workChatCard } : {}),
|
|
85
202
|
}
|
|
86
203
|
: {};
|
|
87
|
-
const tail = { ...pendingTail, ...(
|
|
204
|
+
const tail = { ...pendingTail, ...(readPlan.length ? { readPlan } : {}) };
|
|
88
205
|
const { humanAttention, ...rest } = inbox;
|
|
89
206
|
const payload = ack
|
|
90
|
-
? { acked: ack.acked, ...rest, ...tail }
|
|
91
|
-
: { ...rest, ...tail };
|
|
207
|
+
? { acked: ack.acked, ...rest, scopes, ...tail }
|
|
208
|
+
: { ...rest, scopes, ...tail };
|
|
92
209
|
return humanAttention
|
|
93
210
|
? { humanAttention, ...payload }
|
|
94
211
|
: ack
|
|
95
212
|
? payload
|
|
96
|
-
: { ...inbox, ...tail };
|
|
213
|
+
: { ...inbox, scopes, ...tail };
|
|
97
214
|
}
|
package/dist/tools.js
CHANGED
|
@@ -3,7 +3,7 @@ import { z } from 'zod';
|
|
|
3
3
|
import { getAgreement, getMyAgreements, listMyChats, openConversation, proposeDirectTo, proposeBroadcast, publishOffer, respondToAgreement, ScopeClient, sendChatMessage, ContextDiscoveryClient, ContextReadClient, InboxClient, 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
|
-
import { formatInboxToolResult } from './inboxToolResult.js';
|
|
6
|
+
import { formatInboxToolResult, buildReadContextReadPlan, } from './inboxToolResult.js';
|
|
7
7
|
import { formatPendingDecisionsPayload, resolveWebAppOrigin, buildPendingNextActions, } from './pendingDecisions.js';
|
|
8
8
|
import { PROTOCOL } from './protocol/delegateProtocol.js';
|
|
9
9
|
import { READ_ONLY, WRITE } from './toolAnnotations.js';
|
|
@@ -13,7 +13,9 @@ import { READ_ONLY, WRITE } from './toolAnnotations.js';
|
|
|
13
13
|
const ZIGGS_INBOX_DESCRIPTION = "What's new since your last ack — references only, never content: scopes with new-message/artifact counts, plus agreement proposals awaiting your response. " +
|
|
14
14
|
'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_read_context (type=messages, via=chat:<chatId>). ' +
|
|
15
15
|
`${PROTOCOL.humanAttention} ${PROTOCOL.pendingDecisions} ` +
|
|
16
|
-
'When hasActionable the response includes sessionChatCard (decisions + active tasks), decisionChatCard,
|
|
16
|
+
'When hasActionable the response includes sessionChatCard (decisions + active tasks), decisionChatCard, and workChatCard. ' +
|
|
17
|
+
'A `readPlan` array gives the exact next calls (tool + pre-filled args) for the news in this response — run them verbatim to read each scope and ack (ZIG-634). ' +
|
|
18
|
+
'Each scope also carries the covering `grant` (grantId, temporal, watermarkAt, expiresAt) so you can pin the right contextGrantId on the read without a separate discover_context call (ZIG-635). ' +
|
|
17
19
|
`${PROTOCOL.loop} ${PROTOCOL.ack}`;
|
|
18
20
|
const ZIGGS_PENDING_DECISIONS_DESCRIPTION = 'Session start summary: approve/reject decisions AND active tasks assigned to your delegate (ZIG-625). ' +
|
|
19
21
|
'Call at session start in Cursor/Claude — pull-only MCP has no notification tray. ' +
|
|
@@ -441,7 +443,16 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
441
443
|
catch {
|
|
442
444
|
// omit work card when tasks fail
|
|
443
445
|
}
|
|
444
|
-
|
|
446
|
+
// ZIG-635: tag each scope with the covering grant from the caller's own
|
|
447
|
+
// live reach descriptors. Best-effort — the inbox still works untagged.
|
|
448
|
+
let reach = [];
|
|
449
|
+
try {
|
|
450
|
+
reach = await new ContextDiscoveryClient(creds.operatorKey, creds.agentId).discover();
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
// omit grant tags when discovery fails
|
|
454
|
+
}
|
|
455
|
+
return textResult(formatInboxToolResult(inbox, acked, cfg.ZIGGS_WEB_URL, activeTasks, reach));
|
|
445
456
|
}
|
|
446
457
|
catch (e) {
|
|
447
458
|
return toolError(e.message);
|
|
@@ -457,7 +468,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
457
468
|
return toolError(e.message);
|
|
458
469
|
}
|
|
459
470
|
});
|
|
460
|
-
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. 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.', {
|
|
471
|
+
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 (ZIG-634). 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.', {
|
|
461
472
|
type: contextReadTypeSchema.describe('Resource type to read'),
|
|
462
473
|
via: z
|
|
463
474
|
.string()
|
|
@@ -489,7 +500,8 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
489
500
|
state,
|
|
490
501
|
contextGrantId,
|
|
491
502
|
});
|
|
492
|
-
|
|
503
|
+
const readPlan = buildReadContextReadPlan(result, type, via, contextGrantId);
|
|
504
|
+
return textResult(readPlan.length ? { ...result, readPlan } : result);
|
|
493
505
|
}
|
|
494
506
|
catch (e) {
|
|
495
507
|
return toolError(e.message);
|
package/dist/trustTools.js
CHANGED
|
@@ -18,8 +18,8 @@ const DEFAULT_WEB_URL = 'https://ziggsai.com';
|
|
|
18
18
|
/** ZIG-433 — agent search + context grant management through MCP. */
|
|
19
19
|
export function registerTrustTools(server, creds, cfg) {
|
|
20
20
|
const webUrl = cfg?.ZIGGS_WEB_URL?.replace(/\/$/, '') ?? DEFAULT_WEB_URL;
|
|
21
|
-
server.tool('ziggs_search_agents', 'Find agents (AgentSearchClient). A keyword/natural-language query searches published store
|
|
22
|
-
query: z.string().describe('Keyword/natural-language search (published store
|
|
21
|
+
server.tool('ziggs_search_agents', 'Find agents (AgentSearchClient). A keyword/natural-language query searches the published store AND, scoped to your authority, your own org-mates and any delegate you have an active link with — so you can find a teammate or another user\'s delegate by name and ziggs_open_conversation with it directly, even if it is unpublished/offline and has never been in a chat with you (ZIG-578). Passing an EXACT agent id resolves that one agent even if unpublished/private — use this for a delegate someone shared an id for, then ziggs_request_link if not yet linked (ZIG-480). Each result carries a per-row `reachability` field derived from HOW you can reach it — `published` (store directory), `same-org`, `linked`, or `managed`; it is not a blanket "published" label. If an exact-id lookup matches an unpublished agent you cannot reach, the row is `reachability: "restricted"` and returns the id only with no name/profile (ZIG-638). Use returned agentId in grant/issue tools — do not guess ids.', {
|
|
22
|
+
query: z.string().describe('Keyword/natural-language search (published store + your org-mates + your linked delegates) OR an exact agent id (resolves that agent even if unpublished, when you can reach it)'),
|
|
23
23
|
limit: z.number().optional().describe('Max results (default server-side)'),
|
|
24
24
|
minScore: z.number().optional().describe('Minimum match score filter'),
|
|
25
25
|
}, READ_ONLY, async ({ query, limit, minScore }) => {
|