@senpi/runtime 3.0.9-dev.20260710204324 → 3.0.9-dev.feat-strategy-compos.20260716233548

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.
Files changed (108) hide show
  1. package/dist/cli/composer-commands.d.ts +37 -0
  2. package/dist/cli/composer-commands.d.ts.map +1 -0
  3. package/dist/cli/composer-commands.js +396 -0
  4. package/dist/cli/composer-commands.js.map +1 -0
  5. package/dist/cli/senpi-commands.d.ts.map +1 -1
  6. package/dist/cli/senpi-commands.js +2 -0
  7. package/dist/cli/senpi-commands.js.map +1 -1
  8. package/dist/cli/validate-runtime-config.mjs +190 -0
  9. package/dist/composer/AUTHORING.md +435 -0
  10. package/dist/composer/__init__.py +29 -0
  11. package/dist/composer/authoring/__init__.py +59 -0
  12. package/dist/composer/authoring/answers.py +297 -0
  13. package/dist/composer/authoring/archetypes/__init__.py +26 -0
  14. package/dist/composer/authoring/archetypes/_common.py +410 -0
  15. package/dist/composer/authoring/archetypes/breakout.py +134 -0
  16. package/dist/composer/authoring/archetypes/classifier.py +109 -0
  17. package/dist/composer/authoring/archetypes/skeleton.py +76 -0
  18. package/dist/composer/authoring/archetypes/thesis_fund.py +103 -0
  19. package/dist/composer/authoring/archetypes/trend_momentum.py +132 -0
  20. package/dist/composer/authoring/builder.py +191 -0
  21. package/dist/composer/authoring/emit.py +264 -0
  22. package/dist/composer/authoring/gaps.py +115 -0
  23. package/dist/composer/authoring/generate.py +131 -0
  24. package/dist/composer/authoring/presets.py +168 -0
  25. package/dist/composer/authoring/runtime_emit.py +266 -0
  26. package/dist/composer/authoring/strategy_manifest.py +94 -0
  27. package/dist/composer/cli.py +487 -0
  28. package/dist/composer/compiler/__init__.py +22 -0
  29. package/dist/composer/compiler/compile.py +739 -0
  30. package/dist/composer/examples/breakout_swing.graph.yaml +121 -0
  31. package/dist/composer/examples/conviction_scorer.graph.yaml +216 -0
  32. package/dist/composer/examples/macro_thesis_basket.graph.yaml +247 -0
  33. package/dist/composer/examples/pre_listing_classifier.graph.yaml +204 -0
  34. package/dist/composer/examples/trend_momentum_majors.graph.yaml +133 -0
  35. package/dist/composer/generated/README.md +22 -0
  36. package/dist/composer/generated/catalog.md +118 -0
  37. package/dist/composer/generated/describe.md +1168 -0
  38. package/dist/composer/generated/graph.schema.json +1946 -0
  39. package/dist/composer/primitives/__init__.py +320 -0
  40. package/dist/composer/primitives/_candles.py +76 -0
  41. package/dist/composer/primitives/adapter.py +363 -0
  42. package/dist/composer/primitives/emitters.py +142 -0
  43. package/dist/composer/primitives/filters.py +71 -0
  44. package/dist/composer/primitives/indicators.py +326 -0
  45. package/dist/composer/primitives/rankers.py +40 -0
  46. package/dist/composer/primitives/scorers.py +100 -0
  47. package/dist/composer/primitives/sources.py +979 -0
  48. package/dist/composer/primitives/stateful.py +254 -0
  49. package/dist/composer/pure_fn/__init__.py +34 -0
  50. package/dist/composer/pure_fn/checks.py +39 -0
  51. package/dist/composer/pure_fn/models.py +177 -0
  52. package/dist/composer/pure_fn/purity.py +311 -0
  53. package/dist/composer/pure_fn/runtests.py +155 -0
  54. package/dist/composer/registry/__init__.py +40 -0
  55. package/dist/composer/registry/generate.py +425 -0
  56. package/dist/composer/registry/mcp-tools-snapshot.json +193 -0
  57. package/dist/composer/registry/mcp_closure.py +311 -0
  58. package/dist/composer/registry/models.py +417 -0
  59. package/dist/composer/registry/nodes.py +1297 -0
  60. package/dist/composer/requirements.txt +21 -0
  61. package/dist/composer/scaffold_path.py +118 -0
  62. package/dist/composer/templates/__init__.py +13 -0
  63. package/dist/composer/templates/scan.py.j2 +34 -0
  64. package/dist/composer/validator/__init__.py +29 -0
  65. package/dist/composer/validator/desugar.py +133 -0
  66. package/dist/composer/validator/errors.py +227 -0
  67. package/dist/composer/validator/graph_checks.py +443 -0
  68. package/dist/composer/validator/loader.py +110 -0
  69. package/dist/composer/validator/schema_checks.py +147 -0
  70. package/dist/composer/validator/validate.py +86 -0
  71. package/dist/composer/vendoring.py +103 -0
  72. package/dist/composer/verbs/__init__.py +49 -0
  73. package/dist/composer/verbs/_box.py +308 -0
  74. package/dist/composer/verbs/check.py +254 -0
  75. package/dist/composer/verbs/close.py +494 -0
  76. package/dist/composer/verbs/deploy.py +279 -0
  77. package/dist/composer/verbs/docs.py +30 -0
  78. package/dist/composer/verbs/fund.py +498 -0
  79. package/dist/composer/verbs/install.py +586 -0
  80. package/dist/composer/verbs/runtime_validate.py +422 -0
  81. package/dist/composer/verbs/smoke.py +134 -0
  82. package/dist/composer/verbs/status.py +383 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +2 -9
  85. package/dist/index.js.map +1 -1
  86. package/dist/runtime/load-runtime-config.d.ts +17 -0
  87. package/dist/runtime/load-runtime-config.d.ts.map +1 -1
  88. package/dist/runtime/load-runtime-config.js +2 -2
  89. package/dist/runtime/load-runtime-config.js.map +1 -1
  90. package/dist/runtime-api/config.d.ts +2 -2
  91. package/dist/runtime-api/config.js +4 -4
  92. package/dist/runtime-api/config.js.map +1 -1
  93. package/dist/runtime-api/handlers/ingest-signals.d.ts +3 -5
  94. package/dist/runtime-api/handlers/ingest-signals.d.ts.map +1 -1
  95. package/dist/runtime-api/handlers/ingest-signals.js +5 -14
  96. package/dist/runtime-api/handlers/ingest-signals.js.map +1 -1
  97. package/dist/runtime-api/intake/intake-mount.d.ts.map +1 -1
  98. package/dist/runtime-api/intake/intake-mount.js +2 -8
  99. package/dist/runtime-api/intake/intake-mount.js.map +1 -1
  100. package/dist/skills-manager/manifest.d.ts.map +1 -1
  101. package/dist/skills-manager/manifest.js +1 -3
  102. package/dist/skills-manager/manifest.js.map +1 -1
  103. package/dist/skills-manager/skills-manifest.json +1 -3
  104. package/package.json +4 -2
  105. package/dist/runtime-api/intake/ingest-mutex.d.ts +0 -16
  106. package/dist/runtime-api/intake/ingest-mutex.d.ts.map +0 -1
  107. package/dist/runtime-api/intake/ingest-mutex.js +0 -18
  108. package/dist/runtime-api/intake/ingest-mutex.js.map +0 -1
