@elisym/mcp 0.3.2 → 0.3.3

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 CHANGED
@@ -106,15 +106,15 @@ The bootstrap step is unchanged - the wizard collects the passphrase interactive
106
106
 
107
107
  ## Environment Variables
108
108
 
109
- | Variable | Description |
110
- | --------------------------- | ----------------------------------------------------------------------------- |
111
- | `ELISYM_AGENT` | Load agent from `~/.elisym/agents/<name>/` |
112
- | `ELISYM_NOSTR_SECRET` | Nostr secret key (hex or nsec) for ephemeral mode |
113
- | `ELISYM_AGENT_NAME` | Agent display name (default: mcp-agent) |
114
- | `ELISYM_NETWORK` | Solana network for ephemeral mode: `devnet` or `mainnet` (default: devnet) |
115
- | `ELISYM_PASSPHRASE` | Passphrase for encrypted agent configs (optional) |
116
- | `ELISYM_ALLOW_WITHDRAWAL` | Set to `1` to override per-agent `security.withdrawals_enabled` flag (CI use) |
117
- | `ELISYM_ALLOW_AGENT_SWITCH` | Set to `1` to override per-agent `security.agent_switch_enabled` flag |
109
+ | Variable | Description |
110
+ | --------------------------- | ------------------------------------------------------------------------------- |
111
+ | `ELISYM_AGENT` | Load agent from `~/.elisym/agents/<name>/` |
112
+ | `ELISYM_NOSTR_SECRET` | Nostr secret key (hex or nsec) for ephemeral mode |
113
+ | `ELISYM_AGENT_NAME` | Agent display name (default: mcp-agent) |
114
+ | `ELISYM_NETWORK` | Solana network for ephemeral mode. Only `devnet` is supported (default: devnet) |
115
+ | `ELISYM_PASSPHRASE` | Passphrase for encrypted agent configs (optional) |
116
+ | `ELISYM_ALLOW_WITHDRAWAL` | Set to `1` to override per-agent `security.withdrawals_enabled` flag (CI use) |
117
+ | `ELISYM_ALLOW_AGENT_SWITCH` | Set to `1` to override per-agent `security.agent_switch_enabled` flag |
118
118
 
119
119
  ## Usage Examples
120
120
 
