stelagent 0.0.1 → 0.0.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.
Files changed (3) hide show
  1. package/README.md +63 -43
  2. package/dist/index.mjs +596 -227
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -1,87 +1,105 @@
1
- # @stelagent/cli
1
+ # Stelagent CLI
2
2
 
3
- Modular, agent-first CLI for Stellar — wallet, payments, markets, and DeFi.
3
+ Modular, agent-first CLI for Stellar — wallet, payments, markets, and monitoring.
4
4
 
5
- ## Quick Start
5
+ ## Quick start for AI agents
6
+
7
+ Paste this prompt into any AI agent (Claude Code, Cursor, OpenCode, OpenClaw, Hermes Agent, etc.) and chat naturally:
6
8
 
7
- ```bash
8
- npx @stelagent/cli wallet login -e you@example.com
9
+ ```
10
+ Read https://stelagent.noval.me/AGENTS.md, then set it up for me.
9
11
  ```
10
12
 
11
- Prompts for an OTP sent to your email, then creates or recovers your Stellar wallet.
13
+ The agent reads the skill definition, handles wallet creation, OTP verification, and every Stellar operation on your behalf.
12
14
 
13
- ## Usage
15
+ ### Install the skill
14
16
 
15
17
  ```bash
16
- stelagent <command> [options]
18
+ # Via flins (recommended)
19
+ npx flins@latest add stelagent.noval.me
20
+
21
+ # Via skills.sh
22
+ npx skills add stelagent.noval.me/stelagent-cli
17
23
  ```
18
24
 
19
- ### `wallet login`
25
+ Both commands download the skill into your project's skills directory and wire it into your agent's configuration automatically.
20
26
 
21
- Sign in with email to create or recover your wallet.
27
+ ## Install
22
28
 
23
29
  ```bash
24
- stelagent wallet login -e you@example.com
25
- stelagent wallet login -e you@example.com -n testnet
30
+ npx stelagent@latest <command>
31
+ # or
32
+ bunx stelagent@latest <command>
26
33
  ```
27
34
 
28
- | Flag | Description |
29
- | --------------- | ------------------------------------------ |
30
- | `-e, --email` | Your email address (required) |
31
- | `-n, --network` | `testnet` or `pubnet` (default: `testnet`) |
35
+ No install needed — `npx` always runs the latest published version.
32
36
 
33
- One email always maps to one wallet. Logging in from any device with the same email recovers the same wallet.
37
+ ## Commands
34
38
 
35
- ### `wallet address`
36
-
37
- Show the wallet public key.
39
+ ### Wallet
38
40
 
39
41
  ```bash
40
- stelagent wallet address
42
+ npx stelagent@latest wallet login -e you@example.com # Request OTP
43
+ npx stelagent@latest wallet verify -e you@example.com -o 123456 # Verify OTP
44
+ npx stelagent@latest wallet address # Show public key
45
+ npx stelagent@latest wallet balance # Check balances
46
+ npx stelagent@latest wallet transfer -t GDXXX... -a 10 # Send XLM
47
+ npx stelagent@latest wallet logout # Clear session
41
48
  ```
42
49
 
43
- ### `wallet balance`
44
-
45
- Show token balances.
50
+ ### Payments
46
51
 
47
52
  ```bash
48
- stelagent wallet balance
53
+ npx stelagent@latest send GDXXX... 100 -a USDC:GAXYZ... # Custom asset
54
+ npx stelagent@latest send GDXXX... 50 -a native --memo text:ref-99 # XLM with memo
55
+ npx stelagent@latest pay https://api.example.com/premium # x402 payment
49
56
  ```
50
57
 
51
- ### `wallet transfer`
52
-
53
- Send XLM to another Stellar address.
58
+ ### Account queries
54
59
 
55
60
  ```bash
56
- stelagent wallet transfer -t GDXXX... -a 10
61
+ npx stelagent@latest account details GDXXX... # Balances, signers, thresholds
62
+ npx stelagent@latest account transactions GDXXX... # Transaction history
63
+ npx stelagent@latest account payments GDXXX... # Payment history
64
+ npx stelagent@latest account effects GDXXX... # Effect history
57
65
  ```
58
66
 
59
- | Flag | Description |
60
- | -------------- | --------------------------------- |
61
- | `-t, --to` | Destination public key (required) |
62
- | `-a, --amount` | Amount in XLM (required) |
67
+ ### Assets & fees
63
68
 
64
- ### `wallet logout`
69
+ ```bash
70
+ npx stelagent@latest assets search --code USDC # Search assets
71
+ npx stelagent@latest assets orderbook --selling XLM --buying USDC:G... # Order book
72
+ npx stelagent@latest fee # Fee stats
73
+ ```
65
74
 
66
- Clear the local session.
75
+ ### Streaming
67
76
 
68
77
  ```bash
69
- stelagent wallet logout
78
+ npx stelagent@latest monitor transactions GDXXX... # Stream transactions
79
+ npx stelagent@latest monitor payments GDXXX... # Stream payments
80
+ npx stelagent@latest monitor effects GDXXX... # Stream effects
70
81
  ```
71
82
 
72
- ### `pay <url>`
73
-
74
- Make an x402 payment to access a paywalled resource.
83
+ ### MCP server
75
84
 
76
85
  ```bash
77
- stelagent pay https://api.example.com/premium
86
+ npx stelagent@latest mcp # Start MCP server on stdio
78
87
  ```
79
88
 
80
- If the URL returns HTTP 402, the CLI negotiates payment using the x402 protocol and retries with a signed payment header.
89
+ All commands accept `-n testnet|pubnet` (default: `testnet`) and `-f json|text`. Account and asset queries support `--limit`, `--cursor`, and `--order asc|desc`.
90
+
91
+ ## Authentication
92
+
93
+ Two-step OTP flow:
94
+
95
+ 1. **`wallet login -e <email>`** — sends a one-time code to your email
96
+ 2. **`wallet verify -e <email> -o <code>`** — verifies the code and creates (or recovers) your wallet
97
+
98
+ One email maps to one wallet, recoverable from any device.
81
99
 
82
100
  ## Output
83
101
 
84
- All commands output structured JSON:
102
+ All commands return structured JSON:
85
103
 
86
104
  ```json
