@reclaimprotocol/agent 0.1.17 → 0.1.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/README.md +6 -2
- package/lib/authoring/backend.d.ts +4 -0
- package/lib/authoring/tools.js +4 -1
- package/lib/cdp/capture.d.ts +2 -2
- package/lib/cdp/capture.js +25 -21
- package/lib/cdp/client.d.ts +39 -1
- package/lib/cdp/client.js +125 -1
- package/lib/consts.d.ts +7 -0
- package/lib/consts.js +15 -0
- package/lib/mcp/api/operations.gen.js +33 -9
- package/lib/mcp/instructions.js +27 -3
- package/lib/mcp/start.js +12 -8
- package/lib/mcp/tools/agent/attach.d.ts +18 -4
- package/lib/mcp/tools/agent/attach.js +142 -52
- package/lib/mcp/tools/agent/build.d.ts +1 -1
- package/lib/mcp/tools/agent/build.js +5 -3
- package/lib/mcp/tools/agent/capture.d.ts +1 -0
- package/lib/mcp/tools/agent/capture.js +8 -1
- package/lib/mcp/tools/agent/eval.js +2 -3
- package/lib/mcp/tools/agent/how-it-works.d.ts +1 -1
- package/lib/mcp/tools/agent/how-it-works.js +1 -1
- package/lib/mcp/tools/agent/publish.js +55 -1
- package/lib/mcp/tools/agent/session.d.ts +2 -2
- package/lib/mcp/tools/agent/session.js +21 -5
- package/lib/mcp/tools/agent/shared.js +4 -6
- package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
- package/lib/mcp/tools/authenticate/authenticate.js +10 -4
- package/lib/mcp/tools/authenticate/builder-access.d.ts +8 -0
- package/lib/mcp/tools/authenticate/builder-access.js +31 -0
- package/lib/mcp/tools/authenticate/index.d.ts +1 -0
- package/lib/mcp/tools/authenticate/index.js +1 -0
- package/lib/mcp/tools/mode/mode.d.ts +3 -2
- package/lib/mcp/tools/mode/mode.js +24 -4
- package/lib/old/login-cdp.js +11 -3
- package/lib/old/to-register.d.ts +16 -8
- package/lib/old/to-register.js +29 -21
- package/lib/old/tools/publish.js +109 -9
- package/lib/provider/draft.d.ts +20 -0
- package/lib/provider/draft.js +75 -51
- package/lib/provider/index.d.ts +1 -1
- package/lib/provider/index.js +1 -1
- package/lib/provider/injection-doc.js +4 -3
- package/lib/provider/schema.d.ts +26 -1
- package/lib/provider/secrets.d.ts +37 -0
- package/lib/provider/secrets.js +147 -6
- package/lib/provider/to-version.d.ts +26 -2
- package/lib/provider/to-version.js +29 -5
- package/package.json +2 -2
- package/skill/SKILL.builder.md +43 -95
- package/skill/SKILL.common.md +102 -0
- package/skill/SKILL.old.md +20 -74
- package/skill/builder-mode.md +4 -2
- package/skill/injection-api.md +95 -10
- package/skill/old-mode.md +5 -3
- package/src/authoring/backend.ts +4 -0
- package/src/authoring/tools.ts +4 -1
- package/src/cdp/capture.ts +24 -24
- package/src/cdp/client.ts +197 -2
- package/src/consts.ts +20 -0
- package/src/mcp/api/operations.gen.ts +33 -9
- package/src/mcp/instructions.ts +36 -3
- package/src/mcp/start.ts +13 -8
- package/src/mcp/tools/agent/attach.ts +198 -57
- package/src/mcp/tools/agent/build.ts +9 -2
- package/src/mcp/tools/agent/capture.ts +10 -1
- package/src/mcp/tools/agent/eval.ts +2 -3
- package/src/mcp/tools/agent/how-it-works.ts +1 -1
- package/src/mcp/tools/agent/publish.ts +64 -1
- package/src/mcp/tools/agent/session.ts +29 -8
- package/src/mcp/tools/agent/shared.ts +5 -10
- package/src/mcp/tools/authenticate/authenticate.ts +14 -4
- package/src/mcp/tools/authenticate/builder-access.ts +42 -0
- package/src/mcp/tools/authenticate/index.ts +4 -0
- package/src/mcp/tools/mode/mode.ts +28 -4
- package/src/old/login-cdp.ts +13 -3
- package/src/old/to-register.ts +50 -27
- package/src/old/tools/publish.ts +131 -9
- package/src/provider/draft.ts +98 -56
- package/src/provider/index.ts +9 -1
- package/src/provider/injection-doc.ts +4 -3
- package/src/provider/schema.ts +27 -1
- package/src/provider/secrets.ts +171 -6
- package/src/provider/to-version.ts +53 -7
- package/lib/authoring/find.d.ts +0 -3
- package/lib/authoring/find.js +0 -54
- package/lib/authoring/propose.d.ts +0 -3
- package/lib/authoring/propose.js +0 -109
- package/lib/authoring/replay.d.ts +0 -10
- package/lib/authoring/replay.js +0 -34
- package/lib/cli/args.d.ts +0 -14
- package/lib/cli/args.js +0 -19
- package/lib/cli/commands.d.ts +0 -1
- package/lib/cli/commands.js +0 -19
- package/lib/cli.d.ts +0 -1
- package/lib/cli.js +0 -7
- package/lib/mcp/credentials/authenticate.d.ts +0 -3
- package/lib/mcp/credentials/authenticate.js +0 -108
- package/lib/mcp/credentials/build.d.ts +0 -5
- package/lib/mcp/credentials/build.js +0 -18
- package/lib/mcp/credentials/cache.d.ts +0 -70
- package/lib/mcp/credentials/cache.js +0 -131
- package/lib/mcp/credentials/env-file.d.ts +0 -25
- package/lib/mcp/credentials/env-file.js +0 -71
- package/lib/mcp/credentials/import.d.ts +0 -2
- package/lib/mcp/credentials/import.js +0 -52
- package/lib/mcp/credentials/issue.d.ts +0 -2
- package/lib/mcp/credentials/issue.js +0 -84
- package/lib/mcp/credentials/keypair.d.ts +0 -17
- package/lib/mcp/credentials/keypair.js +0 -48
- package/lib/mcp/credentials/link.d.ts +0 -3
- package/lib/mcp/credentials/link.js +0 -107
- package/lib/mcp/credentials/list-cached.d.ts +0 -2
- package/lib/mcp/credentials/list-cached.js +0 -55
- package/lib/mcp/credentials/owner-key.d.ts +0 -23
- package/lib/mcp/credentials/owner-key.js +0 -58
- package/lib/mcp/credentials/paths.d.ts +0 -12
- package/lib/mcp/credentials/paths.js +0 -35
- package/lib/mcp/credentials/poll.d.ts +0 -27
- package/lib/mcp/credentials/poll.js +0 -61
- package/lib/mcp/credentials/register.d.ts +0 -18
- package/lib/mcp/credentials/register.js +0 -30
- package/lib/mcp/credentials/resolve-owner-key.d.ts +0 -12
- package/lib/mcp/credentials/resolve-owner-key.js +0 -68
- package/lib/mcp/credentials/session-cache.d.ts +0 -14
- package/lib/mcp/credentials/session-cache.js +0 -42
- package/lib/mcp/credentials/status.d.ts +0 -3
- package/lib/mcp/credentials/status.js +0 -76
- package/lib/mcp/register.d.ts +0 -38
- package/lib/mcp/register.js +0 -80
- package/lib/mcp/tools/analyze.d.ts +0 -3
- package/lib/mcp/tools/analyze.js +0 -26
- package/lib/mcp/tools/attach.d.ts +0 -11
- package/lib/mcp/tools/attach.js +0 -72
- package/lib/mcp/tools/capture.d.ts +0 -10
- package/lib/mcp/tools/capture.js +0 -142
- package/lib/mcp/tools/eval.d.ts +0 -5
- package/lib/mcp/tools/eval.js +0 -127
- package/lib/mcp/tools/find.d.ts +0 -3
- package/lib/mcp/tools/find.js +0 -60
- package/lib/mcp/tools/owner-key.d.ts +0 -2
- package/lib/mcp/tools/owner-key.js +0 -63
- package/lib/mcp/tools/proof.d.ts +0 -3
- package/lib/mcp/tools/proof.js +0 -214
- package/lib/mcp/tools/propose.d.ts +0 -9
- package/lib/mcp/tools/propose.js +0 -75
- package/lib/mcp/tools/publish.d.ts +0 -3
- package/lib/mcp/tools/publish.js +0 -59
- package/lib/mcp/tools/replay.d.ts +0 -3
- package/lib/mcp/tools/replay.js +0 -46
- package/lib/mcp/tools/session.d.ts +0 -10
- package/lib/mcp/tools/session.js +0 -29
- package/lib/mcp/tools/verification/diagnose.d.ts +0 -11
- package/lib/mcp/tools/verification/diagnose.js +0 -132
- package/lib/mcp/verifications/build.d.ts +0 -5
- package/lib/mcp/verifications/build.js +0 -14
- package/lib/mcp/verifications/create.d.ts +0 -3
- package/lib/mcp/verifications/create.js +0 -57
- package/lib/mcp/verifications/find.d.ts +0 -3
- package/lib/mcp/verifications/find.js +0 -37
- package/lib/mcp/verifications/get.d.ts +0 -3
- package/lib/mcp/verifications/get.js +0 -26
- package/lib/mcp/verifications/update.d.ts +0 -3
- package/lib/mcp/verifications/update.js +0 -78
- package/lib/old/tools/get-version-config.d.ts +0 -17
- package/lib/old/tools/get-version-config.js +0 -47
- package/lib/routes.gen.d.ts +0 -271
- package/lib/routes.gen.js +0 -73
- package/lib/types/openapi.d.ts +0 -4
- package/lib/types/openapi.gen.d.ts +0 -3967
- package/lib/types/openapi.gen.js +0 -5
- package/lib/types/openapi.js +0 -1
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ or you can even ask it like:
|
|
|
65
65
|
The agent orchestrates the tools for you:
|
|
66
66
|
|
|
67
67
|
1. **`reclaim_authenticate`** — a Chrome window opens the Reclaim dashboard; you sign in. The token is captured automatically and the tab closes. (Or it reuses a still-valid cached identity.)
|
|
68
|
-
2. **`
|
|
68
|
+
2. **`attach_browser` + `navigate`** — uses local Chrome, a Builder-hosted remote browser, or a custom CDP websocket; **`wait_for_page`** waits while the operator logs in.
|
|
69
69
|
3. **`start_capture` → `find_requests_containing` → `get_request`** — records network traffic and locates the request carrying your value.
|
|
70
70
|
4. **`propose_provider`** — drafts the provider (a `contains` match + `jsonPath`/`xPath`/`regex` redaction), keeping any secret headers server-side.
|
|
71
71
|
5. **`replay_request` → `run_proof`** — pre-flights the draft, then produces a verified zkTLS proof via the attestor.
|
|
@@ -83,7 +83,8 @@ These capture/synthesis/proof tools are backend-independent and registered in bo
|
|
|
83
83
|
|
|
84
84
|
| Tool | Purpose |
|
|
85
85
|
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
86
|
-
| `
|
|
86
|
+
| `attach_browser` | Connect via local Chrome, Builder-hosted Popcorn with a shareable live view, or a custom CDP websocket |
|
|
87
|
+
| `attach_chrome` | Backward-compatible alias for `attach_browser` |
|
|
87
88
|
| `list_tabs` | List open Chrome tabs |
|
|
88
89
|
| `navigate`, `wait_for_page`, `eval_in_page` | Drive the tab; poll for login completion |
|
|
89
90
|
| `start_capture`, `stop_capture` | Network capture lifecycle |
|
|
@@ -94,6 +95,9 @@ These capture/synthesis/proof tools are backend-independent and registered in bo
|
|
|
94
95
|
| `replay_request` | Pre-flight replay via Node `fetch` (no attestor); also used for auth-bound check by omitting `secretParamsRef` |
|
|
95
96
|
| `run_proof` | End-to-end zkTLS proof through the attestor SDK |
|
|
96
97
|
| `create_provider_version_from_capture` | Publish a drafted provider to the backend |
|
|
98
|
+
| `authenticate_builder` | In old-devtools mode only, sign in to Builder for remote-browser allocation |
|
|
99
|
+
| `list_builder_organizations` | In old-devtools mode only, list organization ids for a quota-accounted Builder browser |
|
|
100
|
+
| `dispose_browser` | Stop captures, disconnect CDP, and immediately release a Builder-hosted browser |
|
|
97
101
|
| `get_session`, `reset_session` | Session state |
|
|
98
102
|
|
|
99
103
|
## Backends
|
|
@@ -42,6 +42,10 @@ export interface ProveOptions {
|
|
|
42
42
|
* its own result, passed straight through as the tool's output.
|
|
43
43
|
*/
|
|
44
44
|
export interface AuthoringBackend {
|
|
45
|
+
/** Ask Chrome to reject page evaluation when side effects cannot be ruled
|
|
46
|
+
* out. Cloud authoring enables this; local MCP keeps its existing
|
|
47
|
+
* behavior. */
|
|
48
|
+
readonly readOnlyPageEval?: boolean;
|
|
45
49
|
/** Send a raw CDP command scoped to this session's page. */
|
|
46
50
|
sendCdp(method: string, params?: Record<string, unknown>): Promise<unknown>;
|
|
47
51
|
/** Resolve once `method` (a CDP event) fires on this session's page, or
|
package/lib/authoring/tools.js
CHANGED
|
@@ -49,7 +49,10 @@ function globMatch(pattern, str) {
|
|
|
49
49
|
}
|
|
50
50
|
async function evalInPage(backend, expression) {
|
|
51
51
|
const res = (await backend.sendCdp('Runtime.evaluate', {
|
|
52
|
-
expression,
|
|
52
|
+
expression,
|
|
53
|
+
returnByValue: true,
|
|
54
|
+
awaitPromise: true,
|
|
55
|
+
...(backend.readOnlyPageEval ? { throwOnSideEffect: true } : {}),
|
|
53
56
|
}));
|
|
54
57
|
if (res.exceptionDetails) {
|
|
55
58
|
return {
|
package/lib/cdp/capture.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* request/response bookkeeping lives in `capture-engine.ts`, shared with the
|
|
5
5
|
* app's remote-Popcorn transport.
|
|
6
6
|
*/
|
|
7
|
-
import type CDP from 'chrome-remote-interface';
|
|
8
7
|
import { type CaptureSession, type StartCaptureOptions } from './capture-engine.ts';
|
|
8
|
+
import type { CdpTabClient } from './client.ts';
|
|
9
9
|
export { isStaticAsset, isStaticAssetMime, stripPseudoHeaders, } from './capture-engine.ts';
|
|
10
10
|
export type { CaptureSession, StartCaptureOptions } from './capture-engine.ts';
|
|
11
|
-
export declare function startCapture(tab:
|
|
11
|
+
export declare function startCapture(tab: CdpTabClient, opts?: StartCaptureOptions): Promise<CaptureSession>;
|
package/lib/cdp/capture.js
CHANGED
|
@@ -1,35 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local-CDP transport for the capture engine. Binds the page-level `Network.*`
|
|
3
|
+
* events directly and fetches bodies via the page's `getResponseBody`. All the
|
|
4
|
+
* request/response bookkeeping lives in `capture-engine.ts`, shared with the
|
|
5
|
+
* app's remote-Popcorn transport.
|
|
6
|
+
*/
|
|
1
7
|
import { createCaptureEngine, } from "./capture-engine.js";
|
|
2
8
|
export { isStaticAsset, isStaticAssetMime, stripPseudoHeaders, } from "./capture-engine.js";
|
|
3
9
|
export async function startCapture(tab, opts = {}) {
|
|
4
10
|
const engine = createCaptureEngine({
|
|
5
11
|
...(opts.maxBodyBytes !== undefined && { maxBodyBytes: opts.maxBodyBytes }),
|
|
6
|
-
fetchBody:
|
|
7
|
-
});
|
|
8
|
-
const { Network } = tab;
|
|
9
|
-
await Network.enable();
|
|
10
|
-
Network.requestWillBeSent(engine.onRequestWillBeSent);
|
|
11
|
-
// CDP intentionally omits `Cookie` (and other browser-injected headers)
|
|
12
|
-
// from `requestWillBeSent.request.headers` for privacy. The real
|
|
13
|
-
// outgoing headers — including Cookie — arrive on the matching
|
|
14
|
-
// `requestWillBeSentExtraInfo` event.
|
|
15
|
-
Network.requestWillBeSentExtraInfo(({ requestId, headers }) => {
|
|
16
|
-
engine.mergeRequestHeaders(requestId, headers);
|
|
17
|
-
});
|
|
18
|
-
Network.responseReceived(engine.onResponseReceived);
|
|
19
|
-
// Same privacy carve-out applies to `Set-Cookie` and other browser-
|
|
20
|
-
// visible response headers; they arrive on responseReceivedExtraInfo.
|
|
21
|
-
Network.responseReceivedExtraInfo(({ requestId, headers }) => {
|
|
22
|
-
engine.mergeResponseHeaders(requestId, headers);
|
|
23
|
-
});
|
|
24
|
-
Network.loadingFinished(({ requestId }) => {
|
|
25
|
-
void engine.onLoadingFinished(requestId);
|
|
12
|
+
fetchBody: tab.getResponseBody,
|
|
26
13
|
});
|
|
14
|
+
const unsubscribe = [
|
|
15
|
+
tab.subscribe('Network.requestWillBeSent', engine.onRequestWillBeSent),
|
|
16
|
+
tab.subscribe('Network.requestWillBeSentExtraInfo', (event) => {
|
|
17
|
+
engine.mergeRequestHeaders(event.requestId, event.headers);
|
|
18
|
+
}),
|
|
19
|
+
tab.subscribe('Network.responseReceived', engine.onResponseReceived),
|
|
20
|
+
tab.subscribe('Network.responseReceivedExtraInfo', (event) => {
|
|
21
|
+
engine.mergeResponseHeaders(event.requestId, event.headers);
|
|
22
|
+
}),
|
|
23
|
+
tab.subscribe('Network.loadingFinished', (event) => {
|
|
24
|
+
void engine.onLoadingFinished(event.requestId);
|
|
25
|
+
}),
|
|
26
|
+
];
|
|
27
|
+
await tab.send('Network.enable', {});
|
|
27
28
|
return {
|
|
28
29
|
captureId: engine.captureId,
|
|
29
30
|
requests: engine.requests,
|
|
30
31
|
bodies: engine.bodies,
|
|
31
32
|
stop: async () => {
|
|
32
|
-
|
|
33
|
+
for (const stopListening of unsubscribe) {
|
|
34
|
+
stopListening();
|
|
35
|
+
}
|
|
36
|
+
await tab.send('Network.disable', {}).catch(() => { });
|
|
33
37
|
},
|
|
34
38
|
};
|
|
35
39
|
}
|
package/lib/cdp/client.d.ts
CHANGED
|
@@ -1,9 +1,47 @@
|
|
|
1
1
|
import CDP from 'chrome-remote-interface';
|
|
2
|
+
import type { RequestWillBeSentLike, ResponseReceivedLike } from './capture-engine.ts';
|
|
3
|
+
import type { TabInfo } from './tabs.ts';
|
|
4
|
+
interface CdpTabEventMap {
|
|
5
|
+
'Network.requestWillBeSent': RequestWillBeSentLike;
|
|
6
|
+
'Network.requestWillBeSentExtraInfo': {
|
|
7
|
+
requestId: string;
|
|
8
|
+
headers: Record<string, string>;
|
|
9
|
+
};
|
|
10
|
+
'Network.responseReceived': ResponseReceivedLike & {
|
|
11
|
+
response: ResponseReceivedLike['response'] & {
|
|
12
|
+
url: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
'Network.responseReceivedExtraInfo': {
|
|
16
|
+
requestId: string;
|
|
17
|
+
headers: Record<string, string>;
|
|
18
|
+
};
|
|
19
|
+
'Network.loadingFinished': {
|
|
20
|
+
requestId: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface CdpTabClient {
|
|
24
|
+
send: (method: string, params?: object) => Promise<unknown>;
|
|
25
|
+
getResponseBody: (requestId: string) => Promise<{
|
|
26
|
+
body: string;
|
|
27
|
+
base64Encoded: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
subscribe: <K extends keyof CdpTabEventMap>(method: K, listener: (params: CdpTabEventMap[K]) => void) => () => void;
|
|
30
|
+
subscribeOnce: (method: string, listener: () => void) => () => void;
|
|
31
|
+
close: () => Promise<void>;
|
|
32
|
+
}
|
|
2
33
|
export interface CdpHandles {
|
|
3
34
|
/** Browser-level connection (target list, attach). */
|
|
4
35
|
browser: CDP.Client;
|
|
5
36
|
/** Returns a per-target session bound to a tab. */
|
|
6
|
-
attachToTab: (targetId: string) => Promise<
|
|
37
|
+
attachToTab: (targetId: string) => Promise<CdpTabClient>;
|
|
38
|
+
/** Enumerate page targets without assuming a localhost discovery endpoint. */
|
|
39
|
+
listTabs: () => Promise<TabInfo[]>;
|
|
7
40
|
close: () => Promise<void>;
|
|
8
41
|
}
|
|
9
42
|
export declare function connectCdp(port: number): Promise<CdpHandles>;
|
|
43
|
+
/** Connect to a browser-level or page-level CDP websocket. Browser-level
|
|
44
|
+
* endpoints (including Popcorn) use flattened Target sessions; a page-level
|
|
45
|
+
* custom endpoint is exposed as one synthetic tab and used directly. */
|
|
46
|
+
export declare function connectCdpUrl(webSocketUrl: string): Promise<CdpHandles>;
|
|
47
|
+
export {};
|
package/lib/cdp/client.js
CHANGED
|
@@ -1,11 +1,135 @@
|
|
|
1
1
|
import CDP from 'chrome-remote-interface';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { EventEmitter } from 'node:events';
|
|
4
|
+
import { listTabs } from "./tabs.js";
|
|
5
|
+
const DIRECT_TARGET_ID = '__direct_cdp_target__';
|
|
2
6
|
export async function connectCdp(port) {
|
|
3
7
|
const browser = await CDP({ port });
|
|
8
|
+
const tabs = new Set();
|
|
4
9
|
return {
|
|
5
10
|
browser,
|
|
6
|
-
attachToTab: (targetId) =>
|
|
11
|
+
attachToTab: async (targetId) => {
|
|
12
|
+
const tab = adaptCdpClient(await CDP({ port, target: targetId }));
|
|
13
|
+
tabs.add(tab);
|
|
14
|
+
return tab;
|
|
15
|
+
},
|
|
16
|
+
listTabs: () => listTabs(port),
|
|
17
|
+
close: async () => {
|
|
18
|
+
for (const tab of tabs) {
|
|
19
|
+
await tab.close().catch(() => { });
|
|
20
|
+
}
|
|
21
|
+
await browser.close();
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Connect to a browser-level or page-level CDP websocket. Browser-level
|
|
26
|
+
* endpoints (including Popcorn) use flattened Target sessions; a page-level
|
|
27
|
+
* custom endpoint is exposed as one synthetic tab and used directly. */
|
|
28
|
+
export async function connectCdpUrl(webSocketUrl) {
|
|
29
|
+
const browser = await CDP({ target: webSocketUrl, local: true });
|
|
30
|
+
let browserLevel = true;
|
|
31
|
+
try {
|
|
32
|
+
await browser.send('Target.getTargets');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
browserLevel = false;
|
|
36
|
+
}
|
|
37
|
+
const tabs = new Set();
|
|
38
|
+
const directTab = browserLevel ? undefined : adaptCdpClient(browser);
|
|
39
|
+
return {
|
|
40
|
+
browser,
|
|
41
|
+
listTabs: async () => {
|
|
42
|
+
if (!browserLevel) {
|
|
43
|
+
return [{
|
|
44
|
+
tabId: DIRECT_TARGET_ID, url: '', title: 'Direct CDP target',
|
|
45
|
+
}];
|
|
46
|
+
}
|
|
47
|
+
const { targetInfos } = await browser.send('Target.getTargets');
|
|
48
|
+
return targetInfos
|
|
49
|
+
.filter((target) => target.type === 'page')
|
|
50
|
+
.map((target) => ({
|
|
51
|
+
tabId: target.targetId,
|
|
52
|
+
url: target.url,
|
|
53
|
+
title: target.title,
|
|
54
|
+
}));
|
|
55
|
+
},
|
|
56
|
+
attachToTab: async (targetId) => {
|
|
57
|
+
if (!browserLevel) {
|
|
58
|
+
if (targetId !== DIRECT_TARGET_ID) {
|
|
59
|
+
throw new Error(`Unknown direct CDP target ${targetId}`);
|
|
60
|
+
}
|
|
61
|
+
assert(directTab);
|
|
62
|
+
return directTab;
|
|
63
|
+
}
|
|
64
|
+
const { sessionId } = await browser.send('Target.attachToTarget', {
|
|
65
|
+
targetId,
|
|
66
|
+
flatten: true,
|
|
67
|
+
});
|
|
68
|
+
const tab = flattenedSessionClient(browser, sessionId);
|
|
69
|
+
tabs.add(tab);
|
|
70
|
+
return tab;
|
|
71
|
+
},
|
|
7
72
|
close: async () => {
|
|
73
|
+
for (const tab of tabs) {
|
|
74
|
+
await tab.close().catch(() => { });
|
|
75
|
+
}
|
|
8
76
|
await browser.close();
|
|
9
77
|
},
|
|
10
78
|
};
|
|
11
79
|
}
|
|
80
|
+
/** Adapt a flattened browser Target session to the small tab-client surface
|
|
81
|
+
* consumed by capture and authoring. Events are filtered by session id so
|
|
82
|
+
* concurrent tabs cannot observe each other's traffic. */
|
|
83
|
+
function flattenedSessionClient(browser, sessionId) {
|
|
84
|
+
const events = new EventEmitter();
|
|
85
|
+
// The declaration only accepts literal protocol method names; this adapter
|
|
86
|
+
// deliberately forwards the shared authoring layer's dynamic method string.
|
|
87
|
+
const sender = browser.send.bind(browser);
|
|
88
|
+
const onEvent = (message) => {
|
|
89
|
+
if (message.sessionId !== sessionId) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
events.emit('event', message);
|
|
93
|
+
events.emit(message.method, message.params);
|
|
94
|
+
};
|
|
95
|
+
browser.on('event', onEvent);
|
|
96
|
+
return tabClient(events, (method, params) => {
|
|
97
|
+
return sender(method, params ?? {}, sessionId);
|
|
98
|
+
}, (requestId) => {
|
|
99
|
+
return sender('Network.getResponseBody', { requestId }, sessionId);
|
|
100
|
+
}, async () => {
|
|
101
|
+
EventEmitter.prototype.removeListener.call(browser, 'event', onEvent);
|
|
102
|
+
await browser.send('Target.detachFromTarget', { sessionId }).catch(() => { });
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
function adaptCdpClient(client) {
|
|
106
|
+
const events = new EventEmitter();
|
|
107
|
+
const onEvent = (message) => {
|
|
108
|
+
events.emit(message.method, message.params);
|
|
109
|
+
};
|
|
110
|
+
client.on('event', onEvent);
|
|
111
|
+
// CRI intentionally types send against protocol literals. This adapter is
|
|
112
|
+
// the single boundary where shared authoring commands arrive dynamically.
|
|
113
|
+
const send = client.send.bind(client);
|
|
114
|
+
return tabClient(events, send, (requestId) => {
|
|
115
|
+
return client.send('Network.getResponseBody', { requestId });
|
|
116
|
+
}, async () => {
|
|
117
|
+
EventEmitter.prototype.removeListener.call(client, 'event', onEvent);
|
|
118
|
+
await client.close();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function tabClient(events, send, getResponseBody, close) {
|
|
122
|
+
return {
|
|
123
|
+
send,
|
|
124
|
+
getResponseBody,
|
|
125
|
+
subscribe(method, listener) {
|
|
126
|
+
events.on(method, listener);
|
|
127
|
+
return () => events.removeListener(method, listener);
|
|
128
|
+
},
|
|
129
|
+
subscribeOnce(method, listener) {
|
|
130
|
+
events.once(method, listener);
|
|
131
|
+
return () => events.removeListener(method, listener);
|
|
132
|
+
},
|
|
133
|
+
close,
|
|
134
|
+
};
|
|
135
|
+
}
|
package/lib/consts.d.ts
CHANGED
|
@@ -44,6 +44,13 @@ export declare function useOldDevtools(): boolean;
|
|
|
44
44
|
* `ConfigStore` (which would create an import cycle).
|
|
45
45
|
*/
|
|
46
46
|
export declare function resolveOldMode(persisted?: 'old' | 'builder'): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Where the effective mode came from — the same precedence as
|
|
49
|
+
* `resolveOldMode`: explicit `USE_OLD_DEVTOOLS` env var → persisted config
|
|
50
|
+
* `mode` → default (old). Surfaced by the `get_devtools_mode` tool so the
|
|
51
|
+
* user can tell why the server is in the mode it reports.
|
|
52
|
+
*/
|
|
53
|
+
export declare function oldModeSource(persisted?: 'old' | 'builder'): 'env' | 'config' | 'default';
|
|
47
54
|
/** `%LOCALAPPDATA%` on Windows, used to probe a per-user Chrome install. */
|
|
48
55
|
export declare function localAppData(): string | undefined;
|
|
49
56
|
/** Explicit Chrome/Edge/Chromium binary path override. */
|
package/lib/consts.js
CHANGED
|
@@ -76,6 +76,21 @@ export function resolveOldMode(persisted) {
|
|
|
76
76
|
}
|
|
77
77
|
return true;
|
|
78
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* Where the effective mode came from — the same precedence as
|
|
81
|
+
* `resolveOldMode`: explicit `USE_OLD_DEVTOOLS` env var → persisted config
|
|
82
|
+
* `mode` → default (old). Surfaced by the `get_devtools_mode` tool so the
|
|
83
|
+
* user can tell why the server is in the mode it reports.
|
|
84
|
+
*/
|
|
85
|
+
export function oldModeSource(persisted) {
|
|
86
|
+
if (oldDevtoolsEnvSet()) {
|
|
87
|
+
return 'env';
|
|
88
|
+
}
|
|
89
|
+
if (persisted) {
|
|
90
|
+
return 'config';
|
|
91
|
+
}
|
|
92
|
+
return 'default';
|
|
93
|
+
}
|
|
79
94
|
// ---------------------------------------------------------------------------
|
|
80
95
|
// Chrome / CDP launcher.
|
|
81
96
|
// ---------------------------------------------------------------------------
|
|
@@ -161,7 +161,7 @@ export const MCP_TOOLS = {
|
|
|
161
161
|
}),
|
|
162
162
|
},
|
|
163
163
|
"AdminSessions": {
|
|
164
|
-
description: "Explore verification sessions (admin-only)\n\nAdmin only. Search verification sessions across all organizations —
|
|
164
|
+
description: "Explore verification sessions (admin-only)\n\nAdmin only. Search verification sessions across all organizations — filter by `sessionId` (prefix match, at least 5 characters), `providerId`, `claimantClientId`, `orgId` (all exact match), `status`, and a `createdAfter`/`createdBefore` time range. Rows link to the org-scoped session detail page (admins can open any org's pages). Capped at 100, newest first.",
|
|
165
165
|
inputSchema: toZod({
|
|
166
166
|
"type": "object",
|
|
167
167
|
"properties": {
|
|
@@ -172,7 +172,19 @@ export const MCP_TOOLS = {
|
|
|
172
172
|
"query": {
|
|
173
173
|
"type": "object",
|
|
174
174
|
"properties": {
|
|
175
|
-
"
|
|
175
|
+
"sessionId": {
|
|
176
|
+
"type": "string",
|
|
177
|
+
"maxLength": 100
|
|
178
|
+
},
|
|
179
|
+
"providerId": {
|
|
180
|
+
"type": "string",
|
|
181
|
+
"maxLength": 100
|
|
182
|
+
},
|
|
183
|
+
"claimantClientId": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"maxLength": 100
|
|
186
|
+
},
|
|
187
|
+
"orgId": {
|
|
176
188
|
"type": "string",
|
|
177
189
|
"maxLength": 100
|
|
178
190
|
},
|
|
@@ -186,6 +198,14 @@ export const MCP_TOOLS = {
|
|
|
186
198
|
"error",
|
|
187
199
|
"expired"
|
|
188
200
|
]
|
|
201
|
+
},
|
|
202
|
+
"createdAfter": {
|
|
203
|
+
"type": "string",
|
|
204
|
+
"format": "date-time"
|
|
205
|
+
},
|
|
206
|
+
"createdBefore": {
|
|
207
|
+
"type": "string",
|
|
208
|
+
"format": "date-time"
|
|
189
209
|
}
|
|
190
210
|
}
|
|
191
211
|
},
|
|
@@ -589,9 +609,9 @@ export const MCP_TOOLS = {
|
|
|
589
609
|
},
|
|
590
610
|
"prompt": {
|
|
591
611
|
"type": "string",
|
|
592
|
-
"description": "Extraction prompt, used (and required) when `verificationType` is `llm`.",
|
|
612
|
+
"description": "Extraction prompt, used (and required) when `verificationType` is `llm`. Gated by the prompt guard before it is stored (docs/prompt-guard.md).",
|
|
593
613
|
"minLength": 0,
|
|
594
|
-
"maxLength":
|
|
614
|
+
"maxLength": 4000
|
|
595
615
|
},
|
|
596
616
|
"version": {
|
|
597
617
|
"type": "object",
|
|
@@ -1968,7 +1988,7 @@ export const MCP_TOOLS = {
|
|
|
1968
1988
|
}),
|
|
1969
1989
|
},
|
|
1970
1990
|
"ListOrgVerificationSessions": {
|
|
1971
|
-
description: "List an organization's verification sessions\n\nReturns verification sessions created under the organization, newest first. Optionally filter by `providerId`, `status`, `resolvedVersion`, `claimantClientId`, and a `createdAfter`/`createdBefore` time range. Caller must be an active member of the org. Opaque cursor pagination.\n\nNote: `status` filters on the stored column. A session that has passed `expiresAt` but has not yet been swept is reported as `expired` in its body while still stored as `pending`, so `status=expired` may not match such sessions until the sweep runs.",
|
|
1991
|
+
description: "List an organization's verification sessions\n\nReturns verification sessions created under the organization, newest first. Optionally filter by `sessionId` (prefix match, at least 5 characters), `providerId`, `status`, `resolvedVersion`, `claimantClientId`, and a `createdAfter`/`createdBefore` time range. Caller must be an active member of the org. Opaque cursor pagination.\n\nNote: `status` filters on the stored column. A session that has passed `expiresAt` but has not yet been swept is reported as `expired` in its body while still stored as `pending`, so `status=expired` may not match such sessions until the sweep runs.",
|
|
1972
1992
|
inputSchema: toZod({
|
|
1973
1993
|
"type": "object",
|
|
1974
1994
|
"properties": {
|
|
@@ -1988,9 +2008,13 @@ export const MCP_TOOLS = {
|
|
|
1988
2008
|
"query": {
|
|
1989
2009
|
"type": "object",
|
|
1990
2010
|
"properties": {
|
|
2011
|
+
"sessionId": {
|
|
2012
|
+
"type": "string",
|
|
2013
|
+
"maxLength": 100
|
|
2014
|
+
},
|
|
1991
2015
|
"providerId": {
|
|
1992
2016
|
"type": "string",
|
|
1993
|
-
"
|
|
2017
|
+
"maxLength": 100
|
|
1994
2018
|
},
|
|
1995
2019
|
"status": {
|
|
1996
2020
|
"type": "string",
|
|
@@ -2009,7 +2033,7 @@ export const MCP_TOOLS = {
|
|
|
2009
2033
|
},
|
|
2010
2034
|
"claimantClientId": {
|
|
2011
2035
|
"type": "string",
|
|
2012
|
-
"
|
|
2036
|
+
"maxLength": 100
|
|
2013
2037
|
},
|
|
2014
2038
|
"createdAfter": {
|
|
2015
2039
|
"type": "string",
|
|
@@ -2849,9 +2873,9 @@ export const MCP_TOOLS = {
|
|
|
2849
2873
|
},
|
|
2850
2874
|
"prompt": {
|
|
2851
2875
|
"type": "string",
|
|
2852
|
-
"description": "Extraction prompt, used when `verificationType` is `llm`.",
|
|
2876
|
+
"description": "Extraction prompt, used when `verificationType` is `llm`. Gated by the prompt guard before it is stored (docs/prompt-guard.md).",
|
|
2853
2877
|
"minLength": 0,
|
|
2854
|
-
"maxLength":
|
|
2878
|
+
"maxLength": 4000
|
|
2855
2879
|
},
|
|
2856
2880
|
"requests": {
|
|
2857
2881
|
"type": "array",
|
package/lib/mcp/instructions.js
CHANGED
|
@@ -6,9 +6,28 @@
|
|
|
6
6
|
* `lib/mcp` (published) — both are two levels below the package root.
|
|
7
7
|
* Old-devtools mode has its own reduced set in `../old/instructions.ts`.
|
|
8
8
|
*/
|
|
9
|
+
import assert from 'node:assert';
|
|
9
10
|
import { readFileSync } from 'node:fs';
|
|
10
11
|
import { fileURLToPath } from 'node:url';
|
|
11
12
|
export const BUILDER_MODE_INSTRUCTIONS = readFileSync(fileURLToPath(new URL('../../skill/builder-mode.md', import.meta.url)), 'utf8').trim();
|
|
13
|
+
/**
|
|
14
|
+
* `SKILL.builder.md` and `SKILL.old.md` share a lot of prose (steps that are
|
|
15
|
+
* identical regardless of backend, "never do" rules, common troubleshooting).
|
|
16
|
+
* That shared text lives once in `skill/SKILL.common.md`, split into named
|
|
17
|
+
* `<!-- section: name -->`-delimited blocks; each mode file splices a block
|
|
18
|
+
* back in at `<!-- common: name -->`. Parsed once at module load.
|
|
19
|
+
*/
|
|
20
|
+
const COMMON_SECTIONS = parseCommonSections(readFileSync(fileURLToPath(new URL('../../skill/SKILL.common.md', import.meta.url)), 'utf8'));
|
|
21
|
+
function parseCommonSections(text) {
|
|
22
|
+
const sections = {};
|
|
23
|
+
const re = /<!-- section: (\S+) -->\n([\s\S]*?)(?=\n<!-- section: |$)/g;
|
|
24
|
+
for (const match of text.matchAll(re)) {
|
|
25
|
+
// Trim blank LINES only, not inline indentation — some sections are
|
|
26
|
+
// list-continuation text that's intentionally indented on its first line.
|
|
27
|
+
sections[match[1]] = match[2].replace(/^\n+/, '').replace(/\n+$/, '');
|
|
28
|
+
}
|
|
29
|
+
return sections;
|
|
30
|
+
}
|
|
12
31
|
/**
|
|
13
32
|
* The full provider-authoring guide — the deep capture→prove→publish workflow,
|
|
14
33
|
* decision frameworks, and troubleshooting. Served on demand by the
|
|
@@ -19,9 +38,14 @@ export const BUILDER_MODE_INSTRUCTIONS = readFileSync(fileURLToPath(new URL('../
|
|
|
19
38
|
* the instructions above; any leading frontmatter is stripped defensively.
|
|
20
39
|
*/
|
|
21
40
|
function readGuide(file) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
41
|
+
const url = new URL(file, import.meta.url);
|
|
42
|
+
const raw = readFileSync(fileURLToPath(url), 'utf8');
|
|
43
|
+
const stripped = raw.replace(/^---\n[\s\S]*?\n---\n/, '').trim();
|
|
44
|
+
return stripped.replace(/<!-- common: (\S+) -->/g, (whole, name) => {
|
|
45
|
+
const section = COMMON_SECTIONS[name];
|
|
46
|
+
assert(section, `${file}: no SKILL.common.md section named "${name}"`);
|
|
47
|
+
return section;
|
|
48
|
+
});
|
|
25
49
|
}
|
|
26
50
|
export const PROVIDER_GUIDE_BUILDER = readGuide('../../skill/SKILL.builder.md');
|
|
27
51
|
export const PROVIDER_GUIDE_OLD = readGuide('../../skill/SKILL.old.md');
|
package/lib/mcp/start.js
CHANGED
|
@@ -10,7 +10,7 @@ import { ReclaimSdkClient } from "../old/sdk-client.js";
|
|
|
10
10
|
import { buildOldBackendTools } from "../old/tools/build.js";
|
|
11
11
|
import { NAME, VERSION } from "../pkg.js";
|
|
12
12
|
import { registerTools } from "./api/tools.js";
|
|
13
|
-
import { buildAgentTools, buildCredentialTools, buildLocalCredentialTools, buildModeTools, buildVerificationTools, readSession, } from "./tools/index.js";
|
|
13
|
+
import { buildAgentTools, buildBuilderAuthenticationTools, buildBuilderOrganizationTools, buildCredentialTools, buildLocalCredentialTools, buildModeTools, buildVerificationTools, readSession, } from "./tools/index.js";
|
|
14
14
|
import { BUILDER_MODE_INSTRUCTIONS } from "./instructions.js";
|
|
15
15
|
import { registerToolList } from "./server.js";
|
|
16
16
|
export async function startMcpServer() {
|
|
@@ -28,6 +28,13 @@ export async function startMcpServer() {
|
|
|
28
28
|
// deployments — set RECLAIM_AGENT_DISABLED=1 to register only the API
|
|
29
29
|
// surface.
|
|
30
30
|
const agentToolsEnabled = !agentDisabled();
|
|
31
|
+
const builderClient = new ReclaimClient({
|
|
32
|
+
baseUrl: apiUrl(),
|
|
33
|
+
// Builder access is orthogonal to publishing mode. A legacy-devtools
|
|
34
|
+
// session can authenticate here solely to allocate a remote browser.
|
|
35
|
+
token: apiToken() || readSession()?.token,
|
|
36
|
+
headers: { 'User-Agent': `${NAME}/${VERSION}` },
|
|
37
|
+
});
|
|
31
38
|
// Backend-mode toggle — available in both modes and regardless of whether
|
|
32
39
|
// the agent tools are enabled, so a user can always switch either direction.
|
|
33
40
|
registerToolList(server, buildModeTools(config, oldMode));
|
|
@@ -42,21 +49,18 @@ export async function startMcpServer() {
|
|
|
42
49
|
identity: resolveInitialIdentity(),
|
|
43
50
|
});
|
|
44
51
|
const sdkClient = new ReclaimSdkClient({ baseUrl: sdkApiUrl() });
|
|
52
|
+
registerToolList(server, buildBuilderAuthenticationTools(builderClient));
|
|
53
|
+
registerToolList(server, buildBuilderOrganizationTools(builderClient));
|
|
45
54
|
registerToolList(server, buildLocalCredentialTools());
|
|
46
55
|
registerToolList(server, buildOldBackendTools(oldClient, sdkClient));
|
|
47
56
|
if (agentToolsEnabled) {
|
|
48
57
|
// No builder client → no builder publish tool (old mode has its own).
|
|
49
|
-
registerToolList(server, buildAgentTools());
|
|
58
|
+
registerToolList(server, buildAgentTools(undefined, builderClient));
|
|
50
59
|
}
|
|
51
60
|
// NOTE: registerTools() is intentionally NOT called — generated tools off.
|
|
52
61
|
}
|
|
53
62
|
else {
|
|
54
|
-
const client =
|
|
55
|
-
baseUrl: apiUrl(),
|
|
56
|
-
// Priority: explicit env var → cached session token → unauthenticated
|
|
57
|
-
token: apiToken() || readSession()?.token,
|
|
58
|
-
headers: { 'User-Agent': `${NAME}/${VERSION}` },
|
|
59
|
-
});
|
|
63
|
+
const client = builderClient;
|
|
60
64
|
// Tool names must stay disjoint across these three groups: hand-written
|
|
61
65
|
// tools are higher-level orchestrations (multi-call flows, ReclaimProvider
|
|
62
66
|
// translation) and must NOT reuse a generated operation's snake_case name.
|
|
@@ -1,11 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client';
|
|
2
|
+
import { type CdpHandles, connectCdp, connectCdpUrl } from '../../../cdp/client.ts';
|
|
2
3
|
import type { ConfigStore } from '../../../config/store.ts';
|
|
3
4
|
import { type RegisteredTool } from '../../server.ts';
|
|
5
|
+
export type BrowserMode = 'dedicated' | 'attach' | 'builder' | 'custom';
|
|
4
6
|
export interface AttachState {
|
|
5
7
|
handles: CdpHandles;
|
|
6
|
-
|
|
8
|
+
mode: BrowserMode;
|
|
9
|
+
port?: number;
|
|
10
|
+
liveViewUrl?: string;
|
|
11
|
+
browserSessionId?: string;
|
|
12
|
+
orgId?: string;
|
|
13
|
+
disposeRemote?: () => Promise<void>;
|
|
7
14
|
}
|
|
8
|
-
export
|
|
15
|
+
export interface AttachRef {
|
|
9
16
|
current?: AttachState;
|
|
10
17
|
config: ConfigStore;
|
|
11
|
-
}
|
|
18
|
+
}
|
|
19
|
+
interface AttachDependencies {
|
|
20
|
+
connectLocal: typeof connectCdp;
|
|
21
|
+
connectRemote: typeof connectCdpUrl;
|
|
22
|
+
}
|
|
23
|
+
export declare function attachTool(state: AttachRef, builderClient?: ReclaimClient, name?: 'attach_browser' | 'attach_chrome', beforeReplace?: () => Promise<void>, dependencies?: AttachDependencies): RegisteredTool;
|
|
24
|
+
export declare function disposeAttached(state: AttachRef): Promise<void>;
|
|
25
|
+
export {};
|