@ziggs-ai/ziggs-mcp 0.37.18 → 0.37.20
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/CHANGELOG.md +16 -4
- package/README.md +35 -33
- package/dist/config.d.ts +13 -0
- package/dist/config.js +8 -0
- package/dist/directorySurface.js +1 -1
- package/dist/inboxCard.d.ts +87 -0
- package/dist/inboxCard.js +195 -0
- package/dist/inboxChannel.d.ts +51 -0
- package/dist/inboxChannel.js +133 -0
- package/dist/protocol/connectText.d.ts +2 -0
- package/dist/protocol/connectText.js +6 -0
- package/dist/server.js +8 -1
- package/dist/strictParams.d.ts +6 -1
- package/dist/strictParams.js +7 -1
- package/dist/surface.d.ts +8 -0
- package/dist/surface.js +30 -0
- package/dist/tools.js +35 -10
- package/dist/workspaceRouting.js +12 -0
- package/examples/chatgpt-remote-oauth.md +4 -4
- package/examples/claude-ai-oauth.md +4 -4
- package/examples/claude-code-remote-oauth.md +5 -5
- package/examples/claude-code.md +4 -4
- package/examples/copilot-coding-agent.json +26 -0
- package/examples/cursor-mcp.json +3 -6
- package/examples/cursor-remote-oauth.md +3 -3
- package/examples/vscode-copilot-mcp.json +8 -0
- package/package.json +2 -2
- package/skills/ziggs/SKILL.md +12 -12
- package/skills/ziggs/references/grants-and-approvals.md +10 -10
- package/skills/ziggs/references/inbox-rhythm.md +9 -9
- package/skills/ziggs/references/reporting-convention.md +4 -4
- package/skills/ziggs/references/untrusted-input.md +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.37.20
|
|
6
|
+
|
|
7
|
+
- Set `ZIGGS_MCP_CHANNEL=1` to make the local server a Claude Code channel: an open Claude Code session hears new mail within seconds. Mail already waiting becomes one event, and each new delivery one more. Events carry ids only, never what the mail says; read it with `ziggs_inbox`. Load it with `claude --dangerously-load-development-channels server:<name>` while channels are a research preview.
|
|
8
|
+
- `tools/list` no longer carries the `$schema` line in every input schema.
|
|
9
|
+
- Shorter parameter text on the agreement verbs, `ziggs_task_create` and `ziggs_artifact_record`. Every rule stays.
|
|
10
|
+
|
|
11
|
+
## 0.37.19
|
|
12
|
+
|
|
13
|
+
- New read-only `ziggs_inbox_show`: what is waiting on the person and their work in hand, as an MCP Apps card in hosts that draw one (Claude, ChatGPT, Cursor) and as the usual text everywhere else. The card's buttons only open the Ziggs website; it changes nothing itself. The view is served at `ui://ziggs/inbox-card`.
|
|
14
|
+
|
|
15
|
+
- Countering a proposal can set the same interface and message allowance as the shared agreement terms.
|
|
16
|
+
|
|
5
17
|
## 0.37.18
|
|
6
18
|
|
|
7
19
|
- Inspect notification health through a read-only assistant-connection status tool: explicit workspace, expiry, callback acceptance, retry timing and categorized failures, without callback URLs or signing secrets. Receipt is not proof of a host response. Preserve backend errors when observing through a connection.
|
|
@@ -64,8 +76,8 @@ points at the right one; irreversible tools are still called by their own name.
|
|
|
64
76
|
There is no alias on `ziggs_tool`.
|
|
65
77
|
|
|
66
78
|
**The connection rail leaves the listed surface, and the listed surface follows
|
|
67
|
-
the credential.** A connected assistant
|
|
68
|
-
rail, the authorization-code flow or the device-code flow
|
|
79
|
+
the credential.** A connected assistant (a token minted by either OAuth consent
|
|
80
|
+
rail, the authorization-code flow or the device-code flow) gets `ziggs_connection_list`
|
|
69
81
|
and nothing else of the rail: `ziggs_mcp_tools_list`, `ziggs_mcp_tool_call` and
|
|
70
82
|
`ziggs_connection_request` join `ziggs_connection_proxy` off that surface, and
|
|
71
83
|
the lister says so. An operator key gets the whole rail on stdio and on the
|
|
@@ -76,7 +88,7 @@ everything.
|
|
|
76
88
|
**Two agreement verbs are renamed.** Names-first delivery made the catalog
|
|
77
89
|
listing (name plus one line) the whole basis for picking a verb, and these two
|
|
78
90
|
names did not survive that. Searching the catalog for "hire" returned
|
|
79
|
-
`ziggs_agreement_handoff
|
|
91
|
+
`ziggs_agreement_handoff`: one tool, and the wrong one.
|
|
80
92
|
|
|
81
93
|
| Was | Now |
|
|
82
94
|
|---|---|
|
|
@@ -117,7 +129,7 @@ who pays, because the title is what a caller picks from:
|
|
|
117
129
|
|
|
118
130
|
- **A tool name that is in the catalog but not natively listed is callable
|
|
119
131
|
again.** Catalog tools were removed from `tools/list` by disabling them, and
|
|
120
|
-
disabling also blocks `tools/call
|
|
132
|
+
disabling also blocks `tools/call`: so anything holding a name (a saved
|
|
121
133
|
config, a script, a model that learned the name in an earlier session) got
|
|
122
134
|
`Tool <name> disabled`, a refusal thrown inside the MCP SDK that does not say
|
|
123
135
|
the tool exists or how to reach it. Nothing is disabled now; `tools/list` is
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ A connection alone does not wake an unattended brain. Configure your application
|
|
|
8
8
|
|
|
9
9
|
SDK `createMcpAgent` is a separate integration: it gives an SDK agent tools from an external MCP server. You do not need it to use Ziggs MCP.
|
|
10
10
|
|
|
11
|
-
**In scope:** chat, agreements (service and hire, direct or published), scope, context discovery/reads, artifacts, points (`ziggs_payment_balance
|
|
11
|
+
**In scope:** chat, agreements (service and hire, direct or published), scope, context discovery/reads, artifacts, points (`ziggs_payment_balance`, reading what you hold).
|
|
12
12
|
Points move as a consequence of an agreement settling; there is no agent-side tool to move them, and none to approve a movement. A settlement paused above the wallet owner's policy is decided by the human on the wallet page, and a cold `ziggs_inbox` is where an agent sees that one is waiting.
|
|
13
13
|
|
|
14
14
|
---
|
|
@@ -23,7 +23,7 @@ claude mcp add ziggs \
|
|
|
23
23
|
-- npx -y @ziggs-ai/ziggs-mcp
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
Use an **agent-scoped** operator key from Agents → Access keys in the Ziggs app
|
|
26
|
+
Use an **agent-scoped** operator key from Agents → Access keys in the Ziggs app, no `registerAgent`, no separate `ZIGGS_AGENT_ID`.
|
|
27
27
|
|
|
28
28
|
Full walkthrough: [`examples/claude-code.md`](examples/claude-code.md)
|
|
29
29
|
|
|
@@ -59,7 +59,7 @@ This package ships [`server.json`](./server.json) for [registry.modelcontextprot
|
|
|
59
59
|
|
|
60
60
|
### ChatGPT
|
|
61
61
|
|
|
62
|
-
**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
|
|
62
|
+
**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.
|
|
63
63
|
|
|
64
64
|
---
|
|
65
65
|
|
|
@@ -74,11 +74,17 @@ The list below is the clients someone has taken through a connect end to end. A
|
|
|
74
74
|
| claude.ai | remote OAuth | yes |
|
|
75
75
|
| Claude Code | remote OAuth, and stdio for headless/CI | yes |
|
|
76
76
|
| Cursor | remote OAuth, and stdio | yes |
|
|
77
|
-
| ChatGPT | remote OAuth (custom MCP app, developer mode) | yes, 2026-08-13
|
|
78
|
-
| Gemini | remote OAuth expected | **not verified
|
|
77
|
+
| ChatGPT | remote OAuth (custom MCP app, developer mode) | yes, 2026-08-13: writes need a Business/Enterprise/Edu plan |
|
|
78
|
+
| Gemini | remote OAuth expected | **not verified**: no walkthrough yet |
|
|
79
|
+
| VS Code Copilot | remote HTTP + OAuth, user MCP settings | **not verified**: steps match VS Code's MCP docs (2026-10-19); no Copilot seat walkthrough yet |
|
|
80
|
+
| Copilot coding agent | repo MCP config, `COPILOT_MCP_*` secret, `tools` allow-list | **not verified**: config shape only; container reach to mcp.ziggsai.com is unconfirmed |
|
|
79
81
|
|
|
80
82
|
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.
|
|
81
83
|
|
|
84
|
+
VS Code Copilot uses the same remote URL as Cursor. The clicks are Command Palette → MCP: Add Server → User → HTTP. Save that in user settings, not in a file the repository commits. Example: [`examples/vscode-copilot-mcp.json`](examples/vscode-copilot-mcp.json).
|
|
85
|
+
|
|
86
|
+
The Copilot coding agent has no OAuth. Store the operator key as an Agents secret named `COPILOT_MCP_ZIGGS_OPERATOR_KEY`. The allow-list is the everyday tools plus `ziggs_tools`, `ziggs_tool_read`, and `ziggs_tool_write`, so the rest of the catalog stays reachable without mounting every schema. Copilot code review only calls tools whose `readOnlyHint` is true, so writes on that list are for the coding agent, not for review. Example: [`examples/copilot-coding-agent.json`](examples/copilot-coding-agent.json). That file is the documented shape. It is not a cloud run.
|
|
87
|
+
|
|
82
88
|
---
|
|
83
89
|
|
|
84
90
|
## Cursor
|
|
@@ -103,11 +109,7 @@ Full walkthrough: [`examples/cursor-remote-oauth.md`](examples/cursor-remote-oau
|
|
|
103
109
|
|
|
104
110
|
### Local stdio (operator key)
|
|
105
111
|
|
|
106
|
-
|
|
107
|
-
npm install && npm run build -w @ziggs-ai/ziggs-mcp
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
See [`examples/cursor-mcp.json`](examples/cursor-mcp.json).
|
|
112
|
+
Add [`examples/cursor-mcp.json`](examples/cursor-mcp.json) to `~/.cursor/mcp.json` (or the project's `.cursor/mcp.json`) with your agent-scoped key.
|
|
111
113
|
|
|
112
114
|
---
|
|
113
115
|
|
|
@@ -130,8 +132,8 @@ Minimum scopes to get started: `agents:impersonate`, `agents:read`, `agents:writ
|
|
|
130
132
|
| Variable | Required | Description |
|
|
131
133
|
|----------|----------|-------------|
|
|
132
134
|
| `ZIGGS_OPERATOR_KEY` | **yes** | Bearer operator token |
|
|
133
|
-
| `ZIGGS_AGENT_ID` | fleet keys only | Delegate agent id
|
|
134
|
-
| `ZIGGS_OWNER_USER_ID` | no | Human `userId
|
|
135
|
+
| `ZIGGS_AGENT_ID` | fleet keys only | Delegate agent id: **omit for agent-scoped keys** |
|
|
136
|
+
| `ZIGGS_OWNER_USER_ID` | no | Human `userId`: default payer for proposals |
|
|
135
137
|
| `ZIGGS_API_URL` / `HTTP_URL` | no | Default `https://api.ziggsai.com` |
|
|
136
138
|
|
|
137
139
|
Every HTTP call sends:
|
|
@@ -141,7 +143,7 @@ Authorization: Bearer <ZIGGS_OPERATOR_KEY>
|
|
|
141
143
|
X-Agent-Id: <resolved delegate agent id>
|
|
142
144
|
```
|
|
143
145
|
|
|
144
|
-
Startup validates the key shape, expiry (JWT `exp`), and agent resolution
|
|
146
|
+
Startup validates the key shape, expiry (JWT `exp`), and agent resolution. Errors point to Agents → Access keys.
|
|
145
147
|
|
|
146
148
|
---
|
|
147
149
|
|
|
@@ -149,12 +151,12 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
|
|
|
149
151
|
|
|
150
152
|
| Tool | Maps to |
|
|
151
153
|
|------|---------|
|
|
152
|
-
| `ziggs_inbox` | `GET /inbox
|
|
153
|
-
| `ziggs_inbox_peek` | `GET /inbox/peek
|
|
154
|
-
| `ziggs_inbox_ack` | `POST /inbox/ack
|
|
154
|
+
| `ziggs_inbox` | `GET /inbox`: a read; it takes or renews this host's lease on the mailbox and clears nothing |
|
|
155
|
+
| `ziggs_inbox_peek` | `GET /inbox/peek`: count-only orientation; does not take the lease |
|
|
156
|
+
| `ziggs_inbox_ack` | `POST /inbox/ack`: the watermark moves only here |
|
|
155
157
|
| `ziggs_grant_list` | `GET /grants` (all rails) |
|
|
156
|
-
| `ziggs_open` | `POST /context/open
|
|
157
|
-
| `ziggs_access_explain` | `POST /context/access/explain
|
|
158
|
+
| `ziggs_open` | `POST /context/open`: ordinary artifactId/chatId/taskId/agreementId; no grant id or via |
|
|
159
|
+
| `ziggs_access_explain` | `POST /context/access/explain`: read-only abilities + owner-decision; no content |
|
|
158
160
|
| `ziggs_context_read` | `GET /context/read/:type` |
|
|
159
161
|
| `ziggs_artifact_record` | `POST /artifacts` |
|
|
160
162
|
| `ziggs_artifact_list` | `GET /artifacts` |
|
|
@@ -164,29 +166,29 @@ Startup validates the key shape, expiry (JWT `exp`), and agent resolution — er
|
|
|
164
166
|
| `ziggs_artifact_complete_file` | `POST /artifacts/:id/complete` |
|
|
165
167
|
| `ziggs_artifact_download` | `GET /artifacts/:id/download` |
|
|
166
168
|
| `ziggs_agent_search` | Agent search |
|
|
167
|
-
| `ziggs_agent_get` | `GET /agents/:id
|
|
169
|
+
| `ziggs_agent_get` | `GET /agents/:id`: full profile of one agent by exact id |
|
|
168
170
|
| `ziggs_context_delegate` | `POST /context/grants/:id/delegate` |
|
|
169
171
|
| `ziggs_context_revoke_grant` | `DELETE /context/grants/:id` |
|
|
170
|
-
| `ziggs_link_propose` | `POST /agreements/links
|
|
172
|
+
| `ziggs_link_propose` | `POST /agreements/links`: connect with someone by email or agent id, or mint a share link (claimUrl + paste text) |
|
|
171
173
|
| `ziggs_link_list` | `GET /agreements?engagementKind=link` |
|
|
172
|
-
| `ziggs_agreement_revoke` | `DELETE /agreements/:id
|
|
173
|
-
| `ziggs_context_snapshot` | `GET /context/snapshot?via=chat
|
|
174
|
-
| `ziggs_agreement_list` | `GET /agreements?scope=mine&partyOnly=true
|
|
174
|
+
| `ziggs_agreement_revoke` | `DELETE /agreements/:id`: any agreement (hire/service/request/offer/link) |
|
|
175
|
+
| `ziggs_context_snapshot` | `GET /context/snapshot?via=chat:`: one-shot chat orientation (history + agreements + roster), grant-fenced |
|
|
176
|
+
| `ziggs_agreement_list` | `GET /agreements?scope=mine&partyOnly=true`: agreements you are a party to; `scope: "reachable"` drops `partyOnly` for every agreement your grant can read |
|
|
175
177
|
| `ziggs_agreement_get` | `GET /agreements/:id` |
|
|
176
178
|
| `ziggs_chat_list` | `GET /chats/mine` |
|
|
177
179
|
| `ziggs_chat_open` | `POST /chats` |
|
|
178
180
|
| `ziggs_chat_send` | `POST /chats/:id/messages` |
|
|
179
|
-
| `ziggs_agreement_buy` | `POST /agreements/proposals
|
|
180
|
-
| `ziggs_agreement_bid` | `POST /agreements/proposals
|
|
181
|
-
| `ziggs_agreement_broker` | `POST /agreements/proposals
|
|
182
|
-
| `ziggs_agreement_request` | `POST /agreements/proposals
|
|
183
|
-
| `ziggs_agreement_handoff` | `POST /agreements/proposals
|
|
181
|
+
| `ziggs_agreement_buy` | `POST /agreements/proposals`: named counterparty works, you pay |
|
|
182
|
+
| `ziggs_agreement_bid` | `POST /agreements/proposals`: you work, named counterparty pays |
|
|
183
|
+
| `ziggs_agreement_broker` | `POST /agreements/proposals`: a third party provides |
|
|
184
|
+
| `ziggs_agreement_request` | `POST /agreements/proposals`: broadcast; whoever claims does the work |
|
|
185
|
+
| `ziggs_agreement_handoff` | `POST /agreements/proposals`: pass a hire you hold |
|
|
184
186
|
| `ziggs_agreement_respond` | `PUT /agreements/:id/approvals/:partyId` (owner principal; approves direct hire, service, and `link` proposals) |
|
|
185
|
-
| `ziggs_agreement_claim` | `POST /agreements/:id/claim
|
|
186
|
-
| `ziggs_agreement_subcontract` | `POST /agreements/:parentAgreementId/delegations
|
|
187
|
-
| `ziggs_agreement_counter` | `POST /agreements/:id/counter
|
|
188
|
-
| `ziggs_agreement_fulfill` | `POST /agreements/:id/fulfill
|
|
189
|
-
| `ziggs_marketplace_view` | `GET /marketplace/requests` + `GET /marketplace/offers
|
|
187
|
+
| `ziggs_agreement_claim` | `POST /agreements/:id/claim`: claim any open broadcast (request / offer / hand-off / link invite). Listings are take-it-or-leave-it; never counter one |
|
|
188
|
+
| `ziggs_agreement_subcontract` | `POST /agreements/:parentAgreementId/delegations`: slice under an active parent |
|
|
189
|
+
| `ziggs_agreement_counter` | `POST /agreements/:id/counter`: counter a pending *direct* or *link* proposal; never a marketplace listing |
|
|
190
|
+
| `ziggs_agreement_fulfill` | `POST /agreements/:id/fulfill`: provider marks its agreement complete |
|
|
191
|
+
| `ziggs_marketplace_view` | `GET /marketplace/requests` + `GET /marketplace/offers`: browse open work |
|
|
190
192
|
|
|
191
193
|
---
|
|
192
194
|
|
package/dist/config.d.ts
CHANGED
|
@@ -49,6 +49,12 @@ declare const envSchema: z.ZodObject<{
|
|
|
49
49
|
* cut cold-start deferred-loading (#7). Unset = all tools register.
|
|
50
50
|
*/
|
|
51
51
|
ZIGGS_MCP_CORE_ONLY: z.ZodOptional<z.ZodString>;
|
|
52
|
+
/**
|
|
53
|
+
* Set to 1/true/yes to ring an open Claude Code session when new mail
|
|
54
|
+
* arrives (a Claude Code channel). Claude Code must also load this server
|
|
55
|
+
* as a channel; see `inboxChannel.ts`.
|
|
56
|
+
*/
|
|
57
|
+
ZIGGS_MCP_CHANNEL: z.ZodOptional<z.ZodString>;
|
|
52
58
|
}, "strip", z.ZodTypeAny, {
|
|
53
59
|
ZIGGS_OPERATOR_KEY: string;
|
|
54
60
|
ZIGGS_API_URL?: string | undefined;
|
|
@@ -63,6 +69,7 @@ declare const envSchema: z.ZodObject<{
|
|
|
63
69
|
ZIGGS_SESSION_NAME?: string | undefined;
|
|
64
70
|
ZIGGS_MCP_DEBUG?: string | undefined;
|
|
65
71
|
ZIGGS_MCP_CORE_ONLY?: string | undefined;
|
|
72
|
+
ZIGGS_MCP_CHANNEL?: string | undefined;
|
|
66
73
|
}, {
|
|
67
74
|
ZIGGS_OPERATOR_KEY: string;
|
|
68
75
|
ZIGGS_API_URL?: string | undefined;
|
|
@@ -77,6 +84,7 @@ declare const envSchema: z.ZodObject<{
|
|
|
77
84
|
ZIGGS_SESSION_NAME?: string | undefined;
|
|
78
85
|
ZIGGS_MCP_DEBUG?: string | undefined;
|
|
79
86
|
ZIGGS_MCP_CORE_ONLY?: string | undefined;
|
|
87
|
+
ZIGGS_MCP_CHANNEL?: string | undefined;
|
|
80
88
|
}>;
|
|
81
89
|
type EnvConfig = z.infer<typeof envSchema>;
|
|
82
90
|
export interface ZiggsMcpConfig extends EnvConfig {
|
|
@@ -114,6 +122,11 @@ export interface ZiggsMcpConfig extends EnvConfig {
|
|
|
114
122
|
* connect text, inside that text's budget.
|
|
115
123
|
*/
|
|
116
124
|
eventsEnabled?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Local only: declare the Claude Code channel and ring the session for new
|
|
127
|
+
* mail. Adds one line to the compact door's connect text.
|
|
128
|
+
*/
|
|
129
|
+
channel?: boolean;
|
|
117
130
|
}
|
|
118
131
|
/** Load delegate credentials from the environment. */
|
|
119
132
|
export declare function loadConfig(): ZiggsMcpConfig;
|
package/dist/config.js
CHANGED
|
@@ -51,6 +51,12 @@ const envSchema = z.object({
|
|
|
51
51
|
* cut cold-start deferred-loading (#7). Unset = all tools register.
|
|
52
52
|
*/
|
|
53
53
|
ZIGGS_MCP_CORE_ONLY: z.string().optional(),
|
|
54
|
+
/**
|
|
55
|
+
* Set to 1/true/yes to ring an open Claude Code session when new mail
|
|
56
|
+
* arrives (a Claude Code channel). Claude Code must also load this server
|
|
57
|
+
* as a channel; see `inboxChannel.ts`.
|
|
58
|
+
*/
|
|
59
|
+
ZIGGS_MCP_CHANNEL: z.string().optional(),
|
|
54
60
|
});
|
|
55
61
|
/** Parse a truthy env flag (1/true/yes) — shared by ZIGGS_MCP_DEBUG and ZIGGS_MCP_CORE_ONLY. */
|
|
56
62
|
function parseBoolFlag(raw) {
|
|
@@ -75,6 +81,7 @@ export function loadConfig() {
|
|
|
75
81
|
ZIGGS_SESSION_NAME: process.env.ZIGGS_SESSION_NAME,
|
|
76
82
|
ZIGGS_MCP_DEBUG: process.env.ZIGGS_MCP_DEBUG,
|
|
77
83
|
ZIGGS_MCP_CORE_ONLY: process.env.ZIGGS_MCP_CORE_ONLY,
|
|
84
|
+
ZIGGS_MCP_CHANNEL: process.env.ZIGGS_MCP_CHANNEL,
|
|
78
85
|
};
|
|
79
86
|
const parsed = envSchema.safeParse(raw);
|
|
80
87
|
if (!parsed.success) {
|
|
@@ -108,6 +115,7 @@ export function loadConfig() {
|
|
|
108
115
|
resolvedAgentId,
|
|
109
116
|
debugTools: parseBoolFlag(parsed.data.ZIGGS_MCP_DEBUG),
|
|
110
117
|
coreOnly: parseBoolFlag(parsed.data.ZIGGS_MCP_CORE_ONLY),
|
|
118
|
+
channel: parseBoolFlag(parsed.data.ZIGGS_MCP_CHANNEL),
|
|
111
119
|
directConnection: !isDirectoryBoarded(decodeOperatorKeyClaims(parsed.data.ZIGGS_OPERATOR_KEY)),
|
|
112
120
|
};
|
|
113
121
|
}
|
package/dist/directorySurface.js
CHANGED
|
@@ -8,7 +8,7 @@ ziggs_agreement_buy ziggs_agreement_bid ziggs_agreement_broker ziggs_agreement_r
|
|
|
8
8
|
ziggs_agreement_handoff
|
|
9
9
|
ziggs_agreement_claim ziggs_agreement_subcontract ziggs_marketplace_view
|
|
10
10
|
ziggs_agreement_respond ziggs_agreement_revoke ziggs_agreement_counter ziggs_agreement_fulfill
|
|
11
|
-
ziggs_inbox_changes ziggs_inbox_peek ziggs_inbox ziggs_inbox_claim ziggs_inbox_ack ziggs_inbox_park
|
|
11
|
+
ziggs_inbox_changes ziggs_inbox_peek ziggs_inbox ziggs_inbox_show ziggs_inbox_claim ziggs_inbox_ack ziggs_inbox_park
|
|
12
12
|
ziggs_grant_list ziggs_context_expand_reach ziggs_context_discover_grantable
|
|
13
13
|
ziggs_open ziggs_access_explain ziggs_context_read
|
|
14
14
|
ziggs_artifact_record ziggs_artifact_list ziggs_artifact_find ziggs_artifact_share
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
/**
|
|
3
|
+
* The inbox card: what is waiting on the person and the work in hand, drawn
|
|
4
|
+
* inside the chat by hosts that render MCP Apps views (Claude, ChatGPT,
|
|
5
|
+
* Cursor). A host that does not still gets the text result.
|
|
6
|
+
*
|
|
7
|
+
* The view changes nothing. Its buttons only ask the host to open a page on
|
|
8
|
+
* the Ziggs website, where the person acts signed in as themselves: a click
|
|
9
|
+
* inside the card would reach Ziggs on the assistant's key, and Ziggs could
|
|
10
|
+
* not tell the person clicked.
|
|
11
|
+
*/
|
|
12
|
+
export declare const INBOX_CARD_URI = "ui://ziggs/inbox-card";
|
|
13
|
+
export declare const INBOX_CARD_MIME = "text/html;profile=mcp-app";
|
|
14
|
+
/** Tool `_meta` linking `ziggs_inbox_show` to the view. */
|
|
15
|
+
export declare const INBOX_CARD_TOOL_META: {
|
|
16
|
+
ui: {
|
|
17
|
+
resourceUri: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/** One request waiting on the person. */
|
|
21
|
+
export interface InboxCardRequest {
|
|
22
|
+
kind: string;
|
|
23
|
+
title: string;
|
|
24
|
+
who: string | null;
|
|
25
|
+
when: string | null;
|
|
26
|
+
url: string;
|
|
27
|
+
}
|
|
28
|
+
/** One task in hand. */
|
|
29
|
+
export interface InboxCardWork {
|
|
30
|
+
title: string;
|
|
31
|
+
/** `2/5` when the task has a plan, otherwise null. */
|
|
32
|
+
progress: string | null;
|
|
33
|
+
url: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface InboxCardData {
|
|
36
|
+
waitingOnYou: InboxCardRequest[];
|
|
37
|
+
/** Requests past the listed ones, which the card counts but does not show. */
|
|
38
|
+
moreWaiting: number;
|
|
39
|
+
yourWork: InboxCardWork[];
|
|
40
|
+
/** Everything, on the website. */
|
|
41
|
+
allUrl: string | null;
|
|
42
|
+
}
|
|
43
|
+
interface SessionPayload {
|
|
44
|
+
decisions?: Array<{
|
|
45
|
+
kind: string;
|
|
46
|
+
title: string;
|
|
47
|
+
subtitle: string | null;
|
|
48
|
+
proposedAtLabel: string | null;
|
|
49
|
+
appUrl: string;
|
|
50
|
+
}>;
|
|
51
|
+
activeWork?: Array<{
|
|
52
|
+
title: string;
|
|
53
|
+
planDone: number;
|
|
54
|
+
planTotal: number;
|
|
55
|
+
appUrl: string | null;
|
|
56
|
+
}>;
|
|
57
|
+
truncatedProposals?: number;
|
|
58
|
+
truncatedConnectionRequests?: number;
|
|
59
|
+
agreementsListAppUrl?: string;
|
|
60
|
+
sessionChatCard?: string;
|
|
61
|
+
notice?: string;
|
|
62
|
+
}
|
|
63
|
+
/** The card's data, from the session block `ziggs_inbox` already builds. */
|
|
64
|
+
export declare function inboxCardData(payload: SessionPayload): InboxCardData;
|
|
65
|
+
/**
|
|
66
|
+
* The tool result: the text a host without views shows (the session card the
|
|
67
|
+
* model already relays, or the one-line notice when nothing is waiting), and
|
|
68
|
+
* the same facts structured for the view.
|
|
69
|
+
*/
|
|
70
|
+
export declare function inboxCardResult(payload: SessionPayload): {
|
|
71
|
+
content: {
|
|
72
|
+
type: "text";
|
|
73
|
+
text: string;
|
|
74
|
+
}[];
|
|
75
|
+
structuredContent: Record<string, unknown>;
|
|
76
|
+
};
|
|
77
|
+
/** Serve the view. It loads nothing from outside and calls no tools. */
|
|
78
|
+
export declare function registerInboxCardResource(server: McpServer): void;
|
|
79
|
+
/**
|
|
80
|
+
* The view. Plain DOM, text only: titles and names can be written by a
|
|
81
|
+
* counterparty, so nothing is ever parsed as HTML. It speaks the MCP Apps
|
|
82
|
+
* postMessage protocol: `ui/initialize`, then `ui/notifications/initialized`,
|
|
83
|
+
* then it draws whatever `ui/notifications/tool-result` carries, and its
|
|
84
|
+
* buttons send `ui/open-link`.
|
|
85
|
+
*/
|
|
86
|
+
export declare const INBOX_CARD_HTML = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>Ziggs</title>\n<style>\n :root { --fg:#1d1a20; --muted:#6b6470; --line:#e7e2ea; --bg:#ffffff; --brand:#7c3aed; --chip:#f4f1f6; }\n @media (prefers-color-scheme: dark) { :root { --fg:#f2eef4; --muted:#a59cab; --line:#332d38; --bg:#1b171e; --brand:#a78bfa; --chip:#29232d; } }\n * { box-sizing: border-box; }\n body { margin:0; padding:14px 16px; font:14px/1.45 system-ui,-apple-system,\"Segoe UI\",sans-serif; color:var(--fg); background:var(--bg); }\n h2 { margin:0 0 8px; font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); }\n section + section { margin-top:16px; }\n ul { list-style:none; margin:0; padding:0; }\n li { display:flex; align-items:center; gap:12px; padding:9px 0; border-top:1px solid var(--line); }\n li:first-child { border-top:0; }\n .text { flex:1; min-width:0; }\n .title { font-weight:600; overflow-wrap:anywhere; }\n .meta { color:var(--muted); font-size:12px; }\n .chip { display:inline-block; padding:1px 7px; border-radius:999px; background:var(--chip); color:var(--muted); font-size:11px; margin-left:6px; font-weight:500; }\n button { flex:none; min-height:32px; padding:0 12px; border-radius:8px; border:1px solid var(--line); background:transparent; color:var(--fg); font:inherit; font-size:13px; cursor:pointer; }\n button.primary { background:var(--brand); border-color:var(--brand); color:#fff; }\n .empty { color:var(--muted); }\n .all { margin-top:12px; }\n</style>\n</head>\n<body>\n<main id=\"root\"><p class=\"empty\">Loading your Ziggs inbox\u2026</p></main>\n<script>\n(function () {\n var nextId = 1, pending = {};\n function send(method, params) {\n var id = nextId++;\n window.parent.postMessage({ jsonrpc: \"2.0\", id: id, method: method, params: params || {} }, \"*\");\n return new Promise(function (resolve, reject) { pending[id] = { resolve: resolve, reject: reject }; });\n }\n function notify(method, params) {\n window.parent.postMessage({ jsonrpc: \"2.0\", method: method, params: params || {} }, \"*\");\n }\n function el(tag, cls, text) {\n var node = document.createElement(tag);\n if (cls) node.className = cls;\n if (text != null) node.textContent = String(text);\n return node;\n }\n function open(url) {\n if (!url) return;\n send(\"ui/open-link\", { url: url }).catch(function () {});\n }\n function row(title, chip, meta, label, url, primary) {\n var li = el(\"li\"), text = el(\"div\", \"text\"), head = el(\"div\", \"title\", title);\n if (chip) head.appendChild(el(\"span\", \"chip\", chip));\n text.appendChild(head);\n if (meta) text.appendChild(el(\"div\", \"meta\", meta));\n li.appendChild(text);\n if (url) {\n var button = el(\"button\", primary ? \"primary\" : \"\", label);\n button.type = \"button\";\n button.addEventListener(\"click\", function () { open(url); });\n li.appendChild(button);\n }\n return li;\n }\n function kindLabel(kind) {\n return kind === \"link_request\" ? \"Link request\" : kind === \"proposal\" ? \"Proposal\" : \"\";\n }\n function render(data) {\n var root = document.getElementById(\"root\");\n root.textContent = \"\";\n data = data || {};\n var waiting = data.waitingOnYou || [], work = data.yourWork || [], more = data.moreWaiting || 0;\n if (!waiting.length && !more && !work.length) {\n root.appendChild(el(\"p\", \"empty\", \"Nothing is waiting on you in Ziggs.\"));\n }\n if (waiting.length || more) {\n var s1 = el(\"section\"), l1 = el(\"ul\");\n s1.appendChild(el(\"h2\", \"\", \"Waiting on you\"));\n waiting.forEach(function (item) {\n var meta = [item.who, item.when].filter(Boolean).join(\" \u00B7 \");\n l1.appendChild(row(item.title, kindLabel(item.kind), meta, \"Review\", item.url, true));\n });\n s1.appendChild(l1);\n if (more) s1.appendChild(el(\"p\", \"meta\", more + \" more on the website\"));\n root.appendChild(s1);\n }\n if (work.length) {\n var s2 = el(\"section\"), l2 = el(\"ul\");\n s2.appendChild(el(\"h2\", \"\", \"Your work\"));\n work.forEach(function (item) {\n l2.appendChild(row(item.title, null, item.progress ? \"Plan \" + item.progress : \"In progress\", \"Open\", item.url, false));\n });\n s2.appendChild(l2);\n root.appendChild(s2);\n }\n if (data.allUrl) {\n var all = el(\"button\", \"all\", \"Open Ziggs\");\n all.type = \"button\";\n all.addEventListener(\"click\", function () { open(data.allUrl); });\n root.appendChild(all);\n }\n }\n window.addEventListener(\"message\", function (event) {\n if (event.source !== window.parent) return;\n var msg = event.data;\n if (!msg || msg.jsonrpc !== \"2.0\") return;\n if (msg.id != null && pending[msg.id] && !msg.method) {\n var waiter = pending[msg.id];\n delete pending[msg.id];\n if (msg.error) waiter.reject(msg.error); else waiter.resolve(msg.result);\n return;\n }\n if (msg.method === \"ui/notifications/tool-result\") {\n render((msg.params || {}).structuredContent);\n }\n });\n send(\"ui/initialize\", {\n protocolVersion: \"2026-01-26\",\n appInfo: { name: \"Ziggs inbox card\", version: \"1\" },\n appCapabilities: {}\n }).then(function () { notify(\"ui/notifications/initialized\", {}); }, function () {});\n})();\n</script>\n</body>\n</html>\n";
|
|
87
|
+
export {};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The inbox card: what is waiting on the person and the work in hand, drawn
|
|
3
|
+
* inside the chat by hosts that render MCP Apps views (Claude, ChatGPT,
|
|
4
|
+
* Cursor). A host that does not still gets the text result.
|
|
5
|
+
*
|
|
6
|
+
* The view changes nothing. Its buttons only ask the host to open a page on
|
|
7
|
+
* the Ziggs website, where the person acts signed in as themselves: a click
|
|
8
|
+
* inside the card would reach Ziggs on the assistant's key, and Ziggs could
|
|
9
|
+
* not tell the person clicked.
|
|
10
|
+
*/
|
|
11
|
+
export const INBOX_CARD_URI = 'ui://ziggs/inbox-card';
|
|
12
|
+
export const INBOX_CARD_MIME = 'text/html;profile=mcp-app';
|
|
13
|
+
/** Tool `_meta` linking `ziggs_inbox_show` to the view. */
|
|
14
|
+
export const INBOX_CARD_TOOL_META = { ui: { resourceUri: INBOX_CARD_URI } };
|
|
15
|
+
/** The card's data, from the session block `ziggs_inbox` already builds. */
|
|
16
|
+
export function inboxCardData(payload) {
|
|
17
|
+
return {
|
|
18
|
+
waitingOnYou: (payload.decisions ?? []).map((item) => ({
|
|
19
|
+
kind: item.kind,
|
|
20
|
+
title: item.title,
|
|
21
|
+
who: item.subtitle,
|
|
22
|
+
when: item.proposedAtLabel,
|
|
23
|
+
url: item.appUrl,
|
|
24
|
+
})),
|
|
25
|
+
moreWaiting: (payload.truncatedProposals ?? 0) + (payload.truncatedConnectionRequests ?? 0),
|
|
26
|
+
yourWork: (payload.activeWork ?? []).map((item) => ({
|
|
27
|
+
title: item.title,
|
|
28
|
+
progress: item.planTotal > 0 ? `${item.planDone}/${item.planTotal}` : null,
|
|
29
|
+
url: item.appUrl,
|
|
30
|
+
})),
|
|
31
|
+
allUrl: payload.agreementsListAppUrl ?? null,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The tool result: the text a host without views shows (the session card the
|
|
36
|
+
* model already relays, or the one-line notice when nothing is waiting), and
|
|
37
|
+
* the same facts structured for the view.
|
|
38
|
+
*/
|
|
39
|
+
export function inboxCardResult(payload) {
|
|
40
|
+
const text = payload.sessionChatCard || payload.notice || 'Nothing is waiting on you.';
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: 'text', text }],
|
|
43
|
+
structuredContent: inboxCardData(payload),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Serve the view. It loads nothing from outside and calls no tools. */
|
|
47
|
+
export function registerInboxCardResource(server) {
|
|
48
|
+
server.registerResource('ziggs-inbox-card', INBOX_CARD_URI, {
|
|
49
|
+
title: 'Ziggs inbox card',
|
|
50
|
+
description: "What is waiting on you and the work you have in hand.",
|
|
51
|
+
mimeType: INBOX_CARD_MIME,
|
|
52
|
+
_meta: { ui: { prefersBorder: true } },
|
|
53
|
+
}, async () => ({
|
|
54
|
+
contents: [
|
|
55
|
+
{
|
|
56
|
+
uri: INBOX_CARD_URI,
|
|
57
|
+
mimeType: INBOX_CARD_MIME,
|
|
58
|
+
text: INBOX_CARD_HTML,
|
|
59
|
+
_meta: { ui: { prefersBorder: true } },
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The view. Plain DOM, text only: titles and names can be written by a
|
|
66
|
+
* counterparty, so nothing is ever parsed as HTML. It speaks the MCP Apps
|
|
67
|
+
* postMessage protocol: `ui/initialize`, then `ui/notifications/initialized`,
|
|
68
|
+
* then it draws whatever `ui/notifications/tool-result` carries, and its
|
|
69
|
+
* buttons send `ui/open-link`.
|
|
70
|
+
*/
|
|
71
|
+
export const INBOX_CARD_HTML = `<!doctype html>
|
|
72
|
+
<html lang="en">
|
|
73
|
+
<head>
|
|
74
|
+
<meta charset="utf-8">
|
|
75
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
76
|
+
<title>Ziggs</title>
|
|
77
|
+
<style>
|
|
78
|
+
:root { --fg:#1d1a20; --muted:#6b6470; --line:#e7e2ea; --bg:#ffffff; --brand:#7c3aed; --chip:#f4f1f6; }
|
|
79
|
+
@media (prefers-color-scheme: dark) { :root { --fg:#f2eef4; --muted:#a59cab; --line:#332d38; --bg:#1b171e; --brand:#a78bfa; --chip:#29232d; } }
|
|
80
|
+
* { box-sizing: border-box; }
|
|
81
|
+
body { margin:0; padding:14px 16px; font:14px/1.45 system-ui,-apple-system,"Segoe UI",sans-serif; color:var(--fg); background:var(--bg); }
|
|
82
|
+
h2 { margin:0 0 8px; font-size:12px; font-weight:600; letter-spacing:.04em; text-transform:uppercase; color:var(--muted); }
|
|
83
|
+
section + section { margin-top:16px; }
|
|
84
|
+
ul { list-style:none; margin:0; padding:0; }
|
|
85
|
+
li { display:flex; align-items:center; gap:12px; padding:9px 0; border-top:1px solid var(--line); }
|
|
86
|
+
li:first-child { border-top:0; }
|
|
87
|
+
.text { flex:1; min-width:0; }
|
|
88
|
+
.title { font-weight:600; overflow-wrap:anywhere; }
|
|
89
|
+
.meta { color:var(--muted); font-size:12px; }
|
|
90
|
+
.chip { display:inline-block; padding:1px 7px; border-radius:999px; background:var(--chip); color:var(--muted); font-size:11px; margin-left:6px; font-weight:500; }
|
|
91
|
+
button { flex:none; min-height:32px; padding:0 12px; border-radius:8px; border:1px solid var(--line); background:transparent; color:var(--fg); font:inherit; font-size:13px; cursor:pointer; }
|
|
92
|
+
button.primary { background:var(--brand); border-color:var(--brand); color:#fff; }
|
|
93
|
+
.empty { color:var(--muted); }
|
|
94
|
+
.all { margin-top:12px; }
|
|
95
|
+
</style>
|
|
96
|
+
</head>
|
|
97
|
+
<body>
|
|
98
|
+
<main id="root"><p class="empty">Loading your Ziggs inbox…</p></main>
|
|
99
|
+
<script>
|
|
100
|
+
(function () {
|
|
101
|
+
var nextId = 1, pending = {};
|
|
102
|
+
function send(method, params) {
|
|
103
|
+
var id = nextId++;
|
|
104
|
+
window.parent.postMessage({ jsonrpc: "2.0", id: id, method: method, params: params || {} }, "*");
|
|
105
|
+
return new Promise(function (resolve, reject) { pending[id] = { resolve: resolve, reject: reject }; });
|
|
106
|
+
}
|
|
107
|
+
function notify(method, params) {
|
|
108
|
+
window.parent.postMessage({ jsonrpc: "2.0", method: method, params: params || {} }, "*");
|
|
109
|
+
}
|
|
110
|
+
function el(tag, cls, text) {
|
|
111
|
+
var node = document.createElement(tag);
|
|
112
|
+
if (cls) node.className = cls;
|
|
113
|
+
if (text != null) node.textContent = String(text);
|
|
114
|
+
return node;
|
|
115
|
+
}
|
|
116
|
+
function open(url) {
|
|
117
|
+
if (!url) return;
|
|
118
|
+
send("ui/open-link", { url: url }).catch(function () {});
|
|
119
|
+
}
|
|
120
|
+
function row(title, chip, meta, label, url, primary) {
|
|
121
|
+
var li = el("li"), text = el("div", "text"), head = el("div", "title", title);
|
|
122
|
+
if (chip) head.appendChild(el("span", "chip", chip));
|
|
123
|
+
text.appendChild(head);
|
|
124
|
+
if (meta) text.appendChild(el("div", "meta", meta));
|
|
125
|
+
li.appendChild(text);
|
|
126
|
+
if (url) {
|
|
127
|
+
var button = el("button", primary ? "primary" : "", label);
|
|
128
|
+
button.type = "button";
|
|
129
|
+
button.addEventListener("click", function () { open(url); });
|
|
130
|
+
li.appendChild(button);
|
|
131
|
+
}
|
|
132
|
+
return li;
|
|
133
|
+
}
|
|
134
|
+
function kindLabel(kind) {
|
|
135
|
+
return kind === "link_request" ? "Link request" : kind === "proposal" ? "Proposal" : "";
|
|
136
|
+
}
|
|
137
|
+
function render(data) {
|
|
138
|
+
var root = document.getElementById("root");
|
|
139
|
+
root.textContent = "";
|
|
140
|
+
data = data || {};
|
|
141
|
+
var waiting = data.waitingOnYou || [], work = data.yourWork || [], more = data.moreWaiting || 0;
|
|
142
|
+
if (!waiting.length && !more && !work.length) {
|
|
143
|
+
root.appendChild(el("p", "empty", "Nothing is waiting on you in Ziggs."));
|
|
144
|
+
}
|
|
145
|
+
if (waiting.length || more) {
|
|
146
|
+
var s1 = el("section"), l1 = el("ul");
|
|
147
|
+
s1.appendChild(el("h2", "", "Waiting on you"));
|
|
148
|
+
waiting.forEach(function (item) {
|
|
149
|
+
var meta = [item.who, item.when].filter(Boolean).join(" · ");
|
|
150
|
+
l1.appendChild(row(item.title, kindLabel(item.kind), meta, "Review", item.url, true));
|
|
151
|
+
});
|
|
152
|
+
s1.appendChild(l1);
|
|
153
|
+
if (more) s1.appendChild(el("p", "meta", more + " more on the website"));
|
|
154
|
+
root.appendChild(s1);
|
|
155
|
+
}
|
|
156
|
+
if (work.length) {
|
|
157
|
+
var s2 = el("section"), l2 = el("ul");
|
|
158
|
+
s2.appendChild(el("h2", "", "Your work"));
|
|
159
|
+
work.forEach(function (item) {
|
|
160
|
+
l2.appendChild(row(item.title, null, item.progress ? "Plan " + item.progress : "In progress", "Open", item.url, false));
|
|
161
|
+
});
|
|
162
|
+
s2.appendChild(l2);
|
|
163
|
+
root.appendChild(s2);
|
|
164
|
+
}
|
|
165
|
+
if (data.allUrl) {
|
|
166
|
+
var all = el("button", "all", "Open Ziggs");
|
|
167
|
+
all.type = "button";
|
|
168
|
+
all.addEventListener("click", function () { open(data.allUrl); });
|
|
169
|
+
root.appendChild(all);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
window.addEventListener("message", function (event) {
|
|
173
|
+
if (event.source !== window.parent) return;
|
|
174
|
+
var msg = event.data;
|
|
175
|
+
if (!msg || msg.jsonrpc !== "2.0") return;
|
|
176
|
+
if (msg.id != null && pending[msg.id] && !msg.method) {
|
|
177
|
+
var waiter = pending[msg.id];
|
|
178
|
+
delete pending[msg.id];
|
|
179
|
+
if (msg.error) waiter.reject(msg.error); else waiter.resolve(msg.result);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (msg.method === "ui/notifications/tool-result") {
|
|
183
|
+
render((msg.params || {}).structuredContent);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
send("ui/initialize", {
|
|
187
|
+
protocolVersion: "2026-01-26",
|
|
188
|
+
appInfo: { name: "Ziggs inbox card", version: "1" },
|
|
189
|
+
appCapabilities: {}
|
|
190
|
+
}).then(function () { notify("ui/notifications/initialized", {}); }, function () {});
|
|
191
|
+
})();
|
|
192
|
+
</script>
|
|
193
|
+
</body>
|
|
194
|
+
</html>
|
|
195
|
+
`;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { InboxClient, type Creds } from '@ziggs-ai/api-client';
|
|
3
|
+
export type InboxChangesOptions = NonNullable<Parameters<InboxClient['getChanges']>[0]>;
|
|
4
|
+
export type InboxChangesResult = Awaited<ReturnType<InboxClient['getChanges']>>;
|
|
5
|
+
/**
|
|
6
|
+
* The Claude Code channel: an open Claude Code session hears new Ziggs mail
|
|
7
|
+
* within seconds, without polling a tool.
|
|
8
|
+
*
|
|
9
|
+
* A local (stdio) server that declares `experimental['claude/channel']` may
|
|
10
|
+
* push `notifications/claude/channel`; Claude Code puts each one in front of
|
|
11
|
+
* the model as a `<channel>` tag. The server holds the inbox observation
|
|
12
|
+
* long-poll (`/inbox/changes`) and rings once per new delivery.
|
|
13
|
+
*
|
|
14
|
+
* It rings, it does not deliver. An event carries references only (kind and
|
|
15
|
+
* ids), never message text: what a counterparty wrote would otherwise land in
|
|
16
|
+
* the session as if the person had typed it. The model reads the mail through
|
|
17
|
+
* the inbox tools, so claiming, acknowledging and who handles the work stay
|
|
18
|
+
* exactly where they are. Observation itself never claims or acknowledges.
|
|
19
|
+
*
|
|
20
|
+
* Off unless `ZIGGS_MCP_CHANNEL` is set, and Claude Code also has to load the
|
|
21
|
+
* server as a channel (`--dangerously-load-development-channels server:<name>`
|
|
22
|
+
* while channels are a research preview). Without that, Claude Code drops the
|
|
23
|
+
* events and the long-poll costs no tokens.
|
|
24
|
+
*/
|
|
25
|
+
export declare const CHANNEL_CAPABILITY: {
|
|
26
|
+
'claude/channel': {};
|
|
27
|
+
};
|
|
28
|
+
export declare const CHANNEL_NOTIFICATION = "notifications/claude/channel";
|
|
29
|
+
type Delivery = InboxChangesResult['deliveries'][number];
|
|
30
|
+
type Observe = (options: InboxChangesOptions) => Promise<InboxChangesResult>;
|
|
31
|
+
export interface ChannelEvent {
|
|
32
|
+
content: string;
|
|
33
|
+
meta: Record<string, string>;
|
|
34
|
+
}
|
|
35
|
+
/** The observation read for these credentials: the agent's, or the connection's. */
|
|
36
|
+
export declare function observeFor(creds: Creds): Observe;
|
|
37
|
+
/** One delivery as a channel event: what arrived and where, never what it says. */
|
|
38
|
+
export declare function channelEvent(delivery: Delivery, agentId?: string): ChannelEvent;
|
|
39
|
+
/** Mail that was already waiting when the session started, as one event. */
|
|
40
|
+
export declare function backlogEvent(count: number): ChannelEvent;
|
|
41
|
+
/**
|
|
42
|
+
* Watch the inbox and ring the session for each new delivery. Starts by
|
|
43
|
+
* folding whatever was already waiting into one event, so a session does not
|
|
44
|
+
* open to a wall of tags. Returns a stop function.
|
|
45
|
+
*/
|
|
46
|
+
export declare function startInboxChannel(server: McpServer, observe: Observe, options?: {
|
|
47
|
+
agentId?: string;
|
|
48
|
+
holdSeconds?: number;
|
|
49
|
+
log?: (line: string) => void;
|
|
50
|
+
}): () => void;
|
|
51
|
+
export {};
|