@ziggs-ai/ziggs-mcp 0.1.9 → 0.1.11

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 CHANGED
@@ -154,9 +154,9 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
154
154
 
155
155
  ### 4. CLI smoke scripts
156
156
 
157
- ```bash
158
- cd agentplus
157
+ From the **repository root** ([github.com/ZiggsAI/agentplus](https://github.com/ZiggsAI/agentplus)):
159
158
 
159
+ ```bash
160
160
  # ZIG-222
161
161
  ZIGGS_OPERATOR_KEY=... ZIGGS_AGENT_ID=cursor-delegate \
162
162
  node scripts/smoke-ziggs-mcp.mjs
@@ -179,6 +179,12 @@ ZIGGS_OPERATOR_KEY_B=... ZIGGS_AGENT_ID_B=... \
179
179
  ZIGGS_APPROVER_OPERATOR_KEY=... ZIGGS_APPROVER_USER_ID=... \
180
180
  ZIGGS_SMOKE_CHAT_ID=... \
181
181
  node scripts/smoke-ziggs-mcp-z433-e2e.mjs
182
+
183
+ # ZIG-481 / ZIG-520 party handshake (link agreement, prod)
184
+ HTTP_URL=https://api.ziggsai.com \
185
+ OP_KEY_A=... AGENT_A=... USER_B=... OP_KEY_B=... AGENT_B=... \
186
+ npm run smoke:zig-481-prod
187
+ # Runbook: docs/evals/party-connection-two-claude.md (ZIG-521)
182
188
  ```
183
189
 
184
190
  ---
@@ -187,7 +193,7 @@ ZIGGS_SMOKE_CHAT_ID=... \
187
193
 
188
194
  | Tool | Maps to |
189
195
  |------|---------|
190
- | `ziggs_inbox` | `GET /agent-api/v1/inbox` + `POST .../ack` (ZIG-434) |
196
+ | `ziggs_inbox` | `GET /inbox` + `POST /inbox/ack` (ZIG-434, ZIG-491) |
191
197
  | `ziggs_discover_context` | `GET /context/discovery` |
192
198
  | `ziggs_read_context` | `GET /context/read/:type` |
193
199
  | `ziggs_record_artifact` | `POST /artifacts` |
@@ -197,6 +203,9 @@ ZIGGS_SMOKE_CHAT_ID=... \
197
203
  | `ziggs_issue_grant` | Chat admission or `POST /context/grants` |
198
204
  | `ziggs_delegate_grant` | `POST /context/grants/:id/delegate` |
199
205
  | `ziggs_revoke_grant` | `DELETE /context/grants/:id` |
206
+ | `ziggs_request_link` | `POST /agreements` `{engagementKind:"link"}` (ZIG-481 — a link is just an agreement) |
207
+ | `ziggs_list_links` | `GET /agreements?engagementKind=link` |
208
+ | `ziggs_revoke_link` | `DELETE /agreements/:agreementId` |
200
209
  | `ziggs_smoke_impersonation` | Smoke step 1 |
201
210
  | `ziggs_get_scope` | `GET /scope?via=` |
202
211
  | `ziggs_list_my_agreements` | `GET /agreements?scope=mine` |
@@ -206,14 +215,13 @@ ZIGGS_SMOKE_CHAT_ID=... \
206
215
  | `ziggs_list_messages` | `GET /chats/:id/messages` |
207
216
  | `ziggs_send_message` | `POST /chats/:id/messages` |
208
217
  | `ziggs_propose_agreement` | `POST /agreements/proposals` |
209
- | `ziggs_respond_to_agreement` | `POST /agreements/:id/respond` |
218
+ | `ziggs_respond_to_agreement` | `PUT /agreements/:id/approvals/:partyId` (owner principal; approves hire, service, and `link` proposals) |
210
219
 
211
220
  ---
212
221
 
213
222
  ## Develop
214
223
 
215
224
  ```bash
216
- cd agentplus
217
225
  npm install
218
226
  npm run build -w @ziggs-ai/ziggs-mcp
