# io.github.tickerbot/mcp-server

> The stock market, in SQL — scan, replay, or subscribe across ~12k US tickers and top 100 cryptos.

- name: io.github.tickerbot/mcp-server
- version: 0.1.3
- connection class: R2 (oauth_consent_once)
- trust: 90/100
- quality: 71/100 (needs work)
- owner: not claimed
- descriptor: https://api.protogrid.dev/v1/servers/io.github.tickerbot%2Fmcp-server
- tools: https://api.protogrid.dev/v1/servers/io.github.tickerbot%2Fmcp-server/tools

**An agent can connect after a one-time OAuth consent by a human.** Remote endpoint behind OAuth 2. Tokens stay with the agent; the registry only exposes the authorization metadata.

## Connection (mcpServers)

```json
{
  "mcpServers": {
    "tickerbot": {
      "type": "http",
      "url": "https://api.tickerbot.io/mcp",
      "headers": {
        "Authorization": "Bearer ${TICKERBOT_TOKEN}"
      }
    }
  }
}
```

Placeholders to fill: `${TICKERBOT_TOKEN}`.

## Trust

- hygiene: 100
- liveness: 100
- freshness: 65
- provenance: 90
- drivers: +repository +namespace-matches-repo +website +reachable +uptime-100% ~updated-131d-ago
- Derived from observable signals (official registry feed and our own credential-free probes); no code audit performed.

## Quality 71/100 (needs work)

### protocol: n/a (weight 25)

- n/a `protocol.modern`, `protocol.stateless`, `protocol.transport`, `protocol.list_ttl`: no remote answered a protocol handshake (not probed yet, unreachable, or local-only)

### authorization: 83 (weight 15)

- pass `auth.prm`: Protected Resource Metadata (RFC 9728) published
- pass `auth.as_metadata`: authorization server metadata reachable
- warn `auth.cimd`: only Dynamic Client Registration, deprecated in 2026-07-28 in favour of CIMD
- n/a `auth.secret_in_url`: no templated URL

### tool hygiene: 64 (weight 30)

- pass `tools.descriptions`: every tool has a description
- pass `tools.description_length`: descriptions are concise
- pass `tools.schemas`: every tool has an object input schema
- fail `tools.annotations`: no tool declares readOnlyHint or destructiveHint, so clients cannot tell safe calls from risky ones
- fail `tools.directory_hints`: no tool declares all four of readOnlyHint, destructiveHint, idempotentHint and openWorldHint; missing: tickerbot_list_tickers (readOnlyHint, destructiveHint, idempotentHint, openWorldHint); tickerbot_list_signals (readOnlyHint, destructiveHint, idempotentHint, openWorldHint); tickerbot_update_custom_signal (readOnlyHint, destructiveHint, idempotentHint, openWorldHint); …
- warn `tools.token_cost`: about 14,282 tokens to load every tool
- pass `tools.api_dump`: tools are not a one-to-one API dump

### stability: 75 (weight 15)

- warn `stability.changes`: 38 tool changes in 30 days
- pass `stability.rug_pull`: no tool changed its meaning under the same name

### dependencies: 67 (weight 15)

- pass `deps.known_vulns`: no known advisory in 98 resolved packages
- fail `deps.mcp_sdk_version`: @modelcontextprotocol/sdk 1.30.1 has 1 known advisory (GHSA-6qxp-vccf-f47h); update @modelcontextprotocol/sdk to 1.31.0 or later
- pass `deps.resolvable`: @tickerbot/mcp-server@0.1.3 resolved: 98 packages

