@elisym/mcp 0.24.0 → 0.25.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 +2 -2
- package/dist/index.js +85 -82
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,12 +112,12 @@ The bootstrap step is unchanged - the wizard collects the passphrase interactive
|
|
|
112
112
|
| `ELISYM_AGENT` | Load agent from `~/.elisym/<name>/` (or a project-local `.elisym/<name>/`) |
|
|
113
113
|
| `ELISYM_NOSTR_SECRET` | Nostr secret key (hex or nsec) for ephemeral mode |
|
|
114
114
|
| `ELISYM_AGENT_NAME` | Agent display name (default: mcp-agent) |
|
|
115
|
-
| `ELISYM_NETWORK` | Solana network for ephemeral/auto-created identities: `
|
|
115
|
+
| `ELISYM_NETWORK` | Solana network for ephemeral/auto-created identities: `mainnet` (default) or `devnet`. It cannot override a disk-loaded agent - an agent's network is fixed at creation, and a conflicting value fails startup |
|
|
116
116
|
| `ELISYM_PASSPHRASE` | Passphrase for encrypted agent configs (optional) |
|
|
117
117
|
| `ELISYM_ALLOW_WITHDRAWAL` | Set to `1` to override per-agent `security.withdrawals_enabled` flag (CI use) |
|
|
118
118
|
| `ELISYM_ALLOW_AGENT_SWITCH` | Set to `1` to override per-agent `security.agent_switch_enabled` flag |
|
|
119
119
|
|
|
120
|
-
The RPC endpoint is always the public one for each agent's network (`api.devnet.solana.com` / `api.mainnet-beta.solana.com`). The MCP server deliberately does **not** honor `SOLANA_RPC_URL`: one process can host agents on both networks, and a process-wide override could point a devnet agent's payment path at mainnet. Creating a
|
|
120
|
+
The RPC endpoint is always the public one for each agent's network (`api.devnet.solana.com` / `api.mainnet-beta.solana.com`). The MCP server deliberately does **not** honor `SOLANA_RPC_URL`: one process can host agents on both networks, and a process-wide override could point a devnet agent's payment path at mainnet. New agents are mainnet by default and move real funds. Creating a devnet agent to experiment with: `npx @elisym/mcp init <name> --network devnet` (or the `create_agent` tool's `network` parameter); the network is fixed at creation, and `switch_agent` to an agent on the other network is how a session changes network. An agent created before this default flipped keeps the network its YAML records - an upgrade never re-points an existing agent at mainnet.
|
|
121
121
|
|
|
122
122
|
## Usage Examples
|
|
123
123
|
|
package/dist/index.js
CHANGED
|
@@ -22,81 +22,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
22
22
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
23
23
|
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS, getCreateAssociatedTokenIdempotentInstruction, ASSOCIATED_TOKEN_PROGRAM_ADDRESS, getTransferCheckedInstruction } from '@solana-program/token';
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
if (raw === void 0 || raw === "devnet") {
|
|
27
|
-
return "devnet";
|
|
28
|
-
}
|
|
29
|
-
if (raw === "mainnet") {
|
|
30
|
-
return "mainnet";
|
|
31
|
-
}
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Agent "${name}" has unsupported network "${raw}". Expected "devnet" or "mainnet".`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
async function loadAgentConfig(name, passphrase) {
|
|
37
|
-
validateAgentName(name);
|
|
38
|
-
const loaded = await loadAgent(name, process.cwd(), passphrase);
|
|
39
|
-
const solPayment = loaded.yaml.payments.find((entry) => entry.chain === "solana");
|
|
40
|
-
const network = coerceNetwork(solPayment?.network, name);
|
|
41
|
-
return {
|
|
42
|
-
nostrSecretKey: loaded.secrets.nostr_secret_key,
|
|
43
|
-
solanaSecretKey: loaded.secrets.solana_secret_key,
|
|
44
|
-
relays: loaded.yaml.relays.length > 0 ? loaded.yaml.relays : void 0,
|
|
45
|
-
network,
|
|
46
|
-
payments: loaded.yaml.payments.length > 0 ? loaded.yaml.payments.map((payment2) => ({
|
|
47
|
-
chain: payment2.chain,
|
|
48
|
-
network: payment2.network,
|
|
49
|
-
address: payment2.address
|
|
50
|
-
})) : void 0,
|
|
51
|
-
security: {
|
|
52
|
-
withdrawals_enabled: loaded.yaml.security.withdrawals_enabled,
|
|
53
|
-
agent_switch_enabled: loaded.yaml.security.agent_switch_enabled
|
|
54
|
-
},
|
|
55
|
-
encrypted: loaded.encrypted
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
async function saveAgentConfig(name, input) {
|
|
59
|
-
validateAgentName(name);
|
|
60
|
-
const created = await createAgentDir({ target: "home", name, cwd: process.cwd() });
|
|
61
|
-
const network = input.network ?? "devnet";
|
|
62
|
-
try {
|
|
63
|
-
await writeFile(created.paths.yaml, "", { flag: "wx", mode: 420 });
|
|
64
|
-
} catch (error) {
|
|
65
|
-
if (error instanceof Error && "code" in error && error.code === "EEXIST") {
|
|
66
|
-
throw new Error(`Agent "${name}" already exists.`);
|
|
67
|
-
}
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
await writeYamlInitial(created.dir, {
|
|
71
|
-
display_name: void 0,
|
|
72
|
-
description: input.description,
|
|
73
|
-
picture: void 0,
|
|
74
|
-
banner: void 0,
|
|
75
|
-
relays: input.relays,
|
|
76
|
-
payments: input.solanaAddress ? [{ chain: "solana", network, address: input.solanaAddress }] : [],
|
|
77
|
-
llm: void 0,
|
|
78
|
-
security: input.security ?? {}
|
|
79
|
-
});
|
|
80
|
-
await writeExampleSkillTemplate(created.dir);
|
|
81
|
-
await writeSecrets(
|
|
82
|
-
created.dir,
|
|
83
|
-
{
|
|
84
|
-
nostr_secret_key: input.nostrSecretKey,
|
|
85
|
-
solana_secret_key: input.solanaSecretKey
|
|
86
|
-
},
|
|
87
|
-
input.passphrase
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
async function updateAgentSecurity(name, patch, passphrase) {
|
|
91
|
-
const loaded = await loadAgent(name, process.cwd(), passphrase);
|
|
92
|
-
const merged = { ...loaded.yaml.security, ...patch };
|
|
93
|
-
await writeYaml(loaded.dir, { ...loaded.yaml, security: merged });
|
|
94
|
-
return merged;
|
|
95
|
-
}
|
|
96
|
-
async function listAgentNames() {
|
|
97
|
-
const agents = await listAgents(process.cwd());
|
|
98
|
-
return agents.map((agent) => agent.name);
|
|
99
|
-
}
|
|
25
|
+
var DEFAULT_NETWORK = "mainnet";
|
|
100
26
|
function rpcUrlFor(network) {
|
|
101
27
|
return network === "mainnet" ? "https://api.mainnet-beta.solana.com" : "https://api.devnet.solana.com";
|
|
102
28
|
}
|
|
@@ -309,6 +235,83 @@ function takeSpendWarnings(ctx, asset) {
|
|
|
309
235
|
ctx.sessionSpendWarnings.set(key, fired);
|
|
310
236
|
return lines;
|
|
311
237
|
}
|
|
238
|
+
|
|
239
|
+
// src/config.ts
|
|
240
|
+
function coerceNetwork(raw, name) {
|
|
241
|
+
if (raw === void 0 || raw === "devnet") {
|
|
242
|
+
return "devnet";
|
|
243
|
+
}
|
|
244
|
+
if (raw === "mainnet") {
|
|
245
|
+
return "mainnet";
|
|
246
|
+
}
|
|
247
|
+
throw new Error(
|
|
248
|
+
`Agent "${name}" has unsupported network "${raw}". Expected "devnet" or "mainnet".`
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
async function loadAgentConfig(name, passphrase) {
|
|
252
|
+
validateAgentName(name);
|
|
253
|
+
const loaded = await loadAgent(name, process.cwd(), passphrase);
|
|
254
|
+
const solPayment = loaded.yaml.payments.find((entry) => entry.chain === "solana");
|
|
255
|
+
const network = coerceNetwork(solPayment?.network, name);
|
|
256
|
+
return {
|
|
257
|
+
nostrSecretKey: loaded.secrets.nostr_secret_key,
|
|
258
|
+
solanaSecretKey: loaded.secrets.solana_secret_key,
|
|
259
|
+
relays: loaded.yaml.relays.length > 0 ? loaded.yaml.relays : void 0,
|
|
260
|
+
network,
|
|
261
|
+
payments: loaded.yaml.payments.length > 0 ? loaded.yaml.payments.map((payment2) => ({
|
|
262
|
+
chain: payment2.chain,
|
|
263
|
+
network: payment2.network,
|
|
264
|
+
address: payment2.address
|
|
265
|
+
})) : void 0,
|
|
266
|
+
security: {
|
|
267
|
+
withdrawals_enabled: loaded.yaml.security.withdrawals_enabled,
|
|
268
|
+
agent_switch_enabled: loaded.yaml.security.agent_switch_enabled
|
|
269
|
+
},
|
|
270
|
+
encrypted: loaded.encrypted
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async function saveAgentConfig(name, input) {
|
|
274
|
+
validateAgentName(name);
|
|
275
|
+
const created = await createAgentDir({ target: "home", name, cwd: process.cwd() });
|
|
276
|
+
const network = input.network ?? DEFAULT_NETWORK;
|
|
277
|
+
try {
|
|
278
|
+
await writeFile(created.paths.yaml, "", { flag: "wx", mode: 420 });
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (error instanceof Error && "code" in error && error.code === "EEXIST") {
|
|
281
|
+
throw new Error(`Agent "${name}" already exists.`);
|
|
282
|
+
}
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
await writeYamlInitial(created.dir, {
|
|
286
|
+
display_name: void 0,
|
|
287
|
+
description: input.description,
|
|
288
|
+
picture: void 0,
|
|
289
|
+
banner: void 0,
|
|
290
|
+
relays: input.relays,
|
|
291
|
+
payments: input.solanaAddress ? [{ chain: "solana", network, address: input.solanaAddress }] : [],
|
|
292
|
+
llm: void 0,
|
|
293
|
+
security: input.security ?? {}
|
|
294
|
+
});
|
|
295
|
+
await writeExampleSkillTemplate(created.dir);
|
|
296
|
+
await writeSecrets(
|
|
297
|
+
created.dir,
|
|
298
|
+
{
|
|
299
|
+
nostr_secret_key: input.nostrSecretKey,
|
|
300
|
+
solana_secret_key: input.solanaSecretKey
|
|
301
|
+
},
|
|
302
|
+
input.passphrase
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
async function updateAgentSecurity(name, patch, passphrase) {
|
|
306
|
+
const loaded = await loadAgent(name, process.cwd(), passphrase);
|
|
307
|
+
const merged = { ...loaded.yaml.security, ...patch };
|
|
308
|
+
await writeYaml(loaded.dir, { ...loaded.yaml, security: merged });
|
|
309
|
+
return merged;
|
|
310
|
+
}
|
|
311
|
+
async function listAgentNames() {
|
|
312
|
+
const agents = await listAgents(process.cwd());
|
|
313
|
+
return agents.map((agent) => agent.name);
|
|
314
|
+
}
|
|
312
315
|
async function writeFileAtomic(path, content, mode = 384) {
|
|
313
316
|
const tmpPath = `${path}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
|
|
314
317
|
try {
|
|
@@ -1261,8 +1264,8 @@ var CreateAgentSchema = z.object({
|
|
|
1261
1264
|
// customer-mode in 0.1.x and never publishes a NIP-89 capability card,
|
|
1262
1265
|
// so an advertised capability list would be misleading. Provider-mode
|
|
1263
1266
|
// (0.2.0) will reintroduce this field.
|
|
1264
|
-
network: z.enum(["devnet", "mainnet"]).default(
|
|
1265
|
-
|
|
1267
|
+
network: z.enum(["devnet", "mainnet"]).default(DEFAULT_NETWORK).describe(
|
|
1268
|
+
`Solana network this agent is bound to. FIXED AT CREATION: an agent can never change networks - switching networks means creating (or switch_agent-ing to) another agent bound to the other network. Defaults to ${DEFAULT_NETWORK}, where payments move REAL funds; pass "devnet" for an agent to experiment with.`
|
|
1266
1269
|
),
|
|
1267
1270
|
passphrase: z.string().optional().describe("Optional passphrase; if set, secret keys are encrypted at rest."),
|
|
1268
1271
|
activate: z.boolean().default(true)
|
|
@@ -7205,7 +7208,7 @@ program.action(
|
|
|
7205
7208
|
}
|
|
7206
7209
|
const client = new ElisymClient({ relays: RELAYS });
|
|
7207
7210
|
const name = process.env.ELISYM_AGENT_NAME ?? "mcp-agent";
|
|
7208
|
-
const network = envNetwork ??
|
|
7211
|
+
const network = envNetwork ?? DEFAULT_NETWORK;
|
|
7209
7212
|
ctx.register({ client, identity, name, network, security: {} });
|
|
7210
7213
|
console.error(`Ephemeral agent: ${name} (${network})`);
|
|
7211
7214
|
} else {
|
|
@@ -7225,7 +7228,7 @@ program.action(
|
|
|
7225
7228
|
} else {
|
|
7226
7229
|
const identity = ElisymIdentity.generate();
|
|
7227
7230
|
const client = new ElisymClient({ relays: RELAYS });
|
|
7228
|
-
const network = envNetwork ??
|
|
7231
|
+
const network = envNetwork ?? DEFAULT_NETWORK;
|
|
7229
7232
|
ctx.register({ client, identity, name: "mcp-agent", network, security: {} });
|
|
7230
7233
|
console.error(`Created ephemeral agent (no persistent identity, ${network}).`);
|
|
7231
7234
|
}
|
|
@@ -7235,8 +7238,8 @@ program.action(
|
|
|
7235
7238
|
);
|
|
7236
7239
|
program.command("init [name]").description("Create a new agent identity").option("-d, --description <desc>", "Agent description", "Elisym MCP agent").option(
|
|
7237
7240
|
"-n, --network <network>",
|
|
7238
|
-
|
|
7239
|
-
|
|
7241
|
+
`Solana network (devnet or mainnet). Fixed at creation: to change networks later, create a new agent. Defaults to ${DEFAULT_NETWORK}; mainnet payments move real funds.`,
|
|
7242
|
+
DEFAULT_NETWORK
|
|
7240
7243
|
).option("--install", "Also install into MCP clients").option(
|
|
7241
7244
|
"--passphrase <value>",
|
|
7242
7245
|
'Passphrase to encrypt secret keys at rest. Empty string ("") skips encryption. Also reads ELISYM_PASSPHRASE env var. When neither is provided, prompts interactively.'
|
|
@@ -7262,7 +7265,7 @@ program.command("init [name]").description("Create a new agent identity").option
|
|
|
7262
7265
|
name: "network",
|
|
7263
7266
|
message: "Solana network (fixed at creation - create a new agent to change it):",
|
|
7264
7267
|
choices: ["devnet", "mainnet"],
|
|
7265
|
-
default:
|
|
7268
|
+
default: DEFAULT_NETWORK
|
|
7266
7269
|
}
|
|
7267
7270
|
]);
|
|
7268
7271
|
name = answers.name;
|