@stratabook/mcp 0.2.5 → 0.2.6

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
@@ -91,13 +91,15 @@ For normal maker operation, use two calls:
91
91
  such as `0.01 SOL`, and the maker wallet. Duration defaults to ten minutes
92
92
  and levels default to three.
93
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`.
94
+ then pass it, `prepared.maker_control_id`, and the unchanged
95
+ `preparationToken` to `strata_market_making_submit_and_wait`.
96
96
 
97
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.
98
+ the exact product settings. The token contains no signing authority and keeps
99
+ the two-call flow working across stateless HTTP requests or an MCP process
100
+ restart. Use `action: "stop"` through the same pair. The older product-specific
101
+ tools remain available for strategies that deliberately manage every low-level
102
+ array and safety field.
101
103
 
102
104
  For maker funding, initialize the market Vault if needed, activate the Strand
103
105
  or Current, then deposit with `strata_vault_deposit`. The market keeps that
@@ -17,7 +17,7 @@ export declare const STRATA_AGENT_HARNESS: {
17
17
  readonly manifest: "https://api.stratabook.app/.well-known/strata-agent.json";
18
18
  };
19
19
  readonly interfaces: {
20
- readonly mcp_tool_order: readonly ["strata_capabilities", "strata_action_graph", "strata_platform_graph", "strata_status", "strata_markets", "strata_marks", "strata_candles", "strata_twaps", "strata_twap_challenge", "strata_twap_cancel", "strata_twap_prepare", "strata_twap_submit", "strata_portfolio", "strata_portfolio_history", "strata_market_making_status", "strata_market_making_reputation", "strata_vault_status", "strata_vault_setup", "strata_vault_deposit", "strata_vault_withdraw", "strata_vault_delegate", "strata_vault_policy", "strata_vault_pause", "strata_vault_submit", "strata_vault_submission", "strata_rewards", "strata_referrals", "strata_referral_link", "strata_referral_claim", "strata_bugs", "strata_bug_submit", "strata_quote", "strata_exact_output_quote", "strata_swap_quote", "strata_execution_challenge", "strata_execution_prepare", "strata_execution_submit", "strata_execution_status", "strata_order_challenge", "strata_order_prepare", "strata_order_submit", "strata_order_status"];
20
+ readonly mcp_tool_order: readonly ["strata_capabilities", "strata_action_graph", "strata_platform_graph", "strata_status", "strata_markets", "strata_marks", "strata_candles", "strata_twaps", "strata_twap_challenge", "strata_twap_cancel", "strata_twap_prepare", "strata_twap_submit", "strata_portfolio", "strata_portfolio_history", "strata_market_making_status", "strata_market_making_reputation", "strata_market_making_prepare", "strata_market_making_submit_and_wait", "strata_vault_status", "strata_vault_setup", "strata_vault_deposit", "strata_vault_withdraw", "strata_vault_delegate", "strata_vault_policy", "strata_vault_pause", "strata_vault_submit", "strata_vault_submission", "strata_rewards", "strata_referrals", "strata_referral_link", "strata_referral_claim", "strata_bugs", "strata_bug_submit", "strata_quote", "strata_exact_output_quote", "strata_swap_quote", "strata_execution_challenge", "strata_execution_prepare", "strata_execution_submit", "strata_execution_status", "strata_order_challenge", "strata_order_prepare", "strata_order_submit", "strata_order_status"];
21
21
  readonly terminal: readonly ["npx -y @stratabook/sdk capabilities --json", "npx -y @stratabook/sdk action-graph --json", "npx -y @stratabook/sdk platform-graph --json", "npx -y @stratabook/sdk platform-status --json", "npx -y @stratabook/sdk mark --market-id MARKET_ID --json", "npx -y @stratabook/sdk candles --market-id MARKET_ID --from-ms FROM_MS --to-ms TO_MS --resolution-seconds 300 --json", "npx -y @stratabook/sdk execution-status --market-id MARKET_ID --execution-id EXECUTION_ID --json", "npx -y @stratabook/sdk twaps --market-id MARKET_ID --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk twap-challenge --market-id MARKET_ID --owner-wallet OWNER_PUBLIC_KEY --session-public-key SESSION_PUBLIC_KEY --side buy --total-size-atoms TOTAL_ATOMS --slices 10 --tolerance-bps 100 --interval-slots 100 --limit-price-atoms PRICE_ATOMS --json", "npx -y @stratabook/sdk twap-cancel --market-id MARKET_ID --owner-wallet OWNER_PUBLIC_KEY --session-public-key SESSION_PUBLIC_KEY --twap-id TWAP_ID --json", "npx -y @stratabook/sdk twap-prepare --market-id MARKET_ID --owner-wallet OWNER_PUBLIC_KEY --session-public-key SESSION_PUBLIC_KEY --side buy --total-size-atoms TOTAL_ATOMS --slices 10 --tolerance-bps 100 --interval-slots 100 --limit-price-atoms PRICE_ATOMS --json", "npx -y @stratabook/sdk twap-submit --market-id MARKET_ID --twap-control-id CONTROL_ID --signed-transaction-base64 TRANSACTION --idempotency-key KEY --json", "npx -y @stratabook/sdk account --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk portfolio-history --wallet WALLET_PUBLIC_KEY --range 24h --json", "npx -y @stratabook/sdk maker-status --market-id MARKET_ID --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk maker-reputation --market-id MARKET_ID --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk vault-status --wallet WALLET_PUBLIC_KEY --session-public-key SESSION_PUBLIC_KEY --json", "npx -y @stratabook/sdk session-keygen --json", "npx -y @stratabook/sdk vault-setup --wallet WALLET_PUBLIC_KEY --session-public-key SESSION_PUBLIC_KEY --json", "npx -y @stratabook/sdk vault-deposit --wallet WALLET_PUBLIC_KEY --market-id MARKET_ID --asset-id ASSET_ID --amount-atoms AMOUNT --session-public-key SESSION_PUBLIC_KEY --json", "npx -y @stratabook/sdk vault-withdraw --wallet WALLET_PUBLIC_KEY --market-id MARKET_ID --asset-id ASSET_ID --destination-wallet DESTINATION_WALLET --amount-atoms AMOUNT --json", "npx -y @stratabook/sdk vault-delegate --wallet WALLET_PUBLIC_KEY --session-public-key SESSION_PUBLIC_KEY --action revoke --json", "npx -y @stratabook/sdk vault-policy --wallet WALLET_PUBLIC_KEY --mode restricted --allowed-wallets DESTINATION_WALLET --json", "npx -y @stratabook/sdk vault-pause --wallet WALLET_PUBLIC_KEY --paused true --json", "npx -y @stratabook/sdk rewards --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk referrals --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk referral-link --wallet WALLET_PUBLIC_KEY --code REFERRAL_CODE --json", "npx -y @stratabook/sdk referral-claim --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk bugs --wallet WALLET_PUBLIC_KEY --json", "npx -y @stratabook/sdk bug-payload --message REPORT_TEXT --json", "npx -y @stratabook/sdk markets --json", "npx -y @stratabook/sdk quote --market SOL/USDC --side sell --amount-atoms 10000000 --json", "npx -y @stratabook/sdk swap-quote --input-asset-id INPUT_ASSET_ID --output-asset-id OUTPUT_ASSET_ID --amount-atoms 10000000 --json", "npx -y @stratabook/sdk order-slo --market-id MARKET_ID --owner-wallet OWNER_PUBLIC_KEY --json"];
22
22
  };
23
23
  readonly workflow: readonly [{
@@ -551,4 +551,4 @@ export declare const STRATA_ACTION_GRAPH: {
551
551
  }];
552
552
  };
553
553
  export declare const STRATA_ACTION_GRAPH_URI = "strata://action-graph/v1";
554
- export declare const STRATA_AGENT_HARNESS_INSTRUCTIONS = "Strata Agent Harness 1.0. Start every objective with strata_capabilities, then strata_action_graph, then strata_platform_graph, then strata_status, then strata_markets. Read strata://agent-harness/v1, strata://action-graph/v1, and strata://platform-graph/v2. The external agent owner controls permission and signer authority. Strata accepts public keys, detached signatures, and signed transactions, never private keys or seed phrases. Resolve a market and side or catalog input/output asset IDs, plus exact input atoms and tolerance, before strata_quote or strata_swap_quote. When portfolio.read is live, read the owner's live Vault portfolio before sizing any action and treat null USD totals as an incomplete valuation, never as zero. When mm.status.read is live, reconcile the owner's own maker products, exposure, and dead-man guards through the authorized status read before and after every maker action, and when mm.fills.stream is live keep the official SDK's authenticated maker stream open to apply contiguous maker fills and exposure changes. When algos.twap.stream is live, follow TWAP progress through the official SDK's sequenced TWAP stream instead of polling, and when execution.stream is live watch prepared execution handles through the sequenced execution stream. Before selecting a maker transport, read the externally authorized owner-scoped maker reputation record and follow its signed-quote eligibility, cadence, and tier-progress fields. Treat amounts as unsigned base-10 token atoms; check quote bindings, labelled fees, minimum output, and expiry. To execute or control a resting order: request a challenge, verify its quote or exact opaque order bindings, sign canonical authorization bytes externally, prepare, verify and sign the returned transaction externally, then submit with idempotency. When websocket order transport is live, prefer the official SDK persistent command stream, choose explicit self-trade prevention, keep its durable dead-man guard armed for resting exposure, and distinguish immediate RPC broadcast from pushed terminal chain status. If submission is ambiguous, recover durable status with the same control ID and idempotency key. Order control supports place, cancel, bounded cancel-all, atomic replace, and atomic heterogeneous batches of up to six operations. Stop on ambiguity, sequence gaps, unavailable capabilities, paused markets, unsupported contracts, inconsistent bindings, expiry, or missing signer authority.";
554
+ export declare const STRATA_AGENT_HARNESS_INSTRUCTIONS = "Strata Agent Harness 1.0. Start every objective with strata_capabilities, then strata_action_graph, then strata_platform_graph, then strata_status, then strata_markets. Read strata://agent-harness/v1, strata://action-graph/v1, and strata://platform-graph/v2. The external agent owner controls permission and signer authority. Strata accepts public keys, detached signatures, and signed transactions, never private keys or seed phrases. Resolve a market and side or catalog input/output asset IDs, plus exact input atoms and tolerance, before strata_quote or strata_swap_quote. When portfolio.read is live, read the owner's live Vault portfolio before sizing any action and treat null USD totals as an incomplete valuation, never as zero. When mm.status.read is live, reconcile the owner's own maker products, exposure, and dead-man guards through the authorized status read before and after every maker action, and when mm.fills.stream is live keep the official SDK's authenticated maker stream open to apply contiguous maker fills and exposure changes. For normal Strand or Current operation, prefer strata_market_making_prepare, sign only its prepared transaction externally, then pass its unchanged preparationToken with the signed transaction to strata_market_making_submit_and_wait; the token carries no signing authority and survives stateless HTTP requests. When algos.twap.stream is live, follow TWAP progress through the official SDK's sequenced TWAP stream instead of polling, and when execution.stream is live watch prepared execution handles through the sequenced execution stream. Before selecting a maker transport, read the externally authorized owner-scoped maker reputation record and follow its signed-quote eligibility, cadence, and tier-progress fields. Treat amounts as unsigned base-10 token atoms; check quote bindings, labelled fees, minimum output, and expiry. To execute or control a resting order: request a challenge, verify its quote or exact opaque order bindings, sign canonical authorization bytes externally, prepare, verify and sign the returned transaction externally, then submit with idempotency. When websocket order transport is live, prefer the official SDK persistent command stream, choose explicit self-trade prevention, keep its durable dead-man guard armed for resting exposure, and distinguish immediate RPC broadcast from pushed terminal chain status. If submission is ambiguous, recover durable status with the same control ID and idempotency key. Order control supports place, cancel, bounded cancel-all, atomic replace, and atomic heterogeneous batches of up to six operations. Stop on ambiguity, sequence gaps, unavailable capabilities, paused markets, unsupported contracts, inconsistent bindings, expiry, or missing signer authority.";
@@ -35,6 +35,8 @@ export const STRATA_AGENT_HARNESS = {
35
35
  "strata_portfolio_history",
36
36
  "strata_market_making_status",
37
37
  "strata_market_making_reputation",
38
+ "strata_market_making_prepare",
39
+ "strata_market_making_submit_and_wait",
38
40
  "strata_vault_status",
39
41
  "strata_vault_setup",
40
42
  "strata_vault_deposit",
@@ -786,4 +788,4 @@ export const STRATA_ACTION_GRAPH = {
786
788
  ]
787
789
  };
788
790
  export const STRATA_ACTION_GRAPH_URI = "strata://action-graph/v1";
789
- export const STRATA_AGENT_HARNESS_INSTRUCTIONS = "Strata Agent Harness 1.0. Start every objective with strata_capabilities, then strata_action_graph, then strata_platform_graph, then strata_status, then strata_markets. Read strata://agent-harness/v1, strata://action-graph/v1, and strata://platform-graph/v2. The external agent owner controls permission and signer authority. Strata accepts public keys, detached signatures, and signed transactions, never private keys or seed phrases. Resolve a market and side or catalog input/output asset IDs, plus exact input atoms and tolerance, before strata_quote or strata_swap_quote. When portfolio.read is live, read the owner's live Vault portfolio before sizing any action and treat null USD totals as an incomplete valuation, never as zero. When mm.status.read is live, reconcile the owner's own maker products, exposure, and dead-man guards through the authorized status read before and after every maker action, and when mm.fills.stream is live keep the official SDK's authenticated maker stream open to apply contiguous maker fills and exposure changes. When algos.twap.stream is live, follow TWAP progress through the official SDK's sequenced TWAP stream instead of polling, and when execution.stream is live watch prepared execution handles through the sequenced execution stream. Before selecting a maker transport, read the externally authorized owner-scoped maker reputation record and follow its signed-quote eligibility, cadence, and tier-progress fields. Treat amounts as unsigned base-10 token atoms; check quote bindings, labelled fees, minimum output, and expiry. To execute or control a resting order: request a challenge, verify its quote or exact opaque order bindings, sign canonical authorization bytes externally, prepare, verify and sign the returned transaction externally, then submit with idempotency. When websocket order transport is live, prefer the official SDK persistent command stream, choose explicit self-trade prevention, keep its durable dead-man guard armed for resting exposure, and distinguish immediate RPC broadcast from pushed terminal chain status. If submission is ambiguous, recover durable status with the same control ID and idempotency key. Order control supports place, cancel, bounded cancel-all, atomic replace, and atomic heterogeneous batches of up to six operations. Stop on ambiguity, sequence gaps, unavailable capabilities, paused markets, unsupported contracts, inconsistent bindings, expiry, or missing signer authority.";
791
+ export const STRATA_AGENT_HARNESS_INSTRUCTIONS = "Strata Agent Harness 1.0. Start every objective with strata_capabilities, then strata_action_graph, then strata_platform_graph, then strata_status, then strata_markets. Read strata://agent-harness/v1, strata://action-graph/v1, and strata://platform-graph/v2. The external agent owner controls permission and signer authority. Strata accepts public keys, detached signatures, and signed transactions, never private keys or seed phrases. Resolve a market and side or catalog input/output asset IDs, plus exact input atoms and tolerance, before strata_quote or strata_swap_quote. When portfolio.read is live, read the owner's live Vault portfolio before sizing any action and treat null USD totals as an incomplete valuation, never as zero. When mm.status.read is live, reconcile the owner's own maker products, exposure, and dead-man guards through the authorized status read before and after every maker action, and when mm.fills.stream is live keep the official SDK's authenticated maker stream open to apply contiguous maker fills and exposure changes. For normal Strand or Current operation, prefer strata_market_making_prepare, sign only its prepared transaction externally, then pass its unchanged preparationToken with the signed transaction to strata_market_making_submit_and_wait; the token carries no signing authority and survives stateless HTTP requests. When algos.twap.stream is live, follow TWAP progress through the official SDK's sequenced TWAP stream instead of polling, and when execution.stream is live watch prepared execution handles through the sequenced execution stream. Before selecting a maker transport, read the externally authorized owner-scoped maker reputation record and follow its signed-quote eligibility, cadence, and tier-progress fields. Treat amounts as unsigned base-10 token atoms; check quote bindings, labelled fees, minimum output, and expiry. To execute or control a resting order: request a challenge, verify its quote or exact opaque order bindings, sign canonical authorization bytes externally, prepare, verify and sign the returned transaction externally, then submit with idempotency. When websocket order transport is live, prefer the official SDK persistent command stream, choose explicit self-trade prevention, keep its durable dead-man guard armed for resting exposure, and distinguish immediate RPC broadcast from pushed terminal chain status. If submission is ambiguous, recover durable status with the same control ID and idempotency key. Order control supports place, cancel, bounded cancel-all, atomic replace, and atomic heterogeneous batches of up to six operations. Stop on ambiguity, sequence gaps, unavailable capabilities, paused markets, unsupported contracts, inconsistent bindings, expiry, or missing signer authority.";
@@ -6,6 +6,155 @@ 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 makerPublicKeySchema = z.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/);
10
+ const makerMarketIdSchema = z.string().regex(/^market_[0-9a-f]{32}$/);
11
+ const makerAssetIdSchema = z.string().regex(/^asset_[0-9a-f]{32}$/);
12
+ const makerControlIdSchema = z.string().regex(/^mc_[0-9a-f]{32}$/);
13
+ const makerAtomicSchema = z.string().regex(/^(?:0|[1-9][0-9]*)$/).max(20);
14
+ const makerPositiveAtomicSchema = z.string().regex(/^[1-9][0-9]*$/).max(20);
15
+ const makerBase64Schema = z.string().min(4).max(4_096)
16
+ .regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/);
17
+ const makerMarketSchema = z.object({
18
+ market_id: makerMarketIdSchema,
19
+ label: z.string().min(1).max(128),
20
+ base_asset_id: makerAssetIdSchema,
21
+ quote_asset_id: makerAssetIdSchema,
22
+ status: z.enum([
23
+ "active",
24
+ "read_only",
25
+ "quote_only",
26
+ "cancel_only",
27
+ "paused",
28
+ "warming",
29
+ "degraded",
30
+ "unavailable",
31
+ ]),
32
+ available_actions: z.array(z.enum([
33
+ "quote",
34
+ "execute_immediate",
35
+ "place_order",
36
+ "schedule_twap",
37
+ ])).max(4),
38
+ }).strict();
39
+ const makerAssetSchema = z.object({
40
+ asset_id: makerAssetIdSchema,
41
+ symbol: z.string().min(1).max(32),
42
+ name: z.string().min(1).max(128),
43
+ decimals: z.number().int().min(0).max(18),
44
+ logo_url: z.string().url().optional(),
45
+ network: z.literal("solana"),
46
+ }).strict();
47
+ const makerStrandOperationSchema = z.discriminatedUnion("action", [
48
+ z.object({
49
+ action: z.literal("upsert"),
50
+ makerWallet: makerPublicKeySchema,
51
+ enabled: z.boolean(),
52
+ asyncOnly: z.boolean(),
53
+ syncSpreadTicks: z.number().int().min(0).max(65_535),
54
+ midPriceAtoms: makerPositiveAtomicSchema,
55
+ maxExposureBaseAtoms: makerPositiveAtomicSchema,
56
+ bidOffsetsTicks: z.array(z.number().int().min(0).max(65_535)).length(16),
57
+ askOffsetsTicks: z.array(z.number().int().min(0).max(65_535)).length(16),
58
+ bidSizesBaseAtoms: z.array(makerAtomicSchema).length(16),
59
+ askSizesBaseAtoms: z.array(makerAtomicSchema).length(16),
60
+ validUntilSlot: makerPositiveAtomicSchema,
61
+ }).strict(),
62
+ z.object({
63
+ action: z.literal("cancel"),
64
+ makerWallet: makerPublicKeySchema,
65
+ }).strict(),
66
+ ]);
67
+ const makerCurrentOperationSchema = z.discriminatedUnion("action", [
68
+ z.object({
69
+ action: z.literal("upsert"),
70
+ makerWallet: makerPublicKeySchema,
71
+ enabled: z.boolean(),
72
+ asyncOnly: z.boolean(),
73
+ halfSpreadBps: z.number().int().min(1).max(65_535),
74
+ bandStepBps: z.number().int().min(0).max(65_535),
75
+ maxConfidenceBps: z.number().int().min(1).max(100),
76
+ maxOracleDeviationBps: z.number().int().min(1).max(500),
77
+ maxOracleAgeSeconds: z.number().int().min(0).max(4_294_967_295),
78
+ syncSpreadBps: z.number().int().min(0).max(65_535),
79
+ maxExposureBaseAtoms: makerPositiveAtomicSchema,
80
+ bidDepthBaseAtoms: z.array(makerAtomicSchema).length(8),
81
+ askDepthBaseAtoms: z.array(makerAtomicSchema).length(8),
82
+ validUntilSlot: makerPositiveAtomicSchema,
83
+ }).strict(),
84
+ z.object({
85
+ action: z.literal("cancel"),
86
+ makerWallet: makerPublicKeySchema,
87
+ }).strict(),
88
+ ]);
89
+ const makerPreparedResponseSchema = z.object({
90
+ schema_version: z.literal(2),
91
+ contract_version: z.literal("2.0"),
92
+ maker_control_id: makerControlIdSchema,
93
+ market_id: makerMarketIdSchema,
94
+ maker_wallet: makerPublicKeySchema,
95
+ product: z.enum(["strand", "current"]),
96
+ action: z.enum([
97
+ "strand_upsert",
98
+ "strand_recenter",
99
+ "strand_set_enabled",
100
+ "strand_cancel",
101
+ "current_upsert",
102
+ "current_cancel",
103
+ ]),
104
+ transaction_base64: makerBase64Schema,
105
+ recent_blockhash: makerPublicKeySchema,
106
+ last_valid_block_height: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
107
+ expires_at_ms: z.number().int().min(1).max(Number.MAX_SAFE_INTEGER),
108
+ }).strict();
109
+ const makerPreparationSchema = z.object({
110
+ market: makerMarketSchema,
111
+ base_asset: makerAssetSchema.optional(),
112
+ product: z.enum(["strand", "current"]),
113
+ operation: z.union([makerStrandOperationSchema, makerCurrentOperationSchema]),
114
+ prepared: makerPreparedResponseSchema,
115
+ }).strict();
116
+ const makerPreparationEnvelopeSchema = z.object({
117
+ version: z.literal(1),
118
+ preparation: makerPreparationSchema,
119
+ }).strict();
120
+ function encodeMakerPreparationToken(preparation) {
121
+ const checked = makerPreparationSchema.parse(preparation);
122
+ return Buffer.from(JSON.stringify({ version: 1, preparation: checked }), "utf8")
123
+ .toString("base64url");
124
+ }
125
+ function decodeMakerPreparationToken(token) {
126
+ let decoded;
127
+ try {
128
+ const bytes = Buffer.from(token, "base64url");
129
+ if (bytes.toString("base64url") !== token) {
130
+ throw new Error("non-canonical base64url");
131
+ }
132
+ decoded = JSON.parse(bytes.toString("utf8"));
133
+ }
134
+ catch {
135
+ throw new Error("preparationToken is not valid encoded JSON");
136
+ }
137
+ const { preparation } = makerPreparationEnvelopeSchema.parse(decoded);
138
+ const expectedAction = preparation.product === "strand"
139
+ ? preparation.operation.action === "upsert" ? "strand_upsert" : "strand_cancel"
140
+ : preparation.operation.action === "upsert" ? "current_upsert" : "current_cancel";
141
+ if (preparation.prepared.product !== preparation.product
142
+ || preparation.prepared.action !== expectedAction
143
+ || preparation.prepared.market_id !== preparation.market.market_id
144
+ || preparation.prepared.maker_wallet !== preparation.operation.makerWallet
145
+ || (preparation.product === "strand"
146
+ && preparation.operation.action === "upsert"
147
+ && !("midPriceAtoms" in preparation.operation))
148
+ || (preparation.product === "current"
149
+ && preparation.operation.action === "upsert"
150
+ && !("halfSpreadBps" in preparation.operation))
151
+ || (preparation.operation.action === "upsert" && preparation.base_asset === undefined)
152
+ || (preparation.base_asset !== undefined
153
+ && preparation.base_asset.asset_id !== preparation.market.base_asset_id)) {
154
+ throw new Error("preparationToken contains inconsistent maker bindings");
155
+ }
156
+ return preparation;
157
+ }
9
158
  const LEGACY_TOOL_CAPABILITIES = {
10
159
  strata_markets: { ids: ["markets.read"] },
11
160
  strata_quote: { ids: ["quotes.read"] },
@@ -260,7 +409,6 @@ export async function createStrataMcpServer(options = {}) {
260
409
  instructions: STRATA_AGENT_HARNESS_INSTRUCTIONS,
261
410
  });
262
411
  const { registerTool, handles: registeredTools } = trackedToolRegistrar(server);
263
- const makerQuickstartPreparations = new Map();
264
412
  server.registerResource("strata_agent_harness", STRATA_AGENT_HARNESS_URI, {
265
413
  title: "Strata Agent Harness",
266
414
  description: "Canonical capability-gated first-run workflow for Strata agents.",
@@ -1567,13 +1715,6 @@ export async function createStrataMcpServer(options = {}) {
1567
1715
  return toolError("invalid_request", "Starting market making requires spreadBps and size.", false);
1568
1716
  }
1569
1717
  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
1718
  const response = {
1578
1719
  action: args.action,
1579
1720
  market: resolved.market,
@@ -1581,14 +1722,16 @@ export async function createStrataMcpServer(options = {}) {
1581
1722
  ...("base_asset" in resolved ? { base_asset: resolved.base_asset } : {}),
1582
1723
  operation: resolved.operation,
1583
1724
  prepared: resolved.prepared,
1725
+ preparationToken: encodeMakerPreparationToken(resolved),
1584
1726
  };
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.`);
1727
+ return toolResult(response, `Prepared ${args.action} for ${resolved.market.label} as ${resolved.prepared.maker_control_id}. Verify and sign only prepared.transaction_base64, then pass preparationToken unchanged to submit_and_wait within 30 seconds.`);
1586
1728
  }));
