@ziggs-ai/ziggs-mcp 0.13.0 → 0.14.1
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 +5 -2
- package/dist/protocol/delegateProtocol.d.ts +1 -1
- package/dist/protocol/delegateProtocol.js +1 -1
- package/dist/tools.js +82 -34
- package/dist/trustTools.js +4 -1
- package/examples/chatgpt-remote-oauth.md +45 -17
- package/package.json +2 -2
- package/skills/ziggs/.cursorrules +1 -1
- package/skills/ziggs/SKILL.md +1 -1
- package/skills/ziggs/references/inbox-rhythm.md +1 -1
- package/skills/ziggs/references/reporting-convention.md +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ This package ships [`server.json`](./server.json) for [registry.modelcontextprot
|
|
|
63
63
|
|
|
64
64
|
### ChatGPT
|
|
65
65
|
|
|
66
|
-
**Works.**
|
|
66
|
+
**Works, and what it can do depends on your plan.** Custom MCP against the same remote URL, OAuth consent, no key to paste. Business / Enterprise / Edu get the write tools (beta); Pro gets read/fetch only in developer mode, and Plus is not stated consistently by OpenAI, so treat it as read-only. Web only — no mobile. Setup guide: [`examples/chatgpt-remote-oauth.md`](examples/chatgpt-remote-oauth.md). Ziggs half verified 2026-08-13; plan gating and the moved settings path checked 2026-08-26.
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|
|
@@ -78,7 +78,7 @@ The list below is the clients someone has taken through a connect end to end. A
|
|
|
78
78
|
| claude.ai | remote OAuth | yes |
|
|
79
79
|
| Claude Code | remote OAuth, and stdio for headless/CI | yes |
|
|
80
80
|
| Cursor | remote OAuth, and stdio | yes |
|
|
81
|
-
| ChatGPT | remote OAuth (developer
|
|
81
|
+
| ChatGPT | remote OAuth (custom MCP app, developer mode) | yes, 2026-08-13 — writes need a Business/Enterprise/Edu plan |
|
|
82
82
|
| Gemini | remote OAuth expected | **not verified** — no walkthrough yet |
|
|
83
83
|
|
|
84
84
|
Gemini is deliberately listed without a walkthrough. The platform already labels a Gemini delegate, so nothing is known to block it, but nobody has connected one end to end and an untested walkthrough is worse than none.
|
|
@@ -157,6 +157,9 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
|
|
|
157
157
|
| `ziggs_grant_list` | `GET /grants` (all rails) |
|
|
158
158
|
| `ziggs_context_read` | `GET /context/read/:type` |
|
|
159
159
|
| `ziggs_artifact_record` | `POST /artifacts` |
|
|
160
|
+
| `ziggs_artifact_list` | `GET /artifacts` |
|
|
161
|
+
| `ziggs_artifact_share` | `POST /context/artifacts/:id/share` |
|
|
162
|
+
| `ziggs_artifact_attach` | `POST /chats/:id/artifacts`, `POST /tasks/:id/artifacts`, or `POST /agreements/:id/artifacts` |
|
|
160
163
|
| `ziggs_artifact_upload_url` | `POST /artifacts/upload-url` (+ optional S3 PUT) |
|
|
161
164
|
| `ziggs_artifact_complete_file` | `POST /artifacts/:id/complete` |
|
|
162
165
|
| `ziggs_artifact_download` | `GET /artifacts/:id/download` |
|
|
@@ -33,7 +33,7 @@ export declare const PROTOCOL: {
|
|
|
33
33
|
readonly ack: "Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` back VERBATIM as ack (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours.";
|
|
34
34
|
readonly neverRewind: "Never rewind an ack to an older value.";
|
|
35
35
|
/** Tasks are the unit of work. */
|
|
36
|
-
readonly task: "Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress
|
|
36
|
+
readonly task: "Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).";
|
|
37
37
|
/** posted-first: how ANY engagement starts. */
|
|
38
38
|
readonly engage: "Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a request (ziggs_agreement_request) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_buy when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.";
|
|
39
39
|
/**
|
|
@@ -33,7 +33,7 @@ export const PROTOCOL = {
|
|
|
33
33
|
ack: 'Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` back VERBATIM as ack (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours.',
|
|
34
34
|
neverRewind: 'Never rewind an ack to an older value.',
|
|
35
35
|
/** Tasks are the unit of work. */
|
|
36
|
-
task: 'Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress
|
|
36
|
+
task: 'Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).',
|
|
37
37
|
/** posted-first: how ANY engagement starts. */
|
|
38
38
|
engage: 'Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a request (ziggs_agreement_request) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_buy when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.',
|
|
39
39
|
/**
|
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, delegateAgreement, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, PaymentsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, replaceTaskPlan, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchSessionAccess, isMcpOAuthDelegateAgentId, GRANTS_CAPABILITIES, AGREEMENT_VERB_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, marketplaceViewCapability, } from '@ziggs-ai/api-client';
|
|
3
|
+
import { getAgreement, getMyAgreements, listMyChats, delegateAgreement, respondToAgreement, revokeAgreement, counterAgreement, fulfillAgreement, sendChatMessage, ConnectionsClient, PaymentsClient, ContextReadClient, GrantsClient, InboxClient, createTask, updateTaskState, replaceTaskPlan, updateTaskPlanSteps, listTasks, getTask, getBackendUrl, fetchMyOrgs, fetchSessionAccess, isMcpOAuthDelegateAgentId, GRANTS_CAPABILITIES, AGREEMENT_VERB_CAPABILITIES, CONTEXT_GRANT_SCOPE_KINDS, contextReadCapability, contextExpandReachCapability, contextDiscoverGrantableCapability, recordArtifactCapability, listArtifactsCapability, shareArtifactCapability, attachArtifactCapability, uploadArtifactUrlCapability, completeArtifactFileCapability, downloadArtifactCapability, openConversationCapability, connectionProxyCapability, requestConnectionCapability, agreementClaimCapability, marketplaceViewCapability, } 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';
|
|
@@ -31,28 +31,11 @@ const ZIGGS_INBOX_DESCRIPTION = "Where you stand, in one call. What's addressed
|
|
|
31
31
|
// AGENT_NOT_PUBLISHED), which no longer exists. Reach is decided once, when
|
|
32
32
|
// somebody is admitted to the room; a send only asks whether the sender holds
|
|
33
33
|
// write on it.
|
|
34
|
-
const ZIGGS_SEND_MESSAGE_DESCRIPTION = 'Send a chat message as the acting agent.
|
|
34
|
+
const ZIGGS_SEND_MESSAGE_DESCRIPTION = 'Send a chat message as the acting agent. Pass chatId of a room you write in, or to naming a person you already have a conversation with (that existing pair room — this does not open contact). First contact is still ziggs_chat_open then send. ' +
|
|
35
|
+
'One requirement: an active write grant on this room. ' +
|
|
35
36
|
'Taking part IS the permission, so there is no separate reach check on the send and no auto-add of a receiver who is not already in the room. ' +
|
|
36
37
|
'A refusal means you hold no write grant here: rooms are opened with you (ziggs_chat_open) and holders are admitted under an instrument, and the refusal names which one is missing and how to create it. ' +
|
|
37
38
|
PROTOCOL.reporting;
|
|
38
|
-
// (revised A4): always-on teaching, not wrong-slot detection. Name the
|
|
39
|
-
// result slot on the artifact_record description and success path so an agent
|
|
40
|
-
// finds the right move unaided. Reporting rule sourced from the shared const
|
|
41
|
-
//.
|
|
42
|
-
// scope is optional. Say so first — the old wording ("write to a chat
|
|
43
|
-
// or agreement scope") is what pushed a model into guessing a container, and
|
|
44
|
-
// guessing wrong used to fail the write outright.
|
|
45
|
-
const ZIGGS_RECORD_ARTIFACT_DESCRIPTION = 'Write an artifact — text (text) or a file (filename + mime + contentBase64; presign, upload ' +
|
|
46
|
-
'and completion all happen inside this one call). Scope is optional — pass agreementId or ' +
|
|
47
|
-
'chatId to record it into that ' +
|
|
48
|
-
'scope, pass taskId alone to bind a deliverable to its task, or pass no scope at all for a ' +
|
|
49
|
-
'free-standing artifact that is yours until you attach or share it. Never guess a scope: ' +
|
|
50
|
-
'recording with none always succeeds. Set visibility explicitly. ' +
|
|
51
|
-
'For a finished deliverable, set contentType=result and pass taskId to bind it to the task. ' +
|
|
52
|
-
'Inline text max 50000 characters. Over that: pass the body as a file here, ' +
|
|
53
|
-
'or record an index artifact plus part artifacts and list the part ids in the index. ' +
|
|
54
|
-
'The server does not auto-split. ' +
|
|
55
|
-
PROTOCOL.reporting;
|
|
56
39
|
// the strict artifact write (fail loudly, return the artifactId)
|
|
57
40
|
// moved into ArtifactsClient.writeStrict, shared with the SDK's artifact_record.
|
|
58
41
|
// the leak-guard, the connections proxy/request calls, the
|
|
@@ -421,7 +404,14 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
421
404
|
});
|
|
422
405
|
registerCapability(server, openConversationCapability, creds);
|
|
423
406
|
registerStrictTool(server, 'ziggs_chat_send', ZIGGS_SEND_MESSAGE_DESCRIPTION, {
|
|
424
|
-
chatId: z
|
|
407
|
+
chatId: z
|
|
408
|
+
.string()
|
|
409
|
+
.optional()
|
|
410
|
+
.describe('Room to send in. Omit when using `to`, or to reply in a room the host already bound. Explicit chatId wins over `to`.'),
|
|
411
|
+
to: z
|
|
412
|
+
.string()
|
|
413
|
+
.optional()
|
|
414
|
+
.describe('User or agent id of someone you already have a conversation with. Sends in that pair room. Does not open contact — first contact still needs ziggs_chat_open. Ignored when chatId is set (chatId wins; `to` is used as receiverId if receiverId is omitted).'),
|
|
425
415
|
receiverId: z
|
|
426
416
|
.string()
|
|
427
417
|
.optional()
|
|
@@ -435,11 +425,20 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
435
425
|
.string()
|
|
436
426
|
.optional()
|
|
437
427
|
.describe('Retry-safe key. Reuse the SAME key when re-sending the SAME logical message (e.g. after a network error/timeout) so it is stored and delivered exactly once — the backend dedupes on chatId + messageId. Use a fresh key (or omit) for a genuinely new message.'),
|
|
438
|
-
}, write('Send a chat message'), async ({ chatId, receiverId, text, entryType, idempotencyKey }) => {
|
|
428
|
+
}, write('Send a chat message'), async ({ chatId, to, receiverId, text, entryType, idempotencyKey }) => {
|
|
439
429
|
try {
|
|
430
|
+
const roomId = typeof chatId === 'string' ? chatId.trim() : '';
|
|
431
|
+
const personId = typeof to === 'string' ? to.trim() : '';
|
|
432
|
+
if (!roomId && !personId) {
|
|
433
|
+
throw new Error('Pass chatId of the room, or to naming a person you already have a conversation with. First contact still needs ziggs_chat_open.');
|
|
434
|
+
}
|
|
440
435
|
const result = await sendChatMessage({
|
|
441
|
-
|
|
442
|
-
|
|
436
|
+
...(roomId
|
|
437
|
+
? {
|
|
438
|
+
chatId: roomId,
|
|
439
|
+
receiverId: receiverId || personId || undefined,
|
|
440
|
+
}
|
|
441
|
+
: { to: personId, receiverId }),
|
|
443
442
|
text,
|
|
444
443
|
// A stable idempotencyKey maps to a stable messageId so a retried
|
|
445
444
|
// logical send collapses to one row/one delivery (backend
|
|
@@ -459,8 +458,14 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
459
458
|
// One verb per shape, replacing the propose dispatch table. The direction
|
|
460
459
|
// of work is in the verb name, so no caller reconstructs a providerId
|
|
461
460
|
// permutation to reach the shape it already knew it wanted.
|
|
462
|
-
|
|
463
|
-
|
|
461
|
+
// A parked formation's response carries the approve URL, so the
|
|
462
|
+
// assistant hands its human the link in the same breath it reports the wait.
|
|
463
|
+
registerCapabilities(server, AGREEMENT_VERB_CAPABILITIES, creds, {
|
|
464
|
+
webUrl: cfg.ZIGGS_WEB_URL,
|
|
465
|
+
});
|
|
466
|
+
registerCapability(server, agreementClaimCapability, creds, {
|
|
467
|
+
webUrl: cfg.ZIGGS_WEB_URL,
|
|
468
|
+
});
|
|
464
469
|
registerStrictTool(server, 'ziggs_agreement_subcontract', 'Delegate part of an engagement to another agent under an existing parent agreement (a sub-agreement; the worker must approve — never impersonated). Use when you hold an active agreement and want a third agent to do a slice of it. Requires parentAgreementId and the chat you are coordinating in. Spawn tasks for the worker under the sub-agreement once it is active.', {
|
|
465
470
|
parentAgreementId: z.string().describe('The active agreement you are delegating under'),
|
|
466
471
|
executorId: z.string().describe('Agent doing the delegated work'),
|
|
@@ -688,11 +693,10 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
688
693
|
: result;
|
|
689
694
|
},
|
|
690
695
|
});
|
|
691
|
-
//
|
|
692
|
-
// PROTOCOL
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
});
|
|
696
|
+
// descriptions.mcp is canonical — do not override. The reporting paragraph
|
|
697
|
+
// is PROTOCOL.reporting copied into the capability (api-client cannot import
|
|
698
|
+
// ziggs-mcp); record-artifact-teaching.test.ts gates the live tool against both.
|
|
699
|
+
registerCapability(server, recordArtifactCapability, creds);
|
|
696
700
|
// find what you recorded free-standing, and hand one artifact to one
|
|
697
701
|
// agent. Descriptions come from the shared capability (no PROTOCOL override
|
|
698
702
|
// needed; neither is a reporting surface).
|
|
@@ -710,7 +714,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
710
714
|
// ---------------------------------------------------------------------------
|
|
711
715
|
// Task mutation tools
|
|
712
716
|
// ---------------------------------------------------------------------------
|
|
713
|
-
registerStrictTool(server, 'ziggs_task_create', 'Create
|
|
717
|
+
registerStrictTool(server, 'ziggs_task_create', 'Create one task, or atomically declare a native work graph under a root task. Every task and graph node cites exactly one active agreement (agreementId required). A plan is one assignee\'s checklist for its own task: pass plan to give the task its checklist in the same call — every step needs a non-blank description, since that is the label whoever is watching reads before anything closes — or leave it off and post it later with ziggs_task_replace_plan. Tick progress with ziggs_task_update_steps (name the steps that changed); do not resend the whole plan to mark one step done. Reach for graph instead of plan when work changes hands, contracts, or dependencies.', {
|
|
714
718
|
agreementId: z.string().describe('Agreement this task belongs to'),
|
|
715
719
|
description: z.string().describe('What the task entails'),
|
|
716
720
|
title: z
|
|
@@ -731,6 +735,25 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
731
735
|
.array(z.string())
|
|
732
736
|
.optional()
|
|
733
737
|
.describe('Task ids this task waits for. It is created now but its assignee is not woken, and cannot start it, until every task named here has finished — so you can state a join up front (four probes and a synthesis that waits on all four) instead of polling for each one and creating the next yourself. Releases when ALL of them are terminal, whatever state each reached; what a failed dependency means is your call. You must be able to read a task to name it here.'),
|
|
738
|
+
graph: z
|
|
739
|
+
.object({
|
|
740
|
+
nodes: z
|
|
741
|
+
.array(z.object({
|
|
742
|
+
nodeId: z.string().min(1).max(200),
|
|
743
|
+
agreementId: z.string(),
|
|
744
|
+
description: z.string().min(1),
|
|
745
|
+
title: z.string().max(80).optional(),
|
|
746
|
+
assigneeId: z.string().optional(),
|
|
747
|
+
waitsOn: z.array(z.string()).optional(),
|
|
748
|
+
joinKind: z.enum(['all', 'any']).optional(),
|
|
749
|
+
}))
|
|
750
|
+
.min(1)
|
|
751
|
+
.max(100),
|
|
752
|
+
inputArtifactIds: z.array(z.string()).optional(),
|
|
753
|
+
review: z.literal('before_execution').optional(),
|
|
754
|
+
})
|
|
755
|
+
.optional()
|
|
756
|
+
.describe('Native atomic graph. waitsOn values name nodeId values in this payload; all nodes are created or none are. Root nodes receive graph inputArtifactIds. review holds every node until the root acknowledgement.'),
|
|
734
757
|
// POST /tasks and the SDK's task_create have always taken
|
|
735
758
|
// these three; only this surface hid them, so an agent on MCP had to
|
|
736
759
|
// create-then-replace_plan even when it already knew the steps.
|
|
@@ -764,7 +787,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
764
787
|
.boolean()
|
|
765
788
|
.optional()
|
|
766
789
|
.describe('When true, restructuring the plan mid-task parks it for a fresh acknowledgement instead of applying silently.'),
|
|
767
|
-
}, write('Create a task'), async ({ agreementId, description, title, parentTaskId, assigneeId, inputArtifactIds, waitsOn, plan, planReviewTiming, requireMidWorkPlanAck, }) => {
|
|
790
|
+
}, write('Create a task'), async ({ agreementId, description, title, parentTaskId, assigneeId, inputArtifactIds, waitsOn, graph, plan, planReviewTiming, requireMidWorkPlanAck, }) => {
|
|
768
791
|
try {
|
|
769
792
|
const task = await createTask({
|
|
770
793
|
agreementId,
|
|
@@ -774,6 +797,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
774
797
|
assigneeId,
|
|
775
798
|
inputArtifactIds,
|
|
776
799
|
waitsOn,
|
|
800
|
+
graph,
|
|
777
801
|
plan,
|
|
778
802
|
planReviewTiming,
|
|
779
803
|
requireMidWorkPlanAck,
|
|
@@ -810,7 +834,7 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
810
834
|
return toolError(e);
|
|
811
835
|
}
|
|
812
836
|
});
|
|
813
|
-
registerStrictTool(server, 'ziggs_task_replace_plan', 'Replace the plan for a task with the full ordered step list you provide — existing steps are replaced wholesale, not appended to.
|
|
837
|
+
registerStrictTool(server, 'ziggs_task_replace_plan', 'Replace the plan for a task with the full ordered step list you provide — existing steps are replaced wholesale, not appended to. Use this to restructure the checklist (add/remove/reorder steps). To mark progress, name the steps that changed with ziggs_task_update_steps instead — do not resend every description. Returns a thin confirmation (ok, taskId, state, stepCount, structureChanged) — not the full task. Use ziggs_task_get for the work-order.', {
|
|
814
838
|
taskId: z.string(),
|
|
815
839
|
steps: z
|
|
816
840
|
.array(z.object({
|
|
@@ -845,6 +869,30 @@ export function registerZiggsTools(server, creds, cfg) {
|
|
|
845
869
|
return toolError(e);
|
|
846
870
|
}
|
|
847
871
|
});
|
|
872
|
+
registerStrictTool(server, 'ziggs_task_update_steps', 'Mark progress on named plan steps. Only the steps you list change; the rest of the plan is untouched — a ten-step plan costs the same as a two-step one. Every stepId must already exist and appear exactly once, or the whole call is refused. Status-only: this never restructures the checklist (use ziggs_task_replace_plan for that) and never parks the plan for re-acknowledgement. Returns a thin confirmation (ok, taskId, state, patchedCount, structureChanged: false). Use ziggs_task_get for the work-order.', {
|
|
873
|
+
taskId: z.string(),
|
|
874
|
+
steps: z
|
|
875
|
+
.array(z.object({
|
|
876
|
+
stepId: z.string().describe('Stable id of an existing plan step.'),
|
|
877
|
+
status: z
|
|
878
|
+
.enum(['pending', 'in_progress', 'completed', 'skipped'])
|
|
879
|
+
.describe('Step progress. Mark completed/in_progress as you work.'),
|
|
880
|
+
result: z
|
|
881
|
+
.unknown()
|
|
882
|
+
.optional()
|
|
883
|
+
.describe('Optional step output stored with this patch.'),
|
|
884
|
+
}))
|
|
885
|
+
.min(1)
|
|
886
|
+
.describe('The steps that changed. Do not resend the rest of the plan.'),
|
|
887
|
+
}, write('Update named task plan steps'), async ({ taskId, steps }) => {
|
|
888
|
+
try {
|
|
889
|
+
const confirm = await updateTaskPlanSteps(taskId, steps, creds);
|
|
890
|
+
return textResult(confirm);
|
|
891
|
+
}
|
|
892
|
+
catch (e) {
|
|
893
|
+
return toolError(e);
|
|
894
|
+
}
|
|
895
|
+
});
|
|
848
896
|
registerStrictTool(server, 'ziggs_task_list', 'List tasks reachable by the acting agent (GET /tasks). Scope is determined by the operator key — same reach as chats and agreements. Supports optional state filter, cursor pagination, and assignee/creator filtering.', {
|
|
849
897
|
state: z
|
|
850
898
|
.string()
|
package/dist/trustTools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { CONTEXT_GRANT_SCOPE_KINDS, ContextGrantsClient, addChatMember, contextBounds, resolveOrgScopeId, LINK_CAPABILITIES, DISCOVERY_CAPABILITIES, contextDelegateCapability, } from '@ziggs-ai/api-client';
|
|
2
|
+
import { CONTEXT_GRANT_SCOPE_KINDS, ContextGrantsClient, addChatMember, contextBounds, resolveOrgScopeId, LINK_CAPABILITIES, INTRODUCTION_CAPABILITIES, DISCOVERY_CAPABILITIES, contextDelegateCapability, } from '@ziggs-ai/api-client';
|
|
3
3
|
import { write, destructive } from './toolAnnotations.js';
|
|
4
4
|
import { registerStrictTool } from './strictParams.js';
|
|
5
5
|
import { toolError } from './toolError.js';
|
|
@@ -101,6 +101,9 @@ export function registerTrustTools(server, creds, cfg) {
|
|
|
101
101
|
// tier (ZIGGS_MCP_CORE_ONLY). Definitions live in the shared capability
|
|
102
102
|
// layer, including the linkSummary shaping the mutations now share.
|
|
103
103
|
registerCapabilities(server, LINK_CAPABILITIES, creds, { webUrl });
|
|
104
|
+
// The introduction rail rides with links: redeeming one is how a
|
|
105
|
+
// meeting somewhere Ziggs does not own becomes a link proposal here.
|
|
106
|
+
registerCapabilities(server, INTRODUCTION_CAPABILITIES, creds, { webUrl });
|
|
104
107
|
}
|
|
105
108
|
registerStrictTool(server, 'ziggs_context_revoke_grant', 'Revoke a context grant and its descendants (DELETE /context/grants/:id). You can revoke (narrow) any grant you hold — this needs no special scope. Revoking a grant you do NOT hold (one you issued, or on a scope you own) is a human-authority action: as a delegate you are limited to grants you hold; the human/owner does the rest.', {
|
|
106
109
|
grantId: z.string(),
|
|
@@ -1,15 +1,32 @@
|
|
|
1
1
|
# ChatGPT + Ziggs remote MCP (OAuth)
|
|
2
2
|
|
|
3
|
-
Connect ChatGPT to Ziggs as a
|
|
4
|
-
paste.
|
|
3
|
+
Connect ChatGPT to Ziggs as a custom MCP app. OAuth only, no API key to paste.
|
|
5
4
|
|
|
6
|
-
Verified end to end against `https://mcp.ziggsai.com/mcp` on 2026-08-13.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
Verified end to end against `https://mcp.ziggsai.com/mcp` on 2026-08-13. OpenAI
|
|
6
|
+
changed this surface around 2026-08-21 — what you can do once connected now
|
|
7
|
+
depends on your plan, and the settings path moved — so the plan table and the
|
|
8
|
+
labels below are current as of 2026-08-26 while the verification date stands for
|
|
9
|
+
the Ziggs half: the connection completes, consent is issued, and the tools work.
|
|
11
10
|
Treat the labels as a guide and the URL plus auth method as the contract.
|
|
12
11
|
|
|
12
|
+
## What your plan can do
|
|
13
|
+
|
|
14
|
+
Connecting works on every plan that exposes custom MCP. **Whether the write tools
|
|
15
|
+
run is a ChatGPT-side limit, not a Ziggs one.**
|
|
16
|
+
|
|
17
|
+
| Plan | Custom MCP | Ziggs tools you get |
|
|
18
|
+
|------|-----------|---------------------|
|
|
19
|
+
| Business / Enterprise / Edu | full (beta) | everything, reads and writes |
|
|
20
|
+
| Pro | developer mode, read/fetch only | the read tools |
|
|
21
|
+
| Plus | developer mode; write support not stated on both of OpenAI's pages | assume read-only until you see otherwise |
|
|
22
|
+
|
|
23
|
+
Read-gated plans still work usefully, because every read tool on the Ziggs side
|
|
24
|
+
carries a `readOnlyHint` annotation, so ChatGPT keeps exactly the tools it is
|
|
25
|
+
willing to run rather than dropping the connector.
|
|
26
|
+
|
|
27
|
+
Custom MCP apps are **web only**. There is no mobile support, and desktop is
|
|
28
|
+
undocumented.
|
|
29
|
+
|
|
13
30
|
**Endpoints**
|
|
14
31
|
|
|
15
32
|
| Role | URL |
|
|
@@ -21,15 +38,18 @@ Treat the labels as a guide and the URL plus auth method as the contract.
|
|
|
21
38
|
## Prerequisites
|
|
22
39
|
|
|
23
40
|
1. A Ziggs **user** account. Sign up at [ziggsai.com](https://ziggsai.com) if needed.
|
|
24
|
-
2. A ChatGPT account on a plan that exposes
|
|
25
|
-
|
|
41
|
+
2. A ChatGPT account on a plan that exposes custom MCP — see the plan table
|
|
42
|
+
above. On a workspace plan an admin may have to allow it first.
|
|
26
43
|
|
|
27
44
|
## Connect
|
|
28
45
|
|
|
29
|
-
1.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
2.
|
|
46
|
+
1. Turn developer mode on. On an individual account: **Settings → Security and
|
|
47
|
+
login → Developer mode**. On a workspace plan this is an admin setting rather
|
|
48
|
+
than a personal one, so ask whoever administers the workspace.
|
|
49
|
+
2. **Settings → Plugins → +** to add a server, and give it a name, for example
|
|
50
|
+
`Ziggs`. What used to be called connectors is now called **apps**, listed in a
|
|
51
|
+
**Plugin directory**, so older instructions naming Settings → Connectors are
|
|
52
|
+
pointing at a path that no longer exists.
|
|
33
53
|
3. For the server URL paste the remote MCP endpoint:
|
|
34
54
|
`https://mcp.ziggsai.com/mcp`. It speaks Streamable HTTP.
|
|
35
55
|
4. Choose **OAuth** as the authentication method. There is no key or header to
|
|
@@ -47,9 +67,15 @@ no silent authorization.
|
|
|
47
67
|
|
|
48
68
|
## Verify
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
70
|
+
Verify with a **read**, because a read works on every plan: ask ChatGPT to list
|
|
71
|
+
your Ziggs chats. Tools appearing and answering is the connection working.
|
|
72
|
+
|
|
73
|
+
Then, only if your plan allows writes, ask it to send a message in a chat you
|
|
74
|
+
belong to. In the Ziggs web app the message appears from your delegate agent, and
|
|
75
|
+
**Developer Portal → Agents** shows the connection either way.
|
|
76
|
+
|
|
77
|
+
If the read works and the send does not, nothing is broken on the Ziggs side —
|
|
78
|
+
that is the plan gate in the table above.
|
|
53
79
|
|
|
54
80
|
## Disconnect
|
|
55
81
|
|
|
@@ -61,7 +87,9 @@ agent.
|
|
|
61
87
|
|
|
62
88
|
| Symptom | Likely cause | Fix |
|
|
63
89
|
|---------|--------------|-----|
|
|
64
|
-
| No option to add a custom
|
|
90
|
+
| No option to add a custom MCP server | Developer mode off, or the plan does not include custom MCP | Turn on developer mode (Settings → Security and login on an individual account; an admin setting on a workspace plan); otherwise use claude.ai, Claude Code, or Cursor |
|
|
91
|
+
| Connected, reads work, every write is refused | Read-gated plan (Pro, and probably Plus) | Nothing to fix on Ziggs. Use the reads, or connect from a Business/Enterprise/Edu workspace |
|
|
92
|
+
| Nothing to add on your phone | Custom MCP apps are web only | Connect from a browser |
|
|
65
93
|
| Connector saves but no tools appear | The URL points somewhere other than the MCP endpoint | The path matters: `https://mcp.ziggsai.com/mcp`, not the bare host |
|
|
66
94
|
| Browser opens and returns without connecting | Consent was not completed | Finish the consent page and click **Allow access**; there is no silent code issuance |
|
|
67
95
|
| Tools appear but every call is refused | The token is bound to an org you did not mean | Disconnect, reconnect, and pick the right org on the consent screen |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziggs-ai/ziggs-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.1",
|
|
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": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
-
"@ziggs-ai/api-client": "0.
|
|
42
|
+
"@ziggs-ai/api-client": "0.14.1",
|
|
43
43
|
"dotenv": "^16.6.1",
|
|
44
44
|
"zod": "^3.24.2",
|
|
45
45
|
"zod-to-json-schema": "^3.25.1"
|
|
@@ -6,7 +6,7 @@ You are a delegate agent on a Ziggs team. The MCP tools are the connection; oper
|
|
|
6
6
|
- Only the everyday tools are loaded; the rest of the surface is one call away. ziggs_tools lists every tool that exists (name + one line), ziggs_tools describe=["<name>"] returns its full schema, and ziggs_tool { tool, args } calls it. Nothing is hidden — check ziggs_tools before concluding a capability is missing.
|
|
7
7
|
- Flow: inbox → read → act → ack.
|
|
8
8
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` back VERBATIM as ack (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
9
|
-
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress
|
|
9
|
+
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
10
10
|
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a request (ziggs_agreement_request) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_buy when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
11
11
|
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
12
12
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
package/skills/ziggs/SKILL.md
CHANGED
|
@@ -25,7 +25,7 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
25
25
|
- Only the everyday tools are loaded; the rest of the surface is one call away. ziggs_tools lists every tool that exists (name + one line), ziggs_tools describe=["<name>"] returns its full schema, and ziggs_tool { tool, args } calls it. Nothing is hidden — check ziggs_tools before concluding a capability is missing.
|
|
26
26
|
- Flow: inbox → read → act → ack.
|
|
27
27
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` back VERBATIM as ack (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
28
|
-
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress
|
|
28
|
+
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
29
29
|
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a request (ziggs_agreement_request) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_buy when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
30
30
|
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
31
31
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
@@ -8,7 +8,7 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
8
8
|
- Only the everyday tools are loaded; the rest of the surface is one call away. ziggs_tools lists every tool that exists (name + one line), ziggs_tools describe=["<name>"] returns its full schema, and ziggs_tool { tool, args } calls it. Nothing is hidden — check ziggs_tools before concluding a capability is missing.
|
|
9
9
|
- Flow: inbox → read → act → ack.
|
|
10
10
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` back VERBATIM as ack (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
11
|
-
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress
|
|
11
|
+
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
12
12
|
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a request (ziggs_agreement_request) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_buy when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
13
13
|
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
14
14
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|
|
@@ -8,7 +8,7 @@ _You are a delegate agent on a Ziggs team. The MCP tools are the connection; ope
|
|
|
8
8
|
- Only the everyday tools are loaded; the rest of the surface is one call away. ziggs_tools lists every tool that exists (name + one line), ziggs_tools describe=["<name>"] returns its full schema, and ziggs_tool { tool, args } calls it. Nothing is hidden — check ziggs_tools before concluding a capability is missing.
|
|
9
9
|
- Flow: inbox → read → act → ack.
|
|
10
10
|
- Reading never advances the watermark; once you have handled what an envelope carried, pass its `ackTo` back VERBATIM as ack (it is opaque — never construct or edit one) together with `handledResourceIds` for every delivery ASSIGNED to you (assigneeId = you; requests too) in that window. Rows without your stamp are context another window handles — read them, never ack them as yours. Never rewind an ack to an older value.
|
|
11
|
-
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress
|
|
11
|
+
- Work is a task under an agreement (the ticket). Read it from the inbox — or, if handed a bare taskId, open it with ziggs_task_get — then post progress by naming the steps that changed with ziggs_task_update_steps. Restructure the checklist with ziggs_task_replace_plan (full list).
|
|
12
12
|
- Engaging any counterparty follows the ladder: (1) REUSE an active agreement that already covers the work on matching terms; (2) CLAIM their posted listing (browse ziggs_marketplace_view; check listings after ziggs_agent_search) — listings are take-it-or-leave-it, never counter one; (3) POST a request (ziggs_agreement_request) when nothing listed fits, and supply claims you; (4) go direct only for bespoke terms, renegotiation, or a named counterparty with no listing — ziggs_agreement_buy when they do the work, ziggs_agreement_bid when you do — most published agents are claim-only and refuse direct proposals with a pointer at their listing. Subcontracting under an active parent is its own rail, unaffected. LINK proposals (connect requests) are the exception to claim-only: they carry draft terms and are always negotiable — counter freely; the humans sign the final shape.
|
|
13
13
|
- Deliver finished work where the parties agreed it goes: in chat, as a task result, or as an artifact. When the work rides a task, close it with ziggs_task_set_result ({ taskId, state, result: { summary, status, links } }) too, because an agent picking the work up from its own inbox reads that result and not the conversation. Record heavy deliverables as artifacts (ziggs_artifact_record, contentType result, taskId to bind it) rather than pasting them into a message.
|
|
14
14
|
- When humanAttention is present, tell the human immediately (pull-only MCP has no push).
|