Skip to content
Back to skills

Schwab Marketdata Ops En

ASecurity

Use when the user wants to call Schwab Market Data Production via the schwab-marketdata-mcp server, troubleshoot OAuth or 7-day refresh-token expiry, recover from 401/429/5xx errors, or look up the exact input/output schema for any of the 12 market-data tools (quotes×2, price_history, option_chain×2, market_hours×2, movers, search_instruments, get_instrument_by_cusip, health_check, get_server_info). Use this skill instead of schwab-marketdata-workflows-en when the request is about a single MC...

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 26, 2026
developmenttypescriptpythonrustgoshellnextjsnodegitapisecurity

Works with

  • claude code
  • cursor
  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add kevinkda/schwab-marketdata-skill --skill schwab-marketdata-ops-en --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Schwab Marketdata Ops En?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Schwab Marketdata Ops En
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kevinkda-schwab-marketdata-ops-en/badge)](https://www.skillsdirectory.com/skills/kevinkda-schwab-marketdata-ops-en)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: schwab-marketdata-ops-en
compatible_mcp_version: ">=0.3,<0.4"
language_directive: "Always respond to the user in English."
auto_activate: false
activation_handshake: "get_server_info -> health_check"
compatibility: >-
  Designed for Cursor IDE >=0.45, Claude Code >=1.0, Kiro CLI >=0.1,
  Cline >=3.x, Roo Code, and other skills-compatible agents.
dependencies:
  mcp_server: "schwab-marketdata-mcp"
  external_runtime: ["uv", "python>=3.10"]
  optional_runtime: ["gh CLI (for workflows skill)", "jq (for shell ad-hoc)"]
governance:
  data_classification: "non-redistributable (Schwab Market Data)"
  written_artifacts_must_stay_in: "private repositories"
  prohibited_force_push_targets: ["main", "mainline"]
  # "master" no longer included; GitHub default has been "main" since 2020. Legacy repos should be migrated.
description: |
  Use when the user wants to call Schwab Market Data Production via the
  schwab-marketdata-mcp server, troubleshoot OAuth or 7-day refresh-token
  expiry, recover from 401/429/5xx errors, or look up the exact input/output
  schema for any of the 12 market-data tools (quotes×2, price_history,
  option_chain×2, market_hours×2, movers, search_instruments,
  get_instrument_by_cusip, health_check, get_server_info).
  Use this skill instead of schwab-marketdata-workflows-en when the request
  is about a single MCP tool call or troubleshooting (not a multi-step
  playbook).
  Triggers on "Schwab quote", "Schwab price history", "Schwab option
  chain", "Schwab token expired", "reauthorize Schwab",
  "Schwab API error".
  For these scenarios use this skill; always respond to the user in English.
---

# schwab-marketdata-ops-en

> **Responsibility disclaimer**: This skill drives the Schwab Market Data
> Production API. Users are required to read
> <https://www.schwab.com/legal/terms> and
> <https://developer.schwab.com/legal>, and bear sole responsibility for
> compliance of their usage. The authors of `schwab-marketdata-mcp` and
> `schwab-marketdata-skill` are not liable for any usage that violates the
> Schwab Terms of Service.

## Activation handshake (run first)

When activating this skill, the **first step** must be a call to
`get_server_info` to confirm that the returned `server_version` falls
within the frontmatter `compatible_mcp_version` range:

```text
get_server_info()
→ { "server_version": "0.1.x", "supported_tools": [...12 names...] }
```

If the version does not match or the call fails: **stop immediately**,
tell the user to upgrade either `schwab-marketdata-mcp` or this skill,
and **do not** continue any business calls.

Full handshake (including token health check):

```text
1. get_server_info()  → verify server_version ∈ compatible_mcp_version
2. health_check()     → verify token_state == "valid"
                       and token_expires_in_days >= 0.5
3. If either step fails → stop immediately and follow the references
   to repair before continuing.
```

## Quick Start — 7 steps from zero to hello-world

For a first-time onboarding or a new machine, read the following 7 step
files in order:

| Step | File | Expected outcome |
| ---- | ---- | ---------------- |
| 1 | [`references/quick-start/step-1-developer-portal-app.md`](references/quick-start/step-1-developer-portal-app.md) | Schwab Developer Portal app created, App Key / Secret in hand |
| 2 | [`references/quick-start/step-2-credentials-env.md`](references/quick-start/step-2-credentials-env.md) | `.env` populated (chmod 600), pre-commit hooks installed |
| 3 | [`references/quick-start/step-3-first-oauth.md`](references/quick-start/step-3-first-oauth.md) | `token.json` written to disk (chmod 600) |
| 4 | [`references/quick-start/step-4-token-health-check.md`](references/quick-start/step-4-token-health-check.md) | `health` module exits 0, `token_state == "valid"` |
| 5 | [`references/quick-start/step-5-first-mcp-tool-call.md`](references/quick-start/step-5-first-mcp-tool-call.md) | Minimal Python MCP client successfully calls `get_quote("VOO")` |
| 6 | [`references/quick-start/step-6-cursor-integration.md`](references/quick-start/step-6-cursor-integration.md) | `~/.cursor/mcp.json` registered, agent can invoke all 12 tools |
| 7 | [`references/quick-start/step-7-cron-launchd-setup.md`](references/quick-start/step-7-cron-launchd-setup.md) | cron / launchd health probes enabled, desktop notification channel verified |

## Common usage scenarios

Following the helis "I want to..." routing pattern, dispatch directly
to the right tool and reference doc based on user intent.

### "I want a real-time quote"

| Scenario | What to use | Notes |
| -------- | ----------- | ----- |
| Single stock / ETF / index | `get_quote("AAPL")` / `get_quote("$SPX")` | symbol must be UPPERCASE; indexes need a `$` prefix |
| Batch (≤ 50) | `get_quotes(symbols=[…])` | beyond 50, batch on the client side, otherwise `SchwabValidationError(field="symbols")` |
| Option contract | `get_quote("AAPL  240119C00170000")` | **OSI 21 chars**: 6-char root (space-padded) + YYMMDD + C/P + 8-digit strike(×1000) |

→ Full schema in [`references/tools/tool-reference-quotes.md`](references/tools/tool-reference-quotes.md).

### "I want to research an option chain"

1. `get_option_expiration_chain(symbol)` — fetch the list of available expirations first
2. `get_option_chain(symbol, contract_type=…, strike_count=…, strike_range=…)` — pull concrete contracts by ATM/OTM/ITM
3. Multi-step research workflow → switch to the `schwab-marketdata-workflows-en` skill's `option-chain-research.md` playbook

→ Schema in [`references/tools/tool-reference-options.md`](references/tools/tool-reference-options.md);
Greeks freshness covered in the playbook's Cautions section and
[`references/concepts/osi-option-symbol.md`](references/concepts/osi-option-symbol.md).

### "Token expired, what now?"

```text
1. health_check()
   → inspect token_state ∈ {"valid","missing","malformed","insecure_perms"}
   → inspect token_expires_in_days (< 0.5 strongly suggests immediate reauthorize)
2. If SchwabAuthError(reason="refresh_token_expired" | "token_not_initialized"):
   uv run python -m schwab_marketdata_mcp.auth login_flow   # default
   # or manual_flow (headless / SSH-only / WSL2)
3. Re-run health_check() to confirm token_state == "valid"
```

→ Full mapping table and step-by-step remediation in
[`references/troubleshooting/auth-overview.md`](references/troubleshooting/auth-overview.md);
OAuth flow walkthrough in
[`references/oauth/oauth-overview.md`](references/oauth/oauth-overview.md);
token lifecycle in
[`references/oauth/oauth-token-lifecycle.md`](references/oauth/oauth-token-lifecycle.md).

### "Got rate-limited, what now?"

| Symptom | Remediation |
| ------- | ----------- |
| `SchwabRateLimitError(retry_after_seconds=N)` | Wait N seconds and retry; if it happens twice in a row, surface to the user |
| stderr emits `{"event":"rate_limit_warning","remaining":<20}` | Switch bulk requests to `get_quotes` (50 per call); or lower `SCHWAB_RATE_LIMIT_PER_MIN` |
| 0 slots, raises immediately | Apply retry-with-backoff on the agent side (schwab-py already retries `SCHWAB_MAX_RETRIES` times internally) |

→ Token-bucket behavior and triage in
[`references/operations/rate-limit-token-bucket.md`](references/operations/rate-limit-token-bucket.md);
4-symptom rate-limit triage in
[`references/troubleshooting/rate-limit-overview.md`](references/troubleshooting/rate-limit-overview.md).

### "I want to inspect server health"

```text
get_server_info()  → server_version / mcp_sdk_version / schwab_py_version / supported_tools
health_check()     → token_state / token_expires_in_days / rate_limit_remaining_per_min / recent_error_count_24h
```

The two together answer "Is the MCP server healthy, is the token valid,
and have we exhausted the rate limit?" in under 30 seconds.

→ Schema in [`references/tools/tool-reference-meta.md`](references/tools/tool-reference-meta.md).

### "I want candlesticks / historical data"

```text
get_price_history(symbol, period_type, period?, frequency_type, frequency?, ...)
```

The `(period_type, period, frequency_type, frequency)` 4-tuple only
accepts a **restricted set of combinations** — illegal combinations are
silently 400'd by the server. The MCP server pre-rejects them at the
Pydantic layer.

→ Schema + legal-combination table in
[`references/tools/tool-reference-price-history.md`](references/tools/tool-reference-price-history.md);
Cartesian-product error remediation in
[`references/troubleshooting/validation-pricehistory-cartesian.md`](references/troubleshooting/validation-pricehistory-cartesian.md).

### "I want instrument metadata / company fundamentals"

| Scenario | What to use |
| -------- | ----------- |
| Known ticker → metadata | `search_instruments(symbols=["AAPL"], projection="SYMBOL_SEARCH")` |
| Known ticker → fundamentals | `search_instruments(symbols=["AAPL"], projection="FUNDAMENTAL")` |
| Known 9-digit CUSIP | `get_instrument_by_cusip(cusip="037833100")` |
| Fuzzy company-name lookup | `search_instruments(symbols=["TESLA"], projection="DESCRIPTION_SEARCH")` |

→ Schema in [`references/tools/tool-reference-instruments.md`](references/tools/tool-reference-instruments.md).

### "I want top movers / unusual activity today"

```text
get_movers(index="DJI"|"COMPX"|"SPX"|..., sort_order=..., frequency=...)
```

The `index` enum value must be passed as the **enum name** (`"DJI"`),
not the wire value (`"$DJI"`).

→ Schema + enum reference in [`references/tools/tool-reference-movers.md`](references/tools/tool-reference-movers.md).

### "I want to call this from my own Python / TypeScript / Rust app"

| Client | File |
| ------ | ---- |
| Python (mcp SDK) | [`references/integration/python-mcp-client.md`](references/integration/python-mcp-client.md) |
| TypeScript / Node | [`references/integration/typescript-mcp-client.md`](references/integration/typescript-mcp-client.md) |
| Rust (rmcp or hand-rolled) | [`references/integration/rust-mcp-client.md`](references/integration/rust-mcp-client.md) |
| Shell + jq pipe | [`references/integration/cli-jq-pipe.md`](references/integration/cli-jq-pipe.md) |

## Data coverage clarifications

### `get_price_history` is the candlestick / kline endpoint

If you're looking for OHLCV bars (candles, klines, candlesticks),
`get_price_history` is the tool. The response carries a `candles[]`
array, each entry exposing `open` / `high` / `low` / `close` / `volume`
/ `datetime` (epoch milliseconds).

Supported granularity comes from the `(period_type, frequency_type,
frequency)` triple:

- `period_type=DAY`: `MINUTE` × {1, 5, 10, 15, 30} (~48 days for 1-min,
  ~9 months for 5–30 min).
- `period_type=MONTH`: `DAILY` / `WEEKLY` (up to 6 months).
- `period_type=YEAR`: `DAILY` / `WEEKLY` / `MONTHLY` (**up to 20
  years**).
- `period_type=YEAR_TO_DATE`: `DAILY` / `WEEKLY` (year-to-date).

Sub-minute candles (seconds, ticks) are not in the Schwab Market Data
API surface.

→ Full legal-combination table + Cartesian-product error remediation in
[`references/tools/tool-reference-price-history.md`](references/tools/tool-reference-price-history.md)
and the MCP repo's
[README → "Data coverage clarifications"](https://github.com/kevinkda/schwab-marketdata-mcp/blob/main/README.md#data-coverage-clarifications).

### What the Schwab Market Data API does NOT provide

The following data is **architecturally unavailable** through the Schwab
Market Data Production API and would require a third-party provider:

- **Time & sales / tape (trade-level)** — not in REST or Streaming
  since Schwab's 2024 API migration removed the `TIMESALE_*` services.
- **Tick-by-tick history** — not in the Schwab API.
- **Level 2 historical snapshots** — Streaming only, no REST history.
- **Fundamental / earnings time series** (EPS history, revenue
  history, etc.) — `quotes` carry FUNDAMENTAL fields but there is **no**
  historical endpoint.
- **News / SEC filings** — not in the Market Data API.

Recommended third-party providers (Polygon.io, Tiingo, Alpaca,
Databento, FMP, SEC EDGAR) for each data class are listed in the MCP
repo's
[README → "Data coverage clarifications"](https://github.com/kevinkda/schwab-marketdata-mcp/blob/main/README.md#data-coverage-clarifications).

### Trader API is out of scope

**Trader API endpoints** (account, orders, transactions, positions)
are explicitly out of scope — this skill covers **read-only Market
Data only**. If the user asks to place an order or modify positions,
refuse immediately and point them at the MCP README's "Responsible
use" section.

## Decision tree — pick the right tool

| User intent                                | What to use                                              |
| ------------------------------------------ | -------------------------------------------------------- |
| Single stock/ETF/index spot price          | `get_quote(symbol="AAPL")`                               |
| One-shot multi-symbol quotes (≤50)         | `get_quotes(symbols=[...])`                              |
| Historical candles / OHLC                  | `get_price_history(symbol, period_type, …)`              |
| Option chain snapshot                      | `get_option_chain(symbol, contract_type=…)`              |
| Option expiration list                     | `get_option_expiration_chain(symbol)`                    |
| Multi-market open/close status             | `get_market_hours(markets_list=[…])`                     |
| Single-market open/close status            | `get_market_hour_single(market_id)`                      |
| Today's top movers                         | `get_movers(index, sort_order)`                          |
| Fuzzy instrument lookup by ticker          | `search_instruments(symbols, projection)`                |
| Exact lookup by 9-digit CUSIP              | `get_instrument_by_cusip(cusip)`                         |
| Inspect token state / error counters       | `health_check()`                                         |
| Get server metadata (version, 12 tools)    | `get_server_info()`                                      |

**Enum values** must always use the **enum names** defined in
`models.py` (e.g. `"VOLUME"`, `"NASDAQ"`, `"DAY"`); the MCP server
internally translates them into schwab-py wire values.

## Key Concepts

| Term | Definition |
| ---- | ---------- |
| **access_token** | Schwab OAuth bearer token, valid for **90 minutes**; schwab-py auto-renews it before each API call, so the agent never has to think about it. |
| **refresh_token** | **rotate-on-use**: every refresh issues a new refresh_token, with a hard **7-day** lifetime. After expiry you must run `login_flow` again — there is no shortcut (Schwab OAuth is designed this way). |
| **TokenState** | One of 4 enums returned by `health_check()`: `valid` (usable) / `missing` (no token.json) / `insecure_perms` (file mode is not 600/700) / `malformed` (JSON corrupt or missing fields). The first three are fixed by `auth login_flow`; the last typically requires backing up and deleting token.json first. |
| **SchwabAuthError reason** | A 6-way enum attached to auth failures during business calls: `refresh_token_expired_soon` / `refresh_token_expired` / `token_not_initialized` / `token_corrupted` / `insecure_token_perms` / `callback_url_mismatch`. Each reason has its own 5-section remediation page in [`references/troubleshooting/`](references/troubleshooting/). |
| **rate_limit_bucket** | The MCP server uses a **token bucket + sliding window** (capacity = `SCHWAB_RATE_LIMIT_PER_MIN`, default 120) instead of `asyncio.Semaphore`. Difference: a Semaphore would hold its slot during retry sleep, blocking other concurrent tools; the token bucket releases the slot during sleep, letting other tools proceed. |
| **OSI option symbol** | A 21-character fixed format: `{ROOT:6}{YYMMDD:6}{C\|P:1}{STRIKE×1000:8}`, with the root right-padded with spaces if shorter than 6 chars. Example: `"AAPL  240119C00170000"` = AAPL 2024-01-19 Call $170.00; `"BRK/B 240419P00350000"` (contains `/`). Pass this directly to `get_quote`. |
| **pricehistory cartesian product** | Schwab only accepts a **restricted set of combinations** for the `(period_type, period, frequency_type, frequency)` tuple; illegal combinations silently return 400. The MCP server pre-rejects them at the Pydantic layer to avoid wasting billed quota. The full legal-combination table lives in [`references/tools/tool-reference-price-history.md`](references/tools/tool-reference-price-history.md). |
| **Pydantic Literal: enum name vs wire value** | The public API always takes the **enum name** (e.g. `MoversIndex.DJI` = `"DJI"`); the MCP server translates internally to the schwab-py wire value (e.g. `"$DJI"`). This translation layer means the agent never has to remember prefix characters (`$` / lowercase / mixed case). **Never** pass a wire value directly — it triggers `SchwabValidationError`. |
| **Activation handshake** | The two mandatory steps after skill activation: `get_server_info` to verify version compatibility, `health_check` to verify token state. Either failure stops the flow immediately — no silent fallback allowed. |
| **error normalization** | All schwab-py exceptions are wrapped by the server into `{"error": "Schwab*Error", ...}` dicts and returned to the caller — they **do not** propagate as exceptions through the MCP protocol. The agent must always check the `error` field before reading the payload. |
| **non-redistributable** | Schwab Market Data is non-redistributable; any markdown / report writes must stay inside private repositories (the workflows skill enforces this with `gh repo view --json isPrivate`). |

## Architecture overview

```text
┌────────────────────┐
│  AI agent (this    │  Cursor / Claude / Kiro / custom SDK clients
│  skill activates)  │
└────────┬───────────┘
         │ MCP JSON-RPC over stdio
         ▼
┌────────────────────────────────────────────┐
│  schwab-marketdata-mcp (separate repo)     │
│  Pydantic → token-bucket → schwab-py       │
└──────────┬─────────────────────────────────┘
           │ HTTPS Bearer access_token
           ▼
┌────────────────────────┐
│ Schwab Market Data API │ api.schwabapi.com
└────────────────────────┘
```

Full architecture, the 5 core components, data flow, and trust
boundaries are in
[`references/concepts/architecture-overview.md`](references/concepts/architecture-overview.md).

## Glossary

A cross-domain glossary spanning OAuth / MCP / Schwab API / rate
limiting / error handling lives in
[`references/concepts/glossary.md`](references/concepts/glossary.md).

The 11 most critical concepts, each with a "why was it designed this
way" explanation, lives in
[`references/concepts/key-concepts.md`](references/concepts/key-concepts.md).

## Schema cheat-sheet (full per-family schemas under `references/tools/`)

```text
get_quote(symbol: str, fields?: list[QuoteFields])  # symbol must be UPPERCASE; "$SPX"/"AAPL  240119C00170000" also OK
get_quotes(symbols: list[str], fields?, indicative?)  # ≤50 symbols / call

get_price_history(
  symbol, period_type, period?, frequency_type, frequency?,
  start_datetime?, end_datetime?, need_extended_hours_data?, need_previous_close?
)
# Cartesian-product rules in references/tools/tool-reference-price-history.md.  TZ-aware datetimes only.

get_option_chain(symbol, contract_type?, strike_count?, ...)  # see references
get_option_expiration_chain(symbol)
get_market_hours(markets_list: list[MarketEnum], date?)
get_market_hour_single(market_id, date?)
get_movers(index, sort_order?, frequency?)
search_instruments(symbols, projection)
get_instrument_by_cusip(cusip: 9 alphanumerics)
health_check()
get_server_info()
```

## Error normalization

All business tools **return a dict instead of raising** when an error
occurs (so the agent always sees structured data):

| `error` field               | Remediation                                                                              |
| --------------------------- | ---------------------------------------------------------------------------------------- |
| `SchwabAuthError`           | Inspect `reason` → [`references/troubleshooting/auth-overview.md`](references/troubleshooting/auth-overview.md) routing table (6 reasons, each with its own sub-doc). |
| `SchwabRateLimitError`      | Retry after `retry_after_seconds`, or lower `SCHWAB_RATE_LIMIT_PER_MIN`. See [`references/troubleshooting/rate-limit-overview.md`](references/troubleshooting/rate-limit-overview.md). |
| `SchwabTransientError`      | Schwab 5xx / network jitter; safe to retry briefly, but escalate to the user if it persists. See [`references/troubleshooting/transient-errors.md`](references/troubleshooting/transient-errors.md). |
| `SchwabValidationError`     | Bad input parameter; see [`references/troubleshooting/validation-overview.md`](references/troubleshooting/validation-overview.md). |

The full triage index lives in [`references/error-recovery.md`](references/error-recovery.md).

## Compatibility matrix

For per-client coverage of this skill's frontmatter fields, see the
"Compatibility matrix" section in the repo root `README.md` (5 clients
× 8 fields). Key convention: every non-standard frontmatter field
(`compatible_mcp_version` / `required_workspace` / `language_directive`
/ `auto_activate` / `activation_handshake` / `compatibility` /
`dependencies` / `governance`) is **only a documented constraint** —
the actual enforcement lives in the runtime checks of this SKILL.md
body (`get_server_info` version handshake + `cwd` validation +
language directive). Even if a client ignores these fields, the skill
still behaves correctly.

## FAQ

A 25+ entry FAQ lives in
[`references/faq.md`](references/faq.md), grouped into 7 categories:
"getting started / install & registration / OAuth / rate limiting /
calling / security / skill / failures".

## Reference index

A full subject-categorized reference index, mirroring the multi-level
table layout of the helis SKILL.md.

### Quick Start (7 files)

| File | Description |
| ---- | ----------- |
| [quick-start/step-1-developer-portal-app.md](references/quick-start/step-1-developer-portal-app.md) | Register a Schwab Developer Portal app, obtain App Key / Secret |
| [quick-start/step-2-credentials-env.md](references/quick-start/step-2-credentials-env.md) | Populate `.env` (mode 600), install pre-commit hook |
| [quick-start/step-3-first-oauth.md](references/quick-start/step-3-first-oauth.md) | First-time OAuth flow (login_flow / manual_flow) |
| [quick-start/step-4-token-health-check.md](references/quick-start/step-4-token-health-check.md) | Verify the token actually works via the `health` module |
| [quick-start/step-5-first-mcp-tool-call.md](references/quick-start/step-5-first-mcp-tool-call.md) | Minimal MCP client: `health_check` + `get_quote("VOO")` |
| [quick-start/step-6-cursor-integration.md](references/quick-start/step-6-cursor-integration.md) | Register with Cursor / Claude `~/.cursor/mcp.json` |
| [quick-start/step-7-cron-launchd-setup.md](references/quick-start/step-7-cron-launchd-setup.md) | Automate health probes via cron / launchd / systemd |

### Tools (1 index + 7 family files)

| File | Description |
| ---- | ----------- |
| [tools/index.md](references/tools/index.md) | 12-tool routing + cross-tool conventions |
| [tools/tool-reference-quotes.md](references/tools/tool-reference-quotes.md) | `get_quote` / `get_quotes` full schema |
| [tools/tool-reference-price-history.md](references/tools/tool-reference-price-history.md) | `get_price_history` + cartesian-product legal table |
| [tools/tool-reference-options.md](references/tools/tool-reference-options.md) | `get_option_chain` / `get_option_expiration_chain` |
| [tools/tool-reference-markets.md](references/tools/tool-reference-markets.md) | `get_market_hours` / `get_market_hour_single` |
| [tools/tool-reference-movers.md](references/tools/tool-reference-movers.md) | `get_movers` |
| [tools/tool-reference-instruments.md](references/tools/tool-reference-instruments.md) | `search_instruments` / `get_instrument_by_cusip` |
| [tools/tool-reference-meta.md](references/tools/tool-reference-meta.md) | `health_check` / `get_server_info` |

### OAuth (6 files)

| File | Description |
| ---- | ----------- |
| [oauth/oauth-overview.md](references/oauth/oauth-overview.md) | End-to-end 3-legged OAuth flow + mode decision tree |
| [oauth/oauth-login-flow.md](references/oauth/oauth-login-flow.md) | `login_flow` (default) walkthrough |
| [oauth/oauth-manual-flow.md](references/oauth/oauth-manual-flow.md) | `manual_flow` (headless / SSH-only) walkthrough |
| [oauth/oauth-self-host-callback.md](references/oauth/oauth-self-host-callback.md) | Self-hosted callback endpoint (advanced) |
| [oauth/oauth-token-lifecycle.md](references/oauth/oauth-token-lifecycle.md) | access_token / refresh_token / OAuth code lifecycle |
| [oauth/oauth-callback-mismatch.md](references/oauth/oauth-callback-mismatch.md) | 9 forms of callback URL mismatch with detailed triage |

### Concepts (4 files)

| File | Description |
| ---- | ----------- |
| [concepts/architecture-overview.md](references/concepts/architecture-overview.md) | 5 core components, data flow, trust boundaries |
| [concepts/glossary.md](references/concepts/glossary.md) | Cross-domain glossary (OAuth / MCP / Schwab API / rate limit / error / workflow / security / health probe) |
| [concepts/key-concepts.md](references/concepts/key-concepts.md) | The 11 most critical concepts, with "why was it designed this way" deep dives |
| [concepts/osi-option-symbol.md](references/concepts/osi-option-symbol.md) | OSI 21-char fixed format encoding / decoding / 4 typical violations |

### Integration (4 files)

| File | Description |
| ---- | ----------- |
| [integration/python-mcp-client.md](references/integration/python-mcp-client.md) | Write an MCP client with the `mcp` Python SDK (long-lived sessions included) |
| [integration/typescript-mcp-client.md](references/integration/typescript-mcp-client.md) | Write a Node / Next.js client with `@modelcontextprotocol/sdk` |
| [integration/rust-mcp-client.md](references/integration/rust-mcp-client.md) | Use `rmcp` or hand-roll stdio + JSON-RPC |
| [integration/cli-jq-pipe.md](references/integration/cli-jq-pipe.md) | Pipe stdin/stdout directly with shell + jq |

### Troubleshooting (17 files)

| File | Description |
| ---- | ----------- |
| [troubleshooting/auth-overview.md](references/troubleshooting/auth-overview.md) | Top-level error-type routing (4 classes × multiple reasons) |
| [troubleshooting/auth-token-states.md](references/troubleshooting/auth-token-states.md) | Remediation and state machine for the 4 `token_state` values |
| [troubleshooting/auth-refresh-expiring-soon.md](references/troubleshooting/auth-refresh-expiring-soon.md) | reason=`refresh_token_expired_soon` (< 12h warning) |
| [troubleshooting/auth-refresh-expired.md](references/troubleshooting/auth-refresh-expired.md) | reason=`refresh_token_expired` (already expired) |
| [troubleshooting/auth-token-not-initialized.md](references/troubleshooting/auth-token-not-initialized.md) | reason=`token_not_initialized` (first install) |
| [troubleshooting/auth-token-corrupted.md](references/troubleshooting/auth-token-corrupted.md) | reason=`token_corrupted` (JSON damaged) |
| [troubleshooting/auth-insecure-perms.md](references/troubleshooting/auth-insecure-perms.md) | reason=`insecure_token_perms` (permission issue) |
| [troubleshooting/auth-callback-url-mismatch.md](references/troubleshooting/auth-callback-url-mismatch.md) | reason=`callback_url_mismatch` (OAuth phase) |
| [troubleshooting/rate-limit-overview.md](references/troubleshooting/rate-limit-overview.md) | 4-symptom rate-limit triage |
| [troubleshooting/rate-limit-429.md](references/troubleshooting/rate-limit-429.md) | Schwab server-side 429 |
| [troubleshooting/rate-limit-token-bucket-empty.md](references/troubleshooting/rate-limit-token-bucket-empty.md) | Local token bucket at 0 slots |
| [troubleshooting/rate-limit-warning-stderr.md](references/troubleshooting/rate-limit-warning-stderr.md) | `rate_limit_warning` warning (still successful) |
| [troubleshooting/rate-limit-no-retry-after.md](references/troubleshooting/rate-limit-no-retry-after.md) | 429 missing `Retry-After` header (rare) |
| [troubleshooting/validation-overview.md](references/troubleshooting/validation-overview.md) | Pydantic input-validation routing |
| [troubleshooting/validation-symbol.md](references/troubleshooting/validation-symbol.md) | symbol regex mismatch |
| [troubleshooting/validation-pricehistory-cartesian.md](references/troubleshooting/validation-pricehistory-cartesian.md) | price history cartesian product illegal |
| [troubleshooting/validation-batch-50.md](references/troubleshooting/validation-batch-50.md) | `get_quotes` over-50 symbols |
| [troubleshooting/validation-osi-format.md](references/troubleshooting/validation-osi-format.md) | OSI option format wrong |
| [troubleshooting/transient-errors.md](references/troubleshooting/transient-errors.md) | `SchwabTransientError` (5xx / 4xx edge cases) |

### Operations (3 files)

| File | Description |
| ---- | ----------- |
| [operations/rate-limit-token-bucket.md](references/operations/rate-limit-token-bucket.md) | Token-bucket implementation deep dive + tuning |
| [operations/observability-and-caching.md](references/operations/observability-and-caching.md) | server.log / health fields + agent-side caching strategy |
| [operations/multi-machine-multi-account.md](references/operations/multi-machine-multi-account.md) | Correct patterns for multi-machine / multi-account setups |

### Top-level (5 files)

| File | Description |
| ---- | ----------- |
| [error-recovery.md](references/error-recovery.md) | Error-system overview + decision flow |
| [rate-limits-and-paging.md](references/rate-limits-and-paging.md) | Rate limit + paging combined |
| [credentials-rotate-runbook.md](references/credentials-rotate-runbook.md) | Credential-leak emergency runbook (reset Schwab Secret first, then clean repo) |
| [tos-snapshot.md](references/tos-snapshot.md) | Schwab ToS key excerpts (non-redistributable) |
| [faq.md](references/faq.md) | 25+ FAQ quick lookup |

Files in this skill

  • SKILL.md30 KB
  • references/concepts/architecture-overview.md8.1 KB
  • references/concepts/glossary.md9.4 KB
  • references/concepts/key-concepts.md8.1 KB
  • references/concepts/osi-option-symbol.md5.9 KB
  • references/credentials-rotate-runbook.md4.2 KB
  • references/error-recovery.md3.4 KB
  • references/faq.md9.4 KB
  • references/integration/cli-jq-pipe.md5.4 KB
  • references/integration/python-mcp-client.md6.8 KB
  • references/integration/rust-mcp-client.md6.9 KB
  • references/integration/typescript-mcp-client.md7 KB
  • references/oauth/oauth-callback-mismatch.md4.2 KB
  • references/oauth/oauth-login-flow.md4.3 KB
  • references/oauth/oauth-manual-flow.md5.1 KB
  • references/oauth/oauth-overview.md6.6 KB
  • references/oauth/oauth-self-host-callback.md6.6 KB
  • references/oauth/oauth-token-lifecycle.md7.2 KB
  • references/operations/multi-machine-multi-account.md5.1 KB
  • references/operations/observability-and-caching.md6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.