- tools: 32, about 14,282 tokens to load them all
- tool set hash: 8a21fded28e9d816db5ca5fc3a6bc353
- tools last changed: 2026-10-02T18:11:33.102Z
- badge: [![protogrid quality](https://protogrid.dev/badge/io.github.tickerbot/mcp-server.svg)](https://protogrid.dev/servers/io.github.tickerbot/mcp-server)
- Checks run on what our credential-free, read-only probes observe; tools are never called and no code audit is performed.

### Tool changes

- 2026-10-02 `tickerbot_list_events` changed (inputSchema)
- 2026-10-02 `tickerbot_get_bars` changed (inputSchema)
- 2026-10-02 `tickerbot_get_series` changed (inputSchema)
- 2026-10-02 `tickerbot_get_ticker` changed (inputSchema)
- 2026-10-02 `tickerbot_list_events` changed (inputSchema)
- 2026-10-02 `tickerbot_get_bars` changed (inputSchema)
- 2026-10-02 `tickerbot_get_series` changed (inputSchema)
- 2026-10-02 `tickerbot_scan` changed (inputSchema)
- 2026-10-02 `tickerbot_get_signal` changed (inputSchema)
- 2026-10-02 `tickerbot_get_ticker` changed (inputSchema)

Full history: https://api.protogrid.dev/v1/servers/io.github.tickerbot%2Fmcp-server/changes

## Dependencies

- npm @tickerbot/mcp-server@0.1.3: resolved, 97 dependencies, MCP SDK @modelcontextprotocol/sdk 1.30.1
  - GHSA-6qxp-vccf-f47h (high) in `` @modelcontextprotocol/sdk@1.30.1 ``, direct, fixed in `` 1.31.0 ``, advisory summary: "MCP TypeScript SDK: OAuth client could send credentials to an authorization server chosen by the MCP server" https://osv.dev/vulnerability/GHSA-6qxp-vccf-f47h

Dependency graphs from deps.dev; advisories from OSV.dev, including the GitHub Advisory Database and the PyPI Advisory Database; all CC BY 4.0.

## Remotes

- https://api.tickerbot.io/mcp (streamable-http, auth oauth2, reachable true, uptime30d 1)

## Packages

- npm @tickerbot/mcp-server@0.1.3 secrets: TICKERBOT_API_KEY

## Tools (32)

- `tickerbot_create_custom_signal`: A named boolean predicate you can reference anywhere a built-in signal goes.
- `tickerbot_create_universe`: Create a named ticker list owned by your account.
- `tickerbot_create_webhook`: Canonical create: a webhook is a trigger plus a delivery. Trigger shapes: scan {type:"scan", q, universe?}; ticker {type:"ticker", ticker, condition}; signal {type:"signal", signal, ticker?, universe?, condition?}; event {type:"event", kinds, tickers?, universe?, event_q?}. The subscribe tools are flat sugar over exactly this. Webhooks need a paid plan (Free has no webhook slots).
- `tickerbot_delete_custom_signal`: Delete one of your custom signals. Refused by default if another of your signals references it.
- `tickerbot_delete_universe`: Permanently delete one of your universes. Webhooks that reference the deleted universe fail on their next eval — clean those up first.
- `tickerbot_delete_webhook`: Delete a webhook subscription, and its delivery history with it.
- `tickerbot_enable_webhook`: Re-enable a disabled webhook and start it clean. Clears match-state, so the next eval treats every currently-matching ticker as new.
- `tickerbot_get_bars`: OHLCV bars from 1-second through monthly. The prices underneath the table.
- `tickerbot_get_etf_holdings`: Returns an ETF's constituents and their weights, heaviest first. When the ticker is not an ETF, `is_etf` is false and `holdings` is empty; `is_etf: true` with zero holdings means a real ETF whose holdings are not ingested yet. The reverse lookup ("which ETFs hold NVDA") is a scan filter on `etf_holders`, not this tool.
- `tickerbot_get_etf_sectors`: Returns an ETF's sector weights, heaviest first. Always complete, since sector breakdowns are small. When the ticker is not an ETF, `is_etf` is false and `sectors` is empty.
- `tickerbot_get_series`: Any signals for any tickers on one shared time grid — up to 50 tickers by 25 columns per call. One flat row per ticker per interval step, cursor-paged backward. `transitions_only: true` with boolean signals returns only the rows where a boolean flipped.
- `tickerbot_get_signal`: The state of a signal is the set of tickers matching it right now, or with `asof`, as of any moment. One name, the whole market, one call. Booleans need no `condition`; every other type needs one, typed like the signal: ">70" (numeric), "<YYYY-MM-DDTHH:MM:SSZ" (timestamp), ">=YYYY-MM-DD" (date), "=ETF" (string). Sorted by signal value desc for non-booleans.
- `tickerbot_get_ticker`: The full ticker row, every signal on the schema page, for one symbol or a comma list of up to 50. Right now, or with `asof`, as of any past date. Pass a comma list of up to 50 symbols for a batch (`data` keyed by symbol plus `not_found`). Crypto is the X-prefixed pair (X:BTCUSD) — bare BTC/ETH are US-listed ETFs.
- `tickerbot_get_ticker_coverage`: For one ticker, what we hold and how far back — so an empty result is never ambiguous. Ask this before treating a gap in bars or series as an outage. An absent `minute` span is not a gap — sub-hour bars load on the first request for a symbol.
- `tickerbot_get_universe`: Returns the universe doc. Use `top_10`/`top_100` to fetch a system universe; any other slug must be one your account owns.
- `tickerbot_get_webhook`: The current state of one webhook subscription.
- `tickerbot_list_events`: One timeline across every ticker: earnings, dividends, splits, insider filings, analyst actions, plus opt-in signal firings and news. Requires at least one bound: a ticker scope (ticker/tickers/universe), a time window (from/to), or firm/action — `q` alone is not a bound. `firm`/`action` match case-insensitively; a `q` payload match is case-sensitive. `join: state` attaches the ticker state as of each event.
- `tickerbot_list_signals`: Every signal you can name in a query — the built-in signals and your own custom signals, in one catalog. Use to discover the signal names and `q` vocabulary before composing a scan; custom signals appear with `kind: custom`.
- `tickerbot_list_tickers`: Every symbol we track, active or delisted, as one identity row each. Use `/v2/tickers/{ticker}` for the full row.
- `tickerbot_list_universes`: Every universe you can reference: your own named ticker lists and the built-in ones. `owner: system` lists the built-in universes (top_10, top_100); `all` lists both. Use a slug as `universe` on scan, signal, and subscribe tools.
- `tickerbot_list_webhook_deliveries`: Recent deliveries for one webhook — what was sent, and what came back.
- `tickerbot_list_webhooks`: Every webhook subscription on this account, newest first.
- `tickerbot_scan`: Every ticker matching a SQL WHERE clause. Right now, or with `asof`, as of any past date. The `q` grammar is a flat SQL WHERE over signal names: AND/OR/NOT, comparisons, numeric and string literals, custom signals by name. No JOIN or subqueries. With `group_by` the result is rollup rows, not tickers. Example: `gap_up AND market_cap < 2000000000 AND NOT earnings_this_week`.
- `tickerbot_search_news`: SQL query over the news archive. Article rows, or rollups when you group them. Filter to a ticker with the `ticker` param, or in `q` via the auto-unnest alias `tk = 'NVDA'`. `search` is full-text over title and summary. Rollups with group_by/having return `truncated: true` instead of paging.
- `tickerbot_subscribe_events`: Push new events: we POST your endpoint when events of the kinds you chose land in the archives. Webhooks need a paid plan (Free has no webhook slots). `q` filters the ticker STATE; `event_q` filters the EVENT payload in the /v2/events grammar. Latency is the ingest cadence (analyst ≤1h, corporate kinds daily), not sub-minute.
- `tickerbot_subscribe_scan`: Push a whole query: we POST your endpoint every time the match set changes. Webhooks need a paid plan (Free has no webhook slots). Use for "alert me when this happens" requests.
- `tickerbot_subscribe_signal`: Push one signal: we POST your endpoint whenever any ticker starts matching it. Webhooks need a paid plan (Free has no webhook slots). Omit `ticker` to watch the whole universe.
- `tickerbot_subscribe_ticker`: Push one ticker: we POST your endpoint whenever it matches the condition you give. Webhooks need a paid plan (Free has no webhook slots). Omit `target_url` for in-app delivery.
- `tickerbot_test_webhook`: Send a real-shape test POST to your endpoint, instantly. One-shot: a failed test never retries and never auto-disables the webhook. 400 when the webhook has no target_url.
- `tickerbot_update_custom_signal`: Update a custom signal you own — its expression, description, or name.
- `tickerbot_update_universe`: Update one of your universes: its name, description, or members. `tickers` replaces the whole list; `add`/`remove` adjust it. System universes cannot be edited.
- `tickerbot_update_webhook`: Edit a webhook in place — send only the fields you want to change. The trigger and channel are immutable — delete and re-create to change what fires or where it delivers. Unknown fields are a 400.