87
105
  { "ok": true, "data": { ... } }
@@ -91,9 +109,11 @@ All commands output structured JSON:
91
109
  { "ok": false, "error": "..." }
92
110
  ```
93
111
 
112
+ Use `--format text` for human-readable output.
113
+
94
114
  ## Architecture
95
115
 
96
- Wallets are stored server-side — one wallet per email, recoverable from any device. The CLI only holds a session token locally (`~/.stelagent/session.json`). Secret keys are fetched from the server over HTTPS on each command invocation and never persisted to disk.
116
+ Wallets are stored server-side — one wallet per email, recoverable from any device. The CLI only holds a session token locally (`~/.stelagent/session.json`). Secret keys are fetched over HTTPS when needed and never written to disk.
97
117
 
98
118
  ## Development
99
119
 
package/dist/index.mjs CHANGED
@@ -2,13 +2,15 @@
2
2
 
3
3
  import { defineCommand, runMain } from "citty";
4
4
  import { Result, TaggedError, isTaggedError, matchError, matchErrorPartial } from "better-result";
5
- import { cancel, confirm, isCancel, log, text } from "@clack/prompts";
6
- import pc from "picocolors";
7
5
  import { z } from "zod";
8
6
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
9
7
  import { join } from "node:path";
10
8
  import { homedir } from "node:os";
9
+ import pc from "picocolors";
11
10
  import { Horizon } from "@stellar/stellar-sdk";
11
+ import { cancel, confirm, isCancel } from "@clack/prompts";
12
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
14
  //#region src/lib/audit.ts
13
15
  const STELAGENT_DIR$1 = join(homedir(), ".stelagent");
14
16
  const AUDIT_FILE = join(STELAGENT_DIR$1, "audit.jsonl");
@@ -68,16 +70,6 @@ function printResult(result, format) {
68
70
  if (Result.isOk(result)) printText(result.value);
69
71
  else console.error(pc.red(`Error: ${result.error}`));
70
72
  }
71
- function printData(data, format) {
72
- if (format === "json") {
73
- console.log(JSON.stringify({
74
- ok: true,
75
- data
76
- }, null, 2));
77
- return;
78
- }
79
- printText(data);
80
- }
81
73
  function formatSessionError(err) {
82
74
  return matchErrorPartial(err, {
83
75
  SessionNotFoundError: () => "No active session.",
@@ -293,6 +285,89 @@ function verifyOtp(email, otp) {
293
285
  });
294
286
  }
295
287
  //#endregion
288
+ //#region src/lib/args.ts
289
+ const networkArg = {
290
+ type: "string",
291
+ alias: ["n"],
292
+ description: "Network: testnet or pubnet",
293
+ default: "testnet"
294
+ };
295
+ const formatArg = {
296
+ type: "string",
297
+ alias: ["f"],
298
+ description: "Output format: json or text",
299
+ default: "json"
300
+ };
301
+ function parseNetwork$4(value) {
302
+ if (value === "testnet" || value === "pubnet") return Result.ok(value);
303
+ return Result.err(new InvalidNetworkError({ provided: value }));
304
+ }
305
+ function parseFormat(value) {
306
+ if (value === "json" || value === "text") return value;
307
+ return "json";
308
+ }
309
+ //#endregion
310
+ //#region src/domain/validators.ts
311
+ const stellarPublicKey = z.string().regex(/^G[A-Z2-7]{55}$/, "Invalid Stellar public key. Must start with 'G' followed by 55 alphanumeric characters.");
312
+ const emailSchema = z.string().email("Invalid email address.");
313
+ const amountSchema = z.string().regex(/^\d+(\.\d{1,7})?$/, "Invalid amount. Must be a number with up to 7 decimal places.");
314
+ const assetSchema = z.union([z.literal("native"), z.string().regex(/^[A-Za-z0-9]{1,12}:[A-Z2-7]{56}$/, "Invalid asset format. Use 'native' for XLM or 'CODE:ISSUER' for custom assets.")]);
315
+ const memoSchema = z.string().max(28, "Memo text must be 28 characters or fewer.");
316
+ z.string().min(1, "Cursor must be a non-empty string.");
317
+ const otpSchema = z.string().regex(/^\d{4,8}$/, "OTP must be 4-8 digits.");
318
+ const urlSchema = z.string().url("Invalid URL.");
319
+ z.enum(["testnet", "pubnet"]);
320
+ z.enum(["json", "text"]);
321
+ const limitSchema = z.coerce.number().int().min(1).max(200).default(10);
322
+ z.enum(["asc", "desc"]).default("desc");
323
+ z.coerce.number().int().min(6e4);
324
+ //#endregion
325
+ //#region src/commands/wallet-login.ts
326
+ function formatZodError$11(e) {
327
+ return e.issues.map((issue) => issue.message).join(", ");
328
+ }
329
+ const walletLogin = defineCommand({
330
+ meta: {
331
+ name: "login",
332
+ description: "Request an OTP code be sent to your email"
333
+ },
334
+ args: {
335
+ email: {
336
+ type: "string",
337
+ alias: ["e"],
338
+ description: "Your email address",
339
+ required: true
340
+ },
341
+ network: networkArg,
342
+ format: formatArg
343
+ },
344
+ async run({ args }) {
345
+ const email = String(args.email ?? "");
346
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
347
+ const format = parseFormat(String(args.format ?? "json"));
348
+ if (Result.isError(networkResult)) {
349
+ printResult(Result.err(networkResult.error._tag), "json");
350
+ return;
351
+ }
352
+ const validation = Result.try({
353
+ try: () => emailSchema.parse(email),
354
+ catch: (e) => e
355
+ });
356
+ if (Result.isError(validation)) {
357
+ const msg = validation.error instanceof z.ZodError ? formatZodError$11(validation.error) : validation.error instanceof Error ? validation.error.message : String(validation.error);
358
+ printResult(Result.err(msg), format);
359
+ return;
360
+ }
361
+ await runApp("wallet login", async () => {
362
+ const otpResponseResult = await requestOtp(email);
363
+ if (Result.isError(otpResponseResult)) throw new Error(`Could not reach auth server: ${otpResponseResult.error.cause}`);
364
+ const otpResponse = otpResponseResult.value;
365
+ if (!otpResponse || !otpResponse.ok) throw new Error("Failed to send OTP email");
366
+ return { message: otpResponse.message };
367
+ }, format);
368
+ }
369
+ });
370
+ //#endregion
296
371
  //#region src/services/session.ts
297
372
  const STELAGENT_DIR = join(homedir(), ".stelagent");
298
373
  const SESSION_FILE = join(STELAGENT_DIR, "session.json");
@@ -390,132 +465,6 @@ async function createWallet(network) {
390
465
  });
391
466
  }
392
467
  //#endregion
393
- //#region src/lib/args.ts
394
- const networkArg = {
395
- type: "string",
396
- alias: ["n"],
397
- description: "Network: testnet or pubnet",
398
- default: "testnet"
399
- };
400
- const formatArg = {
401
- type: "string",
402
- alias: ["f"],
403
- description: "Output format: json or text",
404
- default: "json"
405
- };
406
- function parseNetwork(value) {
407
- if (value === "testnet" || value === "pubnet") return Result.ok(value);
408
- return Result.err(new InvalidNetworkError({ provided: value }));
409
- }
410
- function parseFormat(value) {
411
- if (value === "json" || value === "text") return value;
412
- return "json";
413
- }
414
- //#endregion
415
- //#region src/domain/validators.ts
416
- const stellarPublicKey = z.string().regex(/^G[A-Z2-7]{55}$/, "Invalid Stellar public key. Must start with 'G' followed by 55 alphanumeric characters.");
417
- const emailSchema = z.string().email("Invalid email address.");
418
- const amountSchema = z.string().regex(/^\d+(\.\d{1,7})?$/, "Invalid amount. Must be a number with up to 7 decimal places.");
419
- const assetSchema = z.union([z.literal("native"), z.string().regex(/^[A-Za-z0-9]{1,12}:[A-Z2-7]{56}$/, "Invalid asset format. Use 'native' for XLM or 'CODE:ISSUER' for custom assets.")]);
420
- const memoSchema = z.string().max(28, "Memo text must be 28 characters or fewer.");
421
- z.string().min(1, "Cursor must be a non-empty string.");
422
- z.string().regex(/^\d{4,8}$/, "OTP must be 4-8 digits.");
423
- const urlSchema = z.string().url("Invalid URL.");
424
- z.enum(["testnet", "pubnet"]);
425
- z.enum(["json", "text"]);
426
- const limitSchema = z.coerce.number().int().min(1).max(200).default(10);
427
- z.enum(["asc", "desc"]).default("desc");
428
- z.coerce.number().int().min(6e4);
429
- //#endregion
430
- //#region src/commands/wallet-login.ts
431
- function formatZodError$11(e) {
432
- return e.issues.map((issue) => issue.message).join(", ");
433
- }
434
- const walletLogin = defineCommand({
435
- meta: {
436
- name: "login",
437
- description: "Sign in with email to create or recover your wallet"
438
- },
439
- args: {
440
- email: {
441
- type: "string",
442
- alias: ["e"],
443
- description: "Your email address",
444
- required: true
445
- },
446
- network: networkArg,
447
- format: formatArg
448
- },
449
- async run({ args }) {
450
- const email = String(args.email ?? "");
451
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
452
- const format = parseFormat(String(args.format ?? "json"));
453
- if (Result.isError(networkResult)) {
454
- printResult(Result.err(networkResult.error._tag), "json");
455
- return;
456
- }
457
- const network = networkResult.value;
458
- const validation = Result.try({
459
- try: () => emailSchema.parse(email),
460
- catch: (e) => e
461
- });
462
- if (Result.isError(validation)) {
463
- const msg = validation.error instanceof z.ZodError ? formatZodError$11(validation.error) : validation.error instanceof Error ? validation.error.message : String(validation.error);
464
- printResult(Result.err(msg), format);
465
- return;
466
- }
467
- log.message(pc.cyan(`Sending OTP to ${email}...`));
468
- const otpResponseResult = await requestOtp(email);
469
- if (Result.isError(otpResponseResult)) {
470
- const msg = `AuthRequestError: ${otpResponseResult.error.cause}`;
471
- printResult(Result.err(msg), format);
472
- return;
473
- }
474
- const otpResponse = otpResponseResult.value;
475
- if (!otpResponse || !otpResponse.ok) return;
476
- log.success(otpResponse.message);
477
- const otpInput = await text({
478
- message: `Enter the OTP sent to ${email}`,
479
- placeholder: "123456",
480
- validate: (value) => {
481
- if (!value || value.trim().length === 0) return "OTP is required";
482
- }
483
- });
484
- if (isCancel(otpInput)) {
485
- cancel("Login cancelled.");
486
- process.exit(0);
487
- }
488
- const otp = String(otpInput);
489
- await runApp("wallet login verify", async () => {
490
- const verifyResult = await verifyOtp(email, otp);
491
- if (Result.isError(verifyResult)) {
492
- printResult(Result.err(`OTP verification failed: ${verifyResult.error.cause}`), format);
493
- return;
494
- }
495
- const verifyResponse = verifyResult.value;
496
- if (!verifyResponse.verified) {
497
- printResult(Result.err("OTP verification failed."), format);
498
- return;
499
- }
500
- const sessionResult = saveSession(verifyResponse.token, verifyResponse.email);
501
- if (Result.isError(sessionResult)) {
502
- printResult(Result.err(formatSessionError(sessionResult.error)), format);
503
- return;
504
- }
505
- log.message(pc.cyan("Setting up your wallet..."));
506
- const walletResult = await createWallet(network);
507
- if (Result.isError(walletResult)) {
508
- printResult(Result.err(formatWalletError(walletResult.error)), format);
509
- return;
510
- }
511
- const wallet = walletResult.value;
512
- log.success(pc.green(wallet.publicKey ? `Wallet ready: ${wallet.publicKey.slice(0, 8)}...` : "Wallet recovered!"));
513
- printData({ wallet }, format);
514
- return wallet;
515
- }, format);
516
- }
517
- });
518
- //#endregion
519
468
  //#region src/commands/wallet-verify.ts
520
469
  const walletVerify = defineCommand({
521
470
  meta: {
@@ -541,7 +490,7 @@ const walletVerify = defineCommand({
541
490
  async run({ args }) {
542
491
  const email = String(args.email ?? "");
543
492
  const otp = String(args.otp ?? "");
544
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
493
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
545
494
  const format = parseFormat(String(args.format ?? "json"));
546
495
  if (Result.isError(networkResult)) {
547
496
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1182,7 +1131,7 @@ const accountDetails = defineCommand({
1182
1131
  format: formatArg
1183
1132
  },
1184
1133
  async run({ args }) {
1185
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1134
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1186
1135
  const format = parseFormat(String(args.format ?? "json"));
1187
1136
  if (Result.isError(networkResult)) {
1188
1137
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1243,7 +1192,7 @@ const accountTransactions = defineCommand({
1243
1192
  }
1244
1193
  },
1245
1194
  async run({ args }) {
1246
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1195
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1247
1196
  const format = parseFormat(String(args.format ?? "json"));
1248
1197
  if (Result.isError(networkResult)) {
1249
1198
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1312,7 +1261,7 @@ const accountPayments = defineCommand({
1312
1261
  }
1313
1262
  },
1314
1263
  async run({ args }) {
1315
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1264
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1316
1265
  const format = parseFormat(String(args.format ?? "json"));
1317
1266
  if (Result.isError(networkResult)) {
1318
1267
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1386,7 +1335,7 @@ const accountCommand = defineCommand({
1386
1335
  }
1387
1336
  },
1388
1337
  async run({ args }) {
1389
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1338
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1390
1339
  const format = parseFormat(String(args.format ?? "json"));
1391
1340
  if (Result.isError(networkResult)) {
1392
1341
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1458,7 +1407,7 @@ const assetsCommand = defineCommand({
1458
1407
  }
1459
1408
  },
1460
1409
  async run({ args }) {
1461
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1410
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1462
1411
  const format = parseFormat(String(args.format ?? "json"));
1463
1412
  if (Result.isError(networkResult)) {
1464
1413
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1516,7 +1465,7 @@ const assetsCommand = defineCommand({
1516
1465
  }
1517
1466
  },
1518
1467
  async run({ args }) {
1519
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1468
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1520
1469
  const format = parseFormat(String(args.format ?? "json"));
1521
1470
  if (Result.isError(networkResult)) {
1522
1471
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1591,7 +1540,7 @@ const sendCommand = defineCommand({
1591
1540
  format: formatArg
1592
1541
  },
1593
1542
  async run({ args }) {
1594
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1543
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1595
1544
  const format = parseFormat(String(args.format ?? "json"));
1596
1545
  if (Result.isError(networkResult)) {
1597
1546
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1644,7 +1593,7 @@ const feeCommand = defineCommand({
1644
1593
  format: formatArg
1645
1594
  },
1646
1595
  async run({ args }) {
1647
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1596
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1648
1597
  const format = parseFormat(String(args.format ?? "json"));
1649
1598
  if (Result.isError(networkResult)) {
1650
1599
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1684,7 +1633,7 @@ const monitorTransactions = defineCommand({
1684
1633
  }
1685
1634
  },
1686
1635
  async run({ args }) {
1687
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1636
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1688
1637
  const format = parseFormat(String(args.format ?? "json"));
1689
1638
  if (Result.isError(networkResult)) {
1690
1639
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1761,7 +1710,7 @@ const monitorPayments = defineCommand({
1761
1710
  }
1762
1711
  },
1763
1712
  async run({ args }) {
1764
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1713
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1765
1714
  const format = parseFormat(String(args.format ?? "json"));
1766
1715
  if (Result.isError(networkResult)) {
1767
1716
  printResult(Result.err(networkResult.error._tag), "json");
@@ -1818,6 +1767,491 @@ function formatZodError(e) {
1818
1767
  return e.issues.map((issue) => issue.message).join(", ");
1819
1768
  }
1820
1769
  //#endregion
1770
+ //#region src/commands/monitor.ts
1771
+ const monitorCommand = defineCommand({
1772
+ meta: {
1773
+ name: "monitor",
1774
+ description: "Stream live data from Horizon via SSE"
1775
+ },
1776
+ subCommands: {
1777
+ transactions: monitorTransactions,
1778
+ payments: monitorPayments,
1779
+ effects: defineCommand({
1780
+ meta: {
1781
+ name: "effects",
1782
+ description: "Stream effects for an account in real-time"
1783
+ },
1784
+ args: {
1785
+ address: {
1786
+ type: "positional",
1787
+ description: "Stellar public key (G...)",
1788
+ required: true
1789
+ },
1790
+ network: networkArg,
1791
+ format: formatArg,
1792
+ cursor: {
1793
+ type: "string",
1794
+ alias: ["c"],
1795
+ description: "Start from this cursor (default: 'now' for new events)",
1796
+ default: "now"
1797
+ }
1798
+ },
1799
+ async run({ args }) {
1800
+ const networkResult = parseNetwork$4(String(args.network ?? "testnet"));
1801
+ const format = parseFormat(String(args.format ?? "json"));
1802
+ if (Result.isError(networkResult)) {
1803
+ printResult(Result.err(networkResult.error._tag), "json");
1804
+ return;
1805
+ }
1806
+ const network = networkResult.value;
1807
+ const address = String(args.address ?? "");
1808
+ const validation = Result.try({
1809
+ try: () => stellarPublicKey.parse(address),
1810
+ catch: (e) => e
1811
+ });
1812
+ if (Result.isError(validation)) {
1813
+ const msg = validation.error instanceof z.ZodError ? formatZodError(validation.error) : validation.error instanceof Error ? validation.error.message : String(validation.error);
1814
+ printResult(Result.err(msg), format);
1815
+ return;
1816
+ }
1817
+ const cursor = args.cursor === "now" ? void 0 : String(args.cursor);
1818
+ let close;
1819
+ try {
1820
+ close = await runApp("monitor effects", async () => {
1821
+ return streamEffects(address, network, { cursor }, (record) => {
1822
+ console.log(JSON.stringify({
1823
+ ok: true,
1824
+ data: record
1825
+ }));
1826
+ }, (error) => {
1827
+ const message = error instanceof Error ? error.message : String(error);
1828
+ console.log(JSON.stringify({
1829
+ ok: false,
1830
+ error: `Stream error: ${message}`
1831
+ }));
1832
+ });
1833
+ }, format);
1834
+ } catch (e) {
1835
+ const message = e instanceof Error ? e.message : String(e);
1836
+ printResult(Result.err(`Failed to stream effects: ${message}`), "json");
1837
+ return;
1838
+ }
1839
+ console.log(JSON.stringify({
1840
+ ok: true,
1841
+ data: { message: `Streaming effects for ${address} on ${network}... Press Ctrl+C to stop.` }
1842
+ }, null, 2));
1843
+ await new Promise((resolve) => {
1844
+ process.on("SIGINT", () => {
1845
+ close();
1846
+ resolve();
1847
+ });
1848
+ });
1849
+ }
1850
+ })
1851
+ }
1852
+ });
1853
+ //#endregion
1854
+ //#region src/mcp/wallet-tools.ts
1855
+ function ok$3(data) {
1856
+ return { content: [{
1857
+ type: "text",
1858
+ text: JSON.stringify({
1859
+ ok: true,
1860
+ data
1861
+ }, null, 2)
1862
+ }] };
1863
+ }
1864
+ function err$3(message) {
1865
+ return { content: [{
1866
+ type: "text",
1867
+ text: JSON.stringify({
1868
+ ok: false,
1869
+ error: message
1870
+ }, null, 2)
1871
+ }] };
1872
+ }
1873
+ function parseNetwork$3(value) {
1874
+ return value === "pubnet" ? "pubnet" : "testnet";
1875
+ }
1876
+ function formatZodIssues(issues) {
1877
+ return issues.map((i) => i.message).join(", ");
1878
+ }
1879
+ function registerWalletTools(server) {
1880
+ server.registerTool("wallet_login", {
1881
+ description: "Request an OTP code be sent to the user's email. After calling this, use wallet_verify with the code the user provides. Does NOT require an active session.",
1882
+ inputSchema: { email: z.string().describe("User's email address") }
1883
+ }, async ({ email }) => {
1884
+ const validation = emailSchema.safeParse(email);
1885
+ if (!validation.success) return err$3(`Invalid email: ${formatZodIssues(validation.error.issues)}`);
1886
+ const result = await requestOtp(email);
1887
+ if (Result.isError(result)) return err$3(`Could not reach auth server: ${result.error.cause}`);
1888
+ const response = result.value;
1889
+ if (!response || !response.ok) return err$3("Failed to send OTP email");
1890
+ return ok$3({ message: response.message });
1891
+ });
1892
+ server.registerTool("wallet_verify", {
1893
+ description: "Verify an OTP code and create or recover the wallet. The session is saved locally so subsequent tools (wallet_address, wallet_balance, wallet_transfer, etc.) work without re-authentication.",
1894
+ inputSchema: {
1895
+ email: z.string().describe("Must match the email used in wallet_login"),
1896
+ otp: z.string().describe("OTP code from email"),
1897
+ network: z.enum(["testnet", "pubnet"]).default("testnet").describe("Stellar network")
1898
+ }
1899
+ }, async ({ email, otp, network }) => {
1900
+ const emailValidation = emailSchema.safeParse(email);
1901
+ if (!emailValidation.success) return err$3(`Invalid email: ${formatZodIssues(emailValidation.error.issues)}`);
1902
+ const otpValidation = otpSchema.safeParse(otp);
1903
+ if (!otpValidation.success) return err$3(`Invalid OTP: ${formatZodIssues(otpValidation.error.issues)}`);
1904
+ const verifyResult = await verifyOtp(email, otp);
1905
+ if (Result.isError(verifyResult)) return err$3(`OTP verification failed: ${verifyResult.error.cause}`);
1906
+ const verifyResponse = verifyResult.value;
1907
+ if (!verifyResponse.verified) return err$3("OTP verification failed.");
1908
+ const sessionResult = saveSession(verifyResponse.token, verifyResponse.email);
1909
+ if (Result.isError(sessionResult)) return err$3(formatSessionError(sessionResult.error));
1910
+ const walletResult = await createWallet(parseNetwork$3(network));
1911
+ if (Result.isError(walletResult)) return err$3(formatWalletError(walletResult.error));
1912
+ const wallet = walletResult.value;
1913
+ return ok$3({ wallet: {
1914
+ email: wallet.email,
1915
+ publicKey: wallet.publicKey,
1916
+ network: wallet.network,
1917
+ createdAt: wallet.createdAt
1918
+ } });
1919
+ });
1920
+ server.registerTool("wallet_address", {
1921
+ description: "Get the logged-in wallet's public key, network, and email. Requires an active session.",
1922
+ inputSchema: {}
1923
+ }, async () => {
1924
+ const addressResult = await fetchAddress();
1925
+ if (Result.isError(addressResult)) return err$3(formatWalletError(addressResult.error));
1926
+ const addr = addressResult.value;
1927
+ return ok$3({
1928
+ publicKey: addr.publicKey,
1929
+ network: addr.network,
1930
+ email: addr.email
1931
+ });
1932
+ });
1933
+ server.registerTool("wallet_balance", {
1934
+ description: "Get all asset balances for the logged-in wallet. Requires an active session.",
1935
+ inputSchema: {}
1936
+ }, async () => {
1937
+ const walletResult = await fetchWallet();
1938
+ if (Result.isError(walletResult)) return err$3(formatWalletError(walletResult.error));
1939
+ const wallet = walletResult.value;
1940
+ const balancesResult = await getBalances(wallet.publicKey, wallet.network);
1941
+ if (Result.isError(balancesResult)) return err$3(formatStellarError(balancesResult.error));
1942
+ return ok$3({
1943
+ address: wallet.publicKey,
1944
+ email: wallet.email,
1945
+ balances: balancesResult.value
1946
+ });
1947
+ });
1948
+ server.registerTool("wallet_transfer", {
1949
+ description: "Send XLM from the logged-in wallet to another Stellar address. Requires an active session.",
1950
+ inputSchema: {
1951
+ destination: z.string().describe("Destination public key (G...)"),
1952
+ amount: z.string().describe("Amount in XLM (up to 7 decimal places)")
1953
+ }
1954
+ }, async ({ destination, amount }) => {
1955
+ const destValidation = stellarPublicKey.safeParse(destination);
1956
+ if (!destValidation.success) return err$3(`Invalid destination: ${formatZodIssues(destValidation.error.issues)}`);
1957
+ const amountValidation = amountSchema.safeParse(amount);
1958
+ if (!amountValidation.success) return err$3(`Invalid amount: ${formatZodIssues(amountValidation.error.issues)}`);
1959
+ const walletResult = await fetchWallet();
1960
+ if (Result.isError(walletResult)) return err$3(formatWalletError(walletResult.error));
1961
+ const result = await transferXlm(walletResult.value.secretKey, destination, amount, walletResult.value.network);
1962
+ if (Result.isError(result)) return err$3(formatStellarError(result.error));
1963
+ return ok$3(result.value);
1964
+ });
1965
+ server.registerTool("wallet_logout", {
1966
+ description: "Clear the local wallet session.",
1967
+ inputSchema: {}
1968
+ }, async () => {
1969
+ const sessionResult = loadSession();
1970
+ if (Result.isError(sessionResult)) return err$3(formatSessionError(sessionResult.error));
1971
+ const email = sessionResult.value.email;
1972
+ const clearResult = clearSession();
1973
+ if (Result.isError(clearResult)) return err$3(formatSessionError(clearResult.error));
1974
+ return ok$3({
1975
+ loggedOut: true,
1976
+ email
1977
+ });
1978
+ });
1979
+ }
1980
+ //#endregion
1981
+ //#region src/mcp/account-tools.ts
1982
+ function ok$2(data) {
1983
+ return { content: [{
1984
+ type: "text",
1985
+ text: JSON.stringify({
1986
+ ok: true,
1987
+ data
1988
+ }, null, 2)
1989
+ }] };
1990
+ }
1991
+ function err$2(message) {
1992
+ return { content: [{
1993
+ type: "text",
1994
+ text: JSON.stringify({
1995
+ ok: false,
1996
+ error: message
1997
+ }, null, 2)
1998
+ }] };
1999
+ }
2000
+ function parseNetwork$2(value) {
2001
+ return value === "pubnet" ? "pubnet" : "testnet";
2002
+ }
2003
+ const addressParam = z.string().describe("Stellar public key (G...)");
2004
+ const networkParam$1 = z.enum(["testnet", "pubnet"]).default("testnet").describe("Stellar network");
2005
+ const limitParam$1 = z.number().int().min(1).max(200).default(10).describe("Max records to return");
2006
+ const cursorParam = z.string().optional().describe("Pagination cursor");
2007
+ const orderParam = z.enum(["asc", "desc"]).default("desc").describe("Sort order");
2008
+ function registerAccountTools(server) {
2009
+ server.registerTool("account_details", {
2010
+ description: "Get detailed account information from Horizon: balances, signers, thresholds, flags, and more.",
2011
+ inputSchema: {
2012
+ address: addressParam,
2013
+ network: networkParam$1
2014
+ }
2015
+ }, async ({ address, network }) => {
2016
+ const validation = stellarPublicKey.safeParse(address);
2017
+ if (!validation.success) return err$2(`Invalid address: ${validation.error.issues.map((i) => i.message).join(", ")}`);
2018
+ const result = await getAccountDetails(address, parseNetwork$2(network));
2019
+ if (Result.isError(result)) return err$2(formatHorizonError(result.error));
2020
+ return ok$2(result.value);
2021
+ });
2022
+ server.registerTool("account_transactions", {
2023
+ description: "Get transaction history for a Stellar account from Horizon.",
2024
+ inputSchema: {
2025
+ address: addressParam,
2026
+ network: networkParam$1,
2027
+ limit: limitParam$1,
2028
+ cursor: cursorParam,
2029
+ order: orderParam
2030
+ }
2031
+ }, async ({ address, network, limit, cursor, order }) => {
2032
+ const validation = stellarPublicKey.safeParse(address);
2033
+ if (!validation.success) return err$2(`Invalid address: ${validation.error.issues.map((i) => i.message).join(", ")}`);
2034
+ const result = await getTransactions(address, parseNetwork$2(network), {
2035
+ limit,
2036
+ cursor,
2037
+ order
2038
+ });
2039
+ if (Result.isError(result)) return err$2(formatHorizonError(result.error));
2040
+ return ok$2({
2041
+ address,
2042
+ count: result.value.length,
2043
+ transactions: result.value
2044
+ });
2045
+ });
2046
+ server.registerTool("account_payments", {
2047
+ description: "Get payment history for a Stellar account from Horizon.",
2048
+ inputSchema: {
2049
+ address: addressParam,
2050
+ network: networkParam$1,
2051
+ limit: limitParam$1,
2052
+ cursor: cursorParam,
2053
+ order: orderParam
2054
+ }
2055
+ }, async ({ address, network, limit, cursor, order }) => {
2056
+ const validation = stellarPublicKey.safeParse(address);
2057
+ if (!validation.success) return err$2(`Invalid address: ${validation.error.issues.map((i) => i.message).join(", ")}`);
2058
+ const result = await getPayments(address, parseNetwork$2(network), {
2059
+ limit,
2060
+ cursor,
2061
+ order
2062
+ });
2063
+ if (Result.isError(result)) return err$2(formatHorizonError(result.error));
2064
+ return ok$2({
2065
+ address,
2066
+ count: result.value.length,
2067
+ payments: result.value
2068
+ });
2069
+ });
2070
+ server.registerTool("account_effects", {
2071
+ description: "Get effects history for a Stellar account from Horizon.",
2072
+ inputSchema: {
2073
+ address: addressParam,
2074
+ network: networkParam$1,
2075
+ limit: limitParam$1,
2076
+ cursor: cursorParam
2077
+ }
2078
+ }, async ({ address, network, limit, cursor }) => {
2079
+ const validation = stellarPublicKey.safeParse(address);
2080
+ if (!validation.success) return err$2(`Invalid address: ${validation.error.issues.map((i) => i.message).join(", ")}`);
2081
+ const result = await getEffects(address, parseNetwork$2(network), {
2082
+ limit,
2083
+ cursor
2084
+ });
2085
+ if (Result.isError(result)) return err$2(formatHorizonError(result.error));
2086
+ return ok$2({
2087
+ address,
2088
+ count: result.value.length,
2089
+ effects: result.value
2090
+ });
2091
+ });
2092
+ }
2093
+ //#endregion
2094
+ //#region src/mcp/asset-tools.ts
2095
+ function ok$1(data) {
2096
+ return { content: [{
2097
+ type: "text",
2098
+ text: JSON.stringify({
2099
+ ok: true,
2100
+ data
2101
+ }, null, 2)
2102
+ }] };
2103
+ }
2104
+ function err$1(message) {
2105
+ return { content: [{
2106
+ type: "text",
2107
+ text: JSON.stringify({
2108
+ ok: false,
2109
+ error: message
2110
+ }, null, 2)
2111
+ }] };
2112
+ }
2113
+ function parseNetwork$1(value) {
2114
+ return value === "pubnet" ? "pubnet" : "testnet";
2115
+ }
2116
+ const networkParam = z.enum(["testnet", "pubnet"]).default("testnet").describe("Stellar network");
2117
+ const limitParam = z.number().int().min(1).max(200).default(10).describe("Max records to return");
2118
+ function parseAssetInput(input) {
2119
+ if (input === "native" || input === "XLM") return { assetType: "native" };
2120
+ const parts = input.split(":");
2121
+ if (parts.length !== 2) throw new Error(`Invalid asset format: "${input}". Use "native" or "CODE:ISSUER".`);
2122
+ return {
2123
+ assetType: parts[0].length <= 4 ? "credit_alphanum4" : "credit_alphanum12",
2124
+ assetCode: parts[0],
2125
+ assetIssuer: parts[1]
2126
+ };
2127
+ }
2128
+ function registerAssetTools(server) {
2129
+ server.registerTool("assets_search", {
2130
+ description: "Search for assets on Stellar. Filter by asset code and/or issuer.",
2131
+ inputSchema: {
2132
+ network: networkParam,
2133
+ code: z.string().optional().describe("Filter by asset code (e.g. USDC)"),
2134
+ issuer: z.string().optional().describe("Filter by asset issuer (G...)"),
2135
+ limit: limitParam
2136
+ }
2137
+ }, async ({ network, code, issuer, limit }) => {
2138
+ const result = await getAssets(parseNetwork$1(network), {
2139
+ limit,
2140
+ code,
2141
+ issuer
2142
+ });
2143
+ if (Result.isError(result)) return err$1(formatHorizonError(result.error));
2144
+ return ok$1({
2145
+ count: result.value.length,
2146
+ assets: result.value
2147
+ });
2148
+ });
2149
+ server.registerTool("assets_orderbook", {
2150
+ description: "View the order book (bids and asks) for a pair of assets on Stellar.",
2151
+ inputSchema: {
2152
+ selling: z.string().describe("Selling asset: 'native' for XLM, or 'CODE:ISSUER'"),
2153
+ buying: z.string().describe("Buying asset: 'native' for XLM, or 'CODE:ISSUER'"),
2154
+ network: networkParam,
2155
+ limit: limitParam
2156
+ }
2157
+ }, async ({ selling, buying, network, limit }) => {
2158
+ try {
2159
+ const result = await getOrderbook(parseAssetInput(selling), parseAssetInput(buying), parseNetwork$1(network), { limit });
2160
+ if (Result.isError(result)) return err$1(formatHorizonError(result.error));
2161
+ return ok$1(result.value);
2162
+ } catch (e) {
2163
+ return err$1(e instanceof Error ? e.message : String(e));
2164
+ }
2165
+ });
2166
+ server.registerTool("fee_stats", {
2167
+ description: "Get current fee statistics from the Stellar network.",
2168
+ inputSchema: { network: networkParam }
2169
+ }, async ({ network }) => {
2170
+ const result = await getFeeStats(parseNetwork$1(network));
2171
+ if (Result.isError(result)) return err$1(formatHorizonError(result.error));
2172
+ return ok$1(result.value);
2173
+ });
2174
+ }
2175
+ //#endregion
2176
+ //#region src/mcp/payment-tools.ts
2177
+ function ok(data) {
2178
+ return { content: [{
2179
+ type: "text",
2180
+ text: JSON.stringify({
2181
+ ok: true,
2182
+ data
2183
+ }, null, 2)
2184
+ }] };
2185
+ }
2186
+ function err(message) {
2187
+ return { content: [{
2188
+ type: "text",
2189
+ text: JSON.stringify({
2190
+ ok: false,
2191
+ error: message
2192
+ }, null, 2)
2193
+ }] };
2194
+ }
2195
+ function parseNetwork(value) {
2196
+ return value === "pubnet" ? "pubnet" : "testnet";
2197
+ }
2198
+ function registerPaymentTools(server) {
2199
+ server.registerTool("send_payment", {
2200
+ description: "Send a payment (XLM or custom asset) from the logged-in wallet to another Stellar address. Requires an active session.",
2201
+ inputSchema: {
2202
+ destination: z.string().describe("Destination public key (G...)"),
2203
+ amount: z.string().describe("Amount to send (up to 7 decimal places)"),
2204
+ asset: z.string().default("native").describe("Asset: 'native' for XLM, or 'CODE:ISSUER' for custom assets"),
2205
+ memo: z.string().optional().describe("Transaction memo text (max 28 chars)"),
2206
+ network: z.enum(["testnet", "pubnet"]).default("testnet").describe("Stellar network")
2207
+ }
2208
+ }, async ({ destination, amount, asset, memo, network }) => {
2209
+ const destValidation = stellarPublicKey.safeParse(destination);
2210
+ if (!destValidation.success) return err(`Invalid destination: ${destValidation.error.issues.map((i) => i.message).join(", ")}`);
2211
+ const amountValidation = amountSchema.safeParse(amount);
2212
+ if (!amountValidation.success) return err(`Invalid amount: ${amountValidation.error.issues.map((i) => i.message).join(", ")}`);
2213
+ const assetValidation = assetSchema.safeParse(asset);
2214
+ if (!assetValidation.success) return err(`Invalid asset: ${assetValidation.error.issues.map((i) => i.message).join(", ")}`);
2215
+ if (memo !== void 0) {
2216
+ const memoValidation = memoSchema.safeParse(memo);
2217
+ if (!memoValidation.success) return err(`Invalid memo: ${memoValidation.error.issues.map((i) => i.message).join(", ")}`);
2218
+ }
2219
+ const walletResult = await fetchWallet();
2220
+ if (Result.isError(walletResult)) return err(formatWalletError(walletResult.error));
2221
+ const result = await sendPayment(walletResult.value.secretKey, destination, amount, asset, parseNetwork(network), memo);
2222
+ if (Result.isError(result)) return err(formatStellarError(result.error));
2223
+ return ok(result.value);
2224
+ });
2225
+ server.registerTool("pay_url", {
2226
+ description: "Make an X402 micropayment to a URL. The CLI detects 402 responses, signs auth entries, and returns the paid content. Requires an active session.",
2227
+ inputSchema: { url: z.string().describe("URL that requires X402 payment") }
2228
+ }, async ({ url }) => {
2229
+ const result = await pay(url);
2230
+ if (Result.isError(result)) return err(formatPaymentError(result.error));
2231
+ return ok(result.value);
2232
+ });
2233
+ }
2234
+ //#endregion
2235
+ //#region src/mcp/mod.ts
2236
+ async function startMcpServer() {
2237
+ const server = new McpServer({
2238
+ name: "stelagent",
2239
+ version: "0.1.0"
2240
+ }, { instructions: [
2241
+ "Stelagent MCP server for Stellar blockchain operations.",
2242
+ "Authentication flow: 1) wallet_login (sends OTP to email) → 2) wallet_verify (verifies OTP, creates session and wallet). After verify, all other wallet tools work.",
2243
+ "Account, asset, and fee tools are public and require no authentication — only a Stellar public key or network parameter.",
2244
+ "All tools return { ok: true, data } on success or { ok: false, error } on failure.",
2245
+ "Network defaults to 'testnet'. Use 'pubnet' for mainnet."
2246
+ ].join("\n") });
2247
+ registerWalletTools(server);
2248
+ registerAccountTools(server);
2249
+ registerAssetTools(server);
2250
+ registerPaymentTools(server);
2251
+ const transport = new StdioServerTransport();
2252
+ await server.connect(transport);
2253
+ }
2254
+ //#endregion
1821
2255
  //#region src/index.ts
1822
2256
  runMain(defineCommand({
1823
2257
  meta: {
@@ -1832,86 +2266,21 @@ runMain(defineCommand({
1832
2266
  assets: assetsCommand,
1833
2267
  send: sendCommand,
1834
2268
  fee: feeCommand,
1835
- monitor: defineCommand({
2269
+ monitor: monitorCommand,
2270
+ mcp: defineCommand({
1836
2271
  meta: {
1837
- name: "monitor",
1838
- description: "Stream live data from Horizon via SSE"
2272
+ name: "mcp",
2273
+ description: "Start the MCP server on stdio"
1839
2274
  },
1840
- subCommands: {
1841
- transactions: monitorTransactions,
1842
- payments: monitorPayments,
1843
- effects: defineCommand({
1844
- meta: {
1845
- name: "effects",
1846
- description: "Stream effects for an account in real-time"
1847
- },
1848
- args: {
1849
- address: {
1850
- type: "positional",
1851
- description: "Stellar public key (G...)",
1852
- required: true
1853
- },
1854
- network: networkArg,
1855
- format: formatArg,
1856
- cursor: {
1857
- type: "string",
1858
- alias: ["c"],
1859
- description: "Start from this cursor (default: 'now' for new events)",
1860
- default: "now"
1861
- }
1862
- },
1863
- async run({ args }) {
1864
- const networkResult = parseNetwork(String(args.network ?? "testnet"));
1865
- const format = parseFormat(String(args.format ?? "json"));
1866
- if (Result.isError(networkResult)) {
1867
- printResult(Result.err(networkResult.error._tag), "json");
1868
- return;
1869
- }
1870
- const network = networkResult.value;
1871
- const address = String(args.address ?? "");
1872
- const validation = Result.try({
1873
- try: () => stellarPublicKey.parse(address),
1874
- catch: (e) => e
1875
- });
1876
- if (Result.isError(validation)) {
1877
- const msg = validation.error instanceof z.ZodError ? formatZodError(validation.error) : validation.error instanceof Error ? validation.error.message : String(validation.error);
1878
- printResult(Result.err(msg), format);
1879
- return;
1880
- }
1881
- const cursor = args.cursor === "now" ? void 0 : String(args.cursor);
1882
- let close;
1883
- try {
1884
- close = await runApp("monitor effects", async () => {
1885
- return streamEffects(address, network, { cursor }, (record) => {
1886
- console.log(JSON.stringify({
1887
- ok: true,
1888
- data: record
1889
- }));
1890
- }, (error) => {
1891
- const message = error instanceof Error ? error.message : String(error);
1892
- console.log(JSON.stringify({
1893
- ok: false,
1894
- error: `Stream error: ${message}`
1895
- }));
1896
- });
1897
- }, format);
1898
- } catch (e) {
1899
- const message = e instanceof Error ? e.message : String(e);
1900
- printResult(Result.err(`Failed to stream effects: ${message}`), "json");
1901
- return;
1902
- }
1903
- console.log(JSON.stringify({
1904
- ok: true,
1905
- data: { message: `Streaming effects for ${address} on ${network}... Press Ctrl+C to stop.` }
1906
- }, null, 2));
1907
- await new Promise((resolve) => {
1908
- process.on("SIGINT", () => {
1909
- close();
1910
- resolve();
1911
- });
1912
- });
1913
- }
1914
- })
2275
+ args: {},
2276
+ async run() {
2277
+ try {
2278
+ await startMcpServer();
2279
+ } catch (e) {
2280
+ const message = e instanceof Error ? e.message : String(e);
2281
+ console.error(`Failed to start MCP server: ${message}`);
2282
+ process.exit(1);
2283
+ }
1915
2284
  }
1916
2285
  })
1917
2286
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stelagent",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Modular, agent-first CLI for Stellar — wallet, payments, markets, and DeFi",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@clack/prompts": "^1.2.0",
28
+ "@modelcontextprotocol/sdk": "^1.29.0",
28
29
  "@stellar/stellar-sdk": "^14.6.1",
29
30
  "@x402/core": "^2.9.0",
30
31
  "@x402/stellar": "^2.9.0",