Merge pull request #73 from 0xrsydn/feat/stockbit

feat: login-free Stockbit intraday and quote provider
This commit is contained in:
dogfiles 2026-11-05 18:03:58 +07:00 • committed by GitHub
commit be9d17bb80
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
157 changed files with 14699 additions and 481 deletions

View file

@ -31,7 +31,7 @@ export default defineConfig({
},
{
label: 'CLI',
items: ['cli/stocks', 'cli/ownership', 'cli/output', 'cli/reference'],
items: ['cli/stocks', 'cli/stockbit', 'cli/ownership', 'cli/output', 'cli/reference'],
},
{
label: 'MCP server',
@ -56,7 +56,7 @@ export default defineConfig({
projectName: 'idx-cli',
details: [
'- `idx` is a CLI; `idx-mcp` is an MCP server over the same library. Both read the same config, cache and ownership database.',
'- Output is JSON whenever stdout is not a terminal. Errors are one JSON object on stderr; exit codes: 2 invalid input, 3 not found, 4 upstream unavailable, 5 unsupported, 1 other.',
'- Output is JSON whenever stdout is not a terminal. Errors are one JSON object on stderr; exit codes: 2 invalid input, 3 not found, 4 upstream unavailable or blocked, 5 unsupported, 1 other.',
'- Every page is also available as Markdown by appending `.md` to its path, for example `/agents/install.md`.',
].join('\n'),
}),

View file

@ -19,7 +19,9 @@ description: Every idx-mcp tool and its inputs, generated from the server's tool
idx-mcp serves these tools. `ownership_sync` is listed only when the server
starts with `--allow-sync`. Results that come from a live provider are wrapped
as `{data, source, fetched_at, stale}`; failures return `{error, code,
message}` with `isError` set.
message}` with `isError` set. Either may carry `warnings`, what idx worked
around (such as a history fallback or a stale cache entry), when there was
any.
EOF

View file

@ -19,10 +19,14 @@ What an agent needs to know:
- On failure stdout is empty and stderr holds one JSON object,
`{"error": true, "code": "...", "message": "..."}`.
- Exit codes say what to do next: `2` fix the input, `3` not found (or no
cached data offline), `4` provider unavailable, retry later, `5`
cached data offline), `4` provider unavailable or blocked, retry later, `5`
unsupported, `1` other.
- Tickers are IDX codes (`BBCA`); `.JK` is optional. Several tickers can be
passed at once to `stocks quote` and `stocks compare`.
- `stocks intraday` and `--provider stockbit` read Stockbit's public page,
paced to one request a second across idx processes. Ask for the symbols
you need rather than looping over the market; on `UPSTREAMBLOCKED`, stop
instead of retrying.
- Ownership queries need `idx ownership sync` once; after that they run
offline. `idx -o json ownership releases` shows which months exist.

View file

@ -32,7 +32,7 @@ stdout is not a terminal, and with `--no-color`.
| `0` | success | |
| `2` | invalid input or config | fix the command |
| `3` | not found, no data, or no cached data offline | check the ticker or name; go online |
| `4` | upstream unavailable | retry later |
| `4` | upstream unavailable or blocked | retry later; for `UPSTREAMBLOCKED`, wait or change network |
| `5` | unsupported | use another command or provider |
| `1` | anything else | read the message |

View file

@ -36,6 +36,34 @@ two releases. `ticker` also reports concentration: the top holder's share,
the top three, the Herfindahl index (HHI) and the free float (shares held by
holders below 1%).
## Insider and major holder movements
With your own Stockbit login (`idx stockbit login`, see [Stockbit](/cli/stockbit/)),
`movements` lists reported changes in holders' stakes, newest first. It reads
Stockbit live and needs no import:
```bash
idx ownership movements BBCA # one stock, 50 rows
idx ownership movements # the whole market
idx ownership movements --action sell --source ksei --pages 3
idx ownership movements --from 2026-10-01 --to 2026-10-31
idx ownership movements --holder 1000004481 # one holder's history
```
Two sources share the list. `IDX` rows are insider disclosures: directors,
commissioners and other insiders, at any size, with the trade price. `KSEI`
rows are changes of holders at 5% and above, which report no price; Stockbit
estimates one, shown with `~`. `--holder` takes the number in the HOLDER ID
column. Shares are exact. Stakes are as Stockbit rounds them, so a director's
small holding reads `<0.01%`.
These are filings, not signals. Many insiders buying at one price on one day
is usually a share plan, transfers are not trades, and a row appears days
after the change. Each page is 50 rows and one request to Stockbit (`--pages`
up to 10); a query's pages are read and cached together, for an hour. JSON's
`more` says rows remain past them. If a later page fails, you get the rows
read before it, with `incomplete` saying why, and nothing is cached.
## Import reports yourself
The snapshot covers most needs. To load a month it lacks, or older history,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,239 @@
---
title: Stockbit
description: Today's session by the minute and quotes with best bid/offer from Stockbit's public page, plus the order book and live quotes with your own Stockbit login. Both opt-in.
---
[Stockbit](https://stockbit.com)'s public symbol page carries a quote, the
best bid and offer, and today's price for every minute of the session. idx
reads that page directly. It needs no Stockbit account, no login and no
helper tools. With your own Stockbit account, idx can also read the order
book and live quotes; see [With your Stockbit login](#with-your-stockbit-login).
```bash
idx stocks intraday BBCA # today's session, by the minute
idx stocks intraday ^JKSE # an index (or IHSG, LQ45)
idx --provider stockbit stocks quote BBCA # quote with bid/offer
idx -o json stocks intraday BBCA | jq '.points | length'
```
## What you get
| | `stocks intraday` | `--provider stockbit stocks quote` |
| --- | --- | --- |
| Price, change, previous close, volume | yes | yes |
| Best bid and offer | while the market is open | while the market is open |
| Market status (`market_status`) | yes | yes |
| Price per minute (`points`) | yes | no |
| Market cap, 52-week range | no | no |
- **Delayed.** Stockbit's terms describe its market data as "a real-time
snapshot, delayed at least 10 minutes". It is not a live feed.
- **Bid and offer only while trading.** Outside the session, Stockbit still
shows the book as it closed. idx leaves `bid` and `offer` out then, so the
closing book never passes for a live one.
- **Top of the book only.** One bid and one offer price, no depth. The full
order book needs your [Stockbit login](#with-your-stockbit-login).
- **Prices only.** The page has no per-minute volume or OHLC.
- **Empty sessions are data.** A suspended or delisted stock answers with
`points: []` and a `session_note` saying so (`Stockbit lists the stock as
suspended`). An active stock with no volume says `no trades this session`,
even when its chart is a flat line at the previous close. Neither is an
error.
`--provider stockbit` (or `IDX_PROVIDER=stockbit`, or
`idx config set general.provider stockbit`) changes only where quotes come
from. Without a login, history still comes from Yahoo. Company data,
fundamentals and screening answer `UNSUPPORTED` under it, so use MSN for
those.
## How idx talks to Stockbit
- One request per symbol to `https://stockbit.com/symbol/<CODE>`, at least one
second apart across all your idx processes (a shell loop of `idx` calls
included), cached for 5 seconds.
- An honest user agent, `idx-cli/<version> (+https://github.com/0xrsydn/idx-cli)`,
not a browser disguise. Stockbit's CDN refuses some generic agents
(`curl/…`) outright but lets this one through. No curl-impersonate
needed.
- No retries. If Stockbit's CDN refuses your machine, the command fails at
once with `UPSTREAMBLOCKED` (exit `4`):
```json
{"error":true,"code":"UPSTREAMBLOCKED","message":"upstream blocked: stockbit.com refused this client (HTTP 403 from its CDN); ..."}
```
Wait, or try from another network. `--provider msn` serves quotes without
Stockbit.
## With your Stockbit login
Opt-in, and only with your own account. idx then talks to Stockbit's own API
(`exodus.stockbit.com`) with your session's tokens.
```bash
idx stockbit login # paste the credentialStorage cookie
idx stockbit status # logged in? when do the tokens expire?
idx stocks orderbook BBCA # depth, queued orders, foreign flow
idx stocks orderbook BBRI --depth 5
idx stocks actions BBCA # dividends, splits, meetings, ...
idx stocks calendar --kind dividend # cum dates in the next 30 days
idx ownership movements BBCA # insider and major holder buys and sells
idx --provider stockbit stocks quote BBCA # live, not delayed
idx --provider stockbit stocks history BBCA --period 1y # Stockbit's daily candles
idx stockbit logout
```
**Logging in.** `idx stockbit login` opens Chrome (or Chromium, Edge, Brave,
Helium) on Stockbit's own sign-in page, in a browser profile only idx uses. Sign in
there as usual, including any one-time code: idx reads the
session cookie over the DevTools protocol, checks it with one request, and
closes the window. Your password never reaches idx. The profile sits beside
`config.toml`; idx takes the session cookie out of it once the session is
stored, so the profile keeps only Stockbit's device cookies. The profile
stays with the browser that made it: idx opens the same one next time, unless
`IDX_STOCKBIT_BROWSER` names another. Choose your browser that way on the
first sign-in if you have several.
Use your Stockbit password in that window. Google can refuse to sign in there
with "This browser or app may not be secure", most likely because idx starts
the browser with remote debugging on. If you only sign in with Google, use the
local page below: sign in from a private window, paste the cookie, then close
that window.
Run `login` again while the session is still good and there is nothing to
do: idx checks it with one request and exits without opening a browser. The
window it does open waits 15 minutes, and closes at once if you close it
yourself.
**How long a session lasts.** Stockbit's refresh token is good for about a
week, and idx spends it on every refresh, which extends the session. Use idx
at least once a week and you stay signed in. A longer gap, a `logout`, or
another tool spending the same token means one more sign-in.
Over SSH, on a machine with no display, without such a browser, or with
`--no-browser`, idx serves a small page on your own machine (127.0.0.1, port
7878 when it is free) instead. With
stockbit.com open and logged in, click the page's **Send my Stockbit
session** bookmarklet: it reads Stockbit's `credentialStorage` cookie in your
browser and hands it to the page, which checks it with one request and says
"Connected". If bookmarklets are blocked, click **Paste from clipboard**
after copying the cookie, or paste it into the box. `--port` picks another
port. Piping the value in still works
(`pbpaste | idx stockbit login`), as does a refresh token on its own. On a
server, set `IDX_STOCKBIT_REFRESH_TOKEN`. Set `IDX_STOCKBIT_BROWSER` to use a
browser idx does not find on its own.
The session lands in `~/.config/idx/stockbit-session.json`, readable only by
you. On macOS without `XDG_CONFIG_HOME` that is
`~/Library/Application Support/idx/` instead; idx finds a session in either
one, so the CLI and an MCP client started from the Dock share it. Tokens never appear in output, logs or errors. The page takes only the
session value, keeps it in memory, and closes when it is done (or after five
minutes). A session Stockbit refuses leaves a previously stored one in place.
## Remote and headless
The page listens on this machine's loopback interface only, and the browser
handoff needs a browser on this machine too. On a server, in a container, or
in a WSL distro, `127.0.0.1` is a different machine and neither will load.
Three ways around that, in order of how little they ask of you.
**Pipe the cookie over SSH.** No tunnel, one command. Copy the
`credentialStorage` value on the machine with the browser (DevTools,
Application, Cookies), then:
```bash
pbpaste | ssh <host> "idx stockbit login"
```
idx reads stdin, verifies the session, and stores it on the server. The value
never reaches a shell history or a file.
**Forward the page.** Run the page on the server and forward its port from the
machine with the browser:
```bash
idx stockbit login # on the server: over SSH, it serves the page
ssh -L 7878:127.0.0.1:7878 <host> # where the browser is, keep it open
```
Then open the printed URL there and click the bookmarklet. Over SSH, idx
notices `SSH_CONNECTION`, skips the browser, and prints the exact forward
itself.
**Seed a token.** For anything unattended, keep the refresh token in a secret
store and pass it at run time:
```bash
op run --env-file=stockbit.env -- idx stocks orderbook BBCA
```
where the file holds `IDX_STOCKBIT_REFRESH_TOKEN=op://Private/stockbit/refresh`.
Only the refresh token is needed; idx mints its own access token.
**Agents.** Over MCP, an agent calls `stockbit_login`, which answers at once
and does the same as `idx stockbit login`. Where idx-mcp runs on your desktop,
a browser window opens on Stockbit's sign-in page (`"method": "browser"`) and
you sign in there. Over SSH or without a display, the answer carries the page
URL and its `ssh -L` forward (`"method": "page"`) for the agent to hand you.
The agent then polls `stockbit_status` until `sign_in.state` is `signed_in`
or `failed`. While it waits, `sign_in.warning` carries the latest problem, such
as Stockbit refusing the session you signed in with, so the agent can tell you
to sign in again. A session that already works answers `signed_in` without opening
anything, a second call while one is waiting returns it again, and
`{"method": "page"}` or `{"method": "browser"}` picks the way. If the server
stops while the window waits, the window closes with it.
One rule for all three: only one machine may own a session. Stockbit retires
the refresh token on every refresh, so a token copied to two machines signs the
other one out. Two machines need two sign-ins.
**One refresher per session.** Stockbit replaces the refresh token every time
it is used. Once idx refreshes your session, a browser holding the same
session has to log in again, and two tools sharing one session log each
other out. `idx stockbit login` keeps its own browser profile, so this only
affects a browser you use yourself. idx processes on one machine take turns
through a lock, so they never spend the token twice.
What the login adds:
| | Without a login | With your login |
| --- | --- | --- |
| `--provider stockbit` quotes | public page, delayed | Stockbit's API, live |
| Bid and offer in quotes | best price, while open | best price, while open |
| `market_status` in quotes | `open` or `close` | `open` or `close` |
| `stocks orderbook` | `AUTHERROR` | every level with lots and queued orders, OHLC, value, trades; foreign buy/sell/net once Stockbit marks them current |
| `stocks history` under `--provider stockbit` | Yahoo | Stockbit's daily candles for `5d` to `1y`; Yahoo for other windows |
| `stocks intraday` | public page | public page |
| `stocks actions`, `stocks calendar` | `AUTHERROR` | corporate actions: one stock's history, or the market's calendar |
| `ownership movements` | `AUTHERROR` | insider and major holder buys and sells, as reported to IDX and KSEI |
The order book is what Stockbit's own app shows. Outside trading it is the
book as the market left it, and the output says so. Foreign buy, sell and net
appear only when Stockbit marks them current: during trading its figures are
the previous session's, so idx leaves them out (`stocks flow` has foreign
flow session by session). Sizes are lots in the
table and shares in JSON. `--history-provider stockbit` asks for Stockbit's
candles explicitly; windows it charts only weekly or hourly (`2y`, `5y`,
`1wk`) answer `UNSUPPORTED`. If Stockbit refuses a stored session, `auto`
history falls back to Yahoo with a warning; an explicit
`--history-provider stockbit` fails with `AUTHERROR`.
`market_status` in quotes is `open` while the regular market trades and
`close` otherwise, including the lunch break and auctions, with or without a
login. `stocks orderbook` keeps Stockbit's state verbatim
(`STATE_NAME_SESSION_2`, `STATE_NAME_BREAK`, ...).
## Terms
Stockbit's [terms](https://stockbit.com/terms) allow its content for personal,
non-commercial use. They prohibit data mining, robots and redistribution
without Stockbit's written consent, which includes serving the data to other
people through `idx-mcp`. idx is not affiliated with or endorsed by
Stockbit. Use this for looking at a stock now and then, not for crawling the
market or republishing prices. With your login, automated use is tied to your
account: keep it human-paced.
Over MCP, the same data comes from `get_intraday`, `get_orderbook`,
`get_corporate_actions`, `get_corporate_calendar`, `get_shareholder_movements` and `stockbit_status`, and from `get_quotes` and `get_price_history` when the
server runs with `IDX_PROVIDER=stockbit`. The server never logs in; run
`idx stockbit login` in a terminal.