1587
1729
  const makerSubmitAndWait = registerTool("strata_market_making_submit_and_wait", {
1588
1730
  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.",
1731
+ description: "Submit the exact externally signed quickstart transaction with the unchanged preparation token and wait until Strata's chain-derived maker state confirms the product started or stopped. The token survives stateless HTTP requests and server restarts; the control ID is its default idempotency key.",
1590
1732
  inputSchema: {
1591
1733
  makerControlId: z.string().regex(/^mc_[0-9a-f]{32}$/),
1734
+ preparationToken: z.string().min(16).max(32_768).regex(/^[A-Za-z0-9_-]+$/),
1592
1735
  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
1736
  idempotencyKey: z.string().min(1).max(64).regex(/^[A-Za-z0-9._-]+$/).optional(),
1594
1737
  confirmationTimeoutMs: z.number().int().min(1_000).max(120_000).optional(),
@@ -1600,9 +1743,15 @@ export async function createStrataMcpServer(options = {}) {
1600
1743
  openWorldHint: true,
1601
1744
  },
1602
1745
  }, 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);
1746
+ let prepared;
1747
+ try {
1748
+ prepared = decodeMakerPreparationToken(args.preparationToken);
1749
+ }
1750
+ catch {
1751
+ return toolError("invalid_request", "preparationToken is invalid. Pass the token returned by strata_market_making_prepare unchanged.", false);
1752
+ }
1753
+ if (prepared.prepared.maker_control_id !== args.makerControlId) {
1754
+ return toolError("binding_mismatch", "makerControlId does not match preparationToken.", false);
1606
1755
  }
1607
1756
  return guardedTool(client, `mm.${prepared.product}.manage`, async () => {
1608
1757
  const response = await platformClient.marketMaking.submitPrepared({
@@ -1613,7 +1762,6 @@ export async function createStrataMcpServer(options = {}) {
1613
1762
  ? {}
1614
1763
  : { confirmationTimeoutMs: args.confirmationTimeoutMs }),
1615
1764
  });
1616
- makerQuickstartPreparations.delete(args.makerControlId);
1617
1765
  return toolResult(response, `${response.product} is confirmed ${response.operation.action === "cancel" ? "stopped" : "live"} on ${response.market.label}.`);
1618
1766
  });
1619
1767
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratabook/mcp",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
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.5",
47
+ "@stratabook/sdk": "0.2.6",
48
48
  "zod": "^3.25.76"
49
49
  },
50
50
  "devDependencies": {