indicato-mcp
v0.1.1
Published
Model Context Protocol server exposing the indicato technical analysis library to AI assistants.
Maintainers
Readme
indicato-mcp
An MCP server that gives an AI assistant the 89 indicators in indicato.
The assistant asks for an RSI; this computes one. It does not guess at the arithmetic, and it does not hallucinate a number that looks about right.
Install
claude mcp add indicato -- npx -y indicato-mcpOr, for any client that reads a JSON config:
{
"mcpServers": {
"indicato": {
"command": "npx",
"args": ["-y", "indicato-mcp"]
}
}
}Node 18 or newer. Nothing else to configure.
Its only dependency is indicato. The MCP protocol is spoken directly — newline-delimited
JSON-RPC on stdin and stdout — rather than through the reference SDK, which declares two
web frameworks, CORS, OAuth PKCE and a JOSE implementation to carry transports this server
does not use. That was 88 packages behind a process that opens no socket and reads your
files.
Where the data comes from
Nowhere. This server has no network access and no market data of its own — the same rule the library follows. Bars arrive one of two ways:
Inline, for a few hundred bars:
{ "indicator": "rsi", "bars": [{"o":100,"h":102,"l":99,"c":101,"v":10000}, ...] }A plain array of numbers is accepted too, and treated as closes.
From a file on the machine running the server, for anything larger:
{ "indicator": "rsi", "file": "/data/RELIANCE.csv", "params": { "n": 21 } }CSV headers are matched case-insensitively against open/high/low/close/volume
and the usual variants — Adj Close, close_price, LTP, and so on. The columns
actually used come back in the response, so a wrong guess is visible rather than silent.
If the rows are newest-first and carry dates, they are reversed and you are told.
Tools
| tool | what it does |
|---|---|
| list_indicators | All 89, with parameters and defaults. Start here. |
| describe_indicator | Parameters and return shape for one. |
| compute | One indicator over your bars. |
| compute_many | Several indicators, one read of the data. |
compute returns the trailing values plus a summary — warmup index, first, last,
min, max, mean — rather than the whole series. Twenty years of daily bars through
bollinger is three arrays of five thousand floats, which no model needs to read to
answer a question about the last month. Pass full: true when you actually want
everything, and tail: 60 when you want more than the default ten.
{
"indicator": "macd",
"values": { "line": [4.21, 4.33, ...], "signal": [...], "hist": [...] },
"summary": { "line": { "warmup": 33, "last": 4.33, "min": -8.1, "max": 11.4, ... } },
"source": { "file": "/data/RELIANCE.csv", "rows": 2480, "columns": { "c": "Close" } },
"showing": "last 10 of 2480"
}Two indicators need a benchmark
beta and correlation relate one instrument to another. Pass a benchmark series of
the same length as the bars, or a benchmarkFile:
{ "indicator": "beta", "file": "/data/RELIANCE.csv", "benchmarkFile": "/data/NIFTY50.csv" }Lengths must match bar for bar. A mismatch is refused rather than quietly truncated — a beta computed against a misaligned index is worse than no beta.
Mistakes it will not let you make
A wrong parameter name is an error, not a shrug:
rsihas no parameterperiod. It takes: n = 14.
A wrong indicator name suggests the right one:
no indicator named
bollingerbands. Did you mean: bollinger, bbWidth, bbPercent?
Both are addressed to the model, which reads the message and corrects itself.
What it can reach
The file system, as you, and nothing else. It opens no network connection.
file is a path, and the thing choosing that path is a language model, which can be
talked into things by what it reads. So a file that fails to parse is not quoted back
into the error message — only fragments that look like column names survive, which
Adj Close does and a line of a private key does not. Files are capped at 64 MB, and
window lengths are bounded before anything is allocated.
Point it at a directory of exports and it is useful. It has no more reach than that.
Licence
MIT.
