@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,84 @@
|
|
|
1
|
+
import { generateKeypair } from '@reclaimprotocol/client';
|
|
2
|
+
import { findProjectRoot } from "../../../paths.js";
|
|
3
|
+
import { defineTool } from "../../server.js";
|
|
4
|
+
import { findCredentialFromCachedBy } from "./cache.js";
|
|
5
|
+
import { persistCredentialLocally } from "./register.js";
|
|
6
|
+
export function issueTool() {
|
|
7
|
+
return defineTool({
|
|
8
|
+
name: 'issue_credentials',
|
|
9
|
+
description: 'Mint a fresh anonymous Reclaim app key-pair (or reuse ' +
|
|
10
|
+
'a cached one via `reuseCachedAppPk`) and cache it locally. ' +
|
|
11
|
+
'By default also writes RECLAIM_PRIVATE_KEY to the project ' +
|
|
12
|
+
'.env (mode 0600) and appends .env to .gitignore; pass ' +
|
|
13
|
+
'`writeEnv: false` to keep it in the local cache (memory) ' +
|
|
14
|
+
'only and leave .env untouched. ALWAYS ask the dev whether ' +
|
|
15
|
+
'to save to .env before writing it. The key-pair is ' +
|
|
16
|
+
'generated locally and is NOT registered with the server — ' +
|
|
17
|
+
'anonymous credentials operate in SANDBOX mode entirely ' +
|
|
18
|
+
'client-side. PREFER REUSE over generating a new credential ' +
|
|
19
|
+
'unless the user explicitly asks for a new app identity. To ' +
|
|
20
|
+
'use a credential for verification in production, link it to ' +
|
|
21
|
+
'an organization (via link_credential) and add billing.',
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: 'object',
|
|
24
|
+
properties: {
|
|
25
|
+
reuseCachedAppPk: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
pattern: '^0x[0-9a-fA-F]{40}$',
|
|
28
|
+
description: 'Lowercase ETH address of a cached credential ' +
|
|
29
|
+
'to reuse instead of minting a new one.',
|
|
30
|
+
},
|
|
31
|
+
label: {
|
|
32
|
+
type: 'string',
|
|
33
|
+
minLength: 1,
|
|
34
|
+
maxLength: 100,
|
|
35
|
+
description: 'Human label stored in the local cache.',
|
|
36
|
+
},
|
|
37
|
+
projectDir: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: 'Project directory to write .env into. Defaults ' +
|
|
40
|
+
'to the closest ancestor of cwd containing ' +
|
|
41
|
+
'.git or package.json.',
|
|
42
|
+
},
|
|
43
|
+
writeEnv: {
|
|
44
|
+
type: 'boolean',
|
|
45
|
+
description: 'Write RECLAIM_PRIVATE_KEY to the project .env ' +
|
|
46
|
+
'(default true). Set false to cache the credential ' +
|
|
47
|
+
'in memory only, without touching .env.',
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}, async (args) => {
|
|
52
|
+
const projectDir = findProjectRoot(args.projectDir || process.cwd());
|
|
53
|
+
let keypair;
|
|
54
|
+
let reused = false;
|
|
55
|
+
if (args.reuseCachedAppPk) {
|
|
56
|
+
const cached = findCredentialFromCachedBy(args.reuseCachedAppPk);
|
|
57
|
+
if (!cached) {
|
|
58
|
+
throw new Error(`No cached credential for ${args.reuseCachedAppPk}. ` +
|
|
59
|
+
'Call list_cached_credentials first.');
|
|
60
|
+
}
|
|
61
|
+
keypair = {
|
|
62
|
+
address: cached.address,
|
|
63
|
+
privateKey: cached.privateKey,
|
|
64
|
+
};
|
|
65
|
+
reused = true;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
keypair = generateKeypair();
|
|
69
|
+
}
|
|
70
|
+
const writeEnv = args.writeEnv ?? true;
|
|
71
|
+
const persisted = persistCredentialLocally(keypair, args.label, projectDir, writeEnv);
|
|
72
|
+
return {
|
|
73
|
+
reused,
|
|
74
|
+
...persisted,
|
|
75
|
+
note: writeEnv
|
|
76
|
+
? 'Private key written to .env (and the local cache); it is ' +
|
|
77
|
+
'NOT in this response on purpose. The credential is ' +
|
|
78
|
+
'local-only (SANDBOX); link it to an org for production.'
|
|
79
|
+
: 'Private key cached in memory only (.env untouched). Sign ' +
|
|
80
|
+
'proofs with it by passing run_proof({ ownerAddress }). The ' +
|
|
81
|
+
'credential is local-only (SANDBOX).',
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { findProjectRoot } from "../../../paths.js";
|
|
2
|
+
import { defineTool } from "../../server.js";
|
|
3
|
+
import { readCredentialsFromCache, suggestCredentialForDir } from "./cache.js";
|
|
4
|
+
export function listCachedTool() {
|
|
5
|
+
return defineTool({
|
|
6
|
+
name: 'list_cached_credentials',
|
|
7
|
+
description: 'List Reclaim app credentials (ETH key-pairs) cached ' +
|
|
8
|
+
'locally in ~/.reclaim/config.json. Returns one ' +
|
|
9
|
+
'row per credential with address, optional label and ' +
|
|
10
|
+
'orgId, createdAt, createdIn (where it was first created), ' +
|
|
11
|
+
'and the project directories that have used it. Private keys ' +
|
|
12
|
+
'are stripped. The response also reports `currentDir` and ' +
|
|
13
|
+
'flags the best credential for it (`suggested`, plus ' +
|
|
14
|
+
'`suggestedAddress`/`suggestionReason`) — prefer reusing that ' +
|
|
15
|
+
'one over minting a new credential. When the cache is empty a ' +
|
|
16
|
+
'`suggestion` string explains how to create one.',
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {},
|
|
20
|
+
},
|
|
21
|
+
}, async () => {
|
|
22
|
+
const doc = readCredentialsFromCache();
|
|
23
|
+
const currentDir = findProjectRoot(process.cwd());
|
|
24
|
+
const suggestion = suggestCredentialForDir(currentDir, doc.credentials);
|
|
25
|
+
const suggestedAddress = suggestion?.credential.address;
|
|
26
|
+
const items = doc.credentials.map((c) => ({
|
|
27
|
+
address: c.address,
|
|
28
|
+
label: c.label,
|
|
29
|
+
orgId: c.orgId,
|
|
30
|
+
createdAt: c.createdAt,
|
|
31
|
+
createdIn: c.createdIn,
|
|
32
|
+
projectDirs: c.projectDirs,
|
|
33
|
+
matchesCurrentDir: c.createdIn === currentDir || c.projectDirs.includes(currentDir),
|
|
34
|
+
suggested: c.address === suggestedAddress,
|
|
35
|
+
}));
|
|
36
|
+
// Promote the single suggested row to the top; stable sort keeps
|
|
37
|
+
// the rest in insertion (newest-first) order.
|
|
38
|
+
items.sort((a, b) => Number(b.suggested) - Number(a.suggested));
|
|
39
|
+
if (items.length === 0) {
|
|
40
|
+
return {
|
|
41
|
+
items,
|
|
42
|
+
currentDir,
|
|
43
|
+
suggestion: 'No cached credentials. Call issue_credentials to mint a new ' +
|
|
44
|
+
'app key-pair, or import_credentials if you already hold a ' +
|
|
45
|
+
'private key. Do not invent credentials.',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
items,
|
|
50
|
+
currentDir,
|
|
51
|
+
suggestedAddress,
|
|
52
|
+
suggestionReason: suggestion?.reason,
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { EthKeypair } from '@reclaimprotocol/client';
|
|
2
|
+
import { type CachedCredential } from './cache.ts';
|
|
3
|
+
import { type EnvWriteResult } from './env-file.ts';
|
|
4
|
+
/** Shape returned by `persistCredentialLocally` — the fields both
|
|
5
|
+
* `issue_credentials` and `import_credentials` expose in their MCP
|
|
6
|
+
* responses. The env-file fields are present only when the credential
|
|
7
|
+
* was persisted to `.env` (`writeEnv: true`); a memory-only credential
|
|
8
|
+
* lives in the cache and omits them. */
|
|
9
|
+
export type PersistedCredential = Pick<CachedCredential, 'address' | 'label'> & Partial<EnvWriteResult> & {
|
|
10
|
+
wroteEnv: boolean;
|
|
11
|
+
};
|
|
12
|
+
/** Local persistence step shared by `issue_credentials` and
|
|
13
|
+
* `import_credentials`. Always upserts the credentials cache; when
|
|
14
|
+
* `writeEnv` is true (default) it also writes `RECLAIM_PRIVATE_KEY` to
|
|
15
|
+
* the project `.env` (0600) and appends `.env` to `.gitignore`. Pass
|
|
16
|
+
* `writeEnv: false` to keep the credential in the cache (memory) only,
|
|
17
|
+
* without touching the project's `.env`. */
|
|
18
|
+
export declare function persistCredentialLocally(keypair: EthKeypair, label: string | undefined, projectDir: string, writeEnv?: boolean): PersistedCredential;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { upsertCredential } from "./cache.js";
|
|
2
|
+
import { writeAppCredentialsToEnv } from "./env-file.js";
|
|
3
|
+
/** Local persistence step shared by `issue_credentials` and
|
|
4
|
+
* `import_credentials`. Always upserts the credentials cache; when
|
|
5
|
+
* `writeEnv` is true (default) it also writes `RECLAIM_PRIVATE_KEY` to
|
|
6
|
+
* the project `.env` (0600) and appends `.env` to `.gitignore`. Pass
|
|
7
|
+
* `writeEnv: false` to keep the credential in the cache (memory) only,
|
|
8
|
+
* without touching the project's `.env`. */
|
|
9
|
+
export function persistCredentialLocally(keypair, label, projectDir, writeEnv = true) {
|
|
10
|
+
const cached = upsertCredential({
|
|
11
|
+
address: keypair.address,
|
|
12
|
+
privateKey: keypair.privateKey,
|
|
13
|
+
label,
|
|
14
|
+
createdAt: new Date().toISOString(),
|
|
15
|
+
createdIn: projectDir,
|
|
16
|
+
projectDirs: [projectDir],
|
|
17
|
+
});
|
|
18
|
+
if (!writeEnv) {
|
|
19
|
+
return { address: cached.address, label: cached.label, wroteEnv: false };
|
|
20
|
+
}
|
|
21
|
+
const env = writeAppCredentialsToEnv(projectDir, keypair.privateKey);
|
|
22
|
+
return {
|
|
23
|
+
address: cached.address,
|
|
24
|
+
label: cached.label,
|
|
25
|
+
wroteEnv: true,
|
|
26
|
+
envPath: env.envPath,
|
|
27
|
+
gitignorePath: env.gitignorePath,
|
|
28
|
+
gitignoreAppended: env.gitignoreAppended,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type RegisteredTool } from '../../server.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Decide which app credential `run_proof` should sign with, BEFORE
|
|
4
|
+
* calling it. `run_proof` requires an `ownerAddress` and does no
|
|
5
|
+
* resolution of its own — this is the single place that "checks memory,
|
|
6
|
+
* env, etc and offers options".
|
|
7
|
+
*
|
|
8
|
+
* Checks, in order: `RECLAIM_PRIVATE_KEY` in the MCP env → the project
|
|
9
|
+
* `.env` → the best cached credential for this directory. Never returns
|
|
10
|
+
* the private key — only the address to pass through as `ownerAddress`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveOwnerKeyTool(): RegisteredTool;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { deriveFromPrivateKey } from '@reclaimprotocol/client';
|
|
2
|
+
import { findProjectRoot } from "../../../paths.js";
|
|
3
|
+
import { defineTool } from "../../server.js";
|
|
4
|
+
import { suggestCredentialForDir } from "./cache.js";
|
|
5
|
+
import { readPrivateKeyFromEnvFile } from "./env-file.js";
|
|
6
|
+
/**
|
|
7
|
+
* Decide which app credential `run_proof` should sign with, BEFORE
|
|
8
|
+
* calling it. `run_proof` requires an `ownerAddress` and does no
|
|
9
|
+
* resolution of its own — this is the single place that "checks memory,
|
|
10
|
+
* env, etc and offers options".
|
|
11
|
+
*
|
|
12
|
+
* Checks, in order: `RECLAIM_PRIVATE_KEY` in the MCP env → the project
|
|
13
|
+
* `.env` → the best cached credential for this directory. Never returns
|
|
14
|
+
* the private key — only the address to pass through as `ownerAddress`.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveOwnerKeyTool() {
|
|
17
|
+
return defineTool({
|
|
18
|
+
name: 'resolve_owner_key',
|
|
19
|
+
description: 'Resolve which app credential run_proof should sign with — ' +
|
|
20
|
+
'call this BEFORE run_proof. Checks, in order: ' +
|
|
21
|
+
'RECLAIM_PRIVATE_KEY in the MCP env, RECLAIM_PRIVATE_KEY in the ' +
|
|
22
|
+
'project .env, then the best cached credential for this ' +
|
|
23
|
+
'directory. Returns { ready: true, address, source } when one ' +
|
|
24
|
+
'is available — pass `address` to run_proof as `ownerAddress`. ' +
|
|
25
|
+
'Returns { ready: false } when none exists: then ask the dev to ' +
|
|
26
|
+
'provide a private key (import_credentials) or create one ' +
|
|
27
|
+
'(issue_credentials) — optionally with writeEnv:false to keep ' +
|
|
28
|
+
'it in the local cache (memory) only — and call resolve_owner_key ' +
|
|
29
|
+
'again. Never exposes the private key.',
|
|
30
|
+
inputSchema: { type: 'object', properties: {} },
|
|
31
|
+
}, async () => {
|
|
32
|
+
const dir = findProjectRoot(process.cwd());
|
|
33
|
+
const envKey = process.env.RECLAIM_PRIVATE_KEY;
|
|
34
|
+
if (envKey) {
|
|
35
|
+
return {
|
|
36
|
+
ready: true,
|
|
37
|
+
address: deriveFromPrivateKey(envKey).address,
|
|
38
|
+
source: 'env',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const dotenvKey = readPrivateKeyFromEnvFile(dir);
|
|
42
|
+
if (dotenvKey) {
|
|
43
|
+
return {
|
|
44
|
+
ready: true,
|
|
45
|
+
address: deriveFromPrivateKey(dotenvKey).address,
|
|
46
|
+
source: 'dotenv',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const suggestion = suggestCredentialForDir(dir);
|
|
50
|
+
if (suggestion) {
|
|
51
|
+
return {
|
|
52
|
+
ready: true,
|
|
53
|
+
address: suggestion.credential.address,
|
|
54
|
+
source: 'cache',
|
|
55
|
+
reason: suggestion.reason,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
ready: false,
|
|
60
|
+
message: 'No app credential available (no RECLAIM_PRIVATE_KEY in env ' +
|
|
61
|
+
'or .env, and the cache is empty). Ask the dev to provide ' +
|
|
62
|
+
'their private key via import_credentials, or create one via ' +
|
|
63
|
+
'issue_credentials (pass writeEnv:false to keep it out of ' +
|
|
64
|
+
'.env). Then call resolve_owner_key again and pass its ' +
|
|
65
|
+
'address to run_proof as ownerAddress.',
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type CachedSession = {
|
|
2
|
+
token: string;
|
|
3
|
+
/** ISO timestamp when the token expires (2 days from issue). */
|
|
4
|
+
expiresAt: string;
|
|
5
|
+
/** Email of the user who authorized (if available). */
|
|
6
|
+
email?: string;
|
|
7
|
+
};
|
|
8
|
+
/** Persist the auth token to the `session` section of the unified config. */
|
|
9
|
+
export declare function writeSession(session: CachedSession): void;
|
|
10
|
+
/**
|
|
11
|
+
* Read the cached session, returning it only if the token is still
|
|
12
|
+
* valid (not yet expired). Returns undefined on any error.
|
|
13
|
+
*/
|
|
14
|
+
export declare function readSession(): CachedSession | undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConfigStore } from "../../../config/store.js";
|
|
2
|
+
/** Persist the auth token to the `session` section of the unified config. */
|
|
3
|
+
export function writeSession(session) {
|
|
4
|
+
new ConfigStore().write({ session });
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Read the cached session, returning it only if the token is still
|
|
8
|
+
* valid (not yet expired). Returns undefined on any error.
|
|
9
|
+
*/
|
|
10
|
+
export function readSession() {
|
|
11
|
+
const s = new ConfigStore().read()?.session;
|
|
12
|
+
if (!s?.token || !s.expiresAt) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (new Date(s.expiresAt) <= new Date()) {
|
|
16
|
+
return undefined; // expired
|
|
17
|
+
}
|
|
18
|
+
return s;
|
|
19
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { deriveFromPrivateKey, } from '@reclaimprotocol/client';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import { findProjectRoot } from "../../../paths.js";
|
|
4
|
+
import { defineTool } from "../../server.js";
|
|
5
|
+
import { suggestCredentialForDir } from "./cache.js";
|
|
6
|
+
export function statusTool(client) {
|
|
7
|
+
return defineTool({
|
|
8
|
+
name: 'reclaim_status',
|
|
9
|
+
description: 'Report the live mode (SANDBOX | PRODUCTION | REVOKED) ' +
|
|
10
|
+
'of a Reclaim app credential. Pass `address` — or omit it ' +
|
|
11
|
+
'to derive the address from RECLAIM_PRIVATE_KEY, falling ' +
|
|
12
|
+
'back to the best cached credential for this directory. ' +
|
|
13
|
+
'Useful to confirm a credential is linked to an org ' +
|
|
14
|
+
'with billing before running a production flow.',
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: 'object',
|
|
17
|
+
properties: {
|
|
18
|
+
address: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
pattern: '^0x[0-9a-fA-F]{40}$',
|
|
21
|
+
description: 'Lowercase ETH address. Defaults to the address ' +
|
|
22
|
+
'derived from RECLAIM_PRIVATE_KEY, then the cached ' +
|
|
23
|
+
'credential suggested for the current directory.',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
}, async (args) => {
|
|
28
|
+
let address = args.address?.toLowerCase();
|
|
29
|
+
if (!address && process.env.RECLAIM_PRIVATE_KEY) {
|
|
30
|
+
address = deriveFromPrivateKey(process.env.RECLAIM_PRIVATE_KEY).address;
|
|
31
|
+
}
|
|
32
|
+
if (!address) {
|
|
33
|
+
address = suggestCredentialForDir(findProjectRoot(process.cwd()))
|
|
34
|
+
?.credential.address;
|
|
35
|
+
}
|
|
36
|
+
assert(address, 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
|
+
const credResult = await client
|
|
40
|
+
.call('GetCredentialByAddress', { params: { address } });
|
|
41
|
+
const cred = credResult.data;
|
|
42
|
+
// A registered credential always carries an `id`; its absence means
|
|
43
|
+
// the server had no row for this address — a local-only credential.
|
|
44
|
+
const localOnly = !cred.id;
|
|
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
|
+
...(localOnly ? {
|
|
53
|
+
note: 'Local-only credential — not registered with the server. '
|
|
54
|
+
+ 'It operates in SANDBOX, which is all you need to publish '
|
|
55
|
+
+ 'a verification (only an auth token is required — no '
|
|
56
|
+
+ 'credential link). Linking it to an org with billing (via '
|
|
57
|
+
+ 'link_credential) is only needed for the upcoming feature '
|
|
58
|
+
+ 'that runs verifications for your own app’s users.',
|
|
59
|
+
} : {}),
|
|
60
|
+
};
|
|
61
|
+
});
|
|
62
|
+
}
|
package/lib/mcp-bin.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/mcp-bin.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Default origin of the OLD devtools backend. The client appends the
|
|
2
|
+
* `/api` route prefix itself, so this is the bare origin. Override with
|
|
3
|
+
* `RECLAIM_OLD_API_URL`. */
|
|
4
|
+
export declare const DEFAULT_OLD_API_URL = "https://devapi.reclaimprotocol.org";
|
|
5
|
+
/**
|
|
6
|
+
* Externally-supplied caller identity. We do NOT mint these — the dev
|
|
7
|
+
* provides one:
|
|
8
|
+
* - `bearer`: a Firebase ID token from the devtools dashboard. Attached
|
|
9
|
+
* as `Authorization: Bearer …`; `POST /api/users/login` provisions the
|
|
10
|
+
* user row on first use.
|
|
11
|
+
* - `eth`: an `eth:<lowercase-address>` uid for an already-provisioned
|
|
12
|
+
* eth user. Attached as the `x-eth-uid` header (no signature — the
|
|
13
|
+
* backend trusts the header for a uid that already exists).
|
|
14
|
+
*/
|
|
15
|
+
export type OldIdentity = {
|
|
16
|
+
kind: 'bearer';
|
|
17
|
+
token: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'eth';
|
|
20
|
+
uid: string;
|
|
21
|
+
};
|
|
22
|
+
export interface RegisterProviderBody {
|
|
23
|
+
name: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
loginUrl: string;
|
|
26
|
+
geoLocation?: string;
|
|
27
|
+
providerType?: 'PRIVATE' | 'PUBLIC';
|
|
28
|
+
requestData: unknown[];
|
|
29
|
+
}
|
|
30
|
+
export interface UpdateProviderConfigBody {
|
|
31
|
+
providerConfig: Record<string, unknown>;
|
|
32
|
+
version: string;
|
|
33
|
+
versionInfo: string;
|
|
34
|
+
}
|
|
35
|
+
export interface MyProvidersQuery {
|
|
36
|
+
pageKey?: number;
|
|
37
|
+
pageSize?: number;
|
|
38
|
+
searchQuery?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Minimal REST client for the OLD devtools Express backend (base `/api`).
|
|
42
|
+
* Deliberately NOT a clone of `@reclaimprotocol/client`'s operationId
|
|
43
|
+
* machinery — the old backend has no OpenAPI spec, so this exposes a few
|
|
44
|
+
* hand-written typed methods for exactly the flows old mode needs:
|
|
45
|
+
* authenticate, publish (register / update version), and list-own.
|
|
46
|
+
*
|
|
47
|
+
* Errors are thrown as `@hapi/boom` so `remapErrorAsResponse` renders them
|
|
48
|
+
* identically to builder-mode `ProblemError`s.
|
|
49
|
+
*/
|
|
50
|
+
export declare class ReclaimOldClient {
|
|
51
|
+
#private;
|
|
52
|
+
readonly baseUrl: string;
|
|
53
|
+
constructor(opts?: {
|
|
54
|
+
baseUrl?: string;
|
|
55
|
+
identity?: OldIdentity;
|
|
56
|
+
fetch?: typeof fetch;
|
|
57
|
+
});
|
|
58
|
+
setIdentity(identity: OldIdentity | undefined): void;
|
|
59
|
+
getIdentity(): OldIdentity | undefined;
|
|
60
|
+
/** Validate + provision a bearer identity. The backend creates the user
|
|
61
|
+
* row on first login, which is what makes later authed calls work. */
|
|
62
|
+
loginWithToken(): Promise<unknown>;
|
|
63
|
+
registerProvider(body: RegisterProviderBody): Promise<unknown>;
|
|
64
|
+
updateProviderConfig(providerId: string, body: UpdateProviderConfigBody): Promise<unknown>;
|
|
65
|
+
listVersions(providerId: string): Promise<unknown>;
|
|
66
|
+
getMyProviders(query?: MyProvidersQuery): Promise<unknown>;
|
|
67
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { Boom } from '@hapi/boom';
|
|
2
|
+
import { NAME, VERSION } from "../pkg.js";
|
|
3
|
+
/** Default origin of the OLD devtools backend. The client appends the
|
|
4
|
+
* `/api` route prefix itself, so this is the bare origin. Override with
|
|
5
|
+
* `RECLAIM_OLD_API_URL`. */
|
|
6
|
+
export const DEFAULT_OLD_API_URL = 'https://devapi.reclaimprotocol.org';
|
|
7
|
+
/**
|
|
8
|
+
* Minimal REST client for the OLD devtools Express backend (base `/api`).
|
|
9
|
+
* Deliberately NOT a clone of `@reclaimprotocol/client`'s operationId
|
|
10
|
+
* machinery — the old backend has no OpenAPI spec, so this exposes a few
|
|
11
|
+
* hand-written typed methods for exactly the flows old mode needs:
|
|
12
|
+
* authenticate, publish (register / update version), and list-own.
|
|
13
|
+
*
|
|
14
|
+
* Errors are thrown as `@hapi/boom` so `remapErrorAsResponse` renders them
|
|
15
|
+
* identically to builder-mode `ProblemError`s.
|
|
16
|
+
*/
|
|
17
|
+
export class ReclaimOldClient {
|
|
18
|
+
baseUrl;
|
|
19
|
+
#identity;
|
|
20
|
+
#fetch;
|
|
21
|
+
constructor(opts = {}) {
|
|
22
|
+
this.baseUrl = (opts.baseUrl || DEFAULT_OLD_API_URL).replace(/\/+$/, '');
|
|
23
|
+
this.#identity = opts.identity;
|
|
24
|
+
this.#fetch = opts.fetch ?? fetch;
|
|
25
|
+
}
|
|
26
|
+
setIdentity(identity) {
|
|
27
|
+
this.#identity = identity;
|
|
28
|
+
}
|
|
29
|
+
getIdentity() {
|
|
30
|
+
return this.#identity;
|
|
31
|
+
}
|
|
32
|
+
/** Validate + provision a bearer identity. The backend creates the user
|
|
33
|
+
* row on first login, which is what makes later authed calls work. */
|
|
34
|
+
loginWithToken() {
|
|
35
|
+
return this.#request('POST', '/users/login', { auth: true });
|
|
36
|
+
}
|
|
37
|
+
registerProvider(body) {
|
|
38
|
+
return this.#request('POST', '/providers/register', { body, auth: true });
|
|
39
|
+
}
|
|
40
|
+
updateProviderConfig(providerId, body) {
|
|
41
|
+
return this.#request('POST', `/providers/${encodeURIComponent(providerId)}/config`, { body, auth: true });
|
|
42
|
+
}
|
|
43
|
+
listVersions(providerId) {
|
|
44
|
+
return this.#request('GET', `/providers/${encodeURIComponent(providerId)}/versions`, { auth: true });
|
|
45
|
+
}
|
|
46
|
+
getMyProviders(query = {}) {
|
|
47
|
+
// Every one of these query params is REQUIRED by the backend
|
|
48
|
+
// validator (missing/invalid → 400), so default them all here.
|
|
49
|
+
return this.#request('GET', '/providers/user/paginated', {
|
|
50
|
+
auth: true,
|
|
51
|
+
query: {
|
|
52
|
+
pageKey: query.pageKey ?? 0,
|
|
53
|
+
pageSize: query.pageSize ?? 20,
|
|
54
|
+
searchQuery: query.searchQuery ?? '',
|
|
55
|
+
sortByCreatedLatest: 'true',
|
|
56
|
+
providerType: 'ALL',
|
|
57
|
+
providerStatus: 'ALL',
|
|
58
|
+
providerVisibility: 'ALL',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async #request(method, path, opts = {}) {
|
|
63
|
+
let url = `${this.baseUrl}/api${path}`;
|
|
64
|
+
if (opts.query) {
|
|
65
|
+
const search = new URLSearchParams();
|
|
66
|
+
for (const [k, v] of Object.entries(opts.query)) {
|
|
67
|
+
if (v === undefined) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
search.append(k, String(v));
|
|
71
|
+
}
|
|
72
|
+
const qs = search.toString();
|
|
73
|
+
if (qs) {
|
|
74
|
+
url += `?${qs}`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const headers = {
|
|
78
|
+
Accept: 'application/json',
|
|
79
|
+
'User-Agent': `${NAME}/${VERSION}`,
|
|
80
|
+
};
|
|
81
|
+
if (opts.auth) {
|
|
82
|
+
headers[this.#authHeaderName()] = this.#authHeaderValue();
|
|
83
|
+
}
|
|
84
|
+
const init = { method, headers };
|
|
85
|
+
if (method !== 'GET' && method !== 'HEAD') {
|
|
86
|
+
init.body = JSON.stringify(opts.body ?? {});
|
|
87
|
+
headers['Content-Type'] = 'application/json';
|
|
88
|
+
}
|
|
89
|
+
const res = await this.#fetch(url, init);
|
|
90
|
+
const json = await this.#parseBody(res);
|
|
91
|
+
// Old API convention: { message, ...data, isSuccess }. Treat a non-2xx
|
|
92
|
+
// OR an explicit isSuccess:false as an error so a 200-with-failure
|
|
93
|
+
// body never slips through as success.
|
|
94
|
+
const failed = !res.ok ||
|
|
95
|
+
(json !== undefined &&
|
|
96
|
+
typeof json === 'object' &&
|
|
97
|
+
json.isSuccess === false);
|
|
98
|
+
if (failed) {
|
|
99
|
+
throw this.#asBoom(res, json);
|
|
100
|
+
}
|
|
101
|
+
return json;
|
|
102
|
+
}
|
|
103
|
+
#authHeaderName() {
|
|
104
|
+
const id = this.#requireIdentity();
|
|
105
|
+
return id.kind === 'bearer' ? 'Authorization' : 'x-eth-uid';
|
|
106
|
+
}
|
|
107
|
+
#authHeaderValue() {
|
|
108
|
+
const id = this.#requireIdentity();
|
|
109
|
+
return id.kind === 'bearer' ? `Bearer ${id.token}` : id.uid;
|
|
110
|
+
}
|
|
111
|
+
#requireIdentity() {
|
|
112
|
+
if (!this.#identity) {
|
|
113
|
+
throw new Boom('Not authenticated against old devtools. Call reclaim_authenticate ' +
|
|
114
|
+
'with a dashboard token (or eth uid) first.', { statusCode: 401, data: { status: 401, detail: 'no identity set' } });
|
|
115
|
+
}
|
|
116
|
+
return this.#identity;
|
|
117
|
+
}
|
|
118
|
+
async #parseBody(res) {
|
|
119
|
+
if (res.status === 204 || res.headers.get('content-length') === '0') {
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
return await res.json();
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
#asBoom(res, json) {
|
|
130
|
+
const message = (json && typeof json === 'object' &&
|
|
131
|
+
typeof json.message === 'string'
|
|
132
|
+
? json.message
|
|
133
|
+
: undefined) || res.statusText || 'Old devtools request failed';
|
|
134
|
+
// Shape `data` like a ProblemDetails so remapErrorAsResponse can render
|
|
135
|
+
// `${message}: ${data.detail}` and expose the raw body as structured.
|
|
136
|
+
return new Boom(message, {
|
|
137
|
+
statusCode: res.ok ? 502 : res.status,
|
|
138
|
+
data: {
|
|
139
|
+
status: res.status,
|
|
140
|
+
title: message,
|
|
141
|
+
detail: message,
|
|
142
|
+
body: json,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `USE_OLD_DEVTOOLS` — single source of truth for which backend the MCP
|
|
3
|
+
* server targets.
|
|
4
|
+
*
|
|
5
|
+
* Defaults to OLD devtools (the system still in production) while the new
|
|
6
|
+
* "builder" backend is under development. Builder mode must be opted into
|
|
7
|
+
* EXPLICITLY by setting `USE_OLD_DEVTOOLS=false`. Anything else — unset,
|
|
8
|
+
* empty, `true`, `1`, a typo — keeps old mode on, so a forgotten env var
|
|
9
|
+
* can never silently route a developer at the half-built builder backend.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useOldDevtools(): boolean;
|
package/lib/old/flag.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `USE_OLD_DEVTOOLS` — single source of truth for which backend the MCP
|
|
3
|
+
* server targets.
|
|
4
|
+
*
|
|
5
|
+
* Defaults to OLD devtools (the system still in production) while the new
|
|
6
|
+
* "builder" backend is under development. Builder mode must be opted into
|
|
7
|
+
* EXPLICITLY by setting `USE_OLD_DEVTOOLS=false`. Anything else — unset,
|
|
8
|
+
* empty, `true`, `1`, a typo — keeps old mode on, so a forgotten env var
|
|
9
|
+
* can never silently route a developer at the half-built builder backend.
|
|
10
|
+
*/
|
|
11
|
+
export function useOldDevtools() {
|
|
12
|
+
return (process.env.USE_OLD_DEVTOOLS ?? '').trim().toLowerCase() !== 'false';
|
|
13
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OldIdentity } from './client.ts';
|
|
2
|
+
/** Normalize an eth uid input to `eth:<lowercase-address>`. Accepts a bare
|
|
3
|
+
* `0x…` address or an already-prefixed `eth:0x…`. */
|
|
4
|
+
export declare function toEthUid(addressOrUid: string): OldIdentity;
|
|
5
|
+
/**
|
|
6
|
+
* Resolve a caller identity from the environment, in priority order:
|
|
7
|
+
* 1. `RECLAIM_OLD_API_TOKEN` — a dashboard Firebase bearer token.
|
|
8
|
+
* 2. `RECLAIM_OLD_ETH_UID` — an `eth:<addr>` (or bare `0x…`) uid.
|
|
9
|
+
* 3. `RECLAIM_PRIVATE_KEY` — derive the eth uid from the local app key.
|
|
10
|
+
* Returns undefined when none is set.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveIdentityFromEnv(): OldIdentity | undefined;
|
|
13
|
+
/** Read the identity persisted by a prior `reclaim_authenticate` call. */
|
|
14
|
+
export declare function loadPersistedIdentity(): OldIdentity | undefined;
|
|
15
|
+
/** Persist the identity so it survives an MCP-server restart. */
|
|
16
|
+
export declare function persistIdentity(identity: OldIdentity): void;
|
|
17
|
+
/**
|
|
18
|
+
* The identity the client should start with: an explicit env var wins
|
|
19
|
+
* (useful for headless / CI), otherwise fall back to whatever the dev
|
|
20
|
+
* authenticated with last time.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveInitialIdentity(): OldIdentity | undefined;
|