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.
- flox_mcp-0.6.0/PKG-INFO +182 -0
- flox_mcp-0.6.0/README.md +158 -0
- flox_mcp-0.6.0/flox_mcp/__init__.py +21 -0
- flox_mcp-0.6.0/flox_mcp/cli.py +187 -0
- flox_mcp-0.6.0/flox_mcp/data/binding_manifest.json +8010 -0
- flox_mcp-0.6.0/flox_mcp/data/c-api.snapshot +491 -0
- flox_mcp-0.6.0/flox_mcp/data/docs.fts.sqlite +0 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_001.md +29 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_002.md +41 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_003.md +34 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_ADF_004.md +35 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_BACKTEST_001.md +36 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_DATA_001.md +44 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_GRAPH_001.md +34 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_GRAPH_002.md +37 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_INPUT_001.md +31 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_IO_001.md +36 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_IO_002.md +34 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_KEY_001.md +46 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_LEN_001.md +49 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_LEN_002.md +31 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_LEN_003.md +36 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_RUN_001.md +35 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_RUN_002.md +40 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_RUN_003.md +40 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_SYM_001.md +72 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_TIME_001.md +36 -0
- flox_mcp-0.6.0/flox_mcp/data/errors/E_VAL_002.md +65 -0
- flox_mcp-0.6.0/flox_mcp/data/examples_index.json +61 -0
- flox_mcp-0.6.0/flox_mcp/data/gotchas.json +57 -0
- flox_mcp-0.6.0/flox_mcp/data/ir.snapshot.json +11671 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/codon/bar-driven.tmpl +38 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/codon/hybrid.tmpl +46 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/codon/trade-driven.tmpl +40 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/node/bar-driven.tmpl +32 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/node/hybrid.tmpl +38 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/node/trade-driven.tmpl +33 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/python/bar-driven.tmpl +30 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/python/hybrid.tmpl +37 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/python/trade-driven.tmpl +30 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/quickjs/bar-driven.tmpl +32 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/quickjs/hybrid.tmpl +39 -0
- flox_mcp-0.6.0/flox_mcp/data/templates/strategy/quickjs/trade-driven.tmpl +33 -0
- flox_mcp-0.6.0/flox_mcp/server.py +1228 -0
- flox_mcp-0.6.0/flox_mcp/tools/__init__.py +6 -0
- flox_mcp-0.6.0/flox_mcp/tools/_data.py +95 -0
- flox_mcp-0.6.0/flox_mcp/tools/_runtime_worker.py +275 -0
- flox_mcp-0.6.0/flox_mcp/tools/analytics.py +159 -0
- flox_mcp-0.6.0/flox_mcp/tools/capi.py +73 -0
- flox_mcp-0.6.0/flox_mcp/tools/control.py +134 -0
- flox_mcp-0.6.0/flox_mcp/tools/docs_search.py +119 -0
- flox_mcp-0.6.0/flox_mcp/tools/errors.py +53 -0
- flox_mcp-0.6.0/flox_mcp/tools/events.py +120 -0
- flox_mcp-0.6.0/flox_mcp/tools/examples.py +122 -0
- flox_mcp-0.6.0/flox_mcp/tools/indicators.py +138 -0
- flox_mcp-0.6.0/flox_mcp/tools/init_project.py +98 -0
- flox_mcp-0.6.0/flox_mcp/tools/lookahead.py +20 -0
- flox_mcp-0.6.0/flox_mcp/tools/lookup.py +356 -0
- flox_mcp-0.6.0/flox_mcp/tools/positions.py +216 -0
- flox_mcp-0.6.0/flox_mcp/tools/record_data.py +173 -0
- flox_mcp-0.6.0/flox_mcp/tools/runtime.py +489 -0
- flox_mcp-0.6.0/flox_mcp/tools/scaffold.py +92 -0
- flox_mcp-0.6.0/flox_mcp/tools/strategy.py +105 -0
- flox_mcp-0.6.0/flox_mcp.egg-info/PKG-INFO +182 -0
- flox_mcp-0.6.0/flox_mcp.egg-info/SOURCES.txt +78 -0
- flox_mcp-0.6.0/flox_mcp.egg-info/dependency_links.txt +1 -0
- flox_mcp-0.6.0/flox_mcp.egg-info/entry_points.txt +2 -0
- flox_mcp-0.6.0/flox_mcp.egg-info/requires.txt +5 -0
- flox_mcp-0.6.0/flox_mcp.egg-info/top_level.txt +1 -0
- flox_mcp-0.6.0/pyproject.toml +50 -0
- flox_mcp-0.6.0/setup.cfg +4 -0
- flox_mcp-0.6.0/tests/test_cli_init.py +173 -0
- flox_mcp-0.6.0/tests/test_data_artifacts.py +290 -0
- flox_mcp-0.6.0/tests/test_init_project.py +58 -0
- flox_mcp-0.6.0/tests/test_no_engine_messages.py +35 -0
- flox_mcp-0.6.0/tests/test_polyglot_tools.py +342 -0
- flox_mcp-0.6.0/tests/test_positions.py +271 -0
- flox_mcp-0.6.0/tests/test_record_data.py +93 -0
- flox_mcp-0.6.0/tests/test_runtime_tools.py +316 -0
- flox_mcp-0.6.0/tests/test_tools.py +133 -0
flox_mcp-0.6.0/PKG-INFO
ADDED
|
@@ -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.
|
flox_mcp-0.6.0/README.md
ADDED
|
@@ -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())
|