@elisym/mcp 0.3.3 → 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 +4 -4
- package/dist/index.js +215 -266
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ npx @elisym/mcp
|
|
|
36
36
|
|
|
37
37
|
### Docker
|
|
38
38
|
|
|
39
|
-
The wallet lives in `~/.elisym
|
|
39
|
+
The wallet lives in `~/.elisym/<name>/` (`elisym.yaml` + encrypted `.secrets.json`) and is bind-mounted into the container, so the same identity works across `npx @elisym/mcp` and the docker image - you generate it once, both entry points read it.
|
|
40
40
|
|
|
41
41
|
**1. Bootstrap an agent** (one-time, interactive):
|
|
42
42
|
|
|
@@ -46,7 +46,7 @@ docker run --rm -it \
|
|
|
46
46
|
ghcr.io/elisymlabs/mcp init
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
Generates a Nostr identity and a Solana keypair and writes them to `~/.elisym
|
|
49
|
+
Generates a Nostr identity and a Solana keypair and writes them to `~/.elisym/<chosen-name>/` on the host.
|
|
50
50
|
|
|
51
51
|
**2. Edit your MCP client's config file** and add the entry below. Replace `/Users/you/.elisym` with the absolute path to your home `.elisym` directory:
|
|
52
52
|
|
|
@@ -70,7 +70,7 @@ Generates a Nostr identity and a Solana keypair and writes them to `~/.elisym/ag
|
|
|
70
70
|
}
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
With a single agent in `~/.elisym
|
|
73
|
+
With a single agent in `~/.elisym/`, you can omit `ELISYM_AGENT`. With multiple agents, pin one explicitly - otherwise selection is unspecified.
|
|
74
74
|
|
|
75
75
|
**Claude Code shortcut.** Instead of editing `~/.claude.json` by hand, use the built-in CLI: `claude mcp add elisym -- docker run --rm -i -e ELISYM_AGENT=<name> -v "$HOME/.elisym:/root/.elisym" ghcr.io/elisymlabs/mcp`.
|
|
76
76
|
|
|
@@ -108,7 +108,7 @@ The bootstrap step is unchanged - the wizard collects the passphrase interactive
|
|
|
108
108
|
|
|
109
109
|
| Variable | Description |
|
|
110
110
|
| --------------------------- | ------------------------------------------------------------------------------- |
|
|
111
|
-
| `ELISYM_AGENT` | Load agent from `~/.elisym
|
|
111
|
+
| `ELISYM_AGENT` | Load agent from `~/.elisym/<name>/` (or a project-local `.elisym/<name>/`) |
|
|
112
112
|
| `ELISYM_NOSTR_SECRET` | Nostr secret key (hex or nsec) for ephemeral mode |
|
|
113
113
|
| `ELISYM_AGENT_NAME` | Agent display name (default: mcp-agent) |
|
|
114
114
|
| `ELISYM_NETWORK` | Solana network for ephemeral mode. Only `devnet` is supported (default: devnet) |
|
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,155 +18,71 @@ 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
|
-
const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
23
|
-
try {
|
|
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, agentName) {
|
|
21
|
+
function coerceNetwork(raw, name) {
|
|
46
22
|
if (raw === void 0 || raw === "devnet") {
|
|
47
23
|
return "devnet";
|
|
48
24
|
}
|
|
49
25
|
if (raw === "mainnet") {
|
|
50
26
|
throw new Error(
|
|
51
|
-
`Agent "${
|
|
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`
|
|
52
28
|
);
|
|
53
29
|
}
|
|
54
|
-
throw new Error(`Agent "${
|
|
30
|
+
throw new Error(`Agent "${name}" has unsupported network "${raw}". Expected "devnet".`);
|
|
55
31
|
}
|
|
56
32
|
async function loadAgentConfig(name, passphrase) {
|
|
57
33
|
validateAgentName(name);
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
const effectivePassphrase = process.env.ELISYM_PASSPHRASE;
|
|
62
|
-
if (rawEncrypted && !effectivePassphrase) {
|
|
63
|
-
throw new Error(
|
|
64
|
-
`Agent "${name}" has an encrypted config but no passphrase. Set the ELISYM_PASSPHRASE environment variable.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
let config;
|
|
68
|
-
try {
|
|
69
|
-
config = parseConfig(raw, effectivePassphrase);
|
|
70
|
-
} catch (e) {
|
|
71
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
72
|
-
throw new Error(
|
|
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.`
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
const network = coerceNetwork(config.wallet?.network ?? config.payments?.[0]?.network, 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);
|
|
77
37
|
return {
|
|
78
|
-
nostrSecretKey:
|
|
79
|
-
solanaSecretKey:
|
|
80
|
-
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,
|
|
81
41
|
network,
|
|
82
|
-
payments:
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
85
52
|
};
|
|
86
53
|
}
|
|
87
|
-
async function saveAgentConfig(name,
|
|
54
|
+
async function saveAgentConfig(name, input) {
|
|
88
55
|
validateAgentName(name);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
{
|
|
107
|
-
chain: "solana",
|
|
108
|
-
network,
|
|
109
|
-
address: config.solanaAddress
|
|
110
|
-
}
|
|
111
|
-
]
|
|
112
|
-
},
|
|
113
|
-
...solanaSecret && {
|
|
114
|
-
wallet: {
|
|
115
|
-
chain: "solana",
|
|
116
|
-
network,
|
|
117
|
-
secret_key: solanaSecret
|
|
118
|
-
}
|
|
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
|
|
119
73
|
},
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
await writeFileAtomic(join(agentDirPath, "config.json"), serializeConfig(agentConfig), 384);
|
|
74
|
+
input.passphrase
|
|
75
|
+
);
|
|
123
76
|
}
|
|
124
77
|
async function updateAgentSecurity(name, patch, passphrase) {
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
const effectivePassphrase = process.env.ELISYM_PASSPHRASE;
|
|
129
|
-
const config = parseConfig(raw, effectivePassphrase);
|
|
130
|
-
const rawEncrypted = rawConfigIsEncrypted(raw);
|
|
131
|
-
if (rawEncrypted && !effectivePassphrase) {
|
|
132
|
-
throw new Error(
|
|
133
|
-
`Agent "${name}" is encrypted - set ELISYM_PASSPHRASE to update security flags.`
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
const merged = { ...config.security, ...patch };
|
|
137
|
-
const { encryptSecret } = await import('@elisym/sdk/node');
|
|
138
|
-
const encrypt = (v) => effectivePassphrase ? encryptSecret(v, effectivePassphrase) : v;
|
|
139
|
-
const next = {
|
|
140
|
-
...config,
|
|
141
|
-
// parseConfig returns decrypted secrets; re-encrypt them if the file was encrypted
|
|
142
|
-
identity: {
|
|
143
|
-
...config.identity,
|
|
144
|
-
secret_key: rawEncrypted ? encrypt(config.identity.secret_key) : config.identity.secret_key
|
|
145
|
-
},
|
|
146
|
-
wallet: config.wallet ? {
|
|
147
|
-
...config.wallet,
|
|
148
|
-
secret_key: rawEncrypted ? encrypt(config.wallet.secret_key) : config.wallet.secret_key
|
|
149
|
-
} : void 0,
|
|
150
|
-
security: merged
|
|
151
|
-
};
|
|
152
|
-
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 });
|
|
153
81
|
return merged;
|
|
154
82
|
}
|
|
155
83
|
async function listAgentNames() {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const names = [];
|
|
159
|
-
for (const entry of entries) {
|
|
160
|
-
try {
|
|
161
|
-
await stat(join(agentsDir(), entry, "config.json"));
|
|
162
|
-
names.push(entry);
|
|
163
|
-
} catch {
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return names;
|
|
167
|
-
} catch {
|
|
168
|
-
return [];
|
|
169
|
-
}
|
|
84
|
+
const agents = await listAgents(process.cwd());
|
|
85
|
+
return agents.map((agent) => agent.name);
|
|
170
86
|
}
|
|
171
87
|
function rpcUrlFor(_network) {
|
|
172
88
|
return "https://api.devnet.solana.com";
|
|
@@ -258,6 +174,19 @@ var AgentContext = class _AgentContext {
|
|
|
258
174
|
return nonce;
|
|
259
175
|
}
|
|
260
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
|
+
}
|
|
261
190
|
var LAMPORTS_PER_SOL = 1000000000n;
|
|
262
191
|
function readPackageVersion() {
|
|
263
192
|
try {
|
|
@@ -690,7 +619,7 @@ async function buildAgentInstance(name, config) {
|
|
|
690
619
|
var agentTools = [
|
|
691
620
|
defineTool({
|
|
692
621
|
name: "create_agent",
|
|
693
|
-
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>`).",
|
|
694
623
|
schema: CreateAgentSchema,
|
|
695
624
|
async handler(ctx, input) {
|
|
696
625
|
ctx.toolRateLimiter.check();
|
|
@@ -2408,150 +2337,170 @@ process.on("warning", (w) => {
|
|
|
2408
2337
|
}
|
|
2409
2338
|
console.warn(w);
|
|
2410
2339
|
});
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
const ctx = new AgentContext();
|
|
2414
|
-
const agentName = process.env.ELISYM_AGENT;
|
|
2415
|
-
const nostrSecret = process.env.ELISYM_NOSTR_SECRET;
|
|
2416
|
-
if (agentName) {
|
|
2340
|
+
function safe(fn) {
|
|
2341
|
+
return async (...args) => {
|
|
2417
2342
|
try {
|
|
2418
|
-
|
|
2419
|
-
const instance = await buildAgentInstance(agentName, config);
|
|
2420
|
-
ctx.register(instance);
|
|
2421
|
-
console.error(`Loaded agent: ${agentName}`);
|
|
2343
|
+
await fn(...args);
|
|
2422
2344
|
} catch (e) {
|
|
2423
|
-
console.error(`
|
|
2345
|
+
console.error(`Error: ${e instanceof Error ? e.message : String(e)}`);
|
|
2424
2346
|
process.exit(1);
|
|
2425
2347
|
}
|
|
2426
|
-
}
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
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) {
|
|
2357
|
+
try {
|
|
2358
|
+
const config = await loadAgentConfig(agentName);
|
|
2359
|
+
const instance = await buildAgentInstance(agentName, config);
|
|
2360
|
+
ctx.register(instance);
|
|
2361
|
+
console.error(`Loaded agent: ${agentName}`);
|
|
2362
|
+
} catch (e) {
|
|
2363
|
+
console.error(`Failed to load agent "${agentName}": ${e.message}`);
|
|
2364
|
+
process.exit(1);
|
|
2365
|
+
}
|
|
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
|
+
}
|
|
2378
|
+
const client = new ElisymClient({ relays: RELAYS });
|
|
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
|
+
);
|
|
2432
2384
|
process.exit(1);
|
|
2433
2385
|
}
|
|
2434
|
-
identity
|
|
2386
|
+
ctx.register({ client, identity, name, network: "devnet", security: {} });
|
|
2387
|
+
console.error(`Ephemeral agent: ${name} (devnet)`);
|
|
2435
2388
|
} else {
|
|
2436
|
-
|
|
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
|
+
}
|
|
2407
|
+
}
|
|
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;
|
|
2437
2440
|
}
|
|
2438
|
-
|
|
2439
|
-
const name = process.env.ELISYM_AGENT_NAME ?? "mcp-agent";
|
|
2440
|
-
if (process.env.ELISYM_NETWORK && process.env.ELISYM_NETWORK !== "devnet") {
|
|
2441
|
+
if (options.network !== "devnet") {
|
|
2441
2442
|
console.error(
|
|
2442
|
-
`
|
|
2443
|
+
`Network must be "devnet", got "${options.network}". Mainnet is not supported until the on-chain protocol program is deployed.`
|
|
2443
2444
|
);
|
|
2444
2445
|
process.exit(1);
|
|
2445
2446
|
}
|
|
2446
|
-
|
|
2447
|
-
console.error(`Ephemeral agent: ${name} (devnet)`);
|
|
2448
|
-
} else {
|
|
2449
|
-
const names = (await listAgentNames()).slice().sort();
|
|
2450
|
-
if (names.length > 0) {
|
|
2451
|
-
const name = names[0];
|
|
2452
|
-
try {
|
|
2453
|
-
const config = await loadAgentConfig(name);
|
|
2454
|
-
const instance = await buildAgentInstance(name, config);
|
|
2455
|
-
ctx.register(instance);
|
|
2456
|
-
console.error(`Loaded default agent: ${name} (${instance.network})`);
|
|
2457
|
-
} catch (e) {
|
|
2458
|
-
console.error(`Failed to load agent "${name}": ${e.message}`);
|
|
2459
|
-
process.exit(1);
|
|
2460
|
-
}
|
|
2461
|
-
} else {
|
|
2462
|
-
const identity = ElisymIdentity.generate();
|
|
2463
|
-
const client = new ElisymClient({ relays: RELAYS });
|
|
2464
|
-
ctx.register({ client, identity, name: "mcp-agent", network: "devnet", security: {} });
|
|
2465
|
-
console.error("Created ephemeral agent (no persistent identity, devnet).");
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
await startServer(ctx);
|
|
2469
|
-
});
|
|
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) => {
|
|
2471
|
-
const { default: inquirer } = await import('inquirer');
|
|
2472
|
-
if (!name) {
|
|
2473
|
-
const answers = await inquirer.prompt([
|
|
2474
|
-
{
|
|
2475
|
-
type: "input",
|
|
2476
|
-
name: "name",
|
|
2477
|
-
message: "Agent name:",
|
|
2478
|
-
validate: (v) => /^[a-zA-Z0-9_-]+$/.test(v) || "Alphanumeric, _, - only"
|
|
2479
|
-
},
|
|
2480
|
-
{
|
|
2481
|
-
type: "input",
|
|
2482
|
-
name: "description",
|
|
2483
|
-
message: "Description:",
|
|
2484
|
-
default: "Elisym MCP agent"
|
|
2485
|
-
},
|
|
2447
|
+
const { passphrase } = await inquirer.prompt([
|
|
2486
2448
|
{
|
|
2487
|
-
type: "
|
|
2488
|
-
name: "
|
|
2489
|
-
message: "
|
|
2490
|
-
|
|
2491
|
-
choices: ["devnet"],
|
|
2492
|
-
default: "devnet"
|
|
2449
|
+
type: "password",
|
|
2450
|
+
name: "passphrase",
|
|
2451
|
+
message: "Passphrase to encrypt secret keys (leave blank for none):",
|
|
2452
|
+
mask: "*"
|
|
2493
2453
|
}
|
|
2494
2454
|
]);
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
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 });
|
|
2511
2482
|
}
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
console.log(` Encrypted: ${passphrase ? "yes" : "no"}`);
|
|
2534
|
-
console.log(` Config: ~/.elisym/agents/${name}/config.json`);
|
|
2535
|
-
if (passphrase) {
|
|
2536
|
-
console.log(` Note: set ELISYM_PASSPHRASE before launching the MCP server.`);
|
|
2537
|
-
}
|
|
2538
|
-
if (options.install) {
|
|
2539
|
-
await runInstall({ agent: name });
|
|
2540
|
-
}
|
|
2541
|
-
});
|
|
2542
|
-
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) => {
|
|
2543
|
-
if (options.list) {
|
|
2544
|
-
await runList();
|
|
2545
|
-
} else {
|
|
2546
|
-
await runInstall({ client: options.client, agent: options.agent });
|
|
2547
|
-
}
|
|
2548
|
-
});
|
|
2549
|
-
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) => {
|
|
2550
|
-
await runUpdate({ client: options.client, agent: options.agent });
|
|
2551
|
-
});
|
|
2552
|
-
program.command("uninstall").description("Remove elisym from MCP client configs").option("--client <name>", "Specific client").action(async (options) => {
|
|
2553
|
-
await runUninstall({ client: options.client });
|
|
2554
|
-
});
|
|
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
|
+
);
|
|
2555
2504
|
async function toggleFlag(agentName, field, enable) {
|
|
2556
2505
|
const { default: inquirer } = await import('inquirer');
|
|
2557
2506
|
if (enable) {
|
|
@@ -2572,10 +2521,10 @@ async function toggleFlag(agentName, field, enable) {
|
|
|
2572
2521
|
console.log(`Agent "${agentName}" security:`, merged);
|
|
2573
2522
|
console.log("Note: restart the MCP server for changes to take effect on a running session.");
|
|
2574
2523
|
}
|
|
2575
|
-
program.command("enable-withdrawals <agent>").description("Enable SOL withdrawals for a specific agent (interactive confirmation)").action((agent) => toggleFlag(agent, "withdrawals_enabled", true));
|
|
2576
|
-
program.command("disable-withdrawals <agent>").description("Disable SOL withdrawals for a specific agent").action((agent) => toggleFlag(agent, "withdrawals_enabled", false));
|
|
2577
|
-
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));
|
|
2578
|
-
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)));
|
|
2579
2528
|
program.parse();
|
|
2580
2529
|
//# sourceMappingURL=index.js.map
|
|
2581
2530
|
//# sourceMappingURL=index.js.map
|