flox-mcp 0.6.0__tar.gz

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 (80) hide show
  1. flox_mcp-0.6.0/PKG-INFO +182 -0
  2. flox_mcp-0.6.0/README.md +158 -0
  3. flox_mcp-0.6.0/flox_mcp/__init__.py +21 -0
  4. flox_mcp-0.6.0/flox_mcp/cli.py +187 -0
  5. flox_mcp-0.6.0/flox_mcp/data/binding_manifest.json +8010 -0
  6. flox_mcp-0.6.0/flox_mcp/data/c-api.snapshot +491 -0
  7. flox_mcp-0.6.0/flox_mcp/data/docs.fts.sqlite +0 -0
  8. flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_001.md +29 -0
  9. flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_002.md +41 -0
  10. flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_003.md +34 -0
  11. flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_004.md +35 -0
  12. flox_mcp-0.6.0/flox_mcp/data/errors/E_BACKTEST_001.md +36 -0
  13. flox_mcp-0.6.0/flox_mcp/data/errors/E_DATA_001.md +44 -0
  14. flox_mcp-0.6.0/flox_mcp/data/errors/E_GRAPH_001.md +34 -0
  15. flox_mcp-0.6.0/flox_mcp/data/errors/E_GRAPH_002.md +37 -0
  16. flox_mcp-0.6.0/flox_mcp/data/errors/E_INPUT_001.md +31 -0
  17. flox_mcp-0.6.0/flox_mcp/data/errors/E_IO_001.md +36 -0
  18. flox_mcp-0.6.0/flox_mcp/data/errors/E_IO_002.md +34 -0
  19. flox_mcp-0.6.0/flox_mcp/data/errors/E_KEY_001.md +46 -0
  20. flox_mcp-0.6.0/flox_mcp/data/errors/E_LEN_001.md +49 -0
  21. flox_mcp-0.6.0/flox_mcp/data/errors/E_LEN_002.md +31 -0
  22. flox_mcp-0.6.0/flox_mcp/data/errors/E_LEN_003.md +36 -0
  23. flox_mcp-0.6.0/flox_mcp/data/errors/E_RUN_001.md +35 -0
  24. flox_mcp-0.6.0/flox_mcp/data/errors/E_RUN_002.md +40 -0
  25. flox_mcp-0.6.0/flox_mcp/data/errors/E_RUN_003.md +40 -0
  26. flox_mcp-0.6.0/flox_mcp/data/errors/E_SYM_001.md +72 -0
  27. flox_mcp-0.6.0/flox_mcp/data/errors/E_TIME_001.md +36 -0
  28. flox_mcp-0.6.0/flox_mcp/data/errors/E_VAL_002.md +65 -0
  29. flox_mcp-0.6.0/flox_mcp/data/examples_index.json +61 -0
  30. flox_mcp-0.6.0/flox_mcp/data/gotchas.json +57 -0
  31. flox_mcp-0.6.0/flox_mcp/data/ir.snapshot.json +11671 -0
  32. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/codon/bar-driven.tmpl +38 -0
  33. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/codon/hybrid.tmpl +46 -0
  34. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/codon/trade-driven.tmpl +40 -0
  35. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/node/bar-driven.tmpl +32 -0
  36. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/node/hybrid.tmpl +38 -0
  37. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/node/trade-driven.tmpl +33 -0
  38. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/python/bar-driven.tmpl +30 -0
  39. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/python/hybrid.tmpl +37 -0
  40. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/python/trade-driven.tmpl +30 -0
  41. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/quickjs/bar-driven.tmpl +32 -0
  42. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/quickjs/hybrid.tmpl +39 -0
  43. flox_mcp-0.6.0/flox_mcp/data/templates/strategy/quickjs/trade-driven.tmpl +33 -0
  44. flox_mcp-0.6.0/flox_mcp/server.py +1228 -0
  45. flox_mcp-0.6.0/flox_mcp/tools/__init__.py +6 -0
  46. flox_mcp-0.6.0/flox_mcp/tools/_data.py +95 -0
  47. flox_mcp-0.6.0/flox_mcp/tools/_runtime_worker.py +275 -0
  48. flox_mcp-0.6.0/flox_mcp/tools/analytics.py +159 -0
  49. flox_mcp-0.6.0/flox_mcp/tools/capi.py +73 -0
  50. flox_mcp-0.6.0/flox_mcp/tools/control.py +134 -0
  51. flox_mcp-0.6.0/flox_mcp/tools/docs_search.py +119 -0
  52. flox_mcp-0.6.0/flox_mcp/tools/errors.py +53 -0
  53. flox_mcp-0.6.0/flox_mcp/tools/events.py +120 -0
  54. flox_mcp-0.6.0/flox_mcp/tools/examples.py +122 -0
  55. flox_mcp-0.6.0/flox_mcp/tools/indicators.py +138 -0
  56. flox_mcp-0.6.0/flox_mcp/tools/init_project.py +98 -0
  57. flox_mcp-0.6.0/flox_mcp/tools/lookahead.py +20 -0
  58. flox_mcp-0.6.0/flox_mcp/tools/lookup.py +356 -0
  59. flox_mcp-0.6.0/flox_mcp/tools/positions.py +216 -0
  60. flox_mcp-0.6.0/flox_mcp/tools/record_data.py +173 -0
  61. flox_mcp-0.6.0/flox_mcp/tools/runtime.py +489 -0
  62. flox_mcp-0.6.0/flox_mcp/tools/scaffold.py +92 -0
  63. flox_mcp-0.6.0/flox_mcp/tools/strategy.py +105 -0
  64. flox_mcp-0.6.0/flox_mcp.egg-info/PKG-INFO +182 -0
  65. flox_mcp-0.6.0/flox_mcp.egg-info/SOURCES.txt +78 -0
  66. flox_mcp-0.6.0/flox_mcp.egg-info/dependency_links.txt +1 -0
  67. flox_mcp-0.6.0/flox_mcp.egg-info/entry_points.txt +2 -0
  68. flox_mcp-0.6.0/flox_mcp.egg-info/requires.txt +5 -0
  69. flox_mcp-0.6.0/flox_mcp.egg-info/top_level.txt +1 -0
  70. flox_mcp-0.6.0/pyproject.toml +50 -0
  71. flox_mcp-0.6.0/setup.cfg +4 -0
  72. flox_mcp-0.6.0/tests/test_cli_init.py +173 -0
  73. flox_mcp-0.6.0/tests/test_data_artifacts.py +290 -0
  74. flox_mcp-0.6.0/tests/test_init_project.py +58 -0
  75. flox_mcp-0.6.0/tests/test_no_engine_messages.py +35 -0
  76. flox_mcp-0.6.0/tests/test_polyglot_tools.py +342 -0
  77. flox_mcp-0.6.0/tests/test_positions.py +271 -0
  78. flox_mcp-0.6.0/tests/test_record_data.py +93 -0
  79. flox_mcp-0.6.0/tests/test_runtime_tools.py +316 -0
  80. flox_mcp-0.6.0/tests/test_tools.py +133 -0
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: flox-mcp
3
+ Version: 0.6.0
4
+ Summary: Model Context Protocol server for FLOX — gives AI agents grounded access to indicators, error codes, and the C API.
5
+ Author: FLOX Foundation
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/FLOX-Foundation/flox
8
+ Project-URL: Repository, https://github.com/FLOX-Foundation/flox
9
+ Project-URL: Docs, https://flox-foundation.github.io/flox/
10
+ Keywords: trading,backtest,indicators,mcp,ai,claude,cursor
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Financial and Insurance Industry
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: mcp>=1.0
21
+ Requires-Dist: flox-py>=0.5
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest>=7; extra == "dev"
24
+
25
+ # flox-mcp — Model Context Protocol server for FLOX
26
+
27
+ Gives AI agents (Cursor, Claude Code, Cline) **grounded** access to the
28
+ FLOX C-API surface, error catalog, and indicator library — so they can
29
+ write code against real signatures instead of guessing.
30
+
31
+ The server runs locally on the developer's machine; the IDE spawns it
32
+ as a child process and talks to it over stdio. There is no public
33
+ hosting; nothing leaves the machine.
34
+
35
+ ## Tools
36
+
37
+ | Tool | What it does |
38
+ |---|---|
39
+ | `list_indicators` | Every indicator in `flox_py` with class signature, batch fn (if any), and shape. Filter by substring. |
40
+ | `lookup_error_code` | `E_SYM_001` → full Markdown page with fix recipe, common causes, diagnostics. |
41
+ | `list_capi_functions` | Search the FLOX C-API surface from the committed ABI snapshot. Returns `name + return type + parameter types`. |
42
+ | `validate_strategy` | Static-analysis check on Python strategy code: AST parses, expected hooks present, no `eval`/`exec`. |
43
+ | `explain_event` | Describe the fields of a FLOX event struct (`FloxTradeData`, `FloxBookData`, `FloxBarData`, `FloxSymbolContext`, `FloxSignal`). Accepts a struct name or a raw event dict. |
44
+ | `lookup_symbol` | Take any binding-local spelling (`FloxBarData`, `BarData`, `flox_indicator_ema`, `ema`) and return what the symbol is called in C-API, Python, Node, and Codon. |
45
+ | `list_bindings` | Enumerate the exports of one binding (capi, python, node, codon, quickjs). Substring filter and limit. |
46
+ | `get_example` | Code from `docs/examples/` matching a topic (strategy, connector, indicator, event-handler, risk, backtest), optionally filtered by language. |
47
+ | `scaffold_strategy` | Starter strategy class for Python or Node. Three kinds: bar-driven, trade-driven, hybrid. The Python output goes through `ast.parse` + `validate_strategy`; the Node output goes through `node --check`. CI fails if either breaks, so templates can't quietly rot. |
48
+ | `docs_search` | Top-k FTS5 search over the docs. The index is built from an allowlist of roots; private trackers and `CLAUDE.md` are not in that allowlist. |
49
+ | `run_backtest` | Run a Python strategy against a CSV dataset in a sandboxed subprocess. Caps CPU, memory, and output size; wall-clock timeout. **MVP sandbox** — caps resources but does not isolate filesystem or network. Treat the same way as any untrusted Python; use nsjail / firejail / Docker for production. |
50
+ | `compute_indicator` | Run one FLOX indicator over a list of floats. Accepts class-form (`EMA`) or function-form (`ema`) names and forwards extra kwargs to the constructor. Input capped at 1 MiB. Needs `flox-py` installed (`pip install "flox-mcp[flox]"`). |
51
+ | `suggest_indicator` | Map an English description ('trend filter', 'momentum oscillator', 'volatility band', 'mean revert', 'regime test') to a ranked shortlist of FLOX indicators. Pure keyword heuristic — no LLM call. Always confirm the chosen indicator with `list_indicators` before using it. |
52
+
53
+ ## Quickstart
54
+
55
+ ```bash
56
+ pip install flox-mcp # MCP server + flox-py (indicators, backtest, engine CLI)
57
+ flox-mcp init # writes ./.mcp.json for the current project
58
+ # restart your MCP client (Claude Code / Cursor / Cline) → done
59
+ ```
60
+
61
+ You get the whole surface in one install: docs / lookup tools,
62
+ indicator introspection, backtest, and the engine CLI
63
+ (`flox engine sim`, `flox tape record`). For tier-5/6 (live state,
64
+ `place_order`, `flatten_positions`), also start a paper engine and
65
+ rerun `init` with the printed token:
66
+
67
+ ```bash
68
+ flox engine sim --strategy s.py --tape ./tape # prints engine URL + token
69
+ flox-mcp init --engine-url URL --token T # appends env to ./.mcp.json
70
+ ```
71
+
72
+ `flox engine sim` boots a `Runner` + `SimulatedExecutor` +
73
+ `ControlServer` and writes a runtime snapshot the read tools poll.
74
+ See [`python/flox_py/engine_cli.py`](../python/flox_py/engine_cli.py)
75
+ for the full set of flags.
76
+
77
+ `flox engine sim` boots a `Runner` + `SimulatedExecutor` + `ControlServer`
78
+ and writes a runtime snapshot the read tools poll. See
79
+ [`python/flox_py/engine_cli.py`](../python/flox_py/engine_cli.py)
80
+ for the full set of flags.
81
+
82
+ ### `flox-mcp init` flags
83
+
84
+ | Flag | What it does |
85
+ |---|---|
86
+ | *(default)* | Writes `./.mcp.json` in the current directory. Merges with an existing file. |
87
+ | `--global` | Writes `~/.config/claude/.mcp.json` instead. |
88
+ | `--overwrite` | Replace an existing `mcpServers.flox` entry instead of refusing. |
89
+ | `--print` | Print the merged config to stdout, don't write. |
90
+ | `--engine-url URL` | Sets `FLOX_CONTROL_URL` (tier-5/6 wiring). |
91
+ | `--token T` | Sets `FLOX_CONTROL_TOKEN` (printed by `flox engine sim`). |
92
+
93
+ ### Manual override
94
+
95
+ If you'd rather hand-edit, the resulting `.mcp.json` is shaped like:
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "flox": {
101
+ "command": "flox-mcp",
102
+ "args": ["serve"],
103
+ "env": {
104
+ "FLOX_RUNTIME_STATE": "$HOME/.flox/runtime.json"
105
+ }
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ Same shape works for Claude Code, Cursor, and Cline — they all read
112
+ `mcpServers.<name>` from a project-local `.mcp.json` (or the
113
+ client-specific global path).
114
+
115
+ ## Develop
116
+
117
+ ```bash
118
+ cd mcp
119
+ pip install -e ".[dev]"
120
+
121
+ # Run the server manually (stdio):
122
+ flox-mcp
123
+
124
+ # Run unit tests:
125
+ pytest tests/
126
+
127
+ # Sync bundled data from canonical sources (.api/, docs/errors/):
128
+ python ../scripts/sync_mcp_data.py
129
+ ```
130
+
131
+ ## Bundled data
132
+
133
+ Read-only copies in the wheel:
134
+
135
+ - `flox_mcp/data/c-api.snapshot` — copy of `.api/c-api.snapshot`.
136
+ - `flox_mcp/data/errors/E_*.md` — copies of `docs/errors/E_*.md`.
137
+ - `flox_mcp/data/ir.snapshot.json` — IR (functions, structs, enums,
138
+ typedefs, function pointers) pulled from the IDL spec via libclang.
139
+ Versioned schema.
140
+ - `flox_mcp/data/binding_manifest.json` — per-binding symbol map keyed
141
+ by IDL group. Built from `binding_parity.yaml` plus scans of
142
+ `flox_py/_flox_py/__init__.pyi`, `node/index.d.ts`, and the codon
143
+ golden file.
144
+ - `flox_mcp/data/examples_index.json` — index of `docs/examples/`
145
+ (path, language, topic, sha256).
146
+ - `flox_mcp/data/docs.fts.sqlite` — SQLite FTS5 index over six doc
147
+ roots: `bindings`, `how-to`, `tutorials`, `reference`, `explanation`,
148
+ `errors`. Anything outside that list is skipped at index build time.
149
+ Private trackers and `CLAUDE.md` are not in the allowlist.
150
+ - `flox_mcp/data/templates/strategy/{python,node}/{bar,trade,hybrid}-driven.tmpl`
151
+ — strategy scaffolds rendered by `scaffold_strategy`.
152
+
153
+ `scripts/sync_mcp_data.py --check` runs in CI. Any drift between a
154
+ bundled copy and its canonical source fails the build. To update
155
+ after editing a canonical source: run the script without `--check`
156
+ and commit the diff.
157
+
158
+ The `flox-mcp` package version is bumped in lockstep with `flox-py`
159
+ and `@flox-foundation/flox` by `scripts/set-version.sh`. An installed
160
+ `flox-mcp x.y.z` was built from the same source tree as the matching
161
+ flox-py / npm release, so what the agent sees lines up with what the
162
+ developer has installed.
163
+
164
+ ## Scope notes
165
+
166
+ - The server is **local**. No network calls. AI clients spawn it as a
167
+ child process and talk via stdio.
168
+ - Most tools are read-only data lookups. The two that execute code —
169
+ `compute_indicator` and `run_backtest` — run in-process / in a
170
+ resource-limited subprocess respectively. `run_backtest` is an MVP
171
+ sandbox: it caps CPU, memory, and output size, but does NOT isolate
172
+ filesystem or network. Wrap with nsjail / firejail / Docker for any
173
+ deployment that takes untrusted input.
174
+ - The snapshot used by `list_capi_functions` is the same
175
+ `.api/c-api.snapshot` enforced by the codegen ABI gate; the IR
176
+ snapshot used by `lookup_symbol` / `list_bindings` is regenerated
177
+ from the same IDL spec on every release. So the agent's view of
178
+ the surface tracks what FLOX actually ships.
179
+
180
+ ## License
181
+
182
+ MIT — same as FLOX.
@@ -0,0 +1,158 @@
1
+ # flox-mcp — Model Context Protocol server for FLOX
2
+
3
+ Gives AI agents (Cursor, Claude Code, Cline) **grounded** access to the
4
+ FLOX C-API surface, error catalog, and indicator library — so they can
5
+ write code against real signatures instead of guessing.
6
+
7
+ The server runs locally on the developer's machine; the IDE spawns it
8
+ as a child process and talks to it over stdio. There is no public
9
+ hosting; nothing leaves the machine.
10
+
11
+ ## Tools
12
+
13
+ | Tool | What it does |
14
+ |---|---|
15
+ | `list_indicators` | Every indicator in `flox_py` with class signature, batch fn (if any), and shape. Filter by substring. |
16
+ | `lookup_error_code` | `E_SYM_001` → full Markdown page with fix recipe, common causes, diagnostics. |
17
+ | `list_capi_functions` | Search the FLOX C-API surface from the committed ABI snapshot. Returns `name + return type + parameter types`. |
18
+ | `validate_strategy` | Static-analysis check on Python strategy code: AST parses, expected hooks present, no `eval`/`exec`. |
19
+ | `explain_event` | Describe the fields of a FLOX event struct (`FloxTradeData`, `FloxBookData`, `FloxBarData`, `FloxSymbolContext`, `FloxSignal`). Accepts a struct name or a raw event dict. |
20
+ | `lookup_symbol` | Take any binding-local spelling (`FloxBarData`, `BarData`, `flox_indicator_ema`, `ema`) and return what the symbol is called in C-API, Python, Node, and Codon. |
21
+ | `list_bindings` | Enumerate the exports of one binding (capi, python, node, codon, quickjs). Substring filter and limit. |
22
+ | `get_example` | Code from `docs/examples/` matching a topic (strategy, connector, indicator, event-handler, risk, backtest), optionally filtered by language. |
23
+ | `scaffold_strategy` | Starter strategy class for Python or Node. Three kinds: bar-driven, trade-driven, hybrid. The Python output goes through `ast.parse` + `validate_strategy`; the Node output goes through `node --check`. CI fails if either breaks, so templates can't quietly rot. |
24
+ | `docs_search` | Top-k FTS5 search over the docs. The index is built from an allowlist of roots; private trackers and `CLAUDE.md` are not in that allowlist. |
25
+ | `run_backtest` | Run a Python strategy against a CSV dataset in a sandboxed subprocess. Caps CPU, memory, and output size; wall-clock timeout. **MVP sandbox** — caps resources but does not isolate filesystem or network. Treat the same way as any untrusted Python; use nsjail / firejail / Docker for production. |
26
+ | `compute_indicator` | Run one FLOX indicator over a list of floats. Accepts class-form (`EMA`) or function-form (`ema`) names and forwards extra kwargs to the constructor. Input capped at 1 MiB. Needs `flox-py` installed (`pip install "flox-mcp[flox]"`). |
27
+ | `suggest_indicator` | Map an English description ('trend filter', 'momentum oscillator', 'volatility band', 'mean revert', 'regime test') to a ranked shortlist of FLOX indicators. Pure keyword heuristic — no LLM call. Always confirm the chosen indicator with `list_indicators` before using it. |
28
+
29
+ ## Quickstart
30
+
31
+ ```bash
32
+ pip install flox-mcp # MCP server + flox-py (indicators, backtest, engine CLI)
33
+ flox-mcp init # writes ./.mcp.json for the current project
34
+ # restart your MCP client (Claude Code / Cursor / Cline) → done
35
+ ```
36
+
37
+ You get the whole surface in one install: docs / lookup tools,
38
+ indicator introspection, backtest, and the engine CLI
39
+ (`flox engine sim`, `flox tape record`). For tier-5/6 (live state,
40
+ `place_order`, `flatten_positions`), also start a paper engine and
41
+ rerun `init` with the printed token:
42
+
43
+ ```bash
44
+ flox engine sim --strategy s.py --tape ./tape # prints engine URL + token
45
+ flox-mcp init --engine-url URL --token T # appends env to ./.mcp.json
46
+ ```
47
+
48
+ `flox engine sim` boots a `Runner` + `SimulatedExecutor` +
49
+ `ControlServer` and writes a runtime snapshot the read tools poll.
50
+ See [`python/flox_py/engine_cli.py`](../python/flox_py/engine_cli.py)
51
+ for the full set of flags.
52
+
53
+ `flox engine sim` boots a `Runner` + `SimulatedExecutor` + `ControlServer`
54
+ and writes a runtime snapshot the read tools poll. See
55
+ [`python/flox_py/engine_cli.py`](../python/flox_py/engine_cli.py)
56
+ for the full set of flags.
57
+
58
+ ### `flox-mcp init` flags
59
+
60
+ | Flag | What it does |
61
+ |---|---|
62
+ | *(default)* | Writes `./.mcp.json` in the current directory. Merges with an existing file. |
63
+ | `--global` | Writes `~/.config/claude/.mcp.json` instead. |
64
+ | `--overwrite` | Replace an existing `mcpServers.flox` entry instead of refusing. |
65
+ | `--print` | Print the merged config to stdout, don't write. |
66
+ | `--engine-url URL` | Sets `FLOX_CONTROL_URL` (tier-5/6 wiring). |
67
+ | `--token T` | Sets `FLOX_CONTROL_TOKEN` (printed by `flox engine sim`). |
68
+
69
+ ### Manual override
70
+
71
+ If you'd rather hand-edit, the resulting `.mcp.json` is shaped like:
72
+
73
+ ```json
74
+ {
75
+ "mcpServers": {
76
+ "flox": {
77
+ "command": "flox-mcp",
78
+ "args": ["serve"],
79
+ "env": {
80
+ "FLOX_RUNTIME_STATE": "$HOME/.flox/runtime.json"
81
+ }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ Same shape works for Claude Code, Cursor, and Cline — they all read
88
+ `mcpServers.<name>` from a project-local `.mcp.json` (or the
89
+ client-specific global path).
90
+
91
+ ## Develop
92
+
93
+ ```bash
94
+ cd mcp
95
+ pip install -e ".[dev]"
96
+
97
+ # Run the server manually (stdio):
98
+ flox-mcp
99
+
100
+ # Run unit tests:
101
+ pytest tests/
102
+
103
+ # Sync bundled data from canonical sources (.api/, docs/errors/):
104
+ python ../scripts/sync_mcp_data.py
105
+ ```
106
+
107
+ ## Bundled data
108
+
109
+ Read-only copies in the wheel:
110
+
111
+ - `flox_mcp/data/c-api.snapshot` — copy of `.api/c-api.snapshot`.
112
+ - `flox_mcp/data/errors/E_*.md` — copies of `docs/errors/E_*.md`.
113
+ - `flox_mcp/data/ir.snapshot.json` — IR (functions, structs, enums,
114
+ typedefs, function pointers) pulled from the IDL spec via libclang.
115
+ Versioned schema.
116
+ - `flox_mcp/data/binding_manifest.json` — per-binding symbol map keyed
117
+ by IDL group. Built from `binding_parity.yaml` plus scans of
118
+ `flox_py/_flox_py/__init__.pyi`, `node/index.d.ts`, and the codon
119
+ golden file.
120
+ - `flox_mcp/data/examples_index.json` — index of `docs/examples/`
121
+ (path, language, topic, sha256).
122
+ - `flox_mcp/data/docs.fts.sqlite` — SQLite FTS5 index over six doc
123
+ roots: `bindings`, `how-to`, `tutorials`, `reference`, `explanation`,
124
+ `errors`. Anything outside that list is skipped at index build time.
125
+ Private trackers and `CLAUDE.md` are not in the allowlist.
126
+ - `flox_mcp/data/templates/strategy/{python,node}/{bar,trade,hybrid}-driven.tmpl`
127
+ — strategy scaffolds rendered by `scaffold_strategy`.
128
+
129
+ `scripts/sync_mcp_data.py --check` runs in CI. Any drift between a
130
+ bundled copy and its canonical source fails the build. To update
131
+ after editing a canonical source: run the script without `--check`
132
+ and commit the diff.
133
+
134
+ The `flox-mcp` package version is bumped in lockstep with `flox-py`
135
+ and `@flox-foundation/flox` by `scripts/set-version.sh`. An installed
136
+ `flox-mcp x.y.z` was built from the same source tree as the matching
137
+ flox-py / npm release, so what the agent sees lines up with what the
138
+ developer has installed.
139
+
140
+ ## Scope notes
141
+
142
+ - The server is **local**. No network calls. AI clients spawn it as a
143
+ child process and talk via stdio.
144
+ - Most tools are read-only data lookups. The two that execute code —
145
+ `compute_indicator` and `run_backtest` — run in-process / in a
146
+ resource-limited subprocess respectively. `run_backtest` is an MVP
147
+ sandbox: it caps CPU, memory, and output size, but does NOT isolate
148
+ filesystem or network. Wrap with nsjail / firejail / Docker for any
149
+ deployment that takes untrusted input.
150
+ - The snapshot used by `list_capi_functions` is the same
151
+ `.api/c-api.snapshot` enforced by the codegen ABI gate; the IR
152
+ snapshot used by `lookup_symbol` / `list_bindings` is regenerated
153
+ from the same IDL spec on every release. So the agent's view of
154
+ the surface tracks what FLOX actually ships.
155
+
156
+ ## License
157
+
158
+ MIT — same as FLOX.
@@ -0,0 +1,21 @@
1
+ """flox_mcp — Model Context Protocol server for FLOX.
2
+
3
+ Run via the `flox-mcp` console script or `python -m flox_mcp`. The server
4
+ speaks MCP over stdio and is meant to be spawned as a child process by
5
+ Cursor / Claude Code / Cline; see README for client config.
6
+
7
+ Exposed tools:
8
+
9
+ - list_indicators — alphabetical map of every indicator in flox_py
10
+ with its constructor signature and class shape.
11
+ - lookup_error_code — given a code like "E_SYM_001", return the full
12
+ Markdown page from the canonical error catalog.
13
+ - list_capi_functions — search the FLOX C-API surface (from the
14
+ committed ABI snapshot); returns name + signature.
15
+ - validate_strategy — Python AST sanity-check on strategy code:
16
+ imports resolve, required hooks present,
17
+ forbidden patterns absent.
18
+ - explain_event — describe the fields of a FLOX event struct
19
+ (FloxTradeData, FloxBookData, FloxBarData, ...).
20
+ """
21
+ __version__ = "0.1.0"
@@ -0,0 +1,187 @@
1
+ """``flox-mcp`` console-script entry point with subcommands.
2
+
3
+ Two verbs:
4
+
5
+ * ``flox-mcp serve`` — start the MCP server over stdio. This is the
6
+ same behaviour the bare ``flox-mcp`` invocation has always had,
7
+ preserved for back-compat (and because that's what MCP clients
8
+ invoke directly).
9
+ * ``flox-mcp init`` — write a working ``.mcp.json`` for the current
10
+ shell environment so users do not hand-craft the JSON.
11
+
12
+ Bare ``flox-mcp`` (no args) keeps invoking ``serve`` for back-compat
13
+ with existing MCP client configurations.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import json
19
+ import os
20
+ import shutil
21
+ import sys
22
+ from pathlib import Path
23
+ from typing import Any, Dict, List, Optional
24
+
25
+
26
+ # ── init ──────────────────────────────────────────────────────────────
27
+
28
+
29
+ _DEFAULT_RUNTIME_STATE = "$HOME/.flox/runtime.json"
30
+
31
+
32
+ def _resolve_command() -> List[str]:
33
+ """Pick the most portable form of 'launch the flox-mcp server'.
34
+
35
+ Prefer the installed console script (`flox-mcp serve`) — it
36
+ works from any active Python regardless of which interpreter the
37
+ MCP client launches. Fall back to `python -m flox_mcp.server`
38
+ when the script is not yet on PATH (e.g. an editable install in
39
+ a venv whose ``bin/`` is not exported)."""
40
+ bin_path = shutil.which("flox-mcp")
41
+ if bin_path:
42
+ return [bin_path, "serve"]
43
+ return [sys.executable, "-m", "flox_mcp.server"]
44
+
45
+
46
+ def _build_flox_entry(engine_url: Optional[str],
47
+ token: Optional[str]) -> Dict[str, Any]:
48
+ cmd = _resolve_command()
49
+ env: Dict[str, str] = {
50
+ "FLOX_RUNTIME_STATE": _DEFAULT_RUNTIME_STATE,
51
+ }
52
+ if engine_url:
53
+ env["FLOX_CONTROL_URL"] = engine_url
54
+ if token:
55
+ env["FLOX_CONTROL_TOKEN"] = token
56
+ return {
57
+ "command": cmd[0],
58
+ "args": cmd[1:],
59
+ "env": env,
60
+ }
61
+
62
+
63
+ def _global_config_path() -> Path:
64
+ """Where MCP clients look for a global server config.
65
+
66
+ Claude Code reads ``~/.config/claude/.mcp.json`` on POSIX and
67
+ Claude Desktop reads a platform-specific path. We standardise on
68
+ the POSIX location; users with the Desktop client can pass an
69
+ explicit path via the upcoming ``--out`` flag (out of scope for
70
+ the v1 init).
71
+ """
72
+ return Path(os.environ.get("XDG_CONFIG_HOME", "~/.config")).expanduser() \
73
+ / "claude" / ".mcp.json"
74
+
75
+
76
+ def _local_config_path() -> Path:
77
+ return Path.cwd() / ".mcp.json"
78
+
79
+
80
+ def _read_existing(path: Path) -> Dict[str, Any]:
81
+ if not path.exists():
82
+ return {}
83
+ try:
84
+ return json.loads(path.read_text())
85
+ except json.JSONDecodeError as exc:
86
+ raise SystemExit(
87
+ f"flox-mcp init: existing {path} is not valid JSON ({exc}). "
88
+ f"Fix or remove it before running init."
89
+ )
90
+
91
+
92
+ def _render(config: Dict[str, Any]) -> str:
93
+ return json.dumps(config, indent=2, sort_keys=True) + "\n"
94
+
95
+
96
+ def cmd_init(args: argparse.Namespace) -> int:
97
+ target = _global_config_path() if args.global_ else _local_config_path()
98
+
99
+ flox_entry = _build_flox_entry(args.engine_url, args.token)
100
+ new_config: Dict[str, Any] = _read_existing(target) if not args.print else {}
101
+ new_config.setdefault("mcpServers", {})
102
+
103
+ if "flox" in new_config["mcpServers"] and not args.overwrite \
104
+ and not args.print:
105
+ print(
106
+ f"flox-mcp init: {target} already has an `mcpServers.flox` "
107
+ f"entry. Re-run with --overwrite to replace it.",
108
+ file=sys.stderr,
109
+ )
110
+ return 2
111
+ new_config["mcpServers"]["flox"] = flox_entry
112
+
113
+ rendered = _render(new_config)
114
+ if args.print:
115
+ sys.stdout.write(rendered)
116
+ return 0
117
+
118
+ target.parent.mkdir(parents=True, exist_ok=True)
119
+ target.write_text(rendered)
120
+ print(f"wrote {target}")
121
+ print("restart your MCP client to pick up the flox server.")
122
+ return 0
123
+
124
+
125
+ # ── dispatcher ────────────────────────────────────────────────────────
126
+
127
+
128
+ def main(argv: Optional[List[str]] = None) -> int:
129
+ """``flox-mcp`` console-script entry point.
130
+
131
+ Bare ``flox-mcp`` (no subcommand) still launches the server, so
132
+ existing MCP-client configurations keep working. ``init`` is the
133
+ new bootstrap path.
134
+ """
135
+ raw = sys.argv[1:] if argv is None else list(argv)
136
+
137
+ # Back-compat: bare invocation = serve. That's what every MCP
138
+ # client config in the wild does.
139
+ if not raw or raw[0] not in ("init", "serve"):
140
+ from flox_mcp.server import main as serve_main
141
+ return serve_main() or 0
142
+
143
+ p = argparse.ArgumentParser(prog="flox-mcp")
144
+ sub = p.add_subparsers(dest="cmd", required=True)
145
+
146
+ init_p = sub.add_parser(
147
+ "init",
148
+ help="Write a working .mcp.json for the current environment.",
149
+ )
150
+ init_p.add_argument(
151
+ "--global", dest="global_", action="store_true",
152
+ help="Write to the user-global config (~/.config/claude/.mcp.json) "
153
+ "instead of ./.mcp.json.",
154
+ )
155
+ init_p.add_argument(
156
+ "--overwrite", action="store_true",
157
+ help="Replace an existing mcpServers.flox entry instead of "
158
+ "refusing.",
159
+ )
160
+ init_p.add_argument(
161
+ "--print", action="store_true",
162
+ help="Print the merged config to stdout, write nothing.",
163
+ )
164
+ init_p.add_argument(
165
+ "--engine-url",
166
+ help="Wire tier-5/6 control tools at this engine URL "
167
+ "(sets FLOX_CONTROL_URL). Pair with --token.",
168
+ )
169
+ init_p.add_argument(
170
+ "--token",
171
+ help="ControlServer token (sets FLOX_CONTROL_TOKEN). The "
172
+ "token is printed by `flox engine sim` (W2-T035).",
173
+ )
174
+
175
+ sub.add_parser("serve", help="Start the MCP server over stdio.")
176
+
177
+ args = p.parse_args(raw)
178
+ if args.cmd == "init":
179
+ return cmd_init(args)
180
+ if args.cmd == "serve":
181
+ from flox_mcp.server import main as serve_main
182
+ return serve_main() or 0
183
+ return 0 # pragma: no cover
184
+
185
+
186
+ if __name__ == "__main__":
187
+ raise SystemExit(main())