@jaw.id/cli 0.1.16 → 0.1.17
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/dist/base-command.js.map +1 -1
- package/dist/commands/config/set.js.map +1 -1
- package/dist/commands/config/show.js +22 -1
- package/dist/commands/config/show.js.map +1 -1
- package/dist/commands/config/write.js.map +1 -1
- package/dist/commands/disconnect.js +14 -14
- package/dist/commands/disconnect.js.map +1 -1
- package/dist/commands/mcp/index.js +308 -84
- package/dist/commands/mcp/index.js.map +1 -1
- package/dist/commands/rpc/call.js +4 -4
- package/dist/commands/rpc/call.js.map +1 -1
- package/dist/commands/session/revoke.js +4 -4
- package/dist/commands/session/revoke.js.map +1 -1
- package/dist/commands/session/setup.js +4 -4
- package/dist/commands/session/setup.js.map +1 -1
- package/dist/commands/session/status.js.map +1 -1
- package/dist/commands/version.js.map +1 -1
- package/dist/index.js +17 -15
- package/dist/index.js.map +1 -1
- package/dist/lib/bridge-singleton.js +17 -17
- package/dist/lib/bridge-singleton.js.map +1 -1
- package/dist/lib/config.js +22 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/keystore.js.map +1 -1
- package/dist/lib/session-bridge.js.map +1 -1
- package/dist/lib/session-config.js.map +1 -1
- package/dist/mcp/handlers/config.js +57 -17
- package/dist/mcp/handlers/config.js.map +1 -1
- package/dist/mcp/handlers/daemon.js +60 -39
- package/dist/mcp/handlers/daemon.js.map +1 -1
- package/dist/mcp/handlers/resources.js +4 -3
- package/dist/mcp/handlers/resources.js.map +1 -1
- package/dist/mcp/handlers/rpc.js +195 -26
- package/dist/mcp/handlers/rpc.js.map +1 -1
- package/dist/mcp/server.js +307 -83
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.js +5 -2
- package/dist/mcp/tools.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +4 -1
|
@@ -2,8 +2,9 @@ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
|
|
3
3
|
// src/mcp/handlers/resources.ts
|
|
4
4
|
var DOCS_BASE = "https://docs.jaw.id/api-reference";
|
|
5
|
+
var FETCH_TIMEOUT_MS = 15e3;
|
|
5
6
|
async function fetchDocs(url) {
|
|
6
|
-
const res = await fetch(url);
|
|
7
|
+
const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });
|
|
7
8
|
if (!res.ok) {
|
|
8
9
|
throw new Error(`Failed to fetch docs: ${res.status} ${res.statusText}`);
|
|
9
10
|
}
|
|
@@ -11,7 +12,7 @@ async function fetchDocs(url) {
|
|
|
11
12
|
return html.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<[^>]+>/g, " ").replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/\s{2,}/g, " ").trim();
|
|
12
13
|
}
|
|
13
14
|
function registerResources(server) {
|
|
14
|
-
server.
|
|
15
|
+
server.registerResource(
|
|
15
16
|
"api-reference",
|
|
16
17
|
"jaw://api-reference",
|
|
17
18
|
{
|
|
@@ -28,7 +29,7 @@ function registerResources(server) {
|
|
|
28
29
|
]
|
|
29
30
|
})
|
|
30
31
|
);
|
|
31
|
-
server.
|
|
32
|
+
server.registerResource(
|
|
32
33
|
"api-reference-method",
|
|
33
34
|
new ResourceTemplate("jaw://api-reference/{method}", { list: void 0 }),
|
|
34
35
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/mcp/handlers/resources.ts"],"names":[],"mappings":";;;AAEA,IAAM,SAAA,GAAY,mCAAA;AAElB,eAAe,UAAU,GAAA,EAA8B;AACrD,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,
|
|
1
|
+
{"version":3,"sources":["../../../src/mcp/handlers/resources.ts"],"names":[],"mappings":";;;AAEA,IAAM,SAAA,GAAY,mCAAA;AAElB,IAAM,gBAAA,GAAmB,IAAA;AAEzB,eAAe,UAAU,GAAA,EAA8B;AACrD,EAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK,EAAE,QAAQ,WAAA,CAAY,OAAA,CAAQ,gBAAgB,CAAA,EAAG,CAAA;AAC9E,EAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,IAAA,MAAM,IAAI,MAAM,CAAA,sBAAA,EAAyB,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,GAAA,CAAI,UAAU,CAAA,CAAE,CAAA;AAAA,EACzE;AACA,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAG5B,EAAA,OAAO,KACJ,OAAA,CAAQ,mCAAA,EAAqC,EAAE,CAAA,CAC/C,OAAA,CAAQ,mCAAmC,EAAE,CAAA,CAC7C,OAAA,CAAQ,UAAA,EAAY,GAAG,CAAA,CACvB,OAAA,CAAQ,WAAW,GAAG,CAAA,CACtB,QAAQ,QAAA,EAAU,GAAG,CAAA,CACrB,OAAA,CAAQ,SAAS,GAAG,CAAA,CACpB,QAAQ,OAAA,EAAS,GAAG,EACpB,OAAA,CAAQ,SAAA,EAAW,GAAG,CAAA,CACtB,OAAA,CAAQ,UAAU,GAAG,CAAA,CACrB,QAAQ,SAAA,EAAW,GAAG,EACtB,IAAA,EAAK;AACV;AAEO,SAAS,kBAAkB,MAAA,EAAyB;AAEzD,EAAA,MAAA,CAAO,gBAAA;AAAA,IACL,eAAA;AAAA,IACA,qBAAA;AAAA,IACA;AAAA,MACE,WAAA,EACE,yJAAA;AAAA,MAEF,QAAA,EAAU;AAAA,KACZ;AAAA,IACA,aAAa;AAAA,MACX,QAAA,EAAU;AAAA,QACR;AAAA,UACE,GAAA,EAAK,qBAAA;AAAA,UACL,QAAA,EAAU,YAAA;AAAA,UACV,IAAA,EAAM,MAAM,SAAA,CAAU,SAAS;AAAA;AACjC;AACF,KACF;AAAA,GACF;AAGA,EAAA,MAAA,CAAO,gBAAA;AAAA,IACL,sBAAA;AAAA,IACA,IAAI,gBAAA,CAAiB,8BAAA,EAAgC,EAAE,IAAA,EAAM,QAAW,CAAA;AAAA,IACxE;AAAA,MACE,WAAA,EACE,2MAAA;AAAA,MAGF,QAAA,EAAU;AAAA,KACZ;AAAA,IACA,OAAO,KAAK,SAAA,KAAc;AACxB,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,CAAU,MAAM,CAAA;AAGtC,MAAA,IAAI,CAAC,UAAA,CAAW,IAAA,CAAK,MAAM,CAAA,EAAG;AAC5B,QAAA,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,MAAM,CAAA,gDAAA,CAAkD,CAAA;AAAA,MACnG;AAEA,MAAA,OAAO;AAAA,QACL,QAAA,EAAU;AAAA,UACR;AAAA,YACE,KAAK,GAAA,CAAI,IAAA;AAAA,YACT,QAAA,EAAU,YAAA;AAAA,YACV,MAAM,MAAM,SAAA,CAAU,GAAG,SAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAE;AAAA;AAChD;AACF,OACF;AAAA,IACF;AAAA,GACF;AACF","file":"resources.js","sourcesContent":["import { type McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nconst DOCS_BASE = 'https://docs.jaw.id/api-reference';\n\nconst FETCH_TIMEOUT_MS = 15_000;\n\nasync function fetchDocs(url: string): Promise<string> {\n const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS) });\n if (!res.ok) {\n throw new Error(`Failed to fetch docs: ${res.status} ${res.statusText}`);\n }\n const html = await res.text();\n // Strip HTML tags for a readable text representation.\n // MCP resources are consumed by LLMs, so plain text is ideal.\n return html\n .replace(/<script[^>]*>[\\s\\S]*?<\\/script>/gi, '')\n .replace(/<style[^>]*>[\\s\\S]*?<\\/style>/gi, '')\n .replace(/<[^>]+>/g, ' ')\n .replace(/ /g, ' ')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/"/g, '\"')\n .replace(/'/g, \"'\")\n .replace(/\\s{2,}/g, ' ')\n .trim();\n}\n\nexport function registerResources(server: McpServer): void {\n // Overview of all RPC methods\n server.registerResource(\n 'api-reference',\n 'jaw://api-reference',\n {\n description:\n 'JAW.id API reference — lists all supported RPC methods with descriptions. ' +\n 'Read this before using the jaw_rpc tool to understand available methods.',\n mimeType: 'text/plain',\n },\n async () => ({\n contents: [\n {\n uri: 'jaw://api-reference',\n mimeType: 'text/plain',\n text: await fetchDocs(DOCS_BASE),\n },\n ],\n })\n );\n\n // Per-method documentation with parameter details\n server.registerResource(\n 'api-reference-method',\n new ResourceTemplate('jaw://api-reference/{method}', { list: undefined }),\n {\n description:\n 'Detailed documentation for a specific RPC method including parameters, ' +\n 'request/response format, and examples. Use the method name from the ' +\n 'api-reference overview (e.g. wallet_sendCalls, personal_sign).',\n mimeType: 'text/plain',\n },\n async (uri, variables) => {\n const method = String(variables.method);\n\n // Validate method name to prevent path traversal (e.g. ../../admin)\n if (!/^[\\w_]+$/.test(method)) {\n throw new Error(`Invalid method name: \"${method}\". Expected an RPC method like wallet_sendCalls.`);\n }\n\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: 'text/plain',\n text: await fetchDocs(`${DOCS_BASE}/${method}`),\n },\n ],\n };\n }\n );\n}\n"]}
|
package/dist/mcp/handlers/rpc.js
CHANGED
|
@@ -13,10 +13,13 @@ var rpcMethodSchema = {
|
|
|
13
13
|
params: z.any().optional().describe(
|
|
14
14
|
"Method parameters \u2014 structure varies by method. Read the jaw://api-reference/{method} resource for the expected format."
|
|
15
15
|
),
|
|
16
|
-
chainId: z.number().optional().describe("Target chain ID (overrides default). E.g., 1 for Ethereum, 8453 for Base, 84532 for Base Sepolia")
|
|
16
|
+
chainId: z.number().optional().describe("Target chain ID (overrides default). E.g., 1 for Ethereum, 8453 for Base, 84532 for Base Sepolia"),
|
|
17
|
+
session: z.boolean().optional().describe(
|
|
18
|
+
"Sign with the local session key instead of opening the browser (requires `jaw session setup`; check jaw_session_status first). Supported methods only: eth_requestAccounts, eth_accounts, wallet_sendCalls, wallet_getCallsStatus, personal_sign, eth_signTypedData_v4. Defaults to the JAW_SESSION env var."
|
|
19
|
+
)
|
|
17
20
|
};
|
|
18
21
|
({
|
|
19
|
-
key: z.enum(["apiKey", "defaultChain", "keysUrl", "ens", "relayUrl"]).describe("Config key"),
|
|
22
|
+
key: z.enum(["apiKey", "defaultChain", "keysUrl", "ens", "relayUrl", "sessionExpiry"]).describe("Config key"),
|
|
20
23
|
value: z.string().describe("Config value")
|
|
21
24
|
});
|
|
22
25
|
|
|
@@ -485,10 +488,6 @@ function safeParse(data) {
|
|
|
485
488
|
return null;
|
|
486
489
|
}
|
|
487
490
|
}
|
|
488
|
-
|
|
489
|
-
// src/lib/bridge-singleton.ts
|
|
490
|
-
var DEFAULT_KEYS_URL = "https://keys.jaw.id";
|
|
491
|
-
var DEFAULT_RELAY_URL = "wss://relay.jaw.id";
|
|
492
491
|
function loadRelaySession() {
|
|
493
492
|
try {
|
|
494
493
|
if (!fs.existsSync(PATHS.relay)) return null;
|
|
@@ -515,6 +514,10 @@ function deleteRelaySession() {
|
|
|
515
514
|
} catch {
|
|
516
515
|
}
|
|
517
516
|
}
|
|
517
|
+
|
|
518
|
+
// src/lib/bridge-singleton.ts
|
|
519
|
+
var DEFAULT_KEYS_URL = "https://keys.jaw.id";
|
|
520
|
+
var DEFAULT_RELAY_URL = "wss://relay.jaw.id";
|
|
518
521
|
async function getBridge(options) {
|
|
519
522
|
const config = loadConfig();
|
|
520
523
|
const keysUrl = options.keysUrl ?? config.keysUrl ?? DEFAULT_KEYS_URL;
|
|
@@ -592,39 +595,205 @@ function buildBridgeUrl(keysUrl, session, relayUrl, cliPublicKeyHex) {
|
|
|
592
595
|
url.hash = `pk=${cliPublicKeyHex}`;
|
|
593
596
|
return url.toString();
|
|
594
597
|
}
|
|
598
|
+
function loadSessionKey() {
|
|
599
|
+
if (!fs.existsSync(PATHS.keystore)) {
|
|
600
|
+
throw new Error("No session configured. Run `jaw session setup` first.");
|
|
601
|
+
}
|
|
602
|
+
const contents = fs.readFileSync(PATHS.keystore, "utf-8");
|
|
603
|
+
let parsed;
|
|
604
|
+
try {
|
|
605
|
+
parsed = JSON.parse(contents);
|
|
606
|
+
} catch {
|
|
607
|
+
throw new Error(`Keystore at ${PATHS.keystore} is corrupted. Run \`jaw session setup\` to recreate it.`);
|
|
608
|
+
}
|
|
609
|
+
return parsed.privateKey;
|
|
610
|
+
}
|
|
611
|
+
function loadSessionConfig() {
|
|
612
|
+
if (!fs.existsSync(PATHS.sessionConfig)) {
|
|
613
|
+
throw new Error("No session configured. Run `jaw session setup` first.");
|
|
614
|
+
}
|
|
615
|
+
const raw = fs.readFileSync(PATHS.sessionConfig, "utf-8");
|
|
616
|
+
try {
|
|
617
|
+
return JSON.parse(raw);
|
|
618
|
+
} catch {
|
|
619
|
+
throw new Error(`Session config at ${PATHS.sessionConfig} is corrupted. Run \`jaw session setup\` to recreate it.`);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/lib/session-bridge.ts
|
|
624
|
+
var SessionBridge = class {
|
|
625
|
+
options;
|
|
626
|
+
session = null;
|
|
627
|
+
constructor(options) {
|
|
628
|
+
this.options = { ...options };
|
|
629
|
+
if (!this.options.paymasterUrl) {
|
|
630
|
+
const config = loadConfig();
|
|
631
|
+
const pm = config.paymasters?.[this.options.chainId];
|
|
632
|
+
if (pm) {
|
|
633
|
+
this.options.paymasterUrl = pm.url;
|
|
634
|
+
this.options.paymasterContext = pm.context;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async getSession() {
|
|
639
|
+
if (this.session) {
|
|
640
|
+
this.checkExpiry(this.session.config);
|
|
641
|
+
return this.session;
|
|
642
|
+
}
|
|
643
|
+
const config = loadSessionConfig();
|
|
644
|
+
this.checkExpiry(config);
|
|
645
|
+
if (config.chainId !== this.options.chainId) {
|
|
646
|
+
throw new Error(
|
|
647
|
+
`Session was created for chain ${config.chainId}, but --chain ${this.options.chainId} was requested. Run \`jaw session setup --chain ${this.options.chainId}\` to create a session for that chain.`
|
|
648
|
+
);
|
|
649
|
+
}
|
|
650
|
+
let privateKeyHex = loadSessionKey();
|
|
651
|
+
const { privateKeyToAccount } = await import('viem/accounts');
|
|
652
|
+
const localAccount = privateKeyToAccount(privateKeyHex);
|
|
653
|
+
privateKeyHex = null;
|
|
654
|
+
const { Account } = await import('@jaw.id/core');
|
|
655
|
+
const account = await Account.fromLocalAccount(
|
|
656
|
+
{
|
|
657
|
+
chainId: this.options.chainId,
|
|
658
|
+
apiKey: this.options.apiKey,
|
|
659
|
+
paymasterUrl: this.options.paymasterUrl,
|
|
660
|
+
paymasterContext: this.options.paymasterContext
|
|
661
|
+
},
|
|
662
|
+
localAccount
|
|
663
|
+
);
|
|
664
|
+
this.session = { account, config };
|
|
665
|
+
return this.session;
|
|
666
|
+
}
|
|
667
|
+
checkExpiry(config) {
|
|
668
|
+
if (config.expiry <= Date.now() / 1e3) {
|
|
669
|
+
const expiryDate = new Date(config.expiry * 1e3).toISOString();
|
|
670
|
+
throw new Error(`Session expired on ${expiryDate}. Run \`jaw session setup\` to create a new session.`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
async request(method, params) {
|
|
674
|
+
const { account, config } = await this.getSession();
|
|
675
|
+
switch (method) {
|
|
676
|
+
case "eth_requestAccounts":
|
|
677
|
+
case "eth_accounts":
|
|
678
|
+
return [config.sessionAddress];
|
|
679
|
+
case "wallet_sendCalls": {
|
|
680
|
+
const payload = Array.isArray(params) ? params[0] : params;
|
|
681
|
+
const { calls } = payload;
|
|
682
|
+
return account.sendCalls(calls, {
|
|
683
|
+
permissionId: config.permissionId
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
case "wallet_getCallsStatus": {
|
|
687
|
+
const batchId = Array.isArray(params) ? params[0] : params;
|
|
688
|
+
return account.getCallStatus(batchId);
|
|
689
|
+
}
|
|
690
|
+
case "personal_sign": {
|
|
691
|
+
const message = Array.isArray(params) ? params[0] : params;
|
|
692
|
+
return account.signMessage(message);
|
|
693
|
+
}
|
|
694
|
+
case "eth_signTypedData_v4": {
|
|
695
|
+
const asArray = Array.isArray(params) ? params : [params];
|
|
696
|
+
const raw = asArray.length > 1 ? asArray[1] : asArray[0];
|
|
697
|
+
const typedData = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
698
|
+
return account.signTypedData(typedData);
|
|
699
|
+
}
|
|
700
|
+
case "wallet_grantPermissions":
|
|
701
|
+
throw new Error("Requires browser \u2014 run `jaw session setup`.");
|
|
702
|
+
case "wallet_revokePermissions":
|
|
703
|
+
throw new Error("Requires browser \u2014 run `jaw session revoke`.");
|
|
704
|
+
default:
|
|
705
|
+
throw new Error(`Method ${method} is not supported in auto mode.`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
close() {
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// src/lib/rpc-classifier.ts
|
|
713
|
+
var SESSION_SUPPORTED_METHODS = /* @__PURE__ */ new Set([
|
|
714
|
+
"eth_requestAccounts",
|
|
715
|
+
"eth_accounts",
|
|
716
|
+
"wallet_sendCalls",
|
|
717
|
+
"wallet_getCallsStatus",
|
|
718
|
+
"personal_sign",
|
|
719
|
+
"eth_signTypedData_v4"
|
|
720
|
+
]);
|
|
721
|
+
function supportsSessionMode(method) {
|
|
722
|
+
return SESSION_SUPPORTED_METHODS.has(method);
|
|
723
|
+
}
|
|
595
724
|
|
|
596
725
|
// src/mcp/handlers/rpc.ts
|
|
597
|
-
function resolveApiKey() {
|
|
598
|
-
const apiKey = process.env["JAW_API_KEY"] ??
|
|
726
|
+
function resolveApiKey(config) {
|
|
727
|
+
const apiKey = process.env["JAW_API_KEY"] ?? config.apiKey;
|
|
599
728
|
if (!apiKey) {
|
|
600
729
|
throw new Error("API key required. Set JAW_API_KEY env var or run: jaw config set apiKey <key>");
|
|
601
730
|
}
|
|
602
731
|
return apiKey;
|
|
603
732
|
}
|
|
733
|
+
function resolveChainId(paramChainId, config) {
|
|
734
|
+
if (paramChainId) return paramChainId;
|
|
735
|
+
const envChainId = parseInt(process.env["JAW_CHAIN_ID"] ?? "", 10);
|
|
736
|
+
if (Number.isInteger(envChainId) && envChainId > 0) return envChainId;
|
|
737
|
+
return config.defaultChain ?? 1;
|
|
738
|
+
}
|
|
739
|
+
function envSessionEnabled() {
|
|
740
|
+
const value = process.env["JAW_SESSION"]?.toLowerCase();
|
|
741
|
+
return value === "1" || value === "true";
|
|
742
|
+
}
|
|
743
|
+
var SIGN_RATE_WINDOW_MS = 6e4;
|
|
744
|
+
var MAX_SIGNS_PER_WINDOW = 5;
|
|
745
|
+
var SESSION_SIGNING_METHODS = ["wallet_sendCalls", "personal_sign", "eth_signTypedData_v4"];
|
|
604
746
|
function registerRpcTool(server) {
|
|
605
|
-
|
|
747
|
+
const recentSigns = [];
|
|
748
|
+
function assertUnderSignLimit() {
|
|
749
|
+
const now = Date.now();
|
|
750
|
+
while (recentSigns.length && now - recentSigns[0] > SIGN_RATE_WINDOW_MS) recentSigns.shift();
|
|
751
|
+
if (recentSigns.length >= MAX_SIGNS_PER_WINDOW) {
|
|
752
|
+
throw new Error("Autonomous signing rate limit reached, retry shortly or call again with session: false.");
|
|
753
|
+
}
|
|
754
|
+
recentSigns.push(now);
|
|
755
|
+
}
|
|
756
|
+
server.registerTool(
|
|
606
757
|
"jaw_rpc",
|
|
607
|
-
|
|
608
|
-
|
|
758
|
+
{
|
|
759
|
+
description: "Execute any JAW.id wallet RPC method. Supports transactions, signing, permissions, and queries. By default, methods that require signing open the browser for passkey authentication. Pass session: true to sign autonomously with the local session key instead (requires a session created via `jaw session setup` \u2014 check jaw_session_status). IMPORTANT: Read the jaw://api-reference resource for the full list of methods, and jaw://api-reference/{method} for detailed parameter formats and examples.",
|
|
760
|
+
inputSchema: rpcMethodSchema
|
|
761
|
+
},
|
|
762
|
+
// @ts-expect-error — MCP SDK deep type inference with z.any() in schema
|
|
609
763
|
async (params) => {
|
|
610
|
-
const config = loadConfig();
|
|
611
|
-
const apiKey = resolveApiKey();
|
|
612
|
-
const chainId = params.chainId ?? config.defaultChain ?? 1;
|
|
613
|
-
const pm = config.paymasters?.[chainId];
|
|
614
|
-
const bridge = await getBridge({
|
|
615
|
-
keysUrl: config.keysUrl,
|
|
616
|
-
apiKey,
|
|
617
|
-
chainId,
|
|
618
|
-
ens: config.ens,
|
|
619
|
-
paymasterUrl: pm?.url
|
|
620
|
-
});
|
|
621
764
|
try {
|
|
622
|
-
const
|
|
623
|
-
|
|
765
|
+
const config = loadConfig();
|
|
766
|
+
const apiKey = resolveApiKey(config);
|
|
767
|
+
const chainId = resolveChainId(params.chainId, config);
|
|
768
|
+
const useSession = params.session ?? envSessionEnabled();
|
|
769
|
+
let bridge;
|
|
770
|
+
if (useSession) {
|
|
771
|
+
if (!supportsSessionMode(params.method)) {
|
|
772
|
+
throw new Error(
|
|
773
|
+
`Method ${params.method} is not supported in session mode. Call again with session: false to route through the browser bridge.`
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
if (SESSION_SIGNING_METHODS.includes(params.method)) {
|
|
777
|
+
assertUnderSignLimit();
|
|
778
|
+
}
|
|
779
|
+
bridge = new SessionBridge({ apiKey, chainId });
|
|
780
|
+
} else {
|
|
781
|
+
bridge = await getBridge({
|
|
782
|
+
keysUrl: config.keysUrl,
|
|
783
|
+
apiKey,
|
|
784
|
+
chainId,
|
|
785
|
+
ens: config.ens,
|
|
786
|
+
paymasterUrl: config.paymasters?.[chainId]?.url
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
try {
|
|
790
|
+
const result = await bridge.request(params.method, params.params);
|
|
791
|
+
return mcpResult(result);
|
|
792
|
+
} finally {
|
|
793
|
+
bridge.close();
|
|
794
|
+
}
|
|
624
795
|
} catch (err) {
|
|
625
796
|
return mcpError(err);
|
|
626
|
-
} finally {
|
|
627
|
-
bridge.close();
|
|
628
797
|
}
|
|
629
798
|
}
|
|
630
799
|
);
|