package/dist/index.js CHANGED
@@ -42,8 +42,16 @@ function agentsDir() {
42
42
  function agentConfigPath(name) {
43
43
  return join(agentsDir(), name, "config.json");
44
44
  }
45
- function coerceNetwork(raw) {
46
- return raw === "mainnet" ? "mainnet" : "devnet";
45
+ function coerceNetwork(raw, agentName) {
46
+ if (raw === void 0 || raw === "devnet") {
47
+ return "devnet";
48
+ }
49
+ if (raw === "mainnet") {
50
+ throw new Error(
51
+ `Agent "${agentName}" is configured for mainnet, which is not supported until the elisym-config program is deployed there. Re-create the agent with --network devnet: rm -rf ~/.elisym/agents/${agentName} && elisym-mcp init ${agentName} --network devnet`
52
+ );
53
+ }
54
+ throw new Error(`Agent "${agentName}" has unsupported network "${raw}". Expected "devnet".`);
47
55
  }
48
56
  async function loadAgentConfig(name, passphrase) {
49
57
  validateAgentName(name);
@@ -65,7 +73,7 @@ async function loadAgentConfig(name, passphrase) {
65
73
  `Failed to load agent "${name}": ${msg}. If this config was created by an earlier version, delete ~/.elisym/agents/${name} and run "elisym-mcp init ${name}" again.`
66
74
  );
67
75
  }
68
- const network = coerceNetwork(config.wallet?.network ?? config.payments?.[0]?.network);
76
+ const network = coerceNetwork(config.wallet?.network ?? config.payments?.[0]?.network, name);
69
77
  return {
70
78
  nostrSecretKey: config.identity.secret_key,
71
79
  solanaSecretKey: config.wallet?.secret_key,
@@ -160,18 +168,17 @@ async function listAgentNames() {
160
168
  return [];
161
169
  }
162
170
  }
163
- function rpcUrlFor(network) {
164
- return network === "mainnet" ? "https://api.mainnet-beta.solana.com" : "https://api.devnet.solana.com";
171
+ function rpcUrlFor(_network) {
172
+ return "https://api.devnet.solana.com";
165
173
  }
166
- async function fetchProtocolConfig(network) {
167
- const cluster = network === "mainnet" ? "mainnet" : "devnet";
168
- const programId = getProtocolProgramId(cluster);
169
- const rpc = createSolanaRpc(rpcUrlFor(cluster));
174
+ async function fetchProtocolConfig(_network) {
175
+ const programId = getProtocolProgramId("devnet");
176
+ const rpc = createSolanaRpc(rpcUrlFor());
170
177
  const config = await getProtocolConfig(rpc, programId, { forceRefresh: true });
171
178
  return { feeBps: config.feeBps, treasury: config.treasury };
172
179
  }
173
- function explorerClusterFor(network) {
174
- return network === "mainnet" ? "mainnet-beta" : "devnet";
180
+ function explorerClusterFor(_network) {
181
+ return "devnet";
175
182
  }
176
183
  var RateLimiter = class {
177
184
  constructor(maxCalls, windowSecs) {
@@ -632,7 +639,10 @@ var CreateAgentSchema = z.object({
632
639
  // customer-mode in 0.1.x and never publishes a NIP-89 capability card,
633
640
  // so an advertised capability list would be misleading. Provider-mode
634
641
  // (0.2.0) will reintroduce this field.
635
- network: z.enum(["devnet", "mainnet"]).default("devnet"),
642
+ // Only devnet is supported until the elisym-config program ships on mainnet.
643
+ // Mainnet was previously advertised here but every paid flow then crashed at
644
+ // payment time - rejecting at schema level surfaces the error up front.
645
+ network: z.enum(["devnet"]).default("devnet"),
636
646
  passphrase: z.string().optional().describe("Optional passphrase; if set, secret keys are encrypted at rest."),
637
647
  activate: z.boolean().default(true)
638
648
  });
@@ -1193,18 +1203,34 @@ function makePaymentFeedbackHandler(opts) {
1193
1203
  );
1194
1204
  return;
1195
1205
  }
1196
- if (opts.maxPriceLamports === void 0 && amount !== void 0 && amount > 0) {
1206
+ let parsedRequest;
1207
+ try {
1208
+ parsedRequest = JSON.parse(paymentRequest);
1209
+ } catch {
1210
+ opts.rejectPayment(new Error("Provider sent a malformed payment_request (not valid JSON)."));
1211
+ return;
1212
+ }
1213
+ const signedAmount = typeof parsedRequest.amount === "number" && Number.isInteger(parsedRequest.amount) && parsedRequest.amount > 0 ? parsedRequest.amount : void 0;
1214
+ if (amount !== void 0 && amount > 0 && signedAmount !== void 0 && amount !== signedAmount) {
1197
1215
  opts.rejectPayment(
1198
1216
  new Error(
1199
- `Payment of ${formatSol(BigInt(amount))} required but no max_price_lamports set. Retry with max_price_lamports to approve.`
1217
+ `Payment request mismatch: feedback tag amount=${amount} differs from signed amount=${signedAmount}. Refusing to proceed.`
1200
1218
  )
1201
1219
  );
1202
1220
  return;
1203
1221
  }
1204
- if (opts.maxPriceLamports !== void 0 && amount !== void 0 && amount > opts.maxPriceLamports) {
1222
+ if (opts.maxPriceLamports === void 0 && signedAmount !== void 0) {
1205
1223
  opts.rejectPayment(
1206
1224
  new Error(
1207
- `Price ${formatSol(BigInt(amount))} exceeds max ${formatSol(BigInt(opts.maxPriceLamports))}`
1225
+ `Payment of ${formatSol(BigInt(signedAmount))} required but no max_price_lamports set. Retry with max_price_lamports to approve.`
1226
+ )
1227
+ );
1228
+ return;
1229
+ }
1230
+ if (opts.maxPriceLamports !== void 0 && signedAmount !== void 0 && signedAmount > opts.maxPriceLamports) {
1231
+ opts.rejectPayment(
1232
+ new Error(
1233
+ `Price ${formatSol(BigInt(signedAmount))} exceeds max ${formatSol(BigInt(opts.maxPriceLamports))}`
1208
1234
  )
1209
1235
  );
1210
1236
  return;
@@ -1212,7 +1238,7 @@ function makePaymentFeedbackHandler(opts) {
1212
1238
  if (!opts.agent.solanaKeypair) {
1213
1239
  opts.resolveNoWallet(
1214
1240
  `Payment required but no Solana wallet configured.
1215
- Amount: ${amount ? formatSol(BigInt(amount)) : "unknown"}
1241
+ Amount: ${signedAmount !== void 0 ? formatSol(BigInt(signedAmount)) : "unknown"}
1216
1242
  Payment request: ${paymentRequest}`
1217
1243
  );
1218
1244
  return;
@@ -1642,7 +1668,7 @@ ${result}`);
1642
1668
  var GetDashboardSchema = z.object({
1643
1669
  top_n: z.number().int().min(1).max(100).default(10),
1644
1670
  chain: z.enum(["solana"]).default("solana"),
1645
- network: z.enum(["devnet", "mainnet"]).optional(),
1671
+ network: z.enum(["devnet"]).optional(),
1646
1672
  timeout_secs: z.number().int().min(1).max(60).default(15)
1647
1673
  });
1648
1674
  var dashboardTools = [
@@ -2411,9 +2437,14 @@ program.action(async () => {
2411
2437
  }
2412
2438
  const client = new ElisymClient({ relays: RELAYS });
2413
2439
  const name = process.env.ELISYM_AGENT_NAME ?? "mcp-agent";
2414
- const network = process.env.ELISYM_NETWORK === "mainnet" ? "mainnet" : "devnet";
2415
- ctx.register({ client, identity, name, network, security: {} });
2416
- console.error(`Ephemeral agent: ${name} (${network})`);
2440
+ if (process.env.ELISYM_NETWORK && process.env.ELISYM_NETWORK !== "devnet") {
2441
+ console.error(
2442
+ `ELISYM_NETWORK="${process.env.ELISYM_NETWORK}" is not supported. Only "devnet" is available until the on-chain protocol program ships on mainnet.`
2443
+ );
2444
+ process.exit(1);
2445
+ }
2446
+ ctx.register({ client, identity, name, network: "devnet", security: {} });
2447
+ console.error(`Ephemeral agent: ${name} (devnet)`);
2417
2448
  } else {
2418
2449
  const names = (await listAgentNames()).slice().sort();
2419
2450
  if (names.length > 0) {
@@ -2436,7 +2467,7 @@ program.action(async () => {
2436
2467
  }
2437
2468
  await startServer(ctx);
2438
2469
  });
2439
- program.command("init [name]").description("Create a new agent identity").option("-d, --description <desc>", "Agent description", "Elisym MCP agent").option("-n, --network <network>", "Solana network (devnet|mainnet)", "devnet").option("--install", "Also install into MCP clients").action(async (name, options) => {
2470
+ program.command("init [name]").description("Create a new agent identity").option("-d, --description <desc>", "Agent description", "Elisym MCP agent").option("-n, --network <network>", "Solana network (devnet only)", "devnet").option("--install", "Also install into MCP clients").action(async (name, options) => {
2440
2471
  const { default: inquirer } = await import('inquirer');
2441
2472
  if (!name) {
2442
2473
  const answers = await inquirer.prompt([
@@ -2456,8 +2487,8 @@ program.command("init [name]").description("Create a new agent identity").option
2456
2487
  type: "list",
2457
2488
  name: "network",
2458
2489
  message: "Solana network:",
2459
- // testnet removed - only devnet and mainnet are supported.
2460
- choices: ["devnet", "mainnet"],
2490
+ // Only devnet is supported until the elisym-config program ships on mainnet.
2491
+ choices: ["devnet"],
2461
2492
  default: "devnet"
2462
2493
  }
2463
2494
  ]);
@@ -2465,8 +2496,10 @@ program.command("init [name]").description("Create a new agent identity").option
2465
2496
  options.description = answers.description;
2466
2497
  options.network = answers.network;
2467
2498
  }
2468
- if (options.network !== "devnet" && options.network !== "mainnet") {
2469
- console.error(`Network must be "devnet" or "mainnet", got "${options.network}".`);
2499
+ if (options.network !== "devnet") {
2500
+ console.error(
2501
+ `Network must be "devnet", got "${options.network}". Mainnet is not supported until the on-chain protocol program is deployed.`
2502
+ );
2470
2503
  process.exit(1);
2471
2504
  }
2472
2505
  const { passphrase } = await inquirer.prompt([
@@ -2488,7 +2521,7 @@ program.command("init [name]").description("Create a new agent identity").option
2488
2521
  nostrSecretKey: Buffer.from(nostrSecretKey).toString("hex"),
2489
2522
  solanaSecretKey: bs58.encode(solanaSecretBytes),
2490
2523
  solanaAddress: solanaSigner.address,
2491
- network: options.network,
2524
+ network: "devnet",
2492
2525
  security: { withdrawals_enabled: false, agent_switch_enabled: false },
2493
2526
  passphrase: passphrase || void 0
2494
2527
  });