@stratabook/mcp 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/cli.js CHANGED
@@ -3,7 +3,8 @@ import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
5
5
  import { DEFAULT_API_BASE } from "@stratabook/sdk";
6
- import { createStrataMcpServer } from "./server.js";
6
+ import { STRATA_ACTION_GRAPH, STRATA_AGENT_HARNESS, } from "./generated-harness.js";
7
+ import { createStrataMcpServer, probeStrataMcpReadiness } from "./server.js";
7
8
  import { SERVER_VERSION } from "./version.js";
8
9
  function parse(argv) {
9
10
  const values = new Map();
@@ -59,7 +60,9 @@ Options:
59
60
  --host HOST HTTP bind host (default: localhost)
60
61
  --port N HTTP port (default: 8787)
61
62
 
62
- This server is read-only. It accepts no wallet, private key, or session key.
63
+ This server exposes capability-gated quote and execution operations. The external
64
+ agent owner controls permission and signing. Strata accepts public keys,
65
+ signatures, and signed transactions, never private keys or seed phrases.
63
66
  `);
64
67
  }
65
68
  async function runStdio(options) {
@@ -78,11 +81,32 @@ async function runHttp(options) {
78
81
  // local MCP installations from DNS-rebinding attacks.
79
82
  const app = createMcpExpressApp({ host: options.host });
80
83
  app.disable("x-powered-by");
81
- app.get("/health", (_request, response) => {
84
+ app.get("/health", async (_request, response) => {
85
+ try {
86
+ const readiness = await probeStrataMcpReadiness(options);
87
+ response.status(200).set("Cache-Control", "no-store").json(readiness);
88
+ }
89
+ catch (error) {
90
+ process.stderr.write(`[strata-mcp] readiness failed: ${safeError(error)}\n`);
91
+ response.status(503).set("Cache-Control", "no-store").json({
92
+ ok: false,
93
+ service: "strata-mcp",
94
+ version: SERVER_VERSION,
95
+ readiness: "failed",
96
+ });
97
+ }
98
+ });
99
+ app.get("/.well-known/strata-agent.json", (_request, response) => {
82
100
  response
83
101
  .status(200)
84
- .set("Cache-Control", "no-store")
85
- .json({ ok: true, service: "strata-mcp", version: SERVER_VERSION });
102
+ .set("Cache-Control", "public, max-age=300")
103
+ .json(STRATA_AGENT_HARNESS);
104
+ });
105
+ app.get("/.well-known/strata-action-graph.json", (_request, response) => {
106
+ response
107
+ .status(200)
108
+ .set("Cache-Control", "public, max-age=300")
109
+ .json(STRATA_ACTION_GRAPH);
86
110
  });
87
111
  app.post("/mcp", async (request, response) => {
88
112
  let runtime;
@@ -100,7 +124,8 @@ async function runHttp(options) {
100
124
  await runtime.server.connect(transport);
101
125
  await transport.handleRequest(request, response, request.body);
102
126
  }
103
- catch {
127
+ catch (error) {
128
+ process.stderr.write(`[strata-mcp] request failed: ${safeError(error)}\n`);
104
129
  if (!response.headersSent) {
105
130
  response.status(500).json({
106
131
  jsonrpc: "2.0",
@@ -128,6 +153,11 @@ async function runHttp(options) {
128
153
  process.once("SIGINT", close);
129
154
  process.once("SIGTERM", close);
130
155
  }
156
+ function safeError(error) {
157
+ if (!(error instanceof Error))
158
+ return "unknown_error";
159
+ return `${error.name}: ${error.message}`.replace(/[\r\n\t]/g, " ").slice(0, 300);
160
+ }
131
161
  async function main() {
132
162
  const options = parse(process.argv.slice(2));
133
163
  if (options.transport === "stdio")
@@ -0,0 +1,199 @@
1
+ export declare const STRATA_AGENT_HARNESS: {
2
+ readonly schema_version: 1;
3
+ readonly harness_version: "1.0";
4
+ readonly contract_version: "1.1";
5
+ readonly name: "Strata Agent Harness";
6
+ readonly summary: "A capability-gated workflow and executable action graph for agents that discover, quote, prepare, externally sign, and submit Strata operations.";
7
+ readonly entrypoints: {
8
+ readonly documentation: "https://stratabook.org/docs/agent-harness/";
9
+ readonly capabilities: "https://api.stratabook.app/sonar/capabilities";
10
+ readonly markets: "https://api.stratabook.app/sonar/markets";
11
+ readonly action_graph: "https://api.stratabook.app/sonar/action-graph";
12
+ readonly mcp: "https://api.stratabook.app/mcp";
13
+ readonly manifest: "https://api.stratabook.app/.well-known/strata-agent.json";
14
+ };
15
+ readonly interfaces: {
16
+ readonly mcp_tool_order: readonly ["strata_capabilities", "strata_action_graph", "strata_markets", "strata_quote", "strata_execution_challenge", "strata_execution_prepare", "strata_execution_submit"];
17
+ readonly terminal: readonly ["npx -y @stratabook/sdk capabilities --json", "npx -y @stratabook/sdk action-graph --json", "npx -y @stratabook/sdk markets --json", "npx -y @stratabook/sdk quote --market SOL/USDC --side sell --amount-atoms 10000000 --json"];
18
+ };
19
+ readonly workflow: readonly [{
20
+ readonly id: "discover_capabilities";
21
+ readonly instruction: "Read the live capability catalog before every objective. Never infer permission from documentation, package support, or an earlier session.";
22
+ }, {
23
+ readonly id: "establish_mode";
24
+ readonly instruction: "Read the action graph and identify which prepare and submit nodes are live. The external agent owner configures its permissions and signer authority; static documentation never enables a Strata operation.";
25
+ }, {
26
+ readonly id: "understand_objective";
27
+ readonly instruction: "Resolve the user's market, side, amount, and tolerance. Ask before proceeding when any economically meaningful input is ambiguous.";
28
+ }, {
29
+ readonly id: "discover_market";
30
+ readonly instruction: "List markets, select one marked ready, and use its discovered base and quote decimals. Do not guess market identifiers or token decimals.";
31
+ }, {
32
+ readonly id: "preserve_atoms";
33
+ readonly instruction: "Represent token amounts as unsigned base-10 atomic strings. Never pass settlement amounts through floating-point arithmetic.";
34
+ }, {
35
+ readonly id: "request_quote";
36
+ readonly instruction: "Request a fresh Sonar quote with an explicit side, exact input atoms, and execution tolerance supplied by the external agent.";
37
+ }, {
38
+ readonly id: "validate_quote";
39
+ readonly instruction: "Verify the quote binds to the selected market, side, and input. Check labelled fees, minimum output, price impact, server time, and expiry.";
40
+ }, {
41
+ readonly id: "report_result";
42
+ readonly instruction: "Report consumed input, expected output, minimum output, fees by asset side, price impact, and remaining validity without inventing private route composition.";
43
+ }, {
44
+ readonly id: "authorize_writes";
45
+ readonly instruction: "When prepare and submit are exposed, keep signing external to Strata: request canonical authorization bytes, sign them with the owner-configured signer, verify the prepared transaction preserves the quote, then submit the externally signed transaction with idempotency.";
46
+ }, {
47
+ readonly id: "monitor_outcome";
48
+ readonly instruction: "After an authorized submission, report the durable receipt or explicit failure. Never claim completion from preparation, signing, or an unconfirmed request.";
49
+ }];
50
+ readonly stop_conditions: readonly ["The required live capability is disabled or absent.", "The market is paused, unavailable, or has no reviewed operation path.", "Market, side, amount, decimals, tolerance, or signer authority is unavailable or ambiguous.", "The contract version is unsupported or a response contains unknown fields.", "A quote is expired or its market, side, amount, fee, minimum-output, or time binding is inconsistent.", "A requested operation exceeds the exposed tool, account, or policy scope.", "A user asks the agent to receive or expose wallet secrets, private keys, seed phrases, session keys, or production credentials."];
51
+ readonly safety_rules: readonly ["Never request or accept wallet secrets, private keys, seed phrases, session keys, or production credentials in a prompt.", "Never call undocumented endpoints or reconstruct private Sonar behavior.", "Never silently widen slippage, refresh changed economics, substitute a market, or retry a non-retryable failure.", "Treat capability removal, revocation, expiry, and emergency disable as immediate stop signals.", "Capability and action-graph availability are authoritative for Strata operations; permission and signer policy remain controlled by the external agent owner."];
52
+ };
53
+ export declare const STRATA_AGENT_HARNESS_URI = "strata://agent-harness/v1";
54
+ export declare const STRATA_ACTION_GRAPH: {
55
+ readonly schema_version: 1;
56
+ readonly graph_version: "1.0";
57
+ readonly contract_version: "1.1";
58
+ readonly entry_node: "discover_capabilities";
59
+ readonly authority: {
60
+ readonly permission_source: "external_agent_owner";
61
+ readonly signing_location: "external";
62
+ readonly accepts_private_keys: false;
63
+ };
64
+ readonly nodes: readonly [{
65
+ readonly id: "discover_capabilities";
66
+ readonly kind: "discovery";
67
+ readonly summary: "Read the live capabilities that currently expose Strata operations.";
68
+ readonly required_capabilities: readonly [];
69
+ readonly available: true;
70
+ readonly operation: {
71
+ readonly method: "GET";
72
+ readonly path: "/sonar/capabilities";
73
+ readonly mcp_tool: "strata_capabilities";
74
+ };
75
+ }, {
76
+ readonly id: "discover_markets";
77
+ readonly kind: "discovery";
78
+ readonly summary: "Discover ready markets, token decimals, and public operation paths.";
79
+ readonly required_capabilities: readonly ["markets.read"];
80
+ readonly available: true;
81
+ readonly operation: {
82
+ readonly method: "GET";
83
+ readonly path: "/sonar/markets";
84
+ readonly mcp_tool: "strata_markets";
85
+ };
86
+ }, {
87
+ readonly id: "discover_action_graph";
88
+ readonly kind: "discovery";
89
+ readonly summary: "Read the executable topology, live node availability, external signing steps, and transition conditions.";
90
+ readonly required_capabilities: readonly [];
91
+ readonly available: true;
92
+ readonly operation: {
93
+ readonly method: "GET";
94
+ readonly path: "/sonar/action-graph";
95
+ readonly mcp_tool: "strata_action_graph";
96
+ };
97
+ }, {
98
+ readonly id: "request_quote";
99
+ readonly kind: "read";
100
+ readonly summary: "Request economics bound to a market, side, exact input atoms, and tolerance.";
101
+ readonly required_capabilities: readonly ["quotes.read"];
102
+ readonly available: true;
103
+ readonly operation: {
104
+ readonly method: "POST";
105
+ readonly path: "/sonar/markets/{market}/quote";
106
+ readonly mcp_tool: "strata_quote";
107
+ };
108
+ }, {
109
+ readonly id: "request_execution_challenge";
110
+ readonly kind: "prepare";
111
+ readonly summary: "Request canonical authorization bytes for an unexpired quote and external signer.";
112
+ readonly required_capabilities: readonly ["trade.prepare"];
113
+ readonly available: true;
114
+ readonly operation: {
115
+ readonly method: "POST";
116
+ readonly path: "/sonar/markets/{market}/execution/challenge";
117
+ readonly mcp_tool: "strata_execution_challenge";
118
+ };
119
+ }, {
120
+ readonly id: "sign_authorization";
121
+ readonly kind: "external_signature";
122
+ readonly summary: "The agent owner's configured signer signs the returned authorization bytes externally.";
123
+ readonly required_capabilities: readonly [];
124
+ readonly available: true;
125
+ }, {
126
+ readonly id: "prepare_execution";
127
+ readonly kind: "prepare";
128
+ readonly summary: "Exchange the authorization signature for a quote-bound partially signed transaction.";
129
+ readonly required_capabilities: readonly ["trade.prepare"];
130
+ readonly available: true;
131
+ readonly operation: {
132
+ readonly method: "POST";
133
+ readonly path: "/sonar/markets/{market}/execution/prepare";
134
+ readonly mcp_tool: "strata_execution_prepare";
135
+ };
136
+ }, {
137
+ readonly id: "sign_transaction";
138
+ readonly kind: "external_signature";
139
+ readonly summary: "The external signer verifies and fills its signature slot without sending key material to Strata.";
140
+ readonly required_capabilities: readonly [];
141
+ readonly available: true;
142
+ }, {
143
+ readonly id: "submit_execution";
144
+ readonly kind: "submit";
145
+ readonly summary: "Submit the signed transaction with an idempotency key.";
146
+ readonly required_capabilities: readonly ["trade.submit"];
147
+ readonly available: true;
148
+ readonly operation: {
149
+ readonly method: "POST";
150
+ readonly path: "/sonar/markets/{market}/execution/submit";
151
+ readonly mcp_tool: "strata_execution_submit";
152
+ };
153
+ }, {
154
+ readonly id: "receive_receipt";
155
+ readonly kind: "receipt";
156
+ readonly summary: "Receive the execution ID, Solana signature, and submitted status.";
157
+ readonly required_capabilities: readonly [];
158
+ readonly available: true;
159
+ }];
160
+ readonly edges: readonly [{
161
+ readonly from: "discover_capabilities";
162
+ readonly to: "discover_action_graph";
163
+ readonly condition: "the returned contract version is supported";
164
+ }, {
165
+ readonly from: "discover_action_graph";
166
+ readonly to: "discover_markets";
167
+ readonly condition: "markets.read is enabled";
168
+ }, {
169
+ readonly from: "discover_markets";
170
+ readonly to: "request_quote";
171
+ readonly condition: "quotes.read is enabled and the market is ready";
172
+ }, {
173
+ readonly from: "request_quote";
174
+ readonly to: "request_execution_challenge";
175
+ readonly condition: "trade.prepare is enabled and the quote is unexpired";
176
+ }, {
177
+ readonly from: "request_execution_challenge";
178
+ readonly to: "sign_authorization";
179
+ readonly condition: "the challenge bindings match the quote and signer";
180
+ }, {
181
+ readonly from: "sign_authorization";
182
+ readonly to: "prepare_execution";
183
+ readonly condition: "a valid external authorization signature is available";
184
+ }, {
185
+ readonly from: "prepare_execution";
186
+ readonly to: "sign_transaction";
187
+ readonly condition: "the prepared transaction preserves the signed bindings";
188
+ }, {
189
+ readonly from: "sign_transaction";
190
+ readonly to: "submit_execution";
191
+ readonly condition: "trade.submit is enabled and the signed transaction is unmodified";
192
+ }, {
193
+ readonly from: "submit_execution";
194
+ readonly to: "receive_receipt";
195
+ readonly condition: "the execution ID and idempotency key match";
196
+ }];
197
+ };
198
+ export declare const STRATA_ACTION_GRAPH_URI = "strata://action-graph/v1";
199
+ export declare const STRATA_AGENT_HARNESS_INSTRUCTIONS = "Strata Agent Harness 1.0. Start every objective with strata_capabilities, then strata_action_graph, then strata_markets. Read strata://agent-harness/v1 and strata://action-graph/v1. 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 the market, side, exact input atoms, and tolerance before strata_quote. Treat amounts as unsigned base-10 token atoms; check quote bindings, labelled fees, minimum output, and expiry. To execute: request a challenge, sign its canonical authorization bytes externally, prepare, verify and sign the returned transaction externally, then submit with idempotency. Stop on ambiguity, unavailable capabilities, paused markets, unsupported contracts, inconsistent bindings, expiry, or missing signer authority.";
@@ -0,0 +1,269 @@
1
+ // @generated by scripts/generate-agent-entry.mjs. Do not edit manually.
2
+ export const STRATA_AGENT_HARNESS = {
3
+ "schema_version": 1,
4
+ "harness_version": "1.0",
5
+ "contract_version": "1.1",
6
+ "name": "Strata Agent Harness",
7
+ "summary": "A capability-gated workflow and executable action graph for agents that discover, quote, prepare, externally sign, and submit Strata operations.",
8
+ "entrypoints": {
9
+ "documentation": "https://stratabook.org/docs/agent-harness/",
10
+ "capabilities": "https://api.stratabook.app/sonar/capabilities",
11
+ "markets": "https://api.stratabook.app/sonar/markets",
12
+ "action_graph": "https://api.stratabook.app/sonar/action-graph",
13
+ "mcp": "https://api.stratabook.app/mcp",
14
+ "manifest": "https://api.stratabook.app/.well-known/strata-agent.json"
15
+ },
16
+ "interfaces": {
17
+ "mcp_tool_order": [
18
+ "strata_capabilities",
19
+ "strata_action_graph",
20
+ "strata_markets",
21
+ "strata_quote",
22
+ "strata_execution_challenge",
23
+ "strata_execution_prepare",
24
+ "strata_execution_submit"
25
+ ],
26
+ "terminal": [
27
+ "npx -y @stratabook/sdk capabilities --json",
28
+ "npx -y @stratabook/sdk action-graph --json",
29
+ "npx -y @stratabook/sdk markets --json",
30
+ "npx -y @stratabook/sdk quote --market SOL/USDC --side sell --amount-atoms 10000000 --json"
31
+ ]
32
+ },
33
+ "workflow": [
34
+ {
35
+ "id": "discover_capabilities",
36
+ "instruction": "Read the live capability catalog before every objective. Never infer permission from documentation, package support, or an earlier session."
37
+ },
38
+ {
39
+ "id": "establish_mode",
40
+ "instruction": "Read the action graph and identify which prepare and submit nodes are live. The external agent owner configures its permissions and signer authority; static documentation never enables a Strata operation."
41
+ },
42
+ {
43
+ "id": "understand_objective",
44
+ "instruction": "Resolve the user's market, side, amount, and tolerance. Ask before proceeding when any economically meaningful input is ambiguous."
45
+ },
46
+ {
47
+ "id": "discover_market",
48
+ "instruction": "List markets, select one marked ready, and use its discovered base and quote decimals. Do not guess market identifiers or token decimals."
49
+ },
50
+ {
51
+ "id": "preserve_atoms",
52
+ "instruction": "Represent token amounts as unsigned base-10 atomic strings. Never pass settlement amounts through floating-point arithmetic."
53
+ },
54
+ {
55
+ "id": "request_quote",
56
+ "instruction": "Request a fresh Sonar quote with an explicit side, exact input atoms, and execution tolerance supplied by the external agent."
57
+ },
58
+ {
59
+ "id": "validate_quote",
60
+ "instruction": "Verify the quote binds to the selected market, side, and input. Check labelled fees, minimum output, price impact, server time, and expiry."
61
+ },
62
+ {
63
+ "id": "report_result",
64
+ "instruction": "Report consumed input, expected output, minimum output, fees by asset side, price impact, and remaining validity without inventing private route composition."
65
+ },
66
+ {
67
+ "id": "authorize_writes",
68
+ "instruction": "When prepare and submit are exposed, keep signing external to Strata: request canonical authorization bytes, sign them with the owner-configured signer, verify the prepared transaction preserves the quote, then submit the externally signed transaction with idempotency."
69
+ },
70
+ {
71
+ "id": "monitor_outcome",
72
+ "instruction": "After an authorized submission, report the durable receipt or explicit failure. Never claim completion from preparation, signing, or an unconfirmed request."
73
+ }
74
+ ],
75
+ "stop_conditions": [
76
+ "The required live capability is disabled or absent.",
77
+ "The market is paused, unavailable, or has no reviewed operation path.",
78
+ "Market, side, amount, decimals, tolerance, or signer authority is unavailable or ambiguous.",
79
+ "The contract version is unsupported or a response contains unknown fields.",
80
+ "A quote is expired or its market, side, amount, fee, minimum-output, or time binding is inconsistent.",
81
+ "A requested operation exceeds the exposed tool, account, or policy scope.",
82
+ "A user asks the agent to receive or expose wallet secrets, private keys, seed phrases, session keys, or production credentials."
83
+ ],
84
+ "safety_rules": [
85
+ "Never request or accept wallet secrets, private keys, seed phrases, session keys, or production credentials in a prompt.",
86
+ "Never call undocumented endpoints or reconstruct private Sonar behavior.",
87
+ "Never silently widen slippage, refresh changed economics, substitute a market, or retry a non-retryable failure.",
88
+ "Treat capability removal, revocation, expiry, and emergency disable as immediate stop signals.",
89
+ "Capability and action-graph availability are authoritative for Strata operations; permission and signer policy remain controlled by the external agent owner."
90
+ ]
91
+ };
92
+ export const STRATA_AGENT_HARNESS_URI = "strata://agent-harness/v1";
93
+ export const STRATA_ACTION_GRAPH = {
94
+ "schema_version": 1,
95
+ "graph_version": "1.0",
96
+ "contract_version": "1.1",
97
+ "entry_node": "discover_capabilities",
98
+ "authority": {
99
+ "permission_source": "external_agent_owner",
100
+ "signing_location": "external",
101
+ "accepts_private_keys": false
102
+ },
103
+ "nodes": [
104
+ {
105
+ "id": "discover_capabilities",
106
+ "kind": "discovery",
107
+ "summary": "Read the live capabilities that currently expose Strata operations.",
108
+ "required_capabilities": [],
109
+ "available": true,
110
+ "operation": {
111
+ "method": "GET",
112
+ "path": "/sonar/capabilities",
113
+ "mcp_tool": "strata_capabilities"
114
+ }
115
+ },
116
+ {
117
+ "id": "discover_markets",
118
+ "kind": "discovery",
119
+ "summary": "Discover ready markets, token decimals, and public operation paths.",
120
+ "required_capabilities": [
121
+ "markets.read"
122
+ ],
123
+ "available": true,
124
+ "operation": {
125
+ "method": "GET",
126
+ "path": "/sonar/markets",
127
+ "mcp_tool": "strata_markets"
128
+ }
129
+ },
130
+ {
131
+ "id": "discover_action_graph",
132
+ "kind": "discovery",
133
+ "summary": "Read the executable topology, live node availability, external signing steps, and transition conditions.",
134
+ "required_capabilities": [],
135
+ "available": true,
136
+ "operation": {
137
+ "method": "GET",
138
+ "path": "/sonar/action-graph",
139
+ "mcp_tool": "strata_action_graph"
140
+ }
141
+ },
142
+ {
143
+ "id": "request_quote",
144
+ "kind": "read",
145
+ "summary": "Request economics bound to a market, side, exact input atoms, and tolerance.",
146
+ "required_capabilities": [
147
+ "quotes.read"
148
+ ],
149
+ "available": true,
150
+ "operation": {
151
+ "method": "POST",
152
+ "path": "/sonar/markets/{market}/quote",
153
+ "mcp_tool": "strata_quote"
154
+ }
155
+ },
156
+ {
157
+ "id": "request_execution_challenge",
158
+ "kind": "prepare",
159
+ "summary": "Request canonical authorization bytes for an unexpired quote and external signer.",
160
+ "required_capabilities": [
161
+ "trade.prepare"
162
+ ],
163
+ "available": true,
164
+ "operation": {
165
+ "method": "POST",
166
+ "path": "/sonar/markets/{market}/execution/challenge",
167
+ "mcp_tool": "strata_execution_challenge"
168
+ }
169
+ },
170
+ {
171
+ "id": "sign_authorization",
172
+ "kind": "external_signature",
173
+ "summary": "The agent owner's configured signer signs the returned authorization bytes externally.",
174
+ "required_capabilities": [],
175
+ "available": true
176
+ },
177
+ {
178
+ "id": "prepare_execution",
179
+ "kind": "prepare",
180
+ "summary": "Exchange the authorization signature for a quote-bound partially signed transaction.",
181
+ "required_capabilities": [
182
+ "trade.prepare"
183
+ ],
184
+ "available": true,
185
+ "operation": {
186
+ "method": "POST",
187
+ "path": "/sonar/markets/{market}/execution/prepare",
188
+ "mcp_tool": "strata_execution_prepare"
189
+ }
190
+ },
191
+ {
192
+ "id": "sign_transaction",
193
+ "kind": "external_signature",
194
+ "summary": "The external signer verifies and fills its signature slot without sending key material to Strata.",
195
+ "required_capabilities": [],
196
+ "available": true
197
+ },
198
+ {
199
+ "id": "submit_execution",
200
+ "kind": "submit",
201
+ "summary": "Submit the signed transaction with an idempotency key.",
202
+ "required_capabilities": [
203
+ "trade.submit"
204
+ ],
205
+ "available": true,
206
+ "operation": {
207
+ "method": "POST",
208
+ "path": "/sonar/markets/{market}/execution/submit",
209
+ "mcp_tool": "strata_execution_submit"
210
+ }
211
+ },
212
+ {
213
+ "id": "receive_receipt",
214
+ "kind": "receipt",
215
+ "summary": "Receive the execution ID, Solana signature, and submitted status.",
216
+ "required_capabilities": [],
217
+ "available": true
218
+ }
219
+ ],
220
+ "edges": [
221
+ {
222
+ "from": "discover_capabilities",
223
+ "to": "discover_action_graph",
224
+ "condition": "the returned contract version is supported"
225
+ },
226
+ {
227
+ "from": "discover_action_graph",
228
+ "to": "discover_markets",
229
+ "condition": "markets.read is enabled"
230
+ },
231
+ {
232
+ "from": "discover_markets",
233
+ "to": "request_quote",
234
+ "condition": "quotes.read is enabled and the market is ready"
235
+ },
236
+ {
237
+ "from": "request_quote",
238
+ "to": "request_execution_challenge",
239
+ "condition": "trade.prepare is enabled and the quote is unexpired"
240
+ },
241
+ {
242
+ "from": "request_execution_challenge",
243
+ "to": "sign_authorization",
244
+ "condition": "the challenge bindings match the quote and signer"
245
+ },
246
+ {
247
+ "from": "sign_authorization",
248
+ "to": "prepare_execution",
249
+ "condition": "a valid external authorization signature is available"
250
+ },
251
+ {
252
+ "from": "prepare_execution",
253
+ "to": "sign_transaction",
254
+ "condition": "the prepared transaction preserves the signed bindings"
255
+ },
256
+ {
257
+ "from": "sign_transaction",
258
+ "to": "submit_execution",
259
+ "condition": "trade.submit is enabled and the signed transaction is unmodified"
260
+ },
261
+ {
262
+ "from": "submit_execution",
263
+ "to": "receive_receipt",
264
+ "condition": "the execution ID and idempotency key match"
265
+ }
266
+ ]
267
+ };
268
+ export const STRATA_ACTION_GRAPH_URI = "strata://action-graph/v1";
269
+ export const STRATA_AGENT_HARNESS_INSTRUCTIONS = "Strata Agent Harness 1.0. Start every objective with strata_capabilities, then strata_action_graph, then strata_markets. Read strata://agent-harness/v1 and strata://action-graph/v1. 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 the market, side, exact input atoms, and tolerance before strata_quote. Treat amounts as unsigned base-10 token atoms; check quote bindings, labelled fees, minimum output, and expiry. To execute: request a challenge, sign its canonical authorization bytes externally, prepare, verify and sign the returned transaction externally, then submit with idempotency. Stop on ambiguity, unavailable capabilities, paused markets, unsupported contracts, inconsistent bindings, expiry, or missing signer authority.";
@@ -1 +1,2 @@
1
- export { capabilityAvailable, createStrataMcpServer, type StrataMcpOptions, type StrataMcpRuntime, } from "./server.js";
1
+ export { capabilityAvailable, createStrataMcpServer, probeStrataMcpReadiness, type StrataMcpOptions, type StrataMcpReadiness, type StrataMcpRuntime, } from "./server.js";
2
+ export { STRATA_AGENT_HARNESS, STRATA_AGENT_HARNESS_INSTRUCTIONS, STRATA_AGENT_HARNESS_URI, STRATA_ACTION_GRAPH, STRATA_ACTION_GRAPH_URI, } from "./generated-harness.js";
package/dist/src/index.js CHANGED
@@ -1 +1,2 @@
1
- export { capabilityAvailable, createStrataMcpServer, } from "./server.js";
1
+ export { capabilityAvailable, createStrataMcpServer, probeStrataMcpReadiness, } from "./server.js";
2
+ export { STRATA_AGENT_HARNESS, STRATA_AGENT_HARNESS_INSTRUCTIONS, STRATA_AGENT_HARNESS_URI, STRATA_ACTION_GRAPH, STRATA_ACTION_GRAPH_URI, } from "./generated-harness.js";
@@ -10,5 +10,13 @@ export interface StrataMcpRuntime {
10
10
  refreshCapabilities(): Promise<void>;
11
11
  close(): Promise<void>;
12
12
  }
13
+ export interface StrataMcpReadiness {
14
+ ok: true;
15
+ service: "strata-mcp";
16
+ version: string;
17
+ contract_version: string;
18
+ harness_version: string;
19
+ }
13
20
  export declare function capabilityAvailable(catalog: CapabilityCatalog, id: string): boolean;
21
+ export declare function probeStrataMcpReadiness(options?: StrataMcpOptions): Promise<StrataMcpReadiness>;
14
22
  export declare function createStrataMcpServer(options?: StrataMcpOptions): Promise<StrataMcpRuntime>;
@@ -1,29 +1,96 @@
1
1
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { DEFAULT_SLIPPAGE_BPS, StrataApiError, StrataClient, } from "@stratabook/sdk";
3
3
  import * as z from "zod/v4";
4
+ import { STRATA_AGENT_HARNESS, STRATA_AGENT_HARNESS_INSTRUCTIONS, STRATA_AGENT_HARNESS_URI, STRATA_ACTION_GRAPH_URI, } from "./generated-harness.js";
4
5
  import { SERVER_VERSION } from "./version.js";
5
6
  const REFRESH_INTERVAL_MS = 5_000;
6
7
  export function capabilityAvailable(catalog, id) {
7
8
  return catalog.capabilities.some((capability) => capability.id === id
8
9
  && capability.default_enabled
9
10
  && capability.public_sdk
10
- && capability.risk === "read"
11
- && capability.mcp_exposure === "read");
11
+ && capability.mcp_exposure !== "none"
12
+ && capability.risk === capability.mcp_exposure);
12
13
  }
13
- export async function createStrataMcpServer(options = {}) {
14
- const client = options.client
14
+ function strataClient(options) {
15
+ return options.client
15
16
  ?? new StrataClient({
16
17
  apiBase: options.apiBase,
17
18
  timeoutMs: options.timeoutMs,
18
19
  });
20
+ }
21
+ export async function probeStrataMcpReadiness(options = {}) {
22
+ const catalog = await strataClient(options).capabilities();
23
+ if (catalog.contract_version !== STRATA_AGENT_HARNESS.contract_version) {
24
+ throw new Error("agent harness and live contract versions differ");
25
+ }
26
+ return {
27
+ ok: true,
28
+ service: "strata-mcp",
29
+ version: SERVER_VERSION,
30
+ contract_version: catalog.contract_version,
31
+ harness_version: STRATA_AGENT_HARNESS.harness_version,
32
+ };
33
+ }
34
+ export async function createStrataMcpServer(options = {}) {
35
+ const client = strataClient(options);
19
36
  const initialCatalog = await client.capabilities();
37
+ if (initialCatalog.contract_version !== STRATA_AGENT_HARNESS.contract_version) {
38
+ throw new Error("agent harness and live contract versions differ");
39
+ }
20
40
  const server = new McpServer({
21
41
  name: "strata",
22
42
  version: SERVER_VERSION,
23
43
  }, {
24
- instructions: "Use Strata to discover available markets and request short-lived Sonar quotes. "
25
- + "Token values use exact atomic decimal strings. Check quote expiry and minimum output.",
44
+ instructions: STRATA_AGENT_HARNESS_INSTRUCTIONS,
26
45
  });
46
+ server.registerResource("strata_agent_harness", STRATA_AGENT_HARNESS_URI, {
47
+ title: "Strata Agent Harness",
48
+ description: "Canonical capability-gated first-run workflow for Strata agents.",
49
+ mimeType: "application/json",
50
+ }, async () => ({
51
+ contents: [
52
+ {
53
+ uri: STRATA_AGENT_HARNESS_URI,
54
+ mimeType: "application/json",
55
+ text: JSON.stringify(STRATA_AGENT_HARNESS),
56
+ },
57
+ ],
58
+ }));
59
+ server.registerResource("strata_action_graph", STRATA_ACTION_GRAPH_URI, {
60
+ title: "Strata Action Graph",
61
+ description: "Live executable topology for discovery, quoting, external signing, and submission.",
62
+ mimeType: "application/json",
63
+ }, async () => ({
64
+ contents: [
65
+ {
66
+ uri: STRATA_ACTION_GRAPH_URI,
67
+ mimeType: "application/json",
68
+ text: JSON.stringify(await client.actionGraph()),
69
+ },
70
+ ],
71
+ }));
72
+ server.registerPrompt("strata_start", {
73
+ title: "Start a Strata objective",
74
+ description: "Apply the Strata Agent Harness to a concrete objective.",
75
+ argsSchema: {
76
+ objective: z
77
+ .string()
78
+ .min(1)
79
+ .max(2_000)
80
+ .describe("The user's concrete Strata market or quote objective."),
81
+ },
82
+ }, async ({ objective }) => ({
83
+ description: "Capability-gated Strata objective",
84
+ messages: [
85
+ {
86
+ role: "user",
87
+ content: {
88
+ type: "text",
89
+ text: `${STRATA_AGENT_HARNESS_INSTRUCTIONS}\n\nObjective: ${objective.trim()}`,
90
+ },
91
+ },
92
+ ],
93
+ }));
27
94
  server.registerTool("strata_capabilities", {
28
95
  title: "Strata capabilities",
29
96
  description: "See which Strata features are currently available to MCP clients.",
@@ -34,6 +101,16 @@ export async function createStrataMcpServer(options = {}) {
34
101
  openWorldHint: true,
35
102
  },
36
103
  }, async () => toolResult(await client.capabilities(), "Current Strata capabilities."));
104
+ server.registerTool("strata_action_graph", {
105
+ title: "Strata action graph",
106
+ description: "Discover live operations, required capabilities, transition conditions, and external signing boundaries.",
107
+ annotations: {
108
+ readOnlyHint: true,
109
+ destructiveHint: false,
110
+ idempotentHint: true,
111
+ openWorldHint: true,
112
+ },
113
+ }, async () => toolResult(await client.actionGraph(), "Current Strata action graph."));
37
114
  const markets = server.registerTool("strata_markets", {
38
115
  title: "Strata markets",
39
116
  description: "List Strata markets and their current Sonar quote availability.",
@@ -104,7 +181,117 @@ export async function createStrataMcpServer(options = {}) {
104
181
  + `for ${response.amount_out_atoms} output atoms; minimum `
105
182
  + `${response.minimum_output_atoms}; expires at ${response.expires_at_ms}.`);
106
183
  }));
107
- const handles = { markets, quote };
184
+ const executionChallenge = server.registerTool("strata_execution_challenge", {
185
+ title: "Strata execution challenge",
186
+ description: "Request canonical quote-bound authorization bytes for the external signer configured by the agent owner.",
187
+ inputSchema: {
188
+ market: z.string().min(1).max(128).describe("Market label or public market ID."),
189
+ quoteId: z.string().regex(/^sq_[0-9a-f]{32}$/).describe("Unexpired Sonar quote ID."),
190
+ ownerWallet: z.string().min(32).max(44).describe("Base58 owner wallet public key."),
191
+ sessionPublicKey: z
192
+ .string()
193
+ .min(32)
194
+ .max(44)
195
+ .describe("Base58 public key for the externally configured signer."),
196
+ accountSequence: z
197
+ .string()
198
+ .regex(/^[0-9]+$/)
199
+ .max(20)
200
+ .describe("Current Vault account sequence as an unsigned decimal string."),
201
+ },
202
+ annotations: {
203
+ readOnlyHint: false,
204
+ destructiveHint: false,
205
+ idempotentHint: false,
206
+ openWorldHint: true,
207
+ },
208
+ }, async ({ market, quoteId, ownerWallet, sessionPublicKey, accountSequence }) => guardedTool(client, "trade.prepare", async () => {
209
+ const request = {
210
+ market,
211
+ quoteId,
212
+ ownerWallet,
213
+ sessionPublicKey,
214
+ accountSequence,
215
+ };
216
+ const response = await client.executionChallenge(request);
217
+ return toolResult(response, `Authorization challenge ${response.challenge_id}; expires at ${response.expires_at_ms}.`);
218
+ }));
219
+ const executionPrepare = server.registerTool("strata_execution_prepare", {
220
+ title: "Prepare Strata execution",
221
+ description: "Exchange an externally signed authorization challenge for a quote-bound partially signed transaction.",
222
+ inputSchema: {
223
+ market: z.string().min(1).max(128).describe("Market label or public market ID."),
224
+ challengeId: z
225
+ .string()
226
+ .regex(/^sc_[0-9a-f]{32}$/)
227
+ .describe("Execution challenge ID returned by Strata."),
228
+ authorizationSignature: z
229
+ .string()
230
+ .min(1)
231
+ .max(128)
232
+ .regex(/^[1-9A-HJ-NP-Za-km-z]+$/)
233
+ .describe("Base58 Ed25519 signature made externally over the challenge payload."),
234
+ },
235
+ annotations: {
236
+ readOnlyHint: false,
237
+ destructiveHint: false,
238
+ idempotentHint: false,
239
+ openWorldHint: true,
240
+ },
241
+ }, async ({ market, challengeId, authorizationSignature }) => guardedTool(client, "trade.prepare", async () => {
242
+ const request = {
243
+ market,
244
+ challengeId,
245
+ authorizationSignature,
246
+ };
247
+ const response = await client.executionPrepare(request);
248
+ return toolResult(response, `Prepared execution ${response.execution_id}; externally verify and sign before ${response.expires_at_ms}.`);
249
+ }));
250
+ const executionSubmit = server.registerTool("strata_execution_submit", {
251
+ title: "Submit Strata execution",
252
+ description: "Submit an externally signed prepared transaction with an idempotency key.",
253
+ inputSchema: {
254
+ market: z.string().min(1).max(128).describe("Market label or public market ID."),
255
+ executionId: z
256
+ .string()
257
+ .regex(/^se_[0-9a-f]{32}$/)
258
+ .describe("Prepared execution ID returned by Strata."),
259
+ signedTransactionBase64: z
260
+ .string()
261
+ .min(4)
262
+ .max(8_192)
263
+ .regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/)
264
+ .describe("The externally signed Solana transaction in canonical base64."),
265
+ idempotencyKey: z
266
+ .string()
267
+ .min(1)
268
+ .max(64)
269
+ .regex(/^[A-Za-z0-9._-]+$/)
270
+ .describe("Stable retry key for exactly this execution."),
271
+ },
272
+ annotations: {
273
+ readOnlyHint: false,
274
+ destructiveHint: true,
275
+ idempotentHint: true,
276
+ openWorldHint: true,
277
+ },
278
+ }, async ({ market, executionId, signedTransactionBase64, idempotencyKey }) => guardedTool(client, "trade.submit", async () => {
279
+ const request = {
280
+ market,
281
+ executionId,
282
+ signedTransactionBase64,
283
+ idempotencyKey,
284
+ };
285
+ const response = await client.executionSubmit(request);
286
+ return toolResult(response, `Submitted execution ${response.execution_id} as ${response.signature}.`);
287
+ }));
288
+ const handles = {
289
+ markets,
290
+ quote,
291
+ executionChallenge,
292
+ executionPrepare,
293
+ executionSubmit,
294
+ };
108
295
  applyCapabilityCatalog(handles, initialCatalog);
109
296
  let closed = false;
110
297
  const refresh = async () => {
@@ -131,6 +318,9 @@ export async function createStrataMcpServer(options = {}) {
131
318
  function applyCapabilityCatalog(handles, catalog) {
132
319
  setToolEnabled(handles.markets, capabilityAvailable(catalog, "markets.read"));
133
320
  setToolEnabled(handles.quote, capabilityAvailable(catalog, "quotes.read"));
321
+ setToolEnabled(handles.executionChallenge, capabilityAvailable(catalog, "trade.prepare"));
322
+ setToolEnabled(handles.executionPrepare, capabilityAvailable(catalog, "trade.prepare"));
323
+ setToolEnabled(handles.executionSubmit, capabilityAvailable(catalog, "trade.submit"));
134
324
  }
135
325
  function setToolEnabled(tool, enabled) {
136
326
  if (enabled)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratabook/mcp",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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.1.1",
47
+ "@stratabook/sdk": "0.1.3",
48
48
  "zod": "^3.25.76"
49
49
  },
50
50
  "devDependencies": {