@reclaimprotocol/agent 0.1.0-dev.1
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 +287 -0
- package/lib/cdp/capture.d.ts +22 -0
- package/lib/cdp/capture.js +159 -0
- package/lib/cdp/client.d.ts +9 -0
- package/lib/cdp/client.js +11 -0
- package/lib/cdp/eviction.d.ts +16 -0
- package/lib/cdp/eviction.js +38 -0
- package/lib/cdp/launcher.d.ts +14 -0
- package/lib/cdp/launcher.js +102 -0
- package/lib/cdp/tabs.d.ts +6 -0
- package/lib/cdp/tabs.js +10 -0
- package/lib/cli/args.d.ts +14 -0
- package/lib/cli/args.js +19 -0
- package/lib/cli/commands.d.ts +1 -0
- package/lib/cli/commands.js +19 -0
- package/lib/cli.d.ts +1 -0
- package/lib/cli.js +7 -0
- package/lib/config/store.d.ts +45 -0
- package/lib/config/store.js +46 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +7 -0
- package/lib/logger.d.ts +2 -0
- package/lib/logger.js +7 -0
- package/lib/mcp/api/operations.gen.d.ts +152 -0
- package/lib/mcp/api/operations.gen.js +2300 -0
- package/lib/mcp/api/tools.d.ts +3 -0
- package/lib/mcp/api/tools.js +36 -0
- package/lib/mcp/remap_errors.d.ts +2 -0
- package/lib/mcp/remap_errors.js +27 -0
- package/lib/mcp/server.d.ts +38 -0
- package/lib/mcp/server.js +76 -0
- package/lib/mcp/start.d.ts +1 -0
- package/lib/mcp/start.js +63 -0
- package/lib/mcp/tools/agent/analyze.d.ts +3 -0
- package/lib/mcp/tools/agent/analyze.js +26 -0
- package/lib/mcp/tools/agent/attach.d.ts +11 -0
- package/lib/mcp/tools/agent/attach.js +72 -0
- package/lib/mcp/tools/agent/build.d.ts +33 -0
- package/lib/mcp/tools/agent/build.js +74 -0
- package/lib/mcp/tools/agent/capture.d.ts +10 -0
- package/lib/mcp/tools/agent/capture.js +142 -0
- package/lib/mcp/tools/agent/eval.d.ts +5 -0
- package/lib/mcp/tools/agent/eval.js +127 -0
- package/lib/mcp/tools/agent/find.d.ts +3 -0
- package/lib/mcp/tools/agent/find.js +60 -0
- package/lib/mcp/tools/agent/proof.d.ts +3 -0
- package/lib/mcp/tools/agent/proof.js +214 -0
- package/lib/mcp/tools/agent/propose.d.ts +9 -0
- package/lib/mcp/tools/agent/propose.js +75 -0
- package/lib/mcp/tools/agent/publish.d.ts +3 -0
- package/lib/mcp/tools/agent/publish.js +122 -0
- package/lib/mcp/tools/agent/replay.d.ts +3 -0
- package/lib/mcp/tools/agent/replay.js +46 -0
- package/lib/mcp/tools/agent/session.d.ts +10 -0
- package/lib/mcp/tools/agent/session.js +29 -0
- package/lib/mcp/tools/credentials/authenticate.d.ts +3 -0
- package/lib/mcp/tools/credentials/authenticate.js +108 -0
- package/lib/mcp/tools/credentials/build.d.ts +16 -0
- package/lib/mcp/tools/credentials/build.js +33 -0
- package/lib/mcp/tools/credentials/cache.d.ts +70 -0
- package/lib/mcp/tools/credentials/cache.js +114 -0
- package/lib/mcp/tools/credentials/env-file.d.ts +25 -0
- package/lib/mcp/tools/credentials/env-file.js +71 -0
- package/lib/mcp/tools/credentials/import.d.ts +2 -0
- package/lib/mcp/tools/credentials/import.js +52 -0
- package/lib/mcp/tools/credentials/issue.d.ts +2 -0
- package/lib/mcp/tools/credentials/issue.js +84 -0
- package/lib/mcp/tools/credentials/list-cached.d.ts +2 -0
- package/lib/mcp/tools/credentials/list-cached.js +55 -0
- package/lib/mcp/tools/credentials/register.d.ts +18 -0
- package/lib/mcp/tools/credentials/register.js +30 -0
- package/lib/mcp/tools/credentials/resolve-owner-key.d.ts +12 -0
- package/lib/mcp/tools/credentials/resolve-owner-key.js +68 -0
- package/lib/mcp/tools/credentials/session-cache.d.ts +14 -0
- package/lib/mcp/tools/credentials/session-cache.js +19 -0
- package/lib/mcp/tools/credentials/status.d.ts +3 -0
- package/lib/mcp/tools/credentials/status.js +62 -0
- package/lib/mcp-bin.d.ts +1 -0
- package/lib/mcp-bin.js +7 -0
- package/lib/old/client.d.ts +67 -0
- package/lib/old/client.js +146 -0
- package/lib/old/flag.d.ts +11 -0
- package/lib/old/flag.js +13 -0
- package/lib/old/identity.d.ts +22 -0
- package/lib/old/identity.js +47 -0
- package/lib/old/instructions.d.ts +7 -0
- package/lib/old/instructions.js +29 -0
- package/lib/old/login-cdp.d.ts +23 -0
- package/lib/old/login-cdp.js +125 -0
- package/lib/old/to-register.d.ts +47 -0
- package/lib/old/to-register.js +133 -0
- package/lib/old/tools/authenticate.d.ts +18 -0
- package/lib/old/tools/authenticate.js +127 -0
- package/lib/old/tools/build.d.ts +9 -0
- package/lib/old/tools/build.js +16 -0
- package/lib/old/tools/me-providers.d.ts +10 -0
- package/lib/old/tools/me-providers.js +32 -0
- package/lib/old/tools/publish.d.ts +16 -0
- package/lib/old/tools/publish.js +97 -0
- package/lib/paths.d.ts +11 -0
- package/lib/paths.js +32 -0
- package/lib/pkg.d.ts +3 -0
- package/lib/pkg.js +13 -0
- package/lib/proof/attestor.d.ts +39 -0
- package/lib/proof/attestor.js +207 -0
- package/lib/proof/replay.d.ts +8 -0
- package/lib/proof/replay.js +73 -0
- package/lib/provider/constraints.d.ts +2 -0
- package/lib/provider/constraints.js +122 -0
- package/lib/provider/draft.d.ts +6 -0
- package/lib/provider/draft.js +68 -0
- package/lib/provider/graphql.d.ts +2 -0
- package/lib/provider/graphql.js +35 -0
- package/lib/provider/matcher.d.ts +36 -0
- package/lib/provider/matcher.js +436 -0
- package/lib/provider/normalize.d.ts +4 -0
- package/lib/provider/normalize.js +51 -0
- package/lib/provider/schema.d.ts +76 -0
- package/lib/provider/schema.js +1 -0
- package/lib/provider/secrets.d.ts +5 -0
- package/lib/provider/secrets.js +22 -0
- package/lib/provider/to-version.d.ts +22 -0
- package/lib/provider/to-version.js +68 -0
- package/lib/types/openapi.d.ts +4 -0
- package/lib/types/openapi.js +1 -0
- package/package.json +88 -0
- package/scripts/download-zk-files.ts +42 -0
- package/scripts/prepend-shebang.ts +17 -0
- package/skill/SKILL.md +248 -0
- package/src/cdp/capture.ts +215 -0
- package/src/cdp/client.ts +20 -0
- package/src/cdp/eviction.ts +52 -0
- package/src/cdp/launcher.ts +133 -0
- package/src/cdp/tabs.ts +22 -0
- package/src/cli/args.ts +20 -0
- package/src/cli/commands.ts +27 -0
- package/src/cli.ts +7 -0
- package/src/config/store.ts +86 -0
- package/src/index.ts +12 -0
- package/src/logger.ts +8 -0
- package/src/mcp/api/operations.gen.ts +2397 -0
- package/src/mcp/api/tools.ts +45 -0
- package/src/mcp/remap_errors.ts +35 -0
- package/src/mcp/server.ts +119 -0
- package/src/mcp/start.ts +78 -0
- package/src/mcp/tools/agent/analyze.ts +34 -0
- package/src/mcp/tools/agent/attach.ts +95 -0
- package/src/mcp/tools/agent/build.ts +99 -0
- package/src/mcp/tools/agent/capture.ts +202 -0
- package/src/mcp/tools/agent/eval.ts +161 -0
- package/src/mcp/tools/agent/find.ts +73 -0
- package/src/mcp/tools/agent/proof.ts +272 -0
- package/src/mcp/tools/agent/propose.ts +111 -0
- package/src/mcp/tools/agent/publish.ts +155 -0
- package/src/mcp/tools/agent/replay.ts +58 -0
- package/src/mcp/tools/agent/session.ts +46 -0
- package/src/mcp/tools/credentials/authenticate.ts +135 -0
- package/src/mcp/tools/credentials/build.ts +37 -0
- package/src/mcp/tools/credentials/cache.ts +161 -0
- package/src/mcp/tools/credentials/env-file.ts +101 -0
- package/src/mcp/tools/credentials/import.ts +72 -0
- package/src/mcp/tools/credentials/issue.ts +107 -0
- package/src/mcp/tools/credentials/list-cached.ts +66 -0
- package/src/mcp/tools/credentials/register.ts +49 -0
- package/src/mcp/tools/credentials/resolve-owner-key.ts +78 -0
- package/src/mcp/tools/credentials/session-cache.ts +31 -0
- package/src/mcp/tools/credentials/status.ts +82 -0
- package/src/mcp-bin.ts +7 -0
- package/src/old/client.ts +236 -0
- package/src/old/flag.ts +13 -0
- package/src/old/identity.ts +56 -0
- package/src/old/instructions.ts +30 -0
- package/src/old/login-cdp.ts +153 -0
- package/src/old/to-register.ts +234 -0
- package/src/old/tools/authenticate.ts +152 -0
- package/src/old/tools/build.ts +21 -0
- package/src/old/tools/me-providers.ts +42 -0
- package/src/old/tools/publish.ts +135 -0
- package/src/paths.ts +37 -0
- package/src/pkg.ts +24 -0
- package/src/proof/attestor.ts +286 -0
- package/src/proof/replay.ts +102 -0
- package/src/provider/constraints.ts +150 -0
- package/src/provider/draft.ts +105 -0
- package/src/provider/graphql.ts +42 -0
- package/src/provider/matcher.ts +616 -0
- package/src/provider/normalize.ts +65 -0
- package/src/provider/schema.ts +92 -0
- package/src/provider/secrets.ts +29 -0
- package/src/provider/to-version.ts +90 -0
- package/src/types/openapi.ts +7 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { draftProvider } from '../../../provider/draft.ts'
|
|
3
|
+
import { OPRF_MAX_BYTES } from '../../../provider/matcher.ts'
|
|
4
|
+
import type { OprfMode } from '../../../provider/schema.ts'
|
|
5
|
+
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
6
|
+
import type { CaptureStore } from './capture.ts'
|
|
7
|
+
|
|
8
|
+
export interface SecretStore {
|
|
9
|
+
put: (values: Record<string, string>) => string
|
|
10
|
+
resolve: (ref: string) => Record<string, string> | undefined
|
|
11
|
+
clear: () => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function makeSecretStore(): SecretStore {
|
|
15
|
+
const map = new Map<string, Record<string, string>>()
|
|
16
|
+
return {
|
|
17
|
+
put: (values) => {
|
|
18
|
+
const id = `sref_${randomUUID()}`
|
|
19
|
+
map.set(id, values)
|
|
20
|
+
return id
|
|
21
|
+
},
|
|
22
|
+
resolve: (ref) => map.get(ref),
|
|
23
|
+
clear: () => map.clear(),
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function proposeTool(
|
|
28
|
+
captures: CaptureStore,
|
|
29
|
+
secrets: SecretStore,
|
|
30
|
+
): RegisteredTool {
|
|
31
|
+
return defineTool<{
|
|
32
|
+
captureId: string
|
|
33
|
+
requestId: string
|
|
34
|
+
target: string
|
|
35
|
+
name: string
|
|
36
|
+
hash?: OprfMode
|
|
37
|
+
}>(
|
|
38
|
+
{
|
|
39
|
+
name: 'propose_provider',
|
|
40
|
+
description:
|
|
41
|
+
'Draft a Reclaim provider JSON from a captured request + ' +
|
|
42
|
+
'a target value. ' +
|
|
43
|
+
'Secret header values are stored server-side and replaced with an ' +
|
|
44
|
+
'opaque secretParamsRef ' +
|
|
45
|
+
'in the response — raw secret values never appear in tool output. ' +
|
|
46
|
+
"`hash` is opt-in: when set, the captured value is OPRF'd so " +
|
|
47
|
+
'the attestor never sees ' +
|
|
48
|
+
'cleartext. Skill should ask the dev first. `oprf-raw` ' +
|
|
49
|
+
'is the only variant that runs ' +
|
|
50
|
+
'without external deps; `oprf` needs gnark (Linux/macOS), ' +
|
|
51
|
+
'`oprf-mpc` needs a TEE attestor.',
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
captureId: { type: 'string' },
|
|
56
|
+
requestId: { type: 'string' },
|
|
57
|
+
target: { type: 'string' },
|
|
58
|
+
name: { type: 'string' },
|
|
59
|
+
hash: { type: 'string', enum: ['oprf-raw', 'oprf', 'oprf-mpc'] },
|
|
60
|
+
},
|
|
61
|
+
required: ['captureId', 'requestId', 'target', 'name'],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
async({ captureId, requestId, target, name, hash }) => {
|
|
65
|
+
if(hash !== undefined && target.length > OPRF_MAX_BYTES) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`OPRF caps hashed values at ${OPRF_MAX_BYTES} bytes; ` +
|
|
68
|
+
`got ${target.length}-byte target ` +
|
|
69
|
+
`"${target}". Try a tighter target ` +
|
|
70
|
+
'(e.g. just the relevant substring) or call ' +
|
|
71
|
+
'propose_provider without `hash`.',
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const session = captures.sessions.get(captureId)
|
|
76
|
+
if(!session) {
|
|
77
|
+
throw new Error(`no capture ${captureId}`)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const r = session.requests.get(requestId)
|
|
81
|
+
if(!r) {
|
|
82
|
+
throw new Error(`no request ${requestId}`)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const body = session.bodies.get(requestId)
|
|
86
|
+
if(!body.available) {
|
|
87
|
+
throw new Error(`request body unavailable: ${body.reason}`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const reqWithBody = { ...r, responseBody: body.body }
|
|
91
|
+
const { provider, secretRefs } = draftProvider(
|
|
92
|
+
reqWithBody,
|
|
93
|
+
target,
|
|
94
|
+
name,
|
|
95
|
+
hash,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// Stash real secret values for later replay/proof.
|
|
99
|
+
const realSecrets: Record<string, string> = {}
|
|
100
|
+
for(const ref of secretRefs) {
|
|
101
|
+
const real = r.requestHeaders[ref.name]
|
|
102
|
+
if(real !== undefined) {
|
|
103
|
+
realSecrets[ref.name] = real
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const secretParamsRef = secrets.put(realSecrets)
|
|
108
|
+
return { provider, secretRefs, secretParamsRef }
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client'
|
|
2
|
+
import type { ReclaimProvider } from '../../../provider/schema.ts'
|
|
3
|
+
import {
|
|
4
|
+
nextVersion,
|
|
5
|
+
providerToCreateVersionRequest,
|
|
6
|
+
} from '../../../provider/to-version.ts'
|
|
7
|
+
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
8
|
+
|
|
9
|
+
export function createVersionFromCaptureTool(
|
|
10
|
+
client: ReclaimClient,
|
|
11
|
+
): RegisteredTool {
|
|
12
|
+
return defineTool<{
|
|
13
|
+
providerId: string
|
|
14
|
+
provider: ReclaimProvider
|
|
15
|
+
initialUrl: string
|
|
16
|
+
notes?: string
|
|
17
|
+
version?: { major: number, minor: number, patch: number }
|
|
18
|
+
}>(
|
|
19
|
+
{
|
|
20
|
+
name: 'create_provider_version_from_capture',
|
|
21
|
+
description:
|
|
22
|
+
'Persist a drafted ReclaimProvider as a verification version. ' +
|
|
23
|
+
'Call this after run_proof succeeds, passing the `provider` object ' +
|
|
24
|
+
'from propose_provider VERBATIM. This tool builds the full version ' +
|
|
25
|
+
'body (the requests[] array, webSettings, etc.) for you — do NOT ' +
|
|
26
|
+
'hand-construct that shape or call create_provider_version directly. ' +
|
|
27
|
+
'Omit `version` to have the tool auto-select the next available ' +
|
|
28
|
+
'version (it lists existing versions and bumps past the highest). ' +
|
|
29
|
+
'For PRIVATE verifications the version goes live immediately; ' +
|
|
30
|
+
'for PUBLIC ones call submit_provider_version_for_review afterwards.',
|
|
31
|
+
inputSchema: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
providerId: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'UUID of the verification to publish the version on.',
|
|
37
|
+
},
|
|
38
|
+
provider: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
description:
|
|
41
|
+
'The `provider` object from propose_provider, passed ' +
|
|
42
|
+
'through verbatim — the tool builds the version body ' +
|
|
43
|
+
'(the requests[] array etc.) for you.',
|
|
44
|
+
properties: {
|
|
45
|
+
name: { type: 'string', description: 'Verification name.' },
|
|
46
|
+
url: {
|
|
47
|
+
type: 'string',
|
|
48
|
+
description:
|
|
49
|
+
'Exact API URL the attestor replays, with {{param}} ' +
|
|
50
|
+
'placeholders for any templated values.',
|
|
51
|
+
},
|
|
52
|
+
method: {
|
|
53
|
+
type: 'string',
|
|
54
|
+
enum: ['GET', 'POST', 'PUT', 'PATCH'],
|
|
55
|
+
},
|
|
56
|
+
headers: {
|
|
57
|
+
type: 'object',
|
|
58
|
+
description:
|
|
59
|
+
'Request headers from the capture. Secret headers are ' +
|
|
60
|
+
'stripped before persistence; the client re-supplies them ' +
|
|
61
|
+
'at runtime.',
|
|
62
|
+
},
|
|
63
|
+
body: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
description: 'Request body template, if any.',
|
|
66
|
+
},
|
|
67
|
+
responseMatches: {
|
|
68
|
+
type: 'array',
|
|
69
|
+
description:
|
|
70
|
+
'What the attestor checks on the revealed response slice.',
|
|
71
|
+
items: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
type: { type: 'string', enum: ['regex', 'contains'] },
|
|
75
|
+
value: { type: 'string' },
|
|
76
|
+
},
|
|
77
|
+
required: ['type', 'value'],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
responseRedactions: {
|
|
81
|
+
type: 'array',
|
|
82
|
+
description:
|
|
83
|
+
'Which portions of the response the prover reveals ' +
|
|
84
|
+
'(xPath / jsonPath / regex; hash to OPRF the value). ' +
|
|
85
|
+
'Paired with responseMatches by index — the tool folds ' +
|
|
86
|
+
'each one into the matching entry\'s `extract` in the ' +
|
|
87
|
+
'persisted version, so keep them aligned 1-to-1.',
|
|
88
|
+
items: {
|
|
89
|
+
type: 'object',
|
|
90
|
+
properties: {
|
|
91
|
+
xPath: { type: 'string' },
|
|
92
|
+
jsonPath: { type: 'string' },
|
|
93
|
+
regex: { type: 'string' },
|
|
94
|
+
hash: {
|
|
95
|
+
type: 'string',
|
|
96
|
+
enum: ['oprf', 'oprf-mpc', 'oprf-raw'],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
paramValues: {
|
|
102
|
+
type: 'object',
|
|
103
|
+
description:
|
|
104
|
+
'Real values for the {{param}} placeholders, ' +
|
|
105
|
+
'e.g. { "username": "Syed" }.',
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
required: ['name', 'url', 'method', 'responseMatches'],
|
|
109
|
+
},
|
|
110
|
+
initialUrl: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description:
|
|
113
|
+
'The website URL the user landed on to trigger the request ' +
|
|
114
|
+
'(e.g. https://github.com/Syed). The URL the verification ' +
|
|
115
|
+
'flow opens for end-users — NOT the API URL inside the provider.',
|
|
116
|
+
},
|
|
117
|
+
notes: {
|
|
118
|
+
type: 'string',
|
|
119
|
+
description:
|
|
120
|
+
'Short change note (e.g. "Initial draft from browser capture").',
|
|
121
|
+
},
|
|
122
|
+
version: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
description:
|
|
125
|
+
'Semver to publish as. Omit to auto-pick the next ' +
|
|
126
|
+
'version above the highest existing one.',
|
|
127
|
+
properties: {
|
|
128
|
+
major: { type: 'number', minimum: 0 },
|
|
129
|
+
minor: { type: 'number', minimum: 0 },
|
|
130
|
+
patch: { type: 'number', minimum: 0 },
|
|
131
|
+
},
|
|
132
|
+
required: ['major', 'minor', 'patch'],
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
required: ['providerId', 'provider', 'initialUrl'],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
async(args) => {
|
|
139
|
+
// Auto-pick the next version if not explicit
|
|
140
|
+
const ver = args.version ?? (await nextVersion(client, args.providerId))
|
|
141
|
+
|
|
142
|
+
const body = providerToCreateVersionRequest(
|
|
143
|
+
args.provider,
|
|
144
|
+
args.initialUrl,
|
|
145
|
+
args.notes,
|
|
146
|
+
ver,
|
|
147
|
+
)
|
|
148
|
+
const result = (await client.call('CreateProviderVersion', {
|
|
149
|
+
params: { providerId: args.providerId },
|
|
150
|
+
body,
|
|
151
|
+
}))
|
|
152
|
+
return result.data
|
|
153
|
+
},
|
|
154
|
+
)
|
|
155
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { replayProvider } from '../../../proof/replay.ts'
|
|
2
|
+
import type { ReclaimProvider } from '../../../provider/schema.ts'
|
|
3
|
+
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
4
|
+
import type { SecretStore } from './propose.ts'
|
|
5
|
+
|
|
6
|
+
export function replayTool(secrets: SecretStore): RegisteredTool {
|
|
7
|
+
return defineTool<{ provider: unknown, secretParamsRef?: string }>(
|
|
8
|
+
{
|
|
9
|
+
name: 'replay_request',
|
|
10
|
+
description:
|
|
11
|
+
'Replay a drafted provider against the live endpoint ' +
|
|
12
|
+
'using Node fetch (no attestor). ' +
|
|
13
|
+
'Pre-flight gate before run_proof — ' +
|
|
14
|
+
'diagnoses CSRF / token-rotation / matcher-too-tight ' +
|
|
15
|
+
'issues with richer hints than the attestor would produce.',
|
|
16
|
+
inputSchema: {
|
|
17
|
+
type: 'object',
|
|
18
|
+
properties: {
|
|
19
|
+
// `type: 'object'` is required so MCP clients (the LLM)
|
|
20
|
+
// serialize the value as an object, not a JSON-stringified
|
|
21
|
+
// string. `properties: {}` is required so zod-from-json-schema
|
|
22
|
+
// compiles to a passthrough instead of strict-strip — leaving
|
|
23
|
+
// it off makes the server receive `{}`.
|
|
24
|
+
provider: { type: 'object', properties: {} },
|
|
25
|
+
secretParamsRef: { type: 'string' },
|
|
26
|
+
},
|
|
27
|
+
required: ['provider'],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
async({ provider, secretParamsRef }) => {
|
|
31
|
+
let resolved: Record<string, string> = {}
|
|
32
|
+
if(secretParamsRef !== undefined) {
|
|
33
|
+
const r = secrets.resolve(secretParamsRef)
|
|
34
|
+
if(!r) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`unknown secretParamsRef "${secretParamsRef}" — ` +
|
|
37
|
+
'the MCP server has restarted or reset_session was called. ' +
|
|
38
|
+
'Re-run propose_provider to mint a fresh ref.',
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
resolved = r
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const result = await replayProvider(provider as ReclaimProvider, resolved)
|
|
46
|
+
const out: Record<string, unknown> = {
|
|
47
|
+
status: result.status,
|
|
48
|
+
matchesOriginal: result.matchesOriginal,
|
|
49
|
+
bodySnippet: result.body.slice(0, 256),
|
|
50
|
+
}
|
|
51
|
+
if(result.hints !== undefined) {
|
|
52
|
+
out.hints = result.hints
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return out
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ConfigStore } from '../../../config/store.ts'
|
|
2
|
+
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
3
|
+
import type { AttachState } from './attach.ts'
|
|
4
|
+
import type { CaptureStore } from './capture.ts'
|
|
5
|
+
|
|
6
|
+
export function sessionTools(
|
|
7
|
+
attachRef: { current?: AttachState, config: ConfigStore },
|
|
8
|
+
captures: CaptureStore,
|
|
9
|
+
secrets: { clear: () => void },
|
|
10
|
+
): RegisteredTool[] {
|
|
11
|
+
return [
|
|
12
|
+
defineTool<Record<string, never>>(
|
|
13
|
+
{
|
|
14
|
+
name: 'get_session',
|
|
15
|
+
description:
|
|
16
|
+
'Return current attach state, active captures, and persisted config.',
|
|
17
|
+
inputSchema: { type: 'object', properties: {} },
|
|
18
|
+
},
|
|
19
|
+
async() => ({
|
|
20
|
+
config: attachRef.config.read()?.browser_agent,
|
|
21
|
+
attached: Boolean(attachRef.current),
|
|
22
|
+
port: attachRef.current?.port,
|
|
23
|
+
captures: [...captures.sessions.keys()],
|
|
24
|
+
activeTab: captures.activeTab,
|
|
25
|
+
}),
|
|
26
|
+
),
|
|
27
|
+
defineTool<Record<string, never>>(
|
|
28
|
+
{
|
|
29
|
+
name: 'reset_session',
|
|
30
|
+
description:
|
|
31
|
+
'Stop all captures, drop in-memory secrets, and clear capture state.',
|
|
32
|
+
inputSchema: { type: 'object', properties: {} },
|
|
33
|
+
},
|
|
34
|
+
async() => {
|
|
35
|
+
for(const s of captures.sessions.values()) {
|
|
36
|
+
await s.stop().catch(() => {})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
captures.sessions.clear()
|
|
40
|
+
delete captures.activeTab
|
|
41
|
+
secrets.clear()
|
|
42
|
+
return { reset: true }
|
|
43
|
+
},
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client'
|
|
2
|
+
import type {
|
|
3
|
+
CredentialAttachSession as Session
|
|
4
|
+
} from '../../../types/openapi.ts'
|
|
5
|
+
import { defineTool, type RegisteredTool } from '../../server.ts'
|
|
6
|
+
import { writeSession } from './session-cache.ts'
|
|
7
|
+
|
|
8
|
+
export function authenticateTool(client: ReclaimClient): RegisteredTool {
|
|
9
|
+
return defineTool<{
|
|
10
|
+
code?: string
|
|
11
|
+
}>(
|
|
12
|
+
{
|
|
13
|
+
name: 'reclaim_authenticate',
|
|
14
|
+
description:
|
|
15
|
+
'Authorize the CLI / MCP to make authenticated API calls by having ' +
|
|
16
|
+
'the user sign in on the dashboard. Two steps:\n\n' +
|
|
17
|
+
'Step 1 — call with no arguments. Returns a `code` and `attachUrl`. ' +
|
|
18
|
+
'Tell the user to open the URL, sign in, and authorize.\n\n' +
|
|
19
|
+
'Step 2 — once the user says they have authorized, call again ' +
|
|
20
|
+
'with the same `code` from step 1 to check if confirmed. ' +
|
|
21
|
+
'Returns `LINKED` with a 2-day bearer token when confirmed, ' +
|
|
22
|
+
'`PENDING` if not yet, or `EXPIRED` if too long has passed.\n\n' +
|
|
23
|
+
'The token is cached in ~/.reclaim/config.json automatically.\n\n' +
|
|
24
|
+
'To then link a credential to an org (needed only to use it ' +
|
|
25
|
+
'for verification in production), use the token with the ' +
|
|
26
|
+
'generated tools: `list_orgs` → `link_credential` with the ' +
|
|
27
|
+
"credential's `address` and the target `orgId`.",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
code: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
pattern: '^[A-HJ-NP-Z2-9]{5}$',
|
|
34
|
+
description:
|
|
35
|
+
'5-character code from a previous step-1 call. ' +
|
|
36
|
+
'When provided, checks whether the user has ' +
|
|
37
|
+
'confirmed that session instead of opening a new one.',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
async(args) => {
|
|
43
|
+
// Step 2: check an existing session
|
|
44
|
+
if(args.code) {
|
|
45
|
+
return checkSession(client, args.code)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Step 1: open a new auth-only session
|
|
49
|
+
const sessionResult = (await client.call('CreateAttachSession'))
|
|
50
|
+
const session = sessionResult.data
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
status: 'PENDING',
|
|
54
|
+
code: session.code,
|
|
55
|
+
attachUrl: session.attachUrl,
|
|
56
|
+
expiresAt: session.expiresAt,
|
|
57
|
+
instructions:
|
|
58
|
+
`Open ${session.attachUrl} in your browser, sign in, and click ` +
|
|
59
|
+
'"Authorize this device". ' +
|
|
60
|
+
'Once done, call authenticate again with code: ' +
|
|
61
|
+
`"${session.code}" to confirm.`,
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function checkSession(
|
|
68
|
+
client: ReclaimClient,
|
|
69
|
+
code: string,
|
|
70
|
+
): Promise<unknown> {
|
|
71
|
+
let session: Session
|
|
72
|
+
try {
|
|
73
|
+
const sessionResult = (await client.call('GetAttachSession', {
|
|
74
|
+
params: { code }
|
|
75
|
+
}))
|
|
76
|
+
session = sessionResult.data
|
|
77
|
+
} catch(err) {
|
|
78
|
+
const status = (err as { status?: number }).status
|
|
79
|
+
if(status === 404) {
|
|
80
|
+
return {
|
|
81
|
+
status: 'NOT_FOUND',
|
|
82
|
+
hint:
|
|
83
|
+
'Session not found — it may have expired (15-min window). ' +
|
|
84
|
+
'Call authenticate without a code to start a new session.',
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
throw new Error(`Failed to check session: ${(err as Error).message}`)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if(session.status === 'LINKED') {
|
|
92
|
+
const token = session.token?.trim()
|
|
93
|
+
if(token) {
|
|
94
|
+
writeSession({
|
|
95
|
+
token,
|
|
96
|
+
expiresAt: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
|
|
97
|
+
})
|
|
98
|
+
client.setToken(token)
|
|
99
|
+
return {
|
|
100
|
+
status: 'LINKED',
|
|
101
|
+
token,
|
|
102
|
+
note: 'Token cached in ~/.reclaim/config.json and active now.',
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
status: 'LINKED_NO_TOKEN',
|
|
108
|
+
hint:
|
|
109
|
+
'Session confirmed but no token issued. ' +
|
|
110
|
+
'Restart the backend (npm run dev) and try again.',
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if(session.status === 'EXPIRED') {
|
|
115
|
+
return {
|
|
116
|
+
status: 'EXPIRED',
|
|
117
|
+
hint:
|
|
118
|
+
'Session expired (15-min window passed). ' +
|
|
119
|
+
'Call authenticate without a code to start a new session.',
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Still PENDING
|
|
124
|
+
return {
|
|
125
|
+
status: 'PENDING',
|
|
126
|
+
code: session.code,
|
|
127
|
+
attachUrl: session.attachUrl,
|
|
128
|
+
hint:
|
|
129
|
+
'Session is still waiting. ' +
|
|
130
|
+
`Make sure you opened ${session.attachUrl}, ` +
|
|
131
|
+
'signed in, and clicked "Authorize this device". ' +
|
|
132
|
+
`Call authenticate again with code: "${session.code}"` +
|
|
133
|
+
' once you have done so.',
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client'
|
|
2
|
+
import type { RegisteredTool } from '../../server.ts'
|
|
3
|
+
import { authenticateTool } from './authenticate.ts'
|
|
4
|
+
import { importTool } from './import.ts'
|
|
5
|
+
import { issueTool } from './issue.ts'
|
|
6
|
+
import { listCachedTool } from './list-cached.ts'
|
|
7
|
+
import { resolveOwnerKeyTool } from './resolve-owner-key.ts'
|
|
8
|
+
import { statusTool } from './status.ts'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Local-only credential tools — no backend client needed. These manage app
|
|
12
|
+
* key-pairs entirely on disk / in the env (mint, import, list, resolve which
|
|
13
|
+
* key to sign proofs with), so they are useful in BOTH builder and
|
|
14
|
+
* OLD-devtools mode.
|
|
15
|
+
*/
|
|
16
|
+
export function buildLocalCredentialTools(): RegisteredTool[] {
|
|
17
|
+
return [
|
|
18
|
+
listCachedTool(),
|
|
19
|
+
resolveOwnerKeyTool(),
|
|
20
|
+
issueTool(),
|
|
21
|
+
importTool(),
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Full builder-mode credential set: the local tools plus the two that talk to
|
|
27
|
+
* the builder backend (`reclaim_authenticate` device-code flow,
|
|
28
|
+
* `reclaim_status`). Old mode does NOT use these — it has its own
|
|
29
|
+
* authenticate and no credential-status analog.
|
|
30
|
+
*/
|
|
31
|
+
export function buildCredentialTools(client: ReclaimClient): RegisteredTool[] {
|
|
32
|
+
return [
|
|
33
|
+
authenticateTool(client),
|
|
34
|
+
statusTool(client),
|
|
35
|
+
...buildLocalCredentialTools(),
|
|
36
|
+
]
|
|
37
|
+
}
|