@supportbridge/sdk 0.8.1 → 0.9.4

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
@@ -1,326 +1,345 @@
1
- # SupportBridge SDK
2
-
3
- `@supportbridge/sdk` is the external, production-oriented SDK for adding tool-call telemetry, optional agent telemetry, configurable support triggers, deterministic operational policies, and MCP-compatible support experiences to software-company MCP servers.
4
-
5
- This package is independent from the internal SupportBridge demo. It does not import the demo server, dashboard, database, or test-user implementation.
6
-
7
- ## What the first release provides
8
-
9
- - Framework-neutral MCP tool instrumentation
10
- - Tool name, timing, outcome, request correlation, sanitized arguments, and bounded result summaries
11
- - Optional agent-run and model-call spans
12
- - Async-local trace propagation from agent runs into MCP tool events
13
- - Immediate or non-blocking support-offer triggers
14
- - Repeated-failure triggers
15
- - Per-user, per-workspace, per-trace, or always-on trigger scopes
16
- - Accept, dismiss, and explicit reset decisions
17
- - Default secret redaction and payload-size limits
18
- - Batched asynchronous HTTP delivery with bounded retries
19
- - Queue overflow protection and fail-open behavior
20
- - Pluggable telemetry transport and trigger-state storage
21
- - Canonical, versioned policy schema with nested AND/OR/NOT conditions
22
- - Approved field and action registries with tenant-reference validation
23
- - Deterministic evaluation, traces, dry runs, conflict detection, and idempotent execution
24
- - Vendor-neutral natural-language compiler interface and deterministic local fake provider
25
- - Approval, immutable-version, rollback, and audit lifecycle primitives
26
- - An interactive MCP policy-builder app for Claude
27
-
28
- ## Install
29
-
30
- During local development:
31
-
32
- ```bash
33
- npm install ./supportbridge-sdk
34
- ```
35
-
36
- After publishing:
37
-
38
- ```bash
39
- npm install @supportbridge/sdk
40
- ```
41
-
42
- Node.js 20 or newer is required.
43
-
44
- ## Recommended one-call MCP installation
45
-
46
- For the official TypeScript MCP SDK, one call installs remote policy synchronization, support decisions, the UI-linked `talk_to_support` tool, the embedded chat app, app-only chat handlers, capability reporting, and graceful shutdown behavior:
47
-
48
- ```ts
49
- import { SupportBridge } from "@supportbridge/sdk";
50
-
51
- const support = SupportBridge.install(server, {
52
- source: "acme-mcp",
53
- apiKey: process.env.SUPPORTBRIDGE_API_KEY!,
54
- identify: context => ({
55
- userId: context.authInfo.subject,
56
- workspaceId: context.authInfo.workspaceId,
57
- traits: { customer: context.authInfo.organizationName }
58
- })
59
- });
60
- ```
61
-
62
- Use the returned installer to wrap each business tool while keeping the same identity resolver:
63
-
64
- ```ts
65
- server.tool(
66
- "search_companies",
67
- searchSchema,
68
- support.instrumentTool("search_companies", searchCompanies)
69
- );
70
- ```
71
-
72
- The wrapper captures bounded telemetry, enforces downloaded support triggers before execution, and retains recent sanitized failures for automatic ticket context. The installation also registers `open_policy_builder` and its embedded interactive app. No support tools or UI resources need to be registered manually.
73
-
74
- ## Deterministic policy engine
75
-
76
- Natural language is never executable. A `PolicyNlpProvider` can propose a `CanonicalPolicy`, then deterministic code validates approved fields, operators, events, action parameters, and workspace-scoped references before simulation or publication.
77
-
78
- ```ts
79
- import {
80
- FakePolicyNlpProvider,
81
- PolicyEngine,
82
- PolicyRegistry,
83
- validatePolicy
84
- } from "@supportbridge/sdk";
85
-
86
- const provider = new FakePolicyNlpProvider();
87
- const draft = await provider.compile({
88
- prompt: "Route urgent enterprise billing conversations to Finance.",
89
- workspaceId: "acme",
90
- actorId: "admin_123",
91
- references: { financeTeamId: "team_finance" }
92
- });
93
-
94
- const registry = new PolicyRegistry();
95
- const validation = validatePolicy(draft.policy, { registry });
96
- if (!validation.valid) throw new Error("Review the validation errors");
97
-
98
- const decision = new PolicyEngine(registry).evaluate(
99
- [{ ...draft.policy, enabled: true, state: "dry_run" }],
100
- { type: "conversation.created" },
101
- { customer: { plan: "enterprise" }, conversation: { priority: "urgent" }, message: { classification: "billing" } },
102
- { now: new Date(), eventId: "evt_1", correlationId: "corr_1", workspaceId: "acme" }
103
- );
104
- ```
105
-
106
- Evaluation produces a decision plan; it never performs actions. Pass a plan to `executeDecisionPlan` with your authorized executor and durable `IdempotencyStore`. See [the policy engine guide](docs/policy-engine.md) for registries, lifecycle, simulations, and production adapters.
107
-
108
- If the embedded widget is missing or has scrolled out of view, the model calls `open_support_chat` with the existing `session_id`. Both `talk_to_support` and `open_support_chat` use the exact UI metadata and conversation lifecycle ported from the known-working SupportBridge connector. The embedded app uses `support_get_messages`, `support_send_message`, and `support_end_session` for the live conversation.
109
-
110
- Availability-gated sales and customer-success policies can assign an offer to a representative in the control plane. Eligible offers are published only while that representative is available. After the user agrees, the model calls `request_assistance`; the resulting live conversation is automatically assigned to the configured representative.
111
-
112
- Verify an installation with:
113
-
114
- ```bash
115
- npx supportbridge doctor
116
- ```
117
-
118
- Set `SUPPORTBRIDGE_URL` and `SUPPORTBRIDGE_API_KEY` first. The command checks health, authentication, policy retrieval, telemetry ingestion, and the packaged MCP App capability. The vendor dashboard reports the MCP source, installed SDK version, capabilities, and last-seen connection state.
119
-
120
- ## Capture an MCP tool
121
-
122
- ```ts
123
- import { SupportBridgeClient, instrumentMcpTool } from "@supportbridge/sdk";
124
-
125
- const supportbridge = new SupportBridgeClient({
126
- source: "acme-mcp",
127
- endpoint: "https://api.supportbridge.example",
128
- apiKey: process.env.SUPPORTBRIDGE_API_KEY!,
129
- triggers: [{
130
- id: "climate-tech-concierge",
131
- kind: "argument_match",
132
- toolName: "list_companies",
133
- path: "industry",
134
- operator: "equals",
135
- value: "Climate Tech",
136
- action: "block_and_offer",
137
- oncePer: "user"
138
- }]
139
- });
140
-
141
- const listCompanies = instrumentMcpTool(
142
- supportbridge,
143
- "list_companies",
144
- async args => ({
145
- content: [{ type: "text", text: "Company results" }],
146
- structuredContent: { count: 12 }
147
- }),
148
- {
149
- identify: (_args, context) => ({
150
- userId: context.auth.subject,
151
- workspaceId: context.auth.workspaceId
152
- })
153
- }
154
- );
155
- ```
156
-
157
- When the blocking trigger matches, the vendor handler is not executed. The MCP result contains:
158
-
159
- ```json
160
- {
161
- "isError": true,
162
- "structuredContent": {
163
- "support_available": true,
164
- "support_offer_blocking": true,
165
- "support_trigger_id": "climate-tech-concierge",
166
- "accept_tool": "accept_support_offer",
167
- "decline_tool": "dismiss_support_offer",
168
- "retry_original_request_after_response": true
169
- }
170
- }
171
- ```
172
-
173
- For a visible offer that does not withhold the business result, use `action: "require_acknowledgement"`. The SDK preserves the original `content` and `structuredContent`, appends a transparent `LIVE ASSISTANCE AVAILABLE` content block, and includes pending offer state plus the consent-gated accept and decline tool names. This is the recommended cross-host mode when an ordinary metadata-only offer may be omitted by the model.
174
-
175
- Register the handlers returned by `supportDecisionTools` as MCP tools. After a user dismisses an offer, the original request can be retried without looping. Call `resetSupport` to make that trigger eligible again.
176
-
177
- ## Embedded support chat (MCP App)
178
-
179
- `accept_support_offer` can create a durable ticket and open an inline chat in hosts that support the MCP Apps extension. Create the app kit and attach its metadata to the registered accept tool:
180
-
181
- ```ts
182
- import {
183
- createSupportChatApp,
184
- supportDecisionTools
185
- } from "@supportbridge/sdk";
186
-
187
- const supportApp = createSupportChatApp(controlPlane);
188
- const decisions = supportDecisionTools(
189
- supportbridge,
190
- context => identifyUser(context),
191
- { controlPlane, appMeta: supportApp.toolMeta }
192
- );
193
- ```
194
-
195
- The MCP server must register these pieces using its framework:
196
-
197
- - `accept_support_offer`, with `supportApp.toolMeta` on the **tool definition**.
198
- - `dismiss_support_offer` as a normal model-visible tool.
199
- - `get_support_chat` and `send_support_message` from `supportApp.tools`, with app-only visibility.
200
- - A resource at `supportApp.resourceUri` whose read callback returns `supportApp.resourceContents()` and whose MIME type is `supportApp.mimeType`.
201
-
202
- For the official TypeScript MCP Apps SDK, use `registerAppTool` and `registerAppResource` from `@modelcontextprotocol/ext-apps/server`. The critical registration shape is:
203
-
204
- ```ts
205
- registerAppTool(server, "accept_support_offer", {
206
- description: "Open the human support conversation after the user consents.",
207
- inputSchema: acceptSchema,
208
- _meta: supportApp.toolMeta
209
- }, decisions.accept_support_offer);
210
-
211
- registerAppTool(server, "get_support_chat", {
212
- inputSchema: getChatSchema,
213
- _meta: { ui: { resourceUri: supportApp.resourceUri, visibility: ["app"] } }
214
- }, supportApp.tools.get_support_chat);
215
-
216
- registerAppTool(server, "send_support_message", {
217
- inputSchema: sendMessageSchema,
218
- _meta: { ui: { resourceUri: supportApp.resourceUri, visibility: ["app"] } }
219
- }, supportApp.tools.send_support_message);
220
-
221
- registerAppResource(server, "SupportBridge chat", supportApp.resourceUri, {
222
- mimeType: supportApp.mimeType,
223
- _meta: supportApp.resourceMeta
224
- }, async () => supportApp.resourceContents());
225
- ```
226
-
227
- The HTML uses the standard `text/html;profile=mcp-app` resource type and MCP Apps JSON-RPC bridge. It receives the ticket ID from the accept-tool result, polls the ticket, and sends user messages through app-only MCP tools. Hosts without MCP Apps support continue to receive the ordinary text result.
228
-
229
- ## Direct tool instrumentation
230
-
231
- For frameworks with custom middleware:
232
-
233
- ```ts
234
- const result = await supportbridge.instrumentTool(
235
- {
236
- toolName: "search_companies",
237
- arguments: { query: "fintech" },
238
- identity: { userId: "user_34892", workspaceId: "acme" }
239
- },
240
- () => searchCompanies("fintech")
241
- );
242
- ```
243
-
244
- The return value is discriminated by `kind`:
245
-
246
- - `result`: the vendor tool executed and `value` contains its result.
247
- - `support_offer`: execution was paused and `offer` contains the support decision payload.
248
-
249
- ## Agent telemetry
250
-
251
- ```ts
252
- import { traceAgentRun } from "@supportbridge/sdk";
253
-
254
- await traceAgentRun(
255
- supportbridge,
256
- {
257
- agentName: "research-agent",
258
- identity: { userId: "user_34892", workspaceId: "acme" }
259
- },
260
- async span => {
261
- const startedAt = performance.now();
262
- const response = await callYourModel();
263
- span.recordModelCall({
264
- provider: "your-provider",
265
- model: "your-model",
266
- startedAt,
267
- inputTokens: response.usage.input,
268
- outputTokens: response.usage.output
269
- });
270
- return response;
271
- }
272
- );
273
- ```
274
-
275
- Tool calls made inside `traceAgentRun` automatically inherit its trace and run identifiers through `AsyncLocalStorage`.
276
-
277
- ## Privacy defaults
278
-
279
- The SDK captures sanitized tool arguments and bounded result summaries. It does **not** capture prompts or model responses unless `captureAgentContent` is explicitly enabled.
280
-
281
- Recognized credential fields are replaced with `[REDACTED]`. Payload depth, strings, arrays, and object keys are bounded before they enter the telemetry queue.
282
-
283
- ```ts
284
- privacy: {
285
- captureToolArguments: true,
286
- captureToolResponses: true,
287
- captureAgentContent: false,
288
- sensitiveKeys: ["customer_access_code"],
289
- maxStringLength: 1000
290
- }
291
- ```
292
-
293
- Software companies should still document telemetry collection, obtain any required consent, and avoid sending regulated or unnecessary data.
294
-
295
- ## Reliability model
296
-
297
- Telemetry is delivered outside the tool execution path. Delivery uses batches, timeouts, bounded exponential retries, and a maximum queue size. If SupportBridge is unavailable, vendor tools continue to operate and telemetry is dropped after the configured retry limit.
298
-
299
- Call `flush()` before short-lived processes exit and `close()` during graceful shutdown:
300
-
301
- ```ts
302
- process.once("SIGTERM", () => void supportbridge.close());
303
- ```
304
-
305
- ## Trigger-state storage
306
-
307
- The default `MemoryTriggerStateStore` is appropriate for local development and a single process. Production vendors should supply a `TriggerStateStore` backed by Redis, PostgreSQL, or the SupportBridge control plane so decisions remain consistent across instances.
308
-
309
- ## Development
310
-
311
- From this directory:
312
-
313
- ```bash
314
- npm run check
315
- npm test
316
- npm run build
317
- ```
318
-
319
- The package intentionally has no runtime dependency on a particular MCP or agent framework. Framework-specific adapters can be added as separate packages without changing the core telemetry contract.
320
-
321
- See [the telemetry contract](docs/telemetry-contract.md) for backend ingestion details and [the security guide](SECURITY.md) before a production deployment.
322
-
323
- ## Hosted control plane
324
-
325
- The repository also contains the first vendor-facing control plane in [`control-plane`](control-plane). It provides tenant-authenticated telemetry ingestion, SDK policy distribution, support conversations, and a live-visitor console with Visitor 360 context. See its [deployment and integration guide](control-plane/README.md).
1
+ # SupportBridge SDK
2
+
3
+ `@supportbridge/sdk` is the external, production-oriented SDK for adding tool-call telemetry, optional agent telemetry, configurable support triggers, deterministic operational policies, and MCP-compatible support experiences to software-company MCP servers.
4
+
5
+ This package is independent from the internal SupportBridge demo. It does not import the demo server, dashboard, database, or test-user implementation.
6
+
7
+ ## What the first release provides
8
+
9
+ - Framework-neutral MCP tool instrumentation
10
+ - Tool name, timing, outcome, request correlation, sanitized arguments, and bounded result summaries
11
+ - Optional agent-run and model-call spans
12
+ - Async-local trace propagation from agent runs into MCP tool events
13
+ - Immediate or non-blocking support-offer triggers
14
+ - Repeated-failure triggers
15
+ - Per-user, per-workspace, per-trace, or always-on trigger scopes
16
+ - Accept, dismiss, and explicit reset decisions
17
+ - Default secret redaction and payload-size limits
18
+ - Batched asynchronous HTTP delivery with bounded retries
19
+ - Queue overflow protection and fail-open behavior
20
+ - Pluggable telemetry transport and trigger-state storage
21
+ - Canonical, versioned policy schema with nested AND/OR/NOT conditions
22
+ - Approved field and action registries with tenant-reference validation
23
+ - Deterministic evaluation, traces, dry runs, conflict detection, and idempotent execution
24
+ - Vendor-neutral natural-language compiler interface and deterministic local fake provider
25
+ - Approval, immutable-version, rollback, and audit lifecycle primitives
26
+ - An interactive MCP policy-builder app for Claude
27
+
28
+ ## Install
29
+
30
+ Install the public package from npm:
31
+
32
+ ```bash
33
+ npm install @supportbridge/sdk
34
+ ```
35
+
36
+ To pin the current release explicitly:
37
+
38
+ ```bash
39
+ npm install @supportbridge/sdk@0.9.4
40
+ ```
41
+
42
+ Node.js 20 or newer is required.
43
+
44
+ ## Recommended one-call MCP installation
45
+
46
+ For the official TypeScript MCP SDK, one call installs remote policy synchronization, support decisions, the UI-linked `talk_to_support` tool, the embedded chat app, app-only chat handlers, capability reporting, and graceful shutdown behavior:
47
+
48
+ ```ts
49
+ import { SupportBridge } from "@supportbridge/sdk";
50
+
51
+ const support = SupportBridge.install(server, {
52
+ source: "acme-mcp",
53
+ apiKey: process.env.SUPPORTBRIDGE_API_KEY!,
54
+ identify: context => ({
55
+ userId: context.authInfo.subject,
56
+ workspaceId: context.authInfo.workspaceId,
57
+ traits: { customer: context.authInfo.organizationName }
58
+ })
59
+ });
60
+ ```
61
+
62
+ Use the returned installer to wrap each business tool while keeping the same identity resolver:
63
+
64
+ ```ts
65
+ server.tool(
66
+ "search_companies",
67
+ searchSchema,
68
+ support.instrumentTool("search_companies", searchCompanies)
69
+ );
70
+ ```
71
+
72
+ The wrapper captures bounded telemetry, enforces downloaded support triggers before execution, and retains recent sanitized failures for automatic ticket context. The installation also registers `open_policy_builder` and its embedded interactive app. No support tools or UI resources need to be registered manually.
73
+
74
+ ## Deterministic policy engine
75
+
76
+ Natural language is never executable. A `PolicyNlpProvider` can propose a `CanonicalPolicy`, then deterministic code validates approved fields, operators, events, action parameters, and workspace-scoped references before simulation or publication.
77
+
78
+ ```ts
79
+ import {
80
+ FakePolicyNlpProvider,
81
+ PolicyEngine,
82
+ PolicyRegistry,
83
+ validatePolicy
84
+ } from "@supportbridge/sdk";
85
+
86
+ const provider = new FakePolicyNlpProvider();
87
+ const draft = await provider.compile({
88
+ prompt: "Route urgent enterprise billing conversations to Finance.",
89
+ workspaceId: "acme",
90
+ actorId: "admin_123",
91
+ references: { financeTeamId: "team_finance" }
92
+ });
93
+
94
+ const registry = new PolicyRegistry();
95
+ const validation = validatePolicy(draft.policy, { registry });
96
+ if (!validation.valid) throw new Error("Review the validation errors");
97
+
98
+ const decision = new PolicyEngine(registry).evaluate(
99
+ [{ ...draft.policy, enabled: true, state: "dry_run" }],
100
+ { type: "conversation.created" },
101
+ { customer: { plan: "enterprise" }, conversation: { priority: "urgent" }, message: { classification: "billing" } },
102
+ { now: new Date(), eventId: "evt_1", correlationId: "corr_1", workspaceId: "acme" }
103
+ );
104
+ ```
105
+
106
+ Evaluation produces a decision plan; it never performs actions. Pass a plan to `executeDecisionPlan` with your authorized executor and durable `IdempotencyStore`. See [the policy engine guide](docs/policy-engine.md) for registries, lifecycle, simulations, and production adapters.
107
+
108
+ If the embedded widget is missing or has scrolled out of view, the model calls `open_support_chat` with the existing `session_id`. Both `talk_to_support` and `open_support_chat` use the exact UI metadata and conversation lifecycle ported from the known-working SupportBridge connector. The embedded app uses `support_get_messages`, `support_send_message`, and `support_end_session` for the live conversation.
109
+
110
+ Availability-gated sales and customer-success policies can assign an offer to a representative in the control plane. Eligible offers are published only while that representative is available. After the user agrees, the model calls `request_assistance`; the resulting live conversation is automatically assigned to the configured representative.
111
+
112
+ Verify an installation with:
113
+
114
+ ```bash
115
+ npx supportbridge doctor
116
+ ```
117
+
118
+ Set `SUPPORTBRIDGE_URL` and `SUPPORTBRIDGE_API_KEY` first. The command checks health, authentication, policy retrieval, telemetry ingestion, and the packaged MCP App capability. The vendor dashboard reports the MCP source, installed SDK version, capabilities, and last-seen connection state.
119
+
120
+ ## Capture an MCP tool
121
+
122
+ ```ts
123
+ import { SupportBridgeClient, instrumentMcpTool } from "@supportbridge/sdk";
124
+
125
+ const supportbridge = new SupportBridgeClient({
126
+ source: "acme-mcp",
127
+ endpoint: "https://api.supportbridge.example",
128
+ apiKey: process.env.SUPPORTBRIDGE_API_KEY!,
129
+ triggers: [{
130
+ id: "climate-tech-concierge",
131
+ kind: "argument_match",
132
+ toolName: "list_companies",
133
+ path: "industry",
134
+ operator: "equals",
135
+ value: "Climate Tech",
136
+ action: "block_and_offer",
137
+ oncePer: "user"
138
+ }]
139
+ });
140
+
141
+ const listCompanies = instrumentMcpTool(
142
+ supportbridge,
143
+ "list_companies",
144
+ async args => ({
145
+ content: [{ type: "text", text: "Company results" }],
146
+ structuredContent: { count: 12 }
147
+ }),
148
+ {
149
+ identify: (_args, context) => ({
150
+ userId: context.auth.subject,
151
+ workspaceId: context.auth.workspaceId
152
+ })
153
+ }
154
+ );
155
+ ```
156
+
157
+ When the blocking trigger matches, the vendor handler is not executed. The MCP result contains:
158
+
159
+ ```json
160
+ {
161
+ "structuredContent": {
162
+ "status": "awaiting_user_decision",
163
+ "support_available": true,
164
+ "support_offer_blocking": true,
165
+ "support_offer_status": "pending",
166
+ "support_offer_requires_acknowledgement": true,
167
+ "support_trigger_id": "climate-tech-concierge",
168
+ "accept_tool": "accept_support_offer",
169
+ "decline_tool": "dismiss_support_offer",
170
+ "retry_original_request_after_response": true,
171
+ "do_not_retry_or_bypass": true
172
+ }
173
+ }
174
+ ```
175
+
176
+ SupportBridge has three offer modes:
177
+
178
+ - `offer` executes the business handler and attaches structured offer metadata without requiring a visible acknowledgement prompt.
179
+ - `require_acknowledgement` executes the business handler, preserves its original `content` and `structuredContent`, and appends a transparent `LIVE ASSISTANCE AVAILABLE` notice with consent-gated accept and decline tools.
180
+ - `block_and_offer` does not execute the business handler. It returns a successful `awaiting_user_decision` state that tells the host to pause until the user accepts or declines. It is not a tool error; genuine handler failures remain errors.
181
+
182
+ MCP hosts control the visual presentation of these results. The SDK supplies portable structured state and explicit text instructions, and it never contacts a representative without the user's consent.
183
+
184
+ Register the handlers returned by `supportDecisionTools` as MCP tools. After a user dismisses an offer, the original request can be retried without looping. Call `resetSupport` to make that trigger eligible again.
185
+
186
+ ## Embedded support chat (MCP App)
187
+
188
+ `accept_support_offer` can create a durable ticket and open an inline chat in hosts that support the MCP Apps extension. Create the app kit and attach its metadata to the registered accept tool:
189
+
190
+ ```ts
191
+ import {
192
+ createSupportChatApp,
193
+ supportDecisionTools
194
+ } from "@supportbridge/sdk";
195
+
196
+ const supportApp = createSupportChatApp(controlPlane);
197
+ const decisions = supportDecisionTools(
198
+ supportbridge,
199
+ context => identifyUser(context),
200
+ { controlPlane, appMeta: supportApp.toolMeta }
201
+ );
202
+ ```
203
+
204
+ The MCP server must register these pieces using its framework:
205
+
206
+ - `accept_support_offer`, with `supportApp.toolMeta` on the **tool definition**.
207
+ - `dismiss_support_offer` as a normal model-visible tool.
208
+ - `get_support_chat` and `send_support_message` from `supportApp.tools`, with app-only visibility.
209
+ - A resource at `supportApp.resourceUri` whose read callback returns `supportApp.resourceContents()` and whose MIME type is `supportApp.mimeType`.
210
+
211
+ For the official TypeScript MCP Apps SDK, use `registerAppTool` and `registerAppResource` from `@modelcontextprotocol/ext-apps/server`. The critical registration shape is:
212
+
213
+ ```ts
214
+ registerAppTool(server, "accept_support_offer", {
215
+ description: "Open the human support conversation after the user consents.",
216
+ inputSchema: acceptSchema,
217
+ _meta: supportApp.toolMeta
218
+ }, decisions.accept_support_offer);
219
+
220
+ registerAppTool(server, "get_support_chat", {
221
+ inputSchema: getChatSchema,
222
+ _meta: { ui: { resourceUri: supportApp.resourceUri, visibility: ["app"] } }
223
+ }, supportApp.tools.get_support_chat);
224
+
225
+ registerAppTool(server, "send_support_message", {
226
+ inputSchema: sendMessageSchema,
227
+ _meta: { ui: { resourceUri: supportApp.resourceUri, visibility: ["app"] } }
228
+ }, supportApp.tools.send_support_message);
229
+
230
+ registerAppResource(server, "SupportBridge chat", supportApp.resourceUri, {
231
+ mimeType: supportApp.mimeType,
232
+ _meta: supportApp.resourceMeta
233
+ }, async () => supportApp.resourceContents());
234
+ ```
235
+
236
+ The HTML uses the standard `text/html;profile=mcp-app` resource type and MCP Apps JSON-RPC bridge. It receives the ticket ID from the accept-tool result, polls the ticket, and sends user messages through app-only MCP tools. Hosts without MCP Apps support continue to receive the ordinary text result.
237
+
238
+ ## Direct tool instrumentation
239
+
240
+ For frameworks with custom middleware:
241
+
242
+ ```ts
243
+ const result = await supportbridge.instrumentTool(
244
+ {
245
+ toolName: "search_companies",
246
+ arguments: { query: "fintech" },
247
+ identity: { userId: "user_34892", workspaceId: "acme" }
248
+ },
249
+ () => searchCompanies("fintech")
250
+ );
251
+ ```
252
+
253
+ The return value is discriminated by `kind`:
254
+
255
+ - `result`: the vendor tool executed and `value` contains its result.
256
+ - `support_offer`: execution was paused and `offer` contains the support decision payload.
257
+
258
+ ## Agent telemetry
259
+
260
+ ```ts
261
+ import { traceAgentRun } from "@supportbridge/sdk";
262
+
263
+ await traceAgentRun(
264
+ supportbridge,
265
+ {
266
+ agentName: "research-agent",
267
+ identity: { userId: "user_34892", workspaceId: "acme" }
268
+ },
269
+ async span => {
270
+ const startedAt = performance.now();
271
+ const response = await callYourModel();
272
+ span.recordModelCall({
273
+ provider: "your-provider",
274
+ model: "your-model",
275
+ startedAt,
276
+ inputTokens: response.usage.input,
277
+ outputTokens: response.usage.output
278
+ });
279
+ return response;
280
+ }
281
+ );
282
+ ```
283
+
284
+ Tool calls made inside `traceAgentRun` automatically inherit its trace and run identifiers through `AsyncLocalStorage`.
285
+
286
+ ## Privacy defaults
287
+
288
+ The SDK captures sanitized tool arguments and bounded result summaries. It does **not** capture prompts or model responses unless `captureAgentContent` is explicitly enabled.
289
+
290
+ Recognized credential fields are replaced with `[REDACTED]`. Payload depth, strings, arrays, and object keys are bounded before they enter the telemetry queue.
291
+
292
+ ```ts
293
+ privacy: {
294
+ captureToolArguments: true,
295
+ captureToolResponses: true,
296
+ captureAgentContent: false,
297
+ sensitiveKeys: ["customer_access_code"],
298
+ maxStringLength: 1000
299
+ }
300
+ ```
301
+
302
+ Software companies should still document telemetry collection, obtain any required consent, and avoid sending regulated or unnecessary data.
303
+
304
+ ## Reliability model
305
+
306
+ Telemetry is delivered outside the tool execution path. Delivery uses batches, timeouts, bounded exponential retries, and a maximum queue size. If SupportBridge is unavailable, vendor tools continue to operate and telemetry is dropped after the configured retry limit.
307
+
308
+ Call `flush()` before short-lived processes exit and `close()` during graceful shutdown:
309
+
310
+ ```ts
311
+ process.once("SIGTERM", () => void supportbridge.close());
312
+ ```
313
+
314
+ ## Trigger-state storage
315
+
316
+ The default `MemoryTriggerStateStore` is appropriate for local development and a single process. Production vendors should supply a `TriggerStateStore` backed by Redis, PostgreSQL, or the SupportBridge control plane so decisions remain consistent across instances.
317
+
318
+ ## Development
319
+
320
+ From this directory:
321
+
322
+ ```bash
323
+ npm run check
324
+ npm test
325
+ npm run build
326
+ ```
327
+
328
+ ## Publishing releases
329
+
330
+ npm releases are published automatically through GitHub Actions and npm Trusted Publishing. No npm token is stored in GitHub.
331
+
332
+ 1. Update the SDK version in `package.json` and update the changelog.
333
+ 2. Commit and push the release changes to `main`.
334
+ 3. Create and push a tag matching the package version exactly, such as `v0.8.2`.
335
+
336
+ The `Publish SDK to npm` workflow verifies that the tag matches `package.json`, runs the type check and all tests, builds the package, and publishes it publicly with npm provenance. A mismatched tag or failed check stops the release before publication.
337
+
338
+ The package intentionally has no runtime dependency on a particular MCP or agent framework. Framework-specific adapters can be added as separate packages without changing the core telemetry contract.
339
+
340
+ See [the telemetry contract](docs/telemetry-contract.md) for backend ingestion details and [the security guide](SECURITY.md) before a production deployment.
341
+
342
+ ## Hosted control plane
343
+
344
+ The repository also contains the first vendor-facing control plane in [`control-plane`](control-plane). It provides tenant-authenticated telemetry ingestion, SDK policy distribution, support conversations, and a live-visitor console with Visitor 360 context. See its [deployment and integration guide](control-plane/README.md).
326
345