@elisym/mcp 0.3.2 → 0.4.0
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 +12 -12
- package/dist/index.js +255 -273
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy,
|
|
3
|
-
import { generateKeyPairSigner,
|
|
2
|
+
import { LIMITS, DEFAULT_KIND_OFFSET, SolanaPaymentStrategy, validateAgentName, RELAYS, toDTag, ElisymIdentity, ElisymClient, getProtocolProgramId, getProtocolConfig } from '@elisym/sdk';
|
|
3
|
+
import { generateKeyPairSigner, address, createSolanaRpcSubscriptions, sendAndConfirmTransactionFactory, getSignatureFromTransaction, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, createKeyPairSignerFromBytes, createSolanaRpc, isAddress } from '@solana/kit';
|
|
4
4
|
import bs58 from 'bs58';
|
|
5
5
|
import { Command } from 'commander';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { generateSecretKey, nip19, getPublicKey } from 'nostr-tools';
|
|
7
|
+
import { listAgents, createAgentDir, writeYaml, writeSecrets, loadAgent } from '@elisym/sdk/agent-store';
|
|
8
|
+
import { readFile, writeFile, rename, unlink } from 'node:fs/promises';
|
|
8
9
|
import { homedir, platform } from 'node:os';
|
|
9
10
|
import { dirname, join } from 'node:path';
|
|
10
|
-
import { parseConfig } from '@elisym/sdk/node';
|
|
11
11
|
import { readFileSync } from 'node:fs';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
@@ -18,160 +18,83 @@ import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
|
18
18
|
import { randomBytes } from 'node:crypto';
|
|
19
19
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
await writeFile(tmpPath, content, { mode });
|
|
25
|
-
await rename(tmpPath, path);
|
|
26
|
-
} catch (err) {
|
|
27
|
-
try {
|
|
28
|
-
await unlink(tmpPath);
|
|
29
|
-
} catch {
|
|
30
|
-
}
|
|
31
|
-
throw err;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// src/config.ts
|
|
36
|
-
function rawConfigIsEncrypted(raw) {
|
|
37
|
-
return raw.includes('"encrypted:v1:');
|
|
38
|
-
}
|
|
39
|
-
function agentsDir() {
|
|
40
|
-
return join(homedir(), ".elisym", "agents");
|
|
41
|
-
}
|
|
42
|
-
function agentConfigPath(name) {
|
|
43
|
-
return join(agentsDir(), name, "config.json");
|
|
44
|
-
}
|
|
45
|
-
function coerceNetwork(raw) {
|
|
46
|
-
return raw === "mainnet" ? "mainnet" : "devnet";
|
|
47
|
-
}
|
|
48
|
-
async function loadAgentConfig(name, passphrase) {
|
|
49
|
-
validateAgentName(name);
|
|
50
|
-
const configPath = agentConfigPath(name);
|
|
51
|
-
const raw = await readFile(configPath, "utf-8");
|
|
52
|
-
const rawEncrypted = rawConfigIsEncrypted(raw);
|
|
53
|
-
const effectivePassphrase = process.env.ELISYM_PASSPHRASE;
|
|
54
|
-
if (rawEncrypted && !effectivePassphrase) {
|
|
55
|
-
throw new Error(
|
|
56
|
-
`Agent "${name}" has an encrypted config but no passphrase. Set the ELISYM_PASSPHRASE environment variable.`
|
|
57
|
-
);
|
|
21
|
+
function coerceNetwork(raw, name) {
|
|
22
|
+
if (raw === void 0 || raw === "devnet") {
|
|
23
|
+
return "devnet";
|
|
58
24
|
}
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
config = parseConfig(raw, effectivePassphrase);
|
|
62
|
-
} catch (e) {
|
|
63
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
25
|
+
if (raw === "mainnet") {
|
|
64
26
|
throw new Error(
|
|
65
|
-
`
|
|
27
|
+
`Agent "${name}" 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/${name} && elisym-mcp init ${name} --network devnet`
|
|
66
28
|
);
|
|
67
29
|
}
|
|
68
|
-
|
|
30
|
+
throw new Error(`Agent "${name}" has unsupported network "${raw}". Expected "devnet".`);
|
|
31
|
+
}
|
|
32
|
+
async function loadAgentConfig(name, passphrase) {
|
|
33
|
+
validateAgentName(name);
|
|
34
|
+
const loaded = await loadAgent(name, process.cwd(), passphrase);
|
|
35
|
+
const solPayment = loaded.yaml.payments.find((entry) => entry.chain === "solana");
|
|
36
|
+
const network = coerceNetwork(solPayment?.network, name);
|
|
69
37
|
return {
|
|
70
|
-
nostrSecretKey:
|
|
71
|
-
solanaSecretKey:
|
|
72
|
-
relays:
|
|
38
|
+
nostrSecretKey: loaded.secrets.nostr_secret_key,
|
|
39
|
+
solanaSecretKey: loaded.secrets.solana_secret_key,
|
|
40
|
+
relays: loaded.yaml.relays.length > 0 ? loaded.yaml.relays : void 0,
|
|
73
41
|
network,
|
|
74
|
-
payments:
|
|
75
|
-
|
|
76
|
-
|
|
42
|
+
payments: loaded.yaml.payments.length > 0 ? loaded.yaml.payments.map((payment2) => ({
|
|
43
|
+
chain: payment2.chain,
|
|
44
|
+
network: payment2.network,
|
|
45
|
+
address: payment2.address
|
|
46
|
+
})) : void 0,
|
|
47
|
+
security: {
|
|
48
|
+
withdrawals_enabled: loaded.yaml.security.withdrawals_enabled,
|
|
49
|
+
agent_switch_enabled: loaded.yaml.security.agent_switch_enabled
|
|
50
|
+
},
|
|
51
|
+
encrypted: loaded.encrypted
|
|
77
52
|
};
|
|
78
53
|
}
|
|
79
|
-
async function saveAgentConfig(name,
|
|
54
|
+
async function saveAgentConfig(name, input) {
|
|
80
55
|
validateAgentName(name);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{
|
|
99
|
-
chain: "solana",
|
|
100
|
-
network,
|
|
101
|
-
address: config.solanaAddress
|
|
102
|
-
}
|
|
103
|
-
]
|
|
104
|
-
},
|
|
105
|
-
...solanaSecret && {
|
|
106
|
-
wallet: {
|
|
107
|
-
chain: "solana",
|
|
108
|
-
network,
|
|
109
|
-
secret_key: solanaSecret
|
|
110
|
-
}
|
|
56
|
+
const created = await createAgentDir({ target: "home", name, cwd: process.cwd() });
|
|
57
|
+
const network = input.network ?? "devnet";
|
|
58
|
+
await writeYaml(created.dir, {
|
|
59
|
+
display_name: void 0,
|
|
60
|
+
description: input.description,
|
|
61
|
+
picture: void 0,
|
|
62
|
+
banner: void 0,
|
|
63
|
+
relays: input.relays,
|
|
64
|
+
payments: input.solanaAddress ? [{ chain: "solana", network, address: input.solanaAddress }] : [],
|
|
65
|
+
llm: void 0,
|
|
66
|
+
security: input.security ?? {}
|
|
67
|
+
});
|
|
68
|
+
await writeSecrets(
|
|
69
|
+
created.dir,
|
|
70
|
+
{
|
|
71
|
+
nostr_secret_key: input.nostrSecretKey,
|
|
72
|
+
solana_secret_key: input.solanaSecretKey
|
|
111
73
|
},
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
await writeFileAtomic(join(agentDirPath, "config.json"), serializeConfig(agentConfig), 384);
|
|
74
|
+
input.passphrase
|
|
75
|
+
);
|
|
115
76
|
}
|
|
116
77
|
async function updateAgentSecurity(name, patch, passphrase) {
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
const effectivePassphrase = process.env.ELISYM_PASSPHRASE;
|
|
121
|
-
const config = parseConfig(raw, effectivePassphrase);
|
|
122
|
-
const rawEncrypted = rawConfigIsEncrypted(raw);
|
|
123
|
-
if (rawEncrypted && !effectivePassphrase) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
`Agent "${name}" is encrypted - set ELISYM_PASSPHRASE to update security flags.`
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
const merged = { ...config.security, ...patch };
|
|
129
|
-
const { encryptSecret } = await import('@elisym/sdk/node');
|
|
130
|
-
const encrypt = (v) => effectivePassphrase ? encryptSecret(v, effectivePassphrase) : v;
|
|
131
|
-
const next = {
|
|
132
|
-
...config,
|
|
133
|
-
// parseConfig returns decrypted secrets; re-encrypt them if the file was encrypted
|
|
134
|
-
identity: {
|
|
135
|
-
...config.identity,
|
|
136
|
-
secret_key: rawEncrypted ? encrypt(config.identity.secret_key) : config.identity.secret_key
|
|
137
|
-
},
|
|
138
|
-
wallet: config.wallet ? {
|
|
139
|
-
...config.wallet,
|
|
140
|
-
secret_key: rawEncrypted ? encrypt(config.wallet.secret_key) : config.wallet.secret_key
|
|
141
|
-
} : void 0,
|
|
142
|
-
security: merged
|
|
143
|
-
};
|
|
144
|
-
await writeFileAtomic(configPath, serializeConfig(next), 384);
|
|
78
|
+
const loaded = await loadAgent(name, process.cwd(), passphrase);
|
|
79
|
+
const merged = { ...loaded.yaml.security, ...patch };
|
|
80
|
+
await writeYaml(loaded.dir, { ...loaded.yaml, security: merged });
|
|
145
81
|
return merged;
|
|
146
82
|
}
|
|
147
83
|
async function listAgentNames() {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const names = [];
|
|
151
|
-
for (const entry of entries) {
|
|
152
|
-
try {
|
|
153
|
-
await stat(join(agentsDir(), entry, "config.json"));
|
|
154
|
-
names.push(entry);
|
|
155
|
-
} catch {
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return names;
|
|
159
|
-
} catch {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
84
|
+
const agents = await listAgents(process.cwd());
|
|
85
|
+
return agents.map((agent) => agent.name);
|
|
162
86
|
}
|
|
163
|
-
function rpcUrlFor(
|
|
164
|
-
return
|
|
87
|
+
function rpcUrlFor(_network) {
|
|
88
|
+
return "https://api.devnet.solana.com";
|
|
165
89
|
}
|
|
166
|
-
async function fetchProtocolConfig(
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const rpc = createSolanaRpc(rpcUrlFor(cluster));
|
|
90
|
+
async function fetchProtocolConfig(_network) {
|
|
91
|
+
const programId = getProtocolProgramId("devnet");
|
|
92
|
+
const rpc = createSolanaRpc(rpcUrlFor());
|
|
170
93
|
const config = await getProtocolConfig(rpc, programId, { forceRefresh: true });
|
|
171
94
|
return { feeBps: config.feeBps, treasury: config.treasury };
|
|
172
95
|
}
|
|
173
|
-
function explorerClusterFor(
|
|
174
|
-
return
|
|
96
|
+
function explorerClusterFor(_network) {
|
|
97
|
+
return "devnet";
|
|
175
98
|
}
|
|
176
99
|
var RateLimiter = class {
|
|
177
100
|
constructor(maxCalls, windowSecs) {
|
|
@@ -251,6 +174,19 @@ var AgentContext = class _AgentContext {
|
|
|
251
174
|
return nonce;
|
|
252
175
|
}
|
|
253
176
|
};
|
|
177
|
+
async function writeFileAtomic(path, content, mode = 384) {
|
|
178
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
179
|
+
try {
|
|
180
|
+
await writeFile(tmpPath, content, { mode });
|
|
181
|
+
await rename(tmpPath, path);
|
|
182
|
+
} catch (err) {
|
|
183
|
+
try {
|
|
184
|
+
await unlink(tmpPath);
|
|
185
|
+
} catch {
|
|
186
|
+
}
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
254
190
|
var LAMPORTS_PER_SOL = 1000000000n;
|
|
255
191
|
function readPackageVersion() {
|
|
256
192
|
try {
|
|
@@ -632,7 +568,10 @@ var CreateAgentSchema = z.object({
|
|
|
632
568
|
// customer-mode in 0.1.x and never publishes a NIP-89 capability card,
|
|
633
569
|
// so an advertised capability list would be misleading. Provider-mode
|
|
634
570
|
// (0.2.0) will reintroduce this field.
|
|
635
|
-
|
|
571
|
+
// Only devnet is supported until the elisym-config program ships on mainnet.
|
|
572
|
+
// Mainnet was previously advertised here but every paid flow then crashed at
|
|
573
|
+
// payment time - rejecting at schema level surfaces the error up front.
|
|
574
|
+
network: z.enum(["devnet"]).default("devnet"),
|
|
636
575
|
passphrase: z.string().optional().describe("Optional passphrase; if set, secret keys are encrypted at rest."),
|
|
637
576
|
activate: z.boolean().default(true)
|
|
638
577
|
});
|
|
@@ -680,7 +619,7 @@ async function buildAgentInstance(name, config) {
|
|
|
680
619
|
var agentTools = [
|
|
681
620
|
defineTool({
|
|
682
621
|
name: "create_agent",
|
|
683
|
-
description: "Create a new agent identity. Generates Nostr keypair and Solana wallet, saves config to ~/.elisym
|
|
622
|
+
description: "Create a new agent identity. Generates Nostr keypair and Solana wallet, saves config to ~/.elisym/<name>/. When activate=true (default), the current active agent must have `security.agent_switch_enabled` set to true, otherwise the new agent is created but NOT activated (pass activate=false or run `elisym-mcp enable-agent-switch <current-agent>`).",
|
|
684
623
|
schema: CreateAgentSchema,
|
|
685
624
|
async handler(ctx, input) {
|
|
686
625
|
ctx.toolRateLimiter.check();
|
|
@@ -1193,18 +1132,34 @@ function makePaymentFeedbackHandler(opts) {
|
|
|
1193
1132
|
);
|
|
1194
1133
|
return;
|
|
1195
1134
|
}
|
|
1196
|
-
|
|
1135
|
+
let parsedRequest;
|
|
1136
|
+
try {
|
|
1137
|
+
parsedRequest = JSON.parse(paymentRequest);
|
|
1138
|
+
} catch {
|
|
1139
|
+
opts.rejectPayment(new Error("Provider sent a malformed payment_request (not valid JSON)."));
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
const signedAmount = typeof parsedRequest.amount === "number" && Number.isInteger(parsedRequest.amount) && parsedRequest.amount > 0 ? parsedRequest.amount : void 0;
|
|
1143
|
+
if (amount !== void 0 && amount > 0 && signedAmount !== void 0 && amount !== signedAmount) {
|
|
1144
|
+
opts.rejectPayment(
|
|
1145
|
+
new Error(
|
|
1146
|
+
`Payment request mismatch: feedback tag amount=${amount} differs from signed amount=${signedAmount}. Refusing to proceed.`
|
|
1147
|
+
)
|
|
1148
|
+
);
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (opts.maxPriceLamports === void 0 && signedAmount !== void 0) {
|
|
1197
1152
|
opts.rejectPayment(
|
|
1198
1153
|
new Error(
|
|
1199
|
-
`Payment of ${formatSol(BigInt(
|
|
1154
|
+
`Payment of ${formatSol(BigInt(signedAmount))} required but no max_price_lamports set. Retry with max_price_lamports to approve.`
|
|
1200
1155
|
)
|
|
1201
1156
|
);
|
|
1202
1157
|
return;
|
|
1203
1158
|
}
|
|
1204
|
-
if (opts.maxPriceLamports !== void 0 &&
|
|
1159
|
+
if (opts.maxPriceLamports !== void 0 && signedAmount !== void 0 && signedAmount > opts.maxPriceLamports) {
|
|
1205
1160
|
opts.rejectPayment(
|
|
1206
1161
|
new Error(
|
|
1207
|
-
`Price ${formatSol(BigInt(
|
|
1162
|
+
`Price ${formatSol(BigInt(signedAmount))} exceeds max ${formatSol(BigInt(opts.maxPriceLamports))}`
|
|
1208
1163
|
)
|
|
1209
1164
|
);
|
|
1210
1165
|
return;
|
|
@@ -1212,7 +1167,7 @@ function makePaymentFeedbackHandler(opts) {
|
|
|
1212
1167
|
if (!opts.agent.solanaKeypair) {
|
|
1213
1168
|
opts.resolveNoWallet(
|
|
1214
1169
|
`Payment required but no Solana wallet configured.
|
|
1215
|
-
Amount: ${
|
|
1170
|
+
Amount: ${signedAmount !== void 0 ? formatSol(BigInt(signedAmount)) : "unknown"}
|
|
1216
1171
|
Payment request: ${paymentRequest}`
|
|
1217
1172
|
);
|
|
1218
1173
|
return;
|
|
@@ -1642,7 +1597,7 @@ ${result}`);
|
|
|
1642
1597
|
var GetDashboardSchema = z.object({
|
|
1643
1598
|
top_n: z.number().int().min(1).max(100).default(10),
|
|
1644
1599
|
chain: z.enum(["solana"]).default("solana"),
|
|
1645
|
-
network: z.enum(["devnet"
|
|
1600
|
+
network: z.enum(["devnet"]).optional(),
|
|
1646
1601
|
timeout_secs: z.number().int().min(1).max(60).default(15)
|
|
1647
1602
|
});
|
|
1648
1603
|
var dashboardTools = [
|
|
@@ -2382,143 +2337,170 @@ process.on("warning", (w) => {
|
|
|
2382
2337
|
}
|
|
2383
2338
|
console.warn(w);
|
|
2384
2339
|
});
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
const ctx = new AgentContext();
|
|
2388
|
-
const agentName = process.env.ELISYM_AGENT;
|
|
2389
|
-
const nostrSecret = process.env.ELISYM_NOSTR_SECRET;
|
|
2390
|
-
if (agentName) {
|
|
2340
|
+
function safe(fn) {
|
|
2341
|
+
return async (...args) => {
|
|
2391
2342
|
try {
|
|
2392
|
-
|
|
2393
|
-
const instance = await buildAgentInstance(agentName, config);
|
|
2394
|
-
ctx.register(instance);
|
|
2395
|
-
console.error(`Loaded agent: ${agentName}`);
|
|
2343
|
+
await fn(...args);
|
|
2396
2344
|
} catch (e) {
|
|
2397
|
-
console.error(`
|
|
2345
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
2398
2346
|
process.exit(1);
|
|
2399
2347
|
}
|
|
2400
|
-
}
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
} else {
|
|
2410
|
-
identity = ElisymIdentity.fromHex(nostrSecret);
|
|
2411
|
-
}
|
|
2412
|
-
const client = new ElisymClient({ relays: RELAYS });
|
|
2413
|
-
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})`);
|
|
2417
|
-
} else {
|
|
2418
|
-
const names = (await listAgentNames()).slice().sort();
|
|
2419
|
-
if (names.length > 0) {
|
|
2420
|
-
const name = names[0];
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
var program = new Command().name("elisym-mcp").description("MCP server for the elisym agent network").version(PACKAGE_VERSION);
|
|
2351
|
+
program.action(
|
|
2352
|
+
safe(async () => {
|
|
2353
|
+
const ctx = new AgentContext();
|
|
2354
|
+
const agentName = process.env.ELISYM_AGENT;
|
|
2355
|
+
const nostrSecret = process.env.ELISYM_NOSTR_SECRET;
|
|
2356
|
+
if (agentName) {
|
|
2421
2357
|
try {
|
|
2422
|
-
const config = await loadAgentConfig(
|
|
2423
|
-
const instance = await buildAgentInstance(
|
|
2358
|
+
const config = await loadAgentConfig(agentName);
|
|
2359
|
+
const instance = await buildAgentInstance(agentName, config);
|
|
2424
2360
|
ctx.register(instance);
|
|
2425
|
-
console.error(`Loaded
|
|
2361
|
+
console.error(`Loaded agent: ${agentName}`);
|
|
2426
2362
|
} catch (e) {
|
|
2427
|
-
console.error(`Failed to load agent "${
|
|
2363
|
+
console.error(`Failed to load agent "${agentName}": ${e.message}`);
|
|
2428
2364
|
process.exit(1);
|
|
2429
2365
|
}
|
|
2430
|
-
} else {
|
|
2431
|
-
|
|
2366
|
+
} else if (nostrSecret) {
|
|
2367
|
+
let identity;
|
|
2368
|
+
if (nostrSecret.startsWith("nsec")) {
|
|
2369
|
+
const decoded = nip19.decode(nostrSecret);
|
|
2370
|
+
if (decoded.type !== "nsec") {
|
|
2371
|
+
console.error(`ELISYM_NOSTR_SECRET: expected nsec, got ${decoded.type}`);
|
|
2372
|
+
process.exit(1);
|
|
2373
|
+
}
|
|
2374
|
+
identity = ElisymIdentity.fromSecretKey(decoded.data);
|
|
2375
|
+
} else {
|
|
2376
|
+
identity = ElisymIdentity.fromHex(nostrSecret);
|
|
2377
|
+
}
|
|
2432
2378
|
const client = new ElisymClient({ relays: RELAYS });
|
|
2433
|
-
|
|
2434
|
-
|
|
2379
|
+
const name = process.env.ELISYM_AGENT_NAME ?? "mcp-agent";
|
|
2380
|
+
if (process.env.ELISYM_NETWORK && process.env.ELISYM_NETWORK !== "devnet") {
|
|
2381
|
+
console.error(
|
|
2382
|
+
`ELISYM_NETWORK="${process.env.ELISYM_NETWORK}" is not supported. Only "devnet" is available until the on-chain protocol program ships on mainnet.`
|
|
2383
|
+
);
|
|
2384
|
+
process.exit(1);
|
|
2385
|
+
}
|
|
2386
|
+
ctx.register({ client, identity, name, network: "devnet", security: {} });
|
|
2387
|
+
console.error(`Ephemeral agent: ${name} (devnet)`);
|
|
2388
|
+
} else {
|
|
2389
|
+
const names = (await listAgentNames()).slice().sort();
|
|
2390
|
+
if (names.length > 0) {
|
|
2391
|
+
const name = names[0];
|
|
2392
|
+
try {
|
|
2393
|
+
const config = await loadAgentConfig(name);
|
|
2394
|
+
const instance = await buildAgentInstance(name, config);
|
|
2395
|
+
ctx.register(instance);
|
|
2396
|
+
console.error(`Loaded default agent: ${name} (${instance.network})`);
|
|
2397
|
+
} catch (e) {
|
|
2398
|
+
console.error(`Failed to load agent "${name}": ${e.message}`);
|
|
2399
|
+
process.exit(1);
|
|
2400
|
+
}
|
|
2401
|
+
} else {
|
|
2402
|
+
const identity = ElisymIdentity.generate();
|
|
2403
|
+
const client = new ElisymClient({ relays: RELAYS });
|
|
2404
|
+
ctx.register({ client, identity, name: "mcp-agent", network: "devnet", security: {} });
|
|
2405
|
+
console.error("Created ephemeral agent (no persistent identity, devnet).");
|
|
2406
|
+
}
|
|
2435
2407
|
}
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
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
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2408
|
+
await startServer(ctx);
|
|
2409
|
+
})
|
|
2410
|
+
);
|
|
2411
|
+
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(
|
|
2412
|
+
safe(async (name, options) => {
|
|
2413
|
+
const { default: inquirer } = await import('inquirer');
|
|
2414
|
+
if (!name) {
|
|
2415
|
+
const answers = await inquirer.prompt([
|
|
2416
|
+
{
|
|
2417
|
+
type: "input",
|
|
2418
|
+
name: "name",
|
|
2419
|
+
message: "Agent name:",
|
|
2420
|
+
validate: (v) => /^[a-zA-Z0-9_-]+$/.test(v) || "Alphanumeric, _, - only"
|
|
2421
|
+
},
|
|
2422
|
+
{
|
|
2423
|
+
type: "input",
|
|
2424
|
+
name: "description",
|
|
2425
|
+
message: "Description:",
|
|
2426
|
+
default: "Elisym MCP agent"
|
|
2427
|
+
},
|
|
2428
|
+
{
|
|
2429
|
+
type: "list",
|
|
2430
|
+
name: "network",
|
|
2431
|
+
message: "Solana network:",
|
|
2432
|
+
// Only devnet is supported until the elisym-config program ships on mainnet.
|
|
2433
|
+
choices: ["devnet"],
|
|
2434
|
+
default: "devnet"
|
|
2435
|
+
}
|
|
2436
|
+
]);
|
|
2437
|
+
name = answers.name;
|
|
2438
|
+
options.description = answers.description;
|
|
2439
|
+
options.network = answers.network;
|
|
2440
|
+
}
|
|
2441
|
+
if (options.network !== "devnet") {
|
|
2442
|
+
console.error(
|
|
2443
|
+
`Network must be "devnet", got "${options.network}". Mainnet is not supported until the on-chain protocol program is deployed.`
|
|
2444
|
+
);
|
|
2445
|
+
process.exit(1);
|
|
2446
|
+
}
|
|
2447
|
+
const { passphrase } = await inquirer.prompt([
|
|
2455
2448
|
{
|
|
2456
|
-
type: "
|
|
2457
|
-
name: "
|
|
2458
|
-
message: "
|
|
2459
|
-
|
|
2460
|
-
choices: ["devnet", "mainnet"],
|
|
2461
|
-
default: "devnet"
|
|
2449
|
+
type: "password",
|
|
2450
|
+
name: "passphrase",
|
|
2451
|
+
message: "Passphrase to encrypt secret keys (leave blank for none):",
|
|
2452
|
+
mask: "*"
|
|
2462
2453
|
}
|
|
2463
2454
|
]);
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2455
|
+
const nostrSecretKey = generateSecretKey();
|
|
2456
|
+
const nostrPubkey = getPublicKey(nostrSecretKey);
|
|
2457
|
+
const solanaSigner = await generateKeyPairSigner(true);
|
|
2458
|
+
const solanaSecretBytes = await exportKeyPairBytes(solanaSigner);
|
|
2459
|
+
await saveAgentConfig(name, {
|
|
2460
|
+
name,
|
|
2461
|
+
description: options.description,
|
|
2462
|
+
relays: [...RELAYS],
|
|
2463
|
+
nostrSecretKey: Buffer.from(nostrSecretKey).toString("hex"),
|
|
2464
|
+
solanaSecretKey: bs58.encode(solanaSecretBytes),
|
|
2465
|
+
solanaAddress: solanaSigner.address,
|
|
2466
|
+
network: "devnet",
|
|
2467
|
+
security: { withdrawals_enabled: false, agent_switch_enabled: false },
|
|
2468
|
+
passphrase: passphrase || void 0
|
|
2469
|
+
});
|
|
2470
|
+
const npub = nip19.npubEncode(nostrPubkey);
|
|
2471
|
+
console.log(`Agent "${name}" created.`);
|
|
2472
|
+
console.log(` Nostr: ${npub}`);
|
|
2473
|
+
console.log(` Solana: ${solanaSigner.address}`);
|
|
2474
|
+
console.log(` Network: ${options.network}`);
|
|
2475
|
+
console.log(` Encrypted: ${passphrase ? "yes" : "no"}`);
|
|
2476
|
+
console.log(` Config: ~/.elisym/${name}/elisym.yaml`);
|
|
2477
|
+
if (passphrase) {
|
|
2478
|
+
console.log(` Note: set ELISYM_PASSPHRASE before launching the MCP server.`);
|
|
2479
|
+
}
|
|
2480
|
+
if (options.install) {
|
|
2481
|
+
await runInstall({ agent: name });
|
|
2478
2482
|
}
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
console.log(` Encrypted: ${passphrase ? "yes" : "no"}`);
|
|
2501
|
-
console.log(` Config: ~/.elisym/agents/${name}/config.json`);
|
|
2502
|
-
if (passphrase) {
|
|
2503
|
-
console.log(` Note: set ELISYM_PASSPHRASE before launching the MCP server.`);
|
|
2504
|
-
}
|
|
2505
|
-
if (options.install) {
|
|
2506
|
-
await runInstall({ agent: name });
|
|
2507
|
-
}
|
|
2508
|
-
});
|
|
2509
|
-
program.command("install").description("Install elisym MCP server into client configs").option("--client <name>", "Specific client (claude-desktop, claude-code, cursor, windsurf)").option("--agent <name>", "Bind to specific agent").option("--list", "List detected clients").action(async (options) => {
|
|
2510
|
-
if (options.list) {
|
|
2511
|
-
await runList();
|
|
2512
|
-
} else {
|
|
2513
|
-
await runInstall({ client: options.client, agent: options.agent });
|
|
2514
|
-
}
|
|
2515
|
-
});
|
|
2516
|
-
program.command("update").description("Refresh the elisym MCP entry in installed client configs").option("--client <name>", "Specific client (claude-desktop, claude-code, cursor, windsurf)").option("--agent <name>", "Override the agent binding").action(async (options) => {
|
|
2517
|
-
await runUpdate({ client: options.client, agent: options.agent });
|
|
2518
|
-
});
|
|
2519
|
-
program.command("uninstall").description("Remove elisym from MCP client configs").option("--client <name>", "Specific client").action(async (options) => {
|
|
2520
|
-
await runUninstall({ client: options.client });
|
|
2521
|
-
});
|
|
2483
|
+
})
|
|
2484
|
+
);
|
|
2485
|
+
program.command("install").description("Install elisym MCP server into client configs").option("--client <name>", "Specific client (claude-desktop, claude-code, cursor, windsurf)").option("--agent <name>", "Bind to specific agent").option("--list", "List detected clients").action(
|
|
2486
|
+
safe(async (options) => {
|
|
2487
|
+
if (options.list) {
|
|
2488
|
+
await runList();
|
|
2489
|
+
} else {
|
|
2490
|
+
await runInstall({ client: options.client, agent: options.agent });
|
|
2491
|
+
}
|
|
2492
|
+
})
|
|
2493
|
+
);
|
|
2494
|
+
program.command("update").description("Refresh the elisym MCP entry in installed client configs").option("--client <name>", "Specific client (claude-desktop, claude-code, cursor, windsurf)").option("--agent <name>", "Override the agent binding").action(
|
|
2495
|
+
safe(async (options) => {
|
|
2496
|
+
await runUpdate({ client: options.client, agent: options.agent });
|
|
2497
|
+
})
|
|
2498
|
+
);
|
|
2499
|
+
program.command("uninstall").description("Remove elisym from MCP client configs").option("--client <name>", "Specific client").action(
|
|
2500
|
+
safe(async (options) => {
|
|
2501
|
+
await runUninstall({ client: options.client });
|
|
2502
|
+
})
|
|
2503
|
+
);
|
|
2522
2504
|
async function toggleFlag(agentName, field, enable) {
|
|
2523
2505
|
const { default: inquirer } = await import('inquirer');
|
|
2524
2506
|
if (enable) {
|
|
@@ -2539,10 +2521,10 @@ async function toggleFlag(agentName, field, enable) {
|
|
|
2539
2521
|
console.log(`Agent "${agentName}" security:`, merged);
|
|
2540
2522
|
console.log("Note: restart the MCP server for changes to take effect on a running session.");
|
|
2541
2523
|
}
|
|
2542
|
-
program.command("enable-withdrawals <agent>").description("Enable SOL withdrawals for a specific agent (interactive confirmation)").action((agent) => toggleFlag(agent, "withdrawals_enabled", true));
|
|
2543
|
-
program.command("disable-withdrawals <agent>").description("Disable SOL withdrawals for a specific agent").action((agent) => toggleFlag(agent, "withdrawals_enabled", false));
|
|
2544
|
-
program.command("enable-agent-switch <agent>").description("Allow the MCP server to switch away from this agent at runtime").action((agent) => toggleFlag(agent, "agent_switch_enabled", true));
|
|
2545
|
-
program.command("disable-agent-switch <agent>").description("Forbid the MCP server from switching away from this agent at runtime").action((agent) => toggleFlag(agent, "agent_switch_enabled", false));
|
|
2524
|
+
program.command("enable-withdrawals <agent>").description("Enable SOL withdrawals for a specific agent (interactive confirmation)").action(safe((agent) => toggleFlag(agent, "withdrawals_enabled", true)));
|
|
2525
|
+
program.command("disable-withdrawals <agent>").description("Disable SOL withdrawals for a specific agent").action(safe((agent) => toggleFlag(agent, "withdrawals_enabled", false)));
|
|
2526
|
+
program.command("enable-agent-switch <agent>").description("Allow the MCP server to switch away from this agent at runtime").action(safe((agent) => toggleFlag(agent, "agent_switch_enabled", true)));
|
|
2527
|
+
program.command("disable-agent-switch <agent>").description("Forbid the MCP server from switching away from this agent at runtime").action(safe((agent) => toggleFlag(agent, "agent_switch_enabled", false)));
|
|
2546
2528
|
program.parse();
|
|
2547
2529
|
//# sourceMappingURL=index.js.map
|
|
2548
2530
|
//# sourceMappingURL=index.js.map
|