219
227
  npm test -w @ziggs-ai/ziggs-mcp
package/dist/tools.js CHANGED
@@ -133,7 +133,7 @@ export function registerZiggsTools(server, creds, cfg) {
133
133
  return toolError(e.message);
134
134
  }
135
135
  });
136
- server.tool('ziggs_open_conversation', 'Open or reuse a chat with a user or agent participant.', {
136
+ server.tool('ziggs_open_conversation', 'Open or reuse a chat with a user or agent participant. To reach an agent in ANOTHER org, an unpublished delegate must establish a link first — call ziggs_request_link (if you have its agent id) or ziggs_create_link_invite (if you do not) and have it approved/claimed — otherwise this fails with AGENT_NOT_PUBLISHED.', {
137
137
  participantId: z.string().describe('User or agent id to converse with'),
138
138
  }, async ({ participantId }) => {
139
139
  try {
@@ -164,7 +164,7 @@ export function registerZiggsTools(server, creds, cfg) {
164
164
  return toolError(e.message);
165
165
  }
166
166
  });
167
- server.tool('ziggs_send_message', 'Send a chat message as the delegate agent (requires chat membership).', {
167
+ server.tool('ziggs_send_message', 'Send a chat message as the delegate agent (requires chat membership). Cross-org first contact requires an ACTIVE link first (ziggs_request_link / ziggs_create_link_invite, then approve/claim); without it, messaging an agent outside your org fails with AGENT_NOT_PUBLISHED.', {
168
168
  chatId: z.string(),
169
169
  receiverId: z.string().describe('User or agent id receiving the message'),
170
170
  text: z.string(),
@@ -217,13 +217,17 @@ export function registerZiggsTools(server, creds, cfg) {
217
217
  return toolError(e.message);
218
218
  }
219
219
  });
220
- server.tool('ziggs_respond_to_agreement', 'Approve or reject a pending proposal as the token owner (payer-side delegate).', {
220
+ server.tool('ziggs_respond_to_agreement', 'Approve or reject a pending agreement (ZIG-524). Uses PUT /approvals/:partyId or POST /claim for open broadcast.', {
221
221
  agreementId: z.string(),
222
222
  action: z.enum(['approve', 'reject']),
223
223
  }, async ({ agreementId, action }) => {
224
224
  try {
225
- const agreement = await respondToAgreement(agreementId, action, creds);
226
- return textResult({ agreement });
225
+ const claims = decodeOperatorKeyClaims(creds.operatorKey);
226
+ const ownerId = claims?.ownerId ?? cfg.ZIGGS_OWNER_USER_ID;
227
+ const updated = await respondToAgreement(agreementId, action, creds, {
228
+ ownerUserId: ownerId,
229
+ });
230
+ return textResult({ agreement: updated });
227
231
  }
228
232
  catch (e) {
229
233
  return toolError(e.message);
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { AgentSearchClient, ContextGrantsClient, addChatMember, } from '@ziggs-ai/api-client';
2
+ import { AgentSearchClient, ContextGrantsClient, createAgreement, listAgreements, revokeAgreement, claimLink, addChatMember, } from '@ziggs-ai/api-client';
3
3
  function textResult(data) {
4
4
  return {
5
5
  content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
@@ -15,8 +15,8 @@ const grantScopeKindSchema = z.enum(['chat', 'agreement', 'org']);
15
15
  const contextTemporalSchema = z.enum(['from-now', 'from-start']);
16
16
  /** ZIG-433 — agent search + context grant management through MCP. */
17
17
  export function registerTrustTools(server, creds) {
18
- server.tool('ziggs_search_agents', 'Search published agents by query (AgentSearchClient). Use returned agentId in grant/issue tools — do not guess ids.', {
19
- query: z.string().describe('Natural language or keyword search'),
18
+ server.tool('ziggs_search_agents', 'Find agents (AgentSearchClient). A keyword/natural-language query searches PUBLISHED agents only. Passing an EXACT agent id resolves that one agent even if it is unpublished/private — use this to look up a delegate someone shared their id for, then ziggs_request_link against it (ZIG-480). Use returned agentId in grant/issue tools — do not guess ids.', {
19
+ query: z.string().describe('Keyword/natural-language search (published agents) OR an exact agent id (resolves that agent even if unpublished)'),
20
20
  limit: z.number().optional().describe('Max results (default server-side)'),
21
21
  minScore: z.number().optional().describe('Minimum match score filter'),
22
22
  }, async ({ query, limit, minScore }) => {
@@ -151,6 +151,99 @@ export function registerTrustTools(server, creds) {
151
151
  return toolError(e.message);
152
152
  }
153
153
  });
154
+ server.tool('ziggs_request_link', 'Request a bilateral trust link with another agent before cross-org reach. A link is just an agreement (POST /agreements {engagementKind:"link"}, ZIG-481). The target OWNER must approve it (via ziggs_respond_to_agreement) before unpublished delegates can message each other.', {
155
+ providerId: z
156
+ .string()
157
+ .describe('Bare agent id to link with (the target delegate). Use ziggs_search_agents or a known delegate id — do not guess.'),
158
+ message: z
159
+ .string()
160
+ .optional()
161
+ .describe('Optional note shown to the counterparty human on approval (agreement description)'),
162
+ }, async ({ providerId, message }) => {
163
+ try {
164
+ const { agreement } = await createAgreement({ engagementKind: 'link', providerId, description: message }, creds);
165
+ return textResult({
166
+ status: 'pending',
167
+ message: 'Link agreement created — the counterparty owner must approve (ziggs_respond_to_agreement) before cross-org reach. Surface pending state to the human.',
168
+ agreement,
169
+ });
170
+ }
171
+ catch (e) {
172
+ return toolError(e.message);
173
+ }
174
+ });
175
+ server.tool('ziggs_create_link_invite', 'Create a shareable OPEN link invite when you do NOT have the counterparty\'s agent id (e.g. connecting across orgs) (ZIG-525). Creates an open link agreement (POST /agreements {engagementKind:"link"}, proposedTo:"everyone"). Share the returned inviteId (agreementId) out-of-band; the recipient forms the link by calling ziggs_claim_link_invite — neither side pastes an agent id. Revoke via ziggs_revoke_link to disable.', {
176
+ message: z
177
+ .string()
178
+ .optional()
179
+ .describe('Optional note shown to whoever opens the invite (agreement description)'),
180
+ }, async ({ message }) => {
181
+ try {
182
+ const { agreement } = await createAgreement({ engagementKind: 'link', description: message }, creds);
183
+ return textResult({
184
+ status: 'open',
185
+ inviteId: agreement.agreementId,
186
+ message: 'Open link invite created. Share inviteId (agreementId) with the counterparty; they claim it via ziggs_claim_link_invite. No agent id needed on either side.',
187
+ agreement,
188
+ });
189
+ }
190
+ catch (e) {
191
+ return toolError(e.message);
192
+ }
193
+ });
194
+ server.tool('ziggs_claim_link_invite', 'Claim an open link invite by its id to form a bilateral link (POST /agreements/:id/claim, ZIG-525). You become the counterparty and the link activates immediately (cross-org reach + bilateral context grants). You cannot claim your own invite.', {
195
+ agreementId: z
196
+ .string()
197
+ .describe('The invite id (agreementId) shared by the issuer'),
198
+ }, async ({ agreementId }) => {
199
+ try {
200
+ const { agreement } = await claimLink(agreementId, creds);
201
+ return textResult({
202
+ status: 'linked',
203
+ message: 'Link invite claimed — you are now linked. A link is reach-only: use ziggs_open_conversation and/or ziggs_issue_grant before reading context.',
204
+ agreement,
205
+ });
206
+ }
207
+ catch (e) {
208
+ return toolError(e.message);
209
+ }
210
+ });
211
+ server.tool('ziggs_list_links', 'List link agreements for this delegate (GET /agreements?engagementKind=link, ZIG-481). Each item exposes parties.creatorAgent (requester), parties.providerAgent (target), parties.proposedTo (target owner), proposal.status and status. Approve pending links via ziggs_respond_to_agreement.', {}, async () => {
212
+ try {
213
+ const links = await listAgreements({ engagementKind: 'link' }, creds);
214
+ const hasActive = links.some((a) => a.status === 'active');
215
+ return textResult({
216
+ count: links.length,
217
+ links,
218
+ ...(hasActive
219
+ ? {
220
+ nextSteps: 'A link is reach-only. Use ziggs_open_conversation (participantId = peer agent id) and/or ziggs_issue_grant before reading context.',
221
+ }
222
+ : {}),
223
+ });
224
+ }
225
+ catch (e) {
226
+ return toolError(e.message);
227
+ }
228
+ });
229
+ server.tool('ziggs_revoke_link', 'Revoke a bilateral link agreement (DELETE /agreements/:agreementId, ZIG-481). Either party may revoke; cross-org reach ends immediately.', {
230
+ agreementId: z
231
+ .string()
232
+ .describe('agreementId of the link agreement (from ziggs_list_links)'),
233
+ }, async ({ agreementId }) => {
234
+ try {
235
+ const result = await revokeAgreement(agreementId, creds);
236
+ return textResult({
237
+ status: 'revoked',
238
+ message: 'Link revoked — unpublished cross-org reach to this peer is blocked again.',
239
+ agreementId,
240
+ agreement: result.agreement,
241
+ });
242
+ }
243
+ catch (e) {
244
+ return toolError(e.message);
245
+ }
246
+ });
154
247
  server.tool('ziggs_revoke_grant', 'Revoke a context grant and its descendants (DELETE /context/grants/:id). Requires context:admin on the operator key.', {
155
248
  grantId: z.string(),
156
249
  }, async ({ grantId }) => {
@@ -15,7 +15,7 @@ Manual smoke runbook for the **real claude.ai connector UI**. Automated coverage
15
15
  Automated probe (same guarantee, no claude.ai UI):
16
16
 
17
17
  ```bash
18
- cd agentplus
18
+ # from github.com/ZiggsAI/agentplus clone root
19
19
  node scripts/smoke-ziggs-mcp-oauth-z474-consent-probe.mjs --auto
20
20
  ```
21
21
 
@@ -94,7 +94,7 @@ If you are already logged in, you still see this screen (ZIG-474). You are **not
94
94
  Full API path smoke (no claude.ai UI):
95
95
 
96
96
  ```bash
97
- cd agentplus
97
+ # from github.com/ZiggsAI/agentplus clone root
98
98
  node scripts/smoke-ziggs-mcp-oauth-z468-e2e.mjs --auto
99
99
  node scripts/smoke-ziggs-mcp-oauth-z474-consent-probe.mjs --auto
100
100
  ```
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "mcpServers": {
3
3
  "ziggs": {
4
+ "type": "http",
4
5
  "url": "https://mcp.ziggsai.com/mcp"
5
6
  }
6
7
  }
@@ -5,9 +5,12 @@ Verify the **fresh Claude Code** journey: empty repo, `.mcp.json` only (no opera
5
5
  Automated prerequisites + Path A parity (no UI):
6
6
 
7
7
  ```bash
8
- cd agentplus
8
+ # from github.com/ZiggsAI/agentplus clone root
9
9
  node scripts/smoke-zig-500-v4-path.mjs --discovery-only
10
10
  node scripts/smoke-zig-500-v4-path.mjs --auto
11
+ node scripts/smoke-zig-500-v4-claude-code.mjs # Claude Code CLI parity
12
+ # or:
13
+ ./scripts/run-zig-500-v4-claude-code.sh
11
14
  ```
12
15
 
13
16
  ---
@@ -19,11 +22,12 @@ node scripts/smoke-zig-500-v4-path.mjs --auto
19
22
  | ZIG-496 discovery (well-known, 401 docs) | `--discovery-only` | Client uses same URLs |
20
23
  | OAuth → Bearer → full tool surface | `--auto` or smoke creds | Connect in IDE |
21
24
  | No stub-only tools | fails if only `authenticate` / `complete_authentication` | Tool picker shows `ziggs_*` |
25
+ | Runtime tools work | `ziggs_connection_status` + `ziggs_inbox` | Same smoke in IDE |
22
26
  | Restart not required | N/A | Tools visible right after OAuth |
23
27
 
24
- **Path A (pass):** Native MCP OAuth — client discovers metadata, gets Bearer, `tools/list` returns ≥12 `ziggs_*` tools.
28
+ **Pass:** Native MCP OAuth — client discovers metadata, gets Bearer, `tools/list` returns ≥12 `ziggs_*` tools, and inbox/connection_status succeed.
25
29
 
26
- **Path B (fail → epic children 501–502):** OAuth succeeds but only stub auth tools appear — needs unauth MCP + `tools/list_changed` design in backend.
30
+ **Fail:** OAuth OK but tools empty, stub-only list, or inbox 404 → comment on [ZIG-500](https://linear.app/ziggsai/issue/ZIG-500) with tool list + smoke output.
27
31
 
28
32
  ---
29
33
 
@@ -37,6 +41,7 @@ Copy [`claude-code-remote-mcp.json`](claude-code-remote-mcp.json):
37
41
  {
38
42
  "mcpServers": {
39
43
  "ziggs": {
44
+ "type": "http",
40
45
  "url": "https://mcp.ziggsai.com/mcp"
41
46
  }
42
47
  }
@@ -52,19 +57,18 @@ Use a **throwaway directory** (no existing Ziggs plugin or stdio MCP).
52
57
  ### 3. Connect / authorize
53
58
 
54
59
  1. Claude Code should prompt for MCP OAuth (or open MCP settings).
55
- 2. Browser → Ziggs consent (`/app/oauth/mcp-consent`) → **Allow**.
60
+ 2. Browser → Ziggs consent (`/app/oauth/mcp-consent`) — pick **org** if you have teams → **Allow**.
56
61
  3. Return to Claude Code — check tool list.
57
62
 
58
63
  ### 4. Smoke tools (no restart)
59
64
 
60
65
  Ask Claude to call in order:
61
66
 
62
- 1. `ziggs_list_chats` or `ziggs_inbox`
63
- 2. `ziggs_send_message` (chat you belong to)
67
+ 1. `ziggs_connection_status` — confirm agent + org binding
68
+ 2. `ziggs_inbox` or `ziggs_list_chats`
69
+ 3. `ziggs_send_message` (chat you belong to)
64
70
 
65
- **Pass:** both succeed; tool picker shows many `ziggs_*` tools (not just 2 auth stubs).
66
-
67
- **Fail:** only `authenticate` / `complete_authentication`; or OAuth OK but tools empty until restart → comment on [ZIG-500](https://linear.app/ziggsai/issue/ZIG-500) with Path B.
71
+ **Pass:** all succeed; tool picker shows many `ziggs_*` tools (not just 2 auth stubs).
68
72
 
69
73
  ---
70
74
 
@@ -92,9 +96,9 @@ For dev or when OAuth is blocked, use [claude-code.md](claude-code.md) Option B
92
96
  | Symptom | Check |
93
97
  |---------|--------|
94
98
  | OAuth loop / 401 on tools | Re-run `smoke-zig-500-v4-path.mjs --auto`; revoke MCP in Agents dashboard → reconnect |
95
- | Only 2 stub tools | Path B — epic needs 501/502; paste tool list in Linear |
96
- | `app.ziggsai.com` in 401 `error` text | ZIG-496 fixed `docs` field; `error` string comes from pinned `@ziggs-ai/ziggs-mcp` npm — bump backend dep after publish |
97
- | Tools after restart only | ZIG-502 `tools/list_changed` candidate |
99
+ | Only 2 stub tools | Should not happen on current prod (Bearer-only `/mcp`); paste tool list in ZIG-500 |
100
+ | `ziggs_inbox` 404 | Backend must pin `@ziggs-ai/ziggs-mcp@0.1.9+` (ZIG-491 `/inbox` path); npm 0.1.10+ fixes Claude Code `.mcp.json` example (`type: http`) |
101
+ | Wrong org | Reconnect; pick org on consent screen (ZIG-504) |
98
102
 
99
103
  ---
100
104
 
@@ -106,9 +110,10 @@ Date:
106
110
  Claude Code version:
107
111
  Repo: empty + .mcp.json only (Y/N)
108
112
  Automated: smoke-zig-500-v4-path.mjs --discovery-only → pass/fail
109
- Automated: smoke-zig-500-v4-path.mjs --auto → Path A/B, N tools
113
+ Automated: smoke-zig-500-v4-path.mjs --auto → Path A, N tools, inbox pass/fail
110
114
  Manual OAuth connect: pass/fail
111
115
  Tool count after OAuth (no restart): __
112
- ziggs_inbox or ziggs_list_chats: pass/fail
116
+ ziggs_connection_status: pass/fail
117
+ ziggs_inbox: pass/fail
113
118
  Notes:
114
119
  ```
@@ -58,7 +58,7 @@ Ask Claude to call tools in order:
58
58
  Paste the session transcript in the PR when verifying ZIG-430, or run:
59
59
 
60
60
  ```bash
61
- cd agentplus
61
+ # from github.com/ZiggsAI/agentplus clone root
62
62
  ZIGGS_OPERATOR_KEY=<agent-scoped> node scripts/smoke-ziggs-mcp-z430-e2e.mjs
63
63
  ```
64
64
 
@@ -80,7 +80,7 @@ Points `authorization_servers` → `https://api.ziggsai.com` and lists MCP scope
80
80
  Automated parity probe:
81
81
 
82
82
  ```bash
83
- cd agentplus
83
+ # from github.com/ZiggsAI/agentplus clone root
84
84
  node scripts/probe-cursor-oauth-parity.mjs
85
85
  ```
86
86
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ziggs-ai/ziggs-mcp",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
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": {
@@ -29,8 +29,24 @@ These commonly surface as **`pending_approval`** or blocked tool errors:
29
29
 
30
30
  ## Trust tool sequence (cross-org)
31
31
 
32
+ A **link is just an agreement** (`engagementKind: "link"`, ZIG-481). Create it, the
33
+ counterparty owner approves it, and unpublished delegates can then reach each other.
34
+
32
35
  1. Human describes goal and counterparty.
33
- 2. `ziggs_search_agents` — present candidates; human picks.
34
- 3. `ziggs_issue_grant` or chat admission flow — wait for approval if required.
35
- 4. `ziggs_read_context` only after grant is active.
36
- 5. `ziggs_revoke_grant` when the human says access should end.
36
+ 2. Create the link with **`ziggs_request_link`** (`providerId` = the target delegate agent id;
37
+ use `ziggs_search_agents` to find published service agents — do not guess ids).
38
+ 3. Target human approves via **`ziggs_respond_to_agreement`** (action `approve`) when the
39
+ pending link agreement shows in their inbox / `ziggs_list_links`. This is the same generic
40
+ approval tool used for hire and service proposals — there is no separate link-response tool.
41
+ 4. **A link ≠ a grant.** The link only allows unpublished delegates to *reach* each other. To
42
+ chat or read context, continue with **`ziggs_open_conversation`** (participantId = peer agent
43
+ id) and/or **`ziggs_issue_grant`** — still approval-gated when exposing existing scope.
44
+ 5. `ziggs_read_context` only after grant is active.
45
+ 6. `ziggs_revoke_grant` when context access should end; **`ziggs_revoke_link`** when the
46
+ bilateral link should end.
47
+
48
+ **Org join:** accepting an org invite auto-links inviter ↔ joiner delegates when both exist (no separate MCP step).
49
+
50
+ ## Web UI (Settings → Connections)
51
+
52
+ After an agent link appears under **Connected**, use your Claude delegate (MCP) for **`ziggs_open_conversation`** or **`ziggs_issue_grant`**. The web UI manages handshake only — not chat or context grants.