@@ -0,0 +1,425 @@
1
+ """generate — deterministic artifact generation from the registry (D4, D6).
2
+
3
+ Everything the agent and the tooling read is generated here from ``NODES`` alone, so it
4
+ cannot drift from the models the validator/compiler use. Three artifacts, written to
5
+ ``composer/generated/`` (see that directory's README):
6
+
7
+ graph.schema.json — JSON Schema for the graph document format (D6). YAML is authored;
8
+ this JSON Schema is canonical. Per-node param constraints are
9
+ derived via if/then on the node name. YAML anchors/merge keys are
10
+ banned at the document level (noted here; enforced in slice 2).
11
+ catalog.md — the ~1-2k-token index: node names + one-liners + param summaries.
12
+ describe.md — full ports/params/example/cost per node (`composer describe`).
13
+
14
+ Determinism is a hard requirement (the golden-file oracle): stable ordering
15
+ (``sorted_nodes``), sorted JSON keys, no timestamps, trailing newline. Run twice → byte
16
+ identical. Entry point:
17
+
18
+ python -m composer.registry.generate # write artifacts
19
+ python -m composer.registry.generate --check # exit 1 if regeneration differs
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import json
25
+ import sys
26
+ from pathlib import Path
27
+
28
+ from .models import NodeDef, Param, PortType, SCHEMA_VERSION
29
+ from .nodes import KIND_ORDER, sorted_nodes
30
+
31
+ GENERATED_DIR = Path(__file__).resolve().parent.parent / "generated"
32
+ SCHEMA_PATH = GENERATED_DIR / "graph.schema.json"
33
+ CATALOG_PATH = GENERATED_DIR / "catalog.md"
34
+ DESCRIBE_PATH = GENERATED_DIR / "describe.md"
35
+
36
+ _JSON_TYPE = {
37
+ "int": "integer",
38
+ "float": "number",
39
+ "str": "string",
40
+ "bool": "boolean",
41
+ }
42
+
43
+
44
+ # --------------------------------------------------------------------------- JSON Schema
45
+
46
+ def _param_schema(param: Param) -> dict:
47
+ """JSON Schema fragment for a single param (with bounds, choices, default)."""
48
+ if param.type == "list[str]":
49
+ node: dict = {"type": "array", "items": {"type": "string"}}
50
+ elif param.type == "map_str_float":
51
+ node = {"type": "object", "additionalProperties": {"type": "number"}}
52
+ elif param.type == "map_str_json":
53
+ # str-keyed JSON object passed verbatim to an MCP tool (mcp_read escape hatch); values
54
+ # are arbitrary JSON, so no additionalProperties constraint beyond "it is an object".
55
+ node = {"type": "object"}
56
+ else:
57
+ node = {"type": _JSON_TYPE[param.type]}
58
+ if param.minimum is not None:
59
+ node["minimum"] = param.minimum
60
+ if param.maximum is not None:
61
+ node["maximum"] = param.maximum
62
+ if param.choices is not None:
63
+ node["enum"] = list(param.choices)
64
+ if param.default is not None:
65
+ node["default"] = list(param.default) if isinstance(param.default, tuple) else param.default
66
+ node["description"] = param.doc
67
+ return node
68
+
69
+
70
+ def _params_object_schema(node: NodeDef) -> dict:
71
+ props = {p.name: _param_schema(p) for p in node.params}
72
+ required = [p.name for p in node.params if p.default is None]
73
+ obj: dict = {"type": "object", "additionalProperties": False, "properties": props}
74
+ if required:
75
+ obj["required"] = sorted(required)
76
+ return obj
77
+
78
+
79
+ def _pure_fn_port_schema() -> dict:
80
+ """A pure_fn declared port: name + a type from the closed PortType vocabulary."""
81
+ return {
82
+ "type": "object",
83
+ "additionalProperties": False,
84
+ "required": ["name", "type"],
85
+ "properties": {
86
+ "name": {"type": "string"},
87
+ "type": {"enum": [t.value for t in PortType]},
88
+ "doc": {"type": "string"},
89
+ },
90
+ }
91
+
92
+
93
+ def _pure_fn_param_schema() -> dict:
94
+ """A pure_fn literal param (config, not an edge): name + type + optional default."""
95
+ return {
96
+ "type": "object",
97
+ "additionalProperties": False,
98
+ "required": ["name", "type"],
99
+ "properties": {
100
+ "name": {"type": "string"},
101
+ "type": {"enum": ["int", "float", "str", "bool", "list[str]", "map_str_float"]},
102
+ "default": {},
103
+ "doc": {"type": "string"},
104
+ },
105
+ }
106
+
107
+
108
+ def _pure_fn_test_schema() -> dict:
109
+ """An inline fixture case: named input values -> expected outputs (run at validate time)."""
110
+ return {
111
+ "type": "object",
112
+ "additionalProperties": False,
113
+ "required": ["name", "inputs", "expect"],
114
+ "properties": {
115
+ "name": {"type": "string"},
116
+ "inputs": {"type": "object"},
117
+ "expect": {"type": "object"},
118
+ },
119
+ }
120
+
121
+
122
+ def build_schema() -> dict:
123
+ nodes = sorted_nodes()
124
+ names = sorted(n.name for n in nodes)
125
+
126
+ defs = {f"params_{n.name}": _params_object_schema(n) for n in nodes}
127
+ all_of = [
128
+ {
129
+ "if": {"properties": {"node": {"const": n.name}}},
130
+ "then": {"properties": {"with": {"$ref": f"#/$defs/params_{n.name}"}}},
131
+ }
132
+ for n in sorted(nodes, key=lambda n: n.name)
133
+ ]
134
+
135
+ return {
136
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
137
+ "$id": "https://senpi.ai/strategy-composer/graph.schema.json",
138
+ "title": "Strategy Composer Graph",
139
+ "description": (
140
+ "Typed dataflow DAG for a trading strategy (D1). YAML is authored; this JSON "
141
+ "Schema is canonical (D6). YAML anchors and merge keys are banned at the "
142
+ "document level (enforced in the validator, slice 2). Generated from the node "
143
+ "registry — do not edit by hand."
144
+ ),
145
+ "type": "object",
146
+ "additionalProperties": False,
147
+ "required": ["nodes"],
148
+ "properties": {
149
+ "version": {"const": SCHEMA_VERSION, "description": "Registry/schema version."},
150
+ "metadata": {
151
+ "type": "object",
152
+ "description": "Interview answers and authoring notes (D7); free-form.",
153
+ },
154
+ "nodes": {
155
+ "type": "array",
156
+ "minItems": 1,
157
+ "description": (
158
+ "The DAG nodes. Each is EITHER a primitive node binding a registry "
159
+ "`node:` to params, OR a per-graph `kind: pure_fn` node (D2 kind 2) "
160
+ "carrying declared typed inputs/outputs and an inline `fn:` body. The "
161
+ "per-node `node`-keyed branches below apply only to primitive nodes; a "
162
+ "pure_fn node (no `node:`) satisfies them vacuously and is checked by the "
163
+ "validator's purity + inline-test stage."
164
+ ),
165
+ "items": {
166
+ "type": "object",
167
+ "additionalProperties": False,
168
+ "required": ["id"],
169
+ "properties": {
170
+ "id": {"type": "string", "description": "Unique node id within the graph."},
171
+ "node": {
172
+ "type": "string",
173
+ "enum": names,
174
+ "description": "Registry node name (primitive nodes only).",
175
+ },
176
+ "with": {"type": "object", "description": "Node parameters."},
177
+ "kind": {
178
+ "enum": ["pure_fn"],
179
+ "description": "Marks a per-graph pure-function node (D2 kind 2).",
180
+ },
181
+ "inputs": {
182
+ "type": "array",
183
+ "items": _pure_fn_port_schema(),
184
+ "description": "pure_fn declared typed input ports.",
185
+ },
186
+ "outputs": {
187
+ "type": "array",
188
+ "items": _pure_fn_port_schema(),
189
+ "description": "pure_fn declared typed output ports.",
190
+ },
191
+ "params": {
192
+ "type": "array",
193
+ "items": _pure_fn_param_schema(),
194
+ "description": "pure_fn literal params (config, not edges).",
195
+ },
196
+ "fn": {
197
+ "type": "string",
198
+ "description": "pure_fn body: exactly one top-level `def` (purity-checked).",
199
+ },
200
+ "tests": {
201
+ "type": "array",
202
+ "items": _pure_fn_test_schema(),
203
+ "description": "pure_fn inline fixture tests, run at validate time.",
204
+ },
205
+ },
206
+ "allOf": all_of,
207
+ },
208
+ },
209
+ "edges": {
210
+ "type": "array",
211
+ "description": "Wiring: from 'nodeId.outPort' to 'nodeId.inPort' (D1).",
212
+ "items": {
213
+ "type": "object",
214
+ "additionalProperties": False,
215
+ "required": ["from", "to"],
216
+ "properties": {
217
+ "from": {"type": "string", "pattern": "^[^.]+\\.[^.]+$"},
218
+ "to": {"type": "string", "pattern": "^[^.]+\\.[^.]+$"},
219
+ },
220
+ },
221
+ },
222
+ "pipeline": {
223
+ "type": "array",
224
+ "items": {"type": "string"},
225
+ "description": (
226
+ "RESERVED shorthand marker for the common linear case (D1/D6). "
227
+ "Desugaring into nodes+edges is slice 2 and is NOT built here."
228
+ ),
229
+ },
230
+ },
231
+ "$defs": defs,
232
+ }
233
+
234
+
235
+ def render_schema() -> str:
236
+ return json.dumps(build_schema(), indent=2, sort_keys=True) + "\n"
237
+
238
+
239
+ # ------------------------------------------------------------------------------- catalog
240
+
241
+ def _param_summary(param: Param) -> str:
242
+ if param.default is None:
243
+ val = "required"
244
+ elif param.type == "list[str]":
245
+ val = "[" + ", ".join(param.default) + "]"
246
+ elif param.type in ("map_str_float", "map_str_json"):
247
+ val = "{...}" if param.default else "{}"
248
+ else:
249
+ val = repr(param.default) if param.type == "str" else str(param.default)
250
+ body = f"{param.name} ({param.type}={val}"
251
+ if param.minimum is not None or param.maximum is not None:
252
+ body += f", {_bound(param, param.minimum)}..{_bound(param, param.maximum)}"
253
+ return body + ")"
254
+
255
+
256
+ def _bound(param: Param, value) -> str:
257
+ if value is None:
258
+ return ""
259
+ return str(int(value)) if param.type == "int" else str(value)
260
+
261
+
262
+ def render_catalog() -> str:
263
+ lines = [
264
+ "# Strategy Composer — Node Catalog",
265
+ "",
266
+ f"Schema version: {SCHEMA_VERSION}. Generated from composer/registry/nodes.py — "
267
+ "do not edit by hand.",
268
+ "",
269
+ "Progressive-disclosure index (D4): node names, one-liners, param summaries. "
270
+ "`composer describe <node>` gives full ports/params/examples.",
271
+ "",
272
+ "## scoring",
273
+ "",
274
+ "Bespoke scoring/classification is a `kind: pure_fn` node (typed ports, one `def`, "
275
+ "math/statistics only, inline tests), never a primitive; `hard_gates_all` covers "
276
+ "simple gate-and-weigh. Repeated shapes promote post-MVP.",
277
+ ]
278
+ nodes = sorted_nodes()
279
+ for kind in KIND_ORDER:
280
+ kind_nodes = [n for n in nodes if n.kind.value == kind]
281
+ if not kind_nodes:
282
+ continue
283
+ lines.append("")
284
+ lines.append(f"## {kind}")
285
+ lines.append("")
286
+ for node in kind_nodes:
287
+ lines.append(f"- **{node.name}** — {node.summary}")
288
+ if node.params:
289
+ params = "; ".join(_param_summary(p) for p in node.params)
290
+ lines.append(f" - params: {params}")
291
+ return "\n".join(lines) + "\n"
292
+
293
+
294
+ # ------------------------------------------------------------------------------ describe
295
+
296
+ def _port_line(port) -> str:
297
+ line = f"- `{port.name}`: {port.type.value} — {port.doc}"
298
+ annotations = []
299
+ if not port.required:
300
+ annotations.append("optional")
301
+ if port.merge:
302
+ annotations.append("merge")
303
+ if annotations:
304
+ line += f" [{', '.join(annotations)}]"
305
+ return line
306
+
307
+
308
+ def _param_detail(param: Param) -> str:
309
+ if param.default is None:
310
+ default = "required"
311
+ elif param.type == "list[str]":
312
+ default = "[" + ", ".join(param.default) + "]"
313
+ elif param.type in ("map_str_float", "map_str_json"):
314
+ default = json.dumps(param.default, sort_keys=True)
315
+ else:
316
+ default = repr(param.default) if param.type == "str" else str(param.default)
317
+ line = f"- `{param.name}`: {param.type} (default: {default})"
318
+ if param.minimum is not None or param.maximum is not None:
319
+ line += f" [{_bound(param, param.minimum)}..{_bound(param, param.maximum)}]"
320
+ if param.choices is not None:
321
+ line += f" choices: {list(param.choices)}"
322
+ return line + f" — {param.doc}"
323
+
324
+
325
+ def _describe_node(node) -> list[str]:
326
+ """The reference block for ONE node (shared by the full describe.md and the per-node
327
+ `composer describe <node>` verb, so the two never drift)."""
328
+ lines = [
329
+ f"## {node.name} ({node.kind.value})",
330
+ "",
331
+ node.doc,
332
+ "",
333
+ "Inputs:",
334
+ ]
335
+ lines.extend([_port_line(p) for p in node.inputs] or ["- (none)"])
336
+ lines.append("")
337
+ lines.append("Outputs:")
338
+ lines.extend([_port_line(p) for p in node.outputs] or ["- (none)"])
339
+ lines.append("")
340
+ lines.append("Params:")
341
+ lines.extend([_param_detail(p) for p in node.params] or ["- (none)"])
342
+ lines.append("")
343
+ lines.append(f"Cost: {node.cost}")
344
+ lines.append("")
345
+ lines.append("Example:")
346
+ lines.append("```yaml")
347
+ lines.append(node.example.rstrip("\n"))
348
+ lines.append("```")
349
+ return lines
350
+
351
+
352
+ def render_describe() -> str:
353
+ lines = [
354
+ "# Strategy Composer — Node Reference",
355
+ "",
356
+ f"Schema version: {SCHEMA_VERSION}. Generated from composer/registry/nodes.py — "
357
+ "do not edit by hand.",
358
+ ]
359
+ for node in sorted_nodes():
360
+ lines.append("")
361
+ lines.extend(_describe_node(node))
362
+ return "\n".join(lines) + "\n"
363
+
364
+
365
+ def render_describe_node(name: str) -> str:
366
+ """Render the reference block for a single node by name (the `composer describe` verb).
367
+
368
+ Raises KeyError with the nearest-alternatives hint when the name is unknown, mirroring
369
+ the validator's unknown-node teaching error so the surface stays consistent.
370
+ """
371
+ from . import NODES_BY_NAME
372
+
373
+ node = NODES_BY_NAME.get(name)
374
+ if node is None:
375
+ known = sorted(NODES_BY_NAME)
376
+ near = [n for n in known if name in n or n in name or n[:3] == name[:3]]
377
+ hint = f" Did you mean: {', '.join(near[:4])}?" if near else ""
378
+ raise KeyError(
379
+ f"unknown node {name!r}.{hint} Run `composer catalog` for the full index."
380
+ )
381
+ return "\n".join(_describe_node(node)) + "\n"
382
+
383
+
384
+ # -------------------------------------------------------------------------------- driver
385
+
386
+ ARTIFACTS = {
387
+ "graph.schema.json": (SCHEMA_PATH, render_schema),
388
+ "catalog.md": (CATALOG_PATH, render_catalog),
389
+ "describe.md": (DESCRIBE_PATH, render_describe),
390
+ }
391
+
392
+
393
+ def write_all() -> None:
394
+ GENERATED_DIR.mkdir(parents=True, exist_ok=True)
395
+ for _name, (path, render) in ARTIFACTS.items():
396
+ path.write_text(render(), encoding="utf-8")
397
+
398
+
399
+ def check_all() -> list[str]:
400
+ """Return the list of artifact names whose committed content differs from a fresh render."""
401
+ stale = []
402
+ for name, (path, render) in ARTIFACTS.items():
403
+ current = path.read_text(encoding="utf-8") if path.exists() else None
404
+ if current != render():
405
+ stale.append(name)
406
+ return stale
407
+
408
+
409
+ def main(argv: list[str] | None = None) -> int:
410
+ argv = sys.argv[1:] if argv is None else argv
411
+ if "--check" in argv:
412
+ stale = check_all()
413
+ if stale:
414
+ print("stale generated artifacts (run `python -m composer.registry.generate`): "
415
+ + ", ".join(stale), file=sys.stderr)
416
+ return 1
417
+ print("generated artifacts are up to date")
418
+ return 0
419
+ write_all()
420
+ print(f"wrote {len(ARTIFACTS)} artifacts to {GENERATED_DIR}")
421
+ return 0
422
+
423
+
424
+ if __name__ == "__main__":
425
+ raise SystemExit(main())
@@ -0,0 +1,193 @@
1
+ {
2
+ "_comment": "Trimmed snapshot of the live prod MCP tools/list (names + input-schema hash only) for the registry closure check. Regenerate via MCP_SNAPSHOT_REFRESH=1 pytest python-tests/composer/test_mcp_closure.py (fetches https://mcp.prod.senpi.ai/mcp, unauthenticated initialize + tools/list). Do not hand-edit.",
3
+ "source": "https://mcp.prod.senpi.ai/mcp",
4
+ "tool_count": 62,
5
+ "tools": {
6
+ "account_get_historical_info": {
7
+ "schema_sha256": "343e4681e9c8d500ccd29de71e8cf39a7ee013f438f3e4e219ce25f40c89f6f9"
8
+ },
9
+ "account_get_portfolio": {
10
+ "schema_sha256": "f9192e9e827d7d1c112bb7f25721e6a2e01955ecc150e238a6bd6a7beecadced"
11
+ },
12
+ "arena_leaderboard": {
13
+ "schema_sha256": "ce4c834ff0444dddf743e5d9f3a366d6e30ab5ee96dcc512e94ad106deb6fad2"
14
+ },
15
+ "arena_pool": {
16
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
17
+ },
18
+ "arena_prizes": {
19
+ "schema_sha256": "421d6192dfab0707b947e318d96d78a94fc80d8d63d99f275093a04e88b05917"
20
+ },
21
+ "arena_roe_chart": {
22
+ "schema_sha256": "ea0f18eda7b70ce3b3d2ddf84f44f7ec8029549c403c55dcf5ee35032c62d535"
23
+ },
24
+ "cancel_order": {
25
+ "schema_sha256": "27093457765e49342e60be640f5160cf839e832942f6f9532fab1246961654c3"
26
+ },
27
+ "close_position": {
28
+ "schema_sha256": "a9992eecf1e84e639be4356e36f79c79319382ac354f2b254f73f8b3b12de829"
29
+ },
30
+ "create_position": {
31
+ "schema_sha256": "01d65e0cb9cf1aec73f37d8246a7f2b75a7826f83f6dfc5c3ee07cf9b0c4b80d"
32
+ },
33
+ "discovery_get_open_position_realized_pnl": {
34
+ "schema_sha256": "c40eeebdd3242b3a97821a9f45c99cdc1bfe476482bb660b9ccdf65055632358"
35
+ },
36
+ "discovery_get_top_strategies": {
37
+ "schema_sha256": "bf9c4d412268be3c8f2aae23c8af4b5a69fb12f15ccdfd487b6fc970ea5f674f"
38
+ },
39
+ "discovery_get_top_traders": {
40
+ "schema_sha256": "38d62d42db6811ff6e2b707036bc92110318d32ee2a405896b0d598057db441e"
41
+ },
42
+ "discovery_get_trader_history": {
43
+ "schema_sha256": "fef06f73272f9a637f937d898be4fddd23b47150062f0bd36eebe3ca0633cc60"
44
+ },
45
+ "discovery_get_trader_state": {
46
+ "schema_sha256": "9a07274df7f2d5721478100cf5e4b9b5b1ef41ccee25b7e94ff01324a94c20f4"
47
+ },
48
+ "edit_position": {
49
+ "schema_sha256": "a5b8e3a7f2ac89b0e262354f74a18aa56239379d37b65897e8d7465865f17b00"
50
+ },
51
+ "estimate_custom_strategy_positions_opening": {
52
+ "schema_sha256": "341b278c6bc5bc7905facf44a39d55d2aa60cda02df0cd231629f030a94b2ba0"
53
+ },
54
+ "execution_estimate_position_opening": {
55
+ "schema_sha256": "ff038e8aeb0baf8dcee1f1c8e6ddb7d2b20ee5ef904a521fa15b548eb4eaad22"
56
+ },
57
+ "execution_get_closed_position_details": {
58
+ "schema_sha256": "3e4eb5ae16ae78866979ee466e52a15716b090dee82669515f584cecf7e8592f"
59
+ },
60
+ "execution_get_open_position_details": {
61
+ "schema_sha256": "406eb43e19077ce59e967b4b73100e72e0bfdec4205ceb9ef82fbc412d9a3d24"
62
+ },
63
+ "execution_get_order_status": {
64
+ "schema_sha256": "22e5705787eb675c913a28f4fa324f80b127a147141a50d1ac5ed1e7ccfb7d2b"
65
+ },
66
+ "get_loyalty_tiers": {
67
+ "schema_sha256": "c8a1ac469a826ea3547ac220c7bbfdcd6b58080d4ec596ff2a0149c5ccb9b699"
68
+ },
69
+ "leaderboard_get_markets": {
70
+ "schema_sha256": "a317a038e3a1303d3f57f75a48829a6eacdb4de0389b0b2c37f2a0b089079dd7"
71
+ },
72
+ "leaderboard_get_momentum_events": {
73
+ "schema_sha256": "0820afef65df3db8c953d3730ec788582dedc829d4f69ee4cde68911d574fcf5"
74
+ },
75
+ "leaderboard_get_status": {
76
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
77
+ },
78
+ "leaderboard_get_top": {
79
+ "schema_sha256": "454650ea0b6ef0d3c649572d64181d76c721501c7bfe493807cd217ebb0b959d"
80
+ },
81
+ "leaderboard_get_trader": {
82
+ "schema_sha256": "0a2734aef6353d2453bf6baf1d98adeb1f2a160fbce63100028ecf14653a598b"
83
+ },
84
+ "leaderboard_get_trader_positions": {
85
+ "schema_sha256": "0a2734aef6353d2453bf6baf1d98adeb1f2a160fbce63100028ecf14653a598b"
86
+ },
87
+ "list_senpi_guides": {
88
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
89
+ },
90
+ "market_get_asset_data": {
91
+ "schema_sha256": "00c3d06ea47ed8d5c15db12cfa1d7cb3c0e13489ed352fb9a0bcab757a654ddb"
92
+ },
93
+ "market_get_cross_asset_flows": {
94
+ "schema_sha256": "64c0acc7ee5112d28137cf5da5b7391e7d46e3059aa9e549dcf8f043524211ba"
95
+ },
96
+ "market_get_funding_history": {
97
+ "schema_sha256": "7ab047ba0d7d08fffe96b677137117584e198c1500e83bd53075a68970638d16"
98
+ },
99
+ "market_get_funding_regime": {
100
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
101
+ },
102
+ "market_get_prices": {
103
+ "schema_sha256": "31d8841aef56629c14676092d10b023aed8d1ebfc83c35b57709980f1c4d5584"
104
+ },
105
+ "market_list_instruments": {
106
+ "schema_sha256": "6c94db6f450c8cbd6e3977beaddddd9861b2534239d96d1e48949fa749f0a0cd"
107
+ },
108
+ "ratchet_stop_add": {
109
+ "schema_sha256": "40be0af285960b6bccd8ee6be9c59672a9b0f3497b7cdcfaffdcdeea4a8dfea8"
110
+ },
111
+ "ratchet_stop_delete": {
112
+ "schema_sha256": "6691ba26604fa3c87424289cf222af89b4cc8e19faf0c23c51f3dea4d12b3274"
113
+ },
114
+ "ratchet_stop_edit": {
115
+ "schema_sha256": "0f3d557613ce310b9e8029f7a36e3fab8936b41acf2a58963fa6d330105b6204"
116
+ },
117
+ "ratchet_stop_events": {
118
+ "schema_sha256": "2571476a51a089c95f8c3466eb8c6dd7144c1a1735801dbdfdd2dd40b76cf485"
119
+ },
120
+ "ratchet_stop_get": {
121
+ "schema_sha256": "567f87bcc34142ab4408542ed895c6662a7dd1b8d294261a58ef00c05622f2d0"
122
+ },
123
+ "ratchet_stop_list": {
124
+ "schema_sha256": "478226aafa7f1497a6fa29610208cd7a7075cbc75d102801f029ab5064bb456b"
125
+ },
126
+ "read_senpi_guide": {
127
+ "schema_sha256": "7e3d0dda419529f5f4f192a2a87e7897d9b13dda157803276df802240a2961ad"
128
+ },
129
+ "strategy_bridge_funds_from_hyperliquid_to_evm": {
130
+ "schema_sha256": "625d5c98d21eb3d3244f68e75af1496e8dbd58a7bbee5a3c7e4431aef025a645"
131
+ },
132
+ "strategy_close": {
133
+ "schema_sha256": "4dd7cae4b3282cc957341124d010f1f66774d4da56c4bb0ee19697b94f8f6ab1"
134
+ },
135
+ "strategy_close_positions": {
136
+ "schema_sha256": "1f301a7d902a96238e905192b135231e1afbb7236329bbdc3c1d4fc6a33ef58f"
137
+ },
138
+ "strategy_create": {
139
+ "schema_sha256": "8d164f7bc0a350ebfc73cb9708cca0065b4e144e2afec5087dce7bde8e46a099"
140
+ },
141
+ "strategy_create_custom_strategy": {
142
+ "schema_sha256": "10647877b62d274a6196fdd4c5586083706cf077fd398e3e5fc2e4e214952664"
143
+ },
144
+ "strategy_get": {
145
+ "schema_sha256": "85a57eda310b79cb1c6be47a3ae1b9f42d1274abbd6afcde1c2ef55734d2fea6"
146
+ },
147
+ "strategy_get_asset_trading_limits": {
148
+ "schema_sha256": "a7e88d43bcc19f61c9364d8008d14f471f6baf7a0b82a095de982959e70fe53d"
149
+ },
150
+ "strategy_get_clearinghouse_state": {
151
+ "schema_sha256": "06a33161f5e39fb38fbc79db0728b2404c18f0351169f28bf48274f5ea27d44e"
152
+ },
153
+ "strategy_get_open_orders": {
154
+ "schema_sha256": "23e715d14e321fe64647512e67a6e0553179268fb6e4dc17aaff4da8b1efed17"
155
+ },
156
+ "strategy_get_pnl_and_account_value_history": {
157
+ "schema_sha256": "0198d02c8c07affda424afe6f761d2c4525a2f66d1b441c5bdeeb56a2b992877"
158
+ },
159
+ "strategy_list": {
160
+ "schema_sha256": "78b1037a28cf446ad73cee48cb55d39a917c47fb0cc363c0836110039cf72a9f"
161
+ },
162
+ "strategy_pause": {
163
+ "schema_sha256": "4dd7cae4b3282cc957341124d010f1f66774d4da56c4bb0ee19697b94f8f6ab1"
164
+ },
165
+ "strategy_top_up": {
166
+ "schema_sha256": "5b90a94f30311ccf4ab6fd207929fef356073783acce189765cf94e5245f6a83"
167
+ },
168
+ "strategy_update": {
169
+ "schema_sha256": "7011937857282acf33e08f1d51c4d4425d325a48d5f7fc52bc580ebce1c52093"
170
+ },
171
+ "strategy_withdraw_funds": {
172
+ "schema_sha256": "297b1e9fb90acc7d62d2e1d95e6cb60a716f1cf6299e9ea6bf4ba3dd47387631"
173
+ },
174
+ "transfer_spot_to_perps": {
175
+ "schema_sha256": "89a78a64ef01c8ceaa381e54158c3111fa1671c98daea6a72e115a2a028568bf"
176
+ },
177
+ "user_claim_referral_rewards": {
178
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
179
+ },
180
+ "user_get_me": {
181
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
182
+ },
183
+ "user_get_referral_rewards": {
184
+ "schema_sha256": "efddc7bd8bbcef73a14eb1ace1ffdaec81e518ef1e13c1e9271d0b8acb694a49"
185
+ },
186
+ "user_get_senpi_points": {
187
+ "schema_sha256": "09aae7dac73afe34d62721e3c59c14cc2e78cdba77f8bee971161a7841379925"
188
+ },
189
+ "user_get_senpi_points_leaderboard": {
190
+ "schema_sha256": "e7289cdc922d69fd9028e0ddf3b9f848424a0bab059d79865a7382cb46dde444"
191
+ }
192
+ }
193
+ }