@tapicker/bridge 1.0.0 → 1.1.0

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.
Files changed (49) hide show
  1. package/README.md +15 -15
  2. package/dist/bridge/bridge-service.d.ts +1 -1
  3. package/dist/bridge/bridge-service.js +7 -7
  4. package/dist/bridge/control.js +1 -1
  5. package/dist/bridge/export-store.d.ts +35 -0
  6. package/dist/bridge/export-store.js +195 -0
  7. package/dist/bridge/extension-session.d.ts +9 -2
  8. package/dist/bridge/extension-session.js +56 -8
  9. package/dist/cli.js +563 -148
  10. package/dist/commands.d.ts +8 -0
  11. package/dist/commands.js +276 -40
  12. package/dist/constants.d.ts +2 -2
  13. package/dist/constants.js +4 -2
  14. package/dist/http-client.d.ts +24 -0
  15. package/dist/http-client.js +101 -0
  16. package/dist/index.d.ts +4 -4
  17. package/dist/index.js +2 -2
  18. package/dist/mcp.d.ts +4 -3
  19. package/dist/mcp.js +221 -134
  20. package/dist/protocol/schema.d.ts +503 -34
  21. package/dist/protocol/schema.js +396 -15
  22. package/dist/server.d.ts +7 -4
  23. package/dist/server.js +70 -26
  24. package/dist/update-notifier-worker.d.ts +2 -0
  25. package/dist/update-notifier-worker.js +3 -0
  26. package/dist/update-notifier.d.ts +9 -0
  27. package/dist/update-notifier.js +178 -0
  28. package/dist/workflow-watch.d.ts +8 -7
  29. package/dist/workflow-watch.js +39 -29
  30. package/package.json +16 -6
  31. package/docs/cli.md +0 -112
  32. package/docs/http-api.md +0 -149
  33. package/docs/mcp.md +0 -90
  34. package/docs/operations.md +0 -85
  35. package/docs/workflow-watch.md +0 -86
  36. package/src/bridge/bridge-service.ts +0 -291
  37. package/src/bridge/control.ts +0 -281
  38. package/src/bridge/daemon.ts +0 -102
  39. package/src/bridge/extension-session.ts +0 -286
  40. package/src/bridge/pairing-state.ts +0 -92
  41. package/src/cli.ts +0 -1019
  42. package/src/commands.ts +0 -158
  43. package/src/constants.ts +0 -10
  44. package/src/errors.ts +0 -72
  45. package/src/index.ts +0 -35
  46. package/src/mcp.ts +0 -386
  47. package/src/protocol/schema.ts +0 -202
  48. package/src/server.ts +0 -623
  49. package/src/workflow-watch.ts +0 -364
@@ -1,18 +1,17 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { WebSocket } from 'ws';
3
- import { DEFAULT_WORKFLOW_WATCH_WS_PATH } from './constants.js';
3
+ import { DEFAULT_WORKFLOW_STATUS_WATCH_WS_PATH } from './constants.js';
4
4
  import { BridgeError } from './errors.js';
5
5
  import { SafeResultSchema, WorkflowStatusEventSchema, createEnvelope, parseEnvelope } from './protocol/schema.js';