View file

@ -19,6 +19,61 @@ session, the table says so under it, and JSON sets `behind_session`: the
provider may still show the previous close. `history` takes `--period` (`1d`
to `5y`) and `--interval` (`1d`, `1wk`, `1mo`).
## Today by the minute
```bash
idx stocks intraday BBCA
idx stocks intraday ^JKSE
idx -o json stocks intraday BBCA | jq '.points[-1]'
```
`intraday` shows today's session from Stockbit's public page, without a
login: the quote, the best bid and offer while the market is open, a
sparkline and the last five minutes. JSON gives every minute in `points`. It
is delayed at least 10 minutes. A chart from before the latest session is
noted under the header and sets `quote.behind_session`, as with quotes. See [Stockbit](/cli/stockbit/) for what it
covers and the terms it comes with.
## Order book (Stockbit login)
```bash
idx stocks orderbook BBCA
idx stocks orderbook BBRI --depth 5
```
With your own Stockbit session (`idx stockbit login`), `orderbook` shows
every bid and offer level with lots and queued orders, the session's OHLC,
value and trades, and foreign buy, sell and net once Stockbit marks them
current (not during trading, when they lag a day). Without one it answers
`AUTHERROR`. See [Stockbit](/cli/stockbit/#with-your-stockbit-login).
## Corporate actions (Stockbit login)
```bash
idx stocks actions BBCA # newest first
idx stocks actions TLKM --kind dividend
idx stocks calendar # today and the next 30 days
idx stocks calendar --kind dividend,rights --to 2026-12-31
```
`actions` lists one stock's cash and stock dividends, bonus shares, rights
issues, warrants, splits and reverse splits, shareholders' meetings, public
exposes and tender offers (`--kind ipo` adds its IPO). `calendar` lists them
across the market between `--from` and `--to` (at most 366 days), including
what is announced ahead, plus rights trading, tender offers and IPO offers
still open in that range, so an offer that began earlier still shows while
you can act on it. An action's date is its cum date for dividends,
bonuses, rights and splits: buy by then to be entitled. For the others it is
the event's first day. When Stockbit gives no cum date, the action sits on
its ex date, marked `(ex)` in the table, and JSON leaves `cum_date` out.
Dividends per share keep their decimals (`Rp 3.8`); one not paid in Rupiah
carries `currency` (e.g. `USD`). JSON's `kind` is the name `--kind` takes.
Both need your Stockbit session and answer `AUTHERROR` without one. Stockbit
serves one kind per request, 1 s apart, so a first call with every kind takes
about ten seconds; answers are then cached for 6 hours. `--kind` asks for
less.
## Company data
| Command | What it shows |
@ -142,6 +197,10 @@ idx stocks screen --filter high-dividend | jq -r '.[].symbol' | idx stocks compa
MSN is the default for quotes, company data and screening; Yahoo serves
price history. Indo Premier (IPOT) serves broker summaries and foreign flow
(IDX's own site with `flow --source idx`), whichever provider is selected. Switch with `IDX_PROVIDER=msn|yahoo` and
`IDX_HISTORY_PROVIDER=auto|yahoo|msn`, or the matching
[config keys](/start/config/).
(IDX's own site with `flow --source idx`), and Stockbit serves `intraday`
and, with your login, `orderbook`, whichever provider is selected. Switch
with `--provider msn|yahoo|stockbit` (or `IDX_PROVIDER`) and
`IDX_HISTORY_PROVIDER=auto|yahoo|msn|stockbit`, or the matching
[config keys](/start/config/). `--provider stockbit` serves quotes only, with
the best bid/offer, live when you are logged in; see
[Stockbit](/cli/stockbit/).

View file

@ -5,9 +5,10 @@ description: idx-mcp serves idx's stock and ownership data to AI agents over MCP
`idx-mcp` is a [Model Context Protocol](https://modelcontextprotocol.io)
server over stdio. It gives an agent the same data as the `idx` CLI, as
[14 tools](/mcp/tools/) (plus an opt-in `ownership_sync`): quotes, price history, screening, company data,
analysis, comparison, broker summaries, bandarmology, foreign flow, and the KSEI
ownership queries.
[21 tools](/mcp/tools/) (plus an opt-in `ownership_sync`): quotes, today's
session by the minute, the order book, corporate actions and insider movements (with a Stockbit
login), price history, screening, company data, analysis, comparison, broker summaries,
bandarmology, foreign flow, and the KSEI ownership queries.
It shares idx's [config, cache and ownership database](/start/config/#files-idx-keeps),
so `idx ownership sync` in a terminal also feeds the server.
@ -27,11 +28,26 @@ Tools that fetch live data return:
}
```
`source` is the provider (`msn` or `yahoo`). Results are cached on disk;
`source` is the provider (`msn`, `yahoo`, `ipot`, `idx` or `stockbit`). Results are cached on disk;
`stale` is true when an expired cache entry was served because a fresh fetch
failed. Ownership tools return their data directly, from the local
database.
What `idx` prints as a warning in a terminal comes back in `warnings`, on an
answer or an error, when there was any. For example, history that fell back to
Yahoo because Stockbit refused your session says so, and a stale answer names
the failure behind it:
```json
{
"data": [{ "date": "2026-11-03", "close": 9875, "...": "..." }],
"source": "yahoo",
"fetched_at": "2026-11-04T03:20:11Z",
"stale": false,
"warnings": ["Stockbit rejected the session; run `idx stockbit login` again; history falls back to yahoo meanwhile"]
}
```
Errors come back as a tool result with `isError` set and the CLI's error
body, so the agent can decide whether to retry:
@ -44,6 +60,8 @@ body, so the agent can decide whether to retry:
| Input | Limit |
| --- | --- |
| tickers per call (`get_quotes`, `compare_stocks`) | 20, duplicates removed |
| `get_intraday` `tail` | 400 minutes (default: the whole session) |
| `get_orderbook` `depth` | 50 levels per side (default 10) |
| `screen_stocks` `limit` | 200 |
| `search_investors` `limit` | 100 |
| `get_ownership_changes` `limit` | 300 per page (default 200); page with `offset` |

View file

@ -8,7 +8,9 @@ description: Every idx-mcp tool and its inputs, generated from the server's tool
idx-mcp serves these tools. `ownership_sync` is listed only when the server
starts with `--allow-sync`. Results that come from a live provider are wrapped
as `{data, source, fetched_at, stale}`; failures return `{error, code,
message}` with `isError` set.
message}` with `isError` set. Either may carry `warnings`, what idx worked
around (such as a history fallback or a stale cache entry), when there was
any.
## `analyze_bandar`
@ -63,6 +65,25 @@ Company data for one IDX ticker: profile, financial statements, earnings, analys
| `sections` | array of `profile` / `financials` / `earnings` / `sentiment` / `insights` / `news` | no | Sections to fetch (default: all). |
| `symbol` | string | yes | IDX ticker, e.g. "BBCA". |
## `get_corporate_actions`
Corporate actions of one IDX stock from the user's own Stockbit session, newest first: `kind` (dividend, stock-dividend, bonus, rights, warrant, split, reverse-split, meeting, expose, ipo, tender), `date` (the cum date for dividends, bonuses, rights and splits, i.e. the last day to buy and be entitled, or the ex date when `cum_date` is absent; else the event's first day), `ex_date`, `record_date`, `pay_date`, and per kind `cash_per_share` (IDR, may have decimals; in `currency`, an ISO code, when that is present), `ratio` {old, new} (1:5 split = 1 share becomes 5; 655:104 rights = 104 rights per 655 held), `factor` (price adjustment), `price` (rights, warrant exercise, tender or IPO price, IDR), `period` and `exercise` {start, end}, `shares`, `percentage_bps`, `time`, `venue`. Absent fields are omitted. One Stockbit request per kind (about 1 s each on a cold cache, then cached 6 hours). AUTHERROR means there is no session: call stockbit_status, then stockbit_login.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `kinds` | array of `dividend` / `stock-dividend` / `bonus` / `rights` / `warrant` / `split` / `reverse-split` / `meeting` / `expose` / `ipo` / `tender` | no | Kinds to list (default: every kind but "ipo"). Each kind is one request to Stockbit. |
| `symbol` | string | yes | IDX ticker, e.g. "BBCA". |
## `get_corporate_calendar`
Corporate actions across IDX from the user's own Stockbit session, oldest first, dated from `from` to `to` (default today and the next 30 days), plus rights trading, tender offers and IPO offers still open in that range (their `period` overlaps it, though `date` may be earlier): upcoming cum dates, meetings, exposes, rights, warrants, IPOs and tender offers, and the past year. Each item has the same fields as get_corporate_actions, with `symbol`. One Stockbit request per kind (about 1 s each on a cold cache, then cached 6 hours); pass `kinds` to ask for less. AUTHERROR means there is no session: call stockbit_status, then stockbit_login.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string | no | First day, YYYY-MM-DD (default today). |
| `kinds` | array of `dividend` / `stock-dividend` / `bonus` / `rights` / `warrant` / `split` / `reverse-split` / `meeting` / `expose` / `ipo` / `tender` | no | Kinds to list (default: every kind). Each kind is one request to Stockbit. |
| `to` | string | no | Last day, YYYY-MM-DD (default 30 days after `from`; at most 366 days). |
## `get_foreign_flow`
Foreign investors' buying and selling of one IDX ticker, session by session (oldest first), with range totals; end of day. With `source` "ipot" (default): `foreign_buy_value`, `foreign_sell_value` and `foreign_net_value` in IDR across all boards, `value` traded, and `foreign_share_bps` (foreign share of trading); share counts, close and `non_regular_bps` are null, and `rounded` = true means one decimal of precision. With "idx": exact foreign share counts on the regular board, values estimated at the session's average price, the close, `change_bps` and `non_regular_bps`.
@ -75,6 +96,15 @@ Foreign investors' buying and selling of one IDX ticker, session by session (old
| `symbol` | string | yes | IDX ticker, e.g. "BBCA". |
| `to` | string | no | Last session of the range, YYYY-MM-DD (default today). |
## `get_intraday`
Today's session of one IDX stock or index from Stockbit's public page (no login), whichever provider the server uses: `quote` (price, change, volume, `market_status`, `bid`/`offer` only while the market is open, `session_note`) and `points`, the price per minute in WIB, oldest first. Delayed at least 10 minutes; no per-minute volume. Empty `points` means the stock did not trade this session (see `session_note`), not an error. `quote.behind_session` is true when the chart is from before the latest IDX session (a holiday, or Stockbit lagging), as in get_quotes. UPSTREAMBLOCKED means stockbit.com refused this machine (HTTP 403): do not retry at once.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `symbol` | string | yes | IDX ticker, e.g. "BBCA"; indices as "^JKSE" or "IHSG". |
| `tail` | integer | no | Only the latest N minutes (default: the whole session, at most 400). |
## `get_investor_holdings`
Every IDX holding (above 1%) of one investor, matched by name.
@ -83,6 +113,15 @@ Every IDX holding (above 1%) of one investor, matched by name.
| --- | --- | --- | --- |
| `entity` | string | yes | Investor name or part of it, e.g. "DWIMURIA"; the closest match is used. |
## `get_orderbook`
Order book of one IDX stock from the user's own Stockbit session: `bids` and `offers` (best price first) of {price, shares, orders}, the session's OHLC, volume (shares), value and frequency, and foreign buy/sell/net in IDR (null while Stockbit marks them not current, as during trading; get_foreign_flow has them per session). `market_status` is Stockbit's state, verbatim: STATE_NAME_SESSION_1 or _2 is trading; in any other state the book is as the market left it. AUTHERROR means there is no session: call stockbit_status, then stockbit_login for a URL the user can open.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `depth` | integer | no | Price levels per side (default 10, at most 50). |
| `symbol` | string | yes | IDX ticker, e.g. "BBCA". |
## `get_ownership_changes`
Holder changes (new, exited, increased, decreased) between two KSEI release dates, optionally for one ticker. Rows come largest change first, one page at a time: `summary` covers all of them (counts by type, tickers with the most changes), and `next_offset` is the `offset` for the next page, or null after the last.
@ -107,12 +146,26 @@ OHLCV price history for one IDX ticker.
## `get_quotes`
Latest quotes (price, change, volume, 52-week range, market cap) for one or more IDX tickers. `as_of` is the last trade in WIB; `behind_session` is true when that is before the latest IDX session, so the price is not that session's close: the provider lags, the stock did not trade, or the day was an exchange holiday.
Latest quotes (price, change, volume, 52-week range, market cap) for one or more IDX tickers. `as_of` is the last trade in WIB; `behind_session` is true when that is before the latest IDX session, so the price is not that session's close: the provider lags, the stock did not trade, or the day was an exchange holiday. When the server runs with the stockbit provider, quotes also carry `market_status` ("open" while the regular market trades, else "close", with or without a login), `bid`/`offer` (best price, only while the market is open) and `session_note` (suspended, delisted, no trades); Stockbit data is delayed at least 10 minutes.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `symbols` | array of string | yes | IDX tickers, e.g. ["BBCA", "BBRI"]; the `.JK` suffix is optional. At most 20. |
## `get_shareholder_movements`
Reported changes in insiders' and major holders' stakes in IDX stocks, from the user's own Stockbit session, newest first: IDX insider disclosures (directors, commissioners and other insiders, any size, with the trade price) and KSEI changes of holders at 5% and above (price estimated: `price_estimated`). One stock's, or the market's without `symbol`. Shares are exact and signed (`shares_change` < 0 is a sale); percentages are basis points as Stockbit rounds them, so a small insider can read 0. `more` is true when rows remain past `pages`; `incomplete`, when present, says a later page failed and the rows are those read before it (retry later for the rest). These are filings, not signals: many insiders buying at one price on one day is usually a share plan, and rows appear days after the change. AUTHERROR means there is no session: call stockbit_status, then stockbit_login.
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `action` | `buy` / `sell` / `transfer` / `cross` / `corporate-action` | no | Only this kind of movement; "corporate-action" covers rights issues, private placements and the like. |
| `from` | string | no | First day, YYYY-MM-DD. |
| `holder_id` | string | no | One holder's movements: the `holder_id` of a row. |
| `pages` | integer | no | Pages of 50 rows (default 1, at most 10); each page is one request to Stockbit. |
| `source` | `idx` / `ksei` | no | "idx" (insider disclosures, any size) or "ksei" (holders at 5% and above). |
| `symbol` | string | no | IDX ticker, e.g. "BBCA"; omit for the whole market. |
| `to` | string | no | Last day, YYYY-MM-DD. |
## `get_shareholders`
Shareholders above 1% of an IDX ticker from the latest KSEI release, with concentration metrics (top1, top3, HHI, free float).
@ -153,3 +206,17 @@ Search investor names in the KSEI ownership database.
| --- | --- | --- | --- |
| `limit` | integer | no | Maximum results (default 20, at most 100). |
| `query` | string | yes | Full-text query over investor names. |
## `stockbit_login`
Start a Stockbit sign-in and return at once; this call does not wait for it. With method auto (the default), idx-mcp opens a browser window on Stockbit's own sign-in page when its machine has a display and is not reached over SSH (`method: "browser"` in the answer): the user signs in there and the window closes by itself. Otherwise it serves a page on 127.0.0.1 (`method: "page"`, with `url` and an `ssh -L` `forward` for a browser on another machine) that takes stockbit.com's session cookie from the user's own browser. Never ask the user for their password, and never type it. Then poll stockbit_status until `sign_in.state` is signed_in or failed. A session that already works answers state signed_in without opening anything; a second call while one is waiting returns it again (`already_open`).
| Input | Type | Required | Description |
| --- | --- | --- | --- |
| `method` | `auto` / `browser` / `page` | no | "auto" (default): a browser window when idx-mcp's machine has a display and no SSH, else the local page; "browser" or "page" picks one. |
## `stockbit_status`
Whether a Stockbit session is stored (or seeded by IDX_STOCKBIT_REFRESH_TOKEN) and when its tokens expire; never the tokens. Without one, get_orderbook fails and stockbit-provider quotes use Stockbit's delayed public page; the user adds one with stockbit_login, or `idx stockbit login` in a terminal. After stockbit_login, `sign_in.state` says whether that sign-in is waiting, signed_in or failed (with `sign_in.error`). While it waits, `sign_in.warning` is the latest problem it hit, such as Stockbit refusing the session the user signed in with: pass it on, since the user may need to sign in again.
No inputs.

View file

@ -28,8 +28,8 @@ in scripts. Ask for JSON with `-o json`, `IDX_OUTPUT=json`, or
```toml
[general]
provider = "msn" # msn | yahoo
history_provider = "auto" # auto | yahoo | msn
provider = "msn" # msn | yahoo | stockbit
history_provider = "auto" # auto | yahoo | msn | stockbit
exchange = "JK"
output = "auto" # auto | table | json | jsonl
color = true
@ -56,6 +56,8 @@ fundamental_ttl = 3600
| `IDX_CACHE_FUNDAMENTAL_TTL` | `cache.fundamental_ttl` |
| `IDX_OWNERSHIP_SNAPSHOT_MANIFEST` | `ownership.snapshot_manifest` |
| `IDX_CURL_IMPERSONATE_BIN` | path to a `curl_chrome*` binary |
| `IDX_STOCKBIT_REFRESH_TOKEN` | seeds a [Stockbit session](/cli/stockbit/#with-your-stockbit-login) when none is stored |
| `IDX_STOCKBIT_ACCESS_TOKEN` | seeds the session's access token |
## Files idx keeps
@ -64,6 +66,7 @@ fundamental_ttl = 3600
| Config | `~/.config/idx/config.toml` |
| Cache | `~/.cache/idx/` (`idx cache info`, `idx cache clear`) |
| Ownership database | `~/.local/share/idx/ownership.db` |
| Stockbit session (after `idx stockbit login`) | `~/.config/idx/stockbit-session.json`, owner-only |
The MCP server reads the same config, cache and database.

View file

@ -11,6 +11,7 @@ Tickers are IDX codes; the `.JK` suffix is optional.
idx stocks quote BBCA # one quote
idx stocks quote BBCA BBRI BMRI # several (spaces or commas)
idx stocks history BBCA --period 1y # daily OHLCV
idx stocks intraday BBCA # today by the minute (Stockbit)
idx stocks technical BBCA # trend, RSI, MACD, moving averages
idx stocks fundamental BBCA # growth, valuation and risk together
idx stocks compare BBCA BBRI BMRI # fundamentals side by side

View file

@ -127,7 +127,7 @@ TLKM.JK 2,250 <span class="up">+0.45%</span> 87,159,900
<div>
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><path d="M3 17l6-6 4 4 8-8"></path><path d="M15 7h6v6"></path></svg>
<h2>Stocks</h2>
<p>Quotes, OHLC history, financial statements and a screener. Technical, growth, valuation and risk analysis, and side-by-side compare.</p>
<p>Quotes, today's session by the minute, OHLC history, financial statements and a screener. Technical, growth, valuation and risk analysis, and side-by-side compare.</p>
<code>idx stocks fundamental BBRI</code>
</div>
<div>
@ -139,7 +139,7 @@ TLKM.JK 2,250 <span class="up">+0.45%</span> 87,159,900
<div>
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><rect x="4" y="7" width="16" height="12" rx="3"></rect><path d="M12 3v4M9 13h.01M15 13h.01"></path></svg>
<h2>Agents</h2>
<p>JSON whenever output is piped, errors as one JSON object, meaningful exit codes. Plus idx-mcp: 11 tools for any MCP client.</p>
<p>JSON whenever output is piped, errors as one JSON object, meaningful exit codes. Plus idx-mcp: 17 tools for any MCP client.</p>
<code>npx -y idx-mcp</code>
</div>
</div>
@ -219,7 +219,7 @@ TLKM.JK 2,250 <span class="up">+0.45%</span> 87,159,900
<dl class="codes">
<dt>2</dt><dd>invalid input or config</dd>
<dt>3</dt><dd>not found, or no cached data offline</dd>
<dt>4</dt><dd>upstream unavailable, retry later</dd>
<dt>4</dt><dd>upstream unavailable or blocked, retry later</dd>
<dt>5</dt><dd>unsupported</dd>
</dl>
</div>