@stratabook/mcp 0.2.2 → 0.2.5

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
@@ -4,6 +4,11 @@ Official capability-gated MCP access to Strata and Sonar. The server delegates
4
4
  to `@stratabook/sdk`: it follows the live capability catalog and contains no
5
5
  separate quote or execution logic.
6
6
 
7
+ The official hosted endpoint currently exposes market, exact-output, and
8
+ asset-to-asset Sonar quotes, together with quote-bound execution tools.
9
+ Capability gating is a runtime safety check so clients stop if policy changes;
10
+ it does **not** mean quotes are inactive.
11
+
7
12
  ## Local stdio
8
13
 
9
14
  ```sh
@@ -40,6 +45,10 @@ The tools currently available are:
40
45
  - `strata_portfolio_history`
41
46
  - `strata_market_making_status`
42
47
  - `strata_market_making_reputation`
48
+ - `strata_market_making_prepare` — start or stop a Strand/Current from a market label, decimal base size, spread, and duration; no arrays or atom conversion
49
+ - `strata_market_making_submit_and_wait` — submit the externally signed preparation idempotently and wait for matching chain-derived state
50
+ - `strata_market_making_strand_prepare`, `strata_market_making_strand_submit`
51
+ - `strata_market_making_current_prepare`, `strata_market_making_current_submit`
43
52
  - `strata_vault_status`
44
53
  - `strata_vault_setup`, `strata_vault_deposit`, `strata_vault_withdraw`, `strata_vault_delegate`, `strata_vault_policy`, `strata_vault_pause` — prepare owner actions with Strata as sponsored fee payer
45
54
  - `strata_vault_submit` — submit the owner-signed preparation; Strata pays and broadcasts
@@ -50,12 +59,12 @@ The tools currently available are:
50
59
  - `strata_referral_claim` — prepare externally signable consent or submit the signed claim
51
60
  - `strata_bugs`
52
61
  - `strata_bug_submit` — prepare externally signable bytes or submit the signed report
53
- - `strata_markets`, when `markets.read` is enabled for MCP
54
- - `strata_quote` and `strata_exact_output_quote` (spend X / receive at least Y), when market quotes are enabled for MCP
55
- - `strata_swap_quote`, when catalog-asset swap quotes are enabled for MCP
56
- - `strata_execution_challenge`, when `trade.prepare` is enabled for MCP
57
- - `strata_execution_prepare`, when `trade.prepare` is enabled for MCP
58
- - `strata_execution_submit`, when `trade.submit` is enabled for MCP
62
+ - `strata_markets`
63
+ - `strata_quote` and `strata_exact_output_quote` — live market quotes (spend X / receive at least Y)
64
+ - `strata_swap_quote` live catalog-asset swap quotes
65
+ - `strata_execution_challenge`
66
+ - `strata_execution_prepare`
67
+ - `strata_execution_submit`
59
68
  - `strata_execution_status` — recover a durable immediate-execution receipt
60
69
  - `strata_order_challenge`, when `orders.prepare` is enabled for MCP
61
70
  - `strata_order_prepare`, when `orders.prepare` is enabled for MCP
@@ -72,7 +81,29 @@ available as `strata://platform-graph/v2`.
72
81
 
73
82
  The tool list follows the live public policy. Every call rechecks that policy,
74
83
  so a disabled capability stops immediately even if a client cached an older
75
- tool list.
84
+ tool list. Tool discovery from the connected server remains authoritative for
85
+ self-hosted deployments or any future policy change.
86
+
87
+ For normal maker operation, use two calls:
88
+
89
+ 1. Call `strata_market_making_prepare` with `action: "start"`, a label such as
90
+ `SOL/USDC`, `product: "current"` or `"strand"`, `spreadBps`, a decimal size
91
+ such as `0.01 SOL`, and the maker wallet. Duration defaults to ten minutes
92
+ and levels default to three.
93
+ 2. Verify and sign only `prepared.transaction_base64` in the external wallet,
94
+ then pass it and `prepared.maker_control_id` to
95
+ `strata_market_making_submit_and_wait`.
96
+
97
+ The second call returns only after Strata's chain-derived maker status matches
98
+ the exact product settings. Use `action: "stop"` through the same pair. The
99
+ older product-specific tools remain available for strategies that deliberately
100
+ manage every low-level array and safety field.
101
+
102
+ For maker funding, initialize the market Vault if needed, activate the Strand
103
+ or Current, then deposit with `strata_vault_deposit`. The market keeps that
104
+ available collateral while a control is live and returns it after the final
105
+ control is disabled, exhausted, expired, or cancelled. Current follows Strata's
106
+ live mark and needs no separate publisher transaction.
76
107
 
77
108
  ## Hosted Streamable HTTP
78
109
 