6
- const terminalStates = new Set(['completed', 'stopped', 'failed']);
7
- export function workflowWatchWebSocketUrl(bridgeUrl) {
8
- const url = new URL(DEFAULT_WORKFLOW_WATCH_WS_PATH, bridgeUrl);
6
+ export function workflowStatusWatchWebSocketUrl(bridgeUrl) {
7
+ const url = new URL(DEFAULT_WORKFLOW_STATUS_WATCH_WS_PATH, bridgeUrl);
9
8
  url.protocol = bridgeUrl.protocol === 'https:' ? 'wss:' : 'ws:';
10
9
  return url;
11
10
  }
12
- export function connectWorkflowWatch(url, input, token, signal) {
11
+ export function connectWorkflowStatusWatch(url, input, token, signal) {
13
12
  return new Promise((resolve, reject) => {
14
13
  if (signal?.aborted) {
15
- reject(new BridgeError('WorkflowWatchCancelled', 'Workflow watch was cancelled', { status: 499 }));
14
+ reject(new BridgeError('WorkflowStatusWatchCancelled', 'Workflow status watch was cancelled', { status: 499 }));
16
15
  return;
17
16
  }
18
17
  const headers = token ? { Authorization: `Bearer ${token}` } : undefined;
@@ -56,17 +55,22 @@ export function connectWorkflowWatch(url, input, token, signal) {
56
55
  intentionalClose = true;
57
56
  cleanupAbortListener();
58
57
  if (socket.readyState === WebSocket.OPEN) {
59
- socket.close(1000, 'Workflow watch closed');
58
+ socket.close(1000, 'Workflow status watch closed');
60
59
  }
61
60
  else if (socket.readyState === WebSocket.CONNECTING) {
62
61
  socket.terminate();
63
62
  }
64
63
  if (acceptedClientId) {
65
- notifyClose(reason ?? new BridgeError('WorkflowWatchClosed', 'Workflow watch was closed by the client', { status: 499 }));
64
+ notifyClose(reason ??
65
+ new BridgeError('WorkflowStatusWatchClosed', 'Workflow status watch was closed by the client', {
66
+ status: 499,
67
+ }));
66
68
  }
67
69
  };
68
70
  abortListener = () => {
69
- const error = new BridgeError('WorkflowWatchCancelled', 'Workflow watch was cancelled', { status: 499 });
71
+ const error = new BridgeError('WorkflowStatusWatchCancelled', 'Workflow status watch was cancelled', {
72
+ status: 499,
73
+ });
70
74
  if (!acceptedClientId) {
71
75
  failBeforeAccept(error);
72
76
  }
@@ -76,7 +80,7 @@ export function connectWorkflowWatch(url, input, token, signal) {
76
80
  };
77
81
  signal?.addEventListener('abort', abortListener, { once: true });
78
82
  socket.once('open', () => {
79
- socket.send(JSON.stringify(createEnvelope('workflow.watch', {
83
+ socket.send(JSON.stringify(createEnvelope('workflow.status.watch', {
80
84
  clientId: input.clientId,
81
85
  input: { workflowId: input.workflowId },
82
86
  }, { id: requestId })));
@@ -84,23 +88,23 @@ export function connectWorkflowWatch(url, input, token, signal) {
84
88
  socket.on('message', (raw) => {
85
89
  const parsedEnvelope = parseEnvelope(raw);
86
90
  if (!parsedEnvelope.success) {
87
- const error = new BridgeError('InvalidBridgeResponse', 'Bridge returned an invalid Workflow watch message', {
91
+ const error = new BridgeError('InvalidBridgeResponse', 'Bridge returned an invalid Workflow status watch message', {
88
92
  status: 502,
89
93
  cause: parsedEnvelope.error,
90
94
  });
91
95
  if (!acceptedClientId)
92
96
  failBeforeAccept(error);
93
97
  else {
94
- socket.close(1007, 'Invalid Workflow watch message');
98
+ socket.close(1007, 'Invalid Workflow status watch message');
95
99
  notifyClose(error);
96
100
  }
97
101
  return;
98
102
  }
99
103
  const message = parsedEnvelope.data;
100
- if (message.type === 'workflow.watch:res' && message.id === requestId && !acceptedClientId) {
104
+ if (message.type === 'workflow.status.watch:res' && message.id === requestId && !acceptedClientId) {
101
105
  const result = SafeResultSchema.safeParse(message.data);
102
106
  if (!result.success) {
103
- failBeforeAccept(new BridgeError('InvalidBridgeResponse', 'Bridge returned an invalid Workflow watch response', {
107
+ failBeforeAccept(new BridgeError('InvalidBridgeResponse', 'Bridge returned an invalid Workflow status watch response', {
104
108
  status: 502,
105
109
  }));
106
110
  return;
@@ -114,7 +118,7 @@ export function connectWorkflowWatch(url, input, token, signal) {
114
118
  }
115
119
  const accepted = WorkflowStatusEventSchema.pick({ clientId: true, workflowId: true }).safeParse(result.data.data);
116
120
  if (!accepted.success || accepted.data.workflowId !== input.workflowId) {
117
- failBeforeAccept(new BridgeError('InvalidBridgeResponse', 'Bridge accepted a different Workflow watch target', {
121
+ failBeforeAccept(new BridgeError('InvalidBridgeResponse', 'Bridge accepted a different Workflow status watch target', {
118
122
  status: 502,
119
123
  }));
120
124
  return;
@@ -143,7 +147,7 @@ export function connectWorkflowWatch(url, input, token, signal) {
143
147
  resolve(connection);
144
148
  return;
145
149
  }
146
- if (message.type !== 'workflow.status' || !acceptedClientId)
150
+ if (message.type !== 'workflow.status.changed' || !acceptedClientId)
147
151
  return;
148
152
  const event = WorkflowStatusEventSchema.safeParse(message.data);
149
153
  if (!event.success)
@@ -172,7 +176,7 @@ export function connectWorkflowWatch(url, input, token, signal) {
172
176
  response.once('end', () => {
173
177
  const body = Buffer.concat(chunks).toString('utf8');
174
178
  let errorCode = 'BridgeRequestFailed';
175
- let errorMessage = response.statusMessage || 'Bridge rejected the Workflow watch connection';
179
+ let errorMessage = response.statusMessage || 'Bridge rejected the Workflow status watch connection';
176
180
  let details;
177
181
  try {
178
182
  const parsed = JSON.parse(body);
@@ -187,7 +191,7 @@ export function connectWorkflowWatch(url, input, token, signal) {
187
191
  });
188
192
  });
189
193
  socket.on('error', (cause) => {
190
- const error = new BridgeError('BridgeUnavailable', 'Unable to connect to Tapicker Bridge Workflow watch', {
194
+ const error = new BridgeError('BridgeUnavailable', 'Unable to connect to Tapicker Bridge Workflow status watch', {
191
195
  status: 503,
192
196
  cause,
193
197
  });
@@ -199,13 +203,13 @@ export function connectWorkflowWatch(url, input, token, signal) {
199
203
  socket.once('close', (code, reason) => {
200
204
  cleanupAbortListener();
201
205
  if (!acceptedClientId) {
202
- failBeforeAccept(new BridgeError('WorkflowWatchClosed', 'Bridge closed the Workflow watch connection before accepting it', {
206
+ failBeforeAccept(new BridgeError('WorkflowStatusWatchClosed', 'Bridge closed the Workflow status watch connection before accepting it', {
203
207
  status: 503,
204
208
  details: { code, reason: reason.toString() },
205
209
  }));
206
210
  }
207
211
  else if (!intentionalClose) {
208
- notifyClose(new BridgeError('WorkflowWatchClosed', 'Bridge closed the Workflow watch connection', {
212
+ notifyClose(new BridgeError('WorkflowStatusWatchClosed', 'Bridge closed the Workflow status watch connection', {
209
213
  status: 503,
210
214
  details: { code, reason: reason.toString() },
211
215
  }));
@@ -227,12 +231,12 @@ export function waitForWorkflowStatus(options) {
227
231
  if (abortListener)
228
232
  options.signal?.removeEventListener('abort', abortListener);
229
233
  };
230
- const finish = (status, timedOut = false) => {
234
+ const finish = (status, isTimedOut = false) => {
231
235
  if (settled)
232
236
  return;
233
237
  settled = true;
234
238
  cleanup();
235
- resolve({ ...status, timedOut });
239
+ resolve({ ...status, isTimedOut });
236
240
  };
237
241
  const fail = (error) => {
238
242
  if (settled)
@@ -246,6 +250,8 @@ export function waitForWorkflowStatus(options) {
246
250
  return;
247
251
  if (status.clientId !== options.clientId || status.workflowId !== options.workflowId)
248
252
  return;
253
+ if (options.runAt !== undefined && status.runAt !== options.runAt)
254
+ return;
249
255
  if (latestStatus && sameStatus(latestStatus, status))
250
256
  return;
251
257
  latestStatus = status;
@@ -256,11 +262,11 @@ export function waitForWorkflowStatus(options) {
256
262
  fail(error);
257
263
  return;
258
264
  }
259
- if (terminalStates.has(status.state))
265
+ if (status.status === 'stopped')
260
266
  finish(status);
261
267
  };
262
268
  if (options.signal?.aborted) {
263
- fail(new BridgeError('WorkflowWatchCancelled', 'Workflow watch was cancelled', { status: 499 }));
269
+ fail(new BridgeError('WorkflowStatusWatchCancelled', 'Workflow status watch was cancelled', { status: 499 }));
264
270
  return;
265
271
  }
266
272
  unsubscribe = options.subscribe(publish, fail);
@@ -269,7 +275,7 @@ export function waitForWorkflowStatus(options) {
269
275
  return;
270
276
  }
271
277
  if (options.signal) {
272
- abortListener = () => fail(new BridgeError('WorkflowWatchCancelled', 'Workflow watch was cancelled', { status: 499 }));
278
+ abortListener = () => fail(new BridgeError('WorkflowStatusWatchCancelled', 'Workflow status watch was cancelled', { status: 499 }));
273
279
  options.signal.addEventListener('abort', abortListener, { once: true });
274
280
  }
275
281
  if (options.timeoutMs !== undefined) {
@@ -277,13 +283,13 @@ export function waitForWorkflowStatus(options) {
277
283
  if (latestStatus)
278
284
  finish(latestStatus, true);
279
285
  else
280
- fail(new BridgeError('WorkflowWatchTimeout', 'Timed out before receiving Workflow status', {
286
+ fail(new BridgeError('WorkflowStatusWatchTimeout', 'Timed out before receiving Workflow status', {
281
287
  status: 504,
282
288
  details: { timeoutMs: options.timeoutMs },
283
289
  }));
284
290
  }, options.timeoutMs);
285
291
  }
286
- void options
292
+ options
287
293
  .readStatus()
288
294
  .then((snapshot) => {
289
295
  if (settled)
@@ -291,7 +297,7 @@ export function waitForWorkflowStatus(options) {
291
297
  const snapshotIsAtLeastAsNew = latestStatus !== undefined && Date.parse(snapshot.updatedAt) >= Date.parse(latestStatus.updatedAt);
292
298
  if (!latestStatus ||
293
299
  snapshotIsAtLeastAsNew ||
294
- (terminalStates.has(snapshot.state) && !terminalStates.has(latestStatus.state))) {
300
+ (snapshot.status === 'stopped' && latestStatus.status !== 'stopped')) {
295
301
  publish(snapshot);
296
302
  }
297
303
  })
@@ -299,6 +305,10 @@ export function waitForWorkflowStatus(options) {
299
305
  });
300
306
  }
301
307
  function sameStatus(left, right) {
302
- return left.state === right.state && left.updatedAt === right.updatedAt;
308
+ return (left.status === right.status &&
309
+ left.reason === right.reason &&
310
+ left.runAt === right.runAt &&
311
+ left.stopAt === right.stopAt &&
312
+ left.updatedAt === right.updatedAt);
303
313
  }
304
314
  //# sourceMappingURL=workflow-watch.js.map
package/package.json CHANGED
@@ -1,8 +1,20 @@
1
1
  {
2
2
  "name": "@tapicker/bridge",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Tapicker local and remote command bridge",
5
- "homepage": "https://www.tapicker.com/",
5
+ "keywords": [
6
+ "tapicker",
7
+ "web-automation",
8
+ "browser-automation",
9
+ "web-scraping",
10
+ "data-extraction",
11
+ "chrome-extension",
12
+ "ai-agents",
13
+ "mcp",
14
+ "mcp-server",
15
+ "cli"
16
+ ],
17
+ "homepage": "https://www.tapicker.com",
6
18
  "license": "MIT",
7
19
  "type": "module",
8
20
  "main": "dist/index.js",
@@ -19,9 +31,7 @@
19
31
  "files": [
20
32
  "dist/**/*.js",
21
33
  "dist/**/*.d.ts",
22
- "src",
23
- "README.md",
24
- "docs"
34
+ "README.md"
25
35
  ],
26
36
  "scripts": {
27
37
  "build": "tsc -p tsconfig.json",
@@ -49,7 +59,7 @@
49
59
  "@types/node": "^22.0.0",
50
60
  "@types/ws": "^8.5.13",
51
61
  "prettier": "^3.6.0",
52
- "typescript": "^5.9.0",
62
+ "typescript": "6.0.3",
53
63
  "vitest": "^4.0.0"
54
64
  }
55
65
  }
package/docs/cli.md DELETED
@@ -1,112 +0,0 @@
1
- # CLI Reference
2
-
3
- The examples below assume the package is installed globally and the `tapicker` command is available in `PATH`. Start with the [Getting Started guide](https://www.tapicker.com/docs/bridge-getting-started). Run `tapicker --help` or `tapicker <command> --help` for concise command help.
4
-
5
- ## Start and Manage Bridge
6
-
7
- ```bash
8
- tapicker bridge start [options]
9
- tapicker bridge stop [--data-dir <path>]
10
- tapicker bridge code [--refresh] [--data-dir <path>]
11
- tapicker bridge extensions [--data-dir <path>]
12
- ```
13
-
14
- | Command | Purpose |
15
- | ----------------------- | ----------------------------------------------------------------------------------------------- |
16
- | `bridge start` | Run in the foreground; press `Ctrl+C` to stop. Prints the listening addresses and pairing code. |
17
- | `bridge start --daemon` | Run in the background. |
18
- | `bridge stop` | Stop a background Bridge. |
19
- | `bridge code` | Display the current four-digit pairing code. |
20
- | `bridge code --refresh` | Rotate the pairing code and unlock pairing after rate limiting. |
21
- | `bridge extensions` | List connected Extension clients, including clients that have not finished pairing. |
22
-
23
- ### `bridge start` Options
24
-
25
- | Option | Default | Description |
26
- | ---------------------------------------- | --------------------------------------- | ----------------------------------------------------------------------- |
27
- | `--host <host>` | `127.0.0.1` | Bind address. Non-loopback binding requires a Token and TLS. |
28
- | `--port <port>` | First available port from `9520`–`9530` | Set a fixed listening port. |
29
- | `--token <token>` | `TAPICKER_BRIDGE_TOKEN` or none | Bearer Token for protected HTTP, MCP, and status subscription requests. |
30
- | `--cors-origin <origins>` | `*` | Comma-separated allowlist of HTTP origins. |
31
- | `--tls-key <path>` / `--tls-cert <path>` | None | TLS private key and certificate; provide both for remote binding. |
32
- | `--data-dir <path>` | `TAPICKER_DATA_DIR` or `~/.tapicker` | Directory for pairing credentials and daemon files. |
33
- | `--daemon` | Off | Run in the background. |
34
-
35
- If you use a custom `--data-dir`, pass the same directory to `stop`, `code`, and `extensions`.
36
-
37
- ## Control a Workflow
38
-
39
- ```bash
40
- tapicker workflow start <workflowId> [options]
41
- tapicker workflow pause <workflowId> [options]
42
- tapicker workflow resume <workflowId> [options]
43
- tapicker workflow stop <workflowId> [options]
44
- tapicker workflow status <workflowId> [options]
45
- tapicker workflow watch <workflowId> [options]
46
- ```
47
-
48
- Start with an input object and stream status updates:
49
-
50
- ```bash
51
- tapicker workflow start jr657xeb --args '{"keyword":"Tapicker"}' --watch
52
- ```
53
-
54
- Other tab targets are `new` and `id:<positive-tabId>`:
55
-
56
- ```bash
57
- tapicker workflow start jr657xeb --tab new
58
- tapicker workflow start jr657xeb --tab id:123
59
- ```
60
-
61
- `--args` must be a JSON object. The default is `{}`. `--tab` defaults to `active`.
62
-
63
- | Option | Default | Description |
64
- | -------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------- |
65
- | `--host <host>` | `127.0.0.1` | Bridge host. |
66
- | `--port <port>` | `9520` | Bridge port. Pass the actual port if Bridge selected another one. |
67
- | `--token <token>` | None | Bridge Bearer Token. |
68
- | `--client-id <uuid>` | Auto-select when exactly one Extension is ready | Select an Extension when multiple are ready. |
69
- | `--https` | Off | Use HTTPS and WSS instead of HTTP and WS. |
70
- | `--args <json>` | `{}` | Workflow input object; `start` only. |
71
- | `--tab <target>` | `active` | `active`, `new`, or `id:<positive-tabId>`; `start` only. |
72
- | `--watch` | Off | After starting, stream updates until a terminal state; `start` only. |
73
- | `--timeout <milliseconds>` | No limit | Limit how long `watch` or `start --watch` waits. Does not stop the Workflow. |
74
-
75
- `start --watch` establishes the watch before starting the Workflow to avoid missing initial status changes. `workflow watch` listens to a Workflow that is already running; it does not start or control it. A watch timeout or `Ctrl+C` ends only the listener, not the Workflow. Output is newline-delimited JSON.
76
-
77
- The start response includes `workflowId`, the full `workflowName`, and the selected `clientId`. A Workflow supports one active instance at a time in its Extension.
78
-
79
- ## Select an Extension
80
-
81
- List clients first:
82
-
83
- ```bash
84
- tapicker bridge extensions
85
- ```
86
-
87
- Pass `--client-id <uuid>` to target one ready Extension. You can omit it only when exactly one ready Extension is connected. Pending pairing connections are not command targets. Bridge never broadcasts a command to every Extension.
88
-
89
- ## Read or Delete Extracted Data
90
-
91
- ```bash
92
- tapicker data get <tableId> [options]
93
- tapicker data delete <tableId> [options]
94
- ```
95
-
96
- Get all rows from a data table:
97
-
98
- ```bash
99
- tapicker data get 4a1b2c3d
100
- ```
101
-
102
- `tableId` is copied from the Extension’s Data list or detail page. `data get` returns `tableId`, `tableName`, the row count, and all rows. The row count consumes the connected Extension account’s export quota; if quota is insufficient or unavailable, the command fails without returning the data. `data delete` removes the table from Tapicker Data.
103
-
104
- Data commands accept `--host`, `--port`, `--token`, `--client-id`, and `--https`.
105
-
106
- ## Connect an MCP Client over stdio
107
-
108
- ```bash
109
- tapicker mcp [--url <http(s)://host:port>] [--token <token>]
110
- ```
111
-
112
- This starts an MCP stdio adapter that connects to an already-running Bridge over HTTP. It does not start another Bridge server. The default URL is `http://127.0.0.1:9520`; set `TAPICKER_BRIDGE_URL` and `TAPICKER_BRIDGE_TOKEN` in the MCP client environment when needed. See the [MCP guide](mcp.md) for client configuration examples.
package/docs/http-api.md DELETED
@@ -1,149 +0,0 @@
1
- # HTTP API Reference
2
-
3
- Bridge serves HTTP, WebSocket, and MCP from one port. The default local base URL is `http://127.0.0.1:9520`. At startup, Bridge tries ports `9520`–`9530` and prints the actual address. Use that address in every client.
4
-
5
- For installation and setup, see the [Getting Started guide](https://www.tapicker.com/docs/bridge-getting-started). For a short integration walkthrough, see [Connect an AI Agent](https://www.tapicker.com/docs/bridge-ai-agents).
6
-
7
- ## Endpoints
8
-
9
- | Method / transport | Path | Token required when configured? | Purpose |
10
- | ------------------- | ----------------------------- | ------------------------------- | ------------------------------------------------------ |
11
- | `GET` | `/v1/health` | No | Check Bridge health. |
12
- | `GET` | `/v1/discovery` | No | Read Bridge metadata and the Extension WebSocket path. |
13
- | `GET` | `/v1/capabilities` | No | Discover commands and their input schemas. |
14
- | `GET` | `/v1/extensions` | Yes | List connected Extension clients. |
15
- | `POST` | `/v1/commands/{command}` | Yes | Call a Workflow or data command. |
16
- | WebSocket | `/v1/commands/workflow.watch` | Yes | Subscribe to one Workflow’s status events. |
17
- | Streamable HTTP MCP | `/mcp` | Yes | Connect a remote MCP client. |
18
- | WebSocket | `/ws` | Pairing credential | Extension pairing and internal command transport. |
19
-
20
- When Bridge is started with a Token, protected requests must include:
21
-
22
- ```http
23
- Authorization: Bearer YOUR_TOKEN
24
- ```
25
-
26
- `/ws` is for Tapicker Extension connections, not external Agent commands. It uses the pairing code and saved Extension credential, not the HTTP Bearer Token.
27
-
28
- ## CORS and Local Security
29
-
30
- HTTP and `/mcp` allow all web origins by default (`--cors-origin *`) and support browser `OPTIONS` preflight requests. Use one comma-separated allowlist to restrict origins:
31
-
32
- ```bash
33
- tapicker bridge start --cors-origin https://agent.example.com,https://app.example.com
34
- ```
35
-
36
- CORS is not authentication. The default local Bridge has no Bearer Token, so a web page may be able to call its local HTTP API. Keep Bridge bound to `127.0.0.1`, and set an origin allowlist when browser access should be limited. WebSocket connections do not use CORS.
37
-
38
- ## Discover Bridge and Extensions
39
-
40
- ```bash
41
- curl http://127.0.0.1:9520/v1/health
42
- curl http://127.0.0.1:9520/v1/discovery
43
- curl http://127.0.0.1:9520/v1/capabilities
44
- curl http://127.0.0.1:9520/v1/extensions
45
- ```
46
-
47
- `/v1/health` reports Bridge health, not Extension readiness. `/v1/extensions` returns connected clients, for example:
48
-
49
- ```json
50
- {
51
- "extensions": [
52
- {
53
- "clientId": "56a450e6-41f1-448e-9ea7-a2d958cbda37",
54
- "clientName": "MacOS.Chrome.152.a1b2",
55
- "connected": true,
56
- "ready": true,
57
- "pairingRequired": false,
58
- "capabilities": ["workflow.start", "workflow.pause"]
59
- }
60
- ]
61
- }
62
- ```
63
-
64
- Only `ready: true` clients can receive commands. The list does not expose pairing codes or saved credentials. Add the Bearer header to `/v1/extensions` when a Token is configured.
65
-
66
- `/v1/capabilities` lists supported commands, descriptions, JSON input schemas, and the WebSocket path for `workflow.watch`.
67
-
68
- ## Call a Command
69
-
70
- All HTTP command requests use this shape:
71
-
72
- ```json
73
- {
74
- "clientId": "optional-extension-uuid",
75
- "input": {
76
- "workflowId": "jr657xeb"
77
- }
78
- }
79
- ```
80
-
81
- `clientId` is optional only when exactly one ready Extension is connected. If multiple are ready, specify one; Bridge returns `409 ClientIdRequired` rather than broadcasting the command. Pending pairing connections are not eligible targets.
82
-
83
- | Command | `input` |
84
- | ----------------- | ---------------------------------------------------------------------------------------------------- |
85
- | `workflow.start` | `workflowId`; optional `args` object (default `{}`); optional `tab` (default `{ "mode": "active" }`) |
86
- | `workflow.pause` | `workflowId` |
87
- | `workflow.resume` | `workflowId` |
88
- | `workflow.stop` | `workflowId` |
89
- | `workflow.status` | `workflowId` |
90
- | `data.get` | `tableId` |
91
- | `data.delete` | `tableId` |
92
-
93
- `tab` can be `{ "mode": "active" }`, `{ "mode": "new" }`, or `{ "mode": "id", "tabId": 123 }`.
94
-
95
- Start a Workflow:
96
-
97
- ```bash
98
- curl -X POST http://127.0.0.1:9520/v1/commands/workflow.start \
99
- -H 'Content-Type: application/json' \
100
- -d '{"input":{"workflowId":"jr657xeb","args":{"keyword":"Tapicker"},"tab":{"mode":"active"}}}'
101
- ```
102
-
103
- If a Token is configured, add `-H 'Authorization: Bearer YOUR_TOKEN'`.
104
-
105
- Pause, resume, stop, and status use the same request format. For example:
106
-
107
- ```bash
108
- curl -X POST http://127.0.0.1:9520/v1/commands/workflow.pause \
109
- -H 'Content-Type: application/json' \
110
- -d '{"clientId":"56a450e6-41f1-448e-9ea7-a2d958cbda37","input":{"workflowId":"jr657xeb"}}'
111
- ```
112
-
113
- A successful start response includes the selected `clientId`, `workflowId`, full `workflowName`, and an accepted state. Other successful responses use `{ "ok": true, "data": ... }`. Errors use `{ "ok": false, "error": { "code", "message", "details?" } }`.
114
-
115
- ## Read or Delete a Data Table
116
-
117
- Use the `tableId` copied from the Extension’s Data list or detail page:
118
-
119
- ```bash
120
- curl -X POST http://127.0.0.1:9520/v1/commands/data.get \
121
- -H 'Content-Type: application/json' \
122
- -d '{"input":{"tableId":"4a1b2c3d"}}'
123
- ```
124
-
125
- `data.get` returns `tableId`, `tableName`, `count`, and all `rows`. The row count consumes the connected Extension account’s export quota. If the quota is insufficient or unavailable, Bridge returns an error without the data.
126
-
127
- Delete a table with `POST /v1/commands/data.delete` and the same `input` shape. It removes the table from Tapicker Data; verify the `tableId` before deleting. The success response contains `tableId` and `tableName`.
128
-
129
- ## Get Workflow Status
130
-
131
- `workflow.status` returns one snapshot with `workflowId`, `state`, and ISO-formatted `updatedAt`. States include `running`, `paused`, `stopping`, `completed`, `stopped`, and `failed`.
132
-
133
- For ongoing updates, subscribe to the WebSocket endpoint `/v1/commands/workflow.watch` instead of repeatedly calling `workflow.status`. After reconnecting, call `workflow.status` once to get the latest snapshot. See [Workflow Status WebSocket](workflow-watch.md).
134
-
135
- The `workflow.watch` path requires a WebSocket upgrade; a regular HTTP request returns `405 WebSocketRequired`.
136
-
137
- ## Common Errors
138
-
139
- | HTTP status | Example codes | Meaning |
140
- | ----------- | ----------------------------------------------------------------------------- | ------------------------------------------------------ |
141
- | `400` | `InvalidArguments`, `InvalidJson` | The request body or command input is invalid. |
142
- | `401` | `Unauthorized` | A required Bearer Token is missing or invalid. |
143
- | `404` | `NotFound`, `CommandNotFound` | The endpoint or command does not exist. |
144
- | `409` | `ClientIdRequired`, `ExtensionNotConnected`, `ExtensionCapabilityUnavailable` | Select a ready Extension or check its availability. |
145
- | `502` | `ExportQuotaExceeded` | Reading the table exceeded the account’s export quota. |
146
- | `503` | `ExtensionUnavailable` | No paired, ready Extension is connected. |
147
- | `504` | `ExtensionRequestTimeout` | The Extension did not respond in time. |
148
-
149
- Use the returned `error.code` and `error.message` as the source of truth for a specific failure.
package/docs/mcp.md DELETED
@@ -1,90 +0,0 @@
1
- # MCP Guide
2
-
3
- Tapicker Bridge exposes Workflow and data commands as MCP tools. The Bridge must already be running, and the target Tapicker Extension must be paired and ready.
4
-
5
- For a first-time setup, follow the [Getting Started guide](https://www.tapicker.com/docs/bridge-getting-started). For an overview of the user flow, see [Connect an AI Agent](https://www.tapicker.com/docs/bridge-ai-agents).
6
-
7
- ## Choose a Transport
8
-
9
- | Transport | How the agent connects | Best for |
10
- | --------------- | -------------------------------------------------------------------- | ------------------------------------------------------ |
11
- | stdio | The agent starts `tapicker mcp`, which connects to Bridge over HTTP. | An agent running on the same computer as Bridge. |
12
- | Streamable HTTP | The agent connects directly to `https://<host>:<port>/mcp`. | A remote Bridge and an agent that supports remote MCP. |
13
-
14
- Both transports expose the same tools. The stdio adapter does not start a Bridge server.
15
-
16
- ## Local MCP over stdio
17
-
18
- Start Bridge separately, then add an MCP server entry to your agent configuration:
19
-
20
- ```json
21
- {
22
- "mcpServers": {
23
- "tapicker": {
24
- "command": "tapicker",
25
- "args": ["mcp"],
26
- "env": {
27
- "TAPICKER_BRIDGE_URL": "http://127.0.0.1:9520"
28
- }
29
- }
30
- }
31
- }
32
- ```
33
-
34
- Restart or reload the agent after changing its configuration. The CLI defaults to `http://127.0.0.1:9520`; if Bridge selected another port, update `TAPICKER_BRIDGE_URL` to the actual HTTP address printed at startup.
35
-
36
- When the Bridge uses a Token, add `TAPICKER_BRIDGE_TOKEN` to the MCP server environment. Prefer environment variables or your agent’s secret store over putting a Token in a shared config file.
37
-
38
- If the agent cannot find `tapicker`, set `command` to the absolute path of the installed executable. When running from a source checkout, use `node` as the command and the absolute path to `dist/cli.js` followed by `mcp` as the arguments.
39
-
40
- You can also launch the adapter manually:
41
-
42
- ```bash
43
- tapicker mcp --url http://127.0.0.1:9520
44
- ```
45
-
46
- ## Remote MCP over Streamable HTTP
47
-
48
- The MCP endpoint uses the same port as HTTP and WebSocket:
49
-
50
- ```text
51
- https://bridge.example.com:9520/mcp
52
- ```
53
-
54
- Configure the agent’s remote MCP connection with this URL. If Bridge uses a Token, configure this request header:
55
-
56
- ```http
57
- Authorization: Bearer YOUR_TOKEN
58
- ```
59
-
60
- Remote deployments require TLS and a Bearer Token. See [Pairing, Configuration, and Security](operations.md).
61
-
62
- ## Available Tools
63
-
64
- | Tool | Parameters | Purpose |
65
- | ----------------- | ------------------------------------------------ | ------------------------------------------------------------- |
66
- | `extensions_list` | None | List Extension clients and find a target `clientId`. |
67
- | `workflow_start` | `workflowId`, optional `args`, `tab`, `clientId` | Start a Workflow. Returns when the start request is accepted. |
68
- | `workflow_pause` | `workflowId`, optional `clientId` | Pause an active Workflow. |
69
- | `workflow_resume` | `workflowId`, optional `clientId` | Resume a paused Workflow. |
70
- | `workflow_stop` | `workflowId`, optional `clientId` | Stop an active Workflow. |
71
- | `workflow_status` | `workflowId`, optional `clientId` | Get one current or most recently persisted status snapshot. |
72
- | `workflow_watch` | `workflowId`, optional `clientId`, `timeoutMs` | Wait for status updates until a terminal state or timeout. |
73
- | `data_get` | `tableId`, optional `clientId` | Read all rows from a data table; consumes export quota. |
74
- | `data_delete` | `tableId`, optional `clientId` | Delete a data table from Tapicker Data. |
75
-
76
- `workflow_start.tab` accepts `{ "mode": "active" }`, `{ "mode": "new" }`, or `{ "mode": "id", "tabId": 123 }`; it defaults to the active tab. `args` must be an object.
77
-
78
- `data_get` returns `tableId`, `tableName`, and all rows. The row count consumes the connected Extension account’s export quota; when the quota is insufficient or unavailable, the operation fails without returning data. `data_delete` removes the table, so verify the `tableId` first.
79
-
80
- ## Select an Extension
81
-
82
- If exactly one ready Extension is connected, `clientId` can be omitted. If multiple are ready, call `extensions_list` and pass the chosen `clientId` to the command. Pending pairing connections may appear in the list but are not command targets. Bridge sends each command to one Extension; it does not broadcast.
83
-
84
- ## Watch a Long-Running Workflow
85
-
86
- `workflow_start` returns after the Extension accepts the start request. Then call `workflow_watch` to wait for updates without repeatedly calling `workflow_status`. Its terminal states are `completed`, `stopped`, and `failed`.
87
-
88
- When supported by the MCP client, Bridge also reports intermediate status updates as progress. A `timeoutMs` expiration returns the latest known status with `timedOut: true`. Cancelling the watch stops waiting only; it does not stop the Workflow. Use `workflow_status` when you need a single snapshot.
89
-
90
- The equivalent HTTP/WebSocket protocol is documented in [Workflow Status WebSocket](workflow-watch.md).