@stratabook/mcp 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,8 @@ 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_strand_prepare`, `strata_market_making_strand_submit`
49
+ - `strata_market_making_current_prepare`, `strata_market_making_current_submit`
43
50
  - `strata_vault_status`
44
51
  - `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
52
  - `strata_vault_submit` — submit the owner-signed preparation; Strata pays and broadcasts
@@ -50,12 +57,12 @@ The tools currently available are:
50
57
  - `strata_referral_claim` — prepare externally signable consent or submit the signed claim
51
58
  - `strata_bugs`
52
59
  - `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
60
+ - `strata_markets`
61
+ - `strata_quote` and `strata_exact_output_quote` — live market quotes (spend X / receive at least Y)
62
+ - `strata_swap_quote` live catalog-asset swap quotes
63
+ - `strata_execution_challenge`
64
+ - `strata_execution_prepare`
65
+ - `strata_execution_submit`
59
66
  - `strata_execution_status` — recover a durable immediate-execution receipt
60
67
  - `strata_order_challenge`, when `orders.prepare` is enabled for MCP
61
68
  - `strata_order_prepare`, when `orders.prepare` is enabled for MCP
@@ -72,7 +79,8 @@ available as `strata://platform-graph/v2`.
72
79
 
73
80
  The tool list follows the live public policy. Every call rechecks that policy,
74
81
  so a disabled capability stops immediately even if a client cached an older
75
- tool list.
82
+ tool list. Tool discovery from the connected server remains authoritative for
83
+ self-hosted deployments or any future policy change.
76
84
 
77
85
  ## Hosted Streamable HTTP
78
86
 
