@sellable/mcp 0.1.752 → 0.1.754

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.
@@ -0,0 +1,322 @@
1
+ import type { AgentIntegrationRequestContext } from "../agent-integration-request-context.js";
2
+ /**
3
+ * The THREE provider-neutral managed-integration tools.
4
+ *
5
+ * ## PROVIDER TOOL NAMES ARE ARGUMENTS. That is the architecture in one sentence.
6
+ *
7
+ * `gmail-find-email` is a `toolName` STRING passed at call time. Nothing writes it
8
+ * into a config file, a Hermes profile, a `tools.include` list, or an MCP tool name.
9
+ * A source scan in `tests/mcp/integration-tools.test.ts` asserts no provider action
10
+ * key appears under `packages/sellable-install/**` or in any generated config, so
11
+ * the deleted materialization design cannot creep back.
12
+ *
13
+ * Three problems disappear from this phase as a consequence, worth recording so
14
+ * nobody re-solves them: the `profile-materializer` tool-name regex (no hyphens,
15
+ * 128 chars, sorted, duplicate-free) never has to accept a hyphenated provider key;
16
+ * the global-flat-list mutation hazard has no list to mutate; and the context-bloat
17
+ * problem of materializing N provider tools into every prompt does not exist.
18
+ *
19
+ * ## THE ACTOR IS NOT A TOOL INPUT
20
+ *
21
+ * No `inputSchema` here carries a requester, a channel, or an actor field, and this
22
+ * module DESIGNS NO ACTOR MECHANISM — it CONSUMES plan 141-04 T2's. The trusted
23
+ * triple arrives from `boundary.context` (which the MCP context proxy injects only
24
+ * after REJECTING any model-supplied `_meta`) and `server.ts` passes it here, where
25
+ * it becomes an explicit versioned BODY field the route re-verifies against
26
+ * `WorkspaceSlackIdentity`. A model-supplied actor claim is worthless, so the model
27
+ * is never offered a place to put one.
28
+ *
29
+ * ## WHY `registry.ts` AND `server.ts` ARE SHARED, NOT OWNED
30
+ *
31
+ * Phase 142 plan 142-05 adds three CONNECT tools to the same two files. Phase 141
32
+ * merges first, so 142's edits are additive to this plan's landed set. Neither plan
33
+ * may claim exclusive ownership of `registry.ts` or `server.ts`, and neither may
34
+ * reorder the other's entries.
35
+ *
36
+ * Registering three tools also grows `SELLABLE_MCP_TOOL_NAMES`, and
37
+ * `src/lib/sellable-agent/worker-broker.ts:832-837` computes `toolInclude` from it
38
+ * into the runtime revision manifest — so `toolsHash` moves ONCE. That is expected:
39
+ * plan 141-04 T1's drift test classifies a moved `toolsHash` as `RELOAD_MCP`, a
40
+ * one-time installer-version event exercised by plan 141-09's canary. It is NOT a
41
+ * per-connect event; 141-04's zero-drift proof is about connects.
42
+ *
43
+ * Release versioning is not tool registration: `mcp/sellable/package.json` is
44
+ * UNTOUCHED here and plan 141-09 T2 owns the `@sellable/mcp` WIP version.
45
+ */
46
+ /**
47
+ * THE PUBLISHED RESULT ENVELOPE — a cross-phase contract, not prose.
48
+ *
49
+ * Plan 142-05 T1 carries `SA142-52`, which requires the connect tools' envelopes to
50
+ * MIRROR this one key-for-key, and instructs its executor to STOP and report rather
51
+ * than guess if the contract "is not importable from the MCP package". So it is
52
+ * exported, frozen, and structurally asserted against every handler result.
53
+ *
54
+ * The key set is STABLE across success and failure — `error` and `guidance` are
55
+ * present as `null` rather than omitted — which is what makes a structural
56
+ * comparison possible at all.
57
+ *
58
+ * | key | meaning |
59
+ * | ------------- | -------------------------------------------------------------- |
60
+ * | `ok` | the success/failure DISCRIMINATOR |
61
+ * | `outcome` | snake_case: `ok`, `refused`, `approval_required`, `catalog_incomplete`, `not_connected`, `upstream_degraded` |
62
+ * | `attribution` | snake_case: `provider`, `configuration`, `policy`, `sellable` |
63
+ * | `error` | the snake_case code, forwarded VERBATIM from the route |
64
+ * | `guidance` | one human sentence, or `null` |
65
+ * | `result` | the payload |
66
+ *
67
+ * `approval_required` and `catalog_incomplete` are RESULTS inside this envelope, not
68
+ * errors: the Agent must be able to explain them, not just fail.
69
+ */
70
+ export declare const INTEGRATIONS_RESULT_ENVELOPE_KEYS: readonly ["ok", "outcome", "attribution", "error", "guidance", "result"];
71
+ export type IntegrationsResultEnvelopeKey = (typeof INTEGRATIONS_RESULT_ENVELOPE_KEYS)[number];
72
+ export type IntegrationsResultEnvelope = {
73
+ ok: boolean;
74
+ outcome: string;
75
+ attribution: string;
76
+ error: string | null;
77
+ guidance: string | null;
78
+ result: unknown;
79
+ };
80
+ export declare const integrationsToolDefinitions: ({
81
+ name: string;
82
+ description: string;
83
+ inputSchema: {
84
+ type: string;
85
+ properties: {
86
+ readonly appSlug: {
87
+ readonly type: "string";
88
+ readonly minLength: 1;
89
+ readonly maxLength: 64;
90
+ readonly description: "The connected app, e.g. 'gmail'. Use this when the workspace has exactly one connected account for the app; if it has more than one, the call is refused as ambiguous and you must pass accountLabel instead.";
91
+ };
92
+ readonly bindingId: {
93
+ readonly type: "string";
94
+ readonly minLength: 1;
95
+ readonly maxLength: 64;
96
+ readonly description: "An exact integration binding id, as returned by a previous integrations_list_tools call.";
97
+ };
98
+ readonly accountLabel: {
99
+ readonly type: "string";
100
+ readonly minLength: 1;
101
+ readonly maxLength: 120;
102
+ readonly description: "The human label the user gave the connected account. This is the ONLY way to tell two connections over the same inbox apart, because the provider's own account name can be byte-identical for both.";
103
+ };
104
+ };
105
+ required: never[];
106
+ additionalProperties: boolean;
107
+ };
108
+ outputSchema: {
109
+ readonly type: "object";
110
+ readonly properties: {
111
+ readonly ok: {
112
+ readonly type: "boolean";
113
+ };
114
+ readonly outcome: {
115
+ readonly type: "string";
116
+ readonly enum: readonly ["ok", "refused", "approval_required", "catalog_incomplete", "not_connected", "upstream_degraded"];
117
+ };
118
+ readonly attribution: {
119
+ readonly type: "string";
120
+ readonly enum: readonly ["provider", "configuration", "policy", "sellable"];
121
+ };
122
+ readonly error: {
123
+ readonly type: readonly ["string", "null"];
124
+ };
125
+ readonly guidance: {
126
+ readonly type: readonly ["string", "null"];
127
+ };
128
+ readonly result: {};
129
+ };
130
+ readonly required: readonly ["ok", "outcome", "attribution", "error", "guidance", "result"];
131
+ readonly additionalProperties: false;
132
+ };
133
+ annotations: {
134
+ title: string;
135
+ readOnlyHint: boolean;
136
+ destructiveHint: boolean;
137
+ idempotentHint: boolean;
138
+ openWorldHint: boolean;
139
+ };
140
+ } | {
141
+ name: string;
142
+ description: string;
143
+ inputSchema: {
144
+ type: string;
145
+ properties: {
146
+ toolName: {
147
+ type: string;
148
+ minLength: number;
149
+ maxLength: number;
150
+ description: string;
151
+ };
152
+ propName: {
153
+ type: string;
154
+ minLength: number;
155
+ maxLength: number;
156
+ description: string;
157
+ };
158
+ optionQuery: {
159
+ type: string;
160
+ minLength: number;
161
+ maxLength: number;
162
+ description: string;
163
+ };
164
+ appSlug: {
165
+ readonly type: "string";
166
+ readonly minLength: 1;
167
+ readonly maxLength: 64;
168
+ readonly description: "The connected app, e.g. 'gmail'. Use this when the workspace has exactly one connected account for the app; if it has more than one, the call is refused as ambiguous and you must pass accountLabel instead.";
169
+ };
170
+ bindingId: {
171
+ readonly type: "string";
172
+ readonly minLength: 1;
173
+ readonly maxLength: 64;
174
+ readonly description: "An exact integration binding id, as returned by a previous integrations_list_tools call.";
175
+ };
176
+ accountLabel: {
177
+ readonly type: "string";
178
+ readonly minLength: 1;
179
+ readonly maxLength: 120;
180
+ readonly description: "The human label the user gave the connected account. This is the ONLY way to tell two connections over the same inbox apart, because the provider's own account name can be byte-identical for both.";
181
+ };
182
+ };
183
+ required: string[];
184
+ additionalProperties: boolean;
185
+ };
186
+ outputSchema: {
187
+ readonly type: "object";
188
+ readonly properties: {
189
+ readonly ok: {
190
+ readonly type: "boolean";
191
+ };
192
+ readonly outcome: {
193
+ readonly type: "string";
194
+ readonly enum: readonly ["ok", "refused", "approval_required", "catalog_incomplete", "not_connected", "upstream_degraded"];
195
+ };
196
+ readonly attribution: {
197
+ readonly type: "string";
198
+ readonly enum: readonly ["provider", "configuration", "policy", "sellable"];
199
+ };
200
+ readonly error: {
201
+ readonly type: readonly ["string", "null"];
202
+ };
203
+ readonly guidance: {
204
+ readonly type: readonly ["string", "null"];
205
+ };
206
+ readonly result: {};
207
+ };
208
+ readonly required: readonly ["ok", "outcome", "attribution", "error", "guidance", "result"];
209
+ readonly additionalProperties: false;
210
+ };
211
+ annotations: {
212
+ title: string;
213
+ readOnlyHint: boolean;
214
+ destructiveHint: boolean;
215
+ idempotentHint: boolean;
216
+ openWorldHint: boolean;
217
+ };
218
+ } | {
219
+ name: string;
220
+ description: string;
221
+ inputSchema: {
222
+ type: string;
223
+ properties: {
224
+ toolName: {
225
+ type: string;
226
+ minLength: number;
227
+ maxLength: number;
228
+ description: string;
229
+ };
230
+ arguments: {
231
+ type: string;
232
+ description: string;
233
+ additionalProperties: boolean;
234
+ };
235
+ approvalId: {
236
+ type: string;
237
+ minLength: number;
238
+ maxLength: number;
239
+ description: string;
240
+ };
241
+ appSlug: {
242
+ readonly type: "string";
243
+ readonly minLength: 1;
244
+ readonly maxLength: 64;
245
+ readonly description: "The connected app, e.g. 'gmail'. Use this when the workspace has exactly one connected account for the app; if it has more than one, the call is refused as ambiguous and you must pass accountLabel instead.";
246
+ };
247
+ bindingId: {
248
+ readonly type: "string";
249
+ readonly minLength: 1;
250
+ readonly maxLength: 64;
251
+ readonly description: "An exact integration binding id, as returned by a previous integrations_list_tools call.";
252
+ };
253
+ accountLabel: {
254
+ readonly type: "string";
255
+ readonly minLength: 1;
256
+ readonly maxLength: 120;
257
+ readonly description: "The human label the user gave the connected account. This is the ONLY way to tell two connections over the same inbox apart, because the provider's own account name can be byte-identical for both.";
258
+ };
259
+ };
260
+ required: string[];
261
+ additionalProperties: boolean;
262
+ };
263
+ outputSchema: {
264
+ readonly type: "object";
265
+ readonly properties: {
266
+ readonly ok: {
267
+ readonly type: "boolean";
268
+ };
269
+ readonly outcome: {
270
+ readonly type: "string";
271
+ readonly enum: readonly ["ok", "refused", "approval_required", "catalog_incomplete", "not_connected", "upstream_degraded"];
272
+ };
273
+ readonly attribution: {
274
+ readonly type: "string";
275
+ readonly enum: readonly ["provider", "configuration", "policy", "sellable"];
276
+ };
277
+ readonly error: {
278
+ readonly type: readonly ["string", "null"];
279
+ };
280
+ readonly guidance: {
281
+ readonly type: readonly ["string", "null"];
282
+ };
283
+ readonly result: {};
284
+ };
285
+ readonly required: readonly ["ok", "outcome", "attribution", "error", "guidance", "result"];
286
+ readonly additionalProperties: false;
287
+ };
288
+ annotations: {
289
+ title: string;
290
+ /**
291
+ * DECLARED HONESTLY as `false`, even though every v1-allowlisted Gmail
292
+ * action is `readOnlyHint: true`.
293
+ *
294
+ * This tool is PROVIDER-NEUTRAL and its allowlist is DATA: the moment a
295
+ * mutating action is promoted on any binding, a `true` here becomes a lie
296
+ * that no test would catch, because nothing would have changed in this file.
297
+ * A false negative on an annotation costs a little caution; a false positive
298
+ * costs a silent write.
299
+ */
300
+ readOnlyHint: boolean;
301
+ destructiveHint: boolean;
302
+ idempotentHint: boolean;
303
+ openWorldHint: boolean;
304
+ };
305
+ })[];
306
+ type SelectorInput = {
307
+ appSlug?: unknown;
308
+ bindingId?: unknown;
309
+ accountLabel?: unknown;
310
+ };
311
+ export declare function integrationsListTools(input?: SelectorInput, actor?: AgentIntegrationRequestContext | null, agentEffectId?: string): Promise<IntegrationsResultEnvelope>;
312
+ export declare function integrationsDescribeTool(input?: SelectorInput & {
313
+ toolName?: unknown;
314
+ propName?: unknown;
315
+ optionQuery?: unknown;
316
+ }, actor?: AgentIntegrationRequestContext | null, agentEffectId?: string): Promise<IntegrationsResultEnvelope>;
317
+ export declare function integrationsCallTool(input?: SelectorInput & {
318
+ toolName?: unknown;
319
+ arguments?: unknown;
320
+ approvalId?: unknown;
321
+ }, actor?: AgentIntegrationRequestContext | null, agentEffectId?: string): Promise<IntegrationsResultEnvelope>;
322
+ export {};