@@ -6,6 +6,74 @@ import { STRATA_AGENT_HARNESS, STRATA_AGENT_HARNESS_INSTRUCTIONS, STRATA_AGENT_H
6
6
  import { SERVER_VERSION } from "./version.js";
7
7
  const REFRESH_INTERVAL_MS = 5_000;
8
8
  export const STRATA_PLATFORM_GRAPH_URI = "strata://platform-graph/v2";
9
+ const LEGACY_TOOL_CAPABILITIES = {
10
+ strata_markets: { ids: ["markets.read"] },
11
+ strata_quote: { ids: ["quotes.read"] },
12
+ strata_exact_output_quote: { ids: ["quotes.read"] },
13
+ strata_execution_challenge: { ids: ["trade.prepare"] },
14
+ strata_execution_prepare: { ids: ["trade.prepare"] },
15
+ strata_execution_submit: { ids: ["trade.submit"] },
16
+ strata_order_challenge: { ids: ["orders.prepare"] },
17
+ strata_order_prepare: { ids: ["orders.prepare"] },
18
+ strata_order_submit: { ids: ["orders.submit"] },
19
+ strata_order_status: { ids: ["orders.submit"] },
20
+ strata_market_making_strand_prepare: { ids: ["mm.strand.manage"] },
21
+ strata_market_making_strand_submit: { ids: ["mm.strand.manage"] },
22
+ strata_market_making_current_prepare: { ids: ["mm.current.manage"] },
23
+ strata_market_making_current_submit: { ids: ["mm.current.manage"] },
24
+ strata_market_making_prepare: { ids: ["mm.strand.manage", "mm.current.manage"], match: "any" },
25
+ strata_market_making_submit_and_wait: { ids: ["mm.strand.manage", "mm.current.manage"], match: "any" },
26
+ strata_execute_quote: { ids: ["trade.submit"] },
27
+ strata_order_execute: { ids: ["orders.prepare", "orders.submit"] },
28
+ };
29
+ const PLATFORM_TOOL_CAPABILITIES = {
30
+ strata_status: { ids: ["platform.status.read"] },
31
+ strata_platform_graph: { ids: ["graphs.read"] },
32
+ strata_candles: { ids: ["market_data.candles.read"] },
33
+ strata_marks: { ids: ["market_data.marks.read"] },
34
+ strata_quote: { ids: ["quotes.market.read"] },
35
+ strata_swap_quote: { ids: ["quotes.swap.read"] },
36
+ strata_exact_output_quote: { ids: ["quotes.exact_output.read"] },
37
+ strata_execution_challenge: { ids: ["execution.prepare"] },
38
+ strata_execution_prepare: { ids: ["execution.prepare"] },
39
+ strata_execution_submit: { ids: ["execution.submit"] },
40
+ strata_execution_status: { ids: ["execution.status.read"] },
41
+ strata_order_challenge: { ids: ["orders.prepare"] },
42
+ strata_order_prepare: { ids: ["orders.prepare"] },
43
+ strata_order_submit: { ids: ["orders.submit"] },
44
+ strata_twap_challenge: { ids: ["algos.twap.place"] },
45
+ strata_twap_cancel: { ids: ["algos.twap.cancel"] },
46
+ strata_twap_prepare: { ids: ["algos.twap.place", "algos.twap.cancel"], match: "any" },
47
+ strata_twap_submit: { ids: ["algos.twap.place", "algos.twap.cancel"], match: "any" },
48
+ strata_twaps: { ids: ["algos.twap.read"] },
49
+ strata_portfolio: { ids: ["portfolio.read"] },
50
+ strata_portfolio_history: { ids: ["portfolio.history.read"] },
51
+ strata_vault_status: { ids: ["vault.status.read"] },
52
+ strata_vault_setup: { ids: ["vault.setup"] },
53
+ strata_vault_deposit: { ids: ["vault.deposit"] },
54
+ strata_vault_withdraw: { ids: ["vault.withdraw"] },
55
+ strata_vault_delegate: { ids: ["vault.delegate.manage"] },
56
+ strata_vault_policy: { ids: ["vault.policy.manage"] },
57
+ strata_vault_pause: { ids: ["vault.pause"] },
58
+ strata_vault_submit: { ids: ["vault.relay"] },
59
+ strata_vault_submission: { ids: ["vault.relay"] },
60
+ strata_market_making_status: { ids: ["mm.status.read"] },
61
+ strata_market_making_reputation: { ids: ["mm.reputation.read"] },
62
+ strata_market_making_strand_prepare: { ids: ["mm.strand.manage"] },
63
+ strata_market_making_strand_submit: { ids: ["mm.strand.manage"] },
64
+ strata_market_making_current_prepare: { ids: ["mm.current.manage"] },
65
+ strata_market_making_current_submit: { ids: ["mm.current.manage"] },
66
+ strata_market_making_prepare: { ids: ["mm.strand.manage", "mm.current.manage"], match: "any" },
67
+ strata_market_making_submit_and_wait: { ids: ["mm.strand.manage", "mm.current.manage"], match: "any" },
68
+ strata_rewards: { ids: ["rewards.read"] },
69
+ strata_referrals: { ids: ["referrals.read"] },
70
+ strata_referral_link: { ids: ["referrals.link"] },
71
+ strata_referral_claim: { ids: ["referrals.claim"] },
72
+ strata_bug_submit: { ids: ["bugs.submit"] },
73
+ strata_bugs: { ids: ["bugs.read"] },
74
+ strata_order_execute: { ids: ["orders.prepare", "orders.submit"] },
75
+ strata_twap_execute: { ids: ["algos.twap.place", "algos.twap.cancel"], match: "any" },
76
+ };
9
77
  export function capabilityAvailable(catalog, id) {
10
78
  return catalog.capabilities.some((capability) => capability.id === id
11
79
  && capability.default_enabled
@@ -184,12 +252,15 @@ export async function createStrataMcpServer(options = {}) {
184
252
  if (initialCatalog.contract_version !== STRATA_AGENT_HARNESS.contract_version) {
185
253
  throw new Error("agent harness and live contract versions differ");
186
254
  }
255
+ const initialPlatformCatalog = await platformClient.discovery.read();
187
256
  const server = new McpServer({
188
257
  name: "strata",
189
258
  version: SERVER_VERSION,
190
259
  }, {
191
260
  instructions: STRATA_AGENT_HARNESS_INSTRUCTIONS,
192
261
  });
262
+ const { registerTool, handles: registeredTools } = trackedToolRegistrar(server);
263
+ const makerQuickstartPreparations = new Map();
193
264
  server.registerResource("strata_agent_harness", STRATA_AGENT_HARNESS_URI, {
194
265
  title: "Strata Agent Harness",
195
266
  description: "Canonical capability-gated first-run workflow for Strata agents.",
@@ -251,7 +322,7 @@ export async function createStrataMcpServer(options = {}) {
251
322
  },
252
323
  ],
253
324
  }));
254
- server.registerTool("strata_capabilities", {
325
+ registerTool("strata_capabilities", {
255
326
  title: "Strata capabilities",
256
327
  description: "See which Strata features are currently available to MCP clients.",
257
328
  annotations: {
@@ -261,7 +332,7 @@ export async function createStrataMcpServer(options = {}) {
261
332
  openWorldHint: true,
262
333
  },
263
334
  }, async () => toolResult(await client.capabilities(), "Current Strata capabilities."));
264
- server.registerTool("strata_action_graph", {
335
+ registerTool("strata_action_graph", {
265
336
  title: "Strata action graph",
266
337
  description: "Discover live operations, required capabilities, transition conditions, and external signing boundaries.",
267
338
  annotations: {
@@ -271,7 +342,7 @@ export async function createStrataMcpServer(options = {}) {
271
342
  openWorldHint: true,
272
343
  },
273
344
  }, async () => toolResult(await client.actionGraph(), "Current Strata action graph."));
274
- server.registerTool("strata_platform_graph", {
345
+ registerTool("strata_platform_graph", {
275
346
  title: "Strata platform graph",
276
347
  description: "Discover every public module, entity relationship, operation binding, workflow, and live availability gate.",
277
348
  annotations: {
@@ -285,7 +356,7 @@ export async function createStrataMcpServer(options = {}) {
285
356
  const liveOperations = graph.operations.filter((operation) => operation.available).length;
286
357
  return toolResult(graph, `${liveOperations} of ${graph.operations.length} mapped Strata operations are currently live.`);
287
358
  });
288
- server.registerTool("strata_market_making_status", {
359
+ registerTool("strata_market_making_status", {
289
360
  title: "Read Strata maker status",
290
361
  description: "A maker's products, live exposure, health, and kill state in one market — public by wallet address, no signature.",
291
362
  inputSchema: {
@@ -302,7 +373,7 @@ export async function createStrataMcpServer(options = {}) {
302
373
  const response = await platformClient.marketMaking.status(marketId, walletAddress);
303
374
  return toolResult(response, `${response.active_products} active maker products; reconcile intent, Strand, Current, signed-quote, and dead-man state before changing exposure.`);
304
375
  });
305
- server.registerTool("strata_market_making_reputation", {
376
+ registerTool("strata_market_making_reputation", {
306
377
  title: "Read Strata maker reputation",
307
378
  description: "A maker's reliability, participation, tier, and signed-quote eligibility in one market — public by wallet address, no signature.",
308
379
  inputSchema: {
@@ -316,7 +387,7 @@ export async function createStrataMcpServer(options = {}) {
316
387
  openWorldHint: true,
317
388
  },
318
389
  }, async ({ marketId, walletAddress }) => toolResult(await platformClient.marketMaking.reputation(marketId, walletAddress), "Maker reputation record. Use tier_progress and signed_quote_stream_eligible before choosing a maker transport."));
319
- server.registerTool("strata_status", {
390
+ registerTool("strata_status", {
320
391
  title: "Strata status",
321
392
  description: "Read product-level readiness and the number of currently live mapped operations.",
322
393
  annotations: {
@@ -329,7 +400,7 @@ export async function createStrataMcpServer(options = {}) {
329
400
  const status = await platformClient.discovery.status();
330
401
  return toolResult(status, `Strata is ${status.status}; ${status.available_operations} mapped operations are live.`);
331
402
  });
332
- server.registerTool("strata_candles", {
403
+ registerTool("strata_candles", {
333
404
  title: "Strata candles",
334
405
  description: "Read bounded time-bucketed candles for one opaque Strata market ID.",
335
406
  inputSchema: {
@@ -352,7 +423,7 @@ export async function createStrataMcpServer(options = {}) {
352
423
  });
353
424
  return toolResult(candles, `${candles.candles.length} Strata candles returned.`);
354
425
  });
355
- server.registerTool("strata_marks", {
426
+ registerTool("strata_marks", {
356
427
  title: "Strata mark",
357
428
  description: "Read the current customer-facing reference price for one opaque market ID.",
358
429
  inputSchema: {
@@ -368,7 +439,7 @@ export async function createStrataMcpServer(options = {}) {
368
439
  const mark = await platformClient.marketData.mark(marketId);
369
440
  return toolResult(mark, mark.stale ? "Strata mark is stale." : "Current Strata mark.");
370
441
  });
371
- server.registerTool("strata_book", {
442
+ registerTool("strata_book", {
372
443
  title: "Strata order book",
373
444
  description: "Read the executable order book for one opaque market ID: bids and asks, one size per price "
374
445
  + "level. Top of book is the best bid and ask.",
@@ -392,7 +463,7 @@ export async function createStrataMcpServer(options = {}) {
392
463
  const book = await platformClient.books.snapshot(marketId, depth === undefined ? {} : { depth });
393
464
  return toolResult(book, `Book for ${marketId}: ${book.bids.length} bid / ${book.asks.length} ask levels at sequence ${book.sequence}.`);
394
465
  });
395
- server.registerTool("strata_bbo", {
466
+ registerTool("strata_bbo", {
396
467
  title: "Strata best bid/ask",
397
468
  description: "Read the current best bid and best ask (top of book) for one opaque market ID.",
398
469
  inputSchema: {
@@ -408,7 +479,7 @@ export async function createStrataMcpServer(options = {}) {
408
479
  const bbo = await platformClient.books.bestBidAsk(marketId);
409
480
  return toolResult(bbo, `BBO for ${marketId}: bid ${bbo.best_bid?.price_atoms ?? "—"} / ask ${bbo.best_ask?.price_atoms ?? "—"}.`);
410
481
  });
411
- server.registerTool("strata_trades", {
482
+ registerTool("strata_trades", {
412
483
  title: "Strata recent trades",
413
484
  description: "Read recent anonymized prints for one opaque market ID: price, size, side, and time.",
414
485
  inputSchema: {
@@ -431,7 +502,7 @@ export async function createStrataMcpServer(options = {}) {
431
502
  const trades = await platformClient.books.trades(marketId, limit === undefined ? {} : { limit });
432
503
  return toolResult(trades, `${trades.trades.length} recent prints for ${marketId}.`);
433
504
  });
434
- server.registerTool("strata_execution_status", {
505
+ registerTool("strata_execution_status", {
435
506
  title: "Strata execution status",
436
507
  description: "Recover prepared state or a restart-durable confirmed execution receipt.",
437
508
  inputSchema: {
@@ -448,7 +519,7 @@ export async function createStrataMcpServer(options = {}) {
448
519
  const receipt = await platformClient.executions.status(marketId, executionId);
449
520
  return toolResult(receipt, `Execution is ${receipt.status}.`);
450
521
  });
451
- server.registerTool("strata_twaps", {
522
+ registerTool("strata_twaps", {
452
523
  title: "Strata TWAPs",
453
524
  description: "Read sanitized progress and terminal receipts for wallet-owned TWAP schedules.",
454
525
  inputSchema: {
@@ -465,7 +536,7 @@ export async function createStrataMcpServer(options = {}) {
465
536
  const response = await platformClient.algos.twaps(marketId, walletAddress);
466
537
  return toolResult(response, `${response.twaps.length} TWAP schedules returned.`);
467
538
  });
468
- server.registerTool("strata_twap_challenge", {
539
+ registerTool("strata_twap_challenge", {
469
540
  title: "Prepare a Strata TWAP authorization",
470
541
  description: "Request exact external-signing bytes for a bounded TWAP schedule.",
471
542
  inputSchema: {
@@ -499,7 +570,7 @@ export async function createStrataMcpServer(options = {}) {
499
570
  });
500
571
  return toolResult(response, "Sign the returned authorization payload externally.");
501
572
  });
502
- server.registerTool("strata_twap_cancel", {
573
+ registerTool("strata_twap_cancel", {
503
574
  title: "Prepare Strata TWAP cancellation",
504
575
  description: "Request exact external-signing bytes to cancel one active owned TWAP.",
505
576
  inputSchema: {
@@ -523,7 +594,7 @@ export async function createStrataMcpServer(options = {}) {
523
594
  });
524
595
  return toolResult(response, "Sign the returned cancellation payload externally.");
525
596
  });
526
- server.registerTool("strata_twap_prepare", {
597
+ registerTool("strata_twap_prepare", {
527
598
  title: "Prepare Strata TWAP transaction",
528
599
  description: "Prepare a canonical TWAP transaction. One signature: pass the action itself (place fields or twapId to cancel) and sign only the returned transaction. (A challengeId + authorizationSignature is still accepted.)",
529
600
  inputSchema: {
@@ -596,7 +667,7 @@ export async function createStrataMcpServer(options = {}) {
596
667
  const response = await platformClient.algos.prepare(input.marketId, { operation });
597
668
  return toolResult(response, "One signature: verify this canonical transaction, then sign it externally with the session key and submit.");
598
669
  });
599
- server.registerTool("strata_twap_submit", {
670
+ registerTool("strata_twap_submit", {
600
671
  title: "Submit Strata TWAP transaction",
601
672
  description: "Submit the exact externally signed TWAP transaction idempotently.",
602
673
  inputSchema: {
@@ -619,7 +690,7 @@ export async function createStrataMcpServer(options = {}) {
619
690
  });
620
691
  return toolResult(response, `TWAP action submitted as ${response.signature}.`);
621
692
  });
622
- server.registerTool("strata_portfolio", {
693
+ registerTool("strata_portfolio", {
623
694
  title: "Strata account",
624
695
  description: "The whole account in one public read, by wallet address: balances (total / available / locked, exact USD), positions, open orders, and recent fills across every live market. No signature, no session key, no market selection.",
625
696
  inputSchema: {
@@ -641,7 +712,7 @@ export async function createStrataMcpServer(options = {}) {
641
712
  ? `${response.balances.length} held assets; ${activity}; equity ${response.equity_usd_micros} USD micros at slot ${response.observed_slot}.`
642
713
  : `${response.balances.length} held assets; ${activity}; ${response.unpriced_asset_ids.length} unpriced, USD totals unavailable.`);
643
714
  });
644
- server.registerTool("strata_portfolio_history", {
715
+ registerTool("strata_portfolio_history", {
645
716
  title: "Strata portfolio history",
646
717
  description: "Read genuine stored account-equity history in exact USD micros.",
647
718
  inputSchema: {
@@ -658,7 +729,7 @@ export async function createStrataMcpServer(options = {}) {
658
729
  const response = await platformClient.account.portfolioHistory(walletAddress, range);
659
730
  return toolResult(response, `${response.points.length} stored equity samples returned.`);
660
731
  });
661
- server.registerTool("strata_vault_status", {
732
+ registerTool("strata_vault_status", {
662
733
  title: "Strata Vault status",
663
734
  description: "Read sealed owner state and optional external-session readiness without construction identifiers.",
664
735
  inputSchema: {
@@ -677,7 +748,7 @@ export async function createStrataMcpServer(options = {}) {
677
748
  ? `Vault is ${response.state}; no session was requested.`
678
749
  : `Vault is ${response.state}; requested session is ${response.session.state}.`);
679
750
  });
680
- server.registerTool("strata_vault_pause", {
751
+ registerTool("strata_vault_pause", {
681
752
  title: "Prepare Strata Vault pause",
682
753
  description: "Prepare an owner-authorized pause or resume transaction for external verification, signing, and broadcast.",
683
754
  inputSchema: {
@@ -694,7 +765,7 @@ export async function createStrataMcpServer(options = {}) {
694
765
  const response = await platformClient.vault.preparePause({ walletAddress, paused });
695
766
  return toolResult(response, `Verify this ${paused ? "pause" : "resume"} transaction, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true).`);
696
767
  });
697
- server.registerTool("strata_vault_setup", {
768
+ registerTool("strata_vault_setup", {
698
769
  title: "Prepare Strata Vault onboarding",
699
770
  description: "One-signature onboarding: register an external session key for a wallet. Only the wallet and the session key are needed; one session then trades every market. Policy fields are optional. A first strata_vault_deposit that names the session key does this in the same transaction.",
700
771
  inputSchema: {
@@ -733,7 +804,7 @@ export async function createStrataMcpServer(options = {}) {
733
804
  });
734
805
  return toolResult(response, "Verify every echoed session policy field, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true).");
735
806
  });
736
- server.registerTool("strata_vault_deposit", {
807
+ registerTool("strata_vault_deposit", {
737
808
  title: "Prepare Strata Vault deposit",
738
809
  description: "Prepare an exact owner-funded Vault deposit using opaque market and asset IDs. Name sessionPublicKey and a first deposit also registers that session in the same transaction (one owner signature onboards and funds the wallet).",
739
810
  inputSchema: {
@@ -761,7 +832,7 @@ export async function createStrataMcpServer(options = {}) {
761
832
  ? "This deposit also registers the session key. Verify the exact market, asset, amount, and session, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true)."
762
833
  : "Verify the exact market, asset, and amount, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true).");
763
834
  });
764
- server.registerTool("strata_vault_withdraw", {
835
+ registerTool("strata_vault_withdraw", {
765
836
  title: "Prepare Strata Vault withdrawal",
766
837
  description: "Prepare an exact owner-authorized withdrawal to a destination wallet.",
767
838
  inputSchema: {
@@ -787,7 +858,7 @@ export async function createStrataMcpServer(options = {}) {
787
858
  });
788
859
  return toolResult(response, "Verify the exact destination and amount, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true).");
789
860
  });
790
- server.registerTool("strata_vault_delegate", {
861
+ registerTool("strata_vault_delegate", {
791
862
  title: "Prepare Strata Vault session control",
792
863
  description: "Prepare owner-authorized revocation of one externally held Vault session key.",
793
864
  inputSchema: {
@@ -809,7 +880,7 @@ export async function createStrataMcpServer(options = {}) {
809
880
  });
810
881
  return toolResult(response, "Verify both identities and the destructive action, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true).");
811
882
  });
812
- server.registerTool("strata_vault_policy", {
883
+ registerTool("strata_vault_policy", {
813
884
  title: "Prepare Strata Vault withdrawal policy",
814
885
  description: "Prepare an owner-authorized blocked or restricted withdrawal access policy.",
815
886
  inputSchema: {
@@ -830,7 +901,7 @@ export async function createStrataMcpServer(options = {}) {
830
901
  });
831
902
  return toolResult(response, "Verify the exact withdrawal access policy, then owner-sign it and pass preparation_id + the signed transaction to strata_vault_submit (Strata pays the fee when sponsored is true).");
832
903
  });
833
- server.registerTool("strata_vault_submit", {
904
+ registerTool("strata_vault_submit", {
834
905
  title: "Submit a prepared Strata Vault transaction",
835
906
  description: "Submit an owner-signed prepared Vault transaction (setup, deposit, withdrawal, session, "
836
907
  + "policy, pause). Strata verifies it is exactly the prepared transaction, pays the network "
@@ -857,7 +928,7 @@ export async function createStrataMcpServer(options = {}) {
857
928
  return toolResult(response, `Vault ${response.action} ${response.status}${response.sponsored ? " (Strata paid the fee)" : ""}: `
858
929
  + `signature ${response.signature}. Poll strata_vault_submission until confirmed.`);
859
930
  });
860
- server.registerTool("strata_vault_submission", {
931
+ registerTool("strata_vault_submission", {
861
932
  title: "Strata Vault submission status",
862
933
  description: "Read the durable outcome of a submitted Vault transaction: submitted, confirmed, or failed.",
863
934
  inputSchema: {
@@ -874,7 +945,7 @@ export async function createStrataMcpServer(options = {}) {
874
945
  return toolResult(response, `Vault ${response.action} is ${response.status}`
875
946
  + `${response.failure_code ? ` (${response.failure_code})` : ""}.`);
876
947
  });
877
- server.registerTool("strata_rewards", {
948
+ registerTool("strata_rewards", {
878
949
  title: "Strata rewards",
879
950
  description: "Read the current rewards season, standings, and optional owner score.",
880
951
  inputSchema: {
@@ -891,7 +962,7 @@ export async function createStrataMcpServer(options = {}) {
891
962
  const response = await platformClient.rewards.read({ walletAddress, limit });
892
963
  return toolResult(response, `${response.standings.length} reward standings returned.`);
893
964
  });
894
- server.registerTool("strata_referrals", {
965
+ registerTool("strata_referrals", {
895
966
  title: "Strata referrals",
896
967
  description: "Read an owner's referral state and exact claimable reward atoms.",
897
968
  inputSchema: {
@@ -907,7 +978,7 @@ export async function createStrataMcpServer(options = {}) {
907
978
  const response = await platformClient.referrals.read(walletAddress);
908
979
  return toolResult(response, response.enabled ? "Referral state returned." : "Referrals are disabled.");
909
980
  });
910
- server.registerTool("strata_referral_link", {
981
+ registerTool("strata_referral_link", {
911
982
  title: "Link a Strata referral",
912
983
  description: "Prepare or submit an externally authorized referral link for a new wallet.",
913
984
  inputSchema: {
@@ -936,7 +1007,7 @@ export async function createStrataMcpServer(options = {}) {
936
1007
  });
937
1008
  return toolResult(response, "Referral link is pending the wallet's first fill.");
938
1009
  });
939
- server.registerTool("strata_referral_claim", {
1010
+ registerTool("strata_referral_claim", {
940
1011
  title: "Claim Strata referral rewards",
941
1012
  description: "Prepare or submit an externally authorized request for currently claimable referral rewards.",
942
1013
  inputSchema: {
@@ -967,7 +1038,7 @@ export async function createStrataMcpServer(options = {}) {
967
1038
  });
968
1039
  return toolResult(response, `${response.claimable_atoms} referral reward atoms requested.`);
969
1040
  });
970
- server.registerTool("strata_bugs", {
1041
+ registerTool("strata_bugs", {
971
1042
  title: "Strata bug reports",
972
1043
  description: "Read an owner's redacted bug reports and confirmed points.",
973
1044
  inputSchema: {
@@ -983,7 +1054,7 @@ export async function createStrataMcpServer(options = {}) {
983
1054
  const response = await platformClient.bugs.read(walletAddress);
984
1055
  return toolResult(response, `${response.reports.length} redacted bug reports returned.`);
985
1056
  });
986
- server.registerTool("strata_bug_submit", {
1057
+ registerTool("strata_bug_submit", {
987
1058
  title: "Submit Strata bug report",
988
1059
  description: "Prepare or submit a bug report. Omit authorizationSignature to receive the exact "
989
1060
  + "payload for the owner wallet to sign externally; provide that hex signature to submit.",
@@ -1013,7 +1084,7 @@ export async function createStrataMcpServer(options = {}) {
1013
1084
  });
1014
1085
  return toolResult(response, `Bug report ${response.bug_id} is pending review.`);
1015
1086
  });
1016
- const markets = server.registerTool("strata_markets", {
1087
+ const markets = registerTool("strata_markets", {
1017
1088
  title: "Strata markets",
1018
1089
  description: "List Strata markets and their current Sonar quote availability.",
1019
1090
  inputSchema: {
@@ -1048,7 +1119,7 @@ export async function createStrataMcpServer(options = {}) {
1048
1119
  ? `; ${identified} carry a market_id — pass it as marketId to every by-market tool.`
1049
1120
  : "."));
1050
1121
  }));
1051
- const quote = server.registerTool("strata_quote", {
1122
+ const quote = registerTool("strata_quote", {
1052
1123
  title: "Sonar quote",
1053
1124
  description: "Request a short-lived Sonar quote for a Strata market. Returns expected "
1054
1125
  + "output, minimum output, fees, price impact, and expiry.",
@@ -1091,7 +1162,7 @@ export async function createStrataMcpServer(options = {}) {
1091
1162
  const response = await client.quote(request);
1092
1163
  return toolResult(response, quoteSummary(response));
1093
1164
  }));
1094
- const exactOutputQuote = server.registerTool("strata_exact_output_quote", {
1165
+ const exactOutputQuote = registerTool("strata_exact_output_quote", {
1095
1166
  title: "Sonar exact-output quote",
1096
1167
  description: "Request a short-lived Sonar quote for an exact output amount (for example: buy "
1097
1168
  + "1 SOL). Strata inverts its best route and returns the input that delivers it as "
@@ -1138,7 +1209,7 @@ export async function createStrataMcpServer(options = {}) {
1138
1209
  const response = await client.quote(request);
1139
1210
  return toolResult(response, quoteSummary(response));
1140
1211
  }));
1141
- server.registerTool("strata_swap_quote", {
1212
+ registerTool("strata_swap_quote", {
1142
1213
  title: "Sonar asset swap quote",
1143
1214
  description: "Request short-lived exact-input customer economics between two opaque Strata asset IDs.",
1144
1215
  inputSchema: {
@@ -1164,7 +1235,7 @@ export async function createStrataMcpServer(options = {}) {
1164
1235
  + `${response.amount_out_atoms} user-net output atoms; minimum `
1165
1236
  + `${response.minimum_output_atoms}; expires at ${response.expires_at_ms}.`);
1166
1237
  });
1167
- const executionChallenge = server.registerTool("strata_execution_challenge", {
1238
+ const executionChallenge = registerTool("strata_execution_challenge", {
1168
1239
  title: "Strata execution challenge",
1169
1240
  description: "Request canonical quote-bound authorization bytes for the external signer configured by the agent owner.",
1170
1241
  inputSchema: {
@@ -1200,7 +1271,7 @@ export async function createStrataMcpServer(options = {}) {
1200
1271
  const response = await client.executionChallenge(request);
1201
1272
  return toolResult(response, `Authorization challenge ${response.challenge_id}; expires at ${response.expires_at_ms}.`);
1202
1273
  }));
1203
- const executionPrepare = server.registerTool("strata_execution_prepare", {
1274
+ const executionPrepare = registerTool("strata_execution_prepare", {
1204
1275
  title: "Prepare Strata execution",
1205
1276
  description: "Prepare a quote-bound partially signed transaction. One signature: pass quoteId + ownerWallet + sessionPublicKey and sign only the returned transaction. (A challengeId + authorizationSignature from strata_execution_challenge is still accepted.)",
1206
1277
  inputSchema: {
@@ -1251,7 +1322,7 @@ export async function createStrataMcpServer(options = {}) {
1251
1322
  const response = await client.executionPrepare(request);
1252
1323
  return toolResult(response, `Prepared execution ${response.execution_id}; verify it, then sign the transaction externally with the session key before ${response.expires_at_ms} and submit.`);
1253
1324
  }));
1254
- const executionSubmit = server.registerTool("strata_execution_submit", {
1325
+ const executionSubmit = registerTool("strata_execution_submit", {
1255
1326
  title: "Submit Strata execution",
1256
1327
  description: "Submit an externally signed prepared transaction with an idempotency key.",
1257
1328
  inputSchema: {
@@ -1289,7 +1360,7 @@ export async function createStrataMcpServer(options = {}) {
1289
1360
  const response = await client.executionSubmit(request);
1290
1361
  return toolResult(response, `Submitted execution ${response.execution_id} as ${response.signature}.`);
1291
1362
  }));
1292
- const orderChallenge = server.registerTool("strata_order_challenge", {
1363
+ const orderChallenge = registerTool("strata_order_challenge", {
1293
1364
  title: "Strata order challenge",
1294
1365
  description: "Bind an atomic place, cancel, cancel-all, replace, or bounded batch request to canonical bytes for the agent owner's external signer.",
1295
1366
  inputSchema: {
@@ -1334,7 +1405,7 @@ export async function createStrataMcpServer(options = {}) {
1334
1405
  const response = await platformClient.orders.challenge(args.marketId, request);
1335
1406
  return toolResult(response, `Order challenge ${response.challenge_id} binds ${response.order_ids.length} opaque order ID(s); expires at ${response.expires_at_ms}.`);
1336
1407
  }));
1337
- const orderPrepare = server.registerTool("strata_order_prepare", {
1408
+ const orderPrepare = registerTool("strata_order_prepare", {
1338
1409
  title: "Prepare Strata order control",
1339
1410
  description: "Prepare an immutable partially signed order-control transaction. One signature: pass the operation itself (same fields as strata_order_challenge) and sign only the returned transaction with the session key. (A challengeId + authorizationSignature from strata_order_challenge is still accepted.)",
1340
1411
  inputSchema: {
@@ -1398,7 +1469,7 @@ export async function createStrataMcpServer(options = {}) {
1398
1469
  const response = await platformClient.orders.prepare(args.marketId, { operation: mapped });
1399
1470
  return toolResult(response, `Prepared ${response.action} control ${response.order_control_id} — one signature: verify it, sign the transaction externally with the session key before ${response.expires_at_ms}, then submit.`);
1400
1471
  }));
1401
- const orderSubmit = server.registerTool("strata_order_submit", {
1472
+ const orderSubmit = registerTool("strata_order_submit", {
1402
1473
  title: "Submit Strata order control",
1403
1474
  description: "Submit an externally signed order transaction with a stable retry key.",
1404
1475
  inputSchema: {
@@ -1425,7 +1496,7 @@ export async function createStrataMcpServer(options = {}) {
1425
1496
  });
1426
1497
  return toolResult(response, `Submitted ${response.action} control ${response.order_control_id} as ${response.signature}.`);
1427
1498
  }));
1428
- const orderStatus = server.registerTool("strata_order_status", {
1499
+ const orderStatus = registerTool("strata_order_status", {
1429
1500
  title: "Read Strata order-control status",
1430
1501
  description: "Recover the durable result for an externally signed order submission after a timeout or restart.",
1431
1502
  inputSchema: {
@@ -1443,7 +1514,110 @@ export async function createStrataMcpServer(options = {}) {
1443
1514
  const response = await platformClient.orders.status(marketId, { orderControlId, idempotencyKey });
1444
1515
  return toolResult(response, `Order control ${response.order_control_id} is ${response.status}.`);
1445
1516
  }));
1446
- const makerStrandPrepare = server.registerTool("strata_market_making_strand_prepare", {
1517
+ const makerPrepare = registerTool("strata_market_making_prepare", {
1518
+ title: "Prepare simple Strata market making",
1519
+ description: "Prepare a pro-level Strand or Current from a market label, decimal base size, spread, and duration. Strata resolves IDs, decimals, live mark, tick grid, expiry, safety bounds, and fixed on-chain arrays. Sign only the returned transaction externally, then call submit_and_wait.",
1520
+ inputSchema: {
1521
+ action: z.enum(["start", "stop"]),
1522
+ market: z.string().min(1).max(128),
1523
+ product: z.enum(["strand", "current"]),
1524
+ makerWallet: z.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/),
1525
+ spreadBps: z.number().int().min(1).max(5_000).optional(),
1526
+ size: z.string().min(1).max(64).optional(),
1527
+ duration: z.union([
1528
+ z.number().int().min(1).max(604_800),
1529
+ z.string().regex(/^[1-9][0-9]*(?:s|m|h|d)$/i),
1530
+ ]).optional(),
1531
+ levels: z.number().int().min(1).max(16).optional(),
1532
+ levelStepBps: z.number().int().min(1).max(5_000).optional(),
1533
+ side: z.enum(["both", "buy", "sell"]).optional(),
1534
+ asyncOnly: z.boolean().optional(),
1535
+ },
1536
+ annotations: {
1537
+ readOnlyHint: false,
1538
+ destructiveHint: true,
1539
+ idempotentHint: false,
1540
+ openWorldHint: true,
1541
+ },
1542
+ }, async (args) => guardedTool(client, `mm.${args.product}.manage`, async () => {
1543
+ const prepared = args.action === "stop"
1544
+ ? await platformClient.marketMaking.prepareStop({
1545
+ market: args.market,
1546
+ product: args.product,
1547
+ makerWallet: args.makerWallet,
1548
+ })
1549
+ : (() => {
1550
+ if (args.spreadBps === undefined || args.size === undefined) {
1551
+ return undefined;
1552
+ }
1553
+ return platformClient.marketMaking.prepareStart({
1554
+ market: args.market,
1555
+ product: args.product,
1556
+ makerWallet: args.makerWallet,
1557
+ spreadBps: args.spreadBps,
1558
+ size: args.size,
1559
+ ...(args.duration === undefined ? {} : { duration: args.duration }),
1560
+ ...(args.levels === undefined ? {} : { levels: args.levels }),
1561
+ ...(args.levelStepBps === undefined ? {} : { levelStepBps: args.levelStepBps }),
1562
+ ...(args.side === undefined ? {} : { side: args.side }),
1563
+ ...(args.asyncOnly === undefined ? {} : { asyncOnly: args.asyncOnly }),
1564
+ });
1565
+ })();
1566
+ if (prepared === undefined) {
1567
+ return toolError("invalid_request", "Starting market making requires spreadBps and size.", false);
1568
+ }
1569
+ const resolved = await prepared;
1570
+ makerQuickstartPreparations.set(resolved.prepared.maker_control_id, resolved);
1571
+ while (makerQuickstartPreparations.size > 128) {
1572
+ const oldest = makerQuickstartPreparations.keys().next().value;
1573
+ if (oldest === undefined)
1574
+ break;
1575
+ makerQuickstartPreparations.delete(oldest);
1576
+ }
1577
+ const response = {
1578
+ action: args.action,
1579
+ market: resolved.market,
1580
+ product: resolved.product,
1581
+ ...("base_asset" in resolved ? { base_asset: resolved.base_asset } : {}),
1582
+ operation: resolved.operation,
1583
+ prepared: resolved.prepared,
1584
+ };
1585
+ return toolResult(response, `Prepared ${args.action} for ${resolved.market.label} as ${resolved.prepared.maker_control_id}. Verify and sign only prepared.transaction_base64, then submit it within 30 seconds.`);
1586
+ }));
1587
+ const makerSubmitAndWait = registerTool("strata_market_making_submit_and_wait", {
1588
+ title: "Submit and confirm Strata market making",
1589
+ description: "Submit the exact externally signed quickstart transaction and wait until Strata's chain-derived maker state confirms the product started or stopped. The control ID is its default idempotency key.",
1590
+ inputSchema: {
1591
+ makerControlId: z.string().regex(/^mc_[0-9a-f]{32}$/),
1592
+ signedTransactionBase64: z.string().min(4).max(4_096).regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/),
1593
+ idempotencyKey: z.string().min(1).max(64).regex(/^[A-Za-z0-9._-]+$/).optional(),
1594
+ confirmationTimeoutMs: z.number().int().min(1_000).max(120_000).optional(),
1595
+ },
1596
+ annotations: {
1597
+ readOnlyHint: false,
1598
+ destructiveHint: true,
1599
+ idempotentHint: true,
1600
+ openWorldHint: true,
1601
+ },
1602
+ }, async (args) => {
1603
+ const prepared = makerQuickstartPreparations.get(args.makerControlId);
1604
+ if (!prepared) {
1605
+ return toolError("session_expired", "This quickstart preparation is not in the current MCP session. Prepare it again; maker transactions expire after 30 seconds.", false);
1606
+ }
1607
+ return guardedTool(client, `mm.${prepared.product}.manage`, async () => {
1608
+ const response = await platformClient.marketMaking.submitPrepared({
1609
+ prepared,
1610
+ signedTransactionBase64: args.signedTransactionBase64,
1611
+ ...(args.idempotencyKey === undefined ? {} : { idempotencyKey: args.idempotencyKey }),
1612
+ ...(args.confirmationTimeoutMs === undefined
1613
+ ? {}
1614
+ : { confirmationTimeoutMs: args.confirmationTimeoutMs }),
1615
+ });
1616
+ makerQuickstartPreparations.delete(args.makerControlId);
1617
+ return toolResult(response, `${response.product} is confirmed ${response.operation.action === "cancel" ? "stopped" : "live"} on ${response.market.label}.`);
1618
+ });
1619
+ });
1620
+ const makerStrandPrepare = registerTool("strata_market_making_strand_prepare", {
1447
1621
  title: "Prepare Strata Strand control",
1448
1622
  description: "Build one exact unsigned maker-owned Strand transaction. Every exposure and level size is expressed in base-asset atoms, never lots or whole tokens. Verify and sign it externally with the maker wallet, then submit it with the Strand submit tool.",
1449
1623
  inputSchema: {
@@ -1516,7 +1690,7 @@ export async function createStrataMcpServer(options = {}) {
1516
1690
  const response = await platformClient.marketMaking.strand.prepare(args.marketId, request);
1517
1691
  return toolResult(response, `Prepared ${response.action} control ${response.maker_control_id}; verify and sign only this transaction with ${response.maker_wallet} before ${response.expires_at_ms}.`);
1518
1692
  }));
1519
- const makerStrandSubmit = server.registerTool("strata_market_making_strand_submit", {
1693
+ const makerStrandSubmit = registerTool("strata_market_making_strand_submit", {
1520
1694
  title: "Submit Strata Strand control",
1521
1695
  description: "Submit the exact externally maker-signed Strand transaction with a stable retry key.",
1522
1696
  inputSchema: {
@@ -1539,9 +1713,9 @@ export async function createStrataMcpServer(options = {}) {
1539
1713
  });
1540
1714
  return toolResult(response, `Submitted ${response.action} as ${response.signature}.`);
1541
1715
  }));
1542
- const makerCurrentPrepare = server.registerTool("strata_market_making_current_prepare", {
1716
+ const makerCurrentPrepare = registerTool("strata_market_making_current_prepare", {
1543
1717
  title: "Prepare Strata Current control",
1544
- description: "Build one exact unsigned maker-owned Current transaction. Upsert fails closed until the market has a verified on-chain reference; cancel does not need that reference.",
1718
+ description: "Build one exact unsigned maker-owned Current transaction. Upsert prices its bands from the market's live Strata mark; cancel remains available independently.",
1545
1719
  inputSchema: {
1546
1720
  marketId: z.string().regex(/^market_[0-9a-f]{32}$/),
1547
1721
  action: z.enum(["upsert", "cancel"]),
@@ -1599,7 +1773,7 @@ export async function createStrataMcpServer(options = {}) {
1599
1773
  const response = await platformClient.marketMaking.current.prepare(args.marketId, request);
1600
1774
  return toolResult(response, `Prepared ${response.action} control ${response.maker_control_id}; verify and sign only this transaction with ${response.maker_wallet} before ${response.expires_at_ms}.`);
1601
1775
  }));
1602
- const makerCurrentSubmit = server.registerTool("strata_market_making_current_submit", {
1776
+ const makerCurrentSubmit = registerTool("strata_market_making_current_submit", {
1603
1777
  title: "Submit Strata Current control",
1604
1778
  description: "Submit the exact externally maker-signed Current transaction with a stable retry key.",
1605
1779
  inputSchema: {
@@ -1622,8 +1796,8 @@ export async function createStrataMcpServer(options = {}) {
1622
1796
  });
1623
1797
  return toolResult(response, `Submitted ${response.action} as ${response.signature}.`);
1624
1798
  }));
1625
- registerAutonomyTools(server, client, platformClient, options.sessionAutonomy, () => typeof Date !== "undefined" ? Date.now() : 0);
1626
- const handles = {
1799
+ registerAutonomyTools(registerTool, client, platformClient, options.sessionAutonomy, () => typeof Date !== "undefined" ? Date.now() : 0);
1800
+ void [
1627
1801
  markets,
1628
1802
  quote,
1629
1803
  exactOutputQuote,
@@ -1634,17 +1808,23 @@ export async function createStrataMcpServer(options = {}) {
1634
1808
  orderPrepare,
1635
1809
  orderSubmit,
1636
1810
  orderStatus,
1811
+ makerPrepare,
1812
+ makerSubmitAndWait,
1637
1813
  makerStrandPrepare,
1638
1814
  makerStrandSubmit,
1639
1815
  makerCurrentPrepare,
1640
1816
  makerCurrentSubmit,
1641
- };
1642
- applyCapabilityCatalog(handles, initialCatalog);
1817
+ ];
1818
+ applyToolAvailability(registeredTools, initialCatalog, initialPlatformCatalog);
1643
1819
  let closed = false;
1644
1820
  const refresh = async () => {
1645
1821
  if (closed)
1646
1822
  return;
1647
- applyCapabilityCatalog(handles, await client.capabilities());
1823
+ const [catalog, platformCatalog] = await Promise.all([
1824
+ client.capabilities(),
1825
+ platformClient.discovery.read(),
1826
+ ]);
1827
+ applyToolAvailability(registeredTools, catalog, platformCatalog);
1648
1828
  };
1649
1829
  const timer = setInterval(() => {
1650
1830
  refresh().catch((error) => {
@@ -1662,10 +1842,10 @@ export async function createStrataMcpServer(options = {}) {
1662
1842
  },
1663
1843
  };
1664
1844
  }
1665
- function registerAutonomyTools(server, client, platformClient, autonomy, nowMs) {
1845
+ function registerAutonomyTools(registerTool, client, platformClient, autonomy, nowMs) {
1666
1846
  // Always present, always read-only: the agent may show the slider and offer
1667
1847
  // to change it, but nothing it calls can raise its own autonomy.
1668
- server.registerTool("strata_autonomy", {
1848
+ registerTool("strata_autonomy", {
1669
1849
  title: "Strata session autonomy",
1670
1850
  description: "Read how much this MCP may finish by itself: the autonomy level (ask / limits / instant), "
1671
1851
  + "any USD ceilings, and how to change it. Read-only — the level is the user's, set out-of-band "
@@ -1730,7 +1910,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1730
1910
  };
1731
1911
  const refuse = (reason, prepared, summary) => toolResult({ executed: false, reason, prepared }, summary);
1732
1912
  // ── one-shot immediate execution from a fresh Sonar quote ─────────────────
1733
- server.registerTool("strata_execute_quote", {
1913
+ registerTool("strata_execute_quote", {
1734
1914
  title: "Execute a Strata quote",
1735
1915
  description: "Take a fresh Sonar quote and, within the autonomy slider, sign it with the session key and "
1736
1916
  + "submit it in one call. Under \"ask\" (or over a \"limits\" ceiling) it does not sign — it returns "
@@ -1775,7 +1955,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1775
1955
  return toolResult({ executed: true, receipt, notional_usd: notional }, `Executed ${quote.side} on ${quote.market_id} as ${receipt.signature}.`);
1776
1956
  }));
1777
1957
  // ── one-shot order control (place / cancel / replace / batch) ─────────────
1778
- server.registerTool("strata_order_execute", {
1958
+ registerTool("strata_order_execute", {
1779
1959
  title: "Execute a Strata order control",
1780
1960
  description: "Place, cancel, replace, or batch orders and, within the autonomy slider, sign with the session "
1781
1961
  + "key and submit in one call. Owner wallet and session key come from the configured session. "
@@ -1837,7 +2017,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1837
2017
  return toolResult({ executed: true, receipt, notional_usd: notional }, `Executed ${receipt.action} control ${receipt.order_control_id} as ${receipt.signature}.`);
1838
2018
  }));
1839
2019
  // ── one-shot TWAP (schedule / cancel) ─────────────────────────────────────
1840
- server.registerTool("strata_twap_execute", {
2020
+ registerTool("strata_twap_execute", {
1841
2021
  title: "Execute a Strata TWAP",
1842
2022
  description: "Schedule or cancel a TWAP and, within the autonomy slider, sign with the session key and submit "
1843
2023
  + "in one call. Owner wallet and session key come from the configured session. Under \"ask\" (or over "
@@ -1860,7 +2040,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1860
2040
  idempotentHint: false,
1861
2041
  openWorldHint: true,
1862
2042
  },
1863
- }, async (args) => guardedTool(client, "algos.submit", async () => {
2043
+ }, async (args) => safeTool(async () => {
1864
2044
  let operation;
1865
2045
  if (args.action === "cancel") {
1866
2046
  if (args.twapId === undefined)
@@ -1909,23 +2089,36 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1909
2089
  return toolResult({ executed: true, receipt, notional_usd: notional }, `Executed TWAP ${receipt.twap_control_id} as ${receipt.signature}.`);
1910
2090
  }));
1911
2091
  }
1912
- function applyCapabilityCatalog(handles, catalog) {
1913
- setToolEnabled(handles.markets, capabilityAvailable(catalog, "markets.read"));
1914
- setToolEnabled(handles.quote, capabilityAvailable(catalog, "quotes.read"));
1915
- setToolEnabled(handles.exactOutputQuote, capabilityAvailable(catalog, "quotes.read"));
1916
- setToolEnabled(handles.executionChallenge, capabilityAvailable(catalog, "trade.prepare"));
1917
- setToolEnabled(handles.executionPrepare, capabilityAvailable(catalog, "trade.prepare"));
1918
- setToolEnabled(handles.executionSubmit, capabilityAvailable(catalog, "trade.submit"));
1919
- setToolEnabled(handles.orderChallenge, capabilityAvailable(catalog, "orders.prepare"));
1920
- setToolEnabled(handles.orderPrepare, capabilityAvailable(catalog, "orders.prepare"));
1921
- setToolEnabled(handles.orderSubmit, capabilityAvailable(catalog, "orders.submit"));
1922
- setToolEnabled(handles.orderStatus, capabilityAvailable(catalog, "orders.submit"));
1923
- const strandEnabled = capabilityAvailable(catalog, "mm.strand.manage");
1924
- setToolEnabled(handles.makerStrandPrepare, strandEnabled);
1925
- setToolEnabled(handles.makerStrandSubmit, strandEnabled);
1926
- const currentEnabled = capabilityAvailable(catalog, "mm.current.manage");
1927
- setToolEnabled(handles.makerCurrentPrepare, currentEnabled);
1928
- setToolEnabled(handles.makerCurrentSubmit, currentEnabled);
2092
+ function trackedToolRegistrar(server) {
2093
+ const handles = new Map();
2094
+ const register = server.registerTool.bind(server);
2095
+ const registerTool = ((...args) => {
2096
+ const tool = register(...args);
2097
+ const name = args[0];
2098
+ if (typeof name === "string")
2099
+ handles.set(name, tool);
2100
+ return tool;
2101
+ });
2102
+ return { registerTool, handles };
2103
+ }
2104
+ function platformCapabilityAvailable(catalog, id) {
2105
+ return catalog.capabilities.some((capability) => capability.id === id
2106
+ && capability.transports.includes("mcp")
2107
+ && capability.mcp_exposure !== "none");
2108
+ }
2109
+ function requirementAvailable(requirement, available) {
2110
+ if (requirement === undefined)
2111
+ return true;
2112
+ return requirement.match === "any"
2113
+ ? requirement.ids.some(available)
2114
+ : requirement.ids.every(available);
2115
+ }
2116
+ function applyToolAvailability(handles, catalog, platformCatalog) {
2117
+ for (const [name, tool] of handles) {
2118
+ const legacyAvailable = requirementAvailable(LEGACY_TOOL_CAPABILITIES[name], (id) => capabilityAvailable(catalog, id));
2119
+ const platformAvailable = requirementAvailable(PLATFORM_TOOL_CAPABILITIES[name], (id) => platformCapabilityAvailable(platformCatalog, id));
2120
+ setToolEnabled(tool, legacyAvailable && platformAvailable);
2121
+ }
1929
2122
  }
1930
2123
  function setToolEnabled(tool, enabled) {
1931
2124
  if (enabled)
@@ -1948,6 +2141,17 @@ async function guardedTool(client, capabilityId, operation) {
1948
2141
  return toolError("request_failed", safeMessage(error), true);
1949
2142
  }
1950
2143
  }
2144
+ async function safeTool(operation) {
2145
+ try {
2146
+ return await operation();
2147
+ }
2148
+ catch (error) {
2149
+ if (error instanceof StrataApiError) {
2150
+ return toolError(error.code, error.message, error.retryable);
2151
+ }
2152
+ return toolError("request_failed", safeMessage(error), true);
2153
+ }
2154
+ }
1951
2155
  /**
1952
2156
  * One line that keeps the two numbers apart: price impact is measured from the
1953
2157
  * book; the tolerance is the caller's own floor.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratabook/mcp",
3
- "version": "0.2.2",
3
+ "version": "0.2.5",
4
4
  "description": "Connect AI agents to Strata markets and Sonar quotes with MCP.",
5
5
  "type": "module",
6
6
  "license": "MIT OR Apache-2.0",
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/sdk": "1.30.0",
47
- "@stratabook/sdk": "0.2.2",
47
+ "@stratabook/sdk": "0.2.5",
48
48
  "zod": "^3.25.76"
49
49
  },
50
50
  "devDependencies": {