@@ -6,6 +6,70 @@ 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_execute_quote: { ids: ["trade.submit"] },
25
+ strata_order_execute: { ids: ["orders.prepare", "orders.submit"] },
26
+ };
27
+ const PLATFORM_TOOL_CAPABILITIES = {
28
+ strata_status: { ids: ["platform.status.read"] },
29
+ strata_platform_graph: { ids: ["graphs.read"] },
30
+ strata_candles: { ids: ["market_data.candles.read"] },
31
+ strata_marks: { ids: ["market_data.marks.read"] },
32
+ strata_quote: { ids: ["quotes.market.read"] },
33
+ strata_swap_quote: { ids: ["quotes.swap.read"] },
34
+ strata_exact_output_quote: { ids: ["quotes.exact_output.read"] },
35
+ strata_execution_challenge: { ids: ["execution.prepare"] },
36
+ strata_execution_prepare: { ids: ["execution.prepare"] },
37
+ strata_execution_submit: { ids: ["execution.submit"] },
38
+ strata_execution_status: { ids: ["execution.status.read"] },
39
+ strata_order_challenge: { ids: ["orders.prepare"] },
40
+ strata_order_prepare: { ids: ["orders.prepare"] },
41
+ strata_order_submit: { ids: ["orders.submit"] },
42
+ strata_twap_challenge: { ids: ["algos.twap.place"] },
43
+ strata_twap_cancel: { ids: ["algos.twap.cancel"] },
44
+ strata_twap_prepare: { ids: ["algos.twap.place", "algos.twap.cancel"], match: "any" },
45
+ strata_twap_submit: { ids: ["algos.twap.place", "algos.twap.cancel"], match: "any" },
46
+ strata_twaps: { ids: ["algos.twap.read"] },
47
+ strata_portfolio: { ids: ["portfolio.read"] },
48
+ strata_portfolio_history: { ids: ["portfolio.history.read"] },
49
+ strata_vault_status: { ids: ["vault.status.read"] },
50
+ strata_vault_setup: { ids: ["vault.setup"] },
51
+ strata_vault_deposit: { ids: ["vault.deposit"] },
52
+ strata_vault_withdraw: { ids: ["vault.withdraw"] },
53
+ strata_vault_delegate: { ids: ["vault.delegate.manage"] },
54
+ strata_vault_policy: { ids: ["vault.policy.manage"] },
55
+ strata_vault_pause: { ids: ["vault.pause"] },
56
+ strata_vault_submit: { ids: ["vault.relay"] },
57
+ strata_vault_submission: { ids: ["vault.relay"] },
58
+ strata_market_making_status: { ids: ["mm.status.read"] },
59
+ strata_market_making_reputation: { ids: ["mm.reputation.read"] },
60
+ strata_market_making_strand_prepare: { ids: ["mm.strand.manage"] },
61
+ strata_market_making_strand_submit: { ids: ["mm.strand.manage"] },
62
+ strata_market_making_current_prepare: { ids: ["mm.current.manage"] },
63
+ strata_market_making_current_submit: { ids: ["mm.current.manage"] },
64
+ strata_rewards: { ids: ["rewards.read"] },
65
+ strata_referrals: { ids: ["referrals.read"] },
66
+ strata_referral_link: { ids: ["referrals.link"] },
67
+ strata_referral_claim: { ids: ["referrals.claim"] },
68
+ strata_bug_submit: { ids: ["bugs.submit"] },
69
+ strata_bugs: { ids: ["bugs.read"] },
70
+ strata_order_execute: { ids: ["orders.prepare", "orders.submit"] },
71
+ strata_twap_execute: { ids: ["algos.twap.place", "algos.twap.cancel"], match: "any" },
72
+ };
9
73
  export function capabilityAvailable(catalog, id) {
10
74
  return catalog.capabilities.some((capability) => capability.id === id
11
75
  && capability.default_enabled
@@ -184,12 +248,14 @@ export async function createStrataMcpServer(options = {}) {
184
248
  if (initialCatalog.contract_version !== STRATA_AGENT_HARNESS.contract_version) {
185
249
  throw new Error("agent harness and live contract versions differ");
186
250
  }
251
+ const initialPlatformCatalog = await platformClient.discovery.read();
187
252
  const server = new McpServer({
188
253
  name: "strata",
189
254
  version: SERVER_VERSION,
190
255
  }, {
191
256
  instructions: STRATA_AGENT_HARNESS_INSTRUCTIONS,
192
257
  });
258
+ const { registerTool, handles: registeredTools } = trackedToolRegistrar(server);
193
259
  server.registerResource("strata_agent_harness", STRATA_AGENT_HARNESS_URI, {
194
260
  title: "Strata Agent Harness",
195
261
  description: "Canonical capability-gated first-run workflow for Strata agents.",
@@ -251,7 +317,7 @@ export async function createStrataMcpServer(options = {}) {
251
317
  },
252
318
  ],
253
319
  }));
254
- server.registerTool("strata_capabilities", {
320
+ registerTool("strata_capabilities", {
255
321
  title: "Strata capabilities",
256
322
  description: "See which Strata features are currently available to MCP clients.",
257
323
  annotations: {
@@ -261,7 +327,7 @@ export async function createStrataMcpServer(options = {}) {
261
327
  openWorldHint: true,
262
328
  },
263
329
  }, async () => toolResult(await client.capabilities(), "Current Strata capabilities."));
264
- server.registerTool("strata_action_graph", {
330
+ registerTool("strata_action_graph", {
265
331
  title: "Strata action graph",
266
332
  description: "Discover live operations, required capabilities, transition conditions, and external signing boundaries.",
267
333
  annotations: {
@@ -271,7 +337,7 @@ export async function createStrataMcpServer(options = {}) {
271
337
  openWorldHint: true,
272
338
  },
273
339
  }, async () => toolResult(await client.actionGraph(), "Current Strata action graph."));
274
- server.registerTool("strata_platform_graph", {
340
+ registerTool("strata_platform_graph", {
275
341
  title: "Strata platform graph",
276
342
  description: "Discover every public module, entity relationship, operation binding, workflow, and live availability gate.",
277
343
  annotations: {
@@ -285,7 +351,7 @@ export async function createStrataMcpServer(options = {}) {
285
351
  const liveOperations = graph.operations.filter((operation) => operation.available).length;
286
352
  return toolResult(graph, `${liveOperations} of ${graph.operations.length} mapped Strata operations are currently live.`);
287
353
  });
288
- server.registerTool("strata_market_making_status", {
354
+ registerTool("strata_market_making_status", {
289
355
  title: "Read Strata maker status",
290
356
  description: "A maker's products, live exposure, health, and kill state in one market — public by wallet address, no signature.",
291
357
  inputSchema: {
@@ -302,7 +368,7 @@ export async function createStrataMcpServer(options = {}) {
302
368
  const response = await platformClient.marketMaking.status(marketId, walletAddress);
303
369
  return toolResult(response, `${response.active_products} active maker products; reconcile intent, Strand, Current, signed-quote, and dead-man state before changing exposure.`);
304
370
  });
305
- server.registerTool("strata_market_making_reputation", {
371
+ registerTool("strata_market_making_reputation", {
306
372
  title: "Read Strata maker reputation",
307
373
  description: "A maker's reliability, participation, tier, and signed-quote eligibility in one market — public by wallet address, no signature.",
308
374
  inputSchema: {
@@ -316,7 +382,7 @@ export async function createStrataMcpServer(options = {}) {
316
382
  openWorldHint: true,
317
383
  },
318
384
  }, 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", {
385
+ registerTool("strata_status", {
320
386
  title: "Strata status",
321
387
  description: "Read product-level readiness and the number of currently live mapped operations.",
322
388
  annotations: {
@@ -329,7 +395,7 @@ export async function createStrataMcpServer(options = {}) {
329
395
  const status = await platformClient.discovery.status();
330
396
  return toolResult(status, `Strata is ${status.status}; ${status.available_operations} mapped operations are live.`);
331
397
  });
332
- server.registerTool("strata_candles", {
398
+ registerTool("strata_candles", {
333
399
  title: "Strata candles",
334
400
  description: "Read bounded time-bucketed candles for one opaque Strata market ID.",
335
401
  inputSchema: {
@@ -352,7 +418,7 @@ export async function createStrataMcpServer(options = {}) {
352
418
  });
353
419
  return toolResult(candles, `${candles.candles.length} Strata candles returned.`);
354
420
  });
355
- server.registerTool("strata_marks", {
421
+ registerTool("strata_marks", {
356
422
  title: "Strata mark",
357
423
  description: "Read the current customer-facing reference price for one opaque market ID.",
358
424
  inputSchema: {
@@ -368,7 +434,7 @@ export async function createStrataMcpServer(options = {}) {
368
434
  const mark = await platformClient.marketData.mark(marketId);
369
435
  return toolResult(mark, mark.stale ? "Strata mark is stale." : "Current Strata mark.");
370
436
  });
371
- server.registerTool("strata_book", {
437
+ registerTool("strata_book", {
372
438
  title: "Strata order book",
373
439
  description: "Read the executable order book for one opaque market ID: bids and asks, one size per price "
374
440
  + "level. Top of book is the best bid and ask.",
@@ -392,7 +458,7 @@ export async function createStrataMcpServer(options = {}) {
392
458
  const book = await platformClient.books.snapshot(marketId, depth === undefined ? {} : { depth });
393
459
  return toolResult(book, `Book for ${marketId}: ${book.bids.length} bid / ${book.asks.length} ask levels at sequence ${book.sequence}.`);
394
460
  });
395
- server.registerTool("strata_bbo", {
461
+ registerTool("strata_bbo", {
396
462
  title: "Strata best bid/ask",
397
463
  description: "Read the current best bid and best ask (top of book) for one opaque market ID.",
398
464
  inputSchema: {
@@ -408,7 +474,7 @@ export async function createStrataMcpServer(options = {}) {
408
474
  const bbo = await platformClient.books.bestBidAsk(marketId);
409
475
  return toolResult(bbo, `BBO for ${marketId}: bid ${bbo.best_bid?.price_atoms ?? "—"} / ask ${bbo.best_ask?.price_atoms ?? "—"}.`);
410
476
  });
411
- server.registerTool("strata_trades", {
477
+ registerTool("strata_trades", {
412
478
  title: "Strata recent trades",
413
479
  description: "Read recent anonymized prints for one opaque market ID: price, size, side, and time.",
414
480
  inputSchema: {
@@ -431,7 +497,7 @@ export async function createStrataMcpServer(options = {}) {
431
497
  const trades = await platformClient.books.trades(marketId, limit === undefined ? {} : { limit });
432
498
  return toolResult(trades, `${trades.trades.length} recent prints for ${marketId}.`);
433
499
  });
434
- server.registerTool("strata_execution_status", {
500
+ registerTool("strata_execution_status", {
435
501
  title: "Strata execution status",
436
502
  description: "Recover prepared state or a restart-durable confirmed execution receipt.",
437
503
  inputSchema: {
@@ -448,7 +514,7 @@ export async function createStrataMcpServer(options = {}) {
448
514
  const receipt = await platformClient.executions.status(marketId, executionId);
449
515
  return toolResult(receipt, `Execution is ${receipt.status}.`);
450
516
  });
451
- server.registerTool("strata_twaps", {
517
+ registerTool("strata_twaps", {
452
518
  title: "Strata TWAPs",
453
519
  description: "Read sanitized progress and terminal receipts for wallet-owned TWAP schedules.",
454
520
  inputSchema: {
@@ -465,7 +531,7 @@ export async function createStrataMcpServer(options = {}) {
465
531
  const response = await platformClient.algos.twaps(marketId, walletAddress);
466
532
  return toolResult(response, `${response.twaps.length} TWAP schedules returned.`);
467
533
  });
468
- server.registerTool("strata_twap_challenge", {
534
+ registerTool("strata_twap_challenge", {
469
535
  title: "Prepare a Strata TWAP authorization",
470
536
  description: "Request exact external-signing bytes for a bounded TWAP schedule.",
471
537
  inputSchema: {
@@ -499,7 +565,7 @@ export async function createStrataMcpServer(options = {}) {
499
565
  });
500
566
  return toolResult(response, "Sign the returned authorization payload externally.");
501
567
  });
502
- server.registerTool("strata_twap_cancel", {
568
+ registerTool("strata_twap_cancel", {
503
569
  title: "Prepare Strata TWAP cancellation",
504
570
  description: "Request exact external-signing bytes to cancel one active owned TWAP.",
505
571
  inputSchema: {
@@ -523,7 +589,7 @@ export async function createStrataMcpServer(options = {}) {
523
589
  });
524
590
  return toolResult(response, "Sign the returned cancellation payload externally.");
525
591
  });
526
- server.registerTool("strata_twap_prepare", {
592
+ registerTool("strata_twap_prepare", {
527
593
  title: "Prepare Strata TWAP transaction",
528
594
  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
595
  inputSchema: {
@@ -596,7 +662,7 @@ export async function createStrataMcpServer(options = {}) {
596
662
  const response = await platformClient.algos.prepare(input.marketId, { operation });
597
663
  return toolResult(response, "One signature: verify this canonical transaction, then sign it externally with the session key and submit.");
598
664
  });
599
- server.registerTool("strata_twap_submit", {
665
+ registerTool("strata_twap_submit", {
600
666
  title: "Submit Strata TWAP transaction",
601
667
  description: "Submit the exact externally signed TWAP transaction idempotently.",
602
668
  inputSchema: {
@@ -619,7 +685,7 @@ export async function createStrataMcpServer(options = {}) {
619
685
  });
620
686
  return toolResult(response, `TWAP action submitted as ${response.signature}.`);
621
687
  });
622
- server.registerTool("strata_portfolio", {
688
+ registerTool("strata_portfolio", {
623
689
  title: "Strata account",
624
690
  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
691
  inputSchema: {
@@ -641,7 +707,7 @@ export async function createStrataMcpServer(options = {}) {
641
707
  ? `${response.balances.length} held assets; ${activity}; equity ${response.equity_usd_micros} USD micros at slot ${response.observed_slot}.`
642
708
  : `${response.balances.length} held assets; ${activity}; ${response.unpriced_asset_ids.length} unpriced, USD totals unavailable.`);
643
709
  });
644
- server.registerTool("strata_portfolio_history", {
710
+ registerTool("strata_portfolio_history", {
645
711
  title: "Strata portfolio history",
646
712
  description: "Read genuine stored account-equity history in exact USD micros.",
647
713
  inputSchema: {
@@ -658,7 +724,7 @@ export async function createStrataMcpServer(options = {}) {
658
724
  const response = await platformClient.account.portfolioHistory(walletAddress, range);
659
725
  return toolResult(response, `${response.points.length} stored equity samples returned.`);
660
726
  });
661
- server.registerTool("strata_vault_status", {
727
+ registerTool("strata_vault_status", {
662
728
  title: "Strata Vault status",
663
729
  description: "Read sealed owner state and optional external-session readiness without construction identifiers.",
664
730
  inputSchema: {
@@ -677,7 +743,7 @@ export async function createStrataMcpServer(options = {}) {
677
743
  ? `Vault is ${response.state}; no session was requested.`
678
744
  : `Vault is ${response.state}; requested session is ${response.session.state}.`);
679
745
  });
680
- server.registerTool("strata_vault_pause", {
746
+ registerTool("strata_vault_pause", {
681
747
  title: "Prepare Strata Vault pause",
682
748
  description: "Prepare an owner-authorized pause or resume transaction for external verification, signing, and broadcast.",
683
749
  inputSchema: {
@@ -694,7 +760,7 @@ export async function createStrataMcpServer(options = {}) {
694
760
  const response = await platformClient.vault.preparePause({ walletAddress, paused });
695
761
  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
762
  });
697
- server.registerTool("strata_vault_setup", {
763
+ registerTool("strata_vault_setup", {
698
764
  title: "Prepare Strata Vault onboarding",
699
765
  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
766
  inputSchema: {
@@ -733,7 +799,7 @@ export async function createStrataMcpServer(options = {}) {
733
799
  });
734
800
  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
801
  });
736
- server.registerTool("strata_vault_deposit", {
802
+ registerTool("strata_vault_deposit", {
737
803
  title: "Prepare Strata Vault deposit",
738
804
  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
805
  inputSchema: {
@@ -761,7 +827,7 @@ export async function createStrataMcpServer(options = {}) {
761
827
  ? "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
828
  : "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
829
  });
764
- server.registerTool("strata_vault_withdraw", {
830
+ registerTool("strata_vault_withdraw", {
765
831
  title: "Prepare Strata Vault withdrawal",
766
832
  description: "Prepare an exact owner-authorized withdrawal to a destination wallet.",
767
833
  inputSchema: {
@@ -787,7 +853,7 @@ export async function createStrataMcpServer(options = {}) {
787
853
  });
788
854
  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
855
  });
790
- server.registerTool("strata_vault_delegate", {
856
+ registerTool("strata_vault_delegate", {
791
857
  title: "Prepare Strata Vault session control",
792
858
  description: "Prepare owner-authorized revocation of one externally held Vault session key.",
793
859
  inputSchema: {
@@ -809,7 +875,7 @@ export async function createStrataMcpServer(options = {}) {
809
875
  });
810
876
  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
877
  });
812
- server.registerTool("strata_vault_policy", {
878
+ registerTool("strata_vault_policy", {
813
879
  title: "Prepare Strata Vault withdrawal policy",
814
880
  description: "Prepare an owner-authorized blocked or restricted withdrawal access policy.",
815
881
  inputSchema: {
@@ -830,7 +896,7 @@ export async function createStrataMcpServer(options = {}) {
830
896
  });
831
897
  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
898
  });
833
- server.registerTool("strata_vault_submit", {
899
+ registerTool("strata_vault_submit", {
834
900
  title: "Submit a prepared Strata Vault transaction",
835
901
  description: "Submit an owner-signed prepared Vault transaction (setup, deposit, withdrawal, session, "
836
902
  + "policy, pause). Strata verifies it is exactly the prepared transaction, pays the network "
@@ -857,7 +923,7 @@ export async function createStrataMcpServer(options = {}) {
857
923
  return toolResult(response, `Vault ${response.action} ${response.status}${response.sponsored ? " (Strata paid the fee)" : ""}: `
858
924
  + `signature ${response.signature}. Poll strata_vault_submission until confirmed.`);
859
925
  });
860
- server.registerTool("strata_vault_submission", {
926
+ registerTool("strata_vault_submission", {
861
927
  title: "Strata Vault submission status",
862
928
  description: "Read the durable outcome of a submitted Vault transaction: submitted, confirmed, or failed.",
863
929
  inputSchema: {
@@ -874,7 +940,7 @@ export async function createStrataMcpServer(options = {}) {
874
940
  return toolResult(response, `Vault ${response.action} is ${response.status}`
875
941
  + `${response.failure_code ? ` (${response.failure_code})` : ""}.`);
876
942
  });
877
- server.registerTool("strata_rewards", {
943
+ registerTool("strata_rewards", {
878
944
  title: "Strata rewards",
879
945
  description: "Read the current rewards season, standings, and optional owner score.",
880
946
  inputSchema: {
@@ -891,7 +957,7 @@ export async function createStrataMcpServer(options = {}) {
891
957
  const response = await platformClient.rewards.read({ walletAddress, limit });
892
958
  return toolResult(response, `${response.standings.length} reward standings returned.`);
893
959
  });
894
- server.registerTool("strata_referrals", {
960
+ registerTool("strata_referrals", {
895
961
  title: "Strata referrals",
896
962
  description: "Read an owner's referral state and exact claimable reward atoms.",
897
963
  inputSchema: {
@@ -907,7 +973,7 @@ export async function createStrataMcpServer(options = {}) {
907
973
  const response = await platformClient.referrals.read(walletAddress);
908
974
  return toolResult(response, response.enabled ? "Referral state returned." : "Referrals are disabled.");
909
975
  });
910
- server.registerTool("strata_referral_link", {
976
+ registerTool("strata_referral_link", {
911
977
  title: "Link a Strata referral",
912
978
  description: "Prepare or submit an externally authorized referral link for a new wallet.",
913
979
  inputSchema: {
@@ -936,7 +1002,7 @@ export async function createStrataMcpServer(options = {}) {
936
1002
  });
937
1003
  return toolResult(response, "Referral link is pending the wallet's first fill.");
938
1004
  });
939
- server.registerTool("strata_referral_claim", {
1005
+ registerTool("strata_referral_claim", {
940
1006
  title: "Claim Strata referral rewards",
941
1007
  description: "Prepare or submit an externally authorized request for currently claimable referral rewards.",
942
1008
  inputSchema: {
@@ -967,7 +1033,7 @@ export async function createStrataMcpServer(options = {}) {
967
1033
  });
968
1034
  return toolResult(response, `${response.claimable_atoms} referral reward atoms requested.`);
969
1035
  });
970
- server.registerTool("strata_bugs", {
1036
+ registerTool("strata_bugs", {
971
1037
  title: "Strata bug reports",
972
1038
  description: "Read an owner's redacted bug reports and confirmed points.",
973
1039
  inputSchema: {
@@ -983,7 +1049,7 @@ export async function createStrataMcpServer(options = {}) {
983
1049
  const response = await platformClient.bugs.read(walletAddress);
984
1050
  return toolResult(response, `${response.reports.length} redacted bug reports returned.`);
985
1051
  });
986
- server.registerTool("strata_bug_submit", {
1052
+ registerTool("strata_bug_submit", {
987
1053
  title: "Submit Strata bug report",
988
1054
  description: "Prepare or submit a bug report. Omit authorizationSignature to receive the exact "
989
1055
  + "payload for the owner wallet to sign externally; provide that hex signature to submit.",
@@ -1013,7 +1079,7 @@ export async function createStrataMcpServer(options = {}) {
1013
1079
  });
1014
1080
  return toolResult(response, `Bug report ${response.bug_id} is pending review.`);
1015
1081
  });
1016
- const markets = server.registerTool("strata_markets", {
1082
+ const markets = registerTool("strata_markets", {
1017
1083
  title: "Strata markets",
1018
1084
  description: "List Strata markets and their current Sonar quote availability.",
1019
1085
  inputSchema: {
@@ -1048,7 +1114,7 @@ export async function createStrataMcpServer(options = {}) {
1048
1114
  ? `; ${identified} carry a market_id — pass it as marketId to every by-market tool.`
1049
1115
  : "."));
1050
1116
  }));
1051
- const quote = server.registerTool("strata_quote", {
1117
+ const quote = registerTool("strata_quote", {
1052
1118
  title: "Sonar quote",
1053
1119
  description: "Request a short-lived Sonar quote for a Strata market. Returns expected "
1054
1120
  + "output, minimum output, fees, price impact, and expiry.",
@@ -1091,7 +1157,7 @@ export async function createStrataMcpServer(options = {}) {
1091
1157
  const response = await client.quote(request);
1092
1158
  return toolResult(response, quoteSummary(response));
1093
1159
  }));
1094
- const exactOutputQuote = server.registerTool("strata_exact_output_quote", {
1160
+ const exactOutputQuote = registerTool("strata_exact_output_quote", {
1095
1161
  title: "Sonar exact-output quote",
1096
1162
  description: "Request a short-lived Sonar quote for an exact output amount (for example: buy "
1097
1163
  + "1 SOL). Strata inverts its best route and returns the input that delivers it as "
@@ -1138,7 +1204,7 @@ export async function createStrataMcpServer(options = {}) {
1138
1204
  const response = await client.quote(request);
1139
1205
  return toolResult(response, quoteSummary(response));
1140
1206
  }));
1141
- server.registerTool("strata_swap_quote", {
1207
+ registerTool("strata_swap_quote", {
1142
1208
  title: "Sonar asset swap quote",
1143
1209
  description: "Request short-lived exact-input customer economics between two opaque Strata asset IDs.",
1144
1210
  inputSchema: {
@@ -1164,7 +1230,7 @@ export async function createStrataMcpServer(options = {}) {
1164
1230
  + `${response.amount_out_atoms} user-net output atoms; minimum `
1165
1231
  + `${response.minimum_output_atoms}; expires at ${response.expires_at_ms}.`);
1166
1232
  });
1167
- const executionChallenge = server.registerTool("strata_execution_challenge", {
1233
+ const executionChallenge = registerTool("strata_execution_challenge", {
1168
1234
  title: "Strata execution challenge",
1169
1235
  description: "Request canonical quote-bound authorization bytes for the external signer configured by the agent owner.",
1170
1236
  inputSchema: {
@@ -1200,7 +1266,7 @@ export async function createStrataMcpServer(options = {}) {
1200
1266
  const response = await client.executionChallenge(request);
1201
1267
  return toolResult(response, `Authorization challenge ${response.challenge_id}; expires at ${response.expires_at_ms}.`);
1202
1268
  }));
1203
- const executionPrepare = server.registerTool("strata_execution_prepare", {
1269
+ const executionPrepare = registerTool("strata_execution_prepare", {
1204
1270
  title: "Prepare Strata execution",
1205
1271
  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
1272
  inputSchema: {
@@ -1251,7 +1317,7 @@ export async function createStrataMcpServer(options = {}) {
1251
1317
  const response = await client.executionPrepare(request);
1252
1318
  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
1319
  }));
1254
- const executionSubmit = server.registerTool("strata_execution_submit", {
1320
+ const executionSubmit = registerTool("strata_execution_submit", {
1255
1321
  title: "Submit Strata execution",
1256
1322
  description: "Submit an externally signed prepared transaction with an idempotency key.",
1257
1323
  inputSchema: {
@@ -1289,7 +1355,7 @@ export async function createStrataMcpServer(options = {}) {
1289
1355
  const response = await client.executionSubmit(request);
1290
1356
  return toolResult(response, `Submitted execution ${response.execution_id} as ${response.signature}.`);
1291
1357
  }));
1292
- const orderChallenge = server.registerTool("strata_order_challenge", {
1358
+ const orderChallenge = registerTool("strata_order_challenge", {
1293
1359
  title: "Strata order challenge",
1294
1360
  description: "Bind an atomic place, cancel, cancel-all, replace, or bounded batch request to canonical bytes for the agent owner's external signer.",
1295
1361
  inputSchema: {
@@ -1334,7 +1400,7 @@ export async function createStrataMcpServer(options = {}) {
1334
1400
  const response = await platformClient.orders.challenge(args.marketId, request);
1335
1401
  return toolResult(response, `Order challenge ${response.challenge_id} binds ${response.order_ids.length} opaque order ID(s); expires at ${response.expires_at_ms}.`);
1336
1402
  }));
1337
- const orderPrepare = server.registerTool("strata_order_prepare", {
1403
+ const orderPrepare = registerTool("strata_order_prepare", {
1338
1404
  title: "Prepare Strata order control",
1339
1405
  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
1406
  inputSchema: {
@@ -1398,7 +1464,7 @@ export async function createStrataMcpServer(options = {}) {
1398
1464
  const response = await platformClient.orders.prepare(args.marketId, { operation: mapped });
1399
1465
  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
1466
  }));
1401
- const orderSubmit = server.registerTool("strata_order_submit", {
1467
+ const orderSubmit = registerTool("strata_order_submit", {
1402
1468
  title: "Submit Strata order control",
1403
1469
  description: "Submit an externally signed order transaction with a stable retry key.",
1404
1470
  inputSchema: {
@@ -1425,7 +1491,7 @@ export async function createStrataMcpServer(options = {}) {
1425
1491
  });
1426
1492
  return toolResult(response, `Submitted ${response.action} control ${response.order_control_id} as ${response.signature}.`);
1427
1493
  }));
1428
- const orderStatus = server.registerTool("strata_order_status", {
1494
+ const orderStatus = registerTool("strata_order_status", {
1429
1495
  title: "Read Strata order-control status",
1430
1496
  description: "Recover the durable result for an externally signed order submission after a timeout or restart.",
1431
1497
  inputSchema: {
@@ -1443,7 +1509,7 @@ export async function createStrataMcpServer(options = {}) {
1443
1509
  const response = await platformClient.orders.status(marketId, { orderControlId, idempotencyKey });
1444
1510
  return toolResult(response, `Order control ${response.order_control_id} is ${response.status}.`);
1445
1511
  }));
1446
- const makerStrandPrepare = server.registerTool("strata_market_making_strand_prepare", {
1512
+ const makerStrandPrepare = registerTool("strata_market_making_strand_prepare", {
1447
1513
  title: "Prepare Strata Strand control",
1448
1514
  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
1515
  inputSchema: {
@@ -1516,7 +1582,7 @@ export async function createStrataMcpServer(options = {}) {
1516
1582
  const response = await platformClient.marketMaking.strand.prepare(args.marketId, request);
1517
1583
  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
1584
  }));
1519
- const makerStrandSubmit = server.registerTool("strata_market_making_strand_submit", {
1585
+ const makerStrandSubmit = registerTool("strata_market_making_strand_submit", {
1520
1586
  title: "Submit Strata Strand control",
1521
1587
  description: "Submit the exact externally maker-signed Strand transaction with a stable retry key.",
1522
1588
  inputSchema: {
@@ -1539,9 +1605,9 @@ export async function createStrataMcpServer(options = {}) {
1539
1605
  });
1540
1606
  return toolResult(response, `Submitted ${response.action} as ${response.signature}.`);
1541
1607
  }));
1542
- const makerCurrentPrepare = server.registerTool("strata_market_making_current_prepare", {
1608
+ const makerCurrentPrepare = registerTool("strata_market_making_current_prepare", {
1543
1609
  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.",
1610
+ 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
1611
  inputSchema: {
1546
1612
  marketId: z.string().regex(/^market_[0-9a-f]{32}$/),
1547
1613
  action: z.enum(["upsert", "cancel"]),
@@ -1599,7 +1665,7 @@ export async function createStrataMcpServer(options = {}) {
1599
1665
  const response = await platformClient.marketMaking.current.prepare(args.marketId, request);
1600
1666
  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
1667
  }));
1602
- const makerCurrentSubmit = server.registerTool("strata_market_making_current_submit", {
1668
+ const makerCurrentSubmit = registerTool("strata_market_making_current_submit", {
1603
1669
  title: "Submit Strata Current control",
1604
1670
  description: "Submit the exact externally maker-signed Current transaction with a stable retry key.",
1605
1671
  inputSchema: {
@@ -1622,8 +1688,8 @@ export async function createStrataMcpServer(options = {}) {
1622
1688
  });
1623
1689
  return toolResult(response, `Submitted ${response.action} as ${response.signature}.`);
1624
1690
  }));
1625
- registerAutonomyTools(server, client, platformClient, options.sessionAutonomy, () => typeof Date !== "undefined" ? Date.now() : 0);
1626
- const handles = {
1691
+ registerAutonomyTools(registerTool, client, platformClient, options.sessionAutonomy, () => typeof Date !== "undefined" ? Date.now() : 0);
1692
+ void [
1627
1693
  markets,
1628
1694
  quote,
1629
1695
  exactOutputQuote,
@@ -1638,13 +1704,17 @@ export async function createStrataMcpServer(options = {}) {
1638
1704
  makerStrandSubmit,
1639
1705
  makerCurrentPrepare,
1640
1706
  makerCurrentSubmit,
1641
- };
1642
- applyCapabilityCatalog(handles, initialCatalog);
1707
+ ];
1708
+ applyToolAvailability(registeredTools, initialCatalog, initialPlatformCatalog);
1643
1709
  let closed = false;
1644
1710
  const refresh = async () => {
1645
1711
  if (closed)
1646
1712
  return;
1647
- applyCapabilityCatalog(handles, await client.capabilities());
1713
+ const [catalog, platformCatalog] = await Promise.all([
1714
+ client.capabilities(),
1715
+ platformClient.discovery.read(),
1716
+ ]);
1717
+ applyToolAvailability(registeredTools, catalog, platformCatalog);
1648
1718
  };
1649
1719
  const timer = setInterval(() => {
1650
1720
  refresh().catch((error) => {
@@ -1662,10 +1732,10 @@ export async function createStrataMcpServer(options = {}) {
1662
1732
  },
1663
1733
  };
1664
1734
  }
1665
- function registerAutonomyTools(server, client, platformClient, autonomy, nowMs) {
1735
+ function registerAutonomyTools(registerTool, client, platformClient, autonomy, nowMs) {
1666
1736
  // Always present, always read-only: the agent may show the slider and offer
1667
1737
  // to change it, but nothing it calls can raise its own autonomy.
1668
- server.registerTool("strata_autonomy", {
1738
+ registerTool("strata_autonomy", {
1669
1739
  title: "Strata session autonomy",
1670
1740
  description: "Read how much this MCP may finish by itself: the autonomy level (ask / limits / instant), "
1671
1741
  + "any USD ceilings, and how to change it. Read-only — the level is the user's, set out-of-band "
@@ -1730,7 +1800,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1730
1800
  };
1731
1801
  const refuse = (reason, prepared, summary) => toolResult({ executed: false, reason, prepared }, summary);
1732
1802
  // ── one-shot immediate execution from a fresh Sonar quote ─────────────────
1733
- server.registerTool("strata_execute_quote", {
1803
+ registerTool("strata_execute_quote", {
1734
1804
  title: "Execute a Strata quote",
1735
1805
  description: "Take a fresh Sonar quote and, within the autonomy slider, sign it with the session key and "
1736
1806
  + "submit it in one call. Under \"ask\" (or over a \"limits\" ceiling) it does not sign — it returns "
@@ -1775,7 +1845,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1775
1845
  return toolResult({ executed: true, receipt, notional_usd: notional }, `Executed ${quote.side} on ${quote.market_id} as ${receipt.signature}.`);
1776
1846
  }));
1777
1847
  // ── one-shot order control (place / cancel / replace / batch) ─────────────
1778
- server.registerTool("strata_order_execute", {
1848
+ registerTool("strata_order_execute", {
1779
1849
  title: "Execute a Strata order control",
1780
1850
  description: "Place, cancel, replace, or batch orders and, within the autonomy slider, sign with the session "
1781
1851
  + "key and submit in one call. Owner wallet and session key come from the configured session. "
@@ -1837,7 +1907,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1837
1907
  return toolResult({ executed: true, receipt, notional_usd: notional }, `Executed ${receipt.action} control ${receipt.order_control_id} as ${receipt.signature}.`);
1838
1908
  }));
1839
1909
  // ── one-shot TWAP (schedule / cancel) ─────────────────────────────────────
1840
- server.registerTool("strata_twap_execute", {
1910
+ registerTool("strata_twap_execute", {
1841
1911
  title: "Execute a Strata TWAP",
1842
1912
  description: "Schedule or cancel a TWAP and, within the autonomy slider, sign with the session key and submit "
1843
1913
  + "in one call. Owner wallet and session key come from the configured session. Under \"ask\" (or over "
@@ -1860,7 +1930,7 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1860
1930
  idempotentHint: false,
1861
1931
  openWorldHint: true,
1862
1932
  },
1863
- }, async (args) => guardedTool(client, "algos.submit", async () => {
1933
+ }, async (args) => safeTool(async () => {
1864
1934
  let operation;
1865
1935
  if (args.action === "cancel") {
1866
1936
  if (args.twapId === undefined)
@@ -1909,23 +1979,36 @@ function registerAutonomyTools(server, client, platformClient, autonomy, nowMs)
1909
1979
  return toolResult({ executed: true, receipt, notional_usd: notional }, `Executed TWAP ${receipt.twap_control_id} as ${receipt.signature}.`);
1910
1980
  }));
1911
1981
  }
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);
1982
+ function trackedToolRegistrar(server) {
1983
+ const handles = new Map();
1984
+ const register = server.registerTool.bind(server);
1985
+ const registerTool = ((...args) => {
1986
+ const tool = register(...args);
1987
+ const name = args[0];
1988
+ if (typeof name === "string")
1989
+ handles.set(name, tool);
1990
+ return tool;
1991
+ });
1992
+ return { registerTool, handles };
1993
+ }
1994
+ function platformCapabilityAvailable(catalog, id) {
1995
+ return catalog.capabilities.some((capability) => capability.id === id
1996
+ && capability.transports.includes("mcp")
1997
+ && capability.mcp_exposure !== "none");
1998
+ }
1999
+ function requirementAvailable(requirement, available) {
2000
+ if (requirement === undefined)
2001
+ return true;
2002
+ return requirement.match === "any"
2003
+ ? requirement.ids.some(available)
2004
+ : requirement.ids.every(available);
2005
+ }
2006
+ function applyToolAvailability(handles, catalog, platformCatalog) {
2007
+ for (const [name, tool] of handles) {
2008
+ const legacyAvailable = requirementAvailable(LEGACY_TOOL_CAPABILITIES[name], (id) => capabilityAvailable(catalog, id));
2009
+ const platformAvailable = requirementAvailable(PLATFORM_TOOL_CAPABILITIES[name], (id) => platformCapabilityAvailable(platformCatalog, id));
2010
+ setToolEnabled(tool, legacyAvailable && platformAvailable);
2011
+ }
1929
2012
  }
1930
2013
  function setToolEnabled(tool, enabled) {
1931
2014
  if (enabled)
@@ -1948,6 +2031,17 @@ async function guardedTool(client, capabilityId, operation) {
1948
2031
  return toolError("request_failed", safeMessage(error), true);
1949
2032
  }
1950
2033
  }
2034
+ async function safeTool(operation) {
2035
+ try {
2036
+ return await operation();
2037
+ }
2038
+ catch (error) {
2039
+ if (error instanceof StrataApiError) {
2040
+ return toolError(error.code, error.message, error.retryable);
2041
+ }
2042
+ return toolError("request_failed", safeMessage(error), true);
2043
+ }
2044
+ }
1951
2045
  /**
1952
2046
  * One line that keeps the two numbers apart: price impact is measured from the
1953
2047
  * 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.3",
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.3",
48
48
  "zod": "^3.25.76"
49
49
  },
50
50
  "devDependencies": {