@reclaimprotocol/agent 0.1.18 → 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/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 +22 -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/old/login-cdp.js +11 -3
- package/lib/old/tools/publish.js +23 -1
- 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/secrets.d.ts +37 -0
- package/lib/provider/secrets.js +147 -6
- package/lib/provider/to-version.d.ts +7 -0
- package/lib/provider/to-version.js +11 -1
- 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 +2 -2
- package/skill/injection-api.md +95 -10
- package/skill/old-mode.md +2 -2
- 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/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 +22 -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/old/login-cdp.ts +13 -3
- package/src/old/tools/publish.ts +25 -3
- package/src/provider/draft.ts +98 -56
- package/src/provider/index.ts +9 -1
- package/src/provider/secrets.ts +171 -6
- package/src/provider/to-version.ts +12 -1
- 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
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { deriveFromPrivateKey, } from '@reclaimprotocol/client';
|
|
2
|
-
import { findProjectRoot } from "../../paths.js";
|
|
3
|
-
import { defineTool } from "../server.js";
|
|
4
|
-
import { findCached, suggestCredentialForDir } from "./cache.js";
|
|
5
|
-
export function statusTool(client) {
|
|
6
|
-
return defineTool({
|
|
7
|
-
name: 'reclaim_status',
|
|
8
|
-
description: 'Report the live mode (SANDBOX | PRODUCTION | REVOKED) ' +
|
|
9
|
-
'of a Reclaim app credential. Pass `address` — or omit it ' +
|
|
10
|
-
'to derive the address from RECLAIM_PRIVATE_KEY, falling ' +
|
|
11
|
-
'back to the best cached credential for this directory. ' +
|
|
12
|
-
'Useful to confirm a credential is linked to an org ' +
|
|
13
|
-
'with billing before running a production flow.',
|
|
14
|
-
inputSchema: {
|
|
15
|
-
type: 'object',
|
|
16
|
-
properties: {
|
|
17
|
-
address: {
|
|
18
|
-
type: 'string',
|
|
19
|
-
pattern: '^0x[0-9a-fA-F]{40}$',
|
|
20
|
-
description: 'Lowercase ETH address. Defaults to the address ' +
|
|
21
|
-
'derived from RECLAIM_PRIVATE_KEY, then the cached ' +
|
|
22
|
-
'credential suggested for the current directory.',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
},
|
|
26
|
-
}, async (args) => {
|
|
27
|
-
let address = args.address?.toLowerCase();
|
|
28
|
-
if (!address && process.env.RECLAIM_PRIVATE_KEY) {
|
|
29
|
-
address = deriveFromPrivateKey(process.env.RECLAIM_PRIVATE_KEY).address;
|
|
30
|
-
}
|
|
31
|
-
if (!address) {
|
|
32
|
-
address = suggestCredentialForDir(findProjectRoot(process.cwd()))
|
|
33
|
-
?.credential.address;
|
|
34
|
-
}
|
|
35
|
-
if (!address) {
|
|
36
|
-
throw new Error('No address given, RECLAIM_PRIVATE_KEY is not set, and the ' +
|
|
37
|
-
'cache is empty. Run issue_credentials or import_credentials ' +
|
|
38
|
-
'first.');
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const credResult = (await client.call('GetCredentialByAddress', {
|
|
42
|
-
params: { address },
|
|
43
|
-
}));
|
|
44
|
-
const cred = credResult.data;
|
|
45
|
-
return {
|
|
46
|
-
address: cred.address,
|
|
47
|
-
mode: cred.mode,
|
|
48
|
-
orgId: cred.orgId,
|
|
49
|
-
label: cred.label,
|
|
50
|
-
revokedAt: cred.revokedAt,
|
|
51
|
-
lastUsedAt: cred.lastUsedAt,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
catch (err) {
|
|
55
|
-
// Anonymous credentials are not registered server-side, so a
|
|
56
|
-
// 404 just means "local-only" — which is SANDBOX. Report what
|
|
57
|
-
// the local cache knows. Other errors propagate. The client
|
|
58
|
-
// throws a Boom-style ProblemError, so the status lives on
|
|
59
|
-
// `output.statusCode` / `problem.status`, not `status`.
|
|
60
|
-
const e = err;
|
|
61
|
-
const code = e.status ?? e.output?.statusCode ?? e.problem?.status;
|
|
62
|
-
if (code !== 404) {
|
|
63
|
-
throw err;
|
|
64
|
-
}
|
|
65
|
-
const cached = findCached(address);
|
|
66
|
-
return {
|
|
67
|
-
address,
|
|
68
|
-
mode: 'SANDBOX',
|
|
69
|
-
...(cached?.label ? { label: cached.label } : {}),
|
|
70
|
-
note: 'Local-only credential — not registered with the server. ' +
|
|
71
|
-
'It operates in SANDBOX. Link it to an org with billing ' +
|
|
72
|
-
'(via link_credential) to reach PRODUCTION.',
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
}
|
package/lib/mcp/register.d.ts
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Factory entry-point for embedding the reclaim-agent's MCP tools into
|
|
3
|
-
* a host MCP server. Two consumers:
|
|
4
|
-
*
|
|
5
|
-
* - The agent's own standalone `src/index.ts` (uses `ReclaimMcpServer`,
|
|
6
|
-
* the low-level wrapper around `@modelcontextprotocol/sdk`'s Server).
|
|
7
|
-
* - `@reclaimprotocol/client`'s MCP server (uses `McpServer`, the
|
|
8
|
-
* high-level SDK wrapper). Calls `registerAgentTools` to fold the
|
|
9
|
-
* agent's 16 tools alongside its auto-generated API tools.
|
|
10
|
-
*
|
|
11
|
-
* Shared state — Chrome attach state, capture store, secret store — is
|
|
12
|
-
* built once per server and threaded through every tool factory.
|
|
13
|
-
*/
|
|
14
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
15
|
-
import type { ReclaimClient } from '@reclaimprotocol/client';
|
|
16
|
-
import { ConfigStore } from '../config/store.ts';
|
|
17
|
-
import { type AttachState } from './tools/attach.ts';
|
|
18
|
-
import { type CaptureStore } from './tools/capture.ts';
|
|
19
|
-
import { type SecretStore } from './tools/propose.ts';
|
|
20
|
-
import { type RegisteredTool } from './server.ts';
|
|
21
|
-
export interface AgentState {
|
|
22
|
-
attach: {
|
|
23
|
-
current?: AttachState;
|
|
24
|
-
config: ConfigStore;
|
|
25
|
-
};
|
|
26
|
-
captures: CaptureStore;
|
|
27
|
-
secrets: SecretStore;
|
|
28
|
-
}
|
|
29
|
-
export declare function buildAgentState(): AgentState;
|
|
30
|
-
/** Build the flat list of `RegisteredTool` entries. Useful when the host
|
|
31
|
-
* uses our internal `ReclaimMcpServer` interface (`.register(tool)`).
|
|
32
|
-
* Pass `client` to include `create_provider_version_from_capture`. */
|
|
33
|
-
export declare function buildAgentTools(state: AgentState, client?: ReclaimClient): RegisteredTool[];
|
|
34
|
-
/** Register agent tools on `@reclaimprotocol/client`'s McpServer. Mirrors
|
|
35
|
-
* the same shape as builder's auto-generated `registerTools(server, client)`
|
|
36
|
-
* in `mcp/api/tools.ts`.
|
|
37
|
-
* Pass `client` to include `create_provider_version_from_capture`. */
|
|
38
|
-
export declare function registerAgentTools(server: McpServer, state?: AgentState, client?: ReclaimClient): void;
|
package/lib/mcp/register.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Factory entry-point for embedding the reclaim-agent's MCP tools into
|
|
3
|
-
* a host MCP server. Two consumers:
|
|
4
|
-
*
|
|
5
|
-
* - The agent's own standalone `src/index.ts` (uses `ReclaimMcpServer`,
|
|
6
|
-
* the low-level wrapper around `@modelcontextprotocol/sdk`'s Server).
|
|
7
|
-
* - `@reclaimprotocol/client`'s MCP server (uses `McpServer`, the
|
|
8
|
-
* high-level SDK wrapper). Calls `registerAgentTools` to fold the
|
|
9
|
-
* agent's 16 tools alongside its auto-generated API tools.
|
|
10
|
-
*
|
|
11
|
-
* Shared state — Chrome attach state, capture store, secret store — is
|
|
12
|
-
* built once per server and threaded through every tool factory.
|
|
13
|
-
*/
|
|
14
|
-
import { setCryptoImplementation } from '@reclaimprotocol/tls';
|
|
15
|
-
import { webcryptoCrypto } from '@reclaimprotocol/tls/webcrypto';
|
|
16
|
-
import { ConfigStore } from "../config/store.js";
|
|
17
|
-
import { analyzeTool } from "./tools/analyze.js";
|
|
18
|
-
import { attachTool } from "./tools/attach.js";
|
|
19
|
-
import { captureTools } from "./tools/capture.js";
|
|
20
|
-
import { browserTools } from "./tools/eval.js";
|
|
21
|
-
import { findTool } from "./tools/find.js";
|
|
22
|
-
import { proofTool } from "./tools/proof.js";
|
|
23
|
-
import { makeSecretStore, proposeTool, } from "./tools/propose.js";
|
|
24
|
-
import { createVersionFromCaptureTool } from "./tools/publish.js";
|
|
25
|
-
import { replayTool } from "./tools/replay.js";
|
|
26
|
-
import { sessionTools } from "./tools/session.js";
|
|
27
|
-
import { registerToolList } from "./server.js";
|
|
28
|
-
let cryptoInitialized = false;
|
|
29
|
-
/** One-time crypto backend init for @reclaimprotocol/tls. The package
|
|
30
|
-
* ships with an empty `crypto = {}` and defers backend choice to the
|
|
31
|
-
* caller; without this, the first proof call dies inside the SDK with
|
|
32
|
-
* `crypto.randomBytes is not a function`. */
|
|
33
|
-
function ensureCryptoInit() {
|
|
34
|
-
if (cryptoInitialized) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
setCryptoImplementation(webcryptoCrypto);
|
|
38
|
-
cryptoInitialized = true;
|
|
39
|
-
}
|
|
40
|
-
export function buildAgentState() {
|
|
41
|
-
return {
|
|
42
|
-
attach: { config: new ConfigStore() },
|
|
43
|
-
captures: { sessions: new Map() },
|
|
44
|
-
secrets: makeSecretStore(),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
/** Build the flat list of `RegisteredTool` entries. Useful when the host
|
|
48
|
-
* uses our internal `ReclaimMcpServer` interface (`.register(tool)`).
|
|
49
|
-
* Pass `client` to include `create_provider_version_from_capture`. */
|
|
50
|
-
export function buildAgentTools(state, client) {
|
|
51
|
-
ensureCryptoInit();
|
|
52
|
-
const tools = [];
|
|
53
|
-
tools.push(attachTool(state.attach));
|
|
54
|
-
for (const t of captureTools(state.attach, state.captures)) {
|
|
55
|
-
tools.push(t);
|
|
56
|
-
}
|
|
57
|
-
tools.push(findTool(state.captures));
|
|
58
|
-
for (const t of browserTools(state.attach)) {
|
|
59
|
-
tools.push(t);
|
|
60
|
-
}
|
|
61
|
-
for (const t of sessionTools(state.attach, state.captures, state.secrets)) {
|
|
62
|
-
tools.push(t);
|
|
63
|
-
}
|
|
64
|
-
tools.push(analyzeTool(state.captures));
|
|
65
|
-
tools.push(proposeTool(state.captures, state.secrets));
|
|
66
|
-
tools.push(replayTool(state.secrets));
|
|
67
|
-
tools.push(proofTool(state.secrets));
|
|
68
|
-
if (client) {
|
|
69
|
-
tools.push(createVersionFromCaptureTool(client));
|
|
70
|
-
}
|
|
71
|
-
return tools;
|
|
72
|
-
}
|
|
73
|
-
/** Register agent tools on `@reclaimprotocol/client`'s McpServer. Mirrors
|
|
74
|
-
* the same shape as builder's auto-generated `registerTools(server, client)`
|
|
75
|
-
* in `mcp/api/tools.ts`.
|
|
76
|
-
* Pass `client` to include `create_provider_version_from_capture`. */
|
|
77
|
-
export function registerAgentTools(server, state, client) {
|
|
78
|
-
const s = state ?? buildAgentState();
|
|
79
|
-
registerToolList(server, buildAgentTools(s, client));
|
|
80
|
-
}
|
package/lib/mcp/tools/analyze.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { detectConcerns } from "../../provider/constraints.js";
|
|
2
|
-
import { defineTool } from "../server.js";
|
|
3
|
-
export function analyzeTool(captures) {
|
|
4
|
-
return defineTool({
|
|
5
|
-
name: 'analyze_request_constraints',
|
|
6
|
-
description: 'Flag potential provider-authoring concerns for a captured request.',
|
|
7
|
-
inputSchema: {
|
|
8
|
-
type: 'object',
|
|
9
|
-
properties: {
|
|
10
|
-
captureId: { type: 'string' },
|
|
11
|
-
requestId: { type: 'string' },
|
|
12
|
-
},
|
|
13
|
-
required: ['captureId', 'requestId'],
|
|
14
|
-
},
|
|
15
|
-
}, async ({ captureId, requestId }) => {
|
|
16
|
-
const session = captures.sessions.get(captureId);
|
|
17
|
-
if (!session) {
|
|
18
|
-
throw new Error(`no capture ${captureId}`);
|
|
19
|
-
}
|
|
20
|
-
const r = session.requests.get(requestId);
|
|
21
|
-
if (!r) {
|
|
22
|
-
throw new Error(`no request ${requestId} in capture ${captureId}`);
|
|
23
|
-
}
|
|
24
|
-
return { concerns: detectConcerns(r) };
|
|
25
|
-
});
|
|
26
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type CdpHandles } from '../../cdp/client.ts';
|
|
2
|
-
import type { ConfigStore } from '../../config/store.ts';
|
|
3
|
-
import { type RegisteredTool } from '../server.ts';
|
|
4
|
-
export interface AttachState {
|
|
5
|
-
handles: CdpHandles;
|
|
6
|
-
port: number;
|
|
7
|
-
}
|
|
8
|
-
export declare function attachTool(state: {
|
|
9
|
-
current?: AttachState;
|
|
10
|
-
config: ConfigStore;
|
|
11
|
-
}): RegisteredTool;
|
package/lib/mcp/tools/attach.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { connectCdp } from "../../cdp/client.js";
|
|
2
|
-
import { defaultProfileDir, launchChrome, waitForCdpReady, } from "../../cdp/launcher.js";
|
|
3
|
-
import { defaultConfigPath } from "../../config/store.js";
|
|
4
|
-
import { defineTool } from "../server.js";
|
|
5
|
-
export function attachTool(state) {
|
|
6
|
-
return defineTool({
|
|
7
|
-
name: 'attach_chrome',
|
|
8
|
-
description: 'Attach to a Chrome instance via CDP. ' +
|
|
9
|
-
'Auto-launches a dedicated-profile Chrome ' +
|
|
10
|
-
'on first call if no attachMode is persisted. ' +
|
|
11
|
-
'Persists the chosen mode in ~/.reclaim/config.json.',
|
|
12
|
-
inputSchema: {
|
|
13
|
-
type: 'object',
|
|
14
|
-
properties: {
|
|
15
|
-
attachMode: { type: 'string', enum: ['dedicated', 'attach'] },
|
|
16
|
-
port: { type: 'number' },
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
}, async (parsed) => {
|
|
20
|
-
let cfg = state.config.read();
|
|
21
|
-
if (parsed.attachMode || parsed.port) {
|
|
22
|
-
cfg = {
|
|
23
|
-
attachMode: parsed.attachMode ?? cfg?.attachMode ?? 'dedicated',
|
|
24
|
-
port: parsed.port ?? cfg?.port ?? 9222,
|
|
25
|
-
};
|
|
26
|
-
state.config.write(cfg);
|
|
27
|
-
}
|
|
28
|
-
if (!cfg) {
|
|
29
|
-
return {
|
|
30
|
-
needsChoice: true,
|
|
31
|
-
message: 'First-run: choose attach mode. ' +
|
|
32
|
-
'Call attach_chrome again with attachMode="dedicated" ' +
|
|
33
|
-
'(auto-launch a Chrome profile under ~/.reclaim/chrome-profile) ' +
|
|
34
|
-
'or attachMode="attach" ' +
|
|
35
|
-
'(connect to a Chrome you launched yourself with ' +
|
|
36
|
-
'BOTH --remote-debugging-port=9222 AND ' +
|
|
37
|
-
'--user-data-dir=<some-non-default-path>; ' +
|
|
38
|
-
'since Chrome 136 the debug-port flag is silently ' +
|
|
39
|
-
'ignored if the default profile is in use).',
|
|
40
|
-
configPath: defaultConfigPath(),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
if (cfg.attachMode === 'dedicated') {
|
|
44
|
-
try {
|
|
45
|
-
await waitForCdpReady(cfg.port, 500);
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
launchChrome({ port: cfg.port, profileDir: defaultProfileDir() });
|
|
49
|
-
await waitForCdpReady(cfg.port, 10_000);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
await waitForCdpReady(cfg.port, 2_000).catch(() => {
|
|
54
|
-
throw new Error(`attach mode: nothing answering CDP on port ${cfg.port}. ` +
|
|
55
|
-
'Chrome must be launched with BOTH ' +
|
|
56
|
-
`--remote-debugging-port=${cfg.port} AND ` +
|
|
57
|
-
'--user-data-dir=<some-non-default-path>. ' +
|
|
58
|
-
'Since Chrome 136 (May 2025), the debug-port flag is ' +
|
|
59
|
-
'silently ignored when the default ' +
|
|
60
|
-
"user profile is in use — that's why nothing opens the port. " +
|
|
61
|
-
'Either re-launch Chrome with ' +
|
|
62
|
-
'a custom --user-data-dir, or switch to attachMode="dedicated".');
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
if (state.current) {
|
|
66
|
-
await state.current.handles.close().catch(() => { });
|
|
67
|
-
}
|
|
68
|
-
const handles = await connectCdp(cfg.port);
|
|
69
|
-
state.current = { handles, port: cfg.port };
|
|
70
|
-
return { attached: true, port: cfg.port, mode: cfg.attachMode };
|
|
71
|
-
});
|
|
72
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type CaptureSession } from '../../cdp/capture.ts';
|
|
2
|
-
import { type RegisteredTool } from '../server.ts';
|
|
3
|
-
import type { AttachState } from './attach.ts';
|
|
4
|
-
export interface CaptureStore {
|
|
5
|
-
sessions: Map<string, CaptureSession>;
|
|
6
|
-
activeTab?: string;
|
|
7
|
-
}
|
|
8
|
-
export declare function captureTools(attachRef: {
|
|
9
|
-
current?: AttachState;
|
|
10
|
-
}, captures: CaptureStore): RegisteredTool[];
|
package/lib/mcp/tools/capture.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { startCapture } from "../../cdp/capture.js";
|
|
2
|
-
import { defineTool } from "../server.js";
|
|
3
|
-
export function captureTools(attachRef, captures) {
|
|
4
|
-
const requireAttached = () => {
|
|
5
|
-
if (!attachRef.current) {
|
|
6
|
-
throw new Error('Not attached. Call attach_chrome first.');
|
|
7
|
-
}
|
|
8
|
-
return attachRef.current;
|
|
9
|
-
};
|
|
10
|
-
const startTool = defineTool({
|
|
11
|
-
name: 'start_capture',
|
|
12
|
-
description: 'Begin recording network requests on the specified tab. ' +
|
|
13
|
-
'Returns a captureId.',
|
|
14
|
-
inputSchema: {
|
|
15
|
-
type: 'object',
|
|
16
|
-
properties: { tabId: { type: 'string' } },
|
|
17
|
-
required: ['tabId'],
|
|
18
|
-
},
|
|
19
|
-
}, async ({ tabId }) => {
|
|
20
|
-
const attached = requireAttached();
|
|
21
|
-
const tabClient = await attached.handles.attachToTab(tabId);
|
|
22
|
-
const session = await startCapture(tabClient);
|
|
23
|
-
captures.sessions.set(session.captureId, session);
|
|
24
|
-
captures.activeTab = tabId;
|
|
25
|
-
return { captureId: session.captureId };
|
|
26
|
-
});
|
|
27
|
-
const stopTool = defineTool({
|
|
28
|
-
name: 'stop_capture',
|
|
29
|
-
description: 'Stop recording on the given capture. ' +
|
|
30
|
-
'Captured data remains queryable until reset_session.',
|
|
31
|
-
inputSchema: {
|
|
32
|
-
type: 'object',
|
|
33
|
-
properties: { captureId: { type: 'string' } },
|
|
34
|
-
required: ['captureId'],
|
|
35
|
-
},
|
|
36
|
-
}, async ({ captureId }) => {
|
|
37
|
-
const session = captures.sessions.get(captureId);
|
|
38
|
-
if (!session) {
|
|
39
|
-
throw new Error(`no capture ${captureId}`);
|
|
40
|
-
}
|
|
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 };
|
|
108
|
-
});
|
|
109
|
-
const getTool = defineTool({
|
|
110
|
-
name: 'get_request',
|
|
111
|
-
description: 'Return full headers + bodies for a specific captured request.',
|
|
112
|
-
inputSchema: {
|
|
113
|
-
type: 'object',
|
|
114
|
-
properties: {
|
|
115
|
-
captureId: { type: 'string' },
|
|
116
|
-
requestId: { type: 'string' },
|
|
117
|
-
},
|
|
118
|
-
required: ['captureId', 'requestId'],
|
|
119
|
-
},
|
|
120
|
-
}, async ({ captureId, requestId }) => {
|
|
121
|
-
const session = captures.sessions.get(captureId);
|
|
122
|
-
if (!session) {
|
|
123
|
-
throw new Error(`no capture ${captureId}`);
|
|
124
|
-
}
|
|
125
|
-
const r = session.requests.get(requestId);
|
|
126
|
-
if (!r) {
|
|
127
|
-
throw new Error(`no request ${requestId} in capture ${captureId}`);
|
|
128
|
-
}
|
|
129
|
-
const body = session.bodies.get(requestId);
|
|
130
|
-
if (!body.available) {
|
|
131
|
-
return { ...r, bodyAvailable: false, reason: body.reason };
|
|
132
|
-
}
|
|
133
|
-
return { ...r, responseBody: body.body, bodyAvailable: true };
|
|
134
|
-
});
|
|
135
|
-
return [startTool, stopTool, listTool, getTool];
|
|
136
|
-
}
|
|
137
|
-
function globMatch(pattern, str) {
|
|
138
|
-
const re = new RegExp('^' +
|
|
139
|
-
pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') +
|
|
140
|
-
'$');
|
|
141
|
-
return re.test(str);
|
|
142
|
-
}
|
package/lib/mcp/tools/eval.d.ts
DELETED
package/lib/mcp/tools/eval.js
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import { listTabs } from "../../cdp/tabs.js";
|
|
2
|
-
import { defineTool } from "../server.js";
|
|
3
|
-
export function browserTools(attachRef) {
|
|
4
|
-
const requireAttached = () => {
|
|
5
|
-
if (!attachRef.current) {
|
|
6
|
-
throw new Error('Not attached. Call attach_chrome first.');
|
|
7
|
-
}
|
|
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
|
-
];
|
|
127
|
-
}
|
package/lib/mcp/tools/find.d.ts
DELETED
package/lib/mcp/tools/find.js
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { normalizeCandidates } from "../../provider/normalize.js";
|
|
2
|
-
import { defineTool } from "../server.js";
|
|
3
|
-
export function findTool(captures) {
|
|
4
|
-
return defineTool({
|
|
5
|
-
name: 'find_requests_containing',
|
|
6
|
-
description: 'Search captured responses for any encoding of `value`.' +
|
|
7
|
-
'Returns a ranked candidate list ' +
|
|
8
|
-
'with the JSON path (when applicable), score, and a short snippet.' +
|
|
9
|
-
'Server-side; bodies stay here.',
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: 'object',
|
|
12
|
-
properties: {
|
|
13
|
-
captureId: { type: 'string' },
|
|
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);
|
|
25
|
-
const ranked = [];
|
|
26
|
-
for (const r of session.requests.values()) {
|
|
27
|
-
if (!r.responseBody) {
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
let hit;
|
|
31
|
-
for (const v of variants) {
|
|
32
|
-
const idx = r.responseBody.indexOf(v);
|
|
33
|
-
if (idx >= 0) {
|
|
34
|
-
const start = Math.max(0, idx - 40);
|
|
35
|
-
const end = Math.min(r.responseBody.length, idx + v.length + 40);
|
|
36
|
-
hit = { snippet: r.responseBody.slice(start, end), variant: v };
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
if (!hit) {
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
// Score: prefer JSON responses + smaller bodies (likely API endpoints).
|
|
44
|
-
const score = (r.contentType.includes('json') ? 10 : 0) +
|
|
45
|
-
Math.max(0, 5 - Math.log10(Math.max(r.size, 1)));
|
|
46
|
-
ranked.push({
|
|
47
|
-
requestId: r.requestId,
|
|
48
|
-
url: r.url,
|
|
49
|
-
method: r.method,
|
|
50
|
-
status: r.status,
|
|
51
|
-
contentType: r.contentType,
|
|
52
|
-
matchedVariant: hit.variant,
|
|
53
|
-
snippet: hit.snippet,
|
|
54
|
-
score,
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
ranked.sort((a, b) => b.score - a.score);
|
|
58
|
-
return { candidates: ranked.slice(0, limit ?? 10) };
|
|
59
|
-
});
|
|
60
|
-
}
|