@ziggs-ai/ziggs-mcp 0.1.24 → 0.1.25
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/pendingDecisions.d.ts +14 -5
- package/dist/pendingDecisions.js +90 -19
- package/dist/tools.js +87 -2
- package/package.json +2 -2
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import type { InboxEnvelope, Task } from '@ziggs-ai/api-client';
|
|
2
|
-
export type PendingDecisionKind = 'proposal' | 'link_request';
|
|
2
|
+
export type PendingDecisionKind = 'proposal' | 'link_request' | 'mcp_server_request';
|
|
3
3
|
export interface PendingDecisionItem {
|
|
4
4
|
kind: PendingDecisionKind;
|
|
5
|
+
/** Agreement id for proposals; requestId for link / MCP server requests. */
|
|
5
6
|
agreementId: string;
|
|
6
7
|
title: string;
|
|
7
8
|
subtitle: string | null;
|
|
8
9
|
proposedAt: string | null;
|
|
9
10
|
proposedAtLabel: string | null;
|
|
10
11
|
appUrl: string;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Null for mcp_server_request — connecting a server (OAuth) happens in the
|
|
14
|
+
* browser at /app/settings/connections; there is no MCP respond tool.
|
|
15
|
+
*/
|
|
16
|
+
respondApprove: string | null;
|
|
17
|
+
respondReject: string | null;
|
|
18
|
+
sayApprove: string | null;
|
|
19
|
+
sayReject: string | null;
|
|
15
20
|
}
|
|
16
21
|
export interface ActiveWorkItem {
|
|
17
22
|
taskId: string;
|
|
@@ -26,11 +31,14 @@ export interface ActiveWorkItem {
|
|
|
26
31
|
export declare function resolveWebAppOrigin(webUrl?: string | null): string;
|
|
27
32
|
export declare function agreementAppUrl(origin: string, agreementId: string): string;
|
|
28
33
|
export declare function agreementsListAppUrl(origin: string): string;
|
|
34
|
+
/** Where the human connects MCP servers and grants tools (ZIG-686). */
|
|
35
|
+
export declare function connectionsSettingsAppUrl(origin: string): string;
|
|
29
36
|
export declare function buildPendingDecisionItems(inbox: InboxEnvelope, webOrigin: string): PendingDecisionItem[];
|
|
30
37
|
export declare function buildActiveWorkItems(tasks: Task[], webOrigin: string): ActiveWorkItem[];
|
|
31
38
|
export declare function buildDecisionChatCard(items: PendingDecisionItem[], opts: {
|
|
32
39
|
truncatedProposals?: number;
|
|
33
40
|
truncatedConnectionRequests?: number;
|
|
41
|
+
truncatedMcpServerRequests?: number;
|
|
34
42
|
agreementsListAppUrl?: string;
|
|
35
43
|
}): string;
|
|
36
44
|
export declare function buildWorkChatCard(work: ActiveWorkItem[], listUrl?: string): string;
|
|
@@ -38,6 +46,7 @@ export declare function buildWorkChatCard(work: ActiveWorkItem[], listUrl?: stri
|
|
|
38
46
|
export declare function buildSessionChatCard(decisions: PendingDecisionItem[], work: ActiveWorkItem[], opts: {
|
|
39
47
|
truncatedProposals?: number;
|
|
40
48
|
truncatedConnectionRequests?: number;
|
|
49
|
+
truncatedMcpServerRequests?: number;
|
|
41
50
|
agreementsListAppUrl?: string;
|
|
42
51
|
}): string;
|
|
43
52
|
/** Structured session payload for MCP tools (ZIG-625 + active work). */
|
package/dist/pendingDecisions.js
CHANGED
|
@@ -9,6 +9,10 @@ export function agreementAppUrl(origin, agreementId) {
|
|
|
9
9
|
export function agreementsListAppUrl(origin) {
|
|
10
10
|
return `${origin}/app/agreements`;
|
|
11
11
|
}
|
|
12
|
+
/** Where the human connects MCP servers and grants tools (ZIG-686). */
|
|
13
|
+
export function connectionsSettingsAppUrl(origin) {
|
|
14
|
+
return `${origin}/app/settings/connections`;
|
|
15
|
+
}
|
|
12
16
|
function truncateText(text, max = TITLE_MAX) {
|
|
13
17
|
const oneLine = text.replace(/\s+/g, ' ').trim();
|
|
14
18
|
if (oneLine.length <= max)
|
|
@@ -75,6 +79,23 @@ function linkToItem(c, origin) {
|
|
|
75
79
|
sayReject: `reject link ${id}`,
|
|
76
80
|
};
|
|
77
81
|
}
|
|
82
|
+
function mcpServerRequestToItem(r, origin) {
|
|
83
|
+
const tools = r.tools?.length ? r.tools.join(', ') : 'no tools listed';
|
|
84
|
+
const reason = r.reason?.trim() || null;
|
|
85
|
+
return {
|
|
86
|
+
kind: 'mcp_server_request',
|
|
87
|
+
agreementId: r.requestId,
|
|
88
|
+
title: truncateText(`Connect MCP server · ${r.serverUrl}`),
|
|
89
|
+
subtitle: truncateText(reason ? `${reason} — tools: ${tools}` : `Tools: ${tools}`, 160),
|
|
90
|
+
proposedAt: r.requestedAt,
|
|
91
|
+
proposedAtLabel: formatWhen(r.requestedAt),
|
|
92
|
+
appUrl: connectionsSettingsAppUrl(origin),
|
|
93
|
+
respondApprove: null,
|
|
94
|
+
respondReject: null,
|
|
95
|
+
sayApprove: null,
|
|
96
|
+
sayReject: null,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
78
99
|
export function buildPendingDecisionItems(inbox, webOrigin) {
|
|
79
100
|
const items = [];
|
|
80
101
|
for (const p of inbox.proposalsAwaitingMe ?? []) {
|
|
@@ -83,6 +104,9 @@ export function buildPendingDecisionItems(inbox, webOrigin) {
|
|
|
83
104
|
for (const c of inbox.connectionRequestsAwaitingMe ?? []) {
|
|
84
105
|
items.push(linkToItem(c, webOrigin));
|
|
85
106
|
}
|
|
107
|
+
for (const r of inbox.mcpServerRequestsAwaitingMe ?? []) {
|
|
108
|
+
items.push(mcpServerRequestToItem(r, webOrigin));
|
|
109
|
+
}
|
|
86
110
|
return items;
|
|
87
111
|
}
|
|
88
112
|
export function buildActiveWorkItems(tasks, webOrigin) {
|
|
@@ -105,17 +129,26 @@ export function buildActiveWorkItems(tasks, webOrigin) {
|
|
|
105
129
|
});
|
|
106
130
|
}
|
|
107
131
|
function kindLabel(kind) {
|
|
108
|
-
|
|
132
|
+
if (kind === 'proposal')
|
|
133
|
+
return 'Agreement proposal';
|
|
134
|
+
if (kind === 'link_request')
|
|
135
|
+
return 'Agent link request';
|
|
136
|
+
return 'MCP server request';
|
|
109
137
|
}
|
|
110
138
|
function kindHint(kind) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
if (kind === 'proposal') {
|
|
140
|
+
return 'Someone proposed work or terms — your approval opens or rejects it.';
|
|
141
|
+
}
|
|
142
|
+
if (kind === 'link_request') {
|
|
143
|
+
return 'Another agent wants to link — your approval enables cross-org reach.';
|
|
144
|
+
}
|
|
145
|
+
return 'Your agent asks you to connect an MCP server and grant it the listed tools — connect (OAuth) or reject in the browser.';
|
|
114
146
|
}
|
|
115
147
|
function buildDecisionSection(items, opts) {
|
|
116
148
|
const truncatedProposals = opts.truncatedProposals ?? 0;
|
|
117
149
|
const truncatedConnectionRequests = opts.truncatedConnectionRequests ?? 0;
|
|
118
|
-
const
|
|
150
|
+
const truncatedMcpServerRequests = opts.truncatedMcpServerRequests ?? 0;
|
|
151
|
+
const truncated = truncatedProposals + truncatedConnectionRequests + truncatedMcpServerRequests;
|
|
119
152
|
if (!items.length && truncated === 0)
|
|
120
153
|
return [];
|
|
121
154
|
const lines = [];
|
|
@@ -136,18 +169,27 @@ function buildDecisionSection(items, opts) {
|
|
|
136
169
|
lines.push(`> ${item.subtitle}`);
|
|
137
170
|
}
|
|
138
171
|
lines.push('');
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
172
|
+
if (item.sayApprove && item.respondApprove && item.sayReject && item.respondReject) {
|
|
173
|
+
lines.push(`[Review in Ziggs →](${item.appUrl})`);
|
|
174
|
+
lines.push('');
|
|
175
|
+
lines.push('| You say in chat | What the agent runs |');
|
|
176
|
+
lines.push('|:----------------|:--------------------|');
|
|
177
|
+
lines.push(`| \`${item.sayApprove}\` | \`${item.respondApprove}\` |`);
|
|
178
|
+
lines.push(`| \`${item.sayReject}\` | \`${item.respondReject}\` |`);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// Browser-only decision (mcp_server_request): connecting a server runs
|
|
182
|
+
// OAuth consent — no MCP tool can approve it from chat.
|
|
183
|
+
lines.push(`[Connect or reject in Ziggs →](${item.appUrl})`);
|
|
184
|
+
lines.push('');
|
|
185
|
+
lines.push('_This one is decided in the browser — nothing to approve from chat._');
|
|
186
|
+
}
|
|
145
187
|
lines.push('');
|
|
146
188
|
}
|
|
147
189
|
if (truncated > 0) {
|
|
148
190
|
lines.push('---');
|
|
149
191
|
lines.push('');
|
|
150
|
-
lines.push(`_+${truncated} more pending (${truncatedProposals} proposal(s), ${truncatedConnectionRequests} link(s)) — not listed here._`);
|
|
192
|
+
lines.push(`_+${truncated} more pending (${truncatedProposals} proposal(s), ${truncatedConnectionRequests} link(s), ${truncatedMcpServerRequests} MCP server request(s)) — not listed here._`);
|
|
151
193
|
lines.push('');
|
|
152
194
|
}
|
|
153
195
|
return lines;
|
|
@@ -188,11 +230,13 @@ function buildWorkSection(work, startIndex = 1) {
|
|
|
188
230
|
export function buildDecisionChatCard(items, opts) {
|
|
189
231
|
const truncatedProposals = opts.truncatedProposals ?? 0;
|
|
190
232
|
const truncatedConnectionRequests = opts.truncatedConnectionRequests ?? 0;
|
|
191
|
-
const
|
|
233
|
+
const truncatedMcpServerRequests = opts.truncatedMcpServerRequests ?? 0;
|
|
234
|
+
const truncated = truncatedProposals + truncatedConnectionRequests + truncatedMcpServerRequests;
|
|
192
235
|
if (!items.length && truncated === 0)
|
|
193
236
|
return '';
|
|
194
237
|
const proposals = items.filter((i) => i.kind === 'proposal').length;
|
|
195
238
|
const links = items.filter((i) => i.kind === 'link_request').length;
|
|
239
|
+
const mcpRequests = items.filter((i) => i.kind === 'mcp_server_request').length;
|
|
196
240
|
const listedTotal = items.length + truncated;
|
|
197
241
|
const lines = [
|
|
198
242
|
`### 🔔 Ziggs — **${listedTotal}** ${listedTotal === 1 ? 'item needs' : 'items need'} your decision`,
|
|
@@ -201,9 +245,12 @@ export function buildDecisionChatCard(items, opts) {
|
|
|
201
245
|
'|:--|--:|',
|
|
202
246
|
`| Agreement proposals | **${proposals}** |`,
|
|
203
247
|
`| Agent link requests | **${links}** |`,
|
|
248
|
+
...(mcpRequests + truncatedMcpServerRequests > 0
|
|
249
|
+
? [`| MCP server requests | **${mcpRequests}** |`]
|
|
250
|
+
: []),
|
|
204
251
|
...(truncated > 0 ? [`| _Not shown (inbox cap)_ | _+${truncated}_ |`] : []),
|
|
205
252
|
'',
|
|
206
|
-
'> **Heads-up:** Ziggs MCP is pull-only — nothing pops up in Cursor until inbox is checked. **You** approve or reject in this chat; the agent calls `ziggs_respond_to_agreement` only after you say so.',
|
|
253
|
+
'> **Heads-up:** Ziggs MCP is pull-only — nothing pops up in Cursor until inbox is checked. **You** approve or reject in this chat; the agent calls `ziggs_respond_to_agreement` only after you say so. MCP server requests are the exception — those are connected or rejected in the browser.',
|
|
207
254
|
'',
|
|
208
255
|
...buildDecisionSection(items, opts),
|
|
209
256
|
];
|
|
@@ -232,12 +279,15 @@ export function buildWorkChatCard(work, listUrl) {
|
|
|
232
279
|
export function buildSessionChatCard(decisions, work, opts) {
|
|
233
280
|
const truncatedProposals = opts.truncatedProposals ?? 0;
|
|
234
281
|
const truncatedConnectionRequests = opts.truncatedConnectionRequests ?? 0;
|
|
235
|
-
const
|
|
282
|
+
const truncatedMcpServerRequests = opts.truncatedMcpServerRequests ?? 0;
|
|
283
|
+
const truncated = truncatedProposals + truncatedConnectionRequests + truncatedMcpServerRequests;
|
|
236
284
|
const decisionListed = decisions.length + truncated;
|
|
237
285
|
const workCount = work.length;
|
|
238
286
|
const total = decisionListed + workCount;
|
|
239
287
|
if (total === 0)
|
|
240
288
|
return '';
|
|
289
|
+
const mcpRequestCount = decisions.filter((d) => d.kind === 'mcp_server_request').length +
|
|
290
|
+
truncatedMcpServerRequests;
|
|
241
291
|
const lines = [
|
|
242
292
|
`### 🔔 Ziggs — **${total}** ${total === 1 ? 'thing needs' : 'things need'} you`,
|
|
243
293
|
'',
|
|
@@ -248,11 +298,16 @@ export function buildSessionChatCard(decisions, work, opts) {
|
|
|
248
298
|
`| Approve / reject | **${decisionListed}** |`,
|
|
249
299
|
`| — proposals | ${decisions.filter((d) => d.kind === 'proposal').length}${truncatedProposals ? ` (+${truncatedProposals} hidden)` : ''} |`,
|
|
250
300
|
`| — link requests | ${decisions.filter((d) => d.kind === 'link_request').length}${truncatedConnectionRequests ? ` (+${truncatedConnectionRequests} hidden)` : ''} |`,
|
|
301
|
+
...(mcpRequestCount > 0
|
|
302
|
+
? [
|
|
303
|
+
`| — MCP server requests | ${decisions.filter((d) => d.kind === 'mcp_server_request').length}${truncatedMcpServerRequests ? ` (+${truncatedMcpServerRequests} hidden)` : ''} |`,
|
|
304
|
+
]
|
|
305
|
+
: []),
|
|
251
306
|
]
|
|
252
307
|
: []),
|
|
253
308
|
...(workCount > 0 ? [`| Active tasks (your work) | **${workCount}** |`] : []),
|
|
254
309
|
'',
|
|
255
|
-
'> **Heads-up:** MCP is pull-only — check at session start. **Decisions:** you say approve/reject. **Tasks:** say `work on <taskId>` — the agent implements and reports on Ziggs.',
|
|
310
|
+
'> **Heads-up:** MCP is pull-only — check at session start. **Decisions:** you say approve/reject (MCP server requests are connected in the browser instead). **Tasks:** say `work on <taskId>` — the agent implements and reports on Ziggs.',
|
|
256
311
|
'',
|
|
257
312
|
];
|
|
258
313
|
let sectionIndex = 1;
|
|
@@ -275,13 +330,18 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
|
|
|
275
330
|
const activeWork = buildActiveWorkItems(opts?.activeTasks ?? [], webOrigin);
|
|
276
331
|
const truncatedProposals = inbox.truncatedProposals ?? 0;
|
|
277
332
|
const truncatedConnectionRequests = inbox.truncatedConnectionRequests ?? 0;
|
|
278
|
-
const
|
|
333
|
+
const truncatedMcpServerRequests = inbox.truncatedMcpServerRequests ?? 0;
|
|
334
|
+
const pendingCount = decisions.length +
|
|
335
|
+
truncatedProposals +
|
|
336
|
+
truncatedConnectionRequests +
|
|
337
|
+
truncatedMcpServerRequests;
|
|
279
338
|
const activeWorkCount = activeWork.length;
|
|
280
339
|
const actionCount = pendingCount + activeWorkCount;
|
|
281
340
|
const listUrl = agreementsListAppUrl(webOrigin);
|
|
282
341
|
const cardOpts = {
|
|
283
342
|
truncatedProposals,
|
|
284
343
|
truncatedConnectionRequests,
|
|
344
|
+
truncatedMcpServerRequests,
|
|
285
345
|
agreementsListAppUrl: listUrl,
|
|
286
346
|
};
|
|
287
347
|
const decisionChatCard = buildDecisionChatCard(decisions, cardOpts);
|
|
@@ -289,6 +349,7 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
|
|
|
289
349
|
const sessionChatCard = buildSessionChatCard(decisions, activeWork, cardOpts);
|
|
290
350
|
const proposalCount = decisions.filter((d) => d.kind === 'proposal').length;
|
|
291
351
|
const linkCount = decisions.filter((d) => d.kind === 'link_request').length;
|
|
352
|
+
const mcpServerRequestCount = decisions.filter((d) => d.kind === 'mcp_server_request').length;
|
|
292
353
|
const instruction = actionCount > 0
|
|
293
354
|
? 'Paste sessionChatCard at the top of your reply. Decisions: wait for explicit approve/reject before ziggs_respond_to_agreement. Tasks: when the human says work on <taskId>, read context and implement.'
|
|
294
355
|
: 'No pending decisions or active tasks — continue with ziggs_inbox for scope news.';
|
|
@@ -302,15 +363,20 @@ export function formatPendingDecisionsPayload(inbox, webOrigin, opts) {
|
|
|
302
363
|
summary: {
|
|
303
364
|
proposals: proposalCount + truncatedProposals,
|
|
304
365
|
linkRequests: linkCount + truncatedConnectionRequests,
|
|
366
|
+
mcpServerRequests: mcpServerRequestCount + truncatedMcpServerRequests,
|
|
305
367
|
activeTasks: activeWorkCount,
|
|
306
368
|
listed: decisions.length,
|
|
307
|
-
truncated: truncatedProposals +
|
|
369
|
+
truncated: truncatedProposals +
|
|
370
|
+
truncatedConnectionRequests +
|
|
371
|
+
truncatedMcpServerRequests,
|
|
308
372
|
},
|
|
309
373
|
decisions,
|
|
310
374
|
activeWork,
|
|
311
375
|
truncatedProposals,
|
|
312
376
|
truncatedConnectionRequests,
|
|
377
|
+
truncatedMcpServerRequests,
|
|
313
378
|
agreementsListAppUrl: listUrl,
|
|
379
|
+
connectionsSettingsAppUrl: connectionsSettingsAppUrl(webOrigin),
|
|
314
380
|
...(decisionChatCard ? { decisionChatCard } : {}),
|
|
315
381
|
...(workChatCard ? { workChatCard } : {}),
|
|
316
382
|
...(sessionChatCard ? { sessionChatCard } : {}),
|
|
@@ -327,7 +393,12 @@ export function buildPendingNextActions(decisions, work = []) {
|
|
|
327
393
|
actions.push('Wait for the human to say approve/reject (e.g. `approve agr-…`) — do not auto-respond.');
|
|
328
394
|
}
|
|
329
395
|
for (const d of decisions.slice(0, 4)) {
|
|
330
|
-
|
|
396
|
+
if (d.sayApprove && d.sayReject) {
|
|
397
|
+
actions.push(`${kindLabel(d.kind)} ${d.agreementId}: \`${d.sayApprove}\` or \`${d.sayReject}\``);
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
actions.push(`${kindLabel(d.kind)} ${d.agreementId}: connect or reject in the browser → ${d.appUrl}`);
|
|
401
|
+
}
|
|
331
402
|
}
|
|
332
403
|
for (const w of work.slice(0, 4)) {
|
|
333
404
|
actions.push(`Active task ${w.taskId}: human says \`${w.sayWork}\` to start implementation.`);
|
package/dist/tools.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getAgreement, getMyAgreements, listMyChats, openConversation, proposeDi
|
|
|
4
4
|
import { decodeOperatorKeyClaims } from './operatorKey.js';
|
|
5
5
|
import { registerTrustTools } from './trustTools.js';
|
|
6
6
|
import { formatInboxToolResult, buildReadContextReadPlan, } from './inboxToolResult.js';
|
|
7
|
-
import { formatPendingDecisionsPayload, resolveWebAppOrigin, buildPendingNextActions, } from './pendingDecisions.js';
|
|
7
|
+
import { connectionsSettingsAppUrl, formatPendingDecisionsPayload, resolveWebAppOrigin, buildPendingNextActions, } from './pendingDecisions.js';
|
|
8
8
|
import { PROTOCOL } from './protocol/delegateProtocol.js';
|
|
9
9
|
import { READ_ONLY, WRITE, DESTRUCTIVE } from './toolAnnotations.js';
|
|
10
10
|
// ZIG-557: the protocol sentences (loop / ack / humanAttention) are sourced
|
|
@@ -205,6 +205,50 @@ async function listConnectionsForHolder(creds) {
|
|
|
205
205
|
}
|
|
206
206
|
return parsed?.['connections'] ?? [];
|
|
207
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* ZIG-686 — agent-initiated MCP connection request: ask the principal to
|
|
210
|
+
* connect a remote MCP server and grant this agent the listed tools. Creates a
|
|
211
|
+
* pending decision the human resolves in the browser (OAuth consent or a grant
|
|
212
|
+
* from an existing connection) — never from chat.
|
|
213
|
+
*/
|
|
214
|
+
async function createMcpConnectionRequest(creds, input) {
|
|
215
|
+
const url = `${getBackendUrl()}/connections/mcp/requests`;
|
|
216
|
+
const res = await fetch(url, {
|
|
217
|
+
method: 'POST',
|
|
218
|
+
headers: {
|
|
219
|
+
'content-type': 'application/json',
|
|
220
|
+
Authorization: `Bearer ${creds.operatorKey}`,
|
|
221
|
+
'X-Agent-Id': creds.agentId,
|
|
222
|
+
},
|
|
223
|
+
body: JSON.stringify({
|
|
224
|
+
serverUrl: input.serverUrl,
|
|
225
|
+
tools: input.tools,
|
|
226
|
+
reason: input.reason,
|
|
227
|
+
}),
|
|
228
|
+
});
|
|
229
|
+
const body = await res.text().catch(() => '');
|
|
230
|
+
if (!res.ok) {
|
|
231
|
+
throw new Error(`POST /connections/mcp/requests ${res.status} ${body.slice(0, 200)}`);
|
|
232
|
+
}
|
|
233
|
+
return body ? JSON.parse(body) : {};
|
|
234
|
+
}
|
|
235
|
+
/** ZIG-686 — the requests this agent made, each with status / connectionId / grantId. */
|
|
236
|
+
async function listMcpConnectionRequests(creds) {
|
|
237
|
+
const url = `${getBackendUrl()}/connections/mcp/requests`;
|
|
238
|
+
const res = await fetch(url, {
|
|
239
|
+
method: 'GET',
|
|
240
|
+
headers: {
|
|
241
|
+
Authorization: `Bearer ${creds.operatorKey}`,
|
|
242
|
+
'X-Agent-Id': creds.agentId,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
const body = await res.text().catch(() => '');
|
|
246
|
+
if (!res.ok) {
|
|
247
|
+
throw new Error(`GET /connections/mcp/requests ${res.status} ${body.slice(0, 200)}`);
|
|
248
|
+
}
|
|
249
|
+
const parsed = body ? JSON.parse(body) : null;
|
|
250
|
+
return parsed?.['requests'] ?? [];
|
|
251
|
+
}
|
|
208
252
|
async function loadSessionActionsPayload(creds, cfg) {
|
|
209
253
|
const webOrigin = resolveWebAppOrigin(cfg.ZIGGS_WEB_URL);
|
|
210
254
|
const client = new InboxClient(creds.operatorKey, creds.agentId);
|
|
@@ -287,7 +331,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
287
331
|
: 'Next: ziggs_inbox or ziggs_pending_decisions at session start.',
|
|
288
332
|
});
|
|
289
333
|
});
|
|
290
|
-
server.tool('ziggs_switch_org', '
|
|
334
|
+
server.tool('ziggs_switch_org', 'Switch which org this MCP OAuth session acts in without reconnecting. Existing Bearer unchanged; runtime org flips server-side. Requires confirm=true (party-identity change). Target org must be one you belong to. Call ziggs_auth_status after to verify actingOrgId.', {
|
|
291
335
|
orgId: z.string().describe('Organization id to act in'),
|
|
292
336
|
confirm: z
|
|
293
337
|
.literal(true)
|
|
@@ -796,5 +840,46 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
796
840
|
return toolError(e.message);
|
|
797
841
|
}
|
|
798
842
|
});
|
|
843
|
+
server.tool('ziggs_request_connection', 'Ask your principal (the human) to connect a remote MCP server and grant you the listed tools. ' +
|
|
844
|
+
'Creates a pending decision — the human connects (OAuth) or rejects it in the browser under Settings → Connections; there is no MCP tool to approve it, so tell them and link the returned approveUrl. ' +
|
|
845
|
+
'Check the outcome with ziggs_request_connection_status: a fulfilled request carries the connectionId + grantId to use with ziggs_connection_proxy.', {
|
|
846
|
+
serverUrl: z.string().describe('Remote MCP server URL (https)'),
|
|
847
|
+
tools: z
|
|
848
|
+
.array(z.string())
|
|
849
|
+
.describe("Tool names you want — become the grant's allowed_actions caveats"),
|
|
850
|
+
reason: z
|
|
851
|
+
.string()
|
|
852
|
+
.optional()
|
|
853
|
+
.describe('Plain-language reason shown to the human deciding'),
|
|
854
|
+
}, WRITE, async ({ serverUrl, tools, reason }) => {
|
|
855
|
+
try {
|
|
856
|
+
const result = await createMcpConnectionRequest(creds, {
|
|
857
|
+
serverUrl,
|
|
858
|
+
tools,
|
|
859
|
+
reason,
|
|
860
|
+
});
|
|
861
|
+
const approveUrl = connectionsSettingsAppUrl(resolveWebAppOrigin(cfg.ZIGGS_WEB_URL));
|
|
862
|
+
return textResult({
|
|
863
|
+
ok: true,
|
|
864
|
+
...result,
|
|
865
|
+
approveUrl,
|
|
866
|
+
note: 'Pending your principal\'s decision. Tell the human now (pull-only MCP has no push) and link the approveUrl — they connect or reject there. ' +
|
|
867
|
+
'Poll ziggs_request_connection_status for the outcome; fulfilled requests carry connectionId + grantId for ziggs_connection_proxy.',
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
catch (e) {
|
|
871
|
+
return toolError(e.message);
|
|
872
|
+
}
|
|
873
|
+
});
|
|
874
|
+
server.tool('ziggs_request_connection_status', 'List the MCP server connection requests this agent made with ziggs_request_connection, each with status pending | fulfilled | rejected. ' +
|
|
875
|
+
'A fulfilled request carries the connectionId + grantId to feed into ziggs_connection_proxy (it also appears in ziggs_list_my_connections).', {}, READ_ONLY, async () => {
|
|
876
|
+
try {
|
|
877
|
+
const requests = await listMcpConnectionRequests(creds);
|
|
878
|
+
return textResult({ requests });
|
|
879
|
+
}
|
|
880
|
+
catch (e) {
|
|
881
|
+
return toolError(e.message);
|
|
882
|
+
}
|
|
883
|
+
});
|
|
799
884
|
registerTrustTools(server, creds, cfg);
|
|
800
885
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/ziggs-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.25",
|
|
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": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
-
"@ziggs-ai/api-client": "^0.1.
|
|
39
|
+
"@ziggs-ai/api-client": "^0.1.20",
|
|
40
40
|
"dotenv": "^16.6.1",
|
|
41
41
|
"zod": "^3.24.2"
|
|
42
42
|
},
|