@ldgr/mcp 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,122 +1,143 @@
1
- # @ldgr/mcp
2
-
3
- LDGR tools for Model Context Protocol clients. LDGR is an independent ledger with a single settlement authority. This package reads public ledger data and prepares unsigned requests for a person's local CLI wallet to review. MCP never signs or submits transactions.
4
-
5
- ## Hosted connection
6
-
7
- Configure a Streamable HTTP MCP connection to `https://mcp.ldgr.ltd/mcp`. Each ledger tool requires `network: "mainnet"` or `network: "testnet"`; asset-specific tools require the exact asset code. Native public IDs are `t1` on mainnet and `t0` on testnet; the signed wire code is `dng`. Mainnet native display is Ledra / LEDRA and testnet is Ledra Testnet / tLEDRA.
8
-
9
- A remote MCP connection cannot install software, access local files, unlock a wallet, or ask for recovery keys. `wallet_setup_instructions` returns commands for the user to run locally.
10
-
11
- ## Local CLI wallet and stdio
12
-
13
- Node.js 20.19 or newer is required. Install the package, then create or import a wallet in your own terminal:
14
-
15
- ```sh
16
- npm install --global @ldgr/mcp@0.1.2
17
- ldgr-mcp wallet create --testnet
18
- # Or: ldgr-mcp wallet import --testnet
19
- ldgr-mcp wallet list
20
- ```
21
-
22
- The package includes `@ldgr/cli`; a separate global CLI installation is unnecessary. Recovery keys and passphrases stay in local interactive prompts. Never paste them into chat, MCP arguments, command-line flags, or environment variables. Omit `--testnet` to create a mainnet wallet. `ldgr-mcp network show` displays the selected network; `ldgr-mcp network set mainnet` or `testnet` switches it using the CLI workflow.
23
-
24
- Without a global installation:
25
-
26
- ```sh
27
- npx --yes --package @ldgr/mcp@latest ldgr-mcp wallet create --testnet
28
- ```
29
-
30
- Configure a local MCP client:
31
-
32
- ```json
33
- {
34
- "mcpServers": {
35
- "ldgr": {
36
- "command": "npx",
37
- "args": ["--yes", "--package", "@ldgr/mcp@latest", "ldgr-mcp"]
38
- }
39
- }
40
- }
41
- ```
42
-
43
- For a custom wallet store, append `--wallet` and its absolute path. Server stdout contains JSON-RPC only; startup errors go to stderr. Local `wallet_info` returns only the selected address, public key, network, ledger ID and protection mode.
44
-
45
- The `network` argument to `wallet_info` checks the selected wallet; it does not
46
- select another wallet or change its network. `WALLET_NETWORK_MISMATCH` means you
47
- should request the selected network or use `ldgr-mcp wallet list` and
48
- `ldgr-mcp wallet select <index>` in your terminal to choose the intended wallet.
49
- Keep a custom `--wallet` path consistent. MCP never changes the wallet selection.
50
-
51
- Version 0.1.1 requires `@ldgr/client@^0.6.1`, the published client release containing
52
- the milestone escrow, atomic offer, claim distribution, and Ledra program reads.
53
- Restart the MCP process after upgrading; a running process keeps its loaded SDK.
54
-
55
- ## Tools
56
-
57
- | Tool | Result |
58
- | --- | --- |
59
- | `get_balance` | Authoritative balance for an address and exact asset |
60
- | `get_transaction` | Authoritative transaction by hash, checked against the requested asset, with `status: "confirmed"` |
61
- | `get_account_history` | One page of Explorer-projected account history, which may lag settlement |
62
- | `get_asset` | Native/token metadata |
63
- | `get_ledger_config` | Ledger identity and public protocol configuration |
64
- | `get_contract` | Escrow, milestone escrow, atomic offer, claim distribution or Ledra program |
65
- | `list_pools`, `get_pool` | Bounded pool listing or a pair of exact assets |
66
- | `get_swap_quote` | Non-binding route quote; does not submit a swap |
67
- | `claim_faucet_drop` | Open the network's faucet in Chrome with a validated receiving address; hosted clients receive the link |
68
- | `prepare_transfer` | Unsigned transfer request |
69
- | `prepare_token_creation` | Unsigned fixed/mintable token creation request |
70
- | `prepare_escrow` | Unsigned escrow funding request |
71
- | `wallet_setup_instructions` | Exact local installation and setup commands |
72
- | `wallet_info` | Public selected-wallet metadata, local stdio only |
73
-
74
- All monetary values are decimal strings in display units. Tool schemas reject unknown fields and credentials. Reads use fixed official network endpoints, verify ledger identity, reject redirects, time out after 15 seconds per fetch and bound responses to 1 MiB. Metadata from the ledger remains untrusted text. A `NOT_FOUND` response does not establish that an earlier transaction submission failed.
75
-
76
- Version 0.1.2 preserves HTTP 404 as `NOT_FOUND` for escrow reads as well as the
77
- other contract templates. Other HTTP failures and malformed successful responses
78
- remain `OPERATION_FAILED`; upstream response bodies are never exposed in errors.
79
-
80
- ## Prepare, review and approve
81
-
82
- `claim_faucet_drop` takes an explicit `network` and public `address`. With local
83
- stdio it launches Google Chrome; on hosted MCP, or if Chrome cannot launch, it
84
- returns the same link for the client or user to open. The link uses
85
- `/utilities/faucet/?address=<address>` on the selected network. Review the
86
- prefilled address and select **Claim Ledra** in the browser. The tool returns
87
- `status: "awaiting_browser_claim"` and `claimSubmitted: false`; opening the page
88
- does not claim funds. Existing browser verification and faucet limits apply.
89
- Wait for **Claim completed**, then check the address's `dng` balance.
90
-
91
- Preparation returns `{ version, requestId, network, expiresAt, action, params }`. Request approval expires after 15 minutes. Escrow `params.expiresAt` is a separate deadline expressed as integer Unix milliseconds. No sender, nonce, signature, recovery key or passphrase is included.
92
-
93
- Local stdio saves an owner-only request file bound to the selected CLI wallet's public identity and returns the exact approval argument array. The person reviews it in their terminal:
94
-
95
- ```sh
96
- ldgr-mcp approve <request-file>
97
- ```
98
-
99
- For hosted MCP, save the returned `request` JSON as a local file, then run:
100
-
101
- ```sh
102
- npx --yes --package @ldgr/mcp@latest ldgr-mcp approve <request-file>
103
- ```
104
-
105
- The CLI validates the request and selected network, resolves fresh asset precision, checks the ledger's current fees and nonces, and presents the transaction for explicit local approval. Do not run approval unattended. Prepared requests may become invalid as balances, fees, configuration, or deadlines change. Reconcile an uncertain submission before trying again.
106
-
107
- ## Embedding
108
-
109
- ```ts
110
- import { createLdgrMcpServer } from "@ldgr/mcp";
111
- const server = createLdgrMcpServer();
112
- await server.connect(yourTransport);
113
- ```
114
-
115
- The root export is browser/Worker compatible and does not import filesystem, process, CLI or signing adapters. It accepts optional `fetch` and `now` injection and explicit local hooks for hosts. Node-only stdio is a separate export:
116
-
117
- ```ts
118
- import { startLdgrMcpStdio } from "@ldgr/mcp/stdio";
119
- await startLdgrMcpStdio({ walletPath: "/absolute/path/wallet.json" });
120
- ```
121
-
122
- Build with `bun run build`, run tests with `bun test`, and check types with `bun run typecheck` from this package.
1
+ # @ldgr/mcp
2
+
3
+ LDGR tools for Model Context Protocol clients. LDGR is an independent ledger with a single settlement authority. This package reads public ledger data and prepares unsigned requests for a person's local CLI wallet to review. MCP never signs or submits transactions.
4
+
5
+ ## Hosted connection
6
+
7
+ Configure a Streamable HTTP MCP connection to `https://mcp.ldgr.ltd/mcp`. Each ledger tool requires `network: "mainnet"` or `network: "testnet"`; asset-specific tools require the exact asset code. Native public IDs are `t1` on mainnet and `t0` on testnet; the signed wire code is `dng`. Mainnet native display is Ledra / LEDRA and testnet is Ledra Testnet / tLEDRA.
8
+
9
+ A remote MCP connection cannot install software, access local files, unlock a wallet, or ask for recovery keys. `wallet_setup_instructions` returns commands for the user to run locally.
10
+
11
+ ## Local CLI wallet and stdio
12
+
13
+ Node.js 20.19 or newer is required. Install the package, then create or import a wallet in your own terminal:
14
+
15
+ ```sh
16
+ npm install --global @ldgr/mcp@0.1.4
17
+ ldgr-mcp wallet create --testnet
18
+ # Or: ldgr-mcp wallet import --testnet
19
+ ldgr-mcp wallet list
20
+ ```
21
+
22
+ From MCP 0.1.3, every local wallet operation runs `@ldgr/cli@latest` through npx with `--prefer-online`, including wallet metadata and unsigned request preparation. Node.js must include npm/npx. npm checks its registry and reuses downloaded packages from its cache; a separate global CLI installation is unnecessary. CLI updates apply on the next wallet operation. Public ledger reads do not launch the CLI. The CLI must be 0.5.1 or newer for the MCP bridge. Recovery keys and passphrases stay in local interactive prompts. Never paste them into chat, MCP arguments, command-line flags, or environment variables. Omit `--testnet` to create a mainnet wallet. `ldgr-mcp network show` displays the selected network; `ldgr-mcp network set mainnet` or `testnet` switches it using the CLI workflow.
23
+
24
+ Without a global installation:
25
+
26
+ ```sh
27
+ npx --yes --package @ldgr/mcp@latest ldgr-mcp wallet create --testnet
28
+ ```
29
+
30
+ Configure a local MCP client:
31
+
32
+ ```json
33
+ {
34
+ "mcpServers": {
35
+ "ldgr": {
36
+ "command": "npx",
37
+ "args": ["--yes", "--package", "@ldgr/mcp@latest", "ldgr-mcp"]
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ For a custom wallet store, append `--wallet` and its absolute path. Server stdout contains JSON-RPC only; startup errors go to stderr. Local `wallet_info` returns only the selected address, public key, network, ledger ID and protection mode.
44
+
45
+ The `network` argument to `wallet_info` checks the selected wallet; it does not
46
+ select another wallet or change its network. `WALLET_NETWORK_MISMATCH` means you
47
+ should request the selected network or use `ldgr-mcp wallet list` and
48
+ `ldgr-mcp wallet select <index>` in your terminal to choose the intended wallet.
49
+ Keep a custom `--wallet` path consistent. MCP never changes the wallet selection.
50
+
51
+ Version 0.1.1 requires `@ldgr/client@^0.6.1`, the published client release containing
52
+ the milestone escrow, atomic offer, claim distribution, and Ledra program reads.
53
+ Restart the MCP process after upgrading; a running process keeps its loaded SDK.
54
+
55
+ ## Tools
56
+
57
+ | Tool | Result |
58
+ | --- | --- |
59
+ | `get_balance` | Authoritative balance for an address and exact asset |
60
+ | `get_transaction` | Authoritative transaction by hash, checked against the requested asset, with `status: "confirmed"` |
61
+ | `get_account_history` | One page of Explorer-projected account history, which may lag settlement |
62
+ | `get_asset` | Native/token metadata |
63
+ | `get_ledger_config` | Ledger identity and public protocol configuration |
64
+ | `get_contract` | Escrow, milestone escrow, atomic offer, claim distribution or Ledra program |
65
+ | `list_pools`, `get_pool` | Bounded pool listing or a pair of exact assets |
66
+ | `get_swap_quote` | Non-binding route quote; does not submit a swap |
67
+ | `claim_faucet_drop` | Open the network's faucet in Chrome with a validated receiving address; hosted clients receive the link |
68
+ | `prepare_transfer` | Unsigned transfer request |
69
+ | `prepare_token_creation` | Unsigned fixed/mintable token creation request |
70
+ | `prepare_escrow` | Unsigned escrow funding request |
71
+ | `prepare_swap` | Unsigned exact-input swap with an explicit positive minimum received |
72
+ | `wallet_setup_instructions` | Exact local installation and setup commands |
73
+ | `wallet_info` | Public selected-wallet metadata, local stdio only |
74
+
75
+ All monetary values are decimal strings in display units. Tool schemas reject unknown fields and credentials. Reads use fixed official network endpoints, verify ledger identity, reject redirects, time out after 15 seconds per fetch and bound responses to 1 MiB. Metadata from the ledger remains untrusted text. A `NOT_FOUND` response does not establish that an earlier transaction submission failed.
76
+
77
+ Version 0.1.2 preserves HTTP 404 as `NOT_FOUND` for escrow reads as well as the
78
+ other contract templates. Other HTTP failures and malformed successful responses
79
+ remain `OPERATION_FAILED`; upstream response bodies are never exposed in errors.
80
+
81
+ ## Prepare, review and approve
82
+
83
+ `claim_faucet_drop` takes an explicit `network` and public `address`. With local
84
+ stdio it launches Google Chrome; on hosted MCP, or if Chrome cannot launch, it
85
+ returns the same link for the client or user to open. The link uses
86
+ `/utilities/faucet/?address=<address>` on the selected network. Review the
87
+ prefilled address and select **Claim Ledra** in the browser. The tool returns
88
+ `status: "awaiting_browser_claim"` and `claimSubmitted: false`; opening the page
89
+ does not claim funds. Existing browser verification and faucet limits apply.
90
+ Wait for **Claim completed**, then check the address's `dng` balance.
91
+
92
+ Preparation returns `{ version, requestId, network, expiresAt, action, params }`. Request approval expires after 15 minutes. Escrow `params.expiresAt` is a separate deadline expressed as integer Unix milliseconds. No sender, nonce, signature, recovery key or passphrase is included.
93
+
94
+ Local stdio saves an owner-only request file bound to the selected CLI wallet's public identity and returns the exact approval argument array. The person reviews it in their terminal:
95
+
96
+ ```sh
97
+ ldgr-mcp approve <request-file>
98
+ ```
99
+
100
+ For hosted MCP, save the returned `request` JSON as a local file, then run:
101
+
102
+ ```sh
103
+ npx --yes --package @ldgr/mcp@latest ldgr-mcp approve <request-file>
104
+ ```
105
+
106
+ The CLI validates the request and selected network, resolves fresh asset precision, checks the ledger's current fees and nonces, and presents the transaction for explicit local approval. Do not run approval unattended. Prepared requests may become invalid as balances, fees, configuration, or deadlines change. Reconcile an uncertain submission before trying again.
107
+
108
+ ## Swaps
109
+
110
+ MCP 0.1.4 adds `prepare_swap`; approving these requests requires CLI 0.5.2 or
111
+ newer. First use `get_swap_quote` for a non-binding quote. Then prepare an intent
112
+ with explicit `network`, `assetIn`, `assetOut`, `amountIn`, and `amountOutMin`.
113
+ Both amounts are positive decimal strings in their respective asset's display
114
+ units. `maxHops` is optional, accepts 1, 2, or 3, and defaults to 3. Native aliases
115
+ `t0`/`t1` resolve to `dng` for the selected network. Identical assets and zero
116
+ minimum output are rejected.
117
+
118
+ Preparation does not select a route or execute a swap. During `approve`, the
119
+ CLI fetches a fresh route, checks the input balance and native fee balance, and
120
+ shows the route, quote, exact input and minimum received. Output returns to the
121
+ selected wallet. The reviewed route and minimum are signed unchanged; the CLI
122
+ never lowers the minimum to make a quote executable. Pool fees are included in
123
+ the quote, while one additional native network fee is charged. Route swaps use
124
+ the `dng` nonce even when swapping between two tokens. The portable request's
125
+ expiry governs local approval; the existing protocol timestamp rules govern
126
+ signed submission validity.
127
+
128
+ ## Embedding
129
+
130
+ ```ts
131
+ import { createLdgrMcpServer } from "@ldgr/mcp";
132
+ const server = createLdgrMcpServer();
133
+ await server.connect(yourTransport);
134
+ ```
135
+
136
+ The root export is browser/Worker compatible and does not import filesystem, process, CLI or signing adapters. It accepts optional `fetch` and `now` injection and explicit local hooks for hosts. Node-only stdio is a separate export:
137
+
138
+ ```ts
139
+ import { startLdgrMcpStdio } from "@ldgr/mcp/stdio";
140
+ await startLdgrMcpStdio({ walletPath: "/absolute/path/wallet.json" });
141
+ ```
142
+
143
+ Build with `bun run build`, run tests with `bun test`, and check types with `bun run typecheck` from this package.
package/dist/cli.js CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- // src/cli.ts
4
- import { spawn as spawn2 } from "node:child_process";
5
- import { fileURLToPath } from "node:url";
3
+ // src/latest-cli.ts
4
+ import { spawn } from "node:child_process";
5
+ import { access, realpath } from "node:fs/promises";
6
+ import { delimiter, dirname, join } from "node:path";
6
7
 
7
8
  // src/commands.ts
8
9
  function walletCommandArgs(input) {
@@ -36,9 +37,94 @@ function walletCommandArgs(input) {
36
37
  return [...input];
37
38
  }
38
39
 
40
+ // src/latest-cli.ts
41
+ var FAILURE = "Latest LDGR CLI could not complete. Check npm connectivity and CLI availability.";
42
+ function npxArguments(envelope) {
43
+ return ["--yes", "--prefer-online", "--offline=false", "--package", "@ldgr/cli@latest", "--", "ldgr", "mcp", envelope];
44
+ }
45
+ async function resolveNpxEntrypoint() {
46
+ const directories = [dirname(process.execPath), ...(process.env.PATH ?? "").split(delimiter).filter(Boolean)];
47
+ const candidates = [];
48
+ const npmPath = process.env.npm_execpath;
49
+ if (npmPath && /[\\/]npm-cli\.js$/.test(npmPath))
50
+ candidates.push(join(dirname(npmPath), "npx-cli.js"));
51
+ for (const directory of directories) {
52
+ candidates.push(join(directory, "node_modules/npm/bin/npx-cli.js"), join(directory, "../lib/node_modules/npm/bin/npx-cli.js"));
53
+ try {
54
+ const target = await realpath(join(directory, "npx"));
55
+ if (target.endsWith("npx-cli.js"))
56
+ candidates.push(target);
57
+ } catch {}
58
+ }
59
+ for (const candidate of candidates) {
60
+ try {
61
+ await access(candidate);
62
+ return candidate;
63
+ } catch {}
64
+ }
65
+ throw new Error("npm/npx is required for the local LDGR CLI. Install Node.js with npm.");
66
+ }
67
+ var runNpx = async (args, options) => {
68
+ const entry = await resolveNpxEntrypoint();
69
+ return new Promise((resolve, reject) => {
70
+ const child = spawn(process.execPath, [entry, ...args], {
71
+ shell: false,
72
+ windowsHide: true,
73
+ stdio: options.interactive ? "inherit" : ["pipe", "pipe", "ignore"]
74
+ });
75
+ let stdout = "", bytes = 0;
76
+ child.once("error", () => reject(new Error(FAILURE)));
77
+ if (!options.interactive) {
78
+ child.stdout.setEncoding("utf8");
79
+ child.stdout.on("data", (chunk) => {
80
+ bytes += Buffer.byteLength(chunk);
81
+ if (bytes > 128 * 1024) {
82
+ child.kill();
83
+ reject(new Error(FAILURE));
84
+ } else
85
+ stdout += chunk;
86
+ });
87
+ child.stdin.on("error", () => reject(new Error(FAILURE)));
88
+ child.stdin.end(options.input);
89
+ }
90
+ child.once("close", (code) => resolve({ code: code ?? 1, stdout }));
91
+ });
92
+ };
93
+ function createLatestCliAdapter(run = runNpx) {
94
+ async function machine(envelope) {
95
+ const input = JSON.stringify(envelope);
96
+ if (Buffer.byteLength(input) > 128 * 1024)
97
+ throw new Error(FAILURE);
98
+ const result = await run(npxArguments("--stdin"), { interactive: false, input });
99
+ try {
100
+ const value = JSON.parse(result.stdout);
101
+ if (result.code !== 0 || !value || typeof value !== "object" || Array.isArray(value) || "error" in value)
102
+ throw new Error;
103
+ return value;
104
+ } catch {
105
+ throw new Error(FAILURE);
106
+ }
107
+ }
108
+ return {
109
+ walletInfo: (walletPath) => machine({ operation: "wallet-info", walletPath }),
110
+ async saveRequest(request, options) {
111
+ const value = await machine({ operation: "save-request", request, ...options });
112
+ if (typeof value.requestPath !== "string" || !Array.isArray(value.approvalArgs) || !value.approvalArgs.every((arg) => typeof arg === "string"))
113
+ throw new Error(FAILURE);
114
+ return { requestPath: value.requestPath, approvalArgs: walletCommandArgs(value.approvalArgs) };
115
+ },
116
+ async command(input) {
117
+ const args = walletCommandArgs(input);
118
+ const envelope = Buffer.from(JSON.stringify({ operation: "command", args })).toString("base64url");
119
+ if (envelope.length > 6000)
120
+ throw new Error("Wallet command arguments are too long");
121
+ return (await run(npxArguments(envelope), { interactive: true })).code;
122
+ }
123
+ };
124
+ }
125
+
39
126
  // src/stdio.ts
40
127
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
41
- import { getMcpWalletInfo, saveMcpRequest, validateMcpRequest } from "@ldgr/cli/mcp";
42
128
 
43
129
  // src/index.ts
44
130
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -163,7 +249,7 @@ class AssetMismatchError extends Error {
163
249
  class WalletNetworkMismatchError extends Error {
164
250
  }
165
251
  function createLdgrMcpServer(options = {}) {
166
- const server = new McpServer({ name: "ldgr", version: "0.1.2" }, { instructions: INSTRUCTIONS });
252
+ const server = new McpServer({ name: "ldgr", version: "0.1.4" }, { instructions: INSTRUCTIONS });
167
253
  const request = readTransport(options.fetch);
168
254
  const now = options.now ?? Date.now;
169
255
  const envelope = (network, source, data) => ({ network, source, data });
@@ -263,12 +349,27 @@ function createLdgrMcpServer(options = {}) {
263
349
  const a = exactAsset(args.assetA, args.network), b = exactAsset(args.assetB, args.network), client = await checkedClient(args.network, request);
264
350
  return envelope(args.network, "ledger", await client.amm.getPoolByPair(a, b));
265
351
  });
266
- tool("get_swap_quote", "Read a non-binding swap quote for explicit input/output assets. Does not prepare, sign or submit a swap.", { network: networkSchema, assetIn: assetSchema, assetOut: assetSchema, amountIn: positiveDecimalSchema }, async (args) => {
352
+ tool("get_swap_quote", "Read a non-binding swap quote for explicit input/output assets. Use prepare_swap with an explicit positive minimum received to prepare an unsigned intent; only human CLI approval can execute it.", { network: networkSchema, assetIn: assetSchema, assetOut: assetSchema, amountIn: positiveDecimalSchema }, async (args) => {
267
353
  const assetIn = exactAsset(args.assetIn, args.network), assetOut = exactAsset(args.assetOut, args.network);
268
354
  checkNativeAmount(assetIn, args.amountIn, args.network);
269
355
  const client = await checkedClient(args.network, request);
270
356
  return envelope(args.network, "ledger", await client.amm.quoteRoute(assetIn, assetOut, args.amountIn, 3));
271
357
  });
358
+ tool("prepare_swap", "Prepare an unsigned exact-input swap for human CLI approval. Specify both assets, amountIn and a positive amountOutMin in decimal display units. The CLI fetches a fresh route of up to maxHops pools and shows it before signing; it never lowers the minimum. Output goes to the selected wallet. Preparation does not quote, unlock, sign or submit.", {
359
+ network: networkSchema,
360
+ assetIn: assetSchema,
361
+ assetOut: assetSchema,
362
+ amountIn: positiveDecimalSchema,
363
+ amountOutMin: positiveDecimalSchema,
364
+ maxHops: z2.number().int().min(1).max(3).default(3)
365
+ }, async (args) => {
366
+ const assetIn = exactAsset(args.assetIn, args.network), assetOut = exactAsset(args.assetOut, args.network);
367
+ if (assetIn === assetOut)
368
+ throw new Error("Swap assets must differ");
369
+ checkNativeAmount(assetIn, args.amountIn, args.network);
370
+ checkNativeAmount(assetOut, args.amountOutMin, args.network);
371
+ return prepared(unsignedRequest(args.network, "swap", { assetIn, assetOut, amountIn: args.amountIn, amountOutMin: args.amountOutMin, maxHops: args.maxHops }, now()));
372
+ }, false);
272
373
  tool("prepare_transfer", "Prepare an unsigned transfer for human review in the local CLI wallet. Does not unlock, sign or submit.", { network: networkSchema, to: addressSchema, asset: assetSchema, amount: positiveDecimalSchema, memo: referenceSchema.optional() }, async (args) => {
273
374
  const to = addressForNetwork(args.to, args.network), asset = exactAsset(args.asset, args.network);
274
375
  checkNativeAmount(asset, args.amount, args.network);
@@ -312,7 +413,7 @@ function createLdgrMcpServer(options = {}) {
312
413
  }, false);
313
414
  tool("wallet_setup_instructions", "Explain user-run CLI wallet installation, creation/import and MCP connection. This tool cannot install software or access local files.", { network: networkSchema }, async (args) => ({
314
415
  network: args.network,
315
- install: "npm install --global @ldgr/mcp@0.1.2",
416
+ install: "npm install --global @ldgr/mcp@0.1.4",
316
417
  node: ">=20.19.0",
317
418
  create: `ldgr-mcp wallet create${args.network === "testnet" ? " --testnet" : ""}`,
318
419
  import: `ldgr-mcp wallet import${args.network === "testnet" ? " --testnet" : ""}`,
@@ -337,10 +438,10 @@ function createLdgrMcpServer(options = {}) {
337
438
  }
338
439
 
339
440
  // src/browser.ts
340
- import { spawn } from "node:child_process";
441
+ import { spawn as spawn2 } from "node:child_process";
341
442
  import { win32 } from "node:path";
342
443
  var launchChrome = (command, args, waitForExit) => new Promise((resolve, reject) => {
343
- const child = spawn(command, [...args], {
444
+ const child = spawn2(command, [...args], {
344
445
  shell: false,
345
446
  stdio: "ignore",
346
447
  windowsHide: true,
@@ -376,13 +477,14 @@ async function openFaucetInBrowser(url, options = {}) {
376
477
 
377
478
  // src/stdio.ts
378
479
  async function startLdgrMcpStdio(options = {}) {
480
+ const cli = createLatestCliAdapter();
379
481
  const server = createLdgrMcpServer({
380
482
  fetch: options.fetch,
381
483
  now: options.now,
382
484
  local: {
383
485
  openBrowser: openFaucetInBrowser,
384
- walletInfo: () => getMcpWalletInfo(options.walletPath),
385
- saveRequest: (request) => saveMcpRequest(validateMcpRequest(request), { walletPath: options.walletPath, directory: options.requestDirectory })
486
+ walletInfo: () => cli.walletInfo(options.walletPath),
487
+ saveRequest: (request) => cli.saveRequest(request, { walletPath: options.walletPath, directory: options.requestDirectory })
386
488
  }
387
489
  });
388
490
  await server.connect(new StdioServerTransport);
@@ -390,7 +492,7 @@ async function startLdgrMcpStdio(options = {}) {
390
492
  }
391
493
 
392
494
  // src/cli.ts
393
- var HELP = `LDGR MCP 0.1.2
495
+ var HELP = `LDGR MCP 0.1.4
394
496
 
395
497
  ldgr-mcp Start local MCP over stdio
396
498
  ldgr-mcp --wallet <path> Start MCP with a selected wallet store
@@ -414,13 +516,7 @@ try {
414
516
  } else if (args.length === 0 || args.length === 2 && args[0] === "--wallet" && args[1] && !args[1].startsWith("-")) {
415
517
  await startLdgrMcpStdio({ walletPath: args[1] });
416
518
  } else {
417
- const forwarded = walletCommandArgs(args);
418
- const entry = fileURLToPath(import.meta.resolve("@ldgr/cli/cli"));
419
- process.exitCode = await new Promise((resolve, reject) => {
420
- const child = spawn2(process.execPath, [entry, ...forwarded], { stdio: "inherit", shell: false });
421
- child.once("error", reject);
422
- child.once("exit", (code) => resolve(code ?? 1));
423
- });
519
+ process.exitCode = await createLatestCliAdapter().command(args);
424
520
  }
425
521
  } catch {
426
522
  console.error("LDGR MCP could not complete. Check --help, the local wallet and request file. Recovery keys and passphrases belong only in local interactive prompts.");
package/dist/index.js CHANGED
@@ -121,7 +121,7 @@ class AssetMismatchError extends Error {
121
121
  class WalletNetworkMismatchError extends Error {
122
122
  }
123
123
  function createLdgrMcpServer(options = {}) {
124
- const server = new McpServer({ name: "ldgr", version: "0.1.2" }, { instructions: INSTRUCTIONS });
124
+ const server = new McpServer({ name: "ldgr", version: "0.1.4" }, { instructions: INSTRUCTIONS });
125
125
  const request = readTransport(options.fetch);
126
126
  const now = options.now ?? Date.now;
127
127
  const envelope = (network, source, data) => ({ network, source, data });
@@ -221,12 +221,27 @@ function createLdgrMcpServer(options = {}) {
221
221
  const a = exactAsset(args.assetA, args.network), b = exactAsset(args.assetB, args.network), client = await checkedClient(args.network, request);
222
222
  return envelope(args.network, "ledger", await client.amm.getPoolByPair(a, b));
223
223
  });
224
- tool("get_swap_quote", "Read a non-binding swap quote for explicit input/output assets. Does not prepare, sign or submit a swap.", { network: networkSchema, assetIn: assetSchema, assetOut: assetSchema, amountIn: positiveDecimalSchema }, async (args) => {
224
+ tool("get_swap_quote", "Read a non-binding swap quote for explicit input/output assets. Use prepare_swap with an explicit positive minimum received to prepare an unsigned intent; only human CLI approval can execute it.", { network: networkSchema, assetIn: assetSchema, assetOut: assetSchema, amountIn: positiveDecimalSchema }, async (args) => {
225
225
  const assetIn = exactAsset(args.assetIn, args.network), assetOut = exactAsset(args.assetOut, args.network);
226
226
  checkNativeAmount(assetIn, args.amountIn, args.network);
227
227
  const client = await checkedClient(args.network, request);
228
228
  return envelope(args.network, "ledger", await client.amm.quoteRoute(assetIn, assetOut, args.amountIn, 3));
229
229
  });
230
+ tool("prepare_swap", "Prepare an unsigned exact-input swap for human CLI approval. Specify both assets, amountIn and a positive amountOutMin in decimal display units. The CLI fetches a fresh route of up to maxHops pools and shows it before signing; it never lowers the minimum. Output goes to the selected wallet. Preparation does not quote, unlock, sign or submit.", {
231
+ network: networkSchema,
232
+ assetIn: assetSchema,
233
+ assetOut: assetSchema,
234
+ amountIn: positiveDecimalSchema,
235
+ amountOutMin: positiveDecimalSchema,
236
+ maxHops: z2.number().int().min(1).max(3).default(3)
237
+ }, async (args) => {
238
+ const assetIn = exactAsset(args.assetIn, args.network), assetOut = exactAsset(args.assetOut, args.network);
239
+ if (assetIn === assetOut)
240
+ throw new Error("Swap assets must differ");
241
+ checkNativeAmount(assetIn, args.amountIn, args.network);
242
+ checkNativeAmount(assetOut, args.amountOutMin, args.network);
243
+ return prepared(unsignedRequest(args.network, "swap", { assetIn, assetOut, amountIn: args.amountIn, amountOutMin: args.amountOutMin, maxHops: args.maxHops }, now()));
244
+ }, false);
230
245
  tool("prepare_transfer", "Prepare an unsigned transfer for human review in the local CLI wallet. Does not unlock, sign or submit.", { network: networkSchema, to: addressSchema, asset: assetSchema, amount: positiveDecimalSchema, memo: referenceSchema.optional() }, async (args) => {
231
246
  const to = addressForNetwork(args.to, args.network), asset = exactAsset(args.asset, args.network);
232
247
  checkNativeAmount(asset, args.amount, args.network);
@@ -270,7 +285,7 @@ function createLdgrMcpServer(options = {}) {
270
285
  }, false);
271
286
  tool("wallet_setup_instructions", "Explain user-run CLI wallet installation, creation/import and MCP connection. This tool cannot install software or access local files.", { network: networkSchema }, async (args) => ({
272
287
  network: args.network,
273
- install: "npm install --global @ldgr/mcp@0.1.2",
288
+ install: "npm install --global @ldgr/mcp@0.1.4",
274
289
  node: ">=20.19.0",
275
290
  create: `ldgr-mcp wallet create${args.network === "testnet" ? " --testnet" : ""}`,
276
291
  import: `ldgr-mcp wallet import${args.network === "testnet" ? " --testnet" : ""}`,
@@ -0,0 +1,25 @@
1
+ import type { UnsignedMcpRequest } from "./requests";
2
+ type RunOptions = {
3
+ readonly interactive: boolean;
4
+ readonly input?: string;
5
+ };
6
+ type Runner = (args: readonly string[], options: RunOptions) => Promise<{
7
+ code: number;
8
+ stdout: string;
9
+ }>;
10
+ /** Only fixed arguments or a base64url envelope reach npm's internal command shell. */
11
+ export declare function npxArguments(envelope: string): string[];
12
+ /** Execute npm's JS entry directly: Windows .cmd launchers require a shell. */
13
+ export declare function resolveNpxEntrypoint(): Promise<string>;
14
+ export declare function createLatestCliAdapter(run?: Runner): {
15
+ walletInfo: (walletPath?: string) => Promise<unknown>;
16
+ saveRequest(request: UnsignedMcpRequest, options: {
17
+ walletPath?: string;
18
+ directory?: string;
19
+ }): Promise<{
20
+ requestPath: string;
21
+ approvalArgs: string[];
22
+ }>;
23
+ command(input: readonly string[]): Promise<number>;
24
+ };
25
+ export {};
@@ -5,7 +5,7 @@ export type UnsignedMcpRequest = {
5
5
  readonly requestId: string;
6
6
  readonly network: McpNetwork;
7
7
  readonly expiresAt: string;
8
- readonly action: "transfer" | "create_token" | "open_escrow";
8
+ readonly action: "transfer" | "create_token" | "open_escrow" | "swap";
9
9
  readonly params: Readonly<Record<string, string | number | undefined>>;
10
10
  };
11
11
  export declare const networkSchema: z.ZodEnum<{
package/dist/stdio.js CHANGED
@@ -1,6 +1,128 @@
1
1
  // src/stdio.ts
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
- import { getMcpWalletInfo, saveMcpRequest, validateMcpRequest } from "@ldgr/cli/mcp";
3
+
4
+ // src/latest-cli.ts
5
+ import { spawn } from "node:child_process";
6
+ import { access, realpath } from "node:fs/promises";
7
+ import { delimiter, dirname, join } from "node:path";
8
+
9
+ // src/commands.ts
10
+ function walletCommandArgs(input) {
11
+ if (input.some((value) => !value || /[\x00-\x1f\x7f]/.test(value)))
12
+ throw new TypeError("Invalid wallet arguments");
13
+ const positional = [];
14
+ let testnet = false, wallet = false;
15
+ for (let index = 0;index < input.length; index++) {
16
+ const value = input[index];
17
+ if (value === "--wallet") {
18
+ if (wallet || !input[index + 1] || input[index + 1].startsWith("-"))
19
+ throw new TypeError("Invalid wallet path");
20
+ wallet = true;
21
+ index++;
22
+ } else if (value === "--testnet") {
23
+ if (testnet)
24
+ throw new TypeError("Repeated network flag");
25
+ testnet = true;
26
+ } else if (value.startsWith("-"))
27
+ throw new TypeError("Unsupported wallet option");
28
+ else
29
+ positional.push(value);
30
+ }
31
+ const [first, second, third] = positional;
32
+ const creation = first === "wallet" && (second === "create" || second === "import");
33
+ if (testnet && !creation)
34
+ throw new TypeError("--testnet only applies to wallet create/import");
35
+ const valid = first === "wallet" && second === "create" && positional.length === 2 || first === "wallet" && second === "import" && (positional.length === 2 || positional.length === 3) || first === "wallet" && second === "list" && positional.length === 2 || first === "wallet" && second === "select" && positional.length === 3 && /^[0-9]+$/.test(third) || first === "approve" && positional.length === 2 || first === "address" && positional.length === 1 || first === "network" && second === "show" && positional.length === 2 || first === "network" && second === "set" && positional.length === 3 && (third === "mainnet" || third === "testnet");
36
+ if (!valid)
37
+ throw new TypeError("Unsupported wallet command");
38
+ return [...input];
39
+ }
40
+
41
+ // src/latest-cli.ts
42
+ var FAILURE = "Latest LDGR CLI could not complete. Check npm connectivity and CLI availability.";
43
+ function npxArguments(envelope) {
44
+ return ["--yes", "--prefer-online", "--offline=false", "--package", "@ldgr/cli@latest", "--", "ldgr", "mcp", envelope];
45
+ }
46
+ async function resolveNpxEntrypoint() {
47
+ const directories = [dirname(process.execPath), ...(process.env.PATH ?? "").split(delimiter).filter(Boolean)];
48
+ const candidates = [];
49
+ const npmPath = process.env.npm_execpath;
50
+ if (npmPath && /[\\/]npm-cli\.js$/.test(npmPath))
51
+ candidates.push(join(dirname(npmPath), "npx-cli.js"));
52
+ for (const directory of directories) {
53
+ candidates.push(join(directory, "node_modules/npm/bin/npx-cli.js"), join(directory, "../lib/node_modules/npm/bin/npx-cli.js"));
54
+ try {
55
+ const target = await realpath(join(directory, "npx"));
56
+ if (target.endsWith("npx-cli.js"))
57
+ candidates.push(target);
58
+ } catch {}
59
+ }
60
+ for (const candidate of candidates) {
61
+ try {
62
+ await access(candidate);
63
+ return candidate;
64
+ } catch {}
65
+ }
66
+ throw new Error("npm/npx is required for the local LDGR CLI. Install Node.js with npm.");
67
+ }
68
+ var runNpx = async (args, options) => {
69
+ const entry = await resolveNpxEntrypoint();
70
+ return new Promise((resolve, reject) => {
71
+ const child = spawn(process.execPath, [entry, ...args], {
72
+ shell: false,
73
+ windowsHide: true,
74
+ stdio: options.interactive ? "inherit" : ["pipe", "pipe", "ignore"]
75
+ });
76
+ let stdout = "", bytes = 0;
77
+ child.once("error", () => reject(new Error(FAILURE)));
78
+ if (!options.interactive) {
79
+ child.stdout.setEncoding("utf8");
80
+ child.stdout.on("data", (chunk) => {
81
+ bytes += Buffer.byteLength(chunk);
82
+ if (bytes > 128 * 1024) {
83
+ child.kill();
84
+ reject(new Error(FAILURE));
85
+ } else
86
+ stdout += chunk;
87
+ });
88
+ child.stdin.on("error", () => reject(new Error(FAILURE)));
89
+ child.stdin.end(options.input);
90
+ }
91
+ child.once("close", (code) => resolve({ code: code ?? 1, stdout }));
92
+ });
93
+ };
94
+ function createLatestCliAdapter(run = runNpx) {
95
+ async function machine(envelope) {
96
+ const input = JSON.stringify(envelope);
97
+ if (Buffer.byteLength(input) > 128 * 1024)
98
+ throw new Error(FAILURE);
99
+ const result = await run(npxArguments("--stdin"), { interactive: false, input });
100
+ try {
101
+ const value = JSON.parse(result.stdout);
102
+ if (result.code !== 0 || !value || typeof value !== "object" || Array.isArray(value) || "error" in value)
103
+ throw new Error;
104
+ return value;
105
+ } catch {
106
+ throw new Error(FAILURE);
107
+ }
108
+ }
109
+ return {
110
+ walletInfo: (walletPath) => machine({ operation: "wallet-info", walletPath }),
111
+ async saveRequest(request, options) {
112
+ const value = await machine({ operation: "save-request", request, ...options });
113
+ if (typeof value.requestPath !== "string" || !Array.isArray(value.approvalArgs) || !value.approvalArgs.every((arg) => typeof arg === "string"))
114
+ throw new Error(FAILURE);
115
+ return { requestPath: value.requestPath, approvalArgs: walletCommandArgs(value.approvalArgs) };
116
+ },
117
+ async command(input) {
118
+ const args = walletCommandArgs(input);
119
+ const envelope = Buffer.from(JSON.stringify({ operation: "command", args })).toString("base64url");
120
+ if (envelope.length > 6000)
121
+ throw new Error("Wallet command arguments are too long");
122
+ return (await run(npxArguments(envelope), { interactive: true })).code;
123
+ }
124
+ };
125
+ }
4
126
 
5
127
  // src/index.ts
6
128
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -125,7 +247,7 @@ class AssetMismatchError extends Error {
125
247
  class WalletNetworkMismatchError extends Error {
126
248
  }
127
249
  function createLdgrMcpServer(options = {}) {
128
- const server = new McpServer({ name: "ldgr", version: "0.1.2" }, { instructions: INSTRUCTIONS });
250
+ const server = new McpServer({ name: "ldgr", version: "0.1.4" }, { instructions: INSTRUCTIONS });
129
251
  const request = readTransport(options.fetch);
130
252
  const now = options.now ?? Date.now;
131
253
  const envelope = (network, source, data) => ({ network, source, data });
@@ -225,12 +347,27 @@ function createLdgrMcpServer(options = {}) {
225
347
  const a = exactAsset(args.assetA, args.network), b = exactAsset(args.assetB, args.network), client = await checkedClient(args.network, request);
226
348
  return envelope(args.network, "ledger", await client.amm.getPoolByPair(a, b));
227
349
  });
228
- tool("get_swap_quote", "Read a non-binding swap quote for explicit input/output assets. Does not prepare, sign or submit a swap.", { network: networkSchema, assetIn: assetSchema, assetOut: assetSchema, amountIn: positiveDecimalSchema }, async (args) => {
350
+ tool("get_swap_quote", "Read a non-binding swap quote for explicit input/output assets. Use prepare_swap with an explicit positive minimum received to prepare an unsigned intent; only human CLI approval can execute it.", { network: networkSchema, assetIn: assetSchema, assetOut: assetSchema, amountIn: positiveDecimalSchema }, async (args) => {
229
351
  const assetIn = exactAsset(args.assetIn, args.network), assetOut = exactAsset(args.assetOut, args.network);
230
352
  checkNativeAmount(assetIn, args.amountIn, args.network);
231
353
  const client = await checkedClient(args.network, request);
232
354
  return envelope(args.network, "ledger", await client.amm.quoteRoute(assetIn, assetOut, args.amountIn, 3));
233
355
  });
356
+ tool("prepare_swap", "Prepare an unsigned exact-input swap for human CLI approval. Specify both assets, amountIn and a positive amountOutMin in decimal display units. The CLI fetches a fresh route of up to maxHops pools and shows it before signing; it never lowers the minimum. Output goes to the selected wallet. Preparation does not quote, unlock, sign or submit.", {
357
+ network: networkSchema,
358
+ assetIn: assetSchema,
359
+ assetOut: assetSchema,
360
+ amountIn: positiveDecimalSchema,
361
+ amountOutMin: positiveDecimalSchema,
362
+ maxHops: z2.number().int().min(1).max(3).default(3)
363
+ }, async (args) => {
364
+ const assetIn = exactAsset(args.assetIn, args.network), assetOut = exactAsset(args.assetOut, args.network);
365
+ if (assetIn === assetOut)
366
+ throw new Error("Swap assets must differ");
367
+ checkNativeAmount(assetIn, args.amountIn, args.network);
368
+ checkNativeAmount(assetOut, args.amountOutMin, args.network);
369
+ return prepared(unsignedRequest(args.network, "swap", { assetIn, assetOut, amountIn: args.amountIn, amountOutMin: args.amountOutMin, maxHops: args.maxHops }, now()));
370
+ }, false);
234
371
  tool("prepare_transfer", "Prepare an unsigned transfer for human review in the local CLI wallet. Does not unlock, sign or submit.", { network: networkSchema, to: addressSchema, asset: assetSchema, amount: positiveDecimalSchema, memo: referenceSchema.optional() }, async (args) => {
235
372
  const to = addressForNetwork(args.to, args.network), asset = exactAsset(args.asset, args.network);
236
373
  checkNativeAmount(asset, args.amount, args.network);
@@ -274,7 +411,7 @@ function createLdgrMcpServer(options = {}) {
274
411
  }, false);
275
412
  tool("wallet_setup_instructions", "Explain user-run CLI wallet installation, creation/import and MCP connection. This tool cannot install software or access local files.", { network: networkSchema }, async (args) => ({
276
413
  network: args.network,
277
- install: "npm install --global @ldgr/mcp@0.1.2",
414
+ install: "npm install --global @ldgr/mcp@0.1.4",
278
415
  node: ">=20.19.0",
279
416
  create: `ldgr-mcp wallet create${args.network === "testnet" ? " --testnet" : ""}`,
280
417
  import: `ldgr-mcp wallet import${args.network === "testnet" ? " --testnet" : ""}`,
@@ -299,10 +436,10 @@ function createLdgrMcpServer(options = {}) {
299
436
  }
300
437
 
301
438
  // src/browser.ts
302
- import { spawn } from "node:child_process";
439
+ import { spawn as spawn2 } from "node:child_process";
303
440
  import { win32 } from "node:path";
304
441
  var launchChrome = (command, args, waitForExit) => new Promise((resolve, reject) => {
305
- const child = spawn(command, [...args], {
442
+ const child = spawn2(command, [...args], {
306
443
  shell: false,
307
444
  stdio: "ignore",
308
445
  windowsHide: true,
@@ -338,13 +475,14 @@ async function openFaucetInBrowser(url, options = {}) {
338
475
 
339
476
  // src/stdio.ts
340
477
  async function startLdgrMcpStdio(options = {}) {
478
+ const cli = createLatestCliAdapter();
341
479
  const server = createLdgrMcpServer({
342
480
  fetch: options.fetch,
343
481
  now: options.now,
344
482
  local: {
345
483
  openBrowser: openFaucetInBrowser,
346
- walletInfo: () => getMcpWalletInfo(options.walletPath),
347
- saveRequest: (request) => saveMcpRequest(validateMcpRequest(request), { walletPath: options.walletPath, directory: options.requestDirectory })
484
+ walletInfo: () => cli.walletInfo(options.walletPath),
485
+ saveRequest: (request) => cli.saveRequest(request, { walletPath: options.walletPath, directory: options.requestDirectory })
348
486
  }
349
487
  });
350
488
  await server.connect(new StdioServerTransport);
package/package.json CHANGED
@@ -1,29 +1,28 @@
1
- {
2
- "name": "@ldgr/mcp",
3
- "version": "0.1.2",
4
- "type": "module",
5
- "types": "./dist/index.d.ts",
6
- "exports": {
7
- ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
8
- "./stdio": { "types": "./dist/stdio.d.ts", "import": "./dist/stdio.js" }
9
- },
10
- "bin": { "ldgr-mcp": "dist/cli.js" },
11
- "files": ["dist", "README.md"],
12
- "publishConfig": { "access": "public" },
13
- "engines": { "node": ">=20.19.0" },
14
- "dependencies": {
15
- "@ldgr/client": "^0.6.1",
16
- "@ldgr/cli": "^0.5.0",
17
- "@ldgr/protocol": "^0.5.0",
18
- "@modelcontextprotocol/sdk": "1.32.2",
19
- "zod": "^4.3.6"
20
- },
21
- "scripts": {
22
- "build": "bun build src/index.ts src/stdio.ts src/cli.ts --outdir dist --target node --format esm --packages external && tsrs -p tsconfig.build.json",
23
- "prepack": "bun run build",
24
- "start": "bun src/cli.ts",
25
- "test": "bun test",
26
- "typecheck": "tsrs --noEmit -p tsconfig.json && tsrs --noEmit -p test/tsconfig.json"
27
- },
28
- "devDependencies": { "tsrs": "0.20.0-ts7.1.0-beta" }
29
- }
1
+ {
2
+ "name": "@ldgr/mcp",
3
+ "version": "0.1.4",
4
+ "type": "module",
5
+ "types": "./dist/index.d.ts",
6
+ "exports": {
7
+ ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" },
8
+ "./stdio": { "types": "./dist/stdio.d.ts", "import": "./dist/stdio.js" }
9
+ },
10
+ "bin": { "ldgr-mcp": "dist/cli.js" },
11
+ "files": ["dist", "README.md"],
12
+ "publishConfig": { "access": "public" },
13
+ "engines": { "node": ">=20.19.0" },
14
+ "dependencies": {
15
+ "@ldgr/client": "^0.6.1",
16
+ "@ldgr/protocol": "^0.5.0",
17
+ "@modelcontextprotocol/sdk": "1.32.2",
18
+ "zod": "^4.3.6"
19
+ },
20
+ "scripts": {
21
+ "build": "bun build src/index.ts src/stdio.ts src/cli.ts --outdir dist --target node --format esm --packages external && tsrs -p tsconfig.build.json",
22
+ "prepack": "bun run build",
23
+ "start": "bun src/cli.ts",
24
+ "test": "bun test",
25
+ "typecheck": "tsrs --noEmit -p tsconfig.json && tsrs --noEmit -p test/tsconfig.json"
26
+ },
27
+ "devDependencies": { "tsrs": "0.20.0-ts7.1.0-beta" }
28
+ }