Obsidian Flow MCP server by The Boy Prodigy - computed order-flow state from NinjaTrader 8 for MCP clients
All 7 tools lack input parameter descriptions entirely. While tool descriptions are present and detailed (100-500+ chars), they do not guide LLM selection or explain WHEN to use each tool vs. alternatives. Parameter schemas exist but lack type information and descriptions, critical for LLM reasoning. No output schemas documented. No error handling guidance. Tools are read-only (good for safety) but lack composition hints and chaining IDs. Naming is generic (health, instruments, latency_report) rather than action-verb style (get_health, list_instruments, fetch_latency_report).
Transport health for the NinjaTrader 8 AddOn link: pipe state, whether the current connection has sent its hello, ring-dropped event count (market events the AddOn's rings dropped, not dropped snapshots - snapshots conflate), and events drained by the AddOn publisher. Answers from an in-process cache, so this is the state as of the last frame received, not a live query. Build steps 1, 2 and 2.5 expose transport counters and the AddOn's own instrumentation (see latency_report); no market state is computed. The `instruments` array gives one resolution state per AddOn config entry: `resolved` (subscribed), `unresolved` with the AddOn's reason (the entry produced no subscription and has no index), `rolled` with `rolledAt` and `previousName` (the entry was a bare futures root and the AddOn moved it to a new front contract, either on this connection or earlier in the AddOn process), or `identity-absent` (a pre-2.5 AddOn). A bare root such as "ES" auto-resolves to the front contract and can roll mid-session; a roll re-announces the hello and appears as a contractRolled entry in recentEvents. Staleness is two separate numbers and they must not be silently added together. receiveToServeMs is measured exactly, on this process's own monotonic clock, from decoding the frame to answering this call. oneWayEstimateMs is an ESTIMATE of the publisher-to-client hop derived from the AddOn's fixed heartbeat cadence: it is a lower bound on transport jitter, not a measurement of one-way latency, it excludes any constant transport delay, and it is null until two heartbeats have arrived on the current connection. The two clocks share no epoch, so no exact end-to-end frame age exists and none is reported.
Instruments announced by the AddOn in the current connection's hello frame, with the identity record the AddOn resolved for each: `name` is the resolved NinjaTrader name (Instrument.FullName), `resolvedFrom` is exactly what the user typed in the AddOn config, and `identity` carries shape, resolvedBy, masterName, instrumentType (Future, Stock, Forex, CryptoCurrency, Index, ...), exchange, currency, tradingHours, expiry (YYYY-MM-DD, null when the instrument does not expire), tickSize, pointValue, rolledAt and rollCount. This record is the fingerprint used to label recorded history: two entries with the same resolvedFrom but different name are different contracts. Config entries take three shapes: a fully qualified name with a contract month is used as-is and never re-resolved; a bare futures root (e.g. "ES") auto-resolves to the front contract by NinjaTrader's own rollover data and CAN ROLL MID-SESSION (checked once a minute and at session boundaries; a roll re-announces this table with the same index and a new identity, and `resolution.state` becomes `rolled`); any non-futures symbol resolves directly. Config entries that did not resolve have no index here; the health tool lists them with the reason. Each entry carries freshness (live | stale | reconnecting) and a staleness block whose two numbers are described by the health tool: receiveToServeMs is measured, oneWayEstimateMs is an estimate. `reconnecting` means the link dropped and no hello has arrived yet: the instrument list from the previous connection is not valid and its indices are not reused. Answers from cache; never blocks.
All parameter descriptions missing. Tools accept parameters (e.g., 'instrument', 'scope') with no description text explaining what they control, valid values, or format constraints. LLMs cannot infer parameter semantics from names alone.
No output schemas documented. Tool descriptions explain what data is returned (bid/ask, volume profiles, etc.) but no structured schema defines field names, types, or nesting. LLMs cannot plan downstream calls or extract data reliably.
Tool names lack action verbs. 'health', 'instruments', 'latency_report' are nouns/descriptors, not verbs. Should be 'get_health', 'list_instruments', 'fetch_latency_report' to signal intent and aid LLM tool selection.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | F | 43 | 2026-07-28+ | v2 |
The NinjaTrader 8 AddOn's OWN in-process measurements, as carried in its last snapshot per instrument. These are not end-to-end figures. Measured: (1) MarketData and MarketDepth handler duration on the NinjaTrader data thread, from handler entry to just after the ring push (data/depth p50, p99, p99.9, max in nanoseconds, quantised to two significant digits, max exact, plus sample counts); (2) bytes allocated on each handler thread over the last 1024 events and since start, and on the publisher thread since its first frame, from GC.GetAllocatedBytesForCurrentThread - a THREAD-WIDE counter: it includes NinjaTrader's own allocations on that thread, and instruments whose handlers share a thread repeat the same number, so it bounds the handler's allocation rather than attributing it; (3) the publisher's frame-serialize time, start of payload serialization to hand-off to the pipe. Allocation figures carry status 'unavailable' with value -1 when the runtime does not expose the counter or the probe has not run yet; 0 always means measured zero. Latency fields are null while the histogram behind them is empty, never 0. Percentiles are recomputed by the AddOn at most once per second.
Order flow snapshot from the in-process cache: the current bid/ask, last trade, session volume profile, prior session volume profile, composite volume profile (all sessions), and high-volume and low-volume nodes. Answers from cache; never blocks. The snapshot is the state as of the last frame received from the AddOn, not a live query.
Current price state from the in-process cache: bid, ask, last trade price, and related market data. Answers from cache; never blocks.
Volume profile data from the in-process cache: session, prior session, and composite volume profiles with high-volume and low-volume nodes. Answers from cache; never blocks.
Session VWAP and related state from the in-process cache. Answers from cache; never blocks.
No error handling guidance. Descriptions do not explain failure modes (e.g., 'instrument not found', 'connection dropped') or recovery steps. LLMs cannot self-correct or retry intelligently.
No tool composition hints. Descriptions do not explain which tools to call first (e.g., 'call instruments() to discover available symbols before calling price_state()') or what IDs/references are needed for chaining.