@reclaimprotocol/agent 0.1.5 → 0.1.6
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/lib/authoring/backend.d.ts +0 -24
- package/lib/authoring/find.d.ts +3 -0
- package/lib/authoring/find.js +54 -0
- package/lib/authoring/index.d.ts +3 -3
- package/lib/authoring/index.js +4 -3
- package/lib/authoring/propose.d.ts +3 -0
- package/lib/authoring/propose.js +109 -0
- package/lib/authoring/replay.d.ts +10 -0
- package/lib/authoring/replay.js +34 -0
- package/lib/authoring/tools.d.ts +13 -6
- package/lib/authoring/tools.js +108 -293
- package/lib/authoring/util.d.ts +27 -0
- package/lib/authoring/util.js +21 -0
- package/lib/consts.d.ts +6 -1
- package/lib/consts.js +9 -2
- package/lib/mcp/server.d.ts +2 -41
- package/lib/mcp/server.js +6 -34
- package/lib/mcp/start.js +4 -2
- package/lib/mcp/tools/agent/analyze.js +3 -3
- package/lib/mcp/tools/agent/build.d.ts +4 -4
- package/lib/mcp/tools/agent/build.js +13 -15
- package/lib/mcp/tools/agent/capture.d.ts +13 -1
- package/lib/mcp/tools/agent/capture.js +18 -85
- package/lib/mcp/tools/agent/eval.d.ts +5 -2
- package/lib/mcp/tools/agent/eval.js +11 -121
- package/lib/mcp/tools/agent/find.d.ts +10 -3
- package/lib/mcp/tools/agent/find.js +25 -31
- package/lib/mcp/tools/agent/proof.d.ts +13 -3
- package/lib/mcp/tools/agent/proof.js +81 -132
- package/lib/mcp/tools/agent/propose.d.ts +10 -9
- package/lib/mcp/tools/agent/propose.js +87 -64
- package/lib/mcp/tools/agent/replay.d.ts +10 -3
- package/lib/mcp/tools/agent/replay.js +29 -36
- package/lib/mcp/tools/agent/session.d.ts +1 -3
- package/lib/mcp/tools/agent/session.js +5 -5
- package/lib/mcp/tools/agent/shared.d.ts +5 -3
- package/lib/mcp/tools/agent/shared.js +29 -19
- package/lib/old/client.d.ts +35 -0
- package/lib/old/client.js +18 -0
- package/lib/old/login-cdp.d.ts +24 -5
- package/lib/old/login-cdp.js +29 -15
- package/lib/old/sdk-client.d.ts +39 -0
- package/lib/old/sdk-client.js +85 -0
- package/lib/old/to-register.d.ts +69 -0
- package/lib/old/to-register.js +52 -2
- package/lib/old/tools/authenticate.js +3 -7
- package/lib/old/tools/build.d.ts +6 -1
- package/lib/old/tools/build.js +11 -1
- package/lib/old/tools/get-provider-info.d.ts +16 -0
- package/lib/old/tools/get-provider-info.js +73 -0
- package/lib/old/tools/get-version-config.d.ts +17 -0
- package/lib/old/tools/get-version-config.js +47 -0
- package/lib/old/tools/publish.js +189 -2
- package/lib/old/tools/update-provider-metadata.d.ts +16 -0
- package/lib/old/tools/update-provider-metadata.js +61 -0
- package/lib/proof/attestor.d.ts +6 -3
- package/lib/proof/attestor.js +10 -1
- package/lib/proof/debug-response-dump.d.ts +4 -0
- package/lib/proof/debug-response-dump.js +55 -0
- package/lib/proof/patch-domhandler.d.ts +1 -0
- package/lib/proof/patch-domhandler.js +59 -0
- package/lib/proof/zk-files.js +7 -6
- package/lib/provider/dom-xpath.d.ts +17 -0
- package/lib/provider/dom-xpath.js +187 -0
- package/lib/provider/draft.d.ts +30 -2
- package/lib/provider/draft.js +117 -28
- package/lib/provider/index.d.ts +4 -0
- package/lib/provider/index.js +7 -0
- package/lib/provider/injection-doc.js +6 -0
- package/lib/provider/matcher.d.ts +17 -20
- package/lib/provider/matcher.js +394 -280
- package/lib/provider/schema.d.ts +7 -5
- package/lib/tools/registry.d.ts +14 -0
- package/lib/tools/registry.js +32 -0
- package/package.json +9 -1
- package/skill/SKILL.builder.md +42 -23
- package/skill/SKILL.old.md +6 -2
- package/skill/builder-mode.md +1 -1
- package/skill/injection-api.md +117 -0
- package/src/authoring/backend.ts +98 -0
- package/src/authoring/index.ts +20 -0
- package/src/authoring/tools.ts +273 -0
- package/src/authoring/util.ts +42 -0
- package/src/consts.ts +15 -2
- package/src/mcp/server.ts +11 -78
- package/src/mcp/start.ts +4 -1
- package/src/mcp/tools/agent/analyze.ts +3 -3
- package/src/mcp/tools/agent/build.ts +13 -21
- package/src/mcp/tools/agent/capture.ts +31 -115
- package/src/mcp/tools/agent/eval.ts +19 -155
- package/src/mcp/tools/agent/find.ts +33 -36
- package/src/mcp/tools/agent/proof.ts +87 -160
- package/src/mcp/tools/agent/propose.ts +93 -85
- package/src/mcp/tools/agent/replay.ts +32 -42
- package/src/mcp/tools/agent/session.ts +4 -5
- package/src/mcp/tools/agent/shared.ts +152 -0
- package/src/old/client.ts +56 -0
- package/src/old/login-cdp.ts +29 -23
- package/src/old/sdk-client.ts +105 -0
- package/src/old/to-register.ts +98 -6
- package/src/old/tools/authenticate.ts +3 -11
- package/src/old/tools/build.ts +12 -0
- package/src/old/tools/get-provider-info.ts +91 -0
- package/src/old/tools/get-version-config.ts +56 -0
- package/src/old/tools/publish.ts +244 -1
- package/src/old/tools/update-provider-metadata.ts +72 -0
- package/src/proof/attestor.ts +16 -4
- package/src/proof/debug-response-dump.ts +70 -0
- package/src/proof/patch-domhandler.ts +68 -0
- package/src/proof/zk-files.ts +7 -6
- package/src/provider/dom-xpath.ts +225 -0
- package/src/provider/draft.ts +168 -35
- package/src/provider/index.ts +8 -0
- package/src/provider/injection-doc.ts +6 -0
- package/src/provider/matcher.ts +477 -357
- package/src/provider/schema.ts +7 -5
- package/src/tools/registry.ts +92 -0
package/lib/mcp/server.js
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import { toZod } from "./api/to-zod.js";
|
|
2
2
|
import { remapErrorAsResponse } from "./remap_errors.js";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* type-narrowed via a Zod schema derived from that same JSON Schema —
|
|
8
|
-
* so the description sent to the MCP client and the validation applied
|
|
9
|
-
* to inbound args cannot drift.
|
|
10
|
-
*/
|
|
11
|
-
export function defineTool(definition, handler) {
|
|
12
|
-
const schema = toZod(definition.inputSchema);
|
|
13
|
-
return {
|
|
14
|
-
definition,
|
|
15
|
-
handler: async (raw, ctx = NOOP_CONTEXT) => {
|
|
16
|
-
const args = schema.parse(raw);
|
|
17
|
-
return await handler(args, ctx);
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
}
|
|
3
|
+
// The tool primitive lives in `../tools/registry.ts` (SDK-free, so the cloud
|
|
4
|
+
// loop can use it too). Re-exported here so the MCP-side tool files can keep
|
|
5
|
+
// importing `defineTool`/`RegisteredTool` from the server module.
|
|
6
|
+
export { defineTool, } from "../tools/registry.js";
|
|
21
7
|
/**
|
|
22
8
|
* Registers a list of RegisteredTool entries on a high-level McpServer.
|
|
23
9
|
* Converts JSON Schema definitions to Zod and provides consistent
|
|
@@ -28,27 +14,13 @@ export function registerToolList(server, tools) {
|
|
|
28
14
|
server.registerTool(tool.definition.name, {
|
|
29
15
|
description: tool.definition.description,
|
|
30
16
|
inputSchema: toZod(tool.definition.inputSchema),
|
|
31
|
-
}, async (args
|
|
17
|
+
}, async (args) => {
|
|
32
18
|
try {
|
|
33
|
-
const progressToken = extra?._meta?.progressToken;
|
|
34
|
-
let progress = 0;
|
|
35
|
-
const ctx = {
|
|
36
|
-
reportProgress: async (message) => {
|
|
37
|
-
if (progressToken === undefined) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
progress += 1;
|
|
41
|
-
await extra.sendNotification({
|
|
42
|
-
method: 'notifications/progress',
|
|
43
|
-
params: { progressToken, progress, message },
|
|
44
|
-
}).catch(() => { });
|
|
45
|
-
},
|
|
46
|
-
};
|
|
47
19
|
return {
|
|
48
20
|
content: [
|
|
49
21
|
{
|
|
50
22
|
type: 'text',
|
|
51
|
-
text: JSON.stringify(await tool.handler(args
|
|
23
|
+
text: JSON.stringify(await tool.handler(args), null, 2),
|
|
52
24
|
},
|
|
53
25
|
],
|
|
54
26
|
};
|
package/lib/mcp/start.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
3
|
import { ReclaimClient } from '@reclaimprotocol/client';
|
|
4
|
-
import { agentDisabled, apiToken, apiUrl, oldApiUrl, oldLogsUrl, useOldDevtools, } from "../consts.js";
|
|
4
|
+
import { agentDisabled, apiToken, apiUrl, oldApiUrl, oldLogsUrl, sdkApiUrl, useOldDevtools, } from "../consts.js";
|
|
5
5
|
import { ReclaimOldClient } from "../old/client.js";
|
|
6
6
|
import { resolveInitialIdentity } from "../old/identity.js";
|
|
7
7
|
import { OLD_MODE_INSTRUCTIONS } from "../old/instructions.js";
|
|
8
|
+
import { ReclaimSdkClient } from "../old/sdk-client.js";
|
|
8
9
|
import { buildOldBackendTools } from "../old/tools/build.js";
|
|
9
10
|
import { NAME, VERSION } from "../pkg.js";
|
|
10
11
|
import { registerTools } from "./api/tools.js";
|
|
@@ -33,8 +34,9 @@ export async function startMcpServer() {
|
|
|
33
34
|
logsBaseUrl: oldLogsUrl(),
|
|
34
35
|
identity: resolveInitialIdentity(),
|
|
35
36
|
});
|
|
37
|
+
const sdkClient = new ReclaimSdkClient({ baseUrl: sdkApiUrl() });
|
|
36
38
|
registerToolList(server, buildLocalCredentialTools());
|
|
37
|
-
registerToolList(server, buildOldBackendTools(oldClient));
|
|
39
|
+
registerToolList(server, buildOldBackendTools(oldClient, sdkClient));
|
|
38
40
|
if (agentToolsEnabled) {
|
|
39
41
|
// No builder client → no builder publish tool (old mode has its own).
|
|
40
42
|
registerToolList(server, buildAgentTools());
|
|
@@ -13,11 +13,11 @@ export function analyzeTool(captures) {
|
|
|
13
13
|
required: ['captureId', 'requestId'],
|
|
14
14
|
},
|
|
15
15
|
}, async ({ captureId, requestId }) => {
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
16
|
+
const entry = captures.sessions.get(captureId);
|
|
17
|
+
if (!entry) {
|
|
18
18
|
throw new Error(`no capture ${captureId}`);
|
|
19
19
|
}
|
|
20
|
-
const r = session.requests.get(requestId);
|
|
20
|
+
const r = entry.session.requests.get(requestId);
|
|
21
21
|
if (!r) {
|
|
22
22
|
throw new Error(`no request ${requestId} in capture ${captureId}`);
|
|
23
23
|
}
|
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
* `@modelcontextprotocol/sdk` `McpServer`. Folds the agent's tools
|
|
5
5
|
* alongside the auto-generated API tools.
|
|
6
6
|
*
|
|
7
|
-
* Shared state — Chrome attach state
|
|
8
|
-
*
|
|
7
|
+
* Shared state — Chrome attach state + capture store — is built once per
|
|
8
|
+
* server and threaded through every tool factory. Per-capture drafts (and the
|
|
9
|
+
* secrets they hold) live inside each capture entry, so a capture IS the
|
|
10
|
+
* session for the shared authoring tools.
|
|
9
11
|
*/
|
|
10
12
|
import type { ReclaimClient } from '@reclaimprotocol/client';
|
|
11
13
|
import { ConfigStore } from '../../../config/store.ts';
|
|
12
14
|
import { type RegisteredTool } from '../../server.ts';
|
|
13
15
|
import { type AttachState } from './attach.ts';
|
|
14
16
|
import { type CaptureStore } from './capture.ts';
|
|
15
|
-
import { type SecretStore } from './propose.ts';
|
|
16
17
|
export interface AgentState {
|
|
17
18
|
attach: {
|
|
18
19
|
current?: AttachState;
|
|
19
20
|
config: ConfigStore;
|
|
20
21
|
};
|
|
21
22
|
captures: CaptureStore;
|
|
22
|
-
secrets: SecretStore;
|
|
23
23
|
}
|
|
24
24
|
/** Build the flat list of `RegisteredTool` entries, ready for
|
|
25
25
|
* `registerToolList`.
|
|
@@ -4,27 +4,25 @@
|
|
|
4
4
|
* `@modelcontextprotocol/sdk` `McpServer`. Folds the agent's tools
|
|
5
5
|
* alongside the auto-generated API tools.
|
|
6
6
|
*
|
|
7
|
-
* Shared state — Chrome attach state
|
|
8
|
-
*
|
|
7
|
+
* Shared state — Chrome attach state + capture store — is built once per
|
|
8
|
+
* server and threaded through every tool factory. Per-capture drafts (and the
|
|
9
|
+
* secrets they hold) live inside each capture entry, so a capture IS the
|
|
10
|
+
* session for the shared authoring tools.
|
|
9
11
|
*/
|
|
10
12
|
import { ConfigStore } from "../../../config/store.js";
|
|
11
13
|
import { ensureAttestorCrypto } from "../../../proof/proof-core.js";
|
|
12
14
|
import { analyzeTool } from "./analyze.js";
|
|
13
15
|
import { attachTool } from "./attach.js";
|
|
14
16
|
import { captureTools } from "./capture.js";
|
|
15
|
-
import {
|
|
16
|
-
import { findTool } from "./find.js";
|
|
17
|
+
import { listTabsTool } from "./eval.js";
|
|
17
18
|
import { howItWorksTool } from "./how-it-works.js";
|
|
18
|
-
import { proofTool } from "./proof.js";
|
|
19
|
-
import { makeSecretStore, proposeTool, } from "./propose.js";
|
|
20
19
|
import { createVersionFromCaptureTool } from "./publish.js";
|
|
21
|
-
import { replayTool } from "./replay.js";
|
|
22
20
|
import { sessionTools } from "./session.js";
|
|
21
|
+
import { sharedAuthoringTools } from "./shared.js";
|
|
23
22
|
function buildAgentState() {
|
|
24
23
|
return {
|
|
25
24
|
attach: { config: new ConfigStore() },
|
|
26
25
|
captures: { sessions: new Map() },
|
|
27
|
-
secrets: makeSecretStore(),
|
|
28
26
|
};
|
|
29
27
|
}
|
|
30
28
|
/** Build the flat list of `RegisteredTool` entries, ready for
|
|
@@ -40,20 +38,20 @@ export function buildAgentTools(client) {
|
|
|
40
38
|
// client. No `client` ⇒ old-devtools mode (see `mcp/start.ts`).
|
|
41
39
|
tools.push(howItWorksTool(!client));
|
|
42
40
|
tools.push(attachTool(state.attach));
|
|
41
|
+
tools.push(listTabsTool(state.attach));
|
|
43
42
|
for (const t of captureTools(state.attach, state.captures)) {
|
|
44
43
|
tools.push(t);
|
|
45
44
|
}
|
|
46
|
-
|
|
47
|
-
for (const t of browserTools(state.attach)) {
|
|
45
|
+
for (const t of sessionTools(state.attach, state.captures)) {
|
|
48
46
|
tools.push(t);
|
|
49
47
|
}
|
|
50
|
-
|
|
48
|
+
tools.push(analyzeTool(state.captures));
|
|
49
|
+
// navigate / eval_in_page / wait_for_page / get_cookies / list_requests /
|
|
50
|
+
// find_requests_containing / propose_provider / replay_request / run_proof —
|
|
51
|
+
// shared with the cloud loop, bound to each capture's local backend.
|
|
52
|
+
for (const t of sharedAuthoringTools(state.captures)) {
|
|
51
53
|
tools.push(t);
|
|
52
54
|
}
|
|
53
|
-
tools.push(analyzeTool(state.captures));
|
|
54
|
-
tools.push(proposeTool(state.captures, state.secrets));
|
|
55
|
-
tools.push(replayTool(state.secrets));
|
|
56
|
-
tools.push(proofTool(state.secrets));
|
|
57
55
|
if (client) {
|
|
58
56
|
tools.push(createVersionFromCaptureTool(client));
|
|
59
57
|
}
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
+
import { type DraftStore } from '../../../authoring/index.ts';
|
|
1
2
|
import { type CaptureSession } from '../../../cdp/capture.ts';
|
|
2
3
|
import { type RegisteredTool } from '../../server.ts';
|
|
3
4
|
import type { AttachState } from './attach.ts';
|
|
5
|
+
/**
|
|
6
|
+
* One capture == a session (network requests) + the tab CDP client it runs on +
|
|
7
|
+
* a draft store. Bundling the tab client is what lets the shared authoring
|
|
8
|
+
* tools treat a `captureId` as a full session (drive the browser AND read
|
|
9
|
+
* capture AND hold drafts) behind one id.
|
|
10
|
+
*/
|
|
11
|
+
export interface CaptureEntry {
|
|
12
|
+
session: CaptureSession;
|
|
13
|
+
tabClient: Awaited<ReturnType<AttachState['handles']['attachToTab']>>;
|
|
14
|
+
drafts: DraftStore;
|
|
15
|
+
}
|
|
4
16
|
export interface CaptureStore {
|
|
5
|
-
sessions: Map<string,
|
|
17
|
+
sessions: Map<string, CaptureEntry>;
|
|
6
18
|
activeTab?: string;
|
|
7
19
|
}
|
|
8
20
|
export declare function captureTools(attachRef: {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { makeDraftStore } from "../../../authoring/index.js";
|
|
1
2
|
import { startCapture } from "../../../cdp/capture.js";
|
|
2
3
|
import { defineTool } from "../../server.js";
|
|
3
4
|
export function captureTools(attachRef, captures) {
|
|
@@ -9,8 +10,9 @@ export function captureTools(attachRef, captures) {
|
|
|
9
10
|
};
|
|
10
11
|
const startTool = defineTool({
|
|
11
12
|
name: 'start_capture',
|
|
12
|
-
description: 'Begin recording network requests on the specified tab. '
|
|
13
|
-
'Returns a captureId
|
|
13
|
+
description: 'Begin recording network requests on the specified tab. '
|
|
14
|
+
+ 'Returns a captureId — the session handle the authoring tools '
|
|
15
|
+
+ '(navigate, propose_provider, run_proof, …) all take.',
|
|
14
16
|
inputSchema: {
|
|
15
17
|
type: 'object',
|
|
16
18
|
properties: { tabId: { type: 'string' } },
|
|
@@ -20,91 +22,28 @@ export function captureTools(attachRef, captures) {
|
|
|
20
22
|
const attached = requireAttached();
|
|
21
23
|
const tabClient = await attached.handles.attachToTab(tabId);
|
|
22
24
|
const session = await startCapture(tabClient);
|
|
23
|
-
captures.sessions.set(session.captureId,
|
|
25
|
+
captures.sessions.set(session.captureId, {
|
|
26
|
+
session, tabClient, drafts: makeDraftStore(),
|
|
27
|
+
});
|
|
24
28
|
captures.activeTab = tabId;
|
|
25
29
|
return { captureId: session.captureId };
|
|
26
30
|
});
|
|
27
31
|
const stopTool = defineTool({
|
|
28
32
|
name: 'stop_capture',
|
|
29
|
-
description: 'Stop recording on the given capture. '
|
|
30
|
-
'Captured data remains queryable until reset_session.',
|
|
33
|
+
description: 'Stop recording on the given capture. '
|
|
34
|
+
+ 'Captured data remains queryable until reset_session.',
|
|
31
35
|
inputSchema: {
|
|
32
36
|
type: 'object',
|
|
33
37
|
properties: { captureId: { type: 'string' } },
|
|
34
38
|
required: ['captureId'],
|
|
35
39
|
},
|
|
36
40
|
}, async ({ captureId }) => {
|
|
37
|
-
const
|
|
38
|
-
if (!
|
|
41
|
+
const entry = captures.sessions.get(captureId);
|
|
42
|
+
if (!entry) {
|
|
39
43
|
throw new Error(`no capture ${captureId}`);
|
|
40
44
|
}
|
|
41
|
-
await session.stop();
|
|
42
|
-
return { stopped: true, requestCount: session.requests.size };
|
|
43
|
-
});
|
|
44
|
-
const listTool = defineTool({
|
|
45
|
-
name: 'list_requests',
|
|
46
|
-
description: 'List metadata for captured requests. ' +
|
|
47
|
-
'Filter by method, URL glob, status, content-type, ' +
|
|
48
|
-
'or graphqlOp. Returns only metadata, never bodies — ' +
|
|
49
|
-
'use get_request for those.',
|
|
50
|
-
inputSchema: {
|
|
51
|
-
type: 'object',
|
|
52
|
-
properties: {
|
|
53
|
-
captureId: { type: 'string' },
|
|
54
|
-
filter: {
|
|
55
|
-
type: 'object',
|
|
56
|
-
properties: {
|
|
57
|
-
method: { type: 'string' },
|
|
58
|
-
urlGlob: { type: 'string' },
|
|
59
|
-
statusMin: { type: 'number' },
|
|
60
|
-
statusMax: { type: 'number' },
|
|
61
|
-
contentType: { type: 'string' },
|
|
62
|
-
graphqlOp: { type: 'string' },
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
required: ['captureId'],
|
|
67
|
-
},
|
|
68
|
-
}, async ({ captureId, filter }) => {
|
|
69
|
-
const session = captures.sessions.get(captureId);
|
|
70
|
-
if (!session) {
|
|
71
|
-
throw new Error(`no capture ${captureId}`);
|
|
72
|
-
}
|
|
73
|
-
const out = [];
|
|
74
|
-
for (const r of session.requests.values()) {
|
|
75
|
-
if (filter?.method && r.method !== filter.method.toUpperCase()) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
if (filter?.urlGlob && !globMatch(filter.urlGlob, r.url)) {
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (filter?.statusMin !== undefined && r.status < filter.statusMin) {
|
|
82
|
-
continue;
|
|
83
|
-
}
|
|
84
|
-
if (filter?.statusMax !== undefined && r.status > filter.statusMax) {
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (filter?.contentType &&
|
|
88
|
-
!r.contentType.includes(filter.contentType)) {
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (filter?.graphqlOp && r.graphqlOp !== filter.graphqlOp) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const item = {
|
|
95
|
-
requestId: r.requestId,
|
|
96
|
-
method: r.method,
|
|
97
|
-
url: r.url,
|
|
98
|
-
status: r.status,
|
|
99
|
-
contentType: r.contentType,
|
|
100
|
-
size: r.size,
|
|
101
|
-
};
|
|
102
|
-
if (r.graphqlOp !== undefined) {
|
|
103
|
-
item.graphqlOp = r.graphqlOp;
|
|
104
|
-
}
|
|
105
|
-
out.push(item);
|
|
106
|
-
}
|
|
107
|
-
return { requests: out };
|
|
45
|
+
await entry.session.stop();
|
|
46
|
+
return { stopped: true, requestCount: entry.session.requests.size };
|
|
108
47
|
});
|
|
109
48
|
const getTool = defineTool({
|
|
110
49
|
name: 'get_request',
|
|
@@ -118,25 +57,19 @@ export function captureTools(attachRef, captures) {
|
|
|
118
57
|
required: ['captureId', 'requestId'],
|
|
119
58
|
},
|
|
120
59
|
}, async ({ captureId, requestId }) => {
|
|
121
|
-
const
|
|
122
|
-
if (!
|
|
60
|
+
const entry = captures.sessions.get(captureId);
|
|
61
|
+
if (!entry) {
|
|
123
62
|
throw new Error(`no capture ${captureId}`);
|
|
124
63
|
}
|
|
125
|
-
const r = session.requests.get(requestId);
|
|
64
|
+
const r = entry.session.requests.get(requestId);
|
|
126
65
|
if (!r) {
|
|
127
66
|
throw new Error(`no request ${requestId} in capture ${captureId}`);
|
|
128
67
|
}
|
|
129
|
-
const body = session.bodies.get(requestId);
|
|
68
|
+
const body = entry.session.bodies.get(requestId);
|
|
130
69
|
if (!body.available) {
|
|
131
70
|
return { ...r, bodyAvailable: false, reason: body.reason };
|
|
132
71
|
}
|
|
133
72
|
return { ...r, responseBody: body.body, bodyAvailable: true };
|
|
134
73
|
});
|
|
135
|
-
return [startTool, stopTool,
|
|
136
|
-
}
|
|
137
|
-
function globMatch(pattern, str) {
|
|
138
|
-
const re = new RegExp('^' +
|
|
139
|
-
pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') +
|
|
140
|
-
'$');
|
|
141
|
-
return re.test(str);
|
|
74
|
+
return [startTool, stopTool, getTool];
|
|
142
75
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { type RegisteredTool } from '../../server.ts';
|
|
2
2
|
import type { AttachState } from './attach.ts';
|
|
3
|
-
|
|
3
|
+
/** The one browser tool that isn't session-bound: enumerate tabs so the caller
|
|
4
|
+
* can pick one to `start_capture` on. Navigate/eval/wait are the shared
|
|
5
|
+
* authoring tools (keyed by captureId — see `shared.ts`). */
|
|
6
|
+
export declare function listTabsTool(attachRef: {
|
|
4
7
|
current?: AttachState;
|
|
5
|
-
}): RegisteredTool
|
|
8
|
+
}): RegisteredTool;
|
|
@@ -1,127 +1,17 @@
|
|
|
1
1
|
import { listTabs } from "../../../cdp/tabs.js";
|
|
2
2
|
import { defineTool } from "../../server.js";
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/** The one browser tool that isn't session-bound: enumerate tabs so the caller
|
|
4
|
+
* can pick one to `start_capture` on. Navigate/eval/wait are the shared
|
|
5
|
+
* authoring tools (keyed by captureId — see `shared.ts`). */
|
|
6
|
+
export function listTabsTool(attachRef) {
|
|
7
|
+
return defineTool({
|
|
8
|
+
name: 'list_tabs',
|
|
9
|
+
description: 'List open Chrome tabs.',
|
|
10
|
+
inputSchema: { type: 'object', properties: {} },
|
|
11
|
+
}, async () => {
|
|
5
12
|
if (!attachRef.current) {
|
|
6
13
|
throw new Error('Not attached. Call attach_chrome first.');
|
|
7
14
|
}
|
|
8
|
-
return attachRef.current;
|
|
9
|
-
};
|
|
10
|
-
return [
|
|
11
|
-
defineTool({
|
|
12
|
-
name: 'list_tabs',
|
|
13
|
-
description: 'List open Chrome tabs.',
|
|
14
|
-
inputSchema: { type: 'object', properties: {} },
|
|
15
|
-
}, async () => {
|
|
16
|
-
const attached = requireAttached();
|
|
17
|
-
return { tabs: await listTabs(attached.port) };
|
|
18
|
-
}),
|
|
19
|
-
defineTool({
|
|
20
|
-
name: 'eval_in_page',
|
|
21
|
-
description: 'Run a JS expression in a tab via Runtime.evaluate ' +
|
|
22
|
-
'and return the result. ' +
|
|
23
|
-
'Useful for reading visible values from the DOM.',
|
|
24
|
-
inputSchema: {
|
|
25
|
-
type: 'object',
|
|
26
|
-
properties: {
|
|
27
|
-
tabId: { type: 'string' },
|
|
28
|
-
expression: { type: 'string' },
|
|
29
|
-
},
|
|
30
|
-
required: ['tabId', 'expression'],
|
|
31
|
-
},
|
|
32
|
-
}, async ({ tabId, expression }) => {
|
|
33
|
-
const attached = requireAttached();
|
|
34
|
-
const { Runtime } = await attached.handles.attachToTab(tabId);
|
|
35
|
-
const result = await Runtime.evaluate({
|
|
36
|
-
expression,
|
|
37
|
-
returnByValue: true,
|
|
38
|
-
});
|
|
39
|
-
if (result.exceptionDetails) {
|
|
40
|
-
return { error: result.exceptionDetails.text };
|
|
41
|
-
}
|
|
42
|
-
return { value: result.result.value };
|
|
43
|
-
}),
|
|
44
|
-
defineTool({
|
|
45
|
-
name: 'navigate',
|
|
46
|
-
description: 'Navigate a tab to a URL via Page.navigate, ' +
|
|
47
|
-
'waiting for the load event. ' +
|
|
48
|
-
'Use this to drive the browser yourself ' +
|
|
49
|
-
'instead of asking the developer to navigate. ' +
|
|
50
|
-
'Returns the final URL after navigation ' +
|
|
51
|
-
'(which may differ from the requested URL due to redirects).',
|
|
52
|
-
inputSchema: {
|
|
53
|
-
type: 'object',
|
|
54
|
-
properties: {
|
|
55
|
-
tabId: { type: 'string' },
|
|
56
|
-
url: { type: 'string' },
|
|
57
|
-
timeoutMs: { type: 'number' },
|
|
58
|
-
},
|
|
59
|
-
required: ['tabId', 'url'],
|
|
60
|
-
},
|
|
61
|
-
}, async ({ tabId, url, timeoutMs }) => {
|
|
62
|
-
const attached = requireAttached();
|
|
63
|
-
const { Page, Runtime } = await attached.handles.attachToTab(tabId);
|
|
64
|
-
await Page.enable();
|
|
65
|
-
const loadPromise = new Promise((resolve) => {
|
|
66
|
-
Page.loadEventFired(() => resolve());
|
|
67
|
-
});
|
|
68
|
-
const nav = await Page.navigate({ url });
|
|
69
|
-
if (nav.errorText) {
|
|
70
|
-
return { ok: false, error: nav.errorText };
|
|
71
|
-
}
|
|
72
|
-
const t = timeoutMs ?? 30_000;
|
|
73
|
-
const raced = await Promise.race([
|
|
74
|
-
loadPromise.then(() => 'loaded'),
|
|
75
|
-
new Promise((resolve) => setTimeout(() => resolve('timeout'), t)),
|
|
76
|
-
]);
|
|
77
|
-
const final = await Runtime.evaluate({
|
|
78
|
-
expression: 'location.href',
|
|
79
|
-
returnByValue: true,
|
|
80
|
-
});
|
|
81
|
-
return {
|
|
82
|
-
ok: true,
|
|
83
|
-
loadStatus: raced,
|
|
84
|
-
finalUrl: final.result.value,
|
|
85
|
-
};
|
|
86
|
-
}),
|
|
87
|
-
defineTool({
|
|
88
|
-
name: 'wait_for_page',
|
|
89
|
-
description: 'Poll a JS expression in a tab until it ' +
|
|
90
|
-
'returns truthy (or timeout). ' +
|
|
91
|
-
'Use this for steps that require the ' +
|
|
92
|
-
'developer to do something in the browser — ' +
|
|
93
|
-
'most commonly waiting for them to finish logging in. ' +
|
|
94
|
-
'Pass an expression that ' +
|
|
95
|
-
'evaluates true once the desired state is reached, e.g. ' +
|
|
96
|
-
'`!!document.querySelector' +
|
|
97
|
-
'(\'summary[aria-label*="View profile"]\')`' +
|
|
98
|
-
' for GitHub post-login.',
|
|
99
|
-
inputSchema: {
|
|
100
|
-
type: 'object',
|
|
101
|
-
properties: {
|
|
102
|
-
tabId: { type: 'string' },
|
|
103
|
-
expression: { type: 'string' },
|
|
104
|
-
timeoutMs: { type: 'number' },
|
|
105
|
-
pollMs: { type: 'number' },
|
|
106
|
-
},
|
|
107
|
-
required: ['tabId', 'expression'],
|
|
108
|
-
},
|
|
109
|
-
}, async ({ tabId, expression, timeoutMs, pollMs }) => {
|
|
110
|
-
const attached = requireAttached();
|
|
111
|
-
const { Runtime } = await attached.handles.attachToTab(tabId);
|
|
112
|
-
const deadline = Date.now() + (timeoutMs ?? 120_000);
|
|
113
|
-
const interval = pollMs ?? 1000;
|
|
114
|
-
while (Date.now() < deadline) {
|
|
115
|
-
const r = await Runtime.evaluate({ expression, returnByValue: true });
|
|
116
|
-
if (r.exceptionDetails) {
|
|
117
|
-
return { matched: false, error: r.exceptionDetails.text };
|
|
118
|
-
}
|
|
119
|
-
if (r.result.value) {
|
|
120
|
-
return { matched: true, value: r.result.value };
|
|
121
|
-
}
|
|
122
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
123
|
-
}
|
|
124
|
-
return { matched: false, reason: 'timeout' };
|
|
125
|
-
}),
|
|
126
|
-
];
|
|
15
|
+
return { tabs: await listTabs(attachRef.current.port) };
|
|
16
|
+
});
|
|
127
17
|
}
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* `find_requests_containing` — search captured responses for any encoding of a
|
|
3
|
+
* value and rank the requests that return it. Authored once as a plain
|
|
4
|
+
* {@link RegisteredTool}; the MCP and cloud loop each pass their own backend
|
|
5
|
+
* resolver (see authoring/tools.ts / shared.ts). Server-side: response bodies
|
|
6
|
+
* stay here, only metadata + a short snippet leave.
|
|
7
|
+
*/
|
|
8
|
+
import { type ResolveBackend } from '../../../authoring/util.ts';
|
|
9
|
+
import { type RegisteredTool } from '../../../tools/registry.ts';
|
|
10
|
+
export declare function findTool(resolveBackend: ResolveBackend): RegisteredTool;
|
|
@@ -1,29 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `find_requests_containing` — search captured responses for any encoding of a
|
|
3
|
+
* value and rank the requests that return it. Authored once as a plain
|
|
4
|
+
* {@link RegisteredTool}; the MCP and cloud loop each pass their own backend
|
|
5
|
+
* resolver (see authoring/tools.ts / shared.ts). Server-side: response bodies
|
|
6
|
+
* stay here, only metadata + a short snippet leave.
|
|
7
|
+
*/
|
|
8
|
+
import { objectSchema, publicRequest, } from "../../../authoring/util.js";
|
|
1
9
|
import { normalizeCandidates } from "../../../provider/normalize.js";
|
|
2
|
-
import { defineTool } from "
|
|
3
|
-
export function findTool(
|
|
10
|
+
import { defineTool } from "../../../tools/registry.js";
|
|
11
|
+
export function findTool(resolveBackend) {
|
|
4
12
|
return defineTool({
|
|
5
13
|
name: 'find_requests_containing',
|
|
6
|
-
description: 'Search captured responses for any encoding of `value
|
|
7
|
-
'Returns a ranked candidate list '
|
|
8
|
-
'
|
|
9
|
-
'Server-side; bodies stay here.',
|
|
10
|
-
inputSchema: {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
value: { type: 'string' },
|
|
15
|
-
limit: { type: 'number' },
|
|
16
|
-
},
|
|
17
|
-
required: ['captureId', 'value'],
|
|
18
|
-
},
|
|
19
|
-
}, async ({ captureId, value, limit }) => {
|
|
20
|
-
const session = captures.sessions.get(captureId);
|
|
21
|
-
if (!session) {
|
|
22
|
-
throw new Error(`no capture ${captureId}`);
|
|
23
|
-
}
|
|
24
|
-
const variants = normalizeCandidates(value);
|
|
14
|
+
description: 'Search captured responses for any encoding of `value` (raw, '
|
|
15
|
+
+ 'URL/JSON/HTML-escaped, …). Returns a ranked candidate list with '
|
|
16
|
+
+ 'the matched variant, score, and a short snippet — used to locate '
|
|
17
|
+
+ 'the request that returns a target. Server-side; bodies stay here.',
|
|
18
|
+
inputSchema: objectSchema({ value: { type: 'string' }, limit: { type: 'number' } }, ['value']),
|
|
19
|
+
}, async (args) => {
|
|
20
|
+
const backend = resolveBackend(args);
|
|
21
|
+
const variants = normalizeCandidates(String(args.value));
|
|
25
22
|
const ranked = [];
|
|
26
|
-
for (const r of
|
|
23
|
+
for (const r of backend.capture.requests.values()) {
|
|
27
24
|
if (!r.responseBody) {
|
|
28
25
|
continue;
|
|
29
26
|
}
|
|
@@ -40,21 +37,18 @@ export function findTool(captures) {
|
|
|
40
37
|
if (!hit) {
|
|
41
38
|
continue;
|
|
42
39
|
}
|
|
43
|
-
//
|
|
44
|
-
const score = (r.contentType.includes('json') ? 10 : 0)
|
|
45
|
-
Math.max(0, 5 - Math.log10(Math.max(r.size, 1)));
|
|
40
|
+
// Prefer JSON responses + smaller bodies (likely API endpoints).
|
|
41
|
+
const score = (r.contentType.includes('json') ? 10 : 0)
|
|
42
|
+
+ Math.max(0, 5 - Math.log10(Math.max(r.size, 1)));
|
|
46
43
|
ranked.push({
|
|
47
|
-
|
|
48
|
-
url: r.url,
|
|
49
|
-
method: r.method,
|
|
50
|
-
status: r.status,
|
|
51
|
-
contentType: r.contentType,
|
|
44
|
+
...publicRequest(r),
|
|
52
45
|
matchedVariant: hit.variant,
|
|
53
46
|
snippet: hit.snippet,
|
|
54
47
|
score,
|
|
55
48
|
});
|
|
56
49
|
}
|
|
57
50
|
ranked.sort((a, b) => b.score - a.score);
|
|
58
|
-
|
|
51
|
+
const limit = Number(args.limit) || 10;
|
|
52
|
+
return { candidates: ranked.slice(0, limit) };
|
|
59
53
|
});
|
|
60
54
|
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { type ProofResult } from '../../../proof/attestor.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The MCP's rich `run_proof` view: persist the full claim to disk, return
|
|
4
|
+
* identifier + extractedParameters + a prettified claim (request dropped, byte
|
|
5
|
+
* arrays as base64) + trimmed logs, and — under `RECLAIM_AGENT_TRACE=1` — a
|
|
6
|
+
* forensic run dump. This is the developer-facing shape; the cloud backend
|
|
7
|
+
* projects the same {@link ProofResult} to a lean summary instead.
|
|
8
|
+
*/
|
|
9
|
+
export declare function shapeRichProofResult(result: ProofResult, opts: {
|
|
10
|
+
provider: unknown;
|
|
11
|
+
hasSecrets: boolean;
|
|
12
|
+
attestorUrl?: string;
|
|
13
|
+
}): Record<string, unknown>;
|