{"owner":"OpenBB-finance","repo":"OpenBB","hasSkills":true,"totalSkillsCount":36,"totalTokensCount":36669,"categories":["anthropic-skill","plugin-manifest"],"hasMcp":true,"mcpConfig":{"mcpServers":{"OpenBB":{"command":"npx","args":["-y","@modelcontextprotocol/server-OpenBB"]}}},"found":["openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/configure_mcp_server/SKILL.md","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/develop_extension/SKILL.md","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/work_with_server/SKILL.md","assets/extensions/router.json","assets/extensions/provider.json","openbb_platform/extensions/crypto/README.md","assets/extensions/obbject.json","openbb_platform/extensions/currency/README.md","openbb_platform/extensions/commodity/README.md","openbb_platform/extensions/README.md","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/build_workspace_app/SKILL.md","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/use_openbb_cli/SKILL.md","openbb_platform/extensions/derivatives/README.md","openbb_platform/extensions/devtools/README.md","openbb_platform/extensions/econometrics/README.md","openbb_platform/extensions/economy/README.md","openbb_platform/extensions/equity/README.md","openbb_platform/extensions/etf/README.md","openbb_platform/extensions/fixedincome/README.md","openbb_platform/extensions/index/README.md","openbb_platform/extensions/mcp_server/README.md","openbb_platform/extensions/news/README.md","openbb_platform/extensions/platform_api/README.md","openbb_platform/extensions/quantitative/README.md","openbb_platform/extensions/technical/README.md","openbb_platform/obbject_extensions/charting/README.md","openbb_platform/extensions/technical/docs/README.md","openbb_platform/extensions/technical/docs/api-reference.md","openbb_platform/extensions/technical/docs/getting-started.md","openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json","openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json","openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json","openbb_platform/extensions/technical/docs/user-guides/01-indicators.md","openbb_platform/extensions/technical/docs/user-guides/02-signals.md","openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md","openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md"],"skills":{"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/configure_mcp_server/SKILL.md":"---\nname: configure_mcp_server\ndescription: This guide covers installation, configuration, authentication, tool discovery, prompt management, and client integration for `openbb-mcp-server`.\n---\n\n# Configure and Build the OpenBB MCP Server\n\nThis guide covers installation, configuration, authentication, tool discovery,\nprompt management, and client integration for `openbb-mcp-server`.\n\n---\n\n## Installation\n\n```\npip install openbb-mcp-server\n```\n\nThis installs the `openbb-mcp` CLI command. To include the default OpenBB\nextensions as tools, also install them:\n\n```\npip install openbb\n```\n\nOr install individual extensions:\n\n```\npip install openbb-equity openbb-economy\n```\n\nThe `cli` extra adds `openbb-cli` and its four dispatcher tools\n(`openbb_dispatch`, `openbb_batch_dispatch`, `openbb_list_commands`,\n`openbb_describe_command`); `enable_cli_tools=false` turns them off:\n\n```\npip install \"openbb-mcp-server[cli]\"\n```\n\n---\n\n## Starting the Server\n\n### Default (all installed OpenBB extensions)\n\n```\nopenbb-mcp\n```\n\nDefaults: `--host 127.0.0.1 --port 8001 --transport streamable-http`\n\n### With a Custom FastAPI Application\n\n```\n# File path with default instance name \"app\"\nopenbb-mcp --app ./my_app.py\n\n# Explicit instance name\nopenbb-mcp --app ./my_app.py --name my_app\n\n# Module import syntax\nopenbb-mcp --app my_package.app:my_app\n\n# Factory function pattern\nopenbb-mcp --app ./my_app.py:create_app --factory\n```\n\n### Transport Options\n\n| Transport | Flag | Use Case |\n|---|---|---|\n| `streamable-http` | `--transport streamable-http` | Default. HTTP-based, works with Cursor, VS Code |\n| `sse` | `--transport sse` | Legacy Server-Sent Events. Required by Cline |\n| `stdio` | `--transport stdio` | Standard I/O. Used by Claude Desktop |\n\n---\n\n## CLI Arguments\n\n| Argument | Description | Default |\n|---|---|---|\n| `--app <path>` | Path to FastAPI application file or `module:instance` (mutually exclusive with `--spec`) | OpenBB default app |\n| `--name <name>` | Name of the FastAPI instance or factory function | `app` |\n| `--factory` | Treat `--name` as a factory function | `false` |\n| `--spec <path>` | Path to an `openbb-cli` generated `.spec` file. Synthesizes a FastAPI proxy app whose routes forward to the spec's `base_url`; FastMCP exposes those routes as MCP tools. Mutually exclusive with `--app`. | None |\n| `--config-file <path>` | Path to an `openbb.toml` config file. Highest-priority TOML layer in the cascade. Also honored via `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` / `OPENBB_CONFIG`. | None |\n| `--host <host>` | Server host | `127.0.0.1` |\n| `--port <port>` | Server port | `8001` |\n| `--transport <type>` | `streamable-http`, `sse`, or `stdio` | `streamable-http` |\n| `--default-categories <csv>` | Categories served when tool discovery is off | `all` |\n| `--allowed-categories <csv>` | The only categories served, with or without discovery | All categories |\n| `--tool-discovery` | Hide the tools behind `available_categories`, `available_tools`, `search_tools`, and `call_tool` | Discovery disabled |\n| `--system-prompt <path>` | Path to a `.txt` system prompt file | None |\n| `--server-prompts <path>` | Path to a `.json` server prompts file | None |\n\nEvery other `--key value` is routed by name: an `MCPSettings` field sets that\nsetting (`--enable-cli-tools false`, `--server-auth '[\"user\", \"pass\"]'`),\n`--httpx-<option>` sets an option of the httpx client that calls the API\n(`--httpx-verify false`), and anything else is passed to uvicorn\n(`--log-level debug`).\n\n---\n\n## Spec-Driven Proxy Mode\n\nInstead of importing a FastAPI app in-process, the launcher can synthesize one\nfrom an `openbb-cli` generated `.spec` file. Each command in the spec becomes\na FastAPI route that proxies to the spec's recorded `base_url`. FastMCP walks\nthose routes and exposes them as MCP tools — same tool surface as a normal\n`--app` install, but the heavy `import openbb` lives on the upstream server.\n\n### Generating a spec\n\nThe spec is produced by `openbb-cli`:\n\n```bash\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n```\n\nThis fetches `/openapi.json` from the server and writes a precomputed `.spec`\nfile with every command's URL path, method, parameter schema, and a\n`content_sha256` integrity hash.\n\n### Single-spec launch\n\n```bash\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML (`--config-file openbb.toml`):\n\n```toml\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\n# Optional: override the spec's recorded base_url at deploy time.\nbase_url = \"https://upstream.example.com\"\n# Optional: pinned SHA-256. When set, the launcher recomputes the spec's\n# canonical-JSON hash and refuses to start unless it matches — protects\n# against silent file drift when the spec is fetched at deploy time.\ncontent_sha256 = \"abc123...\"\n\n[mcp.spec.headers]\n# Static headers injected on every proxied upstream request.\n# ``$VAR`` / ``${VAR}`` are substituted from the current environment;\n# entries with unresolved variables are skipped with a warning.\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\nX-API-Key     = \"$OPENBB_UPSTREAM_KEY\"\n```\n\n`--spec` and `--app` are mutually exclusive.\n\n### Multi-spec mounting\n\nEach `[mcp.spec.NAME]` subtable mounts an independent spec under its own\nprefix. FastMCP turns the union of all mounted routes into MCP tools; per-spec\nhooks fire on every tool dispatch into that spec's mount.\n\n```toml\n[mcp.spec.equity]\npath = \"/etc/openbb/equity.spec\"\nmount = \"/equity\"                          # optional; defaults to \"/<name>\"\ncontent_sha256 = \"abc...\"\n[mcp.spec.equity.headers]\nAuthorization = \"Bearer $EQUITY_TOKEN\"\n[mcp.spec.equity.auth]\nhooks = [\"my_pkg.auth:equity_token_check\"]\n[mcp.spec.equity.middleware]\nhooks = [\"my_pkg.middleware:equity_rate_limit\"]\n\n[mcp.spec.crypto]\npath = \"/etc/openbb/crypto.spec\"\n[mcp.spec.crypto.headers]\nX-API-Key = \"$CRYPTO_KEY\"\n```\n\n---\n\n## openbb.toml Cascade\n\nThe launcher reads the same layered TOML cascade openbb-core ships with,\nlowest priority first:\n\n```\npyproject.toml [tool.openbb] (so [tool.openbb.mcp]) → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD) → --config-file PATH (explicit)\n```\n\nEvery layer is optional, and `.openbb.toml` works in place of `openbb.toml`.\n`--config-file` (or `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` /\n`OPENBB_CONFIG`) is the highest-priority TOML layer. `[mcp]` values are\napplied like CLI flags: a CLI flag beats them, and they beat `OPENBB_MCP_*`\nenvironment variables. `[env]` entries never replace a variable already set\nin the shell or a `.env` file.\n\n### Top-level tables\n\n| Table | Purpose |\n|---|---|\n| `[mcp]` | Default values for any of the `--*` CLI flags (CLI wins). |\n| `[mcp.spec]` | Single-spec proxy mode (path, base_url, content_sha256, headers). |\n| `[mcp.spec.NAME]` | Multi-spec mount (path, base_url, content_sha256, headers, auth, middleware). |\n| `[mcp.auth]` | Top-level auth hook list. |\n| `[mcp.middleware]` | Top-level middleware hook list. |\n| `[env]` | Env vars pushed into `os.environ` before any heavy import runs. Existing shell env vars are NEVER clobbered. Values support `$VAR` / `${VAR}` substitution. |\n\n### Auth and middleware hooks\n\nBoth `[mcp.auth]` and `[mcp.middleware]` accept a `hooks` list of\n`module:attr` entry points. Each hook is a 2-arg async function\n`async def hook(request, call_next)` and is wrapped as a\n`Middleware(BaseHTTPMiddleware, dispatch=fn)` on the MCP server's ASGI stack.\n\n```toml\n[mcp.auth]\nhooks = [\"my_pkg.auth:bearer_token_check\"]\n\n[mcp.middleware]\nhooks = [\"my_pkg.middleware:rate_limit\", \"my_pkg.middleware:request_logger\"]\n```\n\nAuth hooks register before middleware hooks; within each list, registration\norder matches TOML order. Per-spec hooks (under `[mcp.spec.NAME.auth]` /\n`[mcp.spec.NAME.middleware]`) are scoped to that spec's mounted sub-app.\nThe top-level hooks wrap the HTTP transports (`streamable-http`, `sse`);\nthe `stdio` transport has no HTTP stack, so they do not run there.\n\n### Env injection\n\n```toml\n[env]\nOPENBB_MCP_HOST = \"0.0.0.0\"\n# Values support shell-style $VAR / ${VAR} substitution.\nOPENBB_GITHUB_TOKEN = \"$GITHUB_TOKEN\"\n```\n\nUseful in containers to inject `OPENBB_*` keys without shell exports. Any key\nalready present in the real environment is preserved.\n\n---\n\n## Configuration Precedence\n\nSettings are resolved in this order (highest priority first):\n\n1. **CLI arguments** — command-line flags\n2. **`[mcp]` table of the `openbb.toml` cascade** — applied like CLI flags; `--config-file PATH` > project `openbb.toml` > user-global `~/.openbb_platform/openbb.toml` > `[tool.openbb.mcp]` in `pyproject.toml`\n3. **Environment variables** — `OPENBB_MCP_` prefixed, including those set by `[env]`\n4. **JSON settings file** — `~/.openbb_platform/mcp_settings.json`, written with the defaults on first run\n5. **Defaults** — built-in MCPSettings defaults\n\n### Config File Example\n\nCreate `~/.openbb_platform/mcp_settings.json`:\n\n```json\n{\n    \"name\": \"My MCP Server\",\n    \"default_tool_categories\": [\"equity\", \"economy\"],\n    \"enable_tool_discovery\": false,\n    \"describe_responses\": false,\n    \"system_prompt_file\": \"/path/to/system_prompt.txt\",\n    \"server_prompts_file\": \"/path/to/prompts.json\"\n}\n```\n\n### Environment Variables\n\nAll settings map to `OPENBB_MCP_` prefixed environment variables:\n\n```\nOPENBB_MCP_NAME=\"My MCP Server\"\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ENABLE_TOOL_DISCOVERY=true\nOPENBB_MCP_SYSTEM_PROMPT_FILE=\"/path/to/prompt.txt\"\nOPENBB_MCP_SERVER_PROMPTS_FILE=\"/path/to/prompts.json\"\n```\n\n---\n\n## Settings Reference\n\n### Server Identity\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `name` | `OPENBB_MCP_NAME` | `str` | `\"OpenBB MCP\"` |\n| `description` | `OPENBB_MCP_DESCRIPTION` | `str` | Auto-generated |\n| `version` | `OPENBB_MCP_VERSION` | `str \\| None` | `None` |\n| `instructions` | `OPENBB_MCP_INSTRUCTIONS` | `str \\| None` | `None` (the system prompt, when one is loaded) |\n| `mask_error_details` | `OPENBB_MCP_MASK_ERROR_DETAILS` | `bool \\| None` | `None` |\n\n### Tool Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `default_tool_categories` | `OPENBB_MCP_DEFAULT_TOOL_CATEGORIES` | `list[str]` | `[\"all\"]` |\n| `allowed_tool_categories` | `OPENBB_MCP_ALLOWED_TOOL_CATEGORIES` | `list[str] \\| None` | `None` |\n| `enable_tool_discovery` | `OPENBB_MCP_ENABLE_TOOL_DISCOVERY` | `bool` | `false` |\n| `enable_cli_tools` | `OPENBB_MCP_ENABLE_CLI_TOOLS` | `bool` | `true` |\n| `list_page_size` | `OPENBB_MCP_LIST_PAGE_SIZE` | `int \\| None` | `None` |\n| `describe_responses` | `OPENBB_MCP_DESCRIBE_RESPONSES` | `bool` | `false` |\n| `api_prefix` | `OPENBB_MCP_API_PREFIX` | `str \\| None` | `None` |\n\n`enable_cli_tools` registers the `openbb-cli` dispatcher tools when\n`openbb-cli` is installed. `mask_error_details` hides exception details from\nclients.\n\n### Prompt Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `system_prompt_file` | `OPENBB_MCP_SYSTEM_PROMPT_FILE` | `str \\| None` | `None` |\n| `server_prompts_file` | `OPENBB_MCP_SERVER_PROMPTS_FILE` | `str \\| None` | `None` |\n| `default_skills_dir` | `OPENBB_MCP_DEFAULT_SKILLS_DIR` | `str \\| None` | Built-in skills dir |\n| `skills_reload` | `OPENBB_MCP_SKILLS_RELOAD` | `bool` | `false` |\n| `skills_providers` | `OPENBB_MCP_SKILLS_PROVIDERS` | `list[str] \\| None` | `None` |\n\n### HTTP Transport\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `uvicorn_config` | `OPENBB_MCP_UVICORN_CONFIG` | `dict` | `{\"host\": \"127.0.0.1\", \"port\": \"8001\"}` |\n| `httpx_client_kwargs` | `OPENBB_MCP_HTTPX_CLIENT_KWARGS` | `dict` | `{}` |\n\n`httpx_client_kwargs` configures the httpx client that sends tool calls to the\nAPI (headers, timeout, TLS verification).\n\n### Duplicate Handling\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `on_duplicate` | `OPENBB_MCP_ON_DUPLICATE` | `str \\| None` | `None` |\n\nOptions: `\"warn\"`, `\"error\"`, `\"replace\"`, `\"ignore\"`\n\n### Module Exclusion\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `module_exclusion_map` | `OPENBB_MCP_MODULE_EXCLUSION_MAP` | `dict \\| None` | `None` |\n\nMaps route path segments to Python modules: routes under a segment are hidden\nwhile its module is imported. `None` uses `{\"coverage\": \"openbb_core\"}`, which\nhides the platform's `coverage` routes; `{}` hides nothing. Data-processing\nextensions (`technical`, `quantitative`, `econometrics`) are exposed when\ninstalled. Pair them with `run_pipeline`, or leave them out with\n`allowed_tool_categories` (or, with tool discovery off,\n`default_tool_categories`).\n\n---\n\n## Authentication\n\nThree authentication modes are available.\n\n### Server-Side Authentication\n\nProtect incoming MCP requests with a Bearer token:\n\n```json\n{\n    \"server_auth\": [\"username\", \"password\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"username\", \"password\"]'\n```\n\nClients must include `Authorization: Bearer <base64(username:password)>` in\ntheir requests. The token is base64-encoded `username:password`.\n\n### Client-Side Authentication\n\nAuthenticate outbound requests to downstream services:\n\n```json\n{\n    \"client_auth\": [\"api_user\", \"api_key\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_CLIENT_AUTH='[\"api_user\", \"api_key\"]'\n```\n\nThis passes `auth=(user, pass)` to the httpx client used for internal requests.\n\n### Programmatic Authentication\n\nWhen using the server as a library, pass a custom `AuthProvider` to\n`create_mcp_server()`:\n\n```python\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\nsettings = MCPSettings()\nmcp = create_mcp_server(settings, my_fastapi_app, auth=my_auth_provider)\n```\n\n---\n\n## Tool Discovery\n\nWhen `enable_tool_discovery` is `true`, the OpenBB tools are left out of the\ntool list and these tools are available to the agent instead:\n\n| Tool | Description |\n|---|---|\n| `available_categories` | Lists all tool categories with tool counts |\n| `available_tools` | Lists the tools in a category with one-sentence descriptions |\n| `search_tools` | Finds tools by a natural-language query and returns their full definitions |\n| `call_tool` | Runs any tool by name with its arguments |\n\nDiscovery holds no per-session state: every client sees the same tool list, and\na hidden tool can also be called directly by name. It behaves the same over every\ntransport and protocol version, so the server is safe for multi-user deployments.\n\n### Choosing the Served Categories\n\nUse `allowed_tool_categories` to serve only some categories, with or without\ndiscovery:\n\n```\nopenbb-mcp --allowed-categories equity,economy,crypto\n```\n\nRoutes in other categories are never registered: they can't be listed,\nsearched, or called, by name, through `call_tool`, or through `run_pipeline`.\n`None` or `all` serves every category.\n\nWithout discovery, `default_tool_categories` also limits the served tools:\n\n```\nopenbb-mcp --default-categories equity,economy\n```\n\nTools outside these categories are registered but disabled, so they are not\nlisted and calling them fails with \"Unknown tool\". A route's\n`mcp_config.enable` overrides this per tool. Nothing can re-enable a disabled\ntool at runtime. With discovery on, `default_tool_categories` is ignored and\nevery allowed tool is reachable through `search_tools` and `call_tool`.\n\n### Enabling Discovery\n\n```\nopenbb-mcp --tool-discovery\n```\n\nWithout discovery, all tools in `default_tool_categories` are listed and the\ndiscovery tools are not registered.\n\n---\n\n## Tool Naming Convention\n\nTools are named from their API route path after stripping the API prefix:\n\n| Route Path | Tool Name |\n|---|---|\n| `/equity/price/historical` | `equity_price_historical` |\n| `/economy/cpi` | `economy_cpi` |\n| `/my_app/process` | `my_app_process` |\n\nThe first path segment is the **category** and the second, on paths with three\nor more segments, the **subcategory**; otherwise the subcategory is\n`\"general\"`. `{placeholder}` segments are skipped, and a single-segment path\nrepeats its segment (`/hello` is `hello_hello`). When one path serves several\nmethods, the non-GET tools get a `_<method>` suffix (`demo_items` and\n`demo_items_post`). A route's `mcp_config.name` replaces the generated name.\n\n---\n\n## Prompt System\n\nThe server supports three layers of prompts, all accessible via the\n`list_prompts` and `get_prompt` tools. Bundled skills are served alongside them\nas resources.\n\n### 1. System Prompt (tag: `system`)\n\nA plain text file loaded once at startup. Also exposed as\n`resource://system_prompt`.\n\n```\nopenbb-mcp --system-prompt /path/to/system_prompt.txt\n```\n\n### 2. Server Prompts JSON (tag: `server`)\n\nA JSON file defining reusable prompts with optional arguments:\n\n```json\n[\n    {\n        \"name\": \"analyze_stock\",\n        \"description\": \"Framework for analyzing a stock.\",\n        \"content\": \"Analyze {symbol} focusing on {aspect}.\",\n        \"arguments\": [\n            {\n                \"name\": \"symbol\",\n                \"type\": \"str\",\n                \"description\": \"Ticker symbol\"\n            },\n            {\n                \"name\": \"aspect\",\n                \"type\": \"str\",\n                \"default\": \"fundamentals\",\n                \"description\": \"Analysis focus area\"\n            }\n        ],\n        \"tags\": [\"analysis\"]\n    }\n]\n```\n\nArgument types: `str`, `int`, `float`, `bool`, `list`, `dict`, `any`\n\nArguments with a `default` value are optional; those without are required.\nClients pass prompt arguments as strings (`{\"aspect\": \"valuation\"}`,\n`{\"years\": \"10\"}`).\n\n```\nopenbb-mcp --server-prompts /path/to/prompts.json\n```\n\n### 3. Inline Prompts (tag: route-specific)\n\nDefine prompts directly on FastAPI routes via `openapi_extra`:\n\n```python\n@router.command(\n    methods=[\"GET\"],\n    openapi_extra={\n        \"mcp_config\": {\n            \"prompts\": [\n                {\n                    \"name\": \"usage_guide\",\n                    \"description\": \"How to use this endpoint.\",\n                    \"content\": \"To analyze {symbol}, call this endpoint with...\",\n                }\n            ]\n        }\n    },\n)\nasync def my_endpoint(symbol: str) -> OBBject: ...\n```\n\nEach `{placeholder}` in `content` becomes an argument: a prompt argument of the\nsame name, else the endpoint parameter, else a required string. Arguments\nwithout a default are required. The rendered prompt starts with\n`Use the tool, <tool name>, to perform the following task.`, naming the\nroute's tool (including a name set with `mcp_config.name`), and the tool's\ndescription lists its prompts.\n\n### 4. Bundled Skills (Resources)\n\nSkill guides are exposed as MCP resources discoverable via `list_resources()`.\nEach skill is accessible at a `skill://<name>/SKILL.md` URI.\n\n```\n# Discover available skills\nlist_resources()  # returns skill://develop_extension/SKILL.md, etc.\n\n# Read a specific skill\nread_resource(\"skill://configure_mcp_server/SKILL.md\")\n```\n\nCustom skills directory:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=/path/to/my/skills\n```\n\nSet to empty string to disable bundled skills:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=\"\"\n```\n\n### Skills Reload\n\nEnable hot-reload of skill files without restarting the server (useful during development):\n\n```json\n{\n    \"skills_reload\": true\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SKILLS_RELOAD=true\n```\n\n### Vendor Skills Providers\n\nLoad skill directories from well-known vendor locations (e.g. `~/.claude/skills/`).\nSets the `skills_providers` list in `mcp_settings.json`:\n\n```json\n{\n    \"skills_providers\": [\"claude\", \"cursor\"]\n}\n```\n\nOr via environment variable (comma-separated):\n\n```\nOPENBB_MCP_SKILLS_PROVIDERS=\"claude,cursor\"\n```\n\nSupported provider names:\n\n| Name | Default Directory |\n|---|---|\n| `claude` | `~/.claude/skills/` |\n| `cursor` | `~/.cursor/skills/` |\n| `vscode` / `copilot` | `~/.copilot/skills/` |\n| `codex` | `/etc/codex/skills/` + `~/.codex/skills/` |\n| `gemini` | `~/.gemini/skills/` |\n| `goose` | `~/.config/agents/skills/` |\n| `opencode` | `~/.config/opencode/skills/` |\n\n---\n\n## Inline MCP Configuration (MCPConfigModel)\n\nControl how individual routes appear in the MCP server via `openapi_extra`:\n\n```python\n@app.get(\n    \"/my_endpoint\",\n    openapi_extra={\n        \"mcp_config\": {\n            \"expose\": True,\n            \"mcp_type\": \"tool\",\n            \"methods\": [\"GET\"],\n            \"exclude_args\": [\"internal_param\"],\n            \"prompts\": []\n        }\n    },\n)\n```\n\nThe configuration is read from `openapi_extra[\"mcp_config\"]`, or from\n`openapi_extra[\"x-mcp\"]` when `mcp_config` is absent. An invalid configuration\nis logged and ignored for that route.\n\n### MCPConfigModel Fields\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `expose` | `bool \\| None` | `None` | Set `false` to hide route from MCP |\n| `mcp_type` | `str \\| None` | `None` | `\"tool\"`, `\"resource\"`, or `\"resource_template\"` |\n| `methods` | `list[str] \\| None` | `None` | The route's HTTP methods to serve; the others are left out (`\"*\"` for all) |\n| `exclude_args` | `list[str] \\| None` | `None` | Arguments left out of the tool schema; each needs a default, which then applies |\n| `name` | `str \\| None` | `None` | Tool name, replacing the one built from the path |\n| `tags` | `list[str] \\| None` | `None` | Tags added to the tool next to its category |\n| `enable` | `bool \\| None` | `None` | With discovery off, serve (`true`) or hide (`false`) the tool regardless of `default_tool_categories` |\n| `describe_responses` | `bool \\| None` | `None` | Keep (`true`) or cut (`false`) the response documentation in the tool description |\n| `mime_type` | `str \\| None` | `None` | MIME type of a route served as a resource |\n| `prompts` | `list[dict]` | `[]` | Inline prompt definitions |\n\n---\n\n## What Agents Get\n\nBeyond one tool per route, every server provides:\n\n- **`run_pipeline`** — chains tools on the server, feeding an earlier step's\n  `results` (or a chart artifact's rows) into a later step's argument, so\n  data-processing tools (`technical_*`, `quantitative_*`, `econometrics_*`)\n  receive price histories without them passing through the conversation. It\n  calls only tools the server serves.\n- **Parameter choices** — provider `choices` and widget `x-widget_config`\n  options become an `enum`, or a \"Valid values\" note in the description, and\n  an `optionsEndpoint` names the tool that lists the values. Options endpoints\n  hidden from the API schema have no tool to name.\n- **Chart artifacts** — a Plotly figure in a result (a figure route, or an\n  OBBject `chart` from `chart: true`) is replaced by an OpenBB Workspace\n  artifact: a `chart` artifact with rows and `chart_params` for line, bar,\n  scatter, pie, and donut figures, else a `table` artifact of the figure's\n  data.\n- **Prompt and resource tools** — `list_prompts`, `get_prompt`,\n  `list_resources`, and `read_resource` for clients without native prompt or\n  resource support.\n- **`openbb-cli` dispatcher tools** — with the `cli` extra and\n  `enable_cli_tools`.\n- **`install_skill`** — writes a skill into the bundled or a vendor skills\n  directory. Skill names are limited to lowercase letters, digits,\n  underscores, and hyphens, and file paths must stay inside the skill\n  directory, but any connected client can call it. When clients are\n  untrusted, keep the skills directories read-only for the server process or\n  require `server_auth`.\n\n---\n\n## Client Configuration Examples\n\n### Claude Desktop (stdio transport)\n\nIn Claude Desktop's MCP config file:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"--with\", \"openbb\",\n                \"openbb-mcp\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\nFor a custom app:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"openbb-mcp\",\n                \"--app\", \"./my_app.py\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\n### Cursor (streamable-http)\n\n1. Start the server: `openbb-mcp`\n2. In Cursor's `mcp.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\"\n        }\n    }\n}\n```\n\n### VS Code (streamable-http)\n\n1. Enable MCP in VS Code settings (Settings → Chat → MCP)\n2. Start the server: `openbb-mcp`\n3. Open Command Palette → \"MCP: Add Server\" → HTTP\n4. Enter URL: `http://127.0.0.1:8001/mcp`\n\nFor the Cline VS Code extension, use `--transport sse`:\n\n```\nopenbb-mcp --transport sse\n```\n\n### With Authentication\n\nStart with server auth enabled:\n\n```\nopenbb-mcp --host 0.0.0.0 --port 8001\n```\n\nWith `mcp_settings.json`:\n\n```json\n{\n    \"server_auth\": [\"admin\", \"secretpass\"]\n}\n```\n\nClients include the Bearer token in their configuration:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\",\n            \"headers\": {\n                \"Authorization\": \"Bearer YWRtaW46c2VjcmV0cGFzcw==\"\n            }\n        }\n    }\n}\n```\n\nThe token value is `base64(\"admin:secretpass\")`.\n\n---\n\n## Advanced Configuration\n\n### Lists and Dicts in Environment Variables\n\nLists can be passed as comma-separated strings:\n\n```\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ALLOWED_TOOL_CATEGORIES=\"equity,economy\"\n```\n\nDicts and tuples must be JSON-encoded strings:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"user\", \"pass\"]'\nOPENBB_MCP_UVICORN_CONFIG='{\"host\": \"0.0.0.0\", \"port\": 8001, \"log_level\": \"info\"}'\nOPENBB_MCP_HTTPX_CLIENT_KWARGS='{\"timeout\": 30, \"verify\": false}'\n```\n\n### SSL / HTTPS\n\nPass SSL config via Uvicorn:\n\n```\nopenbb-mcp --ssl-keyfile /path/to/key.pem --ssl-certfile /path/to/cert.pem\n```\n\nOr in the config file:\n\n```json\n{\n    \"uvicorn_config\": {\n        \"host\": \"0.0.0.0\",\n        \"port\": 443,\n        \"ssl_keyfile\": \"/path/to/key.pem\",\n        \"ssl_certfile\": \"/path/to/cert.pem\"\n    }\n}\n```\n\n### Using as a Library\n\n```python\nimport asyncio\nfrom fastapi import FastAPI\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\napp = FastAPI()\n\n\n@app.get(\"/hello\")\nasync def hello():\n    return \"Hello World\"\n\n\nsettings = MCPSettings(\n    name=\"My Custom MCP\",\n    default_tool_categories=[\"all\"],\n    enable_tool_discovery=False,\n)\n\nmcp = create_mcp_server(settings, app)\nmcp.run(transport=\"streamable-http\")\n```\n\n---\n\n## Workflow Summary\n\nTo configure and deploy an OpenBB MCP server:\n\n1. **Install**: `pip install openbb-mcp-server` (plus any desired OpenBB extensions).\n2. **Configure**: Create `~/.openbb_platform/mcp_settings.json` with desired settings.\n3. **Add prompts**: Write a system prompt file and/or server prompts JSON.\n4. **Start**: Run `openbb-mcp` with appropriate CLI flags.\n5. **Connect**: Configure your MCP client (Claude Desktop, Cursor, VS Code) with the server URL or stdio command.\n6. **Discover**: Use `available_categories`, `search_tools`, and `call_tool` to find and run tools.\n7. **Iterate**: Adjust settings, add inline `mcp_config` to routes, add skill files.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/develop_extension/SKILL.md":"---\nname: develop_extension\ndescription: This is a complete guide for creating a new OpenBB Platform extension from scratch. Follow every phase in order. When the user says \"build me an application that does X\", use this guide to scaffold, implement, install, and verify the extension.\n---\n\n# Build an OpenBB Platform Extension\n\nThis is a complete guide for creating a new OpenBB Platform extension from scratch.\nFollow every phase in order. When the user says \"build me an application that does X\",\nuse this guide to scaffold, implement, install, and verify the extension.\n\n---\n\n## Phase 1 — Scaffold the Project\n\nThe `openbb-cookiecutter` package must be installed in the active Python environment\nbefore scaffolding. Install it with:\n\n```\npip install openbb-cookiecutter\n```\n\nThen run the CLI to generate the project skeleton.\nAll variables have sensible defaults; override only what you need.\n\n### Template Variables\n\n| Variable | Default | Description |\n|---|---|---|\n| `full_name` | `\"Hello World\"` | Author name |\n| `email` | `\"hello@world.com\"` | Author email |\n| `project_name` | `\"OpenBB Python Extension Template\"` | Human-readable project name |\n| `project_tag` | derived from `project_name` | Hyphenated slug (used as directory name and package identifier) |\n| `package_name` | derived from `project_name` | Python package name (lower_snake_case) |\n| `provider_name` | derived from `project_name` | Provider identifier (lower_snake_case) |\n| `router_name` | derived from `project_name` | Router identifier (lower_snake_case) |\n| `obbject_name` | derived from `project_name` | OBBject accessor name (lower_snake_case) |\n\nAll derived values are automatically generated from `project_name` — you only need\nto supply `project_name` for most cases.\n\n### CLI Command\n\n```\nopenbb-cookiecutter \\\n  -o /path/to/output \\\n  --no-input \\\n  --extra-context project_name=\"My Extension Name\"\n```\n\nAdd more `--extra-context KEY=VALUE` pairs to override individual variables.\nUse `-f` to overwrite an existing directory.\n\n---\n\n## Phase 2 — Understand the Generated Structure\n\nAfter scaffolding, you get this tree (with template variables resolved):\n\n```\n<project_tag>/\n├── pyproject.toml          # Dependencies, entry points (CRITICAL)\n├── <package_name>/\n│   ├── providers/\n│   │   └── <provider_name>/\n│   │       ├── __init__.py     # Provider registration (fetcher_dict)\n│   │       ├── models/\n│   │       │   ├── example.py      # Custom-schema fetcher example\n│   │       │   └── ohlc_example.py # Standard-model fetcher example\n│   │       └── utils/\n│   │           └── helpers.py      # Shared utility functions\n│   ├── routers/\n│   │   ├── __init__.py\n│   │   ├── <router_name>.py        # Router commands (API endpoints)\n│   │   ├── <router_name>_views.py  # Chart views (optional)\n│   │   └── depends.py              # Dependency injection\n│   └── obbject/\n│       └── <obbject_name>/\n│           └── __init__.py         # OBBject accessors (optional)\n└── tests/\n    └── conftest.py\n```\n\n### The Four Plugin Types\n\nOpenBB discovers extensions via Python entry points in `pyproject.toml`.\nEach plugin type has its own entry point group:\n\n| Entry Point Group | What It Registers | Where It Lives |\n|---|---|---|\n| `openbb_provider_extension` | Data provider (fetchers) | `providers/<name>/__init__.py` |\n| `openbb_core_extension` | Router (API endpoints/commands) | `routers/<name>.py` |\n| `openbb_charting_extension` | Chart views | `routers/<name>_views.py` |\n| `openbb_obbject_extension` | Result post-processing accessors | `obbject/<name>/__init__.py` |\n\n---\n\n## Phase 3 — Implement the Data Provider\n\nThis is the core of any extension. A provider fetches data from an external source\nand returns it as typed Pydantic models.\n\n### The Three-Class Fetcher Pattern\n\nEvery data fetcher follows this structure:\n\n**Class 1 — QueryParams** (input schema):\n- Inherits from `openbb_core.provider.abstract.query_params.QueryParams`\n- Define all parameters the user can pass (e.g., `symbol`, `start_date`)\n- Use `pydantic.Field` for descriptions and defaults\n\n**Class 2 — Data** (output schema):\n- Inherits from `openbb_core.provider.abstract.data.Data`\n- Define all fields in the response (e.g., `open`, `high`, `low`, `close`, `volume`)\n- Use `pydantic.Field` for descriptions\n- Use `__alias_dict__` to map source field names to your schema names\n\n**Class 3 — Fetcher** (orchestrator):\n- Inherits from `Fetcher[YourQueryParams, list[YourData]]`\n- Has exactly three static methods:\n\n  1. `transform_query(params: dict) -> YourQueryParams`\n     Pre-process user input. Return a validated QueryParams instance.\n\n  2. `extract_data(query, credentials, **kwargs) -> list[dict]`\n     Make the actual HTTP request to the data source. Return raw data as dicts.\n     For async fetching, name it `aextract_data` instead.\n\n  3. `transform_data(query, data, **kwargs) -> list[YourData]`\n     Convert raw dicts into typed Data model instances.\n\n### Imports for Fetcher Files\n\n```python\nfrom typing import Any\nfrom openbb_core.provider.abstract.data import Data\nfrom openbb_core.provider.abstract.fetcher import Fetcher\nfrom openbb_core.provider.abstract.query_params import QueryParams\nfrom pydantic import Field\n```\n\n### HTTP Requests — Required Utilities\n\n**IMPORTANT:** All HTTP requests inside fetchers and utility helpers **must** use the built-in\nutilities from `openbb_core.provider.utils.helpers`. Do **not** create raw\n`requests`, `aiohttp`, or `httpx` clients from scratch. The built-in helpers\napply the user's configured HTTP settings (proxy, timeout, user-agent, etc.)\nfrom `system_settings.json` automatically.\n\n#### Building Query Strings\n\nConvert a QueryParams model to a URL query string, optionally excluding\nparameters that should not appear in the URL:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"interval\", \"provider\"])\nurl = f\"https://api.example.com/data?{query_string}\"\n```\n\n`model_dump()` strips `None` values automatically. Pass parameter names to\nexclude in the second argument as a list (use `[]` if nothing to exclude).\n\n#### Synchronous Requests\n\nFor use inside `extract_data` (sync fetchers):\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\n# Returns a requests.Response object\nresponse = make_request(url, headers={\"Authorization\": f\"Bearer {api_key}\"})\ndata = response.json()\n```\n\nAll `requests.get`/`requests.post` keyword arguments are passed through.\n\nIf you need a session object for multiple requests:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\nresponse = session.get(url)\n```\n\n#### Asynchronous Requests (Preferred)\n\nFor use inside `aextract_data` (async fetchers). **Always prefer async.**\n\n**Single URL** — returns parsed JSON by default:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\ndata = await amake_request(url)  # returns dict (parsed JSON)\n```\n\n**Multiple URLs** — downloads concurrently and returns a list:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nurls = [f\"https://api.example.com/data/{s}\" for s in symbols]\nall_data = await amake_requests(urls)  # returns list[dict]\n```\n\n**Custom response handling (e.g., CSV):** Both `amake_request` and\n`amake_requests` default to parsing JSON. For non-JSON content (CSV, text,\nbinary), pass a `response_callback`:\n\n```python\nfrom io import StringIO\nfrom typing import Any\nfrom pandas import read_csv\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresults: list[dict] = []\n\n\nasync def csv_callback(response, _: Any):\n    \"\"\"Parse CSV response into list of dicts.\"\"\"\n    text = await response.text()\n    df = read_csv(StringIO(text))\n    results.extend(df.to_dict(\"records\"))\n\n\nawait amake_request(url, response_callback=csv_callback)\n# results now contains the parsed rows\n```\n\nFor CSV files with header rows to skip, pass `skiprows` to `read_csv`:\n\n```python\nasync def csv_callback(response, _: Any):\n    text = await response.text()\n    df = read_csv(StringIO(text), skiprows=3)\n    results.extend(df.to_dict(\"records\"))\n```\n\n**Async session object** — if you need a raw `aiohttp.ClientSession`:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with session.get(url) as response:\n        if response.status != 200:\n            raise OpenBBError(f\"Failed: {response.status} -> {response.reason}\")\n        data = await response.json()\n```\n\n#### Summary: Which Helper to Use\n\n| Scenario | Function | Module |\n|---|---|---|\n| Build a query string | `get_querystring()` | `openbb_core.provider.utils.helpers` |\n| Sync single request | `make_request()` | `openbb_core.provider.utils.helpers` |\n| Sync session | `get_requests_session()` | `openbb_core.provider.utils.helpers` |\n| Async single request (JSON) | `amake_request()` | `openbb_core.provider.utils.helpers` |\n| Async multiple URLs (JSON) | `amake_requests()` | `openbb_core.provider.utils.helpers` |\n| Async single/multi (CSV/text) | `amake_request()` + `response_callback` | `openbb_core.provider.utils.helpers` |\n| Async raw session | `get_async_requests_session()` | `openbb_core.provider.utils.helpers` |\n\n### Using Standard Models (Multi-Provider Endpoints)\n\nTo plug into existing endpoints that other providers already serve (like `EquityHistorical`),\ninherit from the standard query/data classes instead of the abstract base classes:\n\n```python\nfrom openbb_core.provider.standard_models.equity_historical import (\n    EquityHistoricalData,\n    EquityHistoricalQueryParams,\n)\n```\n\nThen add provider-specific fields as extra attributes.\nUse `__alias_dict__` to map the source's field names to the standard field names.\n\n### Registering Fetchers in the Provider\n\nIn `providers/<provider_name>/__init__.py`, create a `Provider` instance:\n\n```python\nfrom openbb_core.provider.abstract.provider import Provider\n\nmy_provider = Provider(\n    name=\"my_provider\",\n    description=\"Description of what this provider does.\",\n    # credentials=[\"api_key\"],  # Uncomment if an API key is needed\n    website=\"https://example.com\",\n    fetcher_dict={\n        \"MyCustomModel\": MyCustomFetcher,\n        \"EquityHistorical\": MyEquityHistoricalFetcher,  # Plugs into existing endpoint\n    },\n)\n```\n\nThe `fetcher_dict` keys are model names. When a key matches a standard model name\n(like `\"EquityHistorical\"`), this provider becomes selectable via the `provider`\nparameter on that existing endpoint.\n\nFor custom/new model names, you must also create a router command that references\nthat model name (see Phase 4).\n\n### Credentials\n\nIf your provider needs an API key:\n1. Add `credentials=[\"api_key\"]` to the Provider constructor\n2. Access it in `extract_data` via `credentials.get(\"<provider_name>_api_key\")` (the `Provider` prefixes each credential with its lower-cased `name`)\n3. Users configure it in their OpenBB user settings\n\n---\n\n## Phase 4 — Implement the Router\n\nThe router defines the API endpoints (commands) that users call.\n\n### Router Basics\n\n```python\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\")\n```\n\nThe top-level prefix is determined by the entry point name in `pyproject.toml`,\nnot by the `prefix` argument. Only set `prefix` for sub-routers.\n\n### Provider-Backed Command (Standard Pattern)\n\nThis is the most common pattern. It connects a router command to one or more\nprovider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyCustomModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters (`cc`, `provider_choices`, `standard_params`, `extra_params`)\nand the `OBBject.from_query(Query(**locals()))` return pattern are mandatory and\nmust be used exactly as shown.\n\nThe `model=\"MyCustomModel\"` string must match a key in at least one provider's\n`fetcher_dict`.\n\n### Free-Form GET Endpoint\n\nFor endpoints that don't use the provider/fetcher system:\n\n```python\n@router.command(methods=[\"GET\"])\nasync def my_endpoint(symbol: str = \"AAPL\") -> OBBject[dict]:\n    \"\"\"Get some data directly.\"\"\"\n    # Make HTTP requests, compute, etc.\n    return OBBject(results={\"key\": \"value\"})\n```\n\n### Free-Form POST Endpoint\n\n```python\n@router.command(methods=[\"POST\"])\nasync def my_post_endpoint(\n    data: BaseModel,  # Body parameters\n    flag: bool = False,  # Query parameters\n) -> OBBject[dict]:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results={\"processed\": True})\n```\n\n### Dependency Injection\n\nUse `routers/depends.py` for shared dependencies:\n\n```python\nfrom typing import Annotated\nimport requests\nfrom fastapi import Depends\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nSession = Annotated[requests.Session, Depends(get_requests_session)]\n```\n\nThen use `session: Session` as a parameter in your router commands.\n\n### Adding Examples\n\n```python\nfrom openbb_core.app.model.example import APIEx, PythonEx\n\n@router.command(\n    model=\"MyModel\",\n    examples=[\n        PythonEx(\n            description=\"Get data for AAPL\",\n            code=[\"obb.my_router.my_command(symbol='AAPL')\"],\n        )\n    ],\n)\n```\n\n---\n\n## Phase 5 — Entry Points in pyproject.toml\n\nThis is **critical** — OpenBB discovers your code entirely through these entry points.\n\n### Provider Entry Point\n\n```toml\n[project.entry-points.\"openbb_provider_extension\"]\nmy_provider = \"my_package.providers.my_provider:my_provider_variable\"\n```\n\nThe variable (`my_provider_variable`) is the `Provider(...)` instance you created\nin Phase 3.\n\n### Router Entry Point\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_router = \"my_package.routers.my_router:router\"\n```\n\nThe entry point **name** (`my_router`) determines the API path prefix.\nFor example, `my_router` means endpoints appear under `/my_router/...`.\n\n### Charting Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_router_views:MyRouterViews\"\n```\n\n### OBBject Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_accessor = \"my_package.obbject.my_obbject:ext\"\nmy_namespace = \"my_package.obbject.my_obbject:class_ext\"\n```\n\n---\n\n## Phase 6 — Chart Views (Optional)\n\nIf you want to add charting support:\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyRouterViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Create a chart for my_command results.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build your chart using fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## Phase 7 — OBBject Accessors (Optional)\n\nResult post-processing extensions that add methods to the `OBBject` response.\n\n### Function Accessor (Property-Like)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"to_csv\", description=\"Convert results to CSV string.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject, **kwargs) -> str:\n    \"\"\"Convert to CSV.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### Class Accessor (Namespaced Methods)\n\n```python\nclass_ext = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@class_ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self, **kwargs):\n        \"\"\"Return a summary.\"\"\"\n        df = self._obbject.to_dataframe()\n        return df.describe()\n```\n\n---\n\n## Phase 8 — Install, Build, and Test\n\n### Install in Development Mode\n\nFrom the generated project root directory:\n\n```\nuv pip install -e . --group dev\n```\n\nThis registers the entry points so OpenBB discovers your extension immediately.\n\n### Build Static Assets with `openbb-build`\n\n**CRITICAL:** After installing a new extension, or after making changes to any\nof the following, you **must** run `openbb-build` before using the Python\ninterface (`obb.<router>.<command>(...)`):\n\n- **Model definitions** — `QueryParams` or `Data` classes (field names, types,\n  defaults, descriptions)\n- **Provider registration** — changes to `fetcher_dict` keys, adding/removing\n  fetchers\n- **Router commands** — adding, removing, or renaming `@router.command()`\n  endpoints\n- **Entry points** — changes to `pyproject.toml` plugin entries\n- **Any importable item in the registration chain** — the `Provider(...)`\n  instance, router module, or model module paths\n\nYou do **not** need to re-run `openbb-build` when changing:\n\n- Logic inside `extract_data` / `aextract_data` / `transform_data` /\n  `transform_query` static methods (the Fetcher method bodies)\n- Utility/helper functions\n- Internal implementation details that don't affect the public schema\n\n```\nopenbb-build\n```\n\nThis regenerates the static assets (type stubs, package interface, provider\nmaps) that the Python interface relies on. Without this step, new or modified\ncommands will not appear on the `obb` object and calls will fail.\n\n**When running as an API server** (e.g., via `uvicorn` or the MCP server),\nstatic assets are **not used** — the API discovers extensions dynamically at\nstartup. You do not need to run `openbb-build` for API-only usage.\n\n### Verify Installation\n\nStart a Python session and check:\n\n```python\nfrom openbb import obb\n# Your new commands should appear:\n# obb.<router_name>.<command_name>(...)\n```\n\nOr start the API server and verify the new endpoints appear.\n\n### Run Tests\n\n```\npytest tests/ -v\n```\n\nThe generated `tests/conftest.py` sets `OPENBB_AUTO_BUILD=true` for proper\ntest environment setup.\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me an application that does X\":\n\n1. **Analyze** — Determine what data sources are needed, what endpoints to expose,\n   and whether to use standard models or custom schemas.\n2. **Scaffold** — Run `openbb-cookiecutter` with a meaningful `project_name`.\n3. **Delete examples** — Remove `example.py` and `ohlc_example.py` from the models\n   directory. Clean up the example router commands.\n4. **Implement models** — Create `QueryParams` + `Data` + `Fetcher` classes for each\n   data source in `providers/<name>/models/`.\n5. **Register fetchers** — Update `providers/<name>/__init__.py` with the `fetcher_dict`.\n6. **Implement router** — Create `@router.command(model=\"...\")` endpoints in\n   `routers/<name>.py`.\n7. **Update entry points** — Ensure `pyproject.toml` entry points match your actual\n   module paths and variable names.\n8. **Add dependencies** — Add any third-party packages to the `[project]`\n   `dependencies` list in `pyproject.toml`.\n9. **Install** — Run `uv pip install -e . --group dev` from the project root.\n10. **Build** — Run `openbb-build` to regenerate static assets for the Python\n    interface. Skip this step if only using the API server.\n11. **Test** — Verify the commands work, then write tests.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/work_with_server/SKILL.md":"---\nname: work_with_server\ndescription: This guide explains how to call tools, interpret responses, discover capabilities, use prompts, and handle errors when interacting with an OpenBB MCP server.\n---\n\n# Working With the OpenBB MCP Server\n\nThis guide explains how to call tools, interpret responses, discover\ncapabilities, use prompts, and handle errors when interacting with an OpenBB\nMCP server.\n\n---\n\n## Tool Discovery Workflow\n\nWhen the server runs with `--tool-discovery`, the OpenBB tools are left out of\nthe tool list, and a small set of discovery tools finds and runs them.\n\nDiscovery holds no per-session state — every client sees the same tool list, and\nnothing needs to be activated before a call.\n\n### Step 1 — List Categories\n\nCall `available_categories` (no arguments) to see what is installed:\n\n```json\n[\n    {\n        \"name\": \"equity\",\n        \"subcategories\": [\n            {\"name\": \"price\", \"tool_count\": 5},\n            {\"name\": \"fundamental\", \"tool_count\": 12}\n        ],\n        \"total_tools\": 17\n    },\n    {\n        \"name\": \"economy\",\n        \"subcategories\": [\n            {\"name\": \"general\", \"tool_count\": 3}\n        ],\n        \"total_tools\": 3\n    }\n]\n```\n\nEach category maps to a top-level API router. Subcategories are nested routers.\n\n### Step 2 — Browse Tools in a Category\n\nCall `available_tools` with a category name:\n\n```json\n// Input\n{\"category\": \"equity\", \"subcategory\": \"price\"}\n\n// Output\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"Get historical price data...\"},\n    {\"name\": \"equity_price_quote\", \"description\": \"Get current price quote...\"}\n]\n```\n\nEach entry has the tool's name and a one-sentence description. The\n`subcategory` argument is optional. Omit it to see all tools in the category.\n\n### Step 3 — Search for Tools\n\nCall `search_tools` with a natural-language query:\n\n```json\n// Input\n{\"query\": \"historical stock prices\"}\n\n// Output - full tool definitions, in the same format as list_tools\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"...\", \"inputSchema\": {\"...\": \"...\"}}\n]\n```\n\nSearch covers the hidden OpenBB tools and returns at most five matches. Use\neach match's `inputSchema` to build the arguments.\n\n### Step 4 — Call a Tool\n\nCall `call_tool` with the tool name and its arguments:\n\n```json\n{\"name\": \"equity_price_historical\", \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"}}\n```\n\n`call_tool` runs the named tool and returns its result. A hidden tool can also\nbe called directly by name, with the same arguments.\n\n### When Discovery Is Disabled\n\nIf the server was started without `--tool-discovery`, the discovery tools are not\navailable. All tools in `default_tool_categories` are listed and called directly;\ntools outside them are not served, and calling one fails with \"Unknown tool\".\n\n---\n\n## openbb-cli Dispatcher Tools\n\nWhen the optional `openbb-mcp-server[cli]` extra is installed (it pulls in\n`openbb-cli`), the server also registers four first-class tools that wrap\n`openbb-cli`'s NDJSON dispatcher protocol. They give an agent the same\n`obb`-namespace command surface that the `openbb` CLI exposes — without\nshelling out or standing up a Python REPL.\n\n### `openbb_dispatch`\n\nExecute a single command and return its serialized result.\n\n```json\n{\n    \"name\": \"openbb_dispatch\",\n    \"arguments\": {\n        \"command\": \"equity.price.historical\",\n        \"params\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"},\n        \"server_url\": null\n    }\n}\n```\n\n| Argument | Type | Notes |\n|---|---|---|\n| `command` | `string` | Dotted command path under the `obb` namespace (e.g. `equity.price.historical`). |\n| `params` | `dict \\| null` | Keyword arguments forwarded to the command. Defaults to `{}`. |\n| `server_url` | `string \\| null` | When set, dispatches against a remote `openbb-platform-api` server (HTTP). When omitted, falls back to `OPENBB_SERVER_URL`, then in-process local dispatch. |\n\nThe result is a dispatcher response. `ok` reports success, `result` holds the\ncommand's OBBject fields (`results`, `provider`, ...), and `error` carries the\n`type` and `message` of a failure instead of raising:\n\n```json\n{\n    \"id\": null,\n    \"ok\": true,\n    \"result\": {\"results\": [{\"date\": \"2025-01-02\", \"close\": 243.85}], \"provider\": \"yfinance\"},\n    \"error\": null\n}\n```\n\nRemote dispatch unwraps a single-row `results` list to the row itself.\n\n### `openbb_batch_dispatch`\n\nExecute many commands concurrently and return results in input order.\n\n```json\n{\n    \"name\": \"openbb_batch_dispatch\",\n    \"arguments\": {\n        \"requests\": [\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"AAPL\"}, \"id\": \"a\"},\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"MSFT\"}, \"id\": \"b\"}\n        ]\n    }\n}\n```\n\nEach entry needs `command` (dotted path); `params` and `id` are optional. The\n`id` is opaque correlation echoed back on the matching response. Failures\nsurface as response objects with `ok=False` and a structured `error` block —\nno exception bubbles up, errors are per-request.\n\n### `openbb_list_commands`\n\nList every command with a one-line description. Takes only the optional\n`server_url`. The response's `result` is a list of `{\"name\", \"description\"}`\nentries. Equivalent to `openbb --list-commands` on the CLI.\n\n### `openbb_describe_command`\n\nReturn one command's parameters and output schema.\n\n```json\n{\n    \"name\": \"openbb_describe_command\",\n    \"arguments\": {\"command\": \"equity.price.historical\", \"provider\": \"yfinance\"}\n}\n```\n\nThe response's `result` has the command `name`, its `parameters` (each with\n`name`, `in`, and `type`, plus `required`, `default`, `choices`, and `help` when\nset), and its `output_schema`. For a command with several providers, pass\n`provider` to get that provider's `parameters` and `output_schema`; without it,\n`result.providers` maps every provider to its own `parameters` and\n`output_schema`. Equivalent to `openbb --describe equity.price.historical`\n(`equity.price.historical:yfinance` for one provider) on the CLI.\n\n### Local vs remote dispatch\n\n| Mode | Trigger | Behavior |\n|---|---|---|\n| Local | No `server_url`, no `OPENBB_SERVER_URL` | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. Single-tenant. |\n| Remote | `server_url` arg or `OPENBB_SERVER_URL` env | `HttpDispatcher` proxies commands to a long-running `openbb-platform-api` server. Multi-tenant; the heavy import lives on the server. |\n\nMode is per-call — different requests in the same session can target different\nservers. Dispatchers are cached for the server's lifetime, so the `import openbb`\ncost (local) and the remote server's OpenAPI download are paid once per target.\n\n---\n\n## Calling Data Tools\n\n### Input Parameters\n\nEvery data tool has a JSON Schema describing its input. A typical tool schema:\n\n```json\n{\n    \"type\": \"object\",\n    \"properties\": {\n        \"symbol\": {\n            \"type\": \"string\",\n            \"description\": \"Symbol to get data for.\"\n        },\n        \"provider\": {\n            \"type\": \"string\",\n            \"enum\": [\"fmp\", \"polygon\", \"yfinance\"],\n            \"description\": \"The provider to use for the query.\"\n        },\n        \"start_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"Start date of the data.\"\n        },\n        \"end_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"End date of the data.\"\n        },\n        \"interval\": {\n            \"type\": \"string\",\n            \"default\": \"1d\",\n            \"description\": \"Time interval of the data.\"\n        }\n    },\n    \"required\": [\"symbol\"]\n}\n```\n\nKey rules:\n\n- **`provider`** — present only on endpoints that use the provider interface.\n  When listed, its enum shows available provider sources. If the endpoint has\n  only one provider, you can omit it and the sole provider is used\n  automatically. When multiple providers are available, select one from the\n  enum. Different providers may return different fields or support different\n  parameters. Endpoints that do not use the provider interface (basic GET/POST\n  routes) have no `provider` parameter at all.\n- **`symbol` formatting** — symbols are case-insensitive. Multiple symbols can\n  be comma-separated: `\"AAPL,MSFT,GOOG\"`.\n- **Dates** — always formatted as `YYYY-MM-DD` strings.\n- **Optional parameters** — have `anyOf` with a `null` type or a `default`\n  value. Omit them to use defaults.\n- **Provider-specific parameters** — some parameters are only relevant for\n  certain providers. The schema unions all of them; irrelevant ones are\n  silently ignored.\n- **Valid values** — never guess a value. A parameter with a fixed set of\n  values has an `enum`. When the values differ by provider, or several can be\n  comma-separated, the description lists them (`Valid values by provider:\n  ...`, `Valid values for provider oecd (comma-separate several): ...`). When\n  another tool lists them, the description names it and its arguments\n  (``Get the valid values from the `derivatives_options_strikes` tool with\n  symbol=<the symbol you pass here>.``) — call that tool first.\n\n### Example Tool Call\n\n```json\n{\n    \"name\": \"equity_price_historical\",\n    \"arguments\": {\n        \"symbol\": \"AAPL\",\n        \"provider\": \"fmp\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-02-01\",\n        \"interval\": \"1d\"\n    }\n}\n```\n\n---\n\n## Understanding the Response\n\nEvery OpenBB tool returns an **OBBject** — a standardized response envelope:\n\n```json\n{\n    \"id\": \"06520558-d54a-7e53-8000-7aafc8a42694\",\n    \"results\": [...],\n    \"provider\": \"fmp\",\n    \"warnings\": null,\n    \"chart\": null,\n    \"extra\": {\n        \"metadata\": {\n            \"arguments\": {...},\n            \"duration\": 565256375,\n            \"route\": \"/equity/price/historical\",\n            \"timestamp\": \"2025-01-15 11:28:57.149548\"\n        }\n    }\n}\n```\n\n### Response Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `id` | `string` | UUID identifying this request |\n| `results` | `list[dict] \\| dict \\| string \\| null` | The actual data. Usually a list of records |\n| `provider` | `string \\| null` | Which provider fulfilled the request |\n| `warnings` | `list[object] \\| null` | Non-fatal warnings from the provider or platform |\n| `chart` | `object \\| null` | Chart artifact if `chart=true` was passed |\n| `extra` | `dict` | Execution metadata and results metadata |\n\n### The `results` Field\n\nThis is the primary data payload. Its structure depends on the endpoint:\n\n**Tabular data** — most common, a list of dictionaries (one per row):\n\n```json\n\"results\": [\n    {\"date\": \"2025-01-02\", \"open\": 150.0, \"high\": 155.0, \"low\": 149.0, \"close\": 153.5, \"volume\": 1000000},\n    {\"date\": \"2025-01-03\", \"open\": 153.0, \"high\": 157.0, \"low\": 152.0, \"close\": 156.2, \"volume\": 1200000}\n]\n```\n\n**Single record** — some endpoints return a single dict:\n\n```json\n\"results\": {\"symbol\": \"AAPL\", \"price\": 185.50, \"change\": 2.30, \"volume\": 45000000}\n```\n\n**Empty results** — when no data is available:\n\n```json\n\"results\": null\n```\n\nor\n\n```json\n\"results\": []\n```\n\n**Field names vary by provider** — different providers may return different\ncolumns for the same endpoint. Always check the keys in the returned records.\n\n### The `warnings` Field\n\nWarnings are non-fatal issues that occurred during execution:\n\n```json\n\"warnings\": [\n    {\n        \"category\": \"OpenBBWarning\",\n        \"message\": \"Parameter 'source' is not supported by fmp. Available for: intrinio.\"\n    }\n]\n```\n\nCommon warning scenarios:\n- Unknown parameters silently ignored by the provider\n- Partial data returned (fewer rows than requested)\n- Provider-specific data quality notes\n\nWhen `warnings` is `null`, no warnings were generated.\n\n### The `extra` Field\n\nContains execution metadata and optional results metadata:\n\n```json\n\"extra\": {\n    \"metadata\": {\n        \"arguments\": {\n            \"provider_choices\": {\"provider\": \"fmp\"},\n            \"standard_params\": {\"symbol\": \"AAPL\", \"start_date\": \"2025-01-01\"},\n            \"extra_params\": {}\n        },\n        \"duration\": 565256375,\n        \"route\": \"/equity/price/historical\",\n        \"timestamp\": \"2025-01-15 11:28:57.149548\"\n    },\n    \"results_metadata\": {\n        \"...provider-specific metadata...\"\n    }\n}\n```\n\n**`metadata`** — always present (unless disabled):\n- `arguments` — the exact parameters used, split into provider choices,\n  standard params, and extra (provider-specific) params\n- `duration` — nanoseconds the request took\n- `route` — the API endpoint path\n- `timestamp` — when the request was made\n\n**`results_metadata`** — present when the provider returns contextual\ninformation about the data (e.g., FRED series metadata, CBOE options\nmetadata). Contents vary by endpoint and provider.\n\n### The `chart` Field\n\nWhen a tool is called with `chart: true` (where supported), the server\nreplaces the Plotly figure with an OpenBB Workspace artifact:\n\n```json\n\"chart\": {\n    \"type\": \"chart\",\n    \"name\": \"AAPL Close\",\n    \"description\": \"Output of the equity_price_historical tool.\",\n    \"uuid\": \"5b0e1c52-6f3e-4a51-9d6f-0f1b6a2f4c11\",\n    \"content\": [{\"date\": \"2025-01-02\", \"close\": 243.85}],\n    \"chart_params\": {\"chartType\": \"line\", \"xKey\": \"date\", \"yKey\": [\"close\"]}\n}\n```\n\n`content` holds the rows the chart is drawn from and `chart_params` says how:\n`chartType` is `line`, `bar`, or `scatter` with `xKey` and `yKey`, or `pie`\nor `donut` with `angleKey` and `calloutLabelKey`. Figures that cannot be\ndrawn that way (surfaces, candlesticks, heatmaps) come back as a `table`\nartifact of their data, without `chart_params`. Tools that return a figure\ndirectly return the artifact as their whole output. Present the artifact\nas it is; do not convert it or re-plot it. Not all endpoints support\ncharting.\n\n---\n\n## Provider Selection\n\n### How Providers Work\n\nEach data endpoint can have multiple provider sources (e.g., FMP, Yahoo Finance,\nPolygon). Providers differ in:\n\n- **Available parameters** — some providers offer extra filtering or options\n- **Returned fields** — column names and data granularity may differ\n- **Rate limits and authentication** — some providers require API keys\n- **Data coverage** — geographic markets, date ranges, asset types\n\n### Choosing a Provider\n\nWhen a tool's input schema includes `provider`, its enum lists all installed\nprovider sources for that endpoint. If there is only one provider, the\nparameter can be omitted — the sole provider is selected automatically.\nWhen multiple providers are available, pick one based on:\n\n1. **Check the enum** — only listed options work\n2. **Consider the data need** — different providers may have different fields\n3. **API key requirements** — some providers need credentials configured in\n   `~/.openbb_platform/user_settings.json`\n\nEndpoints that do not use the provider interface (basic GET/POST routes added\nvia `router.command(methods=[\"GET\"])` or raw FastAPI) have no `provider`\nparameter.\n\nIf a provider fails due to missing credentials, the error message will indicate\nan authentication issue.\n\n### Provider-Specific Parameters\n\nSome parameters only apply to certain providers. For example, the `source`\nparameter might only be available with the `intrinio` provider. Passing it to\n`fmp` generates a warning but does not cause an error.\n\n---\n\n## Working With Prompts\n\nThe server includes a prompt system for accessing documentation, usage guides,\nand analysis frameworks.\n\n### List Available Prompts\n\nCall `list_prompts` (no arguments):\n\n```json\n[\n    {\"name\": \"system_prompt\", \"description\": \"System prompt with guidance...\", \"arguments\": []},\n    {\"name\": \"analyze_stock\", \"description\": \"Analyze a stock.\", \"arguments\": [\n        {\"name\": \"symbol\", \"description\": \"Ticker.\", \"required\": true},\n        {\"name\": \"focus\", \"description\": \"Area.\", \"required\": false}\n    ]}\n]\n```\n\n### Get a Prompt\n\nCall `get_prompt` with the prompt `name` and any `arguments`:\n\n```json\n// Input\n{\"name\": \"analyze_stock\", \"arguments\": {\"symbol\": \"AAPL\"}}\n\n// Output - the rendered prompt\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"Analyze AAPL focusing on fundamentals.\"}\n    ]\n}\n```\n\nPass every argument value as a string (`{\"years\": \"10\"}`, not `{\"years\": 10}`);\nMCP prompt arguments are strings. Prompts without arguments return their\ncontent as-is. Omitted optional arguments fall back to their defaults, and a\nmissing required argument is an error. Skills are resources, not prompts; see\n[Working With Skills](#working-with-skills).\n\n### Prompt Categories by Tag\n\n| Tag | Source | Description |\n|---|---|---|\n| `system` | System prompt file | Server-wide context and instructions |\n| `server` | Server prompts JSON | Reusable analysis frameworks |\n| `route-specific` | Inline on API routes | Endpoint usage guides |\n\n---\n\n## Error Handling\n\n### Error Types\n\n| Scenario | What Happens |\n|---|---|\n| **Invalid parameters** | Error with HTTP 422 details explaining which parameter failed validation |\n| **Missing required parameter** | Error with HTTP 422 indicating the missing field |\n| **Provider authentication failure** | Error with HTTP 401/403 indicating credentials are missing or invalid |\n| **Provider rate limit** | Error with HTTP 429 or provider-specific rate limit message |\n| **No data available** | Successful response with `results: null` or `results: []` |\n| **Tool hidden by discovery** | Tool is not in the tool list; find it with `search_tools` and run it with `call_tool` |\n| **Tool outside the served categories** | \"Unknown tool\" error, by name, through `call_tool`, and through `run_pipeline` |\n| **Unknown tool name** | Standard MCP protocol error |\n| **Category not found** (discovery) | Error listing available categories |\n| **Connection failure** | Error with \"Request error: ...\" |\n\n### Interpreting Empty Results\n\nA response with `results: null` or `results: []` is **not an error** — it\nmeans the provider had no data matching the query. Common causes:\n\n- Date range with no trading days\n- Symbol not covered by the selected provider\n- Data not yet available for the requested period\n\nTry a different provider, adjust the date range, or verify the symbol format.\n\n### Reading Validation Errors\n\nValidation errors (HTTP 422) include detail about what went wrong:\n\n```\nHTTP error 422: Unprocessable Entity - {\"detail\": [{\"loc\": [\"query\", \"symbol\"], \"msg\": \"field required\", \"type\": \"value_error.missing\"}]}\n```\n\nThe `loc` field shows which parameter failed, and `msg` explains why.\n\n---\n\n## Practical Patterns\n\n### Fetching Time Series Data\n\n1. With discovery enabled, find the tool: `search_tools({\"query\": \"historical stock prices\"})`\n2. Call with date range:\n   ```json\n   {\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"start_date\": \"2025-01-01\", \"end_date\": \"2025-02-01\"}\n   ```\n3. Read `results` — each record has `date`, `open`, `high`, `low`, `close`,\n   `volume` (field names depend on provider)\n\n### Comparing Multiple Symbols\n\nPass comma-separated symbols:\n\n```json\n{\"symbol\": \"AAPL,MSFT,GOOG\", \"provider\": \"fmp\"}\n```\n\nResults will contain records for all symbols. Filter by the `symbol` field in\neach record if present, or by the ordering pattern.\n\n### Chaining Tool Calls\n\nUse the output of one tool as input to another:\n\n1. Get peers: `equity_compare_peers({\"symbol\": \"AAPL\", \"provider\": \"fmp\"})`\n2. Extract symbols from `results`\n3. Get quotes: `equity_price_quote({\"symbol\": \"AAPL,PEER1,PEER2\", \"provider\": \"fmp\"})`\n\nWhen a later tool needs the earlier tool's rows rather than a few values picked\nfrom them, use `run_pipeline` (below) so the rows never pass through the\nconversation.\n\n### Running Data-Processing Tools With `run_pipeline`\n\nTechnical, quantitative, and econometrics tools (`technical_rsi`,\n`technical_macd`, ...) take the rows to analyze as a `data` argument. Fetch and\nanalyze in one `run_pipeline` call instead of copying a price history into\ntheir arguments:\n\n```json\n{\n    \"steps\": [\n        {\n            \"id\": \"prices\",\n            \"tool\": \"equity_price_historical\",\n            \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\", \"start_date\": \"2025-01-01\"}\n        },\n        {\"id\": \"rsi\", \"tool\": \"technical_rsi\", \"arguments\": {\"length\": 14}, \"inputs\": {\"data\": \"prices\"}},\n        {\"id\": \"macd\", \"tool\": \"technical_macd\", \"inputs\": {\"data\": \"prices\"}}\n    ],\n    \"outputs\": [\"rsi\", \"macd\"]\n}\n```\n\n- Each step has a `tool`, its `arguments`, an optional `id`, and `inputs`, which\n  maps an argument name to the `id` of an earlier step. That step's `results`\n  fill the argument (its whole output when it has no `results`).\n- A step without an `id` is referred to by its position (`\"0\"`, `\"1\"`, ...).\n- `outputs` lists the steps to return; by default only the last step comes back.\n- The request is validated before any step runs. A failing step is reported as\n  `Step '<id>' (<tool>) failed: ...`.\n- With tool discovery, use `search_tools` to find the tools and their\n  parameters; `run_pipeline` can call tools hidden by discovery by name, but\n  not tools outside the categories the server serves.\n\n### Checking Data Coverage\n\nWhen unsure what providers are available for an endpoint, look at the tool's\ninput schema — the `provider` parameter's `enum` lists all installed options.\n\n### Using Charts\n\nPass `chart: true` to get a pre-built visualization:\n\n```json\n{\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"chart\": true}\n```\n\nThe `chart` field in the response holds a `chart` or `table` artifact (see\nThe `chart` Field). In `run_pipeline`, a step that returns an artifact passes\nits `content` rows to later steps.\n\n---\n\n## User Settings and Defaults\n\nThe server reads user settings from `~/.openbb_platform/user_settings.json`:\n\n### API Keys\n\nProvider credentials are stored under `credentials`:\n\n```json\n{\n    \"credentials\": {\n        \"fmp_api_key\": \"YOUR_KEY\",\n        \"polygon_api_key\": \"YOUR_KEY\"\n    }\n}\n```\n\nWithout the required API key, calls to that provider will fail with an\nauthentication error.\n\n### Default Provider\n\nSet a default provider per endpoint so it is preselected:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\"provider\": \"yfinance\"},\n            \"/economy/cpi\": {\"provider\": \"oecd\"}\n        }\n    }\n}\n```\n\nWhen multiple providers are available, this determines the default selection\nif the parameter is omitted.\n\n### Default Parameters\n\nIndividual parameters can be defaulted so they are applied when not explicitly\npassed:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\n                \"provider\": \"fmp\",\n                \"chart\": true,\n                \"chart_params\": {\n                    \"heikin_ashi\": true,\n                    \"indicators\": {\"sma\": {\"length\": [21, 50]}}\n                }\n            }\n        }\n    }\n}\n```\n\n### Output Preferences\n\nThe `output_type` preference controls how the Python Interface returns data.\nFor MCP, the server always returns the full OBBject JSON regardless of this\nsetting, but it is relevant for the Python Interface:\n\n| Output Type | Description |\n|---|---|\n| `OBBject` | Full response object (default) |\n| `dataframe` | Pandas DataFrame |\n| `numpy` | NumPy array |\n| `dict` | Python dictionary |\n| `polars` | Polars DataFrame |\n| `llm` | JSON-encoded string of results only |\n| `chart` | Chart object |\n\n### LLM Mode\n\nSetting `output_type` to `\"llm\"` in the Python Interface strips everything\nexcept `results` and returns it as a JSON string. This is optimized for token\nefficiency in LLM frameworks. In the REST API / MCP context, the full OBBject\nis always returned.\n\n---\n\n## Working With Skills\n\nSkills are MCP resources exposed at `skill://<name>/SKILL.md` URIs. Discover\nand read them via the standard MCP resource methods.\n\n### Discover Available Skills\n\nCall `list_resources()` (no arguments):\n\n```json\n[\n    {\"uri\": \"skill://develop_extension/SKILL.md\", \"name\": \"develop_extension\"},\n    {\"uri\": \"skill://build_workspace_app/SKILL.md\", \"name\": \"build_workspace_app\"},\n    {\"uri\": \"skill://configure_mcp_server/SKILL.md\", \"name\": \"configure_mcp_server\"},\n    {\"uri\": \"skill://work_with_server/SKILL.md\", \"name\": \"work_with_server\"},\n    {\"uri\": \"skill://use_openbb_cli/SKILL.md\", \"name\": \"use_openbb_cli\"}\n]\n```\n\n### Read a Skill\n\nCall `read_resource()` with the skill URI:\n\n```json\n// Input\n{\"uri\": \"skill://develop_extension/SKILL.md\"}\n\n// Output — full Markdown content of the skill guide\n```\n\n### Supporting Files\n\nSkill directories can contain additional supporting files (e.g. templates,\nexamples). Reference the skill manifest at `skill://<name>/_manifest` to\ndiscover any supporting files packaged alongside the main `SKILL.md`.\n\n---\n\n## Quick Reference\n\n### Admin Tools (Discovery)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `available_categories` | *(none)* | List of categories with subcategories and tool counts |\n| `available_tools` | `category`, `subcategory?` | List of tools with names and descriptions |\n| `search_tools` | `query` | Full definitions of the matching tools |\n| `call_tool` | `name`, `arguments?` | The tool's result |\n\n### Pipeline Tool (Always Listed)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `run_pipeline` | `steps`, `outputs?` | Outputs of the selected steps, keyed by step id |\n\n### OBBject Response Structure\n\n| Field | Always Present | Content |\n|---|---|---|\n| `id` | Yes | Request UUID |\n| `results` | Yes | Data payload (list, dict, string, or null) |\n| `provider` | Yes | Provider name or null |\n| `warnings` | Yes | Warning list or null |\n| `chart` | Yes | Chart artifact or null |\n| `extra` | Yes | Metadata dict (may be empty) |\n","assets/extensions/router.json":"[\n    {\n        \"packageName\": \"openbb-commodity\",\n        \"optional\": true,\n        \"description\": \"Commodity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-crypto\",\n        \"optional\": true,\n        \"description\": \"Cryptocurrency market data.\"\n    },\n    {\n        \"packageName\": \"openbb-currency\",\n        \"optional\": true,\n        \"description\": \"Foreign exchange (FX) market data.\"\n    },\n    {\n        \"packageName\": \"openbb-derivatives\",\n        \"optional\": true,\n        \"description\": \"Derivatives market data.\"\n    },\n    {\n        \"packageName\": \"openbb-econometrics\",\n        \"optional\": true,\n        \"description\": \"Econometrics analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-economy\",\n        \"optional\": true,\n        \"description\": \"Economic data.\"\n    },\n    {\n        \"packageName\": \"openbb-equity\",\n        \"optional\": true,\n        \"description\": \"Equity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-etf\",\n        \"optional\": true,\n        \"description\": \"Exchange Traded Funds market data.\"\n    },\n    {\n        \"packageName\": \"openbb-fixedincome\",\n        \"optional\": true,\n        \"description\": \"Fixed Income market data.\"\n    },\n    {\n        \"packageName\": \"openbb-index\",\n        \"optional\": true,\n        \"description\": \"Indices data.\"\n    },\n    {\n        \"packageName\": \"openbb-news\",\n        \"optional\": false,\n        \"description\": \"Financial market news data.\"\n    },\n    {\n        \"packageName\": \"openbb-quantitative\",\n        \"optional\": true,\n        \"description\": \"Quantitative analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-technical\",\n        \"optional\": true,\n        \"description\": \"Technical Analysis tools.\"\n    }\n]","assets/extensions/provider.json":"[\n    {\n        \"packageName\": \"openbb-bls\",\n        \"optional\": false,\n        \"reprName\": \"Bureau of Labor Statistics' (BLS) Public Data API\",\n        \"description\": \"The Bureau of Labor Statistics' (BLS) Public Data Application Programming Interface (API) gives the public access to economic data from all BLS programs. It is the Bureau's hope that talented developers and programmers will use the BLS Public Data API to create original, inventive applications with published BLS data.\",\n        \"credentials\": [\n            \"bls_api_key\"\n        ],\n        \"website\": \"https://www.bls.gov/developers/api_signature_v2.htm\",\n        \"instructions\": \"Sign up for a free API key here: https://data.bls.gov/registrationEngine/\"\n    },\n    {\n        \"packageName\": \"openbb-cboe\",\n        \"optional\": false,\n        \"reprName\": \"Chicago Board Options Exchange (CBOE)\",\n        \"description\": \"Cboe is the world's go-to derivatives and exchange network,\\ndelivering cutting-edge trading, clearing and investment solutions to people\\naround the world.\",\n        \"credentials\": [],\n        \"website\": \"https://www.cboe.com\"\n    },\n    {\n        \"packageName\": \"openbb-cftc\",\n        \"optional\": false,\n        \"reprName\": \"Commodity Futures Trading Commission (CFTC)\",\n        \"description\": \"The mission of the Commodity Futures Trading Commission (CFTC) is to promote the integrity,\\n    resilience, and vibrancy of the U.S. derivatives markets through sound regulation.\\n    Commitment of Traders reports are sourced from the CFTC Public Reporting API. Swap transaction\\n    and pricing data is sourced from the DTCC Public Price Dissemination service, the swap data\\n    repository that publishes CFTC-jurisdiction transactions. Overnight index swap curves and FX\\n    forward points are constructed from those executed transactions.\",\n        \"credentials\": [\n            \"cftc_app_token\"\n        ],\n        \"website\": \"https://cftc.gov/\",\n        \"instructions\": \"Credentials are not required, but your IP address may be subject to throttling limits\\n    on the Commitment of Traders endpoints. API requests made using an application token are not throttled.\\n    Create an account here: https://evergreen.data.socrata.com/signup\\n    and then generate the app_token by signing in with the credentials\\n    here: https://publicreporting.cftc.gov/profile/edit/developer_settings.\\n    The DTCC Public Price Dissemination endpoints are public and require no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-deribit\",\n        \"optional\": false,\n        \"reprName\": \"Deribit Public Data\",\n        \"description\": \"Deribit is a crypto derivatives exchange listing futures, perpetuals,\\noptions, and spot pairs. This extension covers its public market data API, which\\nneeds no credentials and carries no trading.\",\n        \"credentials\": [],\n        \"website\": \"https://deribit.com/\",\n        \"instructions\": \"This provider needs no credentials and is not meant for trading.\"\n    },\n    {\n        \"packageName\": \"openbb-ecb\",\n        \"optional\": false,\n        \"reprName\": \"European Central Bank (ECB)\",\n        \"description\": \"The ECB Data Portal provides access to all official ECB statistics\\nthrough the SDMX 2.1 REST API, plus euro reference exchange rates, key ECB interest\\nrates, the euro short-term rate (\\u20acSTR), MFI/bank interest rates, the statistical\\nrelease calendar, ECB releases & publications, and the Eurosystem list of eligible\\ncollateral assets.\",\n        \"credentials\": [],\n        \"website\": \"https://data.ecb.europa.eu\"\n    },\n    {\n        \"packageName\": \"openbb-famafrench\",\n        \"optional\": false,\n        \"reprName\": \"Fama-French Research Portfolios and Factors\",\n        \"description\": \"\\n    This provider implements the Fama-French research portfolios and factors data library,\\n    maintained and hosted by Kenneth R. French at Dartmouth College.\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html\"\n    },\n    {\n        \"packageName\": \"openbb-federal-reserve\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve (FED)\",\n        \"description\": \"Access data provided by the Federal Reserve System, the Central Bank of the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.federalreserve.gov/data.htm\"\n    },\n    {\n        \"packageName\": \"openbb-finra\",\n        \"optional\": false,\n        \"reprName\": \"Financial Industry Regulatory Authority (FINRA)\",\n        \"description\": \"FINRA Data provides centralized access to the abundance of data FINRA\\nmakes available to the public, media, researchers and member firms.\",\n        \"credentials\": [],\n        \"website\": \"https://www.finra.org/finra-data\",\n        \"instructions\": \"This provider needs no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-fred\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve Economic Data | St. Louis FED (FRED)\",\n        \"description\": \"Federal Reserve Economic Data is a database maintained by the\\nResearch division of the Federal Reserve Bank of St. Louis that has more than\\n816,000 economic time series from various sources.\",\n        \"credentials\": [\n            \"fred_api_key\"\n        ],\n        \"deprecatedCredentials\": {\n            \"API_FRED_KEY\": \"fred_api_key\"\n        },\n        \"website\": \"https://fred.stlouisfed.org\",\n        \"instructions\": \"Go to: https://fred.stlouisfed.org\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827137-d143ba4c-72cb-467d-a7f4-5cc27c597aec.png)\\n\\nClick on, \\\"My Account\\\", create a new account or sign in with Google:\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827011-65cdd501-27e3-436f-bd9d-b0d8381d46a7.png)\\n\\nAfter completing the sign-up, go to \\\"My Account\\\", and select \\\"API Keys\\\". Then, click on, \\\"Request API Key\\\".\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827577-c869f989-4ef4-4949-ab57-6f3931f2ae9d.png)\\n\\nFill in the box for information about the use-case for FRED, and by clicking, \\\"Request API key\\\", at the bottom of the page, the API key will be issued.\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207828032-0a32d3b8-1378-4db2-9064-aa1eb2111632.png)\"\n    },\n    {\n        \"packageName\": \"openbb-government-us\",\n        \"optional\": false,\n        \"reprName\": \"Congress.gov\",\n        \"description\": \"Legislative data from the U.S. Congress. Bills, bill metadata,\\nsummaries, bill text, amendments, enacted laws, calendars, mandated reports, and\\ncommittees are all sourced from keyless public data (GovInfo bulk data, the\\nGovInfo link service, and the unitedstates dataset). No API key is required for\\nthe 108th Congress onward; older Congresses predate the GovInfo bulk archives\\nand are served from the Congress.gov API, which needs a free API key.\",\n        \"credentials\": [\n            \"congress_gov_api_key\"\n        ],\n        \"website\": \"https://www.govinfo.gov\",\n        \"instructions\": \"Most endpoints use public, keyless data from GovInfo (bulk data\\nand the link service) and the unitedstates dataset, so no credentials are\\nrequired for the 108th Congress (2003) onward.\\n\\nGovInfo does not publish bulk archives for earlier Congresses. Those requests\\nfall back to the Congress.gov API, which reaches back to the 93rd Congress\\n(1973) and requires an API key. Sign up for a free key at\\nhttps://api.congress.gov/sign-up/ and set it as 'congress_gov_api_key'.\"\n    },\n    {\n        \"packageName\": \"openbb-imf\",\n        \"optional\": false,\n        \"reprName\": \"International Monetary Fund (IMF) Data APIs\",\n        \"description\": \"Access International Monetary Fund (IMF) data APIs.\",\n        \"credentials\": [],\n        \"website\": \"https://datahelp.imf.org/knowledgebase/articles/667681-using-json-restful-web-service\"\n    },\n    {\n        \"packageName\": \"openbb-jodi\",\n        \"optional\": false,\n        \"reprName\": \"Joint Organisations Data Initiative (JODI) World Databases\",\n        \"description\": \"The Joint Organisations Data Initiative (JODI) provides free monthly petroleum and natural gas supply, demand, and stock statistics, self-reported by over 100 countries, through the JODI-Oil and JODI-Gas World Databases.\",\n        \"credentials\": [],\n        \"website\": \"https://www.jodidata.org\"\n    },\n    {\n        \"packageName\": \"openbb-nasdaq\",\n        \"optional\": false,\n        \"reprName\": \"Nasdaq\",\n        \"description\": \"Positioned at the nexus of technology and the capital markets, Nasdaq\\nprovides premier platforms and services for global capital markets and beyond with\\nunmatched technology, insights and markets expertise.\",\n        \"credentials\": [],\n        \"website\": \"https://www.nasdaq.com\"\n    },\n    {\n        \"packageName\": \"openbb-oecd\",\n        \"optional\": false,\n        \"reprName\": \"Organization for Economic Co-operation and Development (OECD)\",\n        \"description\": \"Access OECD data via the SDMX REST API.\\nCovers all OECD dataflows including GDP, CPI, unemployment,\\ninterest rates, and hundreds more.\",\n        \"credentials\": [],\n        \"website\": \"https://data-explorer.oecd.org/\"\n    },\n    {\n        \"packageName\": \"openbb-sec\",\n        \"optional\": false,\n        \"reprName\": \"Securities and Exchange Commission (SEC)\",\n        \"description\": \"SEC is the public listings regulatory body for the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.sec.gov/data\"\n    },\n    {\n        \"packageName\": \"openbb-tmx\",\n        \"optional\": false,\n        \"reprName\": \"TMX\",\n        \"description\": \"Unofficial TMX Data Provider Extension\\n    TMX Group Companies\\n        - Toronto Stock Exchange\\n        - TSX Venture Exchange\\n        - TSX Trust\\n        - Montr\\u00e9al Exchange\\n        - TSX Alpha Exchange\\n        - Shorcan\\n        - CDCC\\n        - CDS\\n        - TMX Datalinx\\n        - Trayport\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://www.tmx.com\"\n    },\n    {\n        \"packageName\": \"openbb-us-eia\",\n        \"optional\": false,\n        \"reprName\": \"U.S. Energy Information Administration (EIA) Open Data and API\",\n        \"description\": \"The U.S. Energy Information Administration is committed to its free and open data by making it available through an Application Programming Interface (API) and its open data tools. See https://www.eia.gov/opendata/ for more information.\",\n        \"credentials\": [\n            \"eia_api_key\"\n        ],\n        \"website\": \"https://eia.gov/\",\n        \"instructions\": \"Credentials are required for functions calling the EIA's API.\\n    Register for a free key here: https://www.eia.gov/opendata/register.php\"\n    }\n]","openbb_platform/extensions/crypto/README.md":"# Crypto data extension for OpenBB Platform\n\nThis extension provides a set of commands for crypto data retrieval.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-crypto\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","assets/extensions/obbject.json":"[\n    {\n        \"packageName\": \"openbb-charting\",\n        \"optional\": false,\n        \"description\": \"Create custom charts from OBBject data.\"\n    }\n]","openbb_platform/extensions/currency/README.md":"# OpenBB Currency Extension\n\nThis extension provides currency exchange related data for the OpenBB Platform.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-currency\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","openbb_platform/extensions/commodity/README.md":"# Commodity Extension for OpenBB Platform\n\nThis extension provides a set of commands for commodity-related data.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-commodity\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","openbb_platform/extensions/README.md":"# Extensions\n\nIn this folder you can find the extensions that were created or are supported by OpenBB.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/build_workspace_app/SKILL.md":"---\nname: build_workspace_app\ndescription: This guide covers the full lifecycle of building, running, and serving a custom OpenBB Workspace application from an extension project scaffolded by `openbb-cookiecutter`. It assumes the project shell already exists (see the `develop_extension` skill for scaffolding instructions).\n---\n\n# Build and Run OpenBB Workspace Applications\n\nThis guide covers the full lifecycle of building, running, and serving a custom\nOpenBB Workspace application from an extension project scaffolded by\n`openbb-cookiecutter`. It assumes the project shell already exists\n(see the `develop_extension` skill for scaffolding instructions).\n\n---\n\n## Prerequisites\n\nEnsure the following packages are installed in the active Python environment:\n\n```\npip install openbb-core openbb-platform-api openbb-devtools\n```\n\n- `openbb-core` provides the Router, Provider, OBBject, and Fetcher base classes.\n- `openbb-platform-api` provides the `openbb-api` CLI for serving backends\n  and auto-generating `widgets.json` for OpenBB Workspace.\n- `openbb-devtools` provides `pytest`, cassette recording, and QA utilities.\n\nIf you also need the Python Interface wrapper (the `obb` object), install the\nmain package:\n\n```\npip install openbb --no-deps\n```\n\n---\n\n## Architecture Overview\n\nOpenBB is built on FastAPI and Pydantic. The application has two independent\ninterfaces that share core logic and models:\n\n- **Python Interface** — wraps installed routers into an `obb` Python package\n  with auto-generated docstrings and function signatures. Requires a build step\n  (`openbb-build`) to generate static assets.\n- **REST API** — a FastAPI instance with all installed routers available via\n  HTTP. Import it with `from openbb_core.api.rest_api import app` or launch\n  with `uvicorn openbb_core.api.rest_api:app`.\n\nThe application is the product of all installed extensions. With just `openbb-core`\nthere are no routers or endpoints — users compose their own combinations.\n\n### Key Classes\n\n| Class | Import | Purpose |\n|---|---|---|\n| `Router` | `openbb_core.app.router` | Subclass of `fastapi.APIRouter`; defines commands |\n| `OBBject` | `openbb_core.app.model.obbject` | Standard response object with results, provider, warnings, extra |\n| `Provider` | `openbb_core.provider.abstract.provider` | Registers fetchers for the provider interface |\n| `Fetcher` | `openbb_core.provider.abstract.fetcher` | TET pipeline: Transform query → Extract data → Transform data |\n| `QueryParams` | `openbb_core.provider.abstract.query_params` | Base class for input parameters |\n| `Data` | `openbb_core.provider.abstract.data` | Base class for output data schemas |\n\n---\n\n## Router Extensions (API Endpoints)\n\nRouter extensions are the user-facing endpoints that power the REST API, MCP,\nand Python Interface.\n\n### Creating a Router\n\n```python\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\", description=\"My custom extension.\")\n```\n\nThe top-level API path prefix is determined by the entry point name in\n`pyproject.toml`, not the `prefix` argument. Only use `prefix` for sub-routers.\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.routers.my_router:router\"\n```\n\nCommands appear at `/my_app/...` in the API and `obb.my_app.` in Python.\n\n### Provider Interface Endpoints\n\nConnect a router command to one or more provider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters and the `OBBject.from_query(Query(**locals()))` return are\nmandatory and must be used exactly as shown.\n\n### Basic GET Endpoint\n\n```python\n@router.command(methods=[\"GET\"])\nasync def hello() -> OBBject[str]:\n    \"\"\"OpenBB Hello World.\"\"\"\n    return OBBject(results=\"Hello from the extension!\")\n```\n\n### Basic POST Endpoint\n\n```python\nfrom openbb_core.provider.abstract.data import Data\n\n\n@router.command(methods=[\"POST\"])\nasync def process(data: Data, some_param: str) -> OBBject:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results=data.model_dump())\n```\n\n### Decorator Parameters\n\n`@router.command` accepts:\n- `model` — metamodel name linking to Provider fetchers\n- `methods` — list of HTTP methods, typically `[\"GET\"]` or `[\"POST\"]`\n- `examples` — list of `APIEx` or `PythonEx` for docs\n- `deprecated` — deprecation notice\n- `no_validate` — skip response validation, treat output as `Any`\n- `openapi_extra` — dictionary for inline `widget_config` or `mcp_config`\n\nFields of a response model marked\n`Field(json_schema_extra={\"exclude_from_api\": True})` are dropped from REST\nAPI responses and kept in the Python Interface, as `Chart.fig` is.\n\n### Using FastAPI APIRouter Directly\n\nAccess the underlying FastAPI router via `router._api_router`:\n\n```python\n@router._api_router.get(\"/also_empty\")\nasync def also_empty(param: str) -> str:\n    \"\"\"Also empty.\"\"\"\n    return \"Hello world!\"\n```\n\n---\n\n## From FastAPI — Converting Existing Apps\n\nAny existing FastAPI application can become an OpenBB extension without changing\ncode. Define the entry point in `pyproject.toml` pointed at the FastAPI or\nAPIRouter instance:\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.app:app\"\n```\n\nInstall the package and build static assets:\n\n```\npip install -e .\nopenbb-build\n```\n\nKnown limitations:\n- Authorization hooks are not injected into the Python Interface\n- Request-bound dependencies or those returning None are not injected\n- WebSockets are not supported in the Python Interface\n- Multi-method routes (GET + POST on same path) may not generate correctly\n\n---\n\n## Serving with openbb-api (Workspace Backends)\n\nThe `openbb-api` CLI converts a FastAPI instance into an OpenBB Workspace\nbackend with auto-generated widget definitions.\n\n### Basic Usage\n\n```\n# Start with default OpenBB extensions\nopenbb-api\n\n# Start with a custom FastAPI file\nopenbb-api --app ./my_app.py --host 0.0.0.0 --port 8005\n\n# Factory function pattern\nopenbb-api --app my_app.py:create_app --factory\n\n# Custom FastAPI instance name\nopenbb-api --app my_app.py --name my_app\n```\n\nDefaults are `--host 127.0.0.1 --port 6900`, falling back to the next\navailable port if already in use.\n\n### Key Arguments\n\n| Argument | Description |\n|---|---|\n| `--app` | Path to Python file with a FastAPI instance |\n| `--name` | Name of the FastAPI instance (default: `app`) |\n| `--factory` | Flag if the app name is a factory function |\n| `--editable` | Make `widgets.json` editable at runtime |\n| `--no-build` | Load existing `widgets.json` without checking for updates |\n| `--exclude` | JSON list of API paths to exclude from widgets |\n| `--widgets-json` | Custom path for `widgets.json` |\n| `--apps-json` | Custom path for `workspace_apps.json` |\n\nAll remaining arguments are passed to `uvicorn.run`.\n\n---\n\n## Inline Widget Definitions\n\nWidget properties can be defined inline in your code via `openapi_extra`:\n\n```python\n@app.get(\n    \"/some_endpoint\",\n    openapi_extra={\n        \"widget_config\": {\n            \"name\": \"Custom Widget Name\",\n            \"description\": \"Override docstring description\",\n        }\n    },\n)\nasync def some_endpoint():\n    \"\"\"Description from docstring.\"\"\"\n    pass\n```\n\n### Exclude an Endpoint from Widgets\n\n```python\n@app.get(\n    \"/internal_endpoint\",\n    openapi_extra={\"widget_config\": {\"exclude\": True}},\n)\nasync def internal_endpoint():\n    return [{\"label\": \"Choice 1\", \"value\": \"choice1\"}]\n```\n\n### Dropdown Parameters\n\nDropdowns are auto-generated from `Literal` types:\n\n```python\nfrom typing import Literal\n\n\n@app.get(\"/with_dropdown\")\nasync def with_dropdown(\n    choices: Literal[\"Choice 1\", \"Choice 2\", \"Choice 3\"] = \"Choice 3\",\n):\n    pass\n```\n\n### Column Definitions for Tables\n\nUse Pydantic response models to auto-generate table column definitions:\n\n```python\nimport datetime\nfrom pydantic import BaseModel, Field\n\n\nclass MyData(BaseModel):\n    date: datetime.date = Field(description=\"The date.\")\n    value: float = Field(description=\"The value.\")\n\n\n@app.get(\"/my_data\")\nasync def my_data() -> list[MyData]:\n    \"\"\"Widget with typed columns.\"\"\"\n    return [MyData(date=datetime.date.today(), value=42.0)]\n```\n\n### Widget Types by Return Type\n\n| Return Type | Widget Type |\n|---|---|\n| `list[dict]` or `list[BaseModel]` | Table (AgGrid) |\n| `str` | Markdown |\n| `dict` (with `widget_config.type=\"chart\"`) | Plotly Chart |\n| `MetricResponseModel` | Metric |\n| `PdfResponseModel` | PDF |\n\n### Response Models for Special Widgets\n\n```python\nfrom openbb_platform_api.response_models import MetricResponseModel, PdfResponseModel\n\n\n@app.get(\"/metric\", response_model=MetricResponseModel)\nasync def metric():\n    \"\"\"A metric widget.\"\"\"\n    return dict(label=\"Revenue\", value=12345, delta=5.67)\n\n\n@app.get(\"/pdf\", response_model=PdfResponseModel)\nasync def open_pdf(file_path: str):\n    \"\"\"Open a PDF document.\"\"\"\n    with open(file_path, \"rb\") as f:\n        return dict(content=f.read())\n```\n\n### Plotly Chart Widget\n\n```python\n@app.get(\n    \"/chart\",\n    openapi_extra={\"widget_config\": {\"type\": \"chart\"}},\n)\nasync def chart() -> dict:\n    \"\"\"A chart widget.\"\"\"\n    from plotly.graph_objs import Bar, Layout, Figure\n\n    fig = Figure(\n        data=[Bar(x=[\"A\", \"B\", \"C\"], y=[1, 2, 3])],\n        layout=Layout(title=\"My Chart\", template=\"plotly_dark\"),\n    )\n    return fig.to_plotly_json()\n```\n\n### JSON Schema Extra for Parameters\n\nAnnotate parameters with additional widget configuration:\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query\n\nmy_param: Annotated[\n    str,\n    Query(\n        title=\"My Title\",\n        description=\"Detailed hover text\",\n        json_schema_extra={\n            \"x-widget_config\": {\"optionsEndpoint\": \"/my_choices_endpoint\"}\n        },\n    ),\n]\n```\n\n### Form Input Widget\n\nCreate an input form tied to a table:\n- GET endpoint defines `widget_config.form_endpoint` pointing to the POST route\n- POST route accepts a single Pydantic model argument\n\n---\n\n## OBBject Extensions (Result Post-Processing)\n\nExtend the `OBBject` response with custom methods, accessible in the Python Interface.\n\n### Class Accessor (Namespaced Methods)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self):\n        \"\"\"Return a summary.\"\"\"\n        return self._obbject.to_dataframe().describe()\n```\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_tools = \"my_package.obbject.my_ext:ext\"\n```\n\n### Callable Accessor (Property-Like)\n\n```python\next = Extension(name=\"to_csv\", description=\"Convert results to CSV.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject):\n    \"\"\"Convert to CSV string.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### OBBject Output Conversion Methods\n\nEvery `OBBject` has built-in conversion methods:\n- `to_df()` / `to_dataframe()` — Pandas DataFrame\n- `to_dict(orientation=...)` — Python dict\n- `to_numpy()` — NumPy array\n- `to_polars()` — Polars DataFrame (requires `polars` installed)\n- `model_dump()` — Complete object as dict\n- `model_dump_json()` — Serialized JSON string\n\n---\n\n## OBBject Plugins (Pre-Return Interceptors)\n\nPlugins execute before the response is returned, compatible with both REST API\nand Python Interface. They can conditionally alter the output of any command.\n\n**WARNING**: Plugins are considered potentially dangerous. The environment must\nbe explicitly configured to allow them.\n\nIn `system_settings.json`:\n\n```json\n{\n    \"allow_on_command_output\": true,\n    \"allow_mutable_extensions\": true\n}\n```\n\n### Plugin Configuration\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\nplugin = Extension(\n    name=\"my_plugin\",\n    description=\"Intercept output before return.\",\n    on_command_output=True,\n    command_output_paths=[\"/my_router/my_command\"],\n    immutable=False,\n    results_only=False,\n)\n```\n\nKey parameters:\n- `on_command_output=True` — required for plugins\n- `command_output_paths` — list of endpoint paths to intercept (None = all)\n- `immutable=False` — set to allow modifying the response object\n- `results_only=True` — receive only the `results` portion instead of full OBBject\n\n### Plugin Code\n\n```python\n@plugin.obbject_accessor\ndef my_plugin_func(obbject):\n    \"\"\"Modify or inspect the response before it returns.\"\"\"\n    # Modify obbject directly; do NOT return anything\n    pass\n```\n\n---\n\n## Charting Extensions (Views)\n\nAdd custom chart views to any router endpoint, activated when the user sets\n`chart=True`.\n\n### Structure\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Chart for my_command.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build chart with fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_views:MyViews\"\n```\n\n### Kwargs Available in Views\n\n| Key | Content |\n|---|---|\n| `obbject_item` | Validated results object |\n| `charting_settings` | User charting preferences |\n| `standard_params` | Standard model parameters |\n| `extra_params` | Provider-specific parameters |\n| `provider` | Provider name used |\n| `extra` | Execution metadata |\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## HTTP Requests in Fetchers\n\nUse the built-in utilities instead of creating new clients from scratch.\n\n### Query String Helper\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"exclude_this_param\"])\n```\n\n### Synchronous Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\nresponse = make_request(url, headers=headers, params=params)\n```\n\n### Requests Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\n```\n\n### Asynchronous Requests (AIOHTTP)\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresponse_json = await amake_request(url)\n```\n\n### Multi-URL Async Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nresults = await amake_requests([url1, url2, url3])\n```\n\n### Custom Response Callback\n\n```python\nfrom io import StringIO\nfrom pandas import read_csv\n\nresults = []\n\n\nasync def response_callback(response, _):\n    text = await response.text()\n    data = read_csv(StringIO(text), skiprows=2)\n    results.extend(data.to_dict(\"records\"))\n\n\nawait amake_requests([url1, url2], response_callback=response_callback)\n```\n\n### Async Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with await session.get(url) as response:\n        data = await response.json()\n```\n\n### Async Fetchers\n\nUse `aextract_data` instead of `extract_data` for async fetchers:\n\n```python\n@staticmethod\nasync def aextract_data(\n    query: MyQueryParams,\n    credentials: dict[str, str] | None,\n    **kwargs: Any,\n) -> list[dict]:\n    \"\"\"Async data extraction.\"\"\"\n    ...\n```\n\n---\n\n## Testing\n\n### Built-In Fetcher Test\n\nEvery Fetcher has a `.test()` method for quick validation:\n\n```python\nfrom my_package.providers.my_provider.models.my_model import MyFetcher\n\nfetcher = MyFetcher()\nfetcher.test({\"symbol\": \"AAPL\"}, {})  # Returns None on success\n```\n\n### Unit Tests with Cassettes\n\nInstall dev tools and use `pytest_recorder` for HTTP cassette recording:\n\n```\npip install openbb-devtools\npytest test_my_fetcher.py --record http\n```\n\nSubsequent test runs replay the recorded HTTP interactions.\n\n### Running Tests\n\n```\n# Unit tests only\npytest tests/ -m \"not integration\"\n\n# Integration tests only\npytest tests/ -m integration\n\n# All tests\npytest tests/\n```\n\n### Integration Testing\n\nFor API integration tests, start a local server first:\n\n```\nuvicorn openbb_core.api.rest_api:app --host 0.0.0.0 --port 8000 --reload\n```\n\n---\n\n## Install and Build\n\n### Development Install\n\nFrom the project root:\n\n```\nuv pip install -e . --group dev\n```\n\n### Build Python Interface\n\nAfter installing or removing extensions, regenerate static assets:\n\n```\nopenbb-build\n```\n\n### Verify in Python\n\n```python\nfrom openbb import obb\n# Your commands appear under obb.<router_name>.<command_name>()\n```\n\n### Serve as Workspace Backend\n\n```\nopenbb-api --app ./my_app.py --editable --host 0.0.0.0 --port 6900\n```\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me a Workspace application that does X\":\n\n1. **Scaffold** the project with `openbb-cookiecutter` (see `develop_extension` skill).\n2. **Remove** example files and clean up boilerplate.\n3. **Implement fetchers** for each data source in `providers/<name>/models/`.\n4. **Register fetchers** in `providers/<name>/__init__.py` via `fetcher_dict`.\n5. **Implement router commands** in `routers/<name>.py`.\n6. **Add widget config** via `openapi_extra` for Workspace-specific behavior.\n7. **Update `pyproject.toml`** entry points and dependencies.\n8. **Install** with `uv pip install -e . --group dev`.\n9. **Build** static assets with `openbb-build`.\n10. **Serve** with `openbb-api` and verify widgets in OpenBB Workspace.\n11. **Test** with `pytest` and fetcher `.test()` methods.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/use_openbb_cli/SKILL.md":"---\nname: use_openbb_cli\ndescription: This guide covers the `openbb` command-line surface — running commands, generating `.spec` files, dispatching against precomputed specs, and feeding spec output back into `openbb-mcp` for spec-driven proxy mode.\n---\n\n# Using the openbb-cli\n\n`openbb-cli` ships the `openbb` console-script. It is a self-contained\nfront-end for the same `obb` namespace the MCP server exposes, plus a build\ntool for generating reusable `.spec` files and full installable extension\npackages.\n\nThree console-scripts live on the v5 stack:\n\n| Command | Package | Purpose |\n|---|---|---|\n| `openbb` | `openbb-cli` | Run commands, REPL, generate `.spec` files. |\n| `openbb-mcp` | `openbb-mcp-server` | MCP server launcher (HTTP/SSE/stdio transports). |\n| `openbb-build` | `openbb-core` | Register a newly installed extension into the `obb` namespace. |\n\nThis guide focuses on `openbb`.\n\n---\n\n## Installation\n\n```\npip install openbb-cli\n```\n\nPulls `openbb-core[pandas]`, `prompt-toolkit`, `rich`, and `httpx`.\n\n---\n\n## Execution Modes\n\n`openbb` has six mutually-exclusive modes (the rest of the flags are shared\nacross modes).\n\n| Mode flag | Purpose |\n|---|---|\n| *(default)* | Single-shot: dispatch one dotted command and exit with NDJSON on stdout. |\n| `-i` / `--interactive` | Rich REPL with prompt-toolkit completion. |\n| `--batch` | Read NDJSON requests from stdin; emit NDJSON responses to stdout. |\n| `--generate-spec` | Fetch `openapi.json` from `--server` and write a precomputed `.spec` file to `--output`. |\n| `--generate-extension` | Build a full installable OpenBB Platform extension package from a `.spec` file. |\n| `--list-commands` / `--describe COMMAND[:PROVIDER]` | Print the command catalog or one command's schema as JSON. Needs `--spec` or `--server`. |\n\n### Single-shot\n\n```bash\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n```\n\nOutput is one dispatcher response as a JSON line: `{\"ok\": true, \"result\": ...}`\non success, where `result` holds the command's OBBject fields (`results`,\n`provider`, ...), or `{\"ok\": false, \"error\": {\"type\": ..., \"message\": ...}}` on\nfailure. Unset fields are left out.\nExit code is `0` on success, `1` on a dispatch error, `2` on a usage error.\n\n### Interactive REPL\n\n```bash\nopenbb -i\n```\n\nPrompt-toolkit completion against the resolved command catalog. Same flag\nsurface — `--server` / `--spec` / `--header` etc. work identically.\n\n### Batch (NDJSON in/out)\n\n```bash\necho '{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"AAPL\"},\"id\":\"a\"}\n{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"MSFT\"},\"id\":\"b\"}' \\\n  | openbb --batch\n```\n\nEach input line is one `Request`; each output line is the matching\n`Response`. Use `--batch-concurrency N` (or `OPENBB_CLI_BATCH_CONCURRENCY`) to\ncontrol in-flight parallelism.\n\n---\n\n## Backend Selection\n\nThree backends share the dispatch surface; selection priority is\n`--spec` > `--server` > in-process `obb`.\n\n| Backend | Trigger | Behavior |\n|---|---|---|\n| Local | (default) | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. |\n| Remote HTTP | `--server URL` | `HttpDispatcher` proxies to an `openbb-platform-api` server. The heavy import lives on the server. |\n| Spec-driven | `--spec [NAME=]PATH` | Skips OpenAPI fetch + parse entirely. The spec carries the server URL it was generated against and the per-command HTTP methods. |\n\n```bash\nopenbb --server https://api.example.com economy.gdp\nopenbb --spec /etc/openbb/cli.spec economy.gdp\n```\n\n`--spec` is repeatable. Multiple `NAME=PATH` entries build a multi-spec\ndispatcher and the REPL routes by leading namespace\n(`congress.bill` → `congress` spec, `nyfed.markets.ambs` → `nyfed` spec).\nA single unnamed `--spec PATH` keeps the flat (unprefixed) command surface.\n\n---\n\n## `.spec` Files\n\nA `.spec` file is a precomputed JSON document encoding every command the\nserver exposes — URL path, HTTP method, parameter schema, response schema,\nand a `content_sha256` integrity hash over the canonical-JSON body.\n\n### Generating a spec\n\n```bash\n# From an openbb-platform-api server\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# From a server using a non-default OpenAPI path\nopenbb --generate-spec \\\n  --server https://markets.example.com \\\n  --openapi-path /static/docs/markets-api.yml \\\n  --output markets.spec\n\n# From a Socrata story (no OpenAPI involved — walks the story for datasetUid\n# entries and emits one router namespace per dataset)\nopenbb --generate-spec \\\n  --socrata-story https://data.example.gov/path/to/story.json \\\n  --output socrata.spec\n```\n\n### Using a spec\n\nAfter generation, every dispatch can use the spec to skip OpenAPI fetch +\nparse on every call:\n\n```bash\nopenbb --spec cli.spec economy.gdp\nopenbb --spec cli.spec --list-commands\nopenbb --spec cli.spec --describe equity.price.historical\nopenbb --spec cli.spec --describe equity.price.historical:yfinance\n```\n\nThe spec carries the upstream `base_url` it was generated against, so\n`--server` is not needed alongside `--spec`.\n\n### Filtering commands\n\n`--include` and `--exclude` are repeatable glob patterns over dotted command\nnames, applied by `--generate-extension` (`--generate-spec` always writes every\ncommand). When `--include` is supplied it takes priority — anything not\nmatching is dropped regardless of `--exclude`.\n\n```bash\nopenbb --generate-extension \\\n  --spec cli.spec \\\n  --output ./openbb-equity-subset \\\n  --include 'equity.*' \\\n  --exclude 'equity.fundamentals.*'\n```\n\n---\n\n## Headers and Query Parameters\n\nEvery dispatch (and the OpenAPI fetch during `--generate-spec`) carries the\nsame headers/query-params.\n\n```bash\n# Per-flag, repeatable\nopenbb --server https://api.example.com \\\n  -H 'Authorization: Bearer xxx' \\\n  -H 'X-Tenant: acme' \\\n  -Q 'api_key=secret' \\\n  equity.price.quote --symbol AAPL\n\n# From a JSON file (object of string values)\nopenbb --header-file ./headers.json --query-param-file ./query.json economy.gdp\n```\n\nEnv-var auto-loading: any `OPENBB_HTTP_QUERY_*` env var becomes a query param\n(`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). CLI flags beat env vars.\n\nFor multi-spec dispatchers, prefix a header or query parameter with a\nnamespace to send it to that spec only (`-H 'congress:X-API-Key: xxx'`,\n`-Q 'nyfed:format=json'`), or scope it in the TOML config\n(`[specs.<ns>.headers]`, `[specs.<ns>.query]`).\n\n---\n\n## Configuration File (`openbb.toml`)\n\nFlag defaults can be set via a layered TOML cascade — same pattern openbb-core\nuses. Layers (highest priority last):\n\n```\npyproject.toml [tool.openbb-cli]\n  → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD)\n  → --config PATH (explicit)\n  → .env files\n  → real shell env vars\n```\n\nCLI flags always beat env vars; env vars beat TOML.\n\nThe CLI reads top-level keys of `openbb.toml` (and of `[tool.openbb-cli]` in\n`pyproject.toml`): `server`, `spec`, `openapi-path`, `header-file`,\n`query-param-file`, `auth-hook`, and the `[headers]`, `[query]`,\n`[specs.<ns>]`, and `[settings]` tables. Set the batch concurrency with\n`--batch-concurrency` or `OPENBB_CLI_BATCH_CONCURRENCY`.\n\n```toml\n# openbb.toml\nserver = \"https://api.example.com\"\n\n[headers]\nAuthorization = \"Bearer ...\"\n\n[specs.congress]\npath = \"/etc/openbb/congress.spec\"\n[specs.congress.headers]\n\"X-API-Key\" = \"...\"\n```\n\nHeader and query values are sent as written; they are not expanded from\n`$VAR` references.\n\n```bash\nopenbb --config /etc/openbb/openbb.toml --list-commands\nopenbb --print-config-template      # documented TOML template\nopenbb --show-config                # merged config as JSON\n```\n\n---\n\n## Building Extensions From a Spec\n\n`--generate-extension` builds a full installable OpenBB Platform extension\npackage from exactly one `.spec` file. The project is written to\n`<output>/<project-name>`; the command prints that path with its providers,\nrouters, and install commands.\n\n```bash\nopenbb --generate-extension \\\n  --spec congress.spec \\\n  --output ./extensions \\\n  --provider-name congress_gov \\\n  --project-name openbb-congress-gov \\\n  --package-name openbb_congress_gov\n\npip install -e ./extensions/openbb-congress-gov\nopenbb-build\n```\n\n`--project-name` defaults to `openbb-<provider-name>`, `--package-name` to\n`openbb_<provider-name>`, and `--provider-name` to the name of the `--output`\ndirectory. `--router-name` is accepted but not used by the generator yet.\n\nAfter registration the extension's commands are reachable under the printed\nrouters, as `obb.<router>.*` in Python and `<router>.*` from any backend.\n\n---\n\n## Spec → MCP Round-Trip\n\nThe same `.spec` file can drive the MCP server in spec-driven proxy mode —\nthe heavy `import openbb` lives on the upstream `openbb-platform-api`, the\nMCP launcher only knows the spec.\n\n```bash\n# 1. Generate the spec on the host that already has openbb installed.\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# 2. Hand off the spec to a lightweight MCP container.\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML:\n\n```toml\n# openbb.toml passed via `openbb-mcp --config-file openbb.toml`\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\ncontent_sha256 = \"abc...\"\n\n[mcp.spec.headers]\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\n```\n\nThe MCP server synthesizes a FastAPI proxy app from the spec's commands;\nFastMCP exposes the routes as MCP tools; per-tool dispatch forwards to the\nspec's `base_url` over `aiohttp`. See `configure_mcp_server` for multi-spec\nmounts (`[mcp.spec.NAME]`), per-spec auth/middleware hooks, and the\n`content_sha256` provenance pin.\n\n---\n\n## The Same Commands as MCP Tools\n\nWhen `openbb-cli` is installed next to `openbb-mcp-server`, the MCP server\nexposes the CLI's dispatcher as tools, so an agent gets the same surface:\n\n| CLI | MCP tool |\n|---|---|\n| `openbb <command> --param value` | `openbb_dispatch` (`command`, `params`) |\n| `openbb --batch` | `openbb_batch_dispatch` (`requests`) |\n| `openbb --list-commands` | `openbb_list_commands` |\n| `openbb --describe <command>` | `openbb_describe_command` (`command`, optional `provider`) |\n\nEvery tool takes an optional `server_url`, the counterpart of `--server`, and\nfalls back to `OPENBB_SERVER_URL` like the CLI does.\n\n---\n\n## Useful Environment Variables\n\n| Variable | Purpose |\n|---|---|\n| `OPENBB_SERVER_URL` | Default `--server` URL. |\n| `OPENBB_SPEC_PATH` | Default `--spec` path. |\n| `OPENBB_HTTP_QUERY_*` | Auto-loaded as query parameters (`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). |\n| `OPENBB_CLI_CONFIG` | Default `--config` path. |\n| `OPENBB_CLI_ENV_FILE` | Default `--env-file` path. |\n| `OPENBB_CLI_BATCH_CONCURRENCY` | Default `--batch-concurrency`. |\n| `OPENBB_HEADER_FILE` / `OPENBB_QUERY_PARAM_FILE` | Default `--header-file` / `--query-param-file`. |\n\n`~/.openbb_platform/.env` is always loaded as a fallback `.env` source — real\nshell exports beat both.\n\n---\n\n## Quick Reference\n\n```bash\n# Discover commands (needs --server or --spec)\nopenbb --server URL --list-commands\nopenbb --server URL --describe equity.price.historical\n\n# One-shot dispatch\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n\n# Batch (NDJSON)\ncat requests.ndjson | openbb --batch\n\n# Generate a precomputed spec from a server\nopenbb --generate-spec --server URL --output out.spec\n\n# Dispatch through a precomputed spec\nopenbb --spec out.spec equity.price.quote --symbol AAPL\n\n# Multi-spec routing by leading namespace\nopenbb --spec congress=congress.spec --spec nyfed=nyfed.spec congress.bill\n\n# Generate a full extension package from a spec\nopenbb --generate-extension --spec out.spec --output ./openbb-myprov\n\n# Inspect resolved config\nopenbb --show-config\nopenbb --print-config-template > openbb.toml\n```\n","openbb_platform/extensions/derivatives/README.md":"# derivatives Documentation\n\nPath: `openbb_platform/extensions/derivatives/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/derivatives/README.md)","openbb_platform/extensions/devtools/README.md":"# devtools Documentation\n\nPath: `openbb_platform/extensions/devtools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/devtools/README.md)","openbb_platform/extensions/econometrics/README.md":"# econometrics Documentation\n\nPath: `openbb_platform/extensions/econometrics/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/econometrics/README.md)","openbb_platform/extensions/economy/README.md":"# economy Documentation\n\nPath: `openbb_platform/extensions/economy/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/economy/README.md)","openbb_platform/extensions/equity/README.md":"# equity Documentation\n\nPath: `openbb_platform/extensions/equity/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/equity/README.md)","openbb_platform/extensions/etf/README.md":"# etf Documentation\n\nPath: `openbb_platform/extensions/etf/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/etf/README.md)","openbb_platform/extensions/fixedincome/README.md":"# fixedincome Documentation\n\nPath: `openbb_platform/extensions/fixedincome/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/fixedincome/README.md)","openbb_platform/extensions/index/README.md":"# index Documentation\n\nPath: `openbb_platform/extensions/index/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/index/README.md)","openbb_platform/extensions/mcp_server/README.md":"# mcp_server Documentation\n\nPath: `openbb_platform/extensions/mcp_server/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/README.md)","openbb_platform/extensions/news/README.md":"# news Documentation\n\nPath: `openbb_platform/extensions/news/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/news/README.md)","openbb_platform/extensions/platform_api/README.md":"# platform_api Documentation\n\nPath: `openbb_platform/extensions/platform_api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/README.md)","openbb_platform/extensions/quantitative/README.md":"# quantitative Documentation\n\nPath: `openbb_platform/extensions/quantitative/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/quantitative/README.md)","openbb_platform/extensions/technical/README.md":"# technical Documentation\n\nPath: `openbb_platform/extensions/technical/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/README.md)","openbb_platform/obbject_extensions/charting/README.md":"# charting Documentation\n\nPath: `openbb_platform/obbject_extensions/charting/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/obbject_extensions/charting/README.md)","openbb_platform/extensions/technical/docs/README.md":"# docs Documentation\n\nPath: `openbb_platform/extensions/technical/docs/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/README.md)","openbb_platform/extensions/technical/docs/api-reference.md":"# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/api-reference.md)","openbb_platform/extensions/technical/docs/getting-started.md":"# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/getting-started.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/getting-started.md)","openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json":"# Assets Skill\n\nPath: `openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json)","openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json":"# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json)","openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json":"# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json)","openbb_platform/extensions/technical/docs/user-guides/01-indicators.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/01-indicators.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/01-indicators.md)","openbb_platform/extensions/technical/docs/user-guides/02-signals.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/02-signals.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/02-signals.md)","openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md)","openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md)"},"files":{"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/configure_mcp_server/SKILL.md":"---\nname: configure_mcp_server\ndescription: This guide covers installation, configuration, authentication, tool discovery, prompt management, and client integration for `openbb-mcp-server`.\n---\n\n# Configure and Build the OpenBB MCP Server\n\nThis guide covers installation, configuration, authentication, tool discovery,\nprompt management, and client integration for `openbb-mcp-server`.\n\n---\n\n## Installation\n\n```\npip install openbb-mcp-server\n```\n\nThis installs the `openbb-mcp` CLI command. To include the default OpenBB\nextensions as tools, also install them:\n\n```\npip install openbb\n```\n\nOr install individual extensions:\n\n```\npip install openbb-equity openbb-economy\n```\n\nThe `cli` extra adds `openbb-cli` and its four dispatcher tools\n(`openbb_dispatch`, `openbb_batch_dispatch`, `openbb_list_commands`,\n`openbb_describe_command`); `enable_cli_tools=false` turns them off:\n\n```\npip install \"openbb-mcp-server[cli]\"\n```\n\n---\n\n## Starting the Server\n\n### Default (all installed OpenBB extensions)\n\n```\nopenbb-mcp\n```\n\nDefaults: `--host 127.0.0.1 --port 8001 --transport streamable-http`\n\n### With a Custom FastAPI Application\n\n```\n# File path with default instance name \"app\"\nopenbb-mcp --app ./my_app.py\n\n# Explicit instance name\nopenbb-mcp --app ./my_app.py --name my_app\n\n# Module import syntax\nopenbb-mcp --app my_package.app:my_app\n\n# Factory function pattern\nopenbb-mcp --app ./my_app.py:create_app --factory\n```\n\n### Transport Options\n\n| Transport | Flag | Use Case |\n|---|---|---|\n| `streamable-http` | `--transport streamable-http` | Default. HTTP-based, works with Cursor, VS Code |\n| `sse` | `--transport sse` | Legacy Server-Sent Events. Required by Cline |\n| `stdio` | `--transport stdio` | Standard I/O. Used by Claude Desktop |\n\n---\n\n## CLI Arguments\n\n| Argument | Description | Default |\n|---|---|---|\n| `--app <path>` | Path to FastAPI application file or `module:instance` (mutually exclusive with `--spec`) | OpenBB default app |\n| `--name <name>` | Name of the FastAPI instance or factory function | `app` |\n| `--factory` | Treat `--name` as a factory function | `false` |\n| `--spec <path>` | Path to an `openbb-cli` generated `.spec` file. Synthesizes a FastAPI proxy app whose routes forward to the spec's `base_url`; FastMCP exposes those routes as MCP tools. Mutually exclusive with `--app`. | None |\n| `--config-file <path>` | Path to an `openbb.toml` config file. Highest-priority TOML layer in the cascade. Also honored via `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` / `OPENBB_CONFIG`. | None |\n| `--host <host>` | Server host | `127.0.0.1` |\n| `--port <port>` | Server port | `8001` |\n| `--transport <type>` | `streamable-http`, `sse`, or `stdio` | `streamable-http` |\n| `--default-categories <csv>` | Categories served when tool discovery is off | `all` |\n| `--allowed-categories <csv>` | The only categories served, with or without discovery | All categories |\n| `--tool-discovery` | Hide the tools behind `available_categories`, `available_tools`, `search_tools`, and `call_tool` | Discovery disabled |\n| `--system-prompt <path>` | Path to a `.txt` system prompt file | None |\n| `--server-prompts <path>` | Path to a `.json` server prompts file | None |\n\nEvery other `--key value` is routed by name: an `MCPSettings` field sets that\nsetting (`--enable-cli-tools false`, `--server-auth '[\"user\", \"pass\"]'`),\n`--httpx-<option>` sets an option of the httpx client that calls the API\n(`--httpx-verify false`), and anything else is passed to uvicorn\n(`--log-level debug`).\n\n---\n\n## Spec-Driven Proxy Mode\n\nInstead of importing a FastAPI app in-process, the launcher can synthesize one\nfrom an `openbb-cli` generated `.spec` file. Each command in the spec becomes\na FastAPI route that proxies to the spec's recorded `base_url`. FastMCP walks\nthose routes and exposes them as MCP tools — same tool surface as a normal\n`--app` install, but the heavy `import openbb` lives on the upstream server.\n\n### Generating a spec\n\nThe spec is produced by `openbb-cli`:\n\n```bash\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n```\n\nThis fetches `/openapi.json` from the server and writes a precomputed `.spec`\nfile with every command's URL path, method, parameter schema, and a\n`content_sha256` integrity hash.\n\n### Single-spec launch\n\n```bash\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML (`--config-file openbb.toml`):\n\n```toml\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\n# Optional: override the spec's recorded base_url at deploy time.\nbase_url = \"https://upstream.example.com\"\n# Optional: pinned SHA-256. When set, the launcher recomputes the spec's\n# canonical-JSON hash and refuses to start unless it matches — protects\n# against silent file drift when the spec is fetched at deploy time.\ncontent_sha256 = \"abc123...\"\n\n[mcp.spec.headers]\n# Static headers injected on every proxied upstream request.\n# ``$VAR`` / ``${VAR}`` are substituted from the current environment;\n# entries with unresolved variables are skipped with a warning.\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\nX-API-Key     = \"$OPENBB_UPSTREAM_KEY\"\n```\n\n`--spec` and `--app` are mutually exclusive.\n\n### Multi-spec mounting\n\nEach `[mcp.spec.NAME]` subtable mounts an independent spec under its own\nprefix. FastMCP turns the union of all mounted routes into MCP tools; per-spec\nhooks fire on every tool dispatch into that spec's mount.\n\n```toml\n[mcp.spec.equity]\npath = \"/etc/openbb/equity.spec\"\nmount = \"/equity\"                          # optional; defaults to \"/<name>\"\ncontent_sha256 = \"abc...\"\n[mcp.spec.equity.headers]\nAuthorization = \"Bearer $EQUITY_TOKEN\"\n[mcp.spec.equity.auth]\nhooks = [\"my_pkg.auth:equity_token_check\"]\n[mcp.spec.equity.middleware]\nhooks = [\"my_pkg.middleware:equity_rate_limit\"]\n\n[mcp.spec.crypto]\npath = \"/etc/openbb/crypto.spec\"\n[mcp.spec.crypto.headers]\nX-API-Key = \"$CRYPTO_KEY\"\n```\n\n---\n\n## openbb.toml Cascade\n\nThe launcher reads the same layered TOML cascade openbb-core ships with,\nlowest priority first:\n\n```\npyproject.toml [tool.openbb] (so [tool.openbb.mcp]) → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD) → --config-file PATH (explicit)\n```\n\nEvery layer is optional, and `.openbb.toml` works in place of `openbb.toml`.\n`--config-file` (or `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` /\n`OPENBB_CONFIG`) is the highest-priority TOML layer. `[mcp]` values are\napplied like CLI flags: a CLI flag beats them, and they beat `OPENBB_MCP_*`\nenvironment variables. `[env]` entries never replace a variable already set\nin the shell or a `.env` file.\n\n### Top-level tables\n\n| Table | Purpose |\n|---|---|\n| `[mcp]` | Default values for any of the `--*` CLI flags (CLI wins). |\n| `[mcp.spec]` | Single-spec proxy mode (path, base_url, content_sha256, headers). |\n| `[mcp.spec.NAME]` | Multi-spec mount (path, base_url, content_sha256, headers, auth, middleware). |\n| `[mcp.auth]` | Top-level auth hook list. |\n| `[mcp.middleware]` | Top-level middleware hook list. |\n| `[env]` | Env vars pushed into `os.environ` before any heavy import runs. Existing shell env vars are NEVER clobbered. Values support `$VAR` / `${VAR}` substitution. |\n\n### Auth and middleware hooks\n\nBoth `[mcp.auth]` and `[mcp.middleware]` accept a `hooks` list of\n`module:attr` entry points. Each hook is a 2-arg async function\n`async def hook(request, call_next)` and is wrapped as a\n`Middleware(BaseHTTPMiddleware, dispatch=fn)` on the MCP server's ASGI stack.\n\n```toml\n[mcp.auth]\nhooks = [\"my_pkg.auth:bearer_token_check\"]\n\n[mcp.middleware]\nhooks = [\"my_pkg.middleware:rate_limit\", \"my_pkg.middleware:request_logger\"]\n```\n\nAuth hooks register before middleware hooks; within each list, registration\norder matches TOML order. Per-spec hooks (under `[mcp.spec.NAME.auth]` /\n`[mcp.spec.NAME.middleware]`) are scoped to that spec's mounted sub-app.\nThe top-level hooks wrap the HTTP transports (`streamable-http`, `sse`);\nthe `stdio` transport has no HTTP stack, so they do not run there.\n\n### Env injection\n\n```toml\n[env]\nOPENBB_MCP_HOST = \"0.0.0.0\"\n# Values support shell-style $VAR / ${VAR} substitution.\nOPENBB_GITHUB_TOKEN = \"$GITHUB_TOKEN\"\n```\n\nUseful in containers to inject `OPENBB_*` keys without shell exports. Any key\nalready present in the real environment is preserved.\n\n---\n\n## Configuration Precedence\n\nSettings are resolved in this order (highest priority first):\n\n1. **CLI arguments** — command-line flags\n2. **`[mcp]` table of the `openbb.toml` cascade** — applied like CLI flags; `--config-file PATH` > project `openbb.toml` > user-global `~/.openbb_platform/openbb.toml` > `[tool.openbb.mcp]` in `pyproject.toml`\n3. **Environment variables** — `OPENBB_MCP_` prefixed, including those set by `[env]`\n4. **JSON settings file** — `~/.openbb_platform/mcp_settings.json`, written with the defaults on first run\n5. **Defaults** — built-in MCPSettings defaults\n\n### Config File Example\n\nCreate `~/.openbb_platform/mcp_settings.json`:\n\n```json\n{\n    \"name\": \"My MCP Server\",\n    \"default_tool_categories\": [\"equity\", \"economy\"],\n    \"enable_tool_discovery\": false,\n    \"describe_responses\": false,\n    \"system_prompt_file\": \"/path/to/system_prompt.txt\",\n    \"server_prompts_file\": \"/path/to/prompts.json\"\n}\n```\n\n### Environment Variables\n\nAll settings map to `OPENBB_MCP_` prefixed environment variables:\n\n```\nOPENBB_MCP_NAME=\"My MCP Server\"\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ENABLE_TOOL_DISCOVERY=true\nOPENBB_MCP_SYSTEM_PROMPT_FILE=\"/path/to/prompt.txt\"\nOPENBB_MCP_SERVER_PROMPTS_FILE=\"/path/to/prompts.json\"\n```\n\n---\n\n## Settings Reference\n\n### Server Identity\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `name` | `OPENBB_MCP_NAME` | `str` | `\"OpenBB MCP\"` |\n| `description` | `OPENBB_MCP_DESCRIPTION` | `str` | Auto-generated |\n| `version` | `OPENBB_MCP_VERSION` | `str \\| None` | `None` |\n| `instructions` | `OPENBB_MCP_INSTRUCTIONS` | `str \\| None` | `None` (the system prompt, when one is loaded) |\n| `mask_error_details` | `OPENBB_MCP_MASK_ERROR_DETAILS` | `bool \\| None` | `None` |\n\n### Tool Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `default_tool_categories` | `OPENBB_MCP_DEFAULT_TOOL_CATEGORIES` | `list[str]` | `[\"all\"]` |\n| `allowed_tool_categories` | `OPENBB_MCP_ALLOWED_TOOL_CATEGORIES` | `list[str] \\| None` | `None` |\n| `enable_tool_discovery` | `OPENBB_MCP_ENABLE_TOOL_DISCOVERY` | `bool` | `false` |\n| `enable_cli_tools` | `OPENBB_MCP_ENABLE_CLI_TOOLS` | `bool` | `true` |\n| `list_page_size` | `OPENBB_MCP_LIST_PAGE_SIZE` | `int \\| None` | `None` |\n| `describe_responses` | `OPENBB_MCP_DESCRIBE_RESPONSES` | `bool` | `false` |\n| `api_prefix` | `OPENBB_MCP_API_PREFIX` | `str \\| None` | `None` |\n\n`enable_cli_tools` registers the `openbb-cli` dispatcher tools when\n`openbb-cli` is installed. `mask_error_details` hides exception details from\nclients.\n\n### Prompt Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `system_prompt_file` | `OPENBB_MCP_SYSTEM_PROMPT_FILE` | `str \\| None` | `None` |\n| `server_prompts_file` | `OPENBB_MCP_SERVER_PROMPTS_FILE` | `str \\| None` | `None` |\n| `default_skills_dir` | `OPENBB_MCP_DEFAULT_SKILLS_DIR` | `str \\| None` | Built-in skills dir |\n| `skills_reload` | `OPENBB_MCP_SKILLS_RELOAD` | `bool` | `false` |\n| `skills_providers` | `OPENBB_MCP_SKILLS_PROVIDERS` | `list[str] \\| None` | `None` |\n\n### HTTP Transport\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `uvicorn_config` | `OPENBB_MCP_UVICORN_CONFIG` | `dict` | `{\"host\": \"127.0.0.1\", \"port\": \"8001\"}` |\n| `httpx_client_kwargs` | `OPENBB_MCP_HTTPX_CLIENT_KWARGS` | `dict` | `{}` |\n\n`httpx_client_kwargs` configures the httpx client that sends tool calls to the\nAPI (headers, timeout, TLS verification).\n\n### Duplicate Handling\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `on_duplicate` | `OPENBB_MCP_ON_DUPLICATE` | `str \\| None` | `None` |\n\nOptions: `\"warn\"`, `\"error\"`, `\"replace\"`, `\"ignore\"`\n\n### Module Exclusion\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `module_exclusion_map` | `OPENBB_MCP_MODULE_EXCLUSION_MAP` | `dict \\| None` | `None` |\n\nMaps route path segments to Python modules: routes under a segment are hidden\nwhile its module is imported. `None` uses `{\"coverage\": \"openbb_core\"}`, which\nhides the platform's `coverage` routes; `{}` hides nothing. Data-processing\nextensions (`technical`, `quantitative`, `econometrics`) are exposed when\ninstalled. Pair them with `run_pipeline`, or leave them out with\n`allowed_tool_categories` (or, with tool discovery off,\n`default_tool_categories`).\n\n---\n\n## Authentication\n\nThree authentication modes are available.\n\n### Server-Side Authentication\n\nProtect incoming MCP requests with a Bearer token:\n\n```json\n{\n    \"server_auth\": [\"username\", \"password\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"username\", \"password\"]'\n```\n\nClients must include `Authorization: Bearer <base64(username:password)>` in\ntheir requests. The token is base64-encoded `username:password`.\n\n### Client-Side Authentication\n\nAuthenticate outbound requests to downstream services:\n\n```json\n{\n    \"client_auth\": [\"api_user\", \"api_key\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_CLIENT_AUTH='[\"api_user\", \"api_key\"]'\n```\n\nThis passes `auth=(user, pass)` to the httpx client used for internal requests.\n\n### Programmatic Authentication\n\nWhen using the server as a library, pass a custom `AuthProvider` to\n`create_mcp_server()`:\n\n```python\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\nsettings = MCPSettings()\nmcp = create_mcp_server(settings, my_fastapi_app, auth=my_auth_provider)\n```\n\n---\n\n## Tool Discovery\n\nWhen `enable_tool_discovery` is `true`, the OpenBB tools are left out of the\ntool list and these tools are available to the agent instead:\n\n| Tool | Description |\n|---|---|\n| `available_categories` | Lists all tool categories with tool counts |\n| `available_tools` | Lists the tools in a category with one-sentence descriptions |\n| `search_tools` | Finds tools by a natural-language query and returns their full definitions |\n| `call_tool` | Runs any tool by name with its arguments |\n\nDiscovery holds no per-session state: every client sees the same tool list, and\na hidden tool can also be called directly by name. It behaves the same over every\ntransport and protocol version, so the server is safe for multi-user deployments.\n\n### Choosing the Served Categories\n\nUse `allowed_tool_categories` to serve only some categories, with or without\ndiscovery:\n\n```\nopenbb-mcp --allowed-categories equity,economy,crypto\n```\n\nRoutes in other categories are never registered: they can't be listed,\nsearched, or called, by name, through `call_tool`, or through `run_pipeline`.\n`None` or `all` serves every category.\n\nWithout discovery, `default_tool_categories` also limits the served tools:\n\n```\nopenbb-mcp --default-categories equity,economy\n```\n\nTools outside these categories are registered but disabled, so they are not\nlisted and calling them fails with \"Unknown tool\". A route's\n`mcp_config.enable` overrides this per tool. Nothing can re-enable a disabled\ntool at runtime. With discovery on, `default_tool_categories` is ignored and\nevery allowed tool is reachable through `search_tools` and `call_tool`.\n\n### Enabling Discovery\n\n```\nopenbb-mcp --tool-discovery\n```\n\nWithout discovery, all tools in `default_tool_categories` are listed and the\ndiscovery tools are not registered.\n\n---\n\n## Tool Naming Convention\n\nTools are named from their API route path after stripping the API prefix:\n\n| Route Path | Tool Name |\n|---|---|\n| `/equity/price/historical` | `equity_price_historical` |\n| `/economy/cpi` | `economy_cpi` |\n| `/my_app/process` | `my_app_process` |\n\nThe first path segment is the **category** and the second, on paths with three\nor more segments, the **subcategory**; otherwise the subcategory is\n`\"general\"`. `{placeholder}` segments are skipped, and a single-segment path\nrepeats its segment (`/hello` is `hello_hello`). When one path serves several\nmethods, the non-GET tools get a `_<method>` suffix (`demo_items` and\n`demo_items_post`). A route's `mcp_config.name` replaces the generated name.\n\n---\n\n## Prompt System\n\nThe server supports three layers of prompts, all accessible via the\n`list_prompts` and `get_prompt` tools. Bundled skills are served alongside them\nas resources.\n\n### 1. System Prompt (tag: `system`)\n\nA plain text file loaded once at startup. Also exposed as\n`resource://system_prompt`.\n\n```\nopenbb-mcp --system-prompt /path/to/system_prompt.txt\n```\n\n### 2. Server Prompts JSON (tag: `server`)\n\nA JSON file defining reusable prompts with optional arguments:\n\n```json\n[\n    {\n        \"name\": \"analyze_stock\",\n        \"description\": \"Framework for analyzing a stock.\",\n        \"content\": \"Analyze {symbol} focusing on {aspect}.\",\n        \"arguments\": [\n            {\n                \"name\": \"symbol\",\n                \"type\": \"str\",\n                \"description\": \"Ticker symbol\"\n            },\n            {\n                \"name\": \"aspect\",\n                \"type\": \"str\",\n                \"default\": \"fundamentals\",\n                \"description\": \"Analysis focus area\"\n            }\n        ],\n        \"tags\": [\"analysis\"]\n    }\n]\n```\n\nArgument types: `str`, `int`, `float`, `bool`, `list`, `dict`, `any`\n\nArguments with a `default` value are optional; those without are required.\nClients pass prompt arguments as strings (`{\"aspect\": \"valuation\"}`,\n`{\"years\": \"10\"}`).\n\n```\nopenbb-mcp --server-prompts /path/to/prompts.json\n```\n\n### 3. Inline Prompts (tag: route-specific)\n\nDefine prompts directly on FastAPI routes via `openapi_extra`:\n\n```python\n@router.command(\n    methods=[\"GET\"],\n    openapi_extra={\n        \"mcp_config\": {\n            \"prompts\": [\n                {\n                    \"name\": \"usage_guide\",\n                    \"description\": \"How to use this endpoint.\",\n                    \"content\": \"To analyze {symbol}, call this endpoint with...\",\n                }\n            ]\n        }\n    },\n)\nasync def my_endpoint(symbol: str) -> OBBject: ...\n```\n\nEach `{placeholder}` in `content` becomes an argument: a prompt argument of the\nsame name, else the endpoint parameter, else a required string. Arguments\nwithout a default are required. The rendered prompt starts with\n`Use the tool, <tool name>, to perform the following task.`, naming the\nroute's tool (including a name set with `mcp_config.name`), and the tool's\ndescription lists its prompts.\n\n### 4. Bundled Skills (Resources)\n\nSkill guides are exposed as MCP resources discoverable via `list_resources()`.\nEach skill is accessible at a `skill://<name>/SKILL.md` URI.\n\n```\n# Discover available skills\nlist_resources()  # returns skill://develop_extension/SKILL.md, etc.\n\n# Read a specific skill\nread_resource(\"skill://configure_mcp_server/SKILL.md\")\n```\n\nCustom skills directory:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=/path/to/my/skills\n```\n\nSet to empty string to disable bundled skills:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=\"\"\n```\n\n### Skills Reload\n\nEnable hot-reload of skill files without restarting the server (useful during development):\n\n```json\n{\n    \"skills_reload\": true\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SKILLS_RELOAD=true\n```\n\n### Vendor Skills Providers\n\nLoad skill directories from well-known vendor locations (e.g. `~/.claude/skills/`).\nSets the `skills_providers` list in `mcp_settings.json`:\n\n```json\n{\n    \"skills_providers\": [\"claude\", \"cursor\"]\n}\n```\n\nOr via environment variable (comma-separated):\n\n```\nOPENBB_MCP_SKILLS_PROVIDERS=\"claude,cursor\"\n```\n\nSupported provider names:\n\n| Name | Default Directory |\n|---|---|\n| `claude` | `~/.claude/skills/` |\n| `cursor` | `~/.cursor/skills/` |\n| `vscode` / `copilot` | `~/.copilot/skills/` |\n| `codex` | `/etc/codex/skills/` + `~/.codex/skills/` |\n| `gemini` | `~/.gemini/skills/` |\n| `goose` | `~/.config/agents/skills/` |\n| `opencode` | `~/.config/opencode/skills/` |\n\n---\n\n## Inline MCP Configuration (MCPConfigModel)\n\nControl how individual routes appear in the MCP server via `openapi_extra`:\n\n```python\n@app.get(\n    \"/my_endpoint\",\n    openapi_extra={\n        \"mcp_config\": {\n            \"expose\": True,\n            \"mcp_type\": \"tool\",\n            \"methods\": [\"GET\"],\n            \"exclude_args\": [\"internal_param\"],\n            \"prompts\": []\n        }\n    },\n)\n```\n\nThe configuration is read from `openapi_extra[\"mcp_config\"]`, or from\n`openapi_extra[\"x-mcp\"]` when `mcp_config` is absent. An invalid configuration\nis logged and ignored for that route.\n\n### MCPConfigModel Fields\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `expose` | `bool \\| None` | `None` | Set `false` to hide route from MCP |\n| `mcp_type` | `str \\| None` | `None` | `\"tool\"`, `\"resource\"`, or `\"resource_template\"` |\n| `methods` | `list[str] \\| None` | `None` | The route's HTTP methods to serve; the others are left out (`\"*\"` for all) |\n| `exclude_args` | `list[str] \\| None` | `None` | Arguments left out of the tool schema; each needs a default, which then applies |\n| `name` | `str \\| None` | `None` | Tool name, replacing the one built from the path |\n| `tags` | `list[str] \\| None` | `None` | Tags added to the tool next to its category |\n| `enable` | `bool \\| None` | `None` | With discovery off, serve (`true`) or hide (`false`) the tool regardless of `default_tool_categories` |\n| `describe_responses` | `bool \\| None` | `None` | Keep (`true`) or cut (`false`) the response documentation in the tool description |\n| `mime_type` | `str \\| None` | `None` | MIME type of a route served as a resource |\n| `prompts` | `list[dict]` | `[]` | Inline prompt definitions |\n\n---\n\n## What Agents Get\n\nBeyond one tool per route, every server provides:\n\n- **`run_pipeline`** — chains tools on the server, feeding an earlier step's\n  `results` (or a chart artifact's rows) into a later step's argument, so\n  data-processing tools (`technical_*`, `quantitative_*`, `econometrics_*`)\n  receive price histories without them passing through the conversation. It\n  calls only tools the server serves.\n- **Parameter choices** — provider `choices` and widget `x-widget_config`\n  options become an `enum`, or a \"Valid values\" note in the description, and\n  an `optionsEndpoint` names the tool that lists the values. Options endpoints\n  hidden from the API schema have no tool to name.\n- **Chart artifacts** — a Plotly figure in a result (a figure route, or an\n  OBBject `chart` from `chart: true`) is replaced by an OpenBB Workspace\n  artifact: a `chart` artifact with rows and `chart_params` for line, bar,\n  scatter, pie, and donut figures, else a `table` artifact of the figure's\n  data.\n- **Prompt and resource tools** — `list_prompts`, `get_prompt`,\n  `list_resources`, and `read_resource` for clients without native prompt or\n  resource support.\n- **`openbb-cli` dispatcher tools** — with the `cli` extra and\n  `enable_cli_tools`.\n- **`install_skill`** — writes a skill into the bundled or a vendor skills\n  directory. Skill names are limited to lowercase letters, digits,\n  underscores, and hyphens, and file paths must stay inside the skill\n  directory, but any connected client can call it. When clients are\n  untrusted, keep the skills directories read-only for the server process or\n  require `server_auth`.\n\n---\n\n## Client Configuration Examples\n\n### Claude Desktop (stdio transport)\n\nIn Claude Desktop's MCP config file:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"--with\", \"openbb\",\n                \"openbb-mcp\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\nFor a custom app:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"openbb-mcp\",\n                \"--app\", \"./my_app.py\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\n### Cursor (streamable-http)\n\n1. Start the server: `openbb-mcp`\n2. In Cursor's `mcp.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\"\n        }\n    }\n}\n```\n\n### VS Code (streamable-http)\n\n1. Enable MCP in VS Code settings (Settings → Chat → MCP)\n2. Start the server: `openbb-mcp`\n3. Open Command Palette → \"MCP: Add Server\" → HTTP\n4. Enter URL: `http://127.0.0.1:8001/mcp`\n\nFor the Cline VS Code extension, use `--transport sse`:\n\n```\nopenbb-mcp --transport sse\n```\n\n### With Authentication\n\nStart with server auth enabled:\n\n```\nopenbb-mcp --host 0.0.0.0 --port 8001\n```\n\nWith `mcp_settings.json`:\n\n```json\n{\n    \"server_auth\": [\"admin\", \"secretpass\"]\n}\n```\n\nClients include the Bearer token in their configuration:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\",\n            \"headers\": {\n                \"Authorization\": \"Bearer YWRtaW46c2VjcmV0cGFzcw==\"\n            }\n        }\n    }\n}\n```\n\nThe token value is `base64(\"admin:secretpass\")`.\n\n---\n\n## Advanced Configuration\n\n### Lists and Dicts in Environment Variables\n\nLists can be passed as comma-separated strings:\n\n```\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ALLOWED_TOOL_CATEGORIES=\"equity,economy\"\n```\n\nDicts and tuples must be JSON-encoded strings:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"user\", \"pass\"]'\nOPENBB_MCP_UVICORN_CONFIG='{\"host\": \"0.0.0.0\", \"port\": 8001, \"log_level\": \"info\"}'\nOPENBB_MCP_HTTPX_CLIENT_KWARGS='{\"timeout\": 30, \"verify\": false}'\n```\n\n### SSL / HTTPS\n\nPass SSL config via Uvicorn:\n\n```\nopenbb-mcp --ssl-keyfile /path/to/key.pem --ssl-certfile /path/to/cert.pem\n```\n\nOr in the config file:\n\n```json\n{\n    \"uvicorn_config\": {\n        \"host\": \"0.0.0.0\",\n        \"port\": 443,\n        \"ssl_keyfile\": \"/path/to/key.pem\",\n        \"ssl_certfile\": \"/path/to/cert.pem\"\n    }\n}\n```\n\n### Using as a Library\n\n```python\nimport asyncio\nfrom fastapi import FastAPI\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\napp = FastAPI()\n\n\n@app.get(\"/hello\")\nasync def hello():\n    return \"Hello World\"\n\n\nsettings = MCPSettings(\n    name=\"My Custom MCP\",\n    default_tool_categories=[\"all\"],\n    enable_tool_discovery=False,\n)\n\nmcp = create_mcp_server(settings, app)\nmcp.run(transport=\"streamable-http\")\n```\n\n---\n\n## Workflow Summary\n\nTo configure and deploy an OpenBB MCP server:\n\n1. **Install**: `pip install openbb-mcp-server` (plus any desired OpenBB extensions).\n2. **Configure**: Create `~/.openbb_platform/mcp_settings.json` with desired settings.\n3. **Add prompts**: Write a system prompt file and/or server prompts JSON.\n4. **Start**: Run `openbb-mcp` with appropriate CLI flags.\n5. **Connect**: Configure your MCP client (Claude Desktop, Cursor, VS Code) with the server URL or stdio command.\n6. **Discover**: Use `available_categories`, `search_tools`, and `call_tool` to find and run tools.\n7. **Iterate**: Adjust settings, add inline `mcp_config` to routes, add skill files.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/develop_extension/SKILL.md":"---\nname: develop_extension\ndescription: This is a complete guide for creating a new OpenBB Platform extension from scratch. Follow every phase in order. When the user says \"build me an application that does X\", use this guide to scaffold, implement, install, and verify the extension.\n---\n\n# Build an OpenBB Platform Extension\n\nThis is a complete guide for creating a new OpenBB Platform extension from scratch.\nFollow every phase in order. When the user says \"build me an application that does X\",\nuse this guide to scaffold, implement, install, and verify the extension.\n\n---\n\n## Phase 1 — Scaffold the Project\n\nThe `openbb-cookiecutter` package must be installed in the active Python environment\nbefore scaffolding. Install it with:\n\n```\npip install openbb-cookiecutter\n```\n\nThen run the CLI to generate the project skeleton.\nAll variables have sensible defaults; override only what you need.\n\n### Template Variables\n\n| Variable | Default | Description |\n|---|---|---|\n| `full_name` | `\"Hello World\"` | Author name |\n| `email` | `\"hello@world.com\"` | Author email |\n| `project_name` | `\"OpenBB Python Extension Template\"` | Human-readable project name |\n| `project_tag` | derived from `project_name` | Hyphenated slug (used as directory name and package identifier) |\n| `package_name` | derived from `project_name` | Python package name (lower_snake_case) |\n| `provider_name` | derived from `project_name` | Provider identifier (lower_snake_case) |\n| `router_name` | derived from `project_name` | Router identifier (lower_snake_case) |\n| `obbject_name` | derived from `project_name` | OBBject accessor name (lower_snake_case) |\n\nAll derived values are automatically generated from `project_name` — you only need\nto supply `project_name` for most cases.\n\n### CLI Command\n\n```\nopenbb-cookiecutter \\\n  -o /path/to/output \\\n  --no-input \\\n  --extra-context project_name=\"My Extension Name\"\n```\n\nAdd more `--extra-context KEY=VALUE` pairs to override individual variables.\nUse `-f` to overwrite an existing directory.\n\n---\n\n## Phase 2 — Understand the Generated Structure\n\nAfter scaffolding, you get this tree (with template variables resolved):\n\n```\n<project_tag>/\n├── pyproject.toml          # Dependencies, entry points (CRITICAL)\n├── <package_name>/\n│   ├── providers/\n│   │   └── <provider_name>/\n│   │       ├── __init__.py     # Provider registration (fetcher_dict)\n│   │       ├── models/\n│   │       │   ├── example.py      # Custom-schema fetcher example\n│   │       │   └── ohlc_example.py # Standard-model fetcher example\n│   │       └── utils/\n│   │           └── helpers.py      # Shared utility functions\n│   ├── routers/\n│   │   ├── __init__.py\n│   │   ├── <router_name>.py        # Router commands (API endpoints)\n│   │   ├── <router_name>_views.py  # Chart views (optional)\n│   │   └── depends.py              # Dependency injection\n│   └── obbject/\n│       └── <obbject_name>/\n│           └── __init__.py         # OBBject accessors (optional)\n└── tests/\n    └── conftest.py\n```\n\n### The Four Plugin Types\n\nOpenBB discovers extensions via Python entry points in `pyproject.toml`.\nEach plugin type has its own entry point group:\n\n| Entry Point Group | What It Registers | Where It Lives |\n|---|---|---|\n| `openbb_provider_extension` | Data provider (fetchers) | `providers/<name>/__init__.py` |\n| `openbb_core_extension` | Router (API endpoints/commands) | `routers/<name>.py` |\n| `openbb_charting_extension` | Chart views | `routers/<name>_views.py` |\n| `openbb_obbject_extension` | Result post-processing accessors | `obbject/<name>/__init__.py` |\n\n---\n\n## Phase 3 — Implement the Data Provider\n\nThis is the core of any extension. A provider fetches data from an external source\nand returns it as typed Pydantic models.\n\n### The Three-Class Fetcher Pattern\n\nEvery data fetcher follows this structure:\n\n**Class 1 — QueryParams** (input schema):\n- Inherits from `openbb_core.provider.abstract.query_params.QueryParams`\n- Define all parameters the user can pass (e.g., `symbol`, `start_date`)\n- Use `pydantic.Field` for descriptions and defaults\n\n**Class 2 — Data** (output schema):\n- Inherits from `openbb_core.provider.abstract.data.Data`\n- Define all fields in the response (e.g., `open`, `high`, `low`, `close`, `volume`)\n- Use `pydantic.Field` for descriptions\n- Use `__alias_dict__` to map source field names to your schema names\n\n**Class 3 — Fetcher** (orchestrator):\n- Inherits from `Fetcher[YourQueryParams, list[YourData]]`\n- Has exactly three static methods:\n\n  1. `transform_query(params: dict) -> YourQueryParams`\n     Pre-process user input. Return a validated QueryParams instance.\n\n  2. `extract_data(query, credentials, **kwargs) -> list[dict]`\n     Make the actual HTTP request to the data source. Return raw data as dicts.\n     For async fetching, name it `aextract_data` instead.\n\n  3. `transform_data(query, data, **kwargs) -> list[YourData]`\n     Convert raw dicts into typed Data model instances.\n\n### Imports for Fetcher Files\n\n```python\nfrom typing import Any\nfrom openbb_core.provider.abstract.data import Data\nfrom openbb_core.provider.abstract.fetcher import Fetcher\nfrom openbb_core.provider.abstract.query_params import QueryParams\nfrom pydantic import Field\n```\n\n### HTTP Requests — Required Utilities\n\n**IMPORTANT:** All HTTP requests inside fetchers and utility helpers **must** use the built-in\nutilities from `openbb_core.provider.utils.helpers`. Do **not** create raw\n`requests`, `aiohttp`, or `httpx` clients from scratch. The built-in helpers\napply the user's configured HTTP settings (proxy, timeout, user-agent, etc.)\nfrom `system_settings.json` automatically.\n\n#### Building Query Strings\n\nConvert a QueryParams model to a URL query string, optionally excluding\nparameters that should not appear in the URL:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"interval\", \"provider\"])\nurl = f\"https://api.example.com/data?{query_string}\"\n```\n\n`model_dump()` strips `None` values automatically. Pass parameter names to\nexclude in the second argument as a list (use `[]` if nothing to exclude).\n\n#### Synchronous Requests\n\nFor use inside `extract_data` (sync fetchers):\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\n# Returns a requests.Response object\nresponse = make_request(url, headers={\"Authorization\": f\"Bearer {api_key}\"})\ndata = response.json()\n```\n\nAll `requests.get`/`requests.post` keyword arguments are passed through.\n\nIf you need a session object for multiple requests:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\nresponse = session.get(url)\n```\n\n#### Asynchronous Requests (Preferred)\n\nFor use inside `aextract_data` (async fetchers). **Always prefer async.**\n\n**Single URL** — returns parsed JSON by default:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\ndata = await amake_request(url)  # returns dict (parsed JSON)\n```\n\n**Multiple URLs** — downloads concurrently and returns a list:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nurls = [f\"https://api.example.com/data/{s}\" for s in symbols]\nall_data = await amake_requests(urls)  # returns list[dict]\n```\n\n**Custom response handling (e.g., CSV):** Both `amake_request` and\n`amake_requests` default to parsing JSON. For non-JSON content (CSV, text,\nbinary), pass a `response_callback`:\n\n```python\nfrom io import StringIO\nfrom typing import Any\nfrom pandas import read_csv\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresults: list[dict] = []\n\n\nasync def csv_callback(response, _: Any):\n    \"\"\"Parse CSV response into list of dicts.\"\"\"\n    text = await response.text()\n    df = read_csv(StringIO(text))\n    results.extend(df.to_dict(\"records\"))\n\n\nawait amake_request(url, response_callback=csv_callback)\n# results now contains the parsed rows\n```\n\nFor CSV files with header rows to skip, pass `skiprows` to `read_csv`:\n\n```python\nasync def csv_callback(response, _: Any):\n    text = await response.text()\n    df = read_csv(StringIO(text), skiprows=3)\n    results.extend(df.to_dict(\"records\"))\n```\n\n**Async session object** — if you need a raw `aiohttp.ClientSession`:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with session.get(url) as response:\n        if response.status != 200:\n            raise OpenBBError(f\"Failed: {response.status} -> {response.reason}\")\n        data = await response.json()\n```\n\n#### Summary: Which Helper to Use\n\n| Scenario | Function | Module |\n|---|---|---|\n| Build a query string | `get_querystring()` | `openbb_core.provider.utils.helpers` |\n| Sync single request | `make_request()` | `openbb_core.provider.utils.helpers` |\n| Sync session | `get_requests_session()` | `openbb_core.provider.utils.helpers` |\n| Async single request (JSON) | `amake_request()` | `openbb_core.provider.utils.helpers` |\n| Async multiple URLs (JSON) | `amake_requests()` | `openbb_core.provider.utils.helpers` |\n| Async single/multi (CSV/text) | `amake_request()` + `response_callback` | `openbb_core.provider.utils.helpers` |\n| Async raw session | `get_async_requests_session()` | `openbb_core.provider.utils.helpers` |\n\n### Using Standard Models (Multi-Provider Endpoints)\n\nTo plug into existing endpoints that other providers already serve (like `EquityHistorical`),\ninherit from the standard query/data classes instead of the abstract base classes:\n\n```python\nfrom openbb_core.provider.standard_models.equity_historical import (\n    EquityHistoricalData,\n    EquityHistoricalQueryParams,\n)\n```\n\nThen add provider-specific fields as extra attributes.\nUse `__alias_dict__` to map the source's field names to the standard field names.\n\n### Registering Fetchers in the Provider\n\nIn `providers/<provider_name>/__init__.py`, create a `Provider` instance:\n\n```python\nfrom openbb_core.provider.abstract.provider import Provider\n\nmy_provider = Provider(\n    name=\"my_provider\",\n    description=\"Description of what this provider does.\",\n    # credentials=[\"api_key\"],  # Uncomment if an API key is needed\n    website=\"https://example.com\",\n    fetcher_dict={\n        \"MyCustomModel\": MyCustomFetcher,\n        \"EquityHistorical\": MyEquityHistoricalFetcher,  # Plugs into existing endpoint\n    },\n)\n```\n\nThe `fetcher_dict` keys are model names. When a key matches a standard model name\n(like `\"EquityHistorical\"`), this provider becomes selectable via the `provider`\nparameter on that existing endpoint.\n\nFor custom/new model names, you must also create a router command that references\nthat model name (see Phase 4).\n\n### Credentials\n\nIf your provider needs an API key:\n1. Add `credentials=[\"api_key\"]` to the Provider constructor\n2. Access it in `extract_data` via `credentials.get(\"<provider_name>_api_key\")` (the `Provider` prefixes each credential with its lower-cased `name`)\n3. Users configure it in their OpenBB user settings\n\n---\n\n## Phase 4 — Implement the Router\n\nThe router defines the API endpoints (commands) that users call.\n\n### Router Basics\n\n```python\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\")\n```\n\nThe top-level prefix is determined by the entry point name in `pyproject.toml`,\nnot by the `prefix` argument. Only set `prefix` for sub-routers.\n\n### Provider-Backed Command (Standard Pattern)\n\nThis is the most common pattern. It connects a router command to one or more\nprovider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyCustomModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters (`cc`, `provider_choices`, `standard_params`, `extra_params`)\nand the `OBBject.from_query(Query(**locals()))` return pattern are mandatory and\nmust be used exactly as shown.\n\nThe `model=\"MyCustomModel\"` string must match a key in at least one provider's\n`fetcher_dict`.\n\n### Free-Form GET Endpoint\n\nFor endpoints that don't use the provider/fetcher system:\n\n```python\n@router.command(methods=[\"GET\"])\nasync def my_endpoint(symbol: str = \"AAPL\") -> OBBject[dict]:\n    \"\"\"Get some data directly.\"\"\"\n    # Make HTTP requests, compute, etc.\n    return OBBject(results={\"key\": \"value\"})\n```\n\n### Free-Form POST Endpoint\n\n```python\n@router.command(methods=[\"POST\"])\nasync def my_post_endpoint(\n    data: BaseModel,  # Body parameters\n    flag: bool = False,  # Query parameters\n) -> OBBject[dict]:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results={\"processed\": True})\n```\n\n### Dependency Injection\n\nUse `routers/depends.py` for shared dependencies:\n\n```python\nfrom typing import Annotated\nimport requests\nfrom fastapi import Depends\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nSession = Annotated[requests.Session, Depends(get_requests_session)]\n```\n\nThen use `session: Session` as a parameter in your router commands.\n\n### Adding Examples\n\n```python\nfrom openbb_core.app.model.example import APIEx, PythonEx\n\n@router.command(\n    model=\"MyModel\",\n    examples=[\n        PythonEx(\n            description=\"Get data for AAPL\",\n            code=[\"obb.my_router.my_command(symbol='AAPL')\"],\n        )\n    ],\n)\n```\n\n---\n\n## Phase 5 — Entry Points in pyproject.toml\n\nThis is **critical** — OpenBB discovers your code entirely through these entry points.\n\n### Provider Entry Point\n\n```toml\n[project.entry-points.\"openbb_provider_extension\"]\nmy_provider = \"my_package.providers.my_provider:my_provider_variable\"\n```\n\nThe variable (`my_provider_variable`) is the `Provider(...)` instance you created\nin Phase 3.\n\n### Router Entry Point\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_router = \"my_package.routers.my_router:router\"\n```\n\nThe entry point **name** (`my_router`) determines the API path prefix.\nFor example, `my_router` means endpoints appear under `/my_router/...`.\n\n### Charting Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_router_views:MyRouterViews\"\n```\n\n### OBBject Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_accessor = \"my_package.obbject.my_obbject:ext\"\nmy_namespace = \"my_package.obbject.my_obbject:class_ext\"\n```\n\n---\n\n## Phase 6 — Chart Views (Optional)\n\nIf you want to add charting support:\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyRouterViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Create a chart for my_command results.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build your chart using fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## Phase 7 — OBBject Accessors (Optional)\n\nResult post-processing extensions that add methods to the `OBBject` response.\n\n### Function Accessor (Property-Like)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"to_csv\", description=\"Convert results to CSV string.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject, **kwargs) -> str:\n    \"\"\"Convert to CSV.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### Class Accessor (Namespaced Methods)\n\n```python\nclass_ext = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@class_ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self, **kwargs):\n        \"\"\"Return a summary.\"\"\"\n        df = self._obbject.to_dataframe()\n        return df.describe()\n```\n\n---\n\n## Phase 8 — Install, Build, and Test\n\n### Install in Development Mode\n\nFrom the generated project root directory:\n\n```\nuv pip install -e . --group dev\n```\n\nThis registers the entry points so OpenBB discovers your extension immediately.\n\n### Build Static Assets with `openbb-build`\n\n**CRITICAL:** After installing a new extension, or after making changes to any\nof the following, you **must** run `openbb-build` before using the Python\ninterface (`obb.<router>.<command>(...)`):\n\n- **Model definitions** — `QueryParams` or `Data` classes (field names, types,\n  defaults, descriptions)\n- **Provider registration** — changes to `fetcher_dict` keys, adding/removing\n  fetchers\n- **Router commands** — adding, removing, or renaming `@router.command()`\n  endpoints\n- **Entry points** — changes to `pyproject.toml` plugin entries\n- **Any importable item in the registration chain** — the `Provider(...)`\n  instance, router module, or model module paths\n\nYou do **not** need to re-run `openbb-build` when changing:\n\n- Logic inside `extract_data` / `aextract_data` / `transform_data` /\n  `transform_query` static methods (the Fetcher method bodies)\n- Utility/helper functions\n- Internal implementation details that don't affect the public schema\n\n```\nopenbb-build\n```\n\nThis regenerates the static assets (type stubs, package interface, provider\nmaps) that the Python interface relies on. Without this step, new or modified\ncommands will not appear on the `obb` object and calls will fail.\n\n**When running as an API server** (e.g., via `uvicorn` or the MCP server),\nstatic assets are **not used** — the API discovers extensions dynamically at\nstartup. You do not need to run `openbb-build` for API-only usage.\n\n### Verify Installation\n\nStart a Python session and check:\n\n```python\nfrom openbb import obb\n# Your new commands should appear:\n# obb.<router_name>.<command_name>(...)\n```\n\nOr start the API server and verify the new endpoints appear.\n\n### Run Tests\n\n```\npytest tests/ -v\n```\n\nThe generated `tests/conftest.py` sets `OPENBB_AUTO_BUILD=true` for proper\ntest environment setup.\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me an application that does X\":\n\n1. **Analyze** — Determine what data sources are needed, what endpoints to expose,\n   and whether to use standard models or custom schemas.\n2. **Scaffold** — Run `openbb-cookiecutter` with a meaningful `project_name`.\n3. **Delete examples** — Remove `example.py` and `ohlc_example.py` from the models\n   directory. Clean up the example router commands.\n4. **Implement models** — Create `QueryParams` + `Data` + `Fetcher` classes for each\n   data source in `providers/<name>/models/`.\n5. **Register fetchers** — Update `providers/<name>/__init__.py` with the `fetcher_dict`.\n6. **Implement router** — Create `@router.command(model=\"...\")` endpoints in\n   `routers/<name>.py`.\n7. **Update entry points** — Ensure `pyproject.toml` entry points match your actual\n   module paths and variable names.\n8. **Add dependencies** — Add any third-party packages to the `[project]`\n   `dependencies` list in `pyproject.toml`.\n9. **Install** — Run `uv pip install -e . --group dev` from the project root.\n10. **Build** — Run `openbb-build` to regenerate static assets for the Python\n    interface. Skip this step if only using the API server.\n11. **Test** — Verify the commands work, then write tests.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/work_with_server/SKILL.md":"---\nname: work_with_server\ndescription: This guide explains how to call tools, interpret responses, discover capabilities, use prompts, and handle errors when interacting with an OpenBB MCP server.\n---\n\n# Working With the OpenBB MCP Server\n\nThis guide explains how to call tools, interpret responses, discover\ncapabilities, use prompts, and handle errors when interacting with an OpenBB\nMCP server.\n\n---\n\n## Tool Discovery Workflow\n\nWhen the server runs with `--tool-discovery`, the OpenBB tools are left out of\nthe tool list, and a small set of discovery tools finds and runs them.\n\nDiscovery holds no per-session state — every client sees the same tool list, and\nnothing needs to be activated before a call.\n\n### Step 1 — List Categories\n\nCall `available_categories` (no arguments) to see what is installed:\n\n```json\n[\n    {\n        \"name\": \"equity\",\n        \"subcategories\": [\n            {\"name\": \"price\", \"tool_count\": 5},\n            {\"name\": \"fundamental\", \"tool_count\": 12}\n        ],\n        \"total_tools\": 17\n    },\n    {\n        \"name\": \"economy\",\n        \"subcategories\": [\n            {\"name\": \"general\", \"tool_count\": 3}\n        ],\n        \"total_tools\": 3\n    }\n]\n```\n\nEach category maps to a top-level API router. Subcategories are nested routers.\n\n### Step 2 — Browse Tools in a Category\n\nCall `available_tools` with a category name:\n\n```json\n// Input\n{\"category\": \"equity\", \"subcategory\": \"price\"}\n\n// Output\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"Get historical price data...\"},\n    {\"name\": \"equity_price_quote\", \"description\": \"Get current price quote...\"}\n]\n```\n\nEach entry has the tool's name and a one-sentence description. The\n`subcategory` argument is optional. Omit it to see all tools in the category.\n\n### Step 3 — Search for Tools\n\nCall `search_tools` with a natural-language query:\n\n```json\n// Input\n{\"query\": \"historical stock prices\"}\n\n// Output - full tool definitions, in the same format as list_tools\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"...\", \"inputSchema\": {\"...\": \"...\"}}\n]\n```\n\nSearch covers the hidden OpenBB tools and returns at most five matches. Use\neach match's `inputSchema` to build the arguments.\n\n### Step 4 — Call a Tool\n\nCall `call_tool` with the tool name and its arguments:\n\n```json\n{\"name\": \"equity_price_historical\", \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"}}\n```\n\n`call_tool` runs the named tool and returns its result. A hidden tool can also\nbe called directly by name, with the same arguments.\n\n### When Discovery Is Disabled\n\nIf the server was started without `--tool-discovery`, the discovery tools are not\navailable. All tools in `default_tool_categories` are listed and called directly;\ntools outside them are not served, and calling one fails with \"Unknown tool\".\n\n---\n\n## openbb-cli Dispatcher Tools\n\nWhen the optional `openbb-mcp-server[cli]` extra is installed (it pulls in\n`openbb-cli`), the server also registers four first-class tools that wrap\n`openbb-cli`'s NDJSON dispatcher protocol. They give an agent the same\n`obb`-namespace command surface that the `openbb` CLI exposes — without\nshelling out or standing up a Python REPL.\n\n### `openbb_dispatch`\n\nExecute a single command and return its serialized result.\n\n```json\n{\n    \"name\": \"openbb_dispatch\",\n    \"arguments\": {\n        \"command\": \"equity.price.historical\",\n        \"params\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"},\n        \"server_url\": null\n    }\n}\n```\n\n| Argument | Type | Notes |\n|---|---|---|\n| `command` | `string` | Dotted command path under the `obb` namespace (e.g. `equity.price.historical`). |\n| `params` | `dict \\| null` | Keyword arguments forwarded to the command. Defaults to `{}`. |\n| `server_url` | `string \\| null` | When set, dispatches against a remote `openbb-platform-api` server (HTTP). When omitted, falls back to `OPENBB_SERVER_URL`, then in-process local dispatch. |\n\nThe result is a dispatcher response. `ok` reports success, `result` holds the\ncommand's OBBject fields (`results`, `provider`, ...), and `error` carries the\n`type` and `message` of a failure instead of raising:\n\n```json\n{\n    \"id\": null,\n    \"ok\": true,\n    \"result\": {\"results\": [{\"date\": \"2025-01-02\", \"close\": 243.85}], \"provider\": \"yfinance\"},\n    \"error\": null\n}\n```\n\nRemote dispatch unwraps a single-row `results` list to the row itself.\n\n### `openbb_batch_dispatch`\n\nExecute many commands concurrently and return results in input order.\n\n```json\n{\n    \"name\": \"openbb_batch_dispatch\",\n    \"arguments\": {\n        \"requests\": [\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"AAPL\"}, \"id\": \"a\"},\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"MSFT\"}, \"id\": \"b\"}\n        ]\n    }\n}\n```\n\nEach entry needs `command` (dotted path); `params` and `id` are optional. The\n`id` is opaque correlation echoed back on the matching response. Failures\nsurface as response objects with `ok=False` and a structured `error` block —\nno exception bubbles up, errors are per-request.\n\n### `openbb_list_commands`\n\nList every command with a one-line description. Takes only the optional\n`server_url`. The response's `result` is a list of `{\"name\", \"description\"}`\nentries. Equivalent to `openbb --list-commands` on the CLI.\n\n### `openbb_describe_command`\n\nReturn one command's parameters and output schema.\n\n```json\n{\n    \"name\": \"openbb_describe_command\",\n    \"arguments\": {\"command\": \"equity.price.historical\", \"provider\": \"yfinance\"}\n}\n```\n\nThe response's `result` has the command `name`, its `parameters` (each with\n`name`, `in`, and `type`, plus `required`, `default`, `choices`, and `help` when\nset), and its `output_schema`. For a command with several providers, pass\n`provider` to get that provider's `parameters` and `output_schema`; without it,\n`result.providers` maps every provider to its own `parameters` and\n`output_schema`. Equivalent to `openbb --describe equity.price.historical`\n(`equity.price.historical:yfinance` for one provider) on the CLI.\n\n### Local vs remote dispatch\n\n| Mode | Trigger | Behavior |\n|---|---|---|\n| Local | No `server_url`, no `OPENBB_SERVER_URL` | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. Single-tenant. |\n| Remote | `server_url` arg or `OPENBB_SERVER_URL` env | `HttpDispatcher` proxies commands to a long-running `openbb-platform-api` server. Multi-tenant; the heavy import lives on the server. |\n\nMode is per-call — different requests in the same session can target different\nservers. Dispatchers are cached for the server's lifetime, so the `import openbb`\ncost (local) and the remote server's OpenAPI download are paid once per target.\n\n---\n\n## Calling Data Tools\n\n### Input Parameters\n\nEvery data tool has a JSON Schema describing its input. A typical tool schema:\n\n```json\n{\n    \"type\": \"object\",\n    \"properties\": {\n        \"symbol\": {\n            \"type\": \"string\",\n            \"description\": \"Symbol to get data for.\"\n        },\n        \"provider\": {\n            \"type\": \"string\",\n            \"enum\": [\"fmp\", \"polygon\", \"yfinance\"],\n            \"description\": \"The provider to use for the query.\"\n        },\n        \"start_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"Start date of the data.\"\n        },\n        \"end_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"End date of the data.\"\n        },\n        \"interval\": {\n            \"type\": \"string\",\n            \"default\": \"1d\",\n            \"description\": \"Time interval of the data.\"\n        }\n    },\n    \"required\": [\"symbol\"]\n}\n```\n\nKey rules:\n\n- **`provider`** — present only on endpoints that use the provider interface.\n  When listed, its enum shows available provider sources. If the endpoint has\n  only one provider, you can omit it and the sole provider is used\n  automatically. When multiple providers are available, select one from the\n  enum. Different providers may return different fields or support different\n  parameters. Endpoints that do not use the provider interface (basic GET/POST\n  routes) have no `provider` parameter at all.\n- **`symbol` formatting** — symbols are case-insensitive. Multiple symbols can\n  be comma-separated: `\"AAPL,MSFT,GOOG\"`.\n- **Dates** — always formatted as `YYYY-MM-DD` strings.\n- **Optional parameters** — have `anyOf` with a `null` type or a `default`\n  value. Omit them to use defaults.\n- **Provider-specific parameters** — some parameters are only relevant for\n  certain providers. The schema unions all of them; irrelevant ones are\n  silently ignored.\n- **Valid values** — never guess a value. A parameter with a fixed set of\n  values has an `enum`. When the values differ by provider, or several can be\n  comma-separated, the description lists them (`Valid values by provider:\n  ...`, `Valid values for provider oecd (comma-separate several): ...`). When\n  another tool lists them, the description names it and its arguments\n  (``Get the valid values from the `derivatives_options_strikes` tool with\n  symbol=<the symbol you pass here>.``) — call that tool first.\n\n### Example Tool Call\n\n```json\n{\n    \"name\": \"equity_price_historical\",\n    \"arguments\": {\n        \"symbol\": \"AAPL\",\n        \"provider\": \"fmp\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-02-01\",\n        \"interval\": \"1d\"\n    }\n}\n```\n\n---\n\n## Understanding the Response\n\nEvery OpenBB tool returns an **OBBject** — a standardized response envelope:\n\n```json\n{\n    \"id\": \"06520558-d54a-7e53-8000-7aafc8a42694\",\n    \"results\": [...],\n    \"provider\": \"fmp\",\n    \"warnings\": null,\n    \"chart\": null,\n    \"extra\": {\n        \"metadata\": {\n            \"arguments\": {...},\n            \"duration\": 565256375,\n            \"route\": \"/equity/price/historical\",\n            \"timestamp\": \"2025-01-15 11:28:57.149548\"\n        }\n    }\n}\n```\n\n### Response Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `id` | `string` | UUID identifying this request |\n| `results` | `list[dict] \\| dict \\| string \\| null` | The actual data. Usually a list of records |\n| `provider` | `string \\| null` | Which provider fulfilled the request |\n| `warnings` | `list[object] \\| null` | Non-fatal warnings from the provider or platform |\n| `chart` | `object \\| null` | Chart artifact if `chart=true` was passed |\n| `extra` | `dict` | Execution metadata and results metadata |\n\n### The `results` Field\n\nThis is the primary data payload. Its structure depends on the endpoint:\n\n**Tabular data** — most common, a list of dictionaries (one per row):\n\n```json\n\"results\": [\n    {\"date\": \"2025-01-02\", \"open\": 150.0, \"high\": 155.0, \"low\": 149.0, \"close\": 153.5, \"volume\": 1000000},\n    {\"date\": \"2025-01-03\", \"open\": 153.0, \"high\": 157.0, \"low\": 152.0, \"close\": 156.2, \"volume\": 1200000}\n]\n```\n\n**Single record** — some endpoints return a single dict:\n\n```json\n\"results\": {\"symbol\": \"AAPL\", \"price\": 185.50, \"change\": 2.30, \"volume\": 45000000}\n```\n\n**Empty results** — when no data is available:\n\n```json\n\"results\": null\n```\n\nor\n\n```json\n\"results\": []\n```\n\n**Field names vary by provider** — different providers may return different\ncolumns for the same endpoint. Always check the keys in the returned records.\n\n### The `warnings` Field\n\nWarnings are non-fatal issues that occurred during execution:\n\n```json\n\"warnings\": [\n    {\n        \"category\": \"OpenBBWarning\",\n        \"message\": \"Parameter 'source' is not supported by fmp. Available for: intrinio.\"\n    }\n]\n```\n\nCommon warning scenarios:\n- Unknown parameters silently ignored by the provider\n- Partial data returned (fewer rows than requested)\n- Provider-specific data quality notes\n\nWhen `warnings` is `null`, no warnings were generated.\n\n### The `extra` Field\n\nContains execution metadata and optional results metadata:\n\n```json\n\"extra\": {\n    \"metadata\": {\n        \"arguments\": {\n            \"provider_choices\": {\"provider\": \"fmp\"},\n            \"standard_params\": {\"symbol\": \"AAPL\", \"start_date\": \"2025-01-01\"},\n            \"extra_params\": {}\n        },\n        \"duration\": 565256375,\n        \"route\": \"/equity/price/historical\",\n        \"timestamp\": \"2025-01-15 11:28:57.149548\"\n    },\n    \"results_metadata\": {\n        \"...provider-specific metadata...\"\n    }\n}\n```\n\n**`metadata`** — always present (unless disabled):\n- `arguments` — the exact parameters used, split into provider choices,\n  standard params, and extra (provider-specific) params\n- `duration` — nanoseconds the request took\n- `route` — the API endpoint path\n- `timestamp` — when the request was made\n\n**`results_metadata`** — present when the provider returns contextual\ninformation about the data (e.g., FRED series metadata, CBOE options\nmetadata). Contents vary by endpoint and provider.\n\n### The `chart` Field\n\nWhen a tool is called with `chart: true` (where supported), the server\nreplaces the Plotly figure with an OpenBB Workspace artifact:\n\n```json\n\"chart\": {\n    \"type\": \"chart\",\n    \"name\": \"AAPL Close\",\n    \"description\": \"Output of the equity_price_historical tool.\",\n    \"uuid\": \"5b0e1c52-6f3e-4a51-9d6f-0f1b6a2f4c11\",\n    \"content\": [{\"date\": \"2025-01-02\", \"close\": 243.85}],\n    \"chart_params\": {\"chartType\": \"line\", \"xKey\": \"date\", \"yKey\": [\"close\"]}\n}\n```\n\n`content` holds the rows the chart is drawn from and `chart_params` says how:\n`chartType` is `line`, `bar`, or `scatter` with `xKey` and `yKey`, or `pie`\nor `donut` with `angleKey` and `calloutLabelKey`. Figures that cannot be\ndrawn that way (surfaces, candlesticks, heatmaps) come back as a `table`\nartifact of their data, without `chart_params`. Tools that return a figure\ndirectly return the artifact as their whole output. Present the artifact\nas it is; do not convert it or re-plot it. Not all endpoints support\ncharting.\n\n---\n\n## Provider Selection\n\n### How Providers Work\n\nEach data endpoint can have multiple provider sources (e.g., FMP, Yahoo Finance,\nPolygon). Providers differ in:\n\n- **Available parameters** — some providers offer extra filtering or options\n- **Returned fields** — column names and data granularity may differ\n- **Rate limits and authentication** — some providers require API keys\n- **Data coverage** — geographic markets, date ranges, asset types\n\n### Choosing a Provider\n\nWhen a tool's input schema includes `provider`, its enum lists all installed\nprovider sources for that endpoint. If there is only one provider, the\nparameter can be omitted — the sole provider is selected automatically.\nWhen multiple providers are available, pick one based on:\n\n1. **Check the enum** — only listed options work\n2. **Consider the data need** — different providers may have different fields\n3. **API key requirements** — some providers need credentials configured in\n   `~/.openbb_platform/user_settings.json`\n\nEndpoints that do not use the provider interface (basic GET/POST routes added\nvia `router.command(methods=[\"GET\"])` or raw FastAPI) have no `provider`\nparameter.\n\nIf a provider fails due to missing credentials, the error message will indicate\nan authentication issue.\n\n### Provider-Specific Parameters\n\nSome parameters only apply to certain providers. For example, the `source`\nparameter might only be available with the `intrinio` provider. Passing it to\n`fmp` generates a warning but does not cause an error.\n\n---\n\n## Working With Prompts\n\nThe server includes a prompt system for accessing documentation, usage guides,\nand analysis frameworks.\n\n### List Available Prompts\n\nCall `list_prompts` (no arguments):\n\n```json\n[\n    {\"name\": \"system_prompt\", \"description\": \"System prompt with guidance...\", \"arguments\": []},\n    {\"name\": \"analyze_stock\", \"description\": \"Analyze a stock.\", \"arguments\": [\n        {\"name\": \"symbol\", \"description\": \"Ticker.\", \"required\": true},\n        {\"name\": \"focus\", \"description\": \"Area.\", \"required\": false}\n    ]}\n]\n```\n\n### Get a Prompt\n\nCall `get_prompt` with the prompt `name` and any `arguments`:\n\n```json\n// Input\n{\"name\": \"analyze_stock\", \"arguments\": {\"symbol\": \"AAPL\"}}\n\n// Output - the rendered prompt\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"Analyze AAPL focusing on fundamentals.\"}\n    ]\n}\n```\n\nPass every argument value as a string (`{\"years\": \"10\"}`, not `{\"years\": 10}`);\nMCP prompt arguments are strings. Prompts without arguments return their\ncontent as-is. Omitted optional arguments fall back to their defaults, and a\nmissing required argument is an error. Skills are resources, not prompts; see\n[Working With Skills](#working-with-skills).\n\n### Prompt Categories by Tag\n\n| Tag | Source | Description |\n|---|---|---|\n| `system` | System prompt file | Server-wide context and instructions |\n| `server` | Server prompts JSON | Reusable analysis frameworks |\n| `route-specific` | Inline on API routes | Endpoint usage guides |\n\n---\n\n## Error Handling\n\n### Error Types\n\n| Scenario | What Happens |\n|---|---|\n| **Invalid parameters** | Error with HTTP 422 details explaining which parameter failed validation |\n| **Missing required parameter** | Error with HTTP 422 indicating the missing field |\n| **Provider authentication failure** | Error with HTTP 401/403 indicating credentials are missing or invalid |\n| **Provider rate limit** | Error with HTTP 429 or provider-specific rate limit message |\n| **No data available** | Successful response with `results: null` or `results: []` |\n| **Tool hidden by discovery** | Tool is not in the tool list; find it with `search_tools` and run it with `call_tool` |\n| **Tool outside the served categories** | \"Unknown tool\" error, by name, through `call_tool`, and through `run_pipeline` |\n| **Unknown tool name** | Standard MCP protocol error |\n| **Category not found** (discovery) | Error listing available categories |\n| **Connection failure** | Error with \"Request error: ...\" |\n\n### Interpreting Empty Results\n\nA response with `results: null` or `results: []` is **not an error** — it\nmeans the provider had no data matching the query. Common causes:\n\n- Date range with no trading days\n- Symbol not covered by the selected provider\n- Data not yet available for the requested period\n\nTry a different provider, adjust the date range, or verify the symbol format.\n\n### Reading Validation Errors\n\nValidation errors (HTTP 422) include detail about what went wrong:\n\n```\nHTTP error 422: Unprocessable Entity - {\"detail\": [{\"loc\": [\"query\", \"symbol\"], \"msg\": \"field required\", \"type\": \"value_error.missing\"}]}\n```\n\nThe `loc` field shows which parameter failed, and `msg` explains why.\n\n---\n\n## Practical Patterns\n\n### Fetching Time Series Data\n\n1. With discovery enabled, find the tool: `search_tools({\"query\": \"historical stock prices\"})`\n2. Call with date range:\n   ```json\n   {\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"start_date\": \"2025-01-01\", \"end_date\": \"2025-02-01\"}\n   ```\n3. Read `results` — each record has `date`, `open`, `high`, `low`, `close`,\n   `volume` (field names depend on provider)\n\n### Comparing Multiple Symbols\n\nPass comma-separated symbols:\n\n```json\n{\"symbol\": \"AAPL,MSFT,GOOG\", \"provider\": \"fmp\"}\n```\n\nResults will contain records for all symbols. Filter by the `symbol` field in\neach record if present, or by the ordering pattern.\n\n### Chaining Tool Calls\n\nUse the output of one tool as input to another:\n\n1. Get peers: `equity_compare_peers({\"symbol\": \"AAPL\", \"provider\": \"fmp\"})`\n2. Extract symbols from `results`\n3. Get quotes: `equity_price_quote({\"symbol\": \"AAPL,PEER1,PEER2\", \"provider\": \"fmp\"})`\n\nWhen a later tool needs the earlier tool's rows rather than a few values picked\nfrom them, use `run_pipeline` (below) so the rows never pass through the\nconversation.\n\n### Running Data-Processing Tools With `run_pipeline`\n\nTechnical, quantitative, and econometrics tools (`technical_rsi`,\n`technical_macd`, ...) take the rows to analyze as a `data` argument. Fetch and\nanalyze in one `run_pipeline` call instead of copying a price history into\ntheir arguments:\n\n```json\n{\n    \"steps\": [\n        {\n            \"id\": \"prices\",\n            \"tool\": \"equity_price_historical\",\n            \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\", \"start_date\": \"2025-01-01\"}\n        },\n        {\"id\": \"rsi\", \"tool\": \"technical_rsi\", \"arguments\": {\"length\": 14}, \"inputs\": {\"data\": \"prices\"}},\n        {\"id\": \"macd\", \"tool\": \"technical_macd\", \"inputs\": {\"data\": \"prices\"}}\n    ],\n    \"outputs\": [\"rsi\", \"macd\"]\n}\n```\n\n- Each step has a `tool`, its `arguments`, an optional `id`, and `inputs`, which\n  maps an argument name to the `id` of an earlier step. That step's `results`\n  fill the argument (its whole output when it has no `results`).\n- A step without an `id` is referred to by its position (`\"0\"`, `\"1\"`, ...).\n- `outputs` lists the steps to return; by default only the last step comes back.\n- The request is validated before any step runs. A failing step is reported as\n  `Step '<id>' (<tool>) failed: ...`.\n- With tool discovery, use `search_tools` to find the tools and their\n  parameters; `run_pipeline` can call tools hidden by discovery by name, but\n  not tools outside the categories the server serves.\n\n### Checking Data Coverage\n\nWhen unsure what providers are available for an endpoint, look at the tool's\ninput schema — the `provider` parameter's `enum` lists all installed options.\n\n### Using Charts\n\nPass `chart: true` to get a pre-built visualization:\n\n```json\n{\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"chart\": true}\n```\n\nThe `chart` field in the response holds a `chart` or `table` artifact (see\nThe `chart` Field). In `run_pipeline`, a step that returns an artifact passes\nits `content` rows to later steps.\n\n---\n\n## User Settings and Defaults\n\nThe server reads user settings from `~/.openbb_platform/user_settings.json`:\n\n### API Keys\n\nProvider credentials are stored under `credentials`:\n\n```json\n{\n    \"credentials\": {\n        \"fmp_api_key\": \"YOUR_KEY\",\n        \"polygon_api_key\": \"YOUR_KEY\"\n    }\n}\n```\n\nWithout the required API key, calls to that provider will fail with an\nauthentication error.\n\n### Default Provider\n\nSet a default provider per endpoint so it is preselected:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\"provider\": \"yfinance\"},\n            \"/economy/cpi\": {\"provider\": \"oecd\"}\n        }\n    }\n}\n```\n\nWhen multiple providers are available, this determines the default selection\nif the parameter is omitted.\n\n### Default Parameters\n\nIndividual parameters can be defaulted so they are applied when not explicitly\npassed:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\n                \"provider\": \"fmp\",\n                \"chart\": true,\n                \"chart_params\": {\n                    \"heikin_ashi\": true,\n                    \"indicators\": {\"sma\": {\"length\": [21, 50]}}\n                }\n            }\n        }\n    }\n}\n```\n\n### Output Preferences\n\nThe `output_type` preference controls how the Python Interface returns data.\nFor MCP, the server always returns the full OBBject JSON regardless of this\nsetting, but it is relevant for the Python Interface:\n\n| Output Type | Description |\n|---|---|\n| `OBBject` | Full response object (default) |\n| `dataframe` | Pandas DataFrame |\n| `numpy` | NumPy array |\n| `dict` | Python dictionary |\n| `polars` | Polars DataFrame |\n| `llm` | JSON-encoded string of results only |\n| `chart` | Chart object |\n\n### LLM Mode\n\nSetting `output_type` to `\"llm\"` in the Python Interface strips everything\nexcept `results` and returns it as a JSON string. This is optimized for token\nefficiency in LLM frameworks. In the REST API / MCP context, the full OBBject\nis always returned.\n\n---\n\n## Working With Skills\n\nSkills are MCP resources exposed at `skill://<name>/SKILL.md` URIs. Discover\nand read them via the standard MCP resource methods.\n\n### Discover Available Skills\n\nCall `list_resources()` (no arguments):\n\n```json\n[\n    {\"uri\": \"skill://develop_extension/SKILL.md\", \"name\": \"develop_extension\"},\n    {\"uri\": \"skill://build_workspace_app/SKILL.md\", \"name\": \"build_workspace_app\"},\n    {\"uri\": \"skill://configure_mcp_server/SKILL.md\", \"name\": \"configure_mcp_server\"},\n    {\"uri\": \"skill://work_with_server/SKILL.md\", \"name\": \"work_with_server\"},\n    {\"uri\": \"skill://use_openbb_cli/SKILL.md\", \"name\": \"use_openbb_cli\"}\n]\n```\n\n### Read a Skill\n\nCall `read_resource()` with the skill URI:\n\n```json\n// Input\n{\"uri\": \"skill://develop_extension/SKILL.md\"}\n\n// Output — full Markdown content of the skill guide\n```\n\n### Supporting Files\n\nSkill directories can contain additional supporting files (e.g. templates,\nexamples). Reference the skill manifest at `skill://<name>/_manifest` to\ndiscover any supporting files packaged alongside the main `SKILL.md`.\n\n---\n\n## Quick Reference\n\n### Admin Tools (Discovery)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `available_categories` | *(none)* | List of categories with subcategories and tool counts |\n| `available_tools` | `category`, `subcategory?` | List of tools with names and descriptions |\n| `search_tools` | `query` | Full definitions of the matching tools |\n| `call_tool` | `name`, `arguments?` | The tool's result |\n\n### Pipeline Tool (Always Listed)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `run_pipeline` | `steps`, `outputs?` | Outputs of the selected steps, keyed by step id |\n\n### OBBject Response Structure\n\n| Field | Always Present | Content |\n|---|---|---|\n| `id` | Yes | Request UUID |\n| `results` | Yes | Data payload (list, dict, string, or null) |\n| `provider` | Yes | Provider name or null |\n| `warnings` | Yes | Warning list or null |\n| `chart` | Yes | Chart artifact or null |\n| `extra` | Yes | Metadata dict (may be empty) |\n","assets/extensions/router.json":"[\n    {\n        \"packageName\": \"openbb-commodity\",\n        \"optional\": true,\n        \"description\": \"Commodity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-crypto\",\n        \"optional\": true,\n        \"description\": \"Cryptocurrency market data.\"\n    },\n    {\n        \"packageName\": \"openbb-currency\",\n        \"optional\": true,\n        \"description\": \"Foreign exchange (FX) market data.\"\n    },\n    {\n        \"packageName\": \"openbb-derivatives\",\n        \"optional\": true,\n        \"description\": \"Derivatives market data.\"\n    },\n    {\n        \"packageName\": \"openbb-econometrics\",\n        \"optional\": true,\n        \"description\": \"Econometrics analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-economy\",\n        \"optional\": true,\n        \"description\": \"Economic data.\"\n    },\n    {\n        \"packageName\": \"openbb-equity\",\n        \"optional\": true,\n        \"description\": \"Equity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-etf\",\n        \"optional\": true,\n        \"description\": \"Exchange Traded Funds market data.\"\n    },\n    {\n        \"packageName\": \"openbb-fixedincome\",\n        \"optional\": true,\n        \"description\": \"Fixed Income market data.\"\n    },\n    {\n        \"packageName\": \"openbb-index\",\n        \"optional\": true,\n        \"description\": \"Indices data.\"\n    },\n    {\n        \"packageName\": \"openbb-news\",\n        \"optional\": false,\n        \"description\": \"Financial market news data.\"\n    },\n    {\n        \"packageName\": \"openbb-quantitative\",\n        \"optional\": true,\n        \"description\": \"Quantitative analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-technical\",\n        \"optional\": true,\n        \"description\": \"Technical Analysis tools.\"\n    }\n]","assets/extensions/provider.json":"[\n    {\n        \"packageName\": \"openbb-bls\",\n        \"optional\": false,\n        \"reprName\": \"Bureau of Labor Statistics' (BLS) Public Data API\",\n        \"description\": \"The Bureau of Labor Statistics' (BLS) Public Data Application Programming Interface (API) gives the public access to economic data from all BLS programs. It is the Bureau's hope that talented developers and programmers will use the BLS Public Data API to create original, inventive applications with published BLS data.\",\n        \"credentials\": [\n            \"bls_api_key\"\n        ],\n        \"website\": \"https://www.bls.gov/developers/api_signature_v2.htm\",\n        \"instructions\": \"Sign up for a free API key here: https://data.bls.gov/registrationEngine/\"\n    },\n    {\n        \"packageName\": \"openbb-cboe\",\n        \"optional\": false,\n        \"reprName\": \"Chicago Board Options Exchange (CBOE)\",\n        \"description\": \"Cboe is the world's go-to derivatives and exchange network,\\ndelivering cutting-edge trading, clearing and investment solutions to people\\naround the world.\",\n        \"credentials\": [],\n        \"website\": \"https://www.cboe.com\"\n    },\n    {\n        \"packageName\": \"openbb-cftc\",\n        \"optional\": false,\n        \"reprName\": \"Commodity Futures Trading Commission (CFTC)\",\n        \"description\": \"The mission of the Commodity Futures Trading Commission (CFTC) is to promote the integrity,\\n    resilience, and vibrancy of the U.S. derivatives markets through sound regulation.\\n    Commitment of Traders reports are sourced from the CFTC Public Reporting API. Swap transaction\\n    and pricing data is sourced from the DTCC Public Price Dissemination service, the swap data\\n    repository that publishes CFTC-jurisdiction transactions. Overnight index swap curves and FX\\n    forward points are constructed from those executed transactions.\",\n        \"credentials\": [\n            \"cftc_app_token\"\n        ],\n        \"website\": \"https://cftc.gov/\",\n        \"instructions\": \"Credentials are not required, but your IP address may be subject to throttling limits\\n    on the Commitment of Traders endpoints. API requests made using an application token are not throttled.\\n    Create an account here: https://evergreen.data.socrata.com/signup\\n    and then generate the app_token by signing in with the credentials\\n    here: https://publicreporting.cftc.gov/profile/edit/developer_settings.\\n    The DTCC Public Price Dissemination endpoints are public and require no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-deribit\",\n        \"optional\": false,\n        \"reprName\": \"Deribit Public Data\",\n        \"description\": \"Deribit is a crypto derivatives exchange listing futures, perpetuals,\\noptions, and spot pairs. This extension covers its public market data API, which\\nneeds no credentials and carries no trading.\",\n        \"credentials\": [],\n        \"website\": \"https://deribit.com/\",\n        \"instructions\": \"This provider needs no credentials and is not meant for trading.\"\n    },\n    {\n        \"packageName\": \"openbb-ecb\",\n        \"optional\": false,\n        \"reprName\": \"European Central Bank (ECB)\",\n        \"description\": \"The ECB Data Portal provides access to all official ECB statistics\\nthrough the SDMX 2.1 REST API, plus euro reference exchange rates, key ECB interest\\nrates, the euro short-term rate (\\u20acSTR), MFI/bank interest rates, the statistical\\nrelease calendar, ECB releases & publications, and the Eurosystem list of eligible\\ncollateral assets.\",\n        \"credentials\": [],\n        \"website\": \"https://data.ecb.europa.eu\"\n    },\n    {\n        \"packageName\": \"openbb-famafrench\",\n        \"optional\": false,\n        \"reprName\": \"Fama-French Research Portfolios and Factors\",\n        \"description\": \"\\n    This provider implements the Fama-French research portfolios and factors data library,\\n    maintained and hosted by Kenneth R. French at Dartmouth College.\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html\"\n    },\n    {\n        \"packageName\": \"openbb-federal-reserve\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve (FED)\",\n        \"description\": \"Access data provided by the Federal Reserve System, the Central Bank of the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.federalreserve.gov/data.htm\"\n    },\n    {\n        \"packageName\": \"openbb-finra\",\n        \"optional\": false,\n        \"reprName\": \"Financial Industry Regulatory Authority (FINRA)\",\n        \"description\": \"FINRA Data provides centralized access to the abundance of data FINRA\\nmakes available to the public, media, researchers and member firms.\",\n        \"credentials\": [],\n        \"website\": \"https://www.finra.org/finra-data\",\n        \"instructions\": \"This provider needs no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-fred\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve Economic Data | St. Louis FED (FRED)\",\n        \"description\": \"Federal Reserve Economic Data is a database maintained by the\\nResearch division of the Federal Reserve Bank of St. Louis that has more than\\n816,000 economic time series from various sources.\",\n        \"credentials\": [\n            \"fred_api_key\"\n        ],\n        \"deprecatedCredentials\": {\n            \"API_FRED_KEY\": \"fred_api_key\"\n        },\n        \"website\": \"https://fred.stlouisfed.org\",\n        \"instructions\": \"Go to: https://fred.stlouisfed.org\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827137-d143ba4c-72cb-467d-a7f4-5cc27c597aec.png)\\n\\nClick on, \\\"My Account\\\", create a new account or sign in with Google:\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827011-65cdd501-27e3-436f-bd9d-b0d8381d46a7.png)\\n\\nAfter completing the sign-up, go to \\\"My Account\\\", and select \\\"API Keys\\\". Then, click on, \\\"Request API Key\\\".\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827577-c869f989-4ef4-4949-ab57-6f3931f2ae9d.png)\\n\\nFill in the box for information about the use-case for FRED, and by clicking, \\\"Request API key\\\", at the bottom of the page, the API key will be issued.\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207828032-0a32d3b8-1378-4db2-9064-aa1eb2111632.png)\"\n    },\n    {\n        \"packageName\": \"openbb-government-us\",\n        \"optional\": false,\n        \"reprName\": \"Congress.gov\",\n        \"description\": \"Legislative data from the U.S. Congress. Bills, bill metadata,\\nsummaries, bill text, amendments, enacted laws, calendars, mandated reports, and\\ncommittees are all sourced from keyless public data (GovInfo bulk data, the\\nGovInfo link service, and the unitedstates dataset). No API key is required for\\nthe 108th Congress onward; older Congresses predate the GovInfo bulk archives\\nand are served from the Congress.gov API, which needs a free API key.\",\n        \"credentials\": [\n            \"congress_gov_api_key\"\n        ],\n        \"website\": \"https://www.govinfo.gov\",\n        \"instructions\": \"Most endpoints use public, keyless data from GovInfo (bulk data\\nand the link service) and the unitedstates dataset, so no credentials are\\nrequired for the 108th Congress (2003) onward.\\n\\nGovInfo does not publish bulk archives for earlier Congresses. Those requests\\nfall back to the Congress.gov API, which reaches back to the 93rd Congress\\n(1973) and requires an API key. Sign up for a free key at\\nhttps://api.congress.gov/sign-up/ and set it as 'congress_gov_api_key'.\"\n    },\n    {\n        \"packageName\": \"openbb-imf\",\n        \"optional\": false,\n        \"reprName\": \"International Monetary Fund (IMF) Data APIs\",\n        \"description\": \"Access International Monetary Fund (IMF) data APIs.\",\n        \"credentials\": [],\n        \"website\": \"https://datahelp.imf.org/knowledgebase/articles/667681-using-json-restful-web-service\"\n    },\n    {\n        \"packageName\": \"openbb-jodi\",\n        \"optional\": false,\n        \"reprName\": \"Joint Organisations Data Initiative (JODI) World Databases\",\n        \"description\": \"The Joint Organisations Data Initiative (JODI) provides free monthly petroleum and natural gas supply, demand, and stock statistics, self-reported by over 100 countries, through the JODI-Oil and JODI-Gas World Databases.\",\n        \"credentials\": [],\n        \"website\": \"https://www.jodidata.org\"\n    },\n    {\n        \"packageName\": \"openbb-nasdaq\",\n        \"optional\": false,\n        \"reprName\": \"Nasdaq\",\n        \"description\": \"Positioned at the nexus of technology and the capital markets, Nasdaq\\nprovides premier platforms and services for global capital markets and beyond with\\nunmatched technology, insights and markets expertise.\",\n        \"credentials\": [],\n        \"website\": \"https://www.nasdaq.com\"\n    },\n    {\n        \"packageName\": \"openbb-oecd\",\n        \"optional\": false,\n        \"reprName\": \"Organization for Economic Co-operation and Development (OECD)\",\n        \"description\": \"Access OECD data via the SDMX REST API.\\nCovers all OECD dataflows including GDP, CPI, unemployment,\\ninterest rates, and hundreds more.\",\n        \"credentials\": [],\n        \"website\": \"https://data-explorer.oecd.org/\"\n    },\n    {\n        \"packageName\": \"openbb-sec\",\n        \"optional\": false,\n        \"reprName\": \"Securities and Exchange Commission (SEC)\",\n        \"description\": \"SEC is the public listings regulatory body for the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.sec.gov/data\"\n    },\n    {\n        \"packageName\": \"openbb-tmx\",\n        \"optional\": false,\n        \"reprName\": \"TMX\",\n        \"description\": \"Unofficial TMX Data Provider Extension\\n    TMX Group Companies\\n        - Toronto Stock Exchange\\n        - TSX Venture Exchange\\n        - TSX Trust\\n        - Montr\\u00e9al Exchange\\n        - TSX Alpha Exchange\\n        - Shorcan\\n        - CDCC\\n        - CDS\\n        - TMX Datalinx\\n        - Trayport\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://www.tmx.com\"\n    },\n    {\n        \"packageName\": \"openbb-us-eia\",\n        \"optional\": false,\n        \"reprName\": \"U.S. Energy Information Administration (EIA) Open Data and API\",\n        \"description\": \"The U.S. Energy Information Administration is committed to its free and open data by making it available through an Application Programming Interface (API) and its open data tools. See https://www.eia.gov/opendata/ for more information.\",\n        \"credentials\": [\n            \"eia_api_key\"\n        ],\n        \"website\": \"https://eia.gov/\",\n        \"instructions\": \"Credentials are required for functions calling the EIA's API.\\n    Register for a free key here: https://www.eia.gov/opendata/register.php\"\n    }\n]","openbb_platform/extensions/crypto/README.md":"# Crypto data extension for OpenBB Platform\n\nThis extension provides a set of commands for crypto data retrieval.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-crypto\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","assets/extensions/obbject.json":"[\n    {\n        \"packageName\": \"openbb-charting\",\n        \"optional\": false,\n        \"description\": \"Create custom charts from OBBject data.\"\n    }\n]","openbb_platform/extensions/currency/README.md":"# OpenBB Currency Extension\n\nThis extension provides currency exchange related data for the OpenBB Platform.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-currency\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","openbb_platform/extensions/commodity/README.md":"# Commodity Extension for OpenBB Platform\n\nThis extension provides a set of commands for commodity-related data.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-commodity\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","openbb_platform/extensions/README.md":"# Extensions\n\nIn this folder you can find the extensions that were created or are supported by OpenBB.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/build_workspace_app/SKILL.md":"---\nname: build_workspace_app\ndescription: This guide covers the full lifecycle of building, running, and serving a custom OpenBB Workspace application from an extension project scaffolded by `openbb-cookiecutter`. It assumes the project shell already exists (see the `develop_extension` skill for scaffolding instructions).\n---\n\n# Build and Run OpenBB Workspace Applications\n\nThis guide covers the full lifecycle of building, running, and serving a custom\nOpenBB Workspace application from an extension project scaffolded by\n`openbb-cookiecutter`. It assumes the project shell already exists\n(see the `develop_extension` skill for scaffolding instructions).\n\n---\n\n## Prerequisites\n\nEnsure the following packages are installed in the active Python environment:\n\n```\npip install openbb-core openbb-platform-api openbb-devtools\n```\n\n- `openbb-core` provides the Router, Provider, OBBject, and Fetcher base classes.\n- `openbb-platform-api` provides the `openbb-api` CLI for serving backends\n  and auto-generating `widgets.json` for OpenBB Workspace.\n- `openbb-devtools` provides `pytest`, cassette recording, and QA utilities.\n\nIf you also need the Python Interface wrapper (the `obb` object), install the\nmain package:\n\n```\npip install openbb --no-deps\n```\n\n---\n\n## Architecture Overview\n\nOpenBB is built on FastAPI and Pydantic. The application has two independent\ninterfaces that share core logic and models:\n\n- **Python Interface** — wraps installed routers into an `obb` Python package\n  with auto-generated docstrings and function signatures. Requires a build step\n  (`openbb-build`) to generate static assets.\n- **REST API** — a FastAPI instance with all installed routers available via\n  HTTP. Import it with `from openbb_core.api.rest_api import app` or launch\n  with `uvicorn openbb_core.api.rest_api:app`.\n\nThe application is the product of all installed extensions. With just `openbb-core`\nthere are no routers or endpoints — users compose their own combinations.\n\n### Key Classes\n\n| Class | Import | Purpose |\n|---|---|---|\n| `Router` | `openbb_core.app.router` | Subclass of `fastapi.APIRouter`; defines commands |\n| `OBBject` | `openbb_core.app.model.obbject` | Standard response object with results, provider, warnings, extra |\n| `Provider` | `openbb_core.provider.abstract.provider` | Registers fetchers for the provider interface |\n| `Fetcher` | `openbb_core.provider.abstract.fetcher` | TET pipeline: Transform query → Extract data → Transform data |\n| `QueryParams` | `openbb_core.provider.abstract.query_params` | Base class for input parameters |\n| `Data` | `openbb_core.provider.abstract.data` | Base class for output data schemas |\n\n---\n\n## Router Extensions (API Endpoints)\n\nRouter extensions are the user-facing endpoints that power the REST API, MCP,\nand Python Interface.\n\n### Creating a Router\n\n```python\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\", description=\"My custom extension.\")\n```\n\nThe top-level API path prefix is determined by the entry point name in\n`pyproject.toml`, not the `prefix` argument. Only use `prefix` for sub-routers.\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.routers.my_router:router\"\n```\n\nCommands appear at `/my_app/...` in the API and `obb.my_app.` in Python.\n\n### Provider Interface Endpoints\n\nConnect a router command to one or more provider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters and the `OBBject.from_query(Query(**locals()))` return are\nmandatory and must be used exactly as shown.\n\n### Basic GET Endpoint\n\n```python\n@router.command(methods=[\"GET\"])\nasync def hello() -> OBBject[str]:\n    \"\"\"OpenBB Hello World.\"\"\"\n    return OBBject(results=\"Hello from the extension!\")\n```\n\n### Basic POST Endpoint\n\n```python\nfrom openbb_core.provider.abstract.data import Data\n\n\n@router.command(methods=[\"POST\"])\nasync def process(data: Data, some_param: str) -> OBBject:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results=data.model_dump())\n```\n\n### Decorator Parameters\n\n`@router.command` accepts:\n- `model` — metamodel name linking to Provider fetchers\n- `methods` — list of HTTP methods, typically `[\"GET\"]` or `[\"POST\"]`\n- `examples` — list of `APIEx` or `PythonEx` for docs\n- `deprecated` — deprecation notice\n- `no_validate` — skip response validation, treat output as `Any`\n- `openapi_extra` — dictionary for inline `widget_config` or `mcp_config`\n\nFields of a response model marked\n`Field(json_schema_extra={\"exclude_from_api\": True})` are dropped from REST\nAPI responses and kept in the Python Interface, as `Chart.fig` is.\n\n### Using FastAPI APIRouter Directly\n\nAccess the underlying FastAPI router via `router._api_router`:\n\n```python\n@router._api_router.get(\"/also_empty\")\nasync def also_empty(param: str) -> str:\n    \"\"\"Also empty.\"\"\"\n    return \"Hello world!\"\n```\n\n---\n\n## From FastAPI — Converting Existing Apps\n\nAny existing FastAPI application can become an OpenBB extension without changing\ncode. Define the entry point in `pyproject.toml` pointed at the FastAPI or\nAPIRouter instance:\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.app:app\"\n```\n\nInstall the package and build static assets:\n\n```\npip install -e .\nopenbb-build\n```\n\nKnown limitations:\n- Authorization hooks are not injected into the Python Interface\n- Request-bound dependencies or those returning None are not injected\n- WebSockets are not supported in the Python Interface\n- Multi-method routes (GET + POST on same path) may not generate correctly\n\n---\n\n## Serving with openbb-api (Workspace Backends)\n\nThe `openbb-api` CLI converts a FastAPI instance into an OpenBB Workspace\nbackend with auto-generated widget definitions.\n\n### Basic Usage\n\n```\n# Start with default OpenBB extensions\nopenbb-api\n\n# Start with a custom FastAPI file\nopenbb-api --app ./my_app.py --host 0.0.0.0 --port 8005\n\n# Factory function pattern\nopenbb-api --app my_app.py:create_app --factory\n\n# Custom FastAPI instance name\nopenbb-api --app my_app.py --name my_app\n```\n\nDefaults are `--host 127.0.0.1 --port 6900`, falling back to the next\navailable port if already in use.\n\n### Key Arguments\n\n| Argument | Description |\n|---|---|\n| `--app` | Path to Python file with a FastAPI instance |\n| `--name` | Name of the FastAPI instance (default: `app`) |\n| `--factory` | Flag if the app name is a factory function |\n| `--editable` | Make `widgets.json` editable at runtime |\n| `--no-build` | Load existing `widgets.json` without checking for updates |\n| `--exclude` | JSON list of API paths to exclude from widgets |\n| `--widgets-json` | Custom path for `widgets.json` |\n| `--apps-json` | Custom path for `workspace_apps.json` |\n\nAll remaining arguments are passed to `uvicorn.run`.\n\n---\n\n## Inline Widget Definitions\n\nWidget properties can be defined inline in your code via `openapi_extra`:\n\n```python\n@app.get(\n    \"/some_endpoint\",\n    openapi_extra={\n        \"widget_config\": {\n            \"name\": \"Custom Widget Name\",\n            \"description\": \"Override docstring description\",\n        }\n    },\n)\nasync def some_endpoint():\n    \"\"\"Description from docstring.\"\"\"\n    pass\n```\n\n### Exclude an Endpoint from Widgets\n\n```python\n@app.get(\n    \"/internal_endpoint\",\n    openapi_extra={\"widget_config\": {\"exclude\": True}},\n)\nasync def internal_endpoint():\n    return [{\"label\": \"Choice 1\", \"value\": \"choice1\"}]\n```\n\n### Dropdown Parameters\n\nDropdowns are auto-generated from `Literal` types:\n\n```python\nfrom typing import Literal\n\n\n@app.get(\"/with_dropdown\")\nasync def with_dropdown(\n    choices: Literal[\"Choice 1\", \"Choice 2\", \"Choice 3\"] = \"Choice 3\",\n):\n    pass\n```\n\n### Column Definitions for Tables\n\nUse Pydantic response models to auto-generate table column definitions:\n\n```python\nimport datetime\nfrom pydantic import BaseModel, Field\n\n\nclass MyData(BaseModel):\n    date: datetime.date = Field(description=\"The date.\")\n    value: float = Field(description=\"The value.\")\n\n\n@app.get(\"/my_data\")\nasync def my_data() -> list[MyData]:\n    \"\"\"Widget with typed columns.\"\"\"\n    return [MyData(date=datetime.date.today(), value=42.0)]\n```\n\n### Widget Types by Return Type\n\n| Return Type | Widget Type |\n|---|---|\n| `list[dict]` or `list[BaseModel]` | Table (AgGrid) |\n| `str` | Markdown |\n| `dict` (with `widget_config.type=\"chart\"`) | Plotly Chart |\n| `MetricResponseModel` | Metric |\n| `PdfResponseModel` | PDF |\n\n### Response Models for Special Widgets\n\n```python\nfrom openbb_platform_api.response_models import MetricResponseModel, PdfResponseModel\n\n\n@app.get(\"/metric\", response_model=MetricResponseModel)\nasync def metric():\n    \"\"\"A metric widget.\"\"\"\n    return dict(label=\"Revenue\", value=12345, delta=5.67)\n\n\n@app.get(\"/pdf\", response_model=PdfResponseModel)\nasync def open_pdf(file_path: str):\n    \"\"\"Open a PDF document.\"\"\"\n    with open(file_path, \"rb\") as f:\n        return dict(content=f.read())\n```\n\n### Plotly Chart Widget\n\n```python\n@app.get(\n    \"/chart\",\n    openapi_extra={\"widget_config\": {\"type\": \"chart\"}},\n)\nasync def chart() -> dict:\n    \"\"\"A chart widget.\"\"\"\n    from plotly.graph_objs import Bar, Layout, Figure\n\n    fig = Figure(\n        data=[Bar(x=[\"A\", \"B\", \"C\"], y=[1, 2, 3])],\n        layout=Layout(title=\"My Chart\", template=\"plotly_dark\"),\n    )\n    return fig.to_plotly_json()\n```\n\n### JSON Schema Extra for Parameters\n\nAnnotate parameters with additional widget configuration:\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query\n\nmy_param: Annotated[\n    str,\n    Query(\n        title=\"My Title\",\n        description=\"Detailed hover text\",\n        json_schema_extra={\n            \"x-widget_config\": {\"optionsEndpoint\": \"/my_choices_endpoint\"}\n        },\n    ),\n]\n```\n\n### Form Input Widget\n\nCreate an input form tied to a table:\n- GET endpoint defines `widget_config.form_endpoint` pointing to the POST route\n- POST route accepts a single Pydantic model argument\n\n---\n\n## OBBject Extensions (Result Post-Processing)\n\nExtend the `OBBject` response with custom methods, accessible in the Python Interface.\n\n### Class Accessor (Namespaced Methods)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self):\n        \"\"\"Return a summary.\"\"\"\n        return self._obbject.to_dataframe().describe()\n```\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_tools = \"my_package.obbject.my_ext:ext\"\n```\n\n### Callable Accessor (Property-Like)\n\n```python\next = Extension(name=\"to_csv\", description=\"Convert results to CSV.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject):\n    \"\"\"Convert to CSV string.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### OBBject Output Conversion Methods\n\nEvery `OBBject` has built-in conversion methods:\n- `to_df()` / `to_dataframe()` — Pandas DataFrame\n- `to_dict(orientation=...)` — Python dict\n- `to_numpy()` — NumPy array\n- `to_polars()` — Polars DataFrame (requires `polars` installed)\n- `model_dump()` — Complete object as dict\n- `model_dump_json()` — Serialized JSON string\n\n---\n\n## OBBject Plugins (Pre-Return Interceptors)\n\nPlugins execute before the response is returned, compatible with both REST API\nand Python Interface. They can conditionally alter the output of any command.\n\n**WARNING**: Plugins are considered potentially dangerous. The environment must\nbe explicitly configured to allow them.\n\nIn `system_settings.json`:\n\n```json\n{\n    \"allow_on_command_output\": true,\n    \"allow_mutable_extensions\": true\n}\n```\n\n### Plugin Configuration\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\nplugin = Extension(\n    name=\"my_plugin\",\n    description=\"Intercept output before return.\",\n    on_command_output=True,\n    command_output_paths=[\"/my_router/my_command\"],\n    immutable=False,\n    results_only=False,\n)\n```\n\nKey parameters:\n- `on_command_output=True` — required for plugins\n- `command_output_paths` — list of endpoint paths to intercept (None = all)\n- `immutable=False` — set to allow modifying the response object\n- `results_only=True` — receive only the `results` portion instead of full OBBject\n\n### Plugin Code\n\n```python\n@plugin.obbject_accessor\ndef my_plugin_func(obbject):\n    \"\"\"Modify or inspect the response before it returns.\"\"\"\n    # Modify obbject directly; do NOT return anything\n    pass\n```\n\n---\n\n## Charting Extensions (Views)\n\nAdd custom chart views to any router endpoint, activated when the user sets\n`chart=True`.\n\n### Structure\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Chart for my_command.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build chart with fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_views:MyViews\"\n```\n\n### Kwargs Available in Views\n\n| Key | Content |\n|---|---|\n| `obbject_item` | Validated results object |\n| `charting_settings` | User charting preferences |\n| `standard_params` | Standard model parameters |\n| `extra_params` | Provider-specific parameters |\n| `provider` | Provider name used |\n| `extra` | Execution metadata |\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## HTTP Requests in Fetchers\n\nUse the built-in utilities instead of creating new clients from scratch.\n\n### Query String Helper\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"exclude_this_param\"])\n```\n\n### Synchronous Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\nresponse = make_request(url, headers=headers, params=params)\n```\n\n### Requests Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\n```\n\n### Asynchronous Requests (AIOHTTP)\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresponse_json = await amake_request(url)\n```\n\n### Multi-URL Async Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nresults = await amake_requests([url1, url2, url3])\n```\n\n### Custom Response Callback\n\n```python\nfrom io import StringIO\nfrom pandas import read_csv\n\nresults = []\n\n\nasync def response_callback(response, _):\n    text = await response.text()\n    data = read_csv(StringIO(text), skiprows=2)\n    results.extend(data.to_dict(\"records\"))\n\n\nawait amake_requests([url1, url2], response_callback=response_callback)\n```\n\n### Async Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with await session.get(url) as response:\n        data = await response.json()\n```\n\n### Async Fetchers\n\nUse `aextract_data` instead of `extract_data` for async fetchers:\n\n```python\n@staticmethod\nasync def aextract_data(\n    query: MyQueryParams,\n    credentials: dict[str, str] | None,\n    **kwargs: Any,\n) -> list[dict]:\n    \"\"\"Async data extraction.\"\"\"\n    ...\n```\n\n---\n\n## Testing\n\n### Built-In Fetcher Test\n\nEvery Fetcher has a `.test()` method for quick validation:\n\n```python\nfrom my_package.providers.my_provider.models.my_model import MyFetcher\n\nfetcher = MyFetcher()\nfetcher.test({\"symbol\": \"AAPL\"}, {})  # Returns None on success\n```\n\n### Unit Tests with Cassettes\n\nInstall dev tools and use `pytest_recorder` for HTTP cassette recording:\n\n```\npip install openbb-devtools\npytest test_my_fetcher.py --record http\n```\n\nSubsequent test runs replay the recorded HTTP interactions.\n\n### Running Tests\n\n```\n# Unit tests only\npytest tests/ -m \"not integration\"\n\n# Integration tests only\npytest tests/ -m integration\n\n# All tests\npytest tests/\n```\n\n### Integration Testing\n\nFor API integration tests, start a local server first:\n\n```\nuvicorn openbb_core.api.rest_api:app --host 0.0.0.0 --port 8000 --reload\n```\n\n---\n\n## Install and Build\n\n### Development Install\n\nFrom the project root:\n\n```\nuv pip install -e . --group dev\n```\n\n### Build Python Interface\n\nAfter installing or removing extensions, regenerate static assets:\n\n```\nopenbb-build\n```\n\n### Verify in Python\n\n```python\nfrom openbb import obb\n# Your commands appear under obb.<router_name>.<command_name>()\n```\n\n### Serve as Workspace Backend\n\n```\nopenbb-api --app ./my_app.py --editable --host 0.0.0.0 --port 6900\n```\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me a Workspace application that does X\":\n\n1. **Scaffold** the project with `openbb-cookiecutter` (see `develop_extension` skill).\n2. **Remove** example files and clean up boilerplate.\n3. **Implement fetchers** for each data source in `providers/<name>/models/`.\n4. **Register fetchers** in `providers/<name>/__init__.py` via `fetcher_dict`.\n5. **Implement router commands** in `routers/<name>.py`.\n6. **Add widget config** via `openapi_extra` for Workspace-specific behavior.\n7. **Update `pyproject.toml`** entry points and dependencies.\n8. **Install** with `uv pip install -e . --group dev`.\n9. **Build** static assets with `openbb-build`.\n10. **Serve** with `openbb-api` and verify widgets in OpenBB Workspace.\n11. **Test** with `pytest` and fetcher `.test()` methods.\n","openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/use_openbb_cli/SKILL.md":"---\nname: use_openbb_cli\ndescription: This guide covers the `openbb` command-line surface — running commands, generating `.spec` files, dispatching against precomputed specs, and feeding spec output back into `openbb-mcp` for spec-driven proxy mode.\n---\n\n# Using the openbb-cli\n\n`openbb-cli` ships the `openbb` console-script. It is a self-contained\nfront-end for the same `obb` namespace the MCP server exposes, plus a build\ntool for generating reusable `.spec` files and full installable extension\npackages.\n\nThree console-scripts live on the v5 stack:\n\n| Command | Package | Purpose |\n|---|---|---|\n| `openbb` | `openbb-cli` | Run commands, REPL, generate `.spec` files. |\n| `openbb-mcp` | `openbb-mcp-server` | MCP server launcher (HTTP/SSE/stdio transports). |\n| `openbb-build` | `openbb-core` | Register a newly installed extension into the `obb` namespace. |\n\nThis guide focuses on `openbb`.\n\n---\n\n## Installation\n\n```\npip install openbb-cli\n```\n\nPulls `openbb-core[pandas]`, `prompt-toolkit`, `rich`, and `httpx`.\n\n---\n\n## Execution Modes\n\n`openbb` has six mutually-exclusive modes (the rest of the flags are shared\nacross modes).\n\n| Mode flag | Purpose |\n|---|---|\n| *(default)* | Single-shot: dispatch one dotted command and exit with NDJSON on stdout. |\n| `-i` / `--interactive` | Rich REPL with prompt-toolkit completion. |\n| `--batch` | Read NDJSON requests from stdin; emit NDJSON responses to stdout. |\n| `--generate-spec` | Fetch `openapi.json` from `--server` and write a precomputed `.spec` file to `--output`. |\n| `--generate-extension` | Build a full installable OpenBB Platform extension package from a `.spec` file. |\n| `--list-commands` / `--describe COMMAND[:PROVIDER]` | Print the command catalog or one command's schema as JSON. Needs `--spec` or `--server`. |\n\n### Single-shot\n\n```bash\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n```\n\nOutput is one dispatcher response as a JSON line: `{\"ok\": true, \"result\": ...}`\non success, where `result` holds the command's OBBject fields (`results`,\n`provider`, ...), or `{\"ok\": false, \"error\": {\"type\": ..., \"message\": ...}}` on\nfailure. Unset fields are left out.\nExit code is `0` on success, `1` on a dispatch error, `2` on a usage error.\n\n### Interactive REPL\n\n```bash\nopenbb -i\n```\n\nPrompt-toolkit completion against the resolved command catalog. Same flag\nsurface — `--server` / `--spec` / `--header` etc. work identically.\n\n### Batch (NDJSON in/out)\n\n```bash\necho '{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"AAPL\"},\"id\":\"a\"}\n{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"MSFT\"},\"id\":\"b\"}' \\\n  | openbb --batch\n```\n\nEach input line is one `Request`; each output line is the matching\n`Response`. Use `--batch-concurrency N` (or `OPENBB_CLI_BATCH_CONCURRENCY`) to\ncontrol in-flight parallelism.\n\n---\n\n## Backend Selection\n\nThree backends share the dispatch surface; selection priority is\n`--spec` > `--server` > in-process `obb`.\n\n| Backend | Trigger | Behavior |\n|---|---|---|\n| Local | (default) | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. |\n| Remote HTTP | `--server URL` | `HttpDispatcher` proxies to an `openbb-platform-api` server. The heavy import lives on the server. |\n| Spec-driven | `--spec [NAME=]PATH` | Skips OpenAPI fetch + parse entirely. The spec carries the server URL it was generated against and the per-command HTTP methods. |\n\n```bash\nopenbb --server https://api.example.com economy.gdp\nopenbb --spec /etc/openbb/cli.spec economy.gdp\n```\n\n`--spec` is repeatable. Multiple `NAME=PATH` entries build a multi-spec\ndispatcher and the REPL routes by leading namespace\n(`congress.bill` → `congress` spec, `nyfed.markets.ambs` → `nyfed` spec).\nA single unnamed `--spec PATH` keeps the flat (unprefixed) command surface.\n\n---\n\n## `.spec` Files\n\nA `.spec` file is a precomputed JSON document encoding every command the\nserver exposes — URL path, HTTP method, parameter schema, response schema,\nand a `content_sha256` integrity hash over the canonical-JSON body.\n\n### Generating a spec\n\n```bash\n# From an openbb-platform-api server\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# From a server using a non-default OpenAPI path\nopenbb --generate-spec \\\n  --server https://markets.example.com \\\n  --openapi-path /static/docs/markets-api.yml \\\n  --output markets.spec\n\n# From a Socrata story (no OpenAPI involved — walks the story for datasetUid\n# entries and emits one router namespace per dataset)\nopenbb --generate-spec \\\n  --socrata-story https://data.example.gov/path/to/story.json \\\n  --output socrata.spec\n```\n\n### Using a spec\n\nAfter generation, every dispatch can use the spec to skip OpenAPI fetch +\nparse on every call:\n\n```bash\nopenbb --spec cli.spec economy.gdp\nopenbb --spec cli.spec --list-commands\nopenbb --spec cli.spec --describe equity.price.historical\nopenbb --spec cli.spec --describe equity.price.historical:yfinance\n```\n\nThe spec carries the upstream `base_url` it was generated against, so\n`--server` is not needed alongside `--spec`.\n\n### Filtering commands\n\n`--include` and `--exclude` are repeatable glob patterns over dotted command\nnames, applied by `--generate-extension` (`--generate-spec` always writes every\ncommand). When `--include` is supplied it takes priority — anything not\nmatching is dropped regardless of `--exclude`.\n\n```bash\nopenbb --generate-extension \\\n  --spec cli.spec \\\n  --output ./openbb-equity-subset \\\n  --include 'equity.*' \\\n  --exclude 'equity.fundamentals.*'\n```\n\n---\n\n## Headers and Query Parameters\n\nEvery dispatch (and the OpenAPI fetch during `--generate-spec`) carries the\nsame headers/query-params.\n\n```bash\n# Per-flag, repeatable\nopenbb --server https://api.example.com \\\n  -H 'Authorization: Bearer xxx' \\\n  -H 'X-Tenant: acme' \\\n  -Q 'api_key=secret' \\\n  equity.price.quote --symbol AAPL\n\n# From a JSON file (object of string values)\nopenbb --header-file ./headers.json --query-param-file ./query.json economy.gdp\n```\n\nEnv-var auto-loading: any `OPENBB_HTTP_QUERY_*` env var becomes a query param\n(`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). CLI flags beat env vars.\n\nFor multi-spec dispatchers, prefix a header or query parameter with a\nnamespace to send it to that spec only (`-H 'congress:X-API-Key: xxx'`,\n`-Q 'nyfed:format=json'`), or scope it in the TOML config\n(`[specs.<ns>.headers]`, `[specs.<ns>.query]`).\n\n---\n\n## Configuration File (`openbb.toml`)\n\nFlag defaults can be set via a layered TOML cascade — same pattern openbb-core\nuses. Layers (highest priority last):\n\n```\npyproject.toml [tool.openbb-cli]\n  → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD)\n  → --config PATH (explicit)\n  → .env files\n  → real shell env vars\n```\n\nCLI flags always beat env vars; env vars beat TOML.\n\nThe CLI reads top-level keys of `openbb.toml` (and of `[tool.openbb-cli]` in\n`pyproject.toml`): `server`, `spec`, `openapi-path`, `header-file`,\n`query-param-file`, `auth-hook`, and the `[headers]`, `[query]`,\n`[specs.<ns>]`, and `[settings]` tables. Set the batch concurrency with\n`--batch-concurrency` or `OPENBB_CLI_BATCH_CONCURRENCY`.\n\n```toml\n# openbb.toml\nserver = \"https://api.example.com\"\n\n[headers]\nAuthorization = \"Bearer ...\"\n\n[specs.congress]\npath = \"/etc/openbb/congress.spec\"\n[specs.congress.headers]\n\"X-API-Key\" = \"...\"\n```\n\nHeader and query values are sent as written; they are not expanded from\n`$VAR` references.\n\n```bash\nopenbb --config /etc/openbb/openbb.toml --list-commands\nopenbb --print-config-template      # documented TOML template\nopenbb --show-config                # merged config as JSON\n```\n\n---\n\n## Building Extensions From a Spec\n\n`--generate-extension` builds a full installable OpenBB Platform extension\npackage from exactly one `.spec` file. The project is written to\n`<output>/<project-name>`; the command prints that path with its providers,\nrouters, and install commands.\n\n```bash\nopenbb --generate-extension \\\n  --spec congress.spec \\\n  --output ./extensions \\\n  --provider-name congress_gov \\\n  --project-name openbb-congress-gov \\\n  --package-name openbb_congress_gov\n\npip install -e ./extensions/openbb-congress-gov\nopenbb-build\n```\n\n`--project-name` defaults to `openbb-<provider-name>`, `--package-name` to\n`openbb_<provider-name>`, and `--provider-name` to the name of the `--output`\ndirectory. `--router-name` is accepted but not used by the generator yet.\n\nAfter registration the extension's commands are reachable under the printed\nrouters, as `obb.<router>.*` in Python and `<router>.*` from any backend.\n\n---\n\n## Spec → MCP Round-Trip\n\nThe same `.spec` file can drive the MCP server in spec-driven proxy mode —\nthe heavy `import openbb` lives on the upstream `openbb-platform-api`, the\nMCP launcher only knows the spec.\n\n```bash\n# 1. Generate the spec on the host that already has openbb installed.\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# 2. Hand off the spec to a lightweight MCP container.\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML:\n\n```toml\n# openbb.toml passed via `openbb-mcp --config-file openbb.toml`\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\ncontent_sha256 = \"abc...\"\n\n[mcp.spec.headers]\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\n```\n\nThe MCP server synthesizes a FastAPI proxy app from the spec's commands;\nFastMCP exposes the routes as MCP tools; per-tool dispatch forwards to the\nspec's `base_url` over `aiohttp`. See `configure_mcp_server` for multi-spec\nmounts (`[mcp.spec.NAME]`), per-spec auth/middleware hooks, and the\n`content_sha256` provenance pin.\n\n---\n\n## The Same Commands as MCP Tools\n\nWhen `openbb-cli` is installed next to `openbb-mcp-server`, the MCP server\nexposes the CLI's dispatcher as tools, so an agent gets the same surface:\n\n| CLI | MCP tool |\n|---|---|\n| `openbb <command> --param value` | `openbb_dispatch` (`command`, `params`) |\n| `openbb --batch` | `openbb_batch_dispatch` (`requests`) |\n| `openbb --list-commands` | `openbb_list_commands` |\n| `openbb --describe <command>` | `openbb_describe_command` (`command`, optional `provider`) |\n\nEvery tool takes an optional `server_url`, the counterpart of `--server`, and\nfalls back to `OPENBB_SERVER_URL` like the CLI does.\n\n---\n\n## Useful Environment Variables\n\n| Variable | Purpose |\n|---|---|\n| `OPENBB_SERVER_URL` | Default `--server` URL. |\n| `OPENBB_SPEC_PATH` | Default `--spec` path. |\n| `OPENBB_HTTP_QUERY_*` | Auto-loaded as query parameters (`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). |\n| `OPENBB_CLI_CONFIG` | Default `--config` path. |\n| `OPENBB_CLI_ENV_FILE` | Default `--env-file` path. |\n| `OPENBB_CLI_BATCH_CONCURRENCY` | Default `--batch-concurrency`. |\n| `OPENBB_HEADER_FILE` / `OPENBB_QUERY_PARAM_FILE` | Default `--header-file` / `--query-param-file`. |\n\n`~/.openbb_platform/.env` is always loaded as a fallback `.env` source — real\nshell exports beat both.\n\n---\n\n## Quick Reference\n\n```bash\n# Discover commands (needs --server or --spec)\nopenbb --server URL --list-commands\nopenbb --server URL --describe equity.price.historical\n\n# One-shot dispatch\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n\n# Batch (NDJSON)\ncat requests.ndjson | openbb --batch\n\n# Generate a precomputed spec from a server\nopenbb --generate-spec --server URL --output out.spec\n\n# Dispatch through a precomputed spec\nopenbb --spec out.spec equity.price.quote --symbol AAPL\n\n# Multi-spec routing by leading namespace\nopenbb --spec congress=congress.spec --spec nyfed=nyfed.spec congress.bill\n\n# Generate a full extension package from a spec\nopenbb --generate-extension --spec out.spec --output ./openbb-myprov\n\n# Inspect resolved config\nopenbb --show-config\nopenbb --print-config-template > openbb.toml\n```\n","openbb_platform/extensions/derivatives/README.md":"# derivatives Documentation\n\nPath: `openbb_platform/extensions/derivatives/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/derivatives/README.md)","openbb_platform/extensions/devtools/README.md":"# devtools Documentation\n\nPath: `openbb_platform/extensions/devtools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/devtools/README.md)","openbb_platform/extensions/econometrics/README.md":"# econometrics Documentation\n\nPath: `openbb_platform/extensions/econometrics/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/econometrics/README.md)","openbb_platform/extensions/economy/README.md":"# economy Documentation\n\nPath: `openbb_platform/extensions/economy/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/economy/README.md)","openbb_platform/extensions/equity/README.md":"# equity Documentation\n\nPath: `openbb_platform/extensions/equity/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/equity/README.md)","openbb_platform/extensions/etf/README.md":"# etf Documentation\n\nPath: `openbb_platform/extensions/etf/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/etf/README.md)","openbb_platform/extensions/fixedincome/README.md":"# fixedincome Documentation\n\nPath: `openbb_platform/extensions/fixedincome/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/fixedincome/README.md)","openbb_platform/extensions/index/README.md":"# index Documentation\n\nPath: `openbb_platform/extensions/index/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/index/README.md)","openbb_platform/extensions/mcp_server/README.md":"# mcp_server Documentation\n\nPath: `openbb_platform/extensions/mcp_server/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/README.md)","openbb_platform/extensions/news/README.md":"# news Documentation\n\nPath: `openbb_platform/extensions/news/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/news/README.md)","openbb_platform/extensions/platform_api/README.md":"# platform_api Documentation\n\nPath: `openbb_platform/extensions/platform_api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/README.md)","openbb_platform/extensions/quantitative/README.md":"# quantitative Documentation\n\nPath: `openbb_platform/extensions/quantitative/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/quantitative/README.md)","openbb_platform/extensions/technical/README.md":"# technical Documentation\n\nPath: `openbb_platform/extensions/technical/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/README.md)","openbb_platform/obbject_extensions/charting/README.md":"# charting Documentation\n\nPath: `openbb_platform/obbject_extensions/charting/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/obbject_extensions/charting/README.md)","openbb_platform/extensions/technical/docs/README.md":"# docs Documentation\n\nPath: `openbb_platform/extensions/technical/docs/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/README.md)","openbb_platform/extensions/technical/docs/api-reference.md":"# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/api-reference.md)","openbb_platform/extensions/technical/docs/getting-started.md":"# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/getting-started.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/getting-started.md)","openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json":"# Assets Skill\n\nPath: `openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json)","openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json":"# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json)","openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json":"# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json)","openbb_platform/extensions/technical/docs/user-guides/01-indicators.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/01-indicators.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/01-indicators.md)","openbb_platform/extensions/technical/docs/user-guides/02-signals.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/02-signals.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/02-signals.md)","openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md)","openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md)"},"items":[{"name":"obbject.json","path":"assets/extensions/obbject.json","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/assets/extensions/obbject.json","title":"Extensions Skill","category":"anthropic-skill","format":"json","content":"[\n    {\n        \"packageName\": \"openbb-charting\",\n        \"optional\": false,\n        \"description\": \"Create custom charts from OBBject data.\"\n    }\n]","isInternal":false,"tokens":36,"sizeBytes":149},{"name":"provider.json","path":"assets/extensions/provider.json","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/assets/extensions/provider.json","title":"Extensions Skill","category":"anthropic-skill","format":"json","content":"[\n    {\n        \"packageName\": \"openbb-bls\",\n        \"optional\": false,\n        \"reprName\": \"Bureau of Labor Statistics' (BLS) Public Data API\",\n        \"description\": \"The Bureau of Labor Statistics' (BLS) Public Data Application Programming Interface (API) gives the public access to economic data from all BLS programs. It is the Bureau's hope that talented developers and programmers will use the BLS Public Data API to create original, inventive applications with published BLS data.\",\n        \"credentials\": [\n            \"bls_api_key\"\n        ],\n        \"website\": \"https://www.bls.gov/developers/api_signature_v2.htm\",\n        \"instructions\": \"Sign up for a free API key here: https://data.bls.gov/registrationEngine/\"\n    },\n    {\n        \"packageName\": \"openbb-cboe\",\n        \"optional\": false,\n        \"reprName\": \"Chicago Board Options Exchange (CBOE)\",\n        \"description\": \"Cboe is the world's go-to derivatives and exchange network,\\ndelivering cutting-edge trading, clearing and investment solutions to people\\naround the world.\",\n        \"credentials\": [],\n        \"website\": \"https://www.cboe.com\"\n    },\n    {\n        \"packageName\": \"openbb-cftc\",\n        \"optional\": false,\n        \"reprName\": \"Commodity Futures Trading Commission (CFTC)\",\n        \"description\": \"The mission of the Commodity Futures Trading Commission (CFTC) is to promote the integrity,\\n    resilience, and vibrancy of the U.S. derivatives markets through sound regulation.\\n    Commitment of Traders reports are sourced from the CFTC Public Reporting API. Swap transaction\\n    and pricing data is sourced from the DTCC Public Price Dissemination service, the swap data\\n    repository that publishes CFTC-jurisdiction transactions. Overnight index swap curves and FX\\n    forward points are constructed from those executed transactions.\",\n        \"credentials\": [\n            \"cftc_app_token\"\n        ],\n        \"website\": \"https://cftc.gov/\",\n        \"instructions\": \"Credentials are not required, but your IP address may be subject to throttling limits\\n    on the Commitment of Traders endpoints. API requests made using an application token are not throttled.\\n    Create an account here: https://evergreen.data.socrata.com/signup\\n    and then generate the app_token by signing in with the credentials\\n    here: https://publicreporting.cftc.gov/profile/edit/developer_settings.\\n    The DTCC Public Price Dissemination endpoints are public and require no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-deribit\",\n        \"optional\": false,\n        \"reprName\": \"Deribit Public Data\",\n        \"description\": \"Deribit is a crypto derivatives exchange listing futures, perpetuals,\\noptions, and spot pairs. This extension covers its public market data API, which\\nneeds no credentials and carries no trading.\",\n        \"credentials\": [],\n        \"website\": \"https://deribit.com/\",\n        \"instructions\": \"This provider needs no credentials and is not meant for trading.\"\n    },\n    {\n        \"packageName\": \"openbb-ecb\",\n        \"optional\": false,\n        \"reprName\": \"European Central Bank (ECB)\",\n        \"description\": \"The ECB Data Portal provides access to all official ECB statistics\\nthrough the SDMX 2.1 REST API, plus euro reference exchange rates, key ECB interest\\nrates, the euro short-term rate (\\u20acSTR), MFI/bank interest rates, the statistical\\nrelease calendar, ECB releases & publications, and the Eurosystem list of eligible\\ncollateral assets.\",\n        \"credentials\": [],\n        \"website\": \"https://data.ecb.europa.eu\"\n    },\n    {\n        \"packageName\": \"openbb-famafrench\",\n        \"optional\": false,\n        \"reprName\": \"Fama-French Research Portfolios and Factors\",\n        \"description\": \"\\n    This provider implements the Fama-French research portfolios and factors data library,\\n    maintained and hosted by Kenneth R. French at Dartmouth College.\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html\"\n    },\n    {\n        \"packageName\": \"openbb-federal-reserve\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve (FED)\",\n        \"description\": \"Access data provided by the Federal Reserve System, the Central Bank of the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.federalreserve.gov/data.htm\"\n    },\n    {\n        \"packageName\": \"openbb-finra\",\n        \"optional\": false,\n        \"reprName\": \"Financial Industry Regulatory Authority (FINRA)\",\n        \"description\": \"FINRA Data provides centralized access to the abundance of data FINRA\\nmakes available to the public, media, researchers and member firms.\",\n        \"credentials\": [],\n        \"website\": \"https://www.finra.org/finra-data\",\n        \"instructions\": \"This provider needs no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-fred\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve Economic Data | St. Louis FED (FRED)\",\n        \"description\": \"Federal Reserve Economic Data is a database maintained by the\\nResearch division of the Federal Reserve Bank of St. Louis that has more than\\n816,000 economic time series from various sources.\",\n        \"credentials\": [\n            \"fred_api_key\"\n        ],\n        \"deprecatedCredentials\": {\n            \"API_FRED_KEY\": \"fred_api_key\"\n        },\n        \"website\": \"https://fred.stlouisfed.org\",\n        \"instructions\": \"Go to: https://fred.stlouisfed.org\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827137-d143ba4c-72cb-467d-a7f4-5cc27c597aec.png)\\n\\nClick on, \\\"My Account\\\", create a new account or sign in with Google:\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827011-65cdd501-27e3-436f-bd9d-b0d8381d46a7.png)\\n\\nAfter completing the sign-up, go to \\\"My Account\\\", and select \\\"API Keys\\\". Then, click on, \\\"Request API Key\\\".\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827577-c869f989-4ef4-4949-ab57-6f3931f2ae9d.png)\\n\\nFill in the box for information about the use-case for FRED, and by clicking, \\\"Request API key\\\", at the bottom of the page, the API key will be issued.\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207828032-0a32d3b8-1378-4db2-9064-aa1eb2111632.png)\"\n    },\n    {\n        \"packageName\": \"openbb-government-us\",\n        \"optional\": false,\n        \"reprName\": \"Congress.gov\",\n        \"description\": \"Legislative data from the U.S. Congress. Bills, bill metadata,\\nsummaries, bill text, amendments, enacted laws, calendars, mandated reports, and\\ncommittees are all sourced from keyless public data (GovInfo bulk data, the\\nGovInfo link service, and the unitedstates dataset). No API key is required for\\nthe 108th Congress onward; older Congresses predate the GovInfo bulk archives\\nand are served from the Congress.gov API, which needs a free API key.\",\n        \"credentials\": [\n            \"congress_gov_api_key\"\n        ],\n        \"website\": \"https://www.govinfo.gov\",\n        \"instructions\": \"Most endpoints use public, keyless data from GovInfo (bulk data\\nand the link service) and the unitedstates dataset, so no credentials are\\nrequired for the 108th Congress (2003) onward.\\n\\nGovInfo does not publish bulk archives for earlier Congresses. Those requests\\nfall back to the Congress.gov API, which reaches back to the 93rd Congress\\n(1973) and requires an API key. Sign up for a free key at\\nhttps://api.congress.gov/sign-up/ and set it as 'congress_gov_api_key'.\"\n    },\n    {\n        \"packageName\": \"openbb-imf\",\n        \"optional\": false,\n        \"reprName\": \"International Monetary Fund (IMF) Data APIs\",\n        \"description\": \"Access International Monetary Fund (IMF) data APIs.\",\n        \"credentials\": [],\n        \"website\": \"https://datahelp.imf.org/knowledgebase/articles/667681-using-json-restful-web-service\"\n    },\n    {\n        \"packageName\": \"openbb-jodi\",\n        \"optional\": false,\n        \"reprName\": \"Joint Organisations Data Initiative (JODI) World Databases\",\n        \"description\": \"The Joint Organisations Data Initiative (JODI) provides free monthly petroleum and natural gas supply, demand, and stock statistics, self-reported by over 100 countries, through the JODI-Oil and JODI-Gas World Databases.\",\n        \"credentials\": [],\n        \"website\": \"https://www.jodidata.org\"\n    },\n    {\n        \"packageName\": \"openbb-nasdaq\",\n        \"optional\": false,\n        \"reprName\": \"Nasdaq\",\n        \"description\": \"Positioned at the nexus of technology and the capital markets, Nasdaq\\nprovides premier platforms and services for global capital markets and beyond with\\nunmatched technology, insights and markets expertise.\",\n        \"credentials\": [],\n        \"website\": \"https://www.nasdaq.com\"\n    },\n    {\n        \"packageName\": \"openbb-oecd\",\n        \"optional\": false,\n        \"reprName\": \"Organization for Economic Co-operation and Development (OECD)\",\n        \"description\": \"Access OECD data via the SDMX REST API.\\nCovers all OECD dataflows including GDP, CPI, unemployment,\\ninterest rates, and hundreds more.\",\n        \"credentials\": [],\n        \"website\": \"https://data-explorer.oecd.org/\"\n    },\n    {\n        \"packageName\": \"openbb-sec\",\n        \"optional\": false,\n        \"reprName\": \"Securities and Exchange Commission (SEC)\",\n        \"description\": \"SEC is the public listings regulatory body for the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.sec.gov/data\"\n    },\n    {\n        \"packageName\": \"openbb-tmx\",\n        \"optional\": false,\n        \"reprName\": \"TMX\",\n        \"description\": \"Unofficial TMX Data Provider Extension\\n    TMX Group Companies\\n        - Toronto Stock Exchange\\n        - TSX Venture Exchange\\n        - TSX Trust\\n        - Montr\\u00e9al Exchange\\n        - TSX Alpha Exchange\\n        - Shorcan\\n        - CDCC\\n        - CDS\\n        - TMX Datalinx\\n        - Trayport\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://www.tmx.com\"\n    },\n    {\n        \"packageName\": \"openbb-us-eia\",\n        \"optional\": false,\n        \"reprName\": \"U.S. Energy Information Administration (EIA) Open Data and API\",\n        \"description\": \"The U.S. Energy Information Administration is committed to its free and open data by making it available through an Application Programming Interface (API) and its open data tools. See https://www.eia.gov/opendata/ for more information.\",\n        \"credentials\": [\n            \"eia_api_key\"\n        ],\n        \"website\": \"https://eia.gov/\",\n        \"instructions\": \"Credentials are required for functions calling the EIA's API.\\n    Register for a free key here: https://www.eia.gov/opendata/register.php\"\n    }\n]","isInternal":false,"tokens":2528,"sizeBytes":10657},{"name":"router.json","path":"assets/extensions/router.json","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/assets/extensions/router.json","title":"Extensions Skill","category":"anthropic-skill","format":"json","content":"[\n    {\n        \"packageName\": \"openbb-commodity\",\n        \"optional\": true,\n        \"description\": \"Commodity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-crypto\",\n        \"optional\": true,\n        \"description\": \"Cryptocurrency market data.\"\n    },\n    {\n        \"packageName\": \"openbb-currency\",\n        \"optional\": true,\n        \"description\": \"Foreign exchange (FX) market data.\"\n    },\n    {\n        \"packageName\": \"openbb-derivatives\",\n        \"optional\": true,\n        \"description\": \"Derivatives market data.\"\n    },\n    {\n        \"packageName\": \"openbb-econometrics\",\n        \"optional\": true,\n        \"description\": \"Econometrics analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-economy\",\n        \"optional\": true,\n        \"description\": \"Economic data.\"\n    },\n    {\n        \"packageName\": \"openbb-equity\",\n        \"optional\": true,\n        \"description\": \"Equity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-etf\",\n        \"optional\": true,\n        \"description\": \"Exchange Traded Funds market data.\"\n    },\n    {\n        \"packageName\": \"openbb-fixedincome\",\n        \"optional\": true,\n        \"description\": \"Fixed Income market data.\"\n    },\n    {\n        \"packageName\": \"openbb-index\",\n        \"optional\": true,\n        \"description\": \"Indices data.\"\n    },\n    {\n        \"packageName\": \"openbb-news\",\n        \"optional\": false,\n        \"description\": \"Financial market news data.\"\n    },\n    {\n        \"packageName\": \"openbb-quantitative\",\n        \"optional\": true,\n        \"description\": \"Quantitative analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-technical\",\n        \"optional\": true,\n        \"description\": \"Technical Analysis tools.\"\n    }\n]","isInternal":false,"tokens":396,"sizeBytes":1713},{"name":"SKILL.md","path":"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/build_workspace_app/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/build_workspace_app/SKILL.md","title":"build_workspace_app","category":"anthropic-skill","format":"markdown","content":"---\nname: build_workspace_app\ndescription: This guide covers the full lifecycle of building, running, and serving a custom OpenBB Workspace application from an extension project scaffolded by `openbb-cookiecutter`. It assumes the project shell already exists (see the `develop_extension` skill for scaffolding instructions).\n---\n\n# Build and Run OpenBB Workspace Applications\n\nThis guide covers the full lifecycle of building, running, and serving a custom\nOpenBB Workspace application from an extension project scaffolded by\n`openbb-cookiecutter`. It assumes the project shell already exists\n(see the `develop_extension` skill for scaffolding instructions).\n\n---\n\n## Prerequisites\n\nEnsure the following packages are installed in the active Python environment:\n\n```\npip install openbb-core openbb-platform-api openbb-devtools\n```\n\n- `openbb-core` provides the Router, Provider, OBBject, and Fetcher base classes.\n- `openbb-platform-api` provides the `openbb-api` CLI for serving backends\n  and auto-generating `widgets.json` for OpenBB Workspace.\n- `openbb-devtools` provides `pytest`, cassette recording, and QA utilities.\n\nIf you also need the Python Interface wrapper (the `obb` object), install the\nmain package:\n\n```\npip install openbb --no-deps\n```\n\n---\n\n## Architecture Overview\n\nOpenBB is built on FastAPI and Pydantic. The application has two independent\ninterfaces that share core logic and models:\n\n- **Python Interface** — wraps installed routers into an `obb` Python package\n  with auto-generated docstrings and function signatures. Requires a build step\n  (`openbb-build`) to generate static assets.\n- **REST API** — a FastAPI instance with all installed routers available via\n  HTTP. Import it with `from openbb_core.api.rest_api import app` or launch\n  with `uvicorn openbb_core.api.rest_api:app`.\n\nThe application is the product of all installed extensions. With just `openbb-core`\nthere are no routers or endpoints — users compose their own combinations.\n\n### Key Classes\n\n| Class | Import | Purpose |\n|---|---|---|\n| `Router` | `openbb_core.app.router` | Subclass of `fastapi.APIRouter`; defines commands |\n| `OBBject` | `openbb_core.app.model.obbject` | Standard response object with results, provider, warnings, extra |\n| `Provider` | `openbb_core.provider.abstract.provider` | Registers fetchers for the provider interface |\n| `Fetcher` | `openbb_core.provider.abstract.fetcher` | TET pipeline: Transform query → Extract data → Transform data |\n| `QueryParams` | `openbb_core.provider.abstract.query_params` | Base class for input parameters |\n| `Data` | `openbb_core.provider.abstract.data` | Base class for output data schemas |\n\n---\n\n## Router Extensions (API Endpoints)\n\nRouter extensions are the user-facing endpoints that power the REST API, MCP,\nand Python Interface.\n\n### Creating a Router\n\n```python\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\", description=\"My custom extension.\")\n```\n\nThe top-level API path prefix is determined by the entry point name in\n`pyproject.toml`, not the `prefix` argument. Only use `prefix` for sub-routers.\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.routers.my_router:router\"\n```\n\nCommands appear at `/my_app/...` in the API and `obb.my_app.` in Python.\n\n### Provider Interface Endpoints\n\nConnect a router command to one or more provider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters and the `OBBject.from_query(Query(**locals()))` return are\nmandatory and must be used exactly as shown.\n\n### Basic GET Endpoint\n\n```python\n@router.command(methods=[\"GET\"])\nasync def hello() -> OBBject[str]:\n    \"\"\"OpenBB Hello World.\"\"\"\n    return OBBject(results=\"Hello from the extension!\")\n```\n\n### Basic POST Endpoint\n\n```python\nfrom openbb_core.provider.abstract.data import Data\n\n\n@router.command(methods=[\"POST\"])\nasync def process(data: Data, some_param: str) -> OBBject:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results=data.model_dump())\n```\n\n### Decorator Parameters\n\n`@router.command` accepts:\n- `model` — metamodel name linking to Provider fetchers\n- `methods` — list of HTTP methods, typically `[\"GET\"]` or `[\"POST\"]`\n- `examples` — list of `APIEx` or `PythonEx` for docs\n- `deprecated` — deprecation notice\n- `no_validate` — skip response validation, treat output as `Any`\n- `openapi_extra` — dictionary for inline `widget_config` or `mcp_config`\n\nFields of a response model marked\n`Field(json_schema_extra={\"exclude_from_api\": True})` are dropped from REST\nAPI responses and kept in the Python Interface, as `Chart.fig` is.\n\n### Using FastAPI APIRouter Directly\n\nAccess the underlying FastAPI router via `router._api_router`:\n\n```python\n@router._api_router.get(\"/also_empty\")\nasync def also_empty(param: str) -> str:\n    \"\"\"Also empty.\"\"\"\n    return \"Hello world!\"\n```\n\n---\n\n## From FastAPI — Converting Existing Apps\n\nAny existing FastAPI application can become an OpenBB extension without changing\ncode. Define the entry point in `pyproject.toml` pointed at the FastAPI or\nAPIRouter instance:\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.app:app\"\n```\n\nInstall the package and build static assets:\n\n```\npip install -e .\nopenbb-build\n```\n\nKnown limitations:\n- Authorization hooks are not injected into the Python Interface\n- Request-bound dependencies or those returning None are not injected\n- WebSockets are not supported in the Python Interface\n- Multi-method routes (GET + POST on same path) may not generate correctly\n\n---\n\n## Serving with openbb-api (Workspace Backends)\n\nThe `openbb-api` CLI converts a FastAPI instance into an OpenBB Workspace\nbackend with auto-generated widget definitions.\n\n### Basic Usage\n\n```\n# Start with default OpenBB extensions\nopenbb-api\n\n# Start with a custom FastAPI file\nopenbb-api --app ./my_app.py --host 0.0.0.0 --port 8005\n\n# Factory function pattern\nopenbb-api --app my_app.py:create_app --factory\n\n# Custom FastAPI instance name\nopenbb-api --app my_app.py --name my_app\n```\n\nDefaults are `--host 127.0.0.1 --port 6900`, falling back to the next\navailable port if already in use.\n\n### Key Arguments\n\n| Argument | Description |\n|---|---|\n| `--app` | Path to Python file with a FastAPI instance |\n| `--name` | Name of the FastAPI instance (default: `app`) |\n| `--factory` | Flag if the app name is a factory function |\n| `--editable` | Make `widgets.json` editable at runtime |\n| `--no-build` | Load existing `widgets.json` without checking for updates |\n| `--exclude` | JSON list of API paths to exclude from widgets |\n| `--widgets-json` | Custom path for `widgets.json` |\n| `--apps-json` | Custom path for `workspace_apps.json` |\n\nAll remaining arguments are passed to `uvicorn.run`.\n\n---\n\n## Inline Widget Definitions\n\nWidget properties can be defined inline in your code via `openapi_extra`:\n\n```python\n@app.get(\n    \"/some_endpoint\",\n    openapi_extra={\n        \"widget_config\": {\n            \"name\": \"Custom Widget Name\",\n            \"description\": \"Override docstring description\",\n        }\n    },\n)\nasync def some_endpoint():\n    \"\"\"Description from docstring.\"\"\"\n    pass\n```\n\n### Exclude an Endpoint from Widgets\n\n```python\n@app.get(\n    \"/internal_endpoint\",\n    openapi_extra={\"widget_config\": {\"exclude\": True}},\n)\nasync def internal_endpoint():\n    return [{\"label\": \"Choice 1\", \"value\": \"choice1\"}]\n```\n\n### Dropdown Parameters\n\nDropdowns are auto-generated from `Literal` types:\n\n```python\nfrom typing import Literal\n\n\n@app.get(\"/with_dropdown\")\nasync def with_dropdown(\n    choices: Literal[\"Choice 1\", \"Choice 2\", \"Choice 3\"] = \"Choice 3\",\n):\n    pass\n```\n\n### Column Definitions for Tables\n\nUse Pydantic response models to auto-generate table column definitions:\n\n```python\nimport datetime\nfrom pydantic import BaseModel, Field\n\n\nclass MyData(BaseModel):\n    date: datetime.date = Field(description=\"The date.\")\n    value: float = Field(description=\"The value.\")\n\n\n@app.get(\"/my_data\")\nasync def my_data() -> list[MyData]:\n    \"\"\"Widget with typed columns.\"\"\"\n    return [MyData(date=datetime.date.today(), value=42.0)]\n```\n\n### Widget Types by Return Type\n\n| Return Type | Widget Type |\n|---|---|\n| `list[dict]` or `list[BaseModel]` | Table (AgGrid) |\n| `str` | Markdown |\n| `dict` (with `widget_config.type=\"chart\"`) | Plotly Chart |\n| `MetricResponseModel` | Metric |\n| `PdfResponseModel` | PDF |\n\n### Response Models for Special Widgets\n\n```python\nfrom openbb_platform_api.response_models import MetricResponseModel, PdfResponseModel\n\n\n@app.get(\"/metric\", response_model=MetricResponseModel)\nasync def metric():\n    \"\"\"A metric widget.\"\"\"\n    return dict(label=\"Revenue\", value=12345, delta=5.67)\n\n\n@app.get(\"/pdf\", response_model=PdfResponseModel)\nasync def open_pdf(file_path: str):\n    \"\"\"Open a PDF document.\"\"\"\n    with open(file_path, \"rb\") as f:\n        return dict(content=f.read())\n```\n\n### Plotly Chart Widget\n\n```python\n@app.get(\n    \"/chart\",\n    openapi_extra={\"widget_config\": {\"type\": \"chart\"}},\n)\nasync def chart() -> dict:\n    \"\"\"A chart widget.\"\"\"\n    from plotly.graph_objs import Bar, Layout, Figure\n\n    fig = Figure(\n        data=[Bar(x=[\"A\", \"B\", \"C\"], y=[1, 2, 3])],\n        layout=Layout(title=\"My Chart\", template=\"plotly_dark\"),\n    )\n    return fig.to_plotly_json()\n```\n\n### JSON Schema Extra for Parameters\n\nAnnotate parameters with additional widget configuration:\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query\n\nmy_param: Annotated[\n    str,\n    Query(\n        title=\"My Title\",\n        description=\"Detailed hover text\",\n        json_schema_extra={\n            \"x-widget_config\": {\"optionsEndpoint\": \"/my_choices_endpoint\"}\n        },\n    ),\n]\n```\n\n### Form Input Widget\n\nCreate an input form tied to a table:\n- GET endpoint defines `widget_config.form_endpoint` pointing to the POST route\n- POST route accepts a single Pydantic model argument\n\n---\n\n## OBBject Extensions (Result Post-Processing)\n\nExtend the `OBBject` response with custom methods, accessible in the Python Interface.\n\n### Class Accessor (Namespaced Methods)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self):\n        \"\"\"Return a summary.\"\"\"\n        return self._obbject.to_dataframe().describe()\n```\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_tools = \"my_package.obbject.my_ext:ext\"\n```\n\n### Callable Accessor (Property-Like)\n\n```python\next = Extension(name=\"to_csv\", description=\"Convert results to CSV.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject):\n    \"\"\"Convert to CSV string.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### OBBject Output Conversion Methods\n\nEvery `OBBject` has built-in conversion methods:\n- `to_df()` / `to_dataframe()` — Pandas DataFrame\n- `to_dict(orientation=...)` — Python dict\n- `to_numpy()` — NumPy array\n- `to_polars()` — Polars DataFrame (requires `polars` installed)\n- `model_dump()` — Complete object as dict\n- `model_dump_json()` — Serialized JSON string\n\n---\n\n## OBBject Plugins (Pre-Return Interceptors)\n\nPlugins execute before the response is returned, compatible with both REST API\nand Python Interface. They can conditionally alter the output of any command.\n\n**WARNING**: Plugins are considered potentially dangerous. The environment must\nbe explicitly configured to allow them.\n\nIn `system_settings.json`:\n\n```json\n{\n    \"allow_on_command_output\": true,\n    \"allow_mutable_extensions\": true\n}\n```\n\n### Plugin Configuration\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\nplugin = Extension(\n    name=\"my_plugin\",\n    description=\"Intercept output before return.\",\n    on_command_output=True,\n    command_output_paths=[\"/my_router/my_command\"],\n    immutable=False,\n    results_only=False,\n)\n```\n\nKey parameters:\n- `on_command_output=True` — required for plugins\n- `command_output_paths` — list of endpoint paths to intercept (None = all)\n- `immutable=False` — set to allow modifying the response object\n- `results_only=True` — receive only the `results` portion instead of full OBBject\n\n### Plugin Code\n\n```python\n@plugin.obbject_accessor\ndef my_plugin_func(obbject):\n    \"\"\"Modify or inspect the response before it returns.\"\"\"\n    # Modify obbject directly; do NOT return anything\n    pass\n```\n\n---\n\n## Charting Extensions (Views)\n\nAdd custom chart views to any router endpoint, activated when the user sets\n`chart=True`.\n\n### Structure\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Chart for my_command.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build chart with fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_views:MyViews\"\n```\n\n### Kwargs Available in Views\n\n| Key | Content |\n|---|---|\n| `obbject_item` | Validated results object |\n| `charting_settings` | User charting preferences |\n| `standard_params` | Standard model parameters |\n| `extra_params` | Provider-specific parameters |\n| `provider` | Provider name used |\n| `extra` | Execution metadata |\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## HTTP Requests in Fetchers\n\nUse the built-in utilities instead of creating new clients from scratch.\n\n### Query String Helper\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"exclude_this_param\"])\n```\n\n### Synchronous Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\nresponse = make_request(url, headers=headers, params=params)\n```\n\n### Requests Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\n```\n\n### Asynchronous Requests (AIOHTTP)\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresponse_json = await amake_request(url)\n```\n\n### Multi-URL Async Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nresults = await amake_requests([url1, url2, url3])\n```\n\n### Custom Response Callback\n\n```python\nfrom io import StringIO\nfrom pandas import read_csv\n\nresults = []\n\n\nasync def response_callback(response, _):\n    text = await response.text()\n    data = read_csv(StringIO(text), skiprows=2)\n    results.extend(data.to_dict(\"records\"))\n\n\nawait amake_requests([url1, url2], response_callback=response_callback)\n```\n\n### Async Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with await session.get(url) as response:\n        data = await response.json()\n```\n\n### Async Fetchers\n\nUse `aextract_data` instead of `extract_data` for async fetchers:\n\n```python\n@staticmethod\nasync def aextract_data(\n    query: MyQueryParams,\n    credentials: dict[str, str] | None,\n    **kwargs: Any,\n) -> list[dict]:\n    \"\"\"Async data extraction.\"\"\"\n    ...\n```\n\n---\n\n## Testing\n\n### Built-In Fetcher Test\n\nEvery Fetcher has a `.test()` method for quick validation:\n\n```python\nfrom my_package.providers.my_provider.models.my_model import MyFetcher\n\nfetcher = MyFetcher()\nfetcher.test({\"symbol\": \"AAPL\"}, {})  # Returns None on success\n```\n\n### Unit Tests with Cassettes\n\nInstall dev tools and use `pytest_recorder` for HTTP cassette recording:\n\n```\npip install openbb-devtools\npytest test_my_fetcher.py --record http\n```\n\nSubsequent test runs replay the recorded HTTP interactions.\n\n### Running Tests\n\n```\n# Unit tests only\npytest tests/ -m \"not integration\"\n\n# Integration tests only\npytest tests/ -m integration\n\n# All tests\npytest tests/\n```\n\n### Integration Testing\n\nFor API integration tests, start a local server first:\n\n```\nuvicorn openbb_core.api.rest_api:app --host 0.0.0.0 --port 8000 --reload\n```\n\n---\n\n## Install and Build\n\n### Development Install\n\nFrom the project root:\n\n```\nuv pip install -e . --group dev\n```\n\n### Build Python Interface\n\nAfter installing or removing extensions, regenerate static assets:\n\n```\nopenbb-build\n```\n\n### Verify in Python\n\n```python\nfrom openbb import obb\n# Your commands appear under obb.<router_name>.<command_name>()\n```\n\n### Serve as Workspace Backend\n\n```\nopenbb-api --app ./my_app.py --editable --host 0.0.0.0 --port 6900\n```\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me a Workspace application that does X\":\n\n1. **Scaffold** the project with `openbb-cookiecutter` (see `develop_extension` skill).\n2. **Remove** example files and clean up boilerplate.\n3. **Implement fetchers** for each data source in `providers/<name>/models/`.\n4. **Register fetchers** in `providers/<name>/__init__.py` via `fetcher_dict`.\n5. **Implement router commands** in `routers/<name>.py`.\n6. **Add widget config** via `openapi_extra` for Workspace-specific behavior.\n7. **Update `pyproject.toml`** entry points and dependencies.\n8. **Install** with `uv pip install -e . --group dev`.\n9. **Build** static assets with `openbb-build`.\n10. **Serve** with `openbb-api` and verify widgets in OpenBB Workspace.\n11. **Test** with `pytest` and fetcher `.test()` methods.\n","frontmatter":{"name":"build_workspace_app","description":"This guide covers the full lifecycle of building, running, and serving a custom OpenBB Workspace application from an extension project scaffolded by `openbb-cookiecutter`. It assumes the project shell already exists (see the `develop_extension` skill for scaffolding instructions)."},"isInternal":false,"tokens":4665,"sizeBytes":19719},{"name":"SKILL.md","path":"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/configure_mcp_server/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/configure_mcp_server/SKILL.md","title":"configure_mcp_server","category":"anthropic-skill","format":"markdown","content":"---\nname: configure_mcp_server\ndescription: This guide covers installation, configuration, authentication, tool discovery, prompt management, and client integration for `openbb-mcp-server`.\n---\n\n# Configure and Build the OpenBB MCP Server\n\nThis guide covers installation, configuration, authentication, tool discovery,\nprompt management, and client integration for `openbb-mcp-server`.\n\n---\n\n## Installation\n\n```\npip install openbb-mcp-server\n```\n\nThis installs the `openbb-mcp` CLI command. To include the default OpenBB\nextensions as tools, also install them:\n\n```\npip install openbb\n```\n\nOr install individual extensions:\n\n```\npip install openbb-equity openbb-economy\n```\n\nThe `cli` extra adds `openbb-cli` and its four dispatcher tools\n(`openbb_dispatch`, `openbb_batch_dispatch`, `openbb_list_commands`,\n`openbb_describe_command`); `enable_cli_tools=false` turns them off:\n\n```\npip install \"openbb-mcp-server[cli]\"\n```\n\n---\n\n## Starting the Server\n\n### Default (all installed OpenBB extensions)\n\n```\nopenbb-mcp\n```\n\nDefaults: `--host 127.0.0.1 --port 8001 --transport streamable-http`\n\n### With a Custom FastAPI Application\n\n```\n# File path with default instance name \"app\"\nopenbb-mcp --app ./my_app.py\n\n# Explicit instance name\nopenbb-mcp --app ./my_app.py --name my_app\n\n# Module import syntax\nopenbb-mcp --app my_package.app:my_app\n\n# Factory function pattern\nopenbb-mcp --app ./my_app.py:create_app --factory\n```\n\n### Transport Options\n\n| Transport | Flag | Use Case |\n|---|---|---|\n| `streamable-http` | `--transport streamable-http` | Default. HTTP-based, works with Cursor, VS Code |\n| `sse` | `--transport sse` | Legacy Server-Sent Events. Required by Cline |\n| `stdio` | `--transport stdio` | Standard I/O. Used by Claude Desktop |\n\n---\n\n## CLI Arguments\n\n| Argument | Description | Default |\n|---|---|---|\n| `--app <path>` | Path to FastAPI application file or `module:instance` (mutually exclusive with `--spec`) | OpenBB default app |\n| `--name <name>` | Name of the FastAPI instance or factory function | `app` |\n| `--factory` | Treat `--name` as a factory function | `false` |\n| `--spec <path>` | Path to an `openbb-cli` generated `.spec` file. Synthesizes a FastAPI proxy app whose routes forward to the spec's `base_url`; FastMCP exposes those routes as MCP tools. Mutually exclusive with `--app`. | None |\n| `--config-file <path>` | Path to an `openbb.toml` config file. Highest-priority TOML layer in the cascade. Also honored via `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` / `OPENBB_CONFIG`. | None |\n| `--host <host>` | Server host | `127.0.0.1` |\n| `--port <port>` | Server port | `8001` |\n| `--transport <type>` | `streamable-http`, `sse`, or `stdio` | `streamable-http` |\n| `--default-categories <csv>` | Categories served when tool discovery is off | `all` |\n| `--allowed-categories <csv>` | The only categories served, with or without discovery | All categories |\n| `--tool-discovery` | Hide the tools behind `available_categories`, `available_tools`, `search_tools`, and `call_tool` | Discovery disabled |\n| `--system-prompt <path>` | Path to a `.txt` system prompt file | None |\n| `--server-prompts <path>` | Path to a `.json` server prompts file | None |\n\nEvery other `--key value` is routed by name: an `MCPSettings` field sets that\nsetting (`--enable-cli-tools false`, `--server-auth '[\"user\", \"pass\"]'`),\n`--httpx-<option>` sets an option of the httpx client that calls the API\n(`--httpx-verify false`), and anything else is passed to uvicorn\n(`--log-level debug`).\n\n---\n\n## Spec-Driven Proxy Mode\n\nInstead of importing a FastAPI app in-process, the launcher can synthesize one\nfrom an `openbb-cli` generated `.spec` file. Each command in the spec becomes\na FastAPI route that proxies to the spec's recorded `base_url`. FastMCP walks\nthose routes and exposes them as MCP tools — same tool surface as a normal\n`--app` install, but the heavy `import openbb` lives on the upstream server.\n\n### Generating a spec\n\nThe spec is produced by `openbb-cli`:\n\n```bash\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n```\n\nThis fetches `/openapi.json` from the server and writes a precomputed `.spec`\nfile with every command's URL path, method, parameter schema, and a\n`content_sha256` integrity hash.\n\n### Single-spec launch\n\n```bash\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML (`--config-file openbb.toml`):\n\n```toml\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\n# Optional: override the spec's recorded base_url at deploy time.\nbase_url = \"https://upstream.example.com\"\n# Optional: pinned SHA-256. When set, the launcher recomputes the spec's\n# canonical-JSON hash and refuses to start unless it matches — protects\n# against silent file drift when the spec is fetched at deploy time.\ncontent_sha256 = \"abc123...\"\n\n[mcp.spec.headers]\n# Static headers injected on every proxied upstream request.\n# ``$VAR`` / ``${VAR}`` are substituted from the current environment;\n# entries with unresolved variables are skipped with a warning.\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\nX-API-Key     = \"$OPENBB_UPSTREAM_KEY\"\n```\n\n`--spec` and `--app` are mutually exclusive.\n\n### Multi-spec mounting\n\nEach `[mcp.spec.NAME]` subtable mounts an independent spec under its own\nprefix. FastMCP turns the union of all mounted routes into MCP tools; per-spec\nhooks fire on every tool dispatch into that spec's mount.\n\n```toml\n[mcp.spec.equity]\npath = \"/etc/openbb/equity.spec\"\nmount = \"/equity\"                          # optional; defaults to \"/<name>\"\ncontent_sha256 = \"abc...\"\n[mcp.spec.equity.headers]\nAuthorization = \"Bearer $EQUITY_TOKEN\"\n[mcp.spec.equity.auth]\nhooks = [\"my_pkg.auth:equity_token_check\"]\n[mcp.spec.equity.middleware]\nhooks = [\"my_pkg.middleware:equity_rate_limit\"]\n\n[mcp.spec.crypto]\npath = \"/etc/openbb/crypto.spec\"\n[mcp.spec.crypto.headers]\nX-API-Key = \"$CRYPTO_KEY\"\n```\n\n---\n\n## openbb.toml Cascade\n\nThe launcher reads the same layered TOML cascade openbb-core ships with,\nlowest priority first:\n\n```\npyproject.toml [tool.openbb] (so [tool.openbb.mcp]) → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD) → --config-file PATH (explicit)\n```\n\nEvery layer is optional, and `.openbb.toml` works in place of `openbb.toml`.\n`--config-file` (or `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` /\n`OPENBB_CONFIG`) is the highest-priority TOML layer. `[mcp]` values are\napplied like CLI flags: a CLI flag beats them, and they beat `OPENBB_MCP_*`\nenvironment variables. `[env]` entries never replace a variable already set\nin the shell or a `.env` file.\n\n### Top-level tables\n\n| Table | Purpose |\n|---|---|\n| `[mcp]` | Default values for any of the `--*` CLI flags (CLI wins). |\n| `[mcp.spec]` | Single-spec proxy mode (path, base_url, content_sha256, headers). |\n| `[mcp.spec.NAME]` | Multi-spec mount (path, base_url, content_sha256, headers, auth, middleware). |\n| `[mcp.auth]` | Top-level auth hook list. |\n| `[mcp.middleware]` | Top-level middleware hook list. |\n| `[env]` | Env vars pushed into `os.environ` before any heavy import runs. Existing shell env vars are NEVER clobbered. Values support `$VAR` / `${VAR}` substitution. |\n\n### Auth and middleware hooks\n\nBoth `[mcp.auth]` and `[mcp.middleware]` accept a `hooks` list of\n`module:attr` entry points. Each hook is a 2-arg async function\n`async def hook(request, call_next)` and is wrapped as a\n`Middleware(BaseHTTPMiddleware, dispatch=fn)` on the MCP server's ASGI stack.\n\n```toml\n[mcp.auth]\nhooks = [\"my_pkg.auth:bearer_token_check\"]\n\n[mcp.middleware]\nhooks = [\"my_pkg.middleware:rate_limit\", \"my_pkg.middleware:request_logger\"]\n```\n\nAuth hooks register before middleware hooks; within each list, registration\norder matches TOML order. Per-spec hooks (under `[mcp.spec.NAME.auth]` /\n`[mcp.spec.NAME.middleware]`) are scoped to that spec's mounted sub-app.\nThe top-level hooks wrap the HTTP transports (`streamable-http`, `sse`);\nthe `stdio` transport has no HTTP stack, so they do not run there.\n\n### Env injection\n\n```toml\n[env]\nOPENBB_MCP_HOST = \"0.0.0.0\"\n# Values support shell-style $VAR / ${VAR} substitution.\nOPENBB_GITHUB_TOKEN = \"$GITHUB_TOKEN\"\n```\n\nUseful in containers to inject `OPENBB_*` keys without shell exports. Any key\nalready present in the real environment is preserved.\n\n---\n\n## Configuration Precedence\n\nSettings are resolved in this order (highest priority first):\n\n1. **CLI arguments** — command-line flags\n2. **`[mcp]` table of the `openbb.toml` cascade** — applied like CLI flags; `--config-file PATH` > project `openbb.toml` > user-global `~/.openbb_platform/openbb.toml` > `[tool.openbb.mcp]` in `pyproject.toml`\n3. **Environment variables** — `OPENBB_MCP_` prefixed, including those set by `[env]`\n4. **JSON settings file** — `~/.openbb_platform/mcp_settings.json`, written with the defaults on first run\n5. **Defaults** — built-in MCPSettings defaults\n\n### Config File Example\n\nCreate `~/.openbb_platform/mcp_settings.json`:\n\n```json\n{\n    \"name\": \"My MCP Server\",\n    \"default_tool_categories\": [\"equity\", \"economy\"],\n    \"enable_tool_discovery\": false,\n    \"describe_responses\": false,\n    \"system_prompt_file\": \"/path/to/system_prompt.txt\",\n    \"server_prompts_file\": \"/path/to/prompts.json\"\n}\n```\n\n### Environment Variables\n\nAll settings map to `OPENBB_MCP_` prefixed environment variables:\n\n```\nOPENBB_MCP_NAME=\"My MCP Server\"\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ENABLE_TOOL_DISCOVERY=true\nOPENBB_MCP_SYSTEM_PROMPT_FILE=\"/path/to/prompt.txt\"\nOPENBB_MCP_SERVER_PROMPTS_FILE=\"/path/to/prompts.json\"\n```\n\n---\n\n## Settings Reference\n\n### Server Identity\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `name` | `OPENBB_MCP_NAME` | `str` | `\"OpenBB MCP\"` |\n| `description` | `OPENBB_MCP_DESCRIPTION` | `str` | Auto-generated |\n| `version` | `OPENBB_MCP_VERSION` | `str \\| None` | `None` |\n| `instructions` | `OPENBB_MCP_INSTRUCTIONS` | `str \\| None` | `None` (the system prompt, when one is loaded) |\n| `mask_error_details` | `OPENBB_MCP_MASK_ERROR_DETAILS` | `bool \\| None` | `None` |\n\n### Tool Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `default_tool_categories` | `OPENBB_MCP_DEFAULT_TOOL_CATEGORIES` | `list[str]` | `[\"all\"]` |\n| `allowed_tool_categories` | `OPENBB_MCP_ALLOWED_TOOL_CATEGORIES` | `list[str] \\| None` | `None` |\n| `enable_tool_discovery` | `OPENBB_MCP_ENABLE_TOOL_DISCOVERY` | `bool` | `false` |\n| `enable_cli_tools` | `OPENBB_MCP_ENABLE_CLI_TOOLS` | `bool` | `true` |\n| `list_page_size` | `OPENBB_MCP_LIST_PAGE_SIZE` | `int \\| None` | `None` |\n| `describe_responses` | `OPENBB_MCP_DESCRIBE_RESPONSES` | `bool` | `false` |\n| `api_prefix` | `OPENBB_MCP_API_PREFIX` | `str \\| None` | `None` |\n\n`enable_cli_tools` registers the `openbb-cli` dispatcher tools when\n`openbb-cli` is installed. `mask_error_details` hides exception details from\nclients.\n\n### Prompt Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `system_prompt_file` | `OPENBB_MCP_SYSTEM_PROMPT_FILE` | `str \\| None` | `None` |\n| `server_prompts_file` | `OPENBB_MCP_SERVER_PROMPTS_FILE` | `str \\| None` | `None` |\n| `default_skills_dir` | `OPENBB_MCP_DEFAULT_SKILLS_DIR` | `str \\| None` | Built-in skills dir |\n| `skills_reload` | `OPENBB_MCP_SKILLS_RELOAD` | `bool` | `false` |\n| `skills_providers` | `OPENBB_MCP_SKILLS_PROVIDERS` | `list[str] \\| None` | `None` |\n\n### HTTP Transport\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `uvicorn_config` | `OPENBB_MCP_UVICORN_CONFIG` | `dict` | `{\"host\": \"127.0.0.1\", \"port\": \"8001\"}` |\n| `httpx_client_kwargs` | `OPENBB_MCP_HTTPX_CLIENT_KWARGS` | `dict` | `{}` |\n\n`httpx_client_kwargs` configures the httpx client that sends tool calls to the\nAPI (headers, timeout, TLS verification).\n\n### Duplicate Handling\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `on_duplicate` | `OPENBB_MCP_ON_DUPLICATE` | `str \\| None` | `None` |\n\nOptions: `\"warn\"`, `\"error\"`, `\"replace\"`, `\"ignore\"`\n\n### Module Exclusion\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `module_exclusion_map` | `OPENBB_MCP_MODULE_EXCLUSION_MAP` | `dict \\| None` | `None` |\n\nMaps route path segments to Python modules: routes under a segment are hidden\nwhile its module is imported. `None` uses `{\"coverage\": \"openbb_core\"}`, which\nhides the platform's `coverage` routes; `{}` hides nothing. Data-processing\nextensions (`technical`, `quantitative`, `econometrics`) are exposed when\ninstalled. Pair them with `run_pipeline`, or leave them out with\n`allowed_tool_categories` (or, with tool discovery off,\n`default_tool_categories`).\n\n---\n\n## Authentication\n\nThree authentication modes are available.\n\n### Server-Side Authentication\n\nProtect incoming MCP requests with a Bearer token:\n\n```json\n{\n    \"server_auth\": [\"username\", \"password\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"username\", \"password\"]'\n```\n\nClients must include `Authorization: Bearer <base64(username:password)>` in\ntheir requests. The token is base64-encoded `username:password`.\n\n### Client-Side Authentication\n\nAuthenticate outbound requests to downstream services:\n\n```json\n{\n    \"client_auth\": [\"api_user\", \"api_key\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_CLIENT_AUTH='[\"api_user\", \"api_key\"]'\n```\n\nThis passes `auth=(user, pass)` to the httpx client used for internal requests.\n\n### Programmatic Authentication\n\nWhen using the server as a library, pass a custom `AuthProvider` to\n`create_mcp_server()`:\n\n```python\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\nsettings = MCPSettings()\nmcp = create_mcp_server(settings, my_fastapi_app, auth=my_auth_provider)\n```\n\n---\n\n## Tool Discovery\n\nWhen `enable_tool_discovery` is `true`, the OpenBB tools are left out of the\ntool list and these tools are available to the agent instead:\n\n| Tool | Description |\n|---|---|\n| `available_categories` | Lists all tool categories with tool counts |\n| `available_tools` | Lists the tools in a category with one-sentence descriptions |\n| `search_tools` | Finds tools by a natural-language query and returns their full definitions |\n| `call_tool` | Runs any tool by name with its arguments |\n\nDiscovery holds no per-session state: every client sees the same tool list, and\na hidden tool can also be called directly by name. It behaves the same over every\ntransport and protocol version, so the server is safe for multi-user deployments.\n\n### Choosing the Served Categories\n\nUse `allowed_tool_categories` to serve only some categories, with or without\ndiscovery:\n\n```\nopenbb-mcp --allowed-categories equity,economy,crypto\n```\n\nRoutes in other categories are never registered: they can't be listed,\nsearched, or called, by name, through `call_tool`, or through `run_pipeline`.\n`None` or `all` serves every category.\n\nWithout discovery, `default_tool_categories` also limits the served tools:\n\n```\nopenbb-mcp --default-categories equity,economy\n```\n\nTools outside these categories are registered but disabled, so they are not\nlisted and calling them fails with \"Unknown tool\". A route's\n`mcp_config.enable` overrides this per tool. Nothing can re-enable a disabled\ntool at runtime. With discovery on, `default_tool_categories` is ignored and\nevery allowed tool is reachable through `search_tools` and `call_tool`.\n\n### Enabling Discovery\n\n```\nopenbb-mcp --tool-discovery\n```\n\nWithout discovery, all tools in `default_tool_categories` are listed and the\ndiscovery tools are not registered.\n\n---\n\n## Tool Naming Convention\n\nTools are named from their API route path after stripping the API prefix:\n\n| Route Path | Tool Name |\n|---|---|\n| `/equity/price/historical` | `equity_price_historical` |\n| `/economy/cpi` | `economy_cpi` |\n| `/my_app/process` | `my_app_process` |\n\nThe first path segment is the **category** and the second, on paths with three\nor more segments, the **subcategory**; otherwise the subcategory is\n`\"general\"`. `{placeholder}` segments are skipped, and a single-segment path\nrepeats its segment (`/hello` is `hello_hello`). When one path serves several\nmethods, the non-GET tools get a `_<method>` suffix (`demo_items` and\n`demo_items_post`). A route's `mcp_config.name` replaces the generated name.\n\n---\n\n## Prompt System\n\nThe server supports three layers of prompts, all accessible via the\n`list_prompts` and `get_prompt` tools. Bundled skills are served alongside them\nas resources.\n\n### 1. System Prompt (tag: `system`)\n\nA plain text file loaded once at startup. Also exposed as\n`resource://system_prompt`.\n\n```\nopenbb-mcp --system-prompt /path/to/system_prompt.txt\n```\n\n### 2. Server Prompts JSON (tag: `server`)\n\nA JSON file defining reusable prompts with optional arguments:\n\n```json\n[\n    {\n        \"name\": \"analyze_stock\",\n        \"description\": \"Framework for analyzing a stock.\",\n        \"content\": \"Analyze {symbol} focusing on {aspect}.\",\n        \"arguments\": [\n            {\n                \"name\": \"symbol\",\n                \"type\": \"str\",\n                \"description\": \"Ticker symbol\"\n            },\n            {\n                \"name\": \"aspect\",\n                \"type\": \"str\",\n                \"default\": \"fundamentals\",\n                \"description\": \"Analysis focus area\"\n            }\n        ],\n        \"tags\": [\"analysis\"]\n    }\n]\n```\n\nArgument types: `str`, `int`, `float`, `bool`, `list`, `dict`, `any`\n\nArguments with a `default` value are optional; those without are required.\nClients pass prompt arguments as strings (`{\"aspect\": \"valuation\"}`,\n`{\"years\": \"10\"}`).\n\n```\nopenbb-mcp --server-prompts /path/to/prompts.json\n```\n\n### 3. Inline Prompts (tag: route-specific)\n\nDefine prompts directly on FastAPI routes via `openapi_extra`:\n\n```python\n@router.command(\n    methods=[\"GET\"],\n    openapi_extra={\n        \"mcp_config\": {\n            \"prompts\": [\n                {\n                    \"name\": \"usage_guide\",\n                    \"description\": \"How to use this endpoint.\",\n                    \"content\": \"To analyze {symbol}, call this endpoint with...\",\n                }\n            ]\n        }\n    },\n)\nasync def my_endpoint(symbol: str) -> OBBject: ...\n```\n\nEach `{placeholder}` in `content` becomes an argument: a prompt argument of the\nsame name, else the endpoint parameter, else a required string. Arguments\nwithout a default are required. The rendered prompt starts with\n`Use the tool, <tool name>, to perform the following task.`, naming the\nroute's tool (including a name set with `mcp_config.name`), and the tool's\ndescription lists its prompts.\n\n### 4. Bundled Skills (Resources)\n\nSkill guides are exposed as MCP resources discoverable via `list_resources()`.\nEach skill is accessible at a `skill://<name>/SKILL.md` URI.\n\n```\n# Discover available skills\nlist_resources()  # returns skill://develop_extension/SKILL.md, etc.\n\n# Read a specific skill\nread_resource(\"skill://configure_mcp_server/SKILL.md\")\n```\n\nCustom skills directory:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=/path/to/my/skills\n```\n\nSet to empty string to disable bundled skills:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=\"\"\n```\n\n### Skills Reload\n\nEnable hot-reload of skill files without restarting the server (useful during development):\n\n```json\n{\n    \"skills_reload\": true\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SKILLS_RELOAD=true\n```\n\n### Vendor Skills Providers\n\nLoad skill directories from well-known vendor locations (e.g. `~/.claude/skills/`).\nSets the `skills_providers` list in `mcp_settings.json`:\n\n```json\n{\n    \"skills_providers\": [\"claude\", \"cursor\"]\n}\n```\n\nOr via environment variable (comma-separated):\n\n```\nOPENBB_MCP_SKILLS_PROVIDERS=\"claude,cursor\"\n```\n\nSupported provider names:\n\n| Name | Default Directory |\n|---|---|\n| `claude` | `~/.claude/skills/` |\n| `cursor` | `~/.cursor/skills/` |\n| `vscode` / `copilot` | `~/.copilot/skills/` |\n| `codex` | `/etc/codex/skills/` + `~/.codex/skills/` |\n| `gemini` | `~/.gemini/skills/` |\n| `goose` | `~/.config/agents/skills/` |\n| `opencode` | `~/.config/opencode/skills/` |\n\n---\n\n## Inline MCP Configuration (MCPConfigModel)\n\nControl how individual routes appear in the MCP server via `openapi_extra`:\n\n```python\n@app.get(\n    \"/my_endpoint\",\n    openapi_extra={\n        \"mcp_config\": {\n            \"expose\": True,\n            \"mcp_type\": \"tool\",\n            \"methods\": [\"GET\"],\n            \"exclude_args\": [\"internal_param\"],\n            \"prompts\": []\n        }\n    },\n)\n```\n\nThe configuration is read from `openapi_extra[\"mcp_config\"]`, or from\n`openapi_extra[\"x-mcp\"]` when `mcp_config` is absent. An invalid configuration\nis logged and ignored for that route.\n\n### MCPConfigModel Fields\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `expose` | `bool \\| None` | `None` | Set `false` to hide route from MCP |\n| `mcp_type` | `str \\| None` | `None` | `\"tool\"`, `\"resource\"`, or `\"resource_template\"` |\n| `methods` | `list[str] \\| None` | `None` | The route's HTTP methods to serve; the others are left out (`\"*\"` for all) |\n| `exclude_args` | `list[str] \\| None` | `None` | Arguments left out of the tool schema; each needs a default, which then applies |\n| `name` | `str \\| None` | `None` | Tool name, replacing the one built from the path |\n| `tags` | `list[str] \\| None` | `None` | Tags added to the tool next to its category |\n| `enable` | `bool \\| None` | `None` | With discovery off, serve (`true`) or hide (`false`) the tool regardless of `default_tool_categories` |\n| `describe_responses` | `bool \\| None` | `None` | Keep (`true`) or cut (`false`) the response documentation in the tool description |\n| `mime_type` | `str \\| None` | `None` | MIME type of a route served as a resource |\n| `prompts` | `list[dict]` | `[]` | Inline prompt definitions |\n\n---\n\n## What Agents Get\n\nBeyond one tool per route, every server provides:\n\n- **`run_pipeline`** — chains tools on the server, feeding an earlier step's\n  `results` (or a chart artifact's rows) into a later step's argument, so\n  data-processing tools (`technical_*`, `quantitative_*`, `econometrics_*`)\n  receive price histories without them passing through the conversation. It\n  calls only tools the server serves.\n- **Parameter choices** — provider `choices` and widget `x-widget_config`\n  options become an `enum`, or a \"Valid values\" note in the description, and\n  an `optionsEndpoint` names the tool that lists the values. Options endpoints\n  hidden from the API schema have no tool to name.\n- **Chart artifacts** — a Plotly figure in a result (a figure route, or an\n  OBBject `chart` from `chart: true`) is replaced by an OpenBB Workspace\n  artifact: a `chart` artifact with rows and `chart_params` for line, bar,\n  scatter, pie, and donut figures, else a `table` artifact of the figure's\n  data.\n- **Prompt and resource tools** — `list_prompts`, `get_prompt`,\n  `list_resources`, and `read_resource` for clients without native prompt or\n  resource support.\n- **`openbb-cli` dispatcher tools** — with the `cli` extra and\n  `enable_cli_tools`.\n- **`install_skill`** — writes a skill into the bundled or a vendor skills\n  directory. Skill names are limited to lowercase letters, digits,\n  underscores, and hyphens, and file paths must stay inside the skill\n  directory, but any connected client can call it. When clients are\n  untrusted, keep the skills directories read-only for the server process or\n  require `server_auth`.\n\n---\n\n## Client Configuration Examples\n\n### Claude Desktop (stdio transport)\n\nIn Claude Desktop's MCP config file:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"--with\", \"openbb\",\n                \"openbb-mcp\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\nFor a custom app:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"openbb-mcp\",\n                \"--app\", \"./my_app.py\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\n### Cursor (streamable-http)\n\n1. Start the server: `openbb-mcp`\n2. In Cursor's `mcp.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\"\n        }\n    }\n}\n```\n\n### VS Code (streamable-http)\n\n1. Enable MCP in VS Code settings (Settings → Chat → MCP)\n2. Start the server: `openbb-mcp`\n3. Open Command Palette → \"MCP: Add Server\" → HTTP\n4. Enter URL: `http://127.0.0.1:8001/mcp`\n\nFor the Cline VS Code extension, use `--transport sse`:\n\n```\nopenbb-mcp --transport sse\n```\n\n### With Authentication\n\nStart with server auth enabled:\n\n```\nopenbb-mcp --host 0.0.0.0 --port 8001\n```\n\nWith `mcp_settings.json`:\n\n```json\n{\n    \"server_auth\": [\"admin\", \"secretpass\"]\n}\n```\n\nClients include the Bearer token in their configuration:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\",\n            \"headers\": {\n                \"Authorization\": \"Bearer YWRtaW46c2VjcmV0cGFzcw==\"\n            }\n        }\n    }\n}\n```\n\nThe token value is `base64(\"admin:secretpass\")`.\n\n---\n\n## Advanced Configuration\n\n### Lists and Dicts in Environment Variables\n\nLists can be passed as comma-separated strings:\n\n```\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ALLOWED_TOOL_CATEGORIES=\"equity,economy\"\n```\n\nDicts and tuples must be JSON-encoded strings:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"user\", \"pass\"]'\nOPENBB_MCP_UVICORN_CONFIG='{\"host\": \"0.0.0.0\", \"port\": 8001, \"log_level\": \"info\"}'\nOPENBB_MCP_HTTPX_CLIENT_KWARGS='{\"timeout\": 30, \"verify\": false}'\n```\n\n### SSL / HTTPS\n\nPass SSL config via Uvicorn:\n\n```\nopenbb-mcp --ssl-keyfile /path/to/key.pem --ssl-certfile /path/to/cert.pem\n```\n\nOr in the config file:\n\n```json\n{\n    \"uvicorn_config\": {\n        \"host\": \"0.0.0.0\",\n        \"port\": 443,\n        \"ssl_keyfile\": \"/path/to/key.pem\",\n        \"ssl_certfile\": \"/path/to/cert.pem\"\n    }\n}\n```\n\n### Using as a Library\n\n```python\nimport asyncio\nfrom fastapi import FastAPI\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\napp = FastAPI()\n\n\n@app.get(\"/hello\")\nasync def hello():\n    return \"Hello World\"\n\n\nsettings = MCPSettings(\n    name=\"My Custom MCP\",\n    default_tool_categories=[\"all\"],\n    enable_tool_discovery=False,\n)\n\nmcp = create_mcp_server(settings, app)\nmcp.run(transport=\"streamable-http\")\n```\n\n---\n\n## Workflow Summary\n\nTo configure and deploy an OpenBB MCP server:\n\n1. **Install**: `pip install openbb-mcp-server` (plus any desired OpenBB extensions).\n2. **Configure**: Create `~/.openbb_platform/mcp_settings.json` with desired settings.\n3. **Add prompts**: Write a system prompt file and/or server prompts JSON.\n4. **Start**: Run `openbb-mcp` with appropriate CLI flags.\n5. **Connect**: Configure your MCP client (Claude Desktop, Cursor, VS Code) with the server URL or stdio command.\n6. **Discover**: Use `available_categories`, `search_tools`, and `call_tool` to find and run tools.\n7. **Iterate**: Adjust settings, add inline `mcp_config` to routes, add skill files.\n","frontmatter":{"name":"configure_mcp_server","description":"This guide covers installation, configuration, authentication, tool discovery, prompt management, and client integration for `openbb-mcp-server`."},"isInternal":false,"tokens":7121,"sizeBytes":27087},{"name":"SKILL.md","path":"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/develop_extension/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/develop_extension/SKILL.md","title":"develop_extension","category":"anthropic-skill","format":"markdown","content":"---\nname: develop_extension\ndescription: This is a complete guide for creating a new OpenBB Platform extension from scratch. Follow every phase in order. When the user says \"build me an application that does X\", use this guide to scaffold, implement, install, and verify the extension.\n---\n\n# Build an OpenBB Platform Extension\n\nThis is a complete guide for creating a new OpenBB Platform extension from scratch.\nFollow every phase in order. When the user says \"build me an application that does X\",\nuse this guide to scaffold, implement, install, and verify the extension.\n\n---\n\n## Phase 1 — Scaffold the Project\n\nThe `openbb-cookiecutter` package must be installed in the active Python environment\nbefore scaffolding. Install it with:\n\n```\npip install openbb-cookiecutter\n```\n\nThen run the CLI to generate the project skeleton.\nAll variables have sensible defaults; override only what you need.\n\n### Template Variables\n\n| Variable | Default | Description |\n|---|---|---|\n| `full_name` | `\"Hello World\"` | Author name |\n| `email` | `\"hello@world.com\"` | Author email |\n| `project_name` | `\"OpenBB Python Extension Template\"` | Human-readable project name |\n| `project_tag` | derived from `project_name` | Hyphenated slug (used as directory name and package identifier) |\n| `package_name` | derived from `project_name` | Python package name (lower_snake_case) |\n| `provider_name` | derived from `project_name` | Provider identifier (lower_snake_case) |\n| `router_name` | derived from `project_name` | Router identifier (lower_snake_case) |\n| `obbject_name` | derived from `project_name` | OBBject accessor name (lower_snake_case) |\n\nAll derived values are automatically generated from `project_name` — you only need\nto supply `project_name` for most cases.\n\n### CLI Command\n\n```\nopenbb-cookiecutter \\\n  -o /path/to/output \\\n  --no-input \\\n  --extra-context project_name=\"My Extension Name\"\n```\n\nAdd more `--extra-context KEY=VALUE` pairs to override individual variables.\nUse `-f` to overwrite an existing directory.\n\n---\n\n## Phase 2 — Understand the Generated Structure\n\nAfter scaffolding, you get this tree (with template variables resolved):\n\n```\n<project_tag>/\n├── pyproject.toml          # Dependencies, entry points (CRITICAL)\n├── <package_name>/\n│   ├── providers/\n│   │   └── <provider_name>/\n│   │       ├── __init__.py     # Provider registration (fetcher_dict)\n│   │       ├── models/\n│   │       │   ├── example.py      # Custom-schema fetcher example\n│   │       │   └── ohlc_example.py # Standard-model fetcher example\n│   │       └── utils/\n│   │           └── helpers.py      # Shared utility functions\n│   ├── routers/\n│   │   ├── __init__.py\n│   │   ├── <router_name>.py        # Router commands (API endpoints)\n│   │   ├── <router_name>_views.py  # Chart views (optional)\n│   │   └── depends.py              # Dependency injection\n│   └── obbject/\n│       └── <obbject_name>/\n│           └── __init__.py         # OBBject accessors (optional)\n└── tests/\n    └── conftest.py\n```\n\n### The Four Plugin Types\n\nOpenBB discovers extensions via Python entry points in `pyproject.toml`.\nEach plugin type has its own entry point group:\n\n| Entry Point Group | What It Registers | Where It Lives |\n|---|---|---|\n| `openbb_provider_extension` | Data provider (fetchers) | `providers/<name>/__init__.py` |\n| `openbb_core_extension` | Router (API endpoints/commands) | `routers/<name>.py` |\n| `openbb_charting_extension` | Chart views | `routers/<name>_views.py` |\n| `openbb_obbject_extension` | Result post-processing accessors | `obbject/<name>/__init__.py` |\n\n---\n\n## Phase 3 — Implement the Data Provider\n\nThis is the core of any extension. A provider fetches data from an external source\nand returns it as typed Pydantic models.\n\n### The Three-Class Fetcher Pattern\n\nEvery data fetcher follows this structure:\n\n**Class 1 — QueryParams** (input schema):\n- Inherits from `openbb_core.provider.abstract.query_params.QueryParams`\n- Define all parameters the user can pass (e.g., `symbol`, `start_date`)\n- Use `pydantic.Field` for descriptions and defaults\n\n**Class 2 — Data** (output schema):\n- Inherits from `openbb_core.provider.abstract.data.Data`\n- Define all fields in the response (e.g., `open`, `high`, `low`, `close`, `volume`)\n- Use `pydantic.Field` for descriptions\n- Use `__alias_dict__` to map source field names to your schema names\n\n**Class 3 — Fetcher** (orchestrator):\n- Inherits from `Fetcher[YourQueryParams, list[YourData]]`\n- Has exactly three static methods:\n\n  1. `transform_query(params: dict) -> YourQueryParams`\n     Pre-process user input. Return a validated QueryParams instance.\n\n  2. `extract_data(query, credentials, **kwargs) -> list[dict]`\n     Make the actual HTTP request to the data source. Return raw data as dicts.\n     For async fetching, name it `aextract_data` instead.\n\n  3. `transform_data(query, data, **kwargs) -> list[YourData]`\n     Convert raw dicts into typed Data model instances.\n\n### Imports for Fetcher Files\n\n```python\nfrom typing import Any\nfrom openbb_core.provider.abstract.data import Data\nfrom openbb_core.provider.abstract.fetcher import Fetcher\nfrom openbb_core.provider.abstract.query_params import QueryParams\nfrom pydantic import Field\n```\n\n### HTTP Requests — Required Utilities\n\n**IMPORTANT:** All HTTP requests inside fetchers and utility helpers **must** use the built-in\nutilities from `openbb_core.provider.utils.helpers`. Do **not** create raw\n`requests`, `aiohttp`, or `httpx` clients from scratch. The built-in helpers\napply the user's configured HTTP settings (proxy, timeout, user-agent, etc.)\nfrom `system_settings.json` automatically.\n\n#### Building Query Strings\n\nConvert a QueryParams model to a URL query string, optionally excluding\nparameters that should not appear in the URL:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"interval\", \"provider\"])\nurl = f\"https://api.example.com/data?{query_string}\"\n```\n\n`model_dump()` strips `None` values automatically. Pass parameter names to\nexclude in the second argument as a list (use `[]` if nothing to exclude).\n\n#### Synchronous Requests\n\nFor use inside `extract_data` (sync fetchers):\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\n# Returns a requests.Response object\nresponse = make_request(url, headers={\"Authorization\": f\"Bearer {api_key}\"})\ndata = response.json()\n```\n\nAll `requests.get`/`requests.post` keyword arguments are passed through.\n\nIf you need a session object for multiple requests:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\nresponse = session.get(url)\n```\n\n#### Asynchronous Requests (Preferred)\n\nFor use inside `aextract_data` (async fetchers). **Always prefer async.**\n\n**Single URL** — returns parsed JSON by default:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\ndata = await amake_request(url)  # returns dict (parsed JSON)\n```\n\n**Multiple URLs** — downloads concurrently and returns a list:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nurls = [f\"https://api.example.com/data/{s}\" for s in symbols]\nall_data = await amake_requests(urls)  # returns list[dict]\n```\n\n**Custom response handling (e.g., CSV):** Both `amake_request` and\n`amake_requests` default to parsing JSON. For non-JSON content (CSV, text,\nbinary), pass a `response_callback`:\n\n```python\nfrom io import StringIO\nfrom typing import Any\nfrom pandas import read_csv\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresults: list[dict] = []\n\n\nasync def csv_callback(response, _: Any):\n    \"\"\"Parse CSV response into list of dicts.\"\"\"\n    text = await response.text()\n    df = read_csv(StringIO(text))\n    results.extend(df.to_dict(\"records\"))\n\n\nawait amake_request(url, response_callback=csv_callback)\n# results now contains the parsed rows\n```\n\nFor CSV files with header rows to skip, pass `skiprows` to `read_csv`:\n\n```python\nasync def csv_callback(response, _: Any):\n    text = await response.text()\n    df = read_csv(StringIO(text), skiprows=3)\n    results.extend(df.to_dict(\"records\"))\n```\n\n**Async session object** — if you need a raw `aiohttp.ClientSession`:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with session.get(url) as response:\n        if response.status != 200:\n            raise OpenBBError(f\"Failed: {response.status} -> {response.reason}\")\n        data = await response.json()\n```\n\n#### Summary: Which Helper to Use\n\n| Scenario | Function | Module |\n|---|---|---|\n| Build a query string | `get_querystring()` | `openbb_core.provider.utils.helpers` |\n| Sync single request | `make_request()` | `openbb_core.provider.utils.helpers` |\n| Sync session | `get_requests_session()` | `openbb_core.provider.utils.helpers` |\n| Async single request (JSON) | `amake_request()` | `openbb_core.provider.utils.helpers` |\n| Async multiple URLs (JSON) | `amake_requests()` | `openbb_core.provider.utils.helpers` |\n| Async single/multi (CSV/text) | `amake_request()` + `response_callback` | `openbb_core.provider.utils.helpers` |\n| Async raw session | `get_async_requests_session()` | `openbb_core.provider.utils.helpers` |\n\n### Using Standard Models (Multi-Provider Endpoints)\n\nTo plug into existing endpoints that other providers already serve (like `EquityHistorical`),\ninherit from the standard query/data classes instead of the abstract base classes:\n\n```python\nfrom openbb_core.provider.standard_models.equity_historical import (\n    EquityHistoricalData,\n    EquityHistoricalQueryParams,\n)\n```\n\nThen add provider-specific fields as extra attributes.\nUse `__alias_dict__` to map the source's field names to the standard field names.\n\n### Registering Fetchers in the Provider\n\nIn `providers/<provider_name>/__init__.py`, create a `Provider` instance:\n\n```python\nfrom openbb_core.provider.abstract.provider import Provider\n\nmy_provider = Provider(\n    name=\"my_provider\",\n    description=\"Description of what this provider does.\",\n    # credentials=[\"api_key\"],  # Uncomment if an API key is needed\n    website=\"https://example.com\",\n    fetcher_dict={\n        \"MyCustomModel\": MyCustomFetcher,\n        \"EquityHistorical\": MyEquityHistoricalFetcher,  # Plugs into existing endpoint\n    },\n)\n```\n\nThe `fetcher_dict` keys are model names. When a key matches a standard model name\n(like `\"EquityHistorical\"`), this provider becomes selectable via the `provider`\nparameter on that existing endpoint.\n\nFor custom/new model names, you must also create a router command that references\nthat model name (see Phase 4).\n\n### Credentials\n\nIf your provider needs an API key:\n1. Add `credentials=[\"api_key\"]` to the Provider constructor\n2. Access it in `extract_data` via `credentials.get(\"<provider_name>_api_key\")` (the `Provider` prefixes each credential with its lower-cased `name`)\n3. Users configure it in their OpenBB user settings\n\n---\n\n## Phase 4 — Implement the Router\n\nThe router defines the API endpoints (commands) that users call.\n\n### Router Basics\n\n```python\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\")\n```\n\nThe top-level prefix is determined by the entry point name in `pyproject.toml`,\nnot by the `prefix` argument. Only set `prefix` for sub-routers.\n\n### Provider-Backed Command (Standard Pattern)\n\nThis is the most common pattern. It connects a router command to one or more\nprovider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyCustomModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters (`cc`, `provider_choices`, `standard_params`, `extra_params`)\nand the `OBBject.from_query(Query(**locals()))` return pattern are mandatory and\nmust be used exactly as shown.\n\nThe `model=\"MyCustomModel\"` string must match a key in at least one provider's\n`fetcher_dict`.\n\n### Free-Form GET Endpoint\n\nFor endpoints that don't use the provider/fetcher system:\n\n```python\n@router.command(methods=[\"GET\"])\nasync def my_endpoint(symbol: str = \"AAPL\") -> OBBject[dict]:\n    \"\"\"Get some data directly.\"\"\"\n    # Make HTTP requests, compute, etc.\n    return OBBject(results={\"key\": \"value\"})\n```\n\n### Free-Form POST Endpoint\n\n```python\n@router.command(methods=[\"POST\"])\nasync def my_post_endpoint(\n    data: BaseModel,  # Body parameters\n    flag: bool = False,  # Query parameters\n) -> OBBject[dict]:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results={\"processed\": True})\n```\n\n### Dependency Injection\n\nUse `routers/depends.py` for shared dependencies:\n\n```python\nfrom typing import Annotated\nimport requests\nfrom fastapi import Depends\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nSession = Annotated[requests.Session, Depends(get_requests_session)]\n```\n\nThen use `session: Session` as a parameter in your router commands.\n\n### Adding Examples\n\n```python\nfrom openbb_core.app.model.example import APIEx, PythonEx\n\n@router.command(\n    model=\"MyModel\",\n    examples=[\n        PythonEx(\n            description=\"Get data for AAPL\",\n            code=[\"obb.my_router.my_command(symbol='AAPL')\"],\n        )\n    ],\n)\n```\n\n---\n\n## Phase 5 — Entry Points in pyproject.toml\n\nThis is **critical** — OpenBB discovers your code entirely through these entry points.\n\n### Provider Entry Point\n\n```toml\n[project.entry-points.\"openbb_provider_extension\"]\nmy_provider = \"my_package.providers.my_provider:my_provider_variable\"\n```\n\nThe variable (`my_provider_variable`) is the `Provider(...)` instance you created\nin Phase 3.\n\n### Router Entry Point\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_router = \"my_package.routers.my_router:router\"\n```\n\nThe entry point **name** (`my_router`) determines the API path prefix.\nFor example, `my_router` means endpoints appear under `/my_router/...`.\n\n### Charting Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_router_views:MyRouterViews\"\n```\n\n### OBBject Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_accessor = \"my_package.obbject.my_obbject:ext\"\nmy_namespace = \"my_package.obbject.my_obbject:class_ext\"\n```\n\n---\n\n## Phase 6 — Chart Views (Optional)\n\nIf you want to add charting support:\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyRouterViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Create a chart for my_command results.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build your chart using fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## Phase 7 — OBBject Accessors (Optional)\n\nResult post-processing extensions that add methods to the `OBBject` response.\n\n### Function Accessor (Property-Like)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"to_csv\", description=\"Convert results to CSV string.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject, **kwargs) -> str:\n    \"\"\"Convert to CSV.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### Class Accessor (Namespaced Methods)\n\n```python\nclass_ext = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@class_ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self, **kwargs):\n        \"\"\"Return a summary.\"\"\"\n        df = self._obbject.to_dataframe()\n        return df.describe()\n```\n\n---\n\n## Phase 8 — Install, Build, and Test\n\n### Install in Development Mode\n\nFrom the generated project root directory:\n\n```\nuv pip install -e . --group dev\n```\n\nThis registers the entry points so OpenBB discovers your extension immediately.\n\n### Build Static Assets with `openbb-build`\n\n**CRITICAL:** After installing a new extension, or after making changes to any\nof the following, you **must** run `openbb-build` before using the Python\ninterface (`obb.<router>.<command>(...)`):\n\n- **Model definitions** — `QueryParams` or `Data` classes (field names, types,\n  defaults, descriptions)\n- **Provider registration** — changes to `fetcher_dict` keys, adding/removing\n  fetchers\n- **Router commands** — adding, removing, or renaming `@router.command()`\n  endpoints\n- **Entry points** — changes to `pyproject.toml` plugin entries\n- **Any importable item in the registration chain** — the `Provider(...)`\n  instance, router module, or model module paths\n\nYou do **not** need to re-run `openbb-build` when changing:\n\n- Logic inside `extract_data` / `aextract_data` / `transform_data` /\n  `transform_query` static methods (the Fetcher method bodies)\n- Utility/helper functions\n- Internal implementation details that don't affect the public schema\n\n```\nopenbb-build\n```\n\nThis regenerates the static assets (type stubs, package interface, provider\nmaps) that the Python interface relies on. Without this step, new or modified\ncommands will not appear on the `obb` object and calls will fail.\n\n**When running as an API server** (e.g., via `uvicorn` or the MCP server),\nstatic assets are **not used** — the API discovers extensions dynamically at\nstartup. You do not need to run `openbb-build` for API-only usage.\n\n### Verify Installation\n\nStart a Python session and check:\n\n```python\nfrom openbb import obb\n# Your new commands should appear:\n# obb.<router_name>.<command_name>(...)\n```\n\nOr start the API server and verify the new endpoints appear.\n\n### Run Tests\n\n```\npytest tests/ -v\n```\n\nThe generated `tests/conftest.py` sets `OPENBB_AUTO_BUILD=true` for proper\ntest environment setup.\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me an application that does X\":\n\n1. **Analyze** — Determine what data sources are needed, what endpoints to expose,\n   and whether to use standard models or custom schemas.\n2. **Scaffold** — Run `openbb-cookiecutter` with a meaningful `project_name`.\n3. **Delete examples** — Remove `example.py` and `ohlc_example.py` from the models\n   directory. Clean up the example router commands.\n4. **Implement models** — Create `QueryParams` + `Data` + `Fetcher` classes for each\n   data source in `providers/<name>/models/`.\n5. **Register fetchers** — Update `providers/<name>/__init__.py` with the `fetcher_dict`.\n6. **Implement router** — Create `@router.command(model=\"...\")` endpoints in\n   `routers/<name>.py`.\n7. **Update entry points** — Ensure `pyproject.toml` entry points match your actual\n   module paths and variable names.\n8. **Add dependencies** — Add any third-party packages to the `[project]`\n   `dependencies` list in `pyproject.toml`.\n9. **Install** — Run `uv pip install -e . --group dev` from the project root.\n10. **Build** — Run `openbb-build` to regenerate static assets for the Python\n    interface. Skip this step if only using the API server.\n11. **Test** — Verify the commands work, then write tests.\n","frontmatter":{"name":"develop_extension","description":"This is a complete guide for creating a new OpenBB Platform extension from scratch. Follow every phase in order. When the user says \"build me an application that does X\", use this guide to scaffold, implement, install, and verify the extension."},"isInternal":false,"tokens":5019,"sizeBytes":21372},{"name":"SKILL.md","path":"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/use_openbb_cli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/use_openbb_cli/SKILL.md","title":"use_openbb_cli","category":"anthropic-skill","format":"markdown","content":"---\nname: use_openbb_cli\ndescription: This guide covers the `openbb` command-line surface — running commands, generating `.spec` files, dispatching against precomputed specs, and feeding spec output back into `openbb-mcp` for spec-driven proxy mode.\n---\n\n# Using the openbb-cli\n\n`openbb-cli` ships the `openbb` console-script. It is a self-contained\nfront-end for the same `obb` namespace the MCP server exposes, plus a build\ntool for generating reusable `.spec` files and full installable extension\npackages.\n\nThree console-scripts live on the v5 stack:\n\n| Command | Package | Purpose |\n|---|---|---|\n| `openbb` | `openbb-cli` | Run commands, REPL, generate `.spec` files. |\n| `openbb-mcp` | `openbb-mcp-server` | MCP server launcher (HTTP/SSE/stdio transports). |\n| `openbb-build` | `openbb-core` | Register a newly installed extension into the `obb` namespace. |\n\nThis guide focuses on `openbb`.\n\n---\n\n## Installation\n\n```\npip install openbb-cli\n```\n\nPulls `openbb-core[pandas]`, `prompt-toolkit`, `rich`, and `httpx`.\n\n---\n\n## Execution Modes\n\n`openbb` has six mutually-exclusive modes (the rest of the flags are shared\nacross modes).\n\n| Mode flag | Purpose |\n|---|---|\n| *(default)* | Single-shot: dispatch one dotted command and exit with NDJSON on stdout. |\n| `-i` / `--interactive` | Rich REPL with prompt-toolkit completion. |\n| `--batch` | Read NDJSON requests from stdin; emit NDJSON responses to stdout. |\n| `--generate-spec` | Fetch `openapi.json` from `--server` and write a precomputed `.spec` file to `--output`. |\n| `--generate-extension` | Build a full installable OpenBB Platform extension package from a `.spec` file. |\n| `--list-commands` / `--describe COMMAND[:PROVIDER]` | Print the command catalog or one command's schema as JSON. Needs `--spec` or `--server`. |\n\n### Single-shot\n\n```bash\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n```\n\nOutput is one dispatcher response as a JSON line: `{\"ok\": true, \"result\": ...}`\non success, where `result` holds the command's OBBject fields (`results`,\n`provider`, ...), or `{\"ok\": false, \"error\": {\"type\": ..., \"message\": ...}}` on\nfailure. Unset fields are left out.\nExit code is `0` on success, `1` on a dispatch error, `2` on a usage error.\n\n### Interactive REPL\n\n```bash\nopenbb -i\n```\n\nPrompt-toolkit completion against the resolved command catalog. Same flag\nsurface — `--server` / `--spec` / `--header` etc. work identically.\n\n### Batch (NDJSON in/out)\n\n```bash\necho '{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"AAPL\"},\"id\":\"a\"}\n{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"MSFT\"},\"id\":\"b\"}' \\\n  | openbb --batch\n```\n\nEach input line is one `Request`; each output line is the matching\n`Response`. Use `--batch-concurrency N` (or `OPENBB_CLI_BATCH_CONCURRENCY`) to\ncontrol in-flight parallelism.\n\n---\n\n## Backend Selection\n\nThree backends share the dispatch surface; selection priority is\n`--spec` > `--server` > in-process `obb`.\n\n| Backend | Trigger | Behavior |\n|---|---|---|\n| Local | (default) | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. |\n| Remote HTTP | `--server URL` | `HttpDispatcher` proxies to an `openbb-platform-api` server. The heavy import lives on the server. |\n| Spec-driven | `--spec [NAME=]PATH` | Skips OpenAPI fetch + parse entirely. The spec carries the server URL it was generated against and the per-command HTTP methods. |\n\n```bash\nopenbb --server https://api.example.com economy.gdp\nopenbb --spec /etc/openbb/cli.spec economy.gdp\n```\n\n`--spec` is repeatable. Multiple `NAME=PATH` entries build a multi-spec\ndispatcher and the REPL routes by leading namespace\n(`congress.bill` → `congress` spec, `nyfed.markets.ambs` → `nyfed` spec).\nA single unnamed `--spec PATH` keeps the flat (unprefixed) command surface.\n\n---\n\n## `.spec` Files\n\nA `.spec` file is a precomputed JSON document encoding every command the\nserver exposes — URL path, HTTP method, parameter schema, response schema,\nand a `content_sha256` integrity hash over the canonical-JSON body.\n\n### Generating a spec\n\n```bash\n# From an openbb-platform-api server\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# From a server using a non-default OpenAPI path\nopenbb --generate-spec \\\n  --server https://markets.example.com \\\n  --openapi-path /static/docs/markets-api.yml \\\n  --output markets.spec\n\n# From a Socrata story (no OpenAPI involved — walks the story for datasetUid\n# entries and emits one router namespace per dataset)\nopenbb --generate-spec \\\n  --socrata-story https://data.example.gov/path/to/story.json \\\n  --output socrata.spec\n```\n\n### Using a spec\n\nAfter generation, every dispatch can use the spec to skip OpenAPI fetch +\nparse on every call:\n\n```bash\nopenbb --spec cli.spec economy.gdp\nopenbb --spec cli.spec --list-commands\nopenbb --spec cli.spec --describe equity.price.historical\nopenbb --spec cli.spec --describe equity.price.historical:yfinance\n```\n\nThe spec carries the upstream `base_url` it was generated against, so\n`--server` is not needed alongside `--spec`.\n\n### Filtering commands\n\n`--include` and `--exclude` are repeatable glob patterns over dotted command\nnames, applied by `--generate-extension` (`--generate-spec` always writes every\ncommand). When `--include` is supplied it takes priority — anything not\nmatching is dropped regardless of `--exclude`.\n\n```bash\nopenbb --generate-extension \\\n  --spec cli.spec \\\n  --output ./openbb-equity-subset \\\n  --include 'equity.*' \\\n  --exclude 'equity.fundamentals.*'\n```\n\n---\n\n## Headers and Query Parameters\n\nEvery dispatch (and the OpenAPI fetch during `--generate-spec`) carries the\nsame headers/query-params.\n\n```bash\n# Per-flag, repeatable\nopenbb --server https://api.example.com \\\n  -H 'Authorization: Bearer xxx' \\\n  -H 'X-Tenant: acme' \\\n  -Q 'api_key=secret' \\\n  equity.price.quote --symbol AAPL\n\n# From a JSON file (object of string values)\nopenbb --header-file ./headers.json --query-param-file ./query.json economy.gdp\n```\n\nEnv-var auto-loading: any `OPENBB_HTTP_QUERY_*` env var becomes a query param\n(`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). CLI flags beat env vars.\n\nFor multi-spec dispatchers, prefix a header or query parameter with a\nnamespace to send it to that spec only (`-H 'congress:X-API-Key: xxx'`,\n`-Q 'nyfed:format=json'`), or scope it in the TOML config\n(`[specs.<ns>.headers]`, `[specs.<ns>.query]`).\n\n---\n\n## Configuration File (`openbb.toml`)\n\nFlag defaults can be set via a layered TOML cascade — same pattern openbb-core\nuses. Layers (highest priority last):\n\n```\npyproject.toml [tool.openbb-cli]\n  → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD)\n  → --config PATH (explicit)\n  → .env files\n  → real shell env vars\n```\n\nCLI flags always beat env vars; env vars beat TOML.\n\nThe CLI reads top-level keys of `openbb.toml` (and of `[tool.openbb-cli]` in\n`pyproject.toml`): `server`, `spec`, `openapi-path`, `header-file`,\n`query-param-file`, `auth-hook`, and the `[headers]`, `[query]`,\n`[specs.<ns>]`, and `[settings]` tables. Set the batch concurrency with\n`--batch-concurrency` or `OPENBB_CLI_BATCH_CONCURRENCY`.\n\n```toml\n# openbb.toml\nserver = \"https://api.example.com\"\n\n[headers]\nAuthorization = \"Bearer ...\"\n\n[specs.congress]\npath = \"/etc/openbb/congress.spec\"\n[specs.congress.headers]\n\"X-API-Key\" = \"...\"\n```\n\nHeader and query values are sent as written; they are not expanded from\n`$VAR` references.\n\n```bash\nopenbb --config /etc/openbb/openbb.toml --list-commands\nopenbb --print-config-template      # documented TOML template\nopenbb --show-config                # merged config as JSON\n```\n\n---\n\n## Building Extensions From a Spec\n\n`--generate-extension` builds a full installable OpenBB Platform extension\npackage from exactly one `.spec` file. The project is written to\n`<output>/<project-name>`; the command prints that path with its providers,\nrouters, and install commands.\n\n```bash\nopenbb --generate-extension \\\n  --spec congress.spec \\\n  --output ./extensions \\\n  --provider-name congress_gov \\\n  --project-name openbb-congress-gov \\\n  --package-name openbb_congress_gov\n\npip install -e ./extensions/openbb-congress-gov\nopenbb-build\n```\n\n`--project-name` defaults to `openbb-<provider-name>`, `--package-name` to\n`openbb_<provider-name>`, and `--provider-name` to the name of the `--output`\ndirectory. `--router-name` is accepted but not used by the generator yet.\n\nAfter registration the extension's commands are reachable under the printed\nrouters, as `obb.<router>.*` in Python and `<router>.*` from any backend.\n\n---\n\n## Spec → MCP Round-Trip\n\nThe same `.spec` file can drive the MCP server in spec-driven proxy mode —\nthe heavy `import openbb` lives on the upstream `openbb-platform-api`, the\nMCP launcher only knows the spec.\n\n```bash\n# 1. Generate the spec on the host that already has openbb installed.\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# 2. Hand off the spec to a lightweight MCP container.\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML:\n\n```toml\n# openbb.toml passed via `openbb-mcp --config-file openbb.toml`\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\ncontent_sha256 = \"abc...\"\n\n[mcp.spec.headers]\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\n```\n\nThe MCP server synthesizes a FastAPI proxy app from the spec's commands;\nFastMCP exposes the routes as MCP tools; per-tool dispatch forwards to the\nspec's `base_url` over `aiohttp`. See `configure_mcp_server` for multi-spec\nmounts (`[mcp.spec.NAME]`), per-spec auth/middleware hooks, and the\n`content_sha256` provenance pin.\n\n---\n\n## The Same Commands as MCP Tools\n\nWhen `openbb-cli` is installed next to `openbb-mcp-server`, the MCP server\nexposes the CLI's dispatcher as tools, so an agent gets the same surface:\n\n| CLI | MCP tool |\n|---|---|\n| `openbb <command> --param value` | `openbb_dispatch` (`command`, `params`) |\n| `openbb --batch` | `openbb_batch_dispatch` (`requests`) |\n| `openbb --list-commands` | `openbb_list_commands` |\n| `openbb --describe <command>` | `openbb_describe_command` (`command`, optional `provider`) |\n\nEvery tool takes an optional `server_url`, the counterpart of `--server`, and\nfalls back to `OPENBB_SERVER_URL` like the CLI does.\n\n---\n\n## Useful Environment Variables\n\n| Variable | Purpose |\n|---|---|\n| `OPENBB_SERVER_URL` | Default `--server` URL. |\n| `OPENBB_SPEC_PATH` | Default `--spec` path. |\n| `OPENBB_HTTP_QUERY_*` | Auto-loaded as query parameters (`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). |\n| `OPENBB_CLI_CONFIG` | Default `--config` path. |\n| `OPENBB_CLI_ENV_FILE` | Default `--env-file` path. |\n| `OPENBB_CLI_BATCH_CONCURRENCY` | Default `--batch-concurrency`. |\n| `OPENBB_HEADER_FILE` / `OPENBB_QUERY_PARAM_FILE` | Default `--header-file` / `--query-param-file`. |\n\n`~/.openbb_platform/.env` is always loaded as a fallback `.env` source — real\nshell exports beat both.\n\n---\n\n## Quick Reference\n\n```bash\n# Discover commands (needs --server or --spec)\nopenbb --server URL --list-commands\nopenbb --server URL --describe equity.price.historical\n\n# One-shot dispatch\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n\n# Batch (NDJSON)\ncat requests.ndjson | openbb --batch\n\n# Generate a precomputed spec from a server\nopenbb --generate-spec --server URL --output out.spec\n\n# Dispatch through a precomputed spec\nopenbb --spec out.spec equity.price.quote --symbol AAPL\n\n# Multi-spec routing by leading namespace\nopenbb --spec congress=congress.spec --spec nyfed=nyfed.spec congress.bill\n\n# Generate a full extension package from a spec\nopenbb --generate-extension --spec out.spec --output ./openbb-myprov\n\n# Inspect resolved config\nopenbb --show-config\nopenbb --print-config-template > openbb.toml\n```\n","frontmatter":{"name":"use_openbb_cli","description":"This guide covers the `openbb` command-line surface — running commands, generating `.spec` files, dispatching against precomputed specs, and feeding spec output back into `openbb-mcp` for spec-driven proxy mode."},"isInternal":false,"tokens":3076,"sizeBytes":11789},{"name":"SKILL.md","path":"openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/work_with_server/SKILL.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/work_with_server/SKILL.md","title":"work_with_server","category":"anthropic-skill","format":"markdown","content":"---\nname: work_with_server\ndescription: This guide explains how to call tools, interpret responses, discover capabilities, use prompts, and handle errors when interacting with an OpenBB MCP server.\n---\n\n# Working With the OpenBB MCP Server\n\nThis guide explains how to call tools, interpret responses, discover\ncapabilities, use prompts, and handle errors when interacting with an OpenBB\nMCP server.\n\n---\n\n## Tool Discovery Workflow\n\nWhen the server runs with `--tool-discovery`, the OpenBB tools are left out of\nthe tool list, and a small set of discovery tools finds and runs them.\n\nDiscovery holds no per-session state — every client sees the same tool list, and\nnothing needs to be activated before a call.\n\n### Step 1 — List Categories\n\nCall `available_categories` (no arguments) to see what is installed:\n\n```json\n[\n    {\n        \"name\": \"equity\",\n        \"subcategories\": [\n            {\"name\": \"price\", \"tool_count\": 5},\n            {\"name\": \"fundamental\", \"tool_count\": 12}\n        ],\n        \"total_tools\": 17\n    },\n    {\n        \"name\": \"economy\",\n        \"subcategories\": [\n            {\"name\": \"general\", \"tool_count\": 3}\n        ],\n        \"total_tools\": 3\n    }\n]\n```\n\nEach category maps to a top-level API router. Subcategories are nested routers.\n\n### Step 2 — Browse Tools in a Category\n\nCall `available_tools` with a category name:\n\n```json\n// Input\n{\"category\": \"equity\", \"subcategory\": \"price\"}\n\n// Output\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"Get historical price data...\"},\n    {\"name\": \"equity_price_quote\", \"description\": \"Get current price quote...\"}\n]\n```\n\nEach entry has the tool's name and a one-sentence description. The\n`subcategory` argument is optional. Omit it to see all tools in the category.\n\n### Step 3 — Search for Tools\n\nCall `search_tools` with a natural-language query:\n\n```json\n// Input\n{\"query\": \"historical stock prices\"}\n\n// Output - full tool definitions, in the same format as list_tools\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"...\", \"inputSchema\": {\"...\": \"...\"}}\n]\n```\n\nSearch covers the hidden OpenBB tools and returns at most five matches. Use\neach match's `inputSchema` to build the arguments.\n\n### Step 4 — Call a Tool\n\nCall `call_tool` with the tool name and its arguments:\n\n```json\n{\"name\": \"equity_price_historical\", \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"}}\n```\n\n`call_tool` runs the named tool and returns its result. A hidden tool can also\nbe called directly by name, with the same arguments.\n\n### When Discovery Is Disabled\n\nIf the server was started without `--tool-discovery`, the discovery tools are not\navailable. All tools in `default_tool_categories` are listed and called directly;\ntools outside them are not served, and calling one fails with \"Unknown tool\".\n\n---\n\n## openbb-cli Dispatcher Tools\n\nWhen the optional `openbb-mcp-server[cli]` extra is installed (it pulls in\n`openbb-cli`), the server also registers four first-class tools that wrap\n`openbb-cli`'s NDJSON dispatcher protocol. They give an agent the same\n`obb`-namespace command surface that the `openbb` CLI exposes — without\nshelling out or standing up a Python REPL.\n\n### `openbb_dispatch`\n\nExecute a single command and return its serialized result.\n\n```json\n{\n    \"name\": \"openbb_dispatch\",\n    \"arguments\": {\n        \"command\": \"equity.price.historical\",\n        \"params\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"},\n        \"server_url\": null\n    }\n}\n```\n\n| Argument | Type | Notes |\n|---|---|---|\n| `command` | `string` | Dotted command path under the `obb` namespace (e.g. `equity.price.historical`). |\n| `params` | `dict \\| null` | Keyword arguments forwarded to the command. Defaults to `{}`. |\n| `server_url` | `string \\| null` | When set, dispatches against a remote `openbb-platform-api` server (HTTP). When omitted, falls back to `OPENBB_SERVER_URL`, then in-process local dispatch. |\n\nThe result is a dispatcher response. `ok` reports success, `result` holds the\ncommand's OBBject fields (`results`, `provider`, ...), and `error` carries the\n`type` and `message` of a failure instead of raising:\n\n```json\n{\n    \"id\": null,\n    \"ok\": true,\n    \"result\": {\"results\": [{\"date\": \"2025-01-02\", \"close\": 243.85}], \"provider\": \"yfinance\"},\n    \"error\": null\n}\n```\n\nRemote dispatch unwraps a single-row `results` list to the row itself.\n\n### `openbb_batch_dispatch`\n\nExecute many commands concurrently and return results in input order.\n\n```json\n{\n    \"name\": \"openbb_batch_dispatch\",\n    \"arguments\": {\n        \"requests\": [\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"AAPL\"}, \"id\": \"a\"},\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"MSFT\"}, \"id\": \"b\"}\n        ]\n    }\n}\n```\n\nEach entry needs `command` (dotted path); `params` and `id` are optional. The\n`id` is opaque correlation echoed back on the matching response. Failures\nsurface as response objects with `ok=False` and a structured `error` block —\nno exception bubbles up, errors are per-request.\n\n### `openbb_list_commands`\n\nList every command with a one-line description. Takes only the optional\n`server_url`. The response's `result` is a list of `{\"name\", \"description\"}`\nentries. Equivalent to `openbb --list-commands` on the CLI.\n\n### `openbb_describe_command`\n\nReturn one command's parameters and output schema.\n\n```json\n{\n    \"name\": \"openbb_describe_command\",\n    \"arguments\": {\"command\": \"equity.price.historical\", \"provider\": \"yfinance\"}\n}\n```\n\nThe response's `result` has the command `name`, its `parameters` (each with\n`name`, `in`, and `type`, plus `required`, `default`, `choices`, and `help` when\nset), and its `output_schema`. For a command with several providers, pass\n`provider` to get that provider's `parameters` and `output_schema`; without it,\n`result.providers` maps every provider to its own `parameters` and\n`output_schema`. Equivalent to `openbb --describe equity.price.historical`\n(`equity.price.historical:yfinance` for one provider) on the CLI.\n\n### Local vs remote dispatch\n\n| Mode | Trigger | Behavior |\n|---|---|---|\n| Local | No `server_url`, no `OPENBB_SERVER_URL` | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. Single-tenant. |\n| Remote | `server_url` arg or `OPENBB_SERVER_URL` env | `HttpDispatcher` proxies commands to a long-running `openbb-platform-api` server. Multi-tenant; the heavy import lives on the server. |\n\nMode is per-call — different requests in the same session can target different\nservers. Dispatchers are cached for the server's lifetime, so the `import openbb`\ncost (local) and the remote server's OpenAPI download are paid once per target.\n\n---\n\n## Calling Data Tools\n\n### Input Parameters\n\nEvery data tool has a JSON Schema describing its input. A typical tool schema:\n\n```json\n{\n    \"type\": \"object\",\n    \"properties\": {\n        \"symbol\": {\n            \"type\": \"string\",\n            \"description\": \"Symbol to get data for.\"\n        },\n        \"provider\": {\n            \"type\": \"string\",\n            \"enum\": [\"fmp\", \"polygon\", \"yfinance\"],\n            \"description\": \"The provider to use for the query.\"\n        },\n        \"start_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"Start date of the data.\"\n        },\n        \"end_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"End date of the data.\"\n        },\n        \"interval\": {\n            \"type\": \"string\",\n            \"default\": \"1d\",\n            \"description\": \"Time interval of the data.\"\n        }\n    },\n    \"required\": [\"symbol\"]\n}\n```\n\nKey rules:\n\n- **`provider`** — present only on endpoints that use the provider interface.\n  When listed, its enum shows available provider sources. If the endpoint has\n  only one provider, you can omit it and the sole provider is used\n  automatically. When multiple providers are available, select one from the\n  enum. Different providers may return different fields or support different\n  parameters. Endpoints that do not use the provider interface (basic GET/POST\n  routes) have no `provider` parameter at all.\n- **`symbol` formatting** — symbols are case-insensitive. Multiple symbols can\n  be comma-separated: `\"AAPL,MSFT,GOOG\"`.\n- **Dates** — always formatted as `YYYY-MM-DD` strings.\n- **Optional parameters** — have `anyOf` with a `null` type or a `default`\n  value. Omit them to use defaults.\n- **Provider-specific parameters** — some parameters are only relevant for\n  certain providers. The schema unions all of them; irrelevant ones are\n  silently ignored.\n- **Valid values** — never guess a value. A parameter with a fixed set of\n  values has an `enum`. When the values differ by provider, or several can be\n  comma-separated, the description lists them (`Valid values by provider:\n  ...`, `Valid values for provider oecd (comma-separate several): ...`). When\n  another tool lists them, the description names it and its arguments\n  (``Get the valid values from the `derivatives_options_strikes` tool with\n  symbol=<the symbol you pass here>.``) — call that tool first.\n\n### Example Tool Call\n\n```json\n{\n    \"name\": \"equity_price_historical\",\n    \"arguments\": {\n        \"symbol\": \"AAPL\",\n        \"provider\": \"fmp\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-02-01\",\n        \"interval\": \"1d\"\n    }\n}\n```\n\n---\n\n## Understanding the Response\n\nEvery OpenBB tool returns an **OBBject** — a standardized response envelope:\n\n```json\n{\n    \"id\": \"06520558-d54a-7e53-8000-7aafc8a42694\",\n    \"results\": [...],\n    \"provider\": \"fmp\",\n    \"warnings\": null,\n    \"chart\": null,\n    \"extra\": {\n        \"metadata\": {\n            \"arguments\": {...},\n            \"duration\": 565256375,\n            \"route\": \"/equity/price/historical\",\n            \"timestamp\": \"2025-01-15 11:28:57.149548\"\n        }\n    }\n}\n```\n\n### Response Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `id` | `string` | UUID identifying this request |\n| `results` | `list[dict] \\| dict \\| string \\| null` | The actual data. Usually a list of records |\n| `provider` | `string \\| null` | Which provider fulfilled the request |\n| `warnings` | `list[object] \\| null` | Non-fatal warnings from the provider or platform |\n| `chart` | `object \\| null` | Chart artifact if `chart=true` was passed |\n| `extra` | `dict` | Execution metadata and results metadata |\n\n### The `results` Field\n\nThis is the primary data payload. Its structure depends on the endpoint:\n\n**Tabular data** — most common, a list of dictionaries (one per row):\n\n```json\n\"results\": [\n    {\"date\": \"2025-01-02\", \"open\": 150.0, \"high\": 155.0, \"low\": 149.0, \"close\": 153.5, \"volume\": 1000000},\n    {\"date\": \"2025-01-03\", \"open\": 153.0, \"high\": 157.0, \"low\": 152.0, \"close\": 156.2, \"volume\": 1200000}\n]\n```\n\n**Single record** — some endpoints return a single dict:\n\n```json\n\"results\": {\"symbol\": \"AAPL\", \"price\": 185.50, \"change\": 2.30, \"volume\": 45000000}\n```\n\n**Empty results** — when no data is available:\n\n```json\n\"results\": null\n```\n\nor\n\n```json\n\"results\": []\n```\n\n**Field names vary by provider** — different providers may return different\ncolumns for the same endpoint. Always check the keys in the returned records.\n\n### The `warnings` Field\n\nWarnings are non-fatal issues that occurred during execution:\n\n```json\n\"warnings\": [\n    {\n        \"category\": \"OpenBBWarning\",\n        \"message\": \"Parameter 'source' is not supported by fmp. Available for: intrinio.\"\n    }\n]\n```\n\nCommon warning scenarios:\n- Unknown parameters silently ignored by the provider\n- Partial data returned (fewer rows than requested)\n- Provider-specific data quality notes\n\nWhen `warnings` is `null`, no warnings were generated.\n\n### The `extra` Field\n\nContains execution metadata and optional results metadata:\n\n```json\n\"extra\": {\n    \"metadata\": {\n        \"arguments\": {\n            \"provider_choices\": {\"provider\": \"fmp\"},\n            \"standard_params\": {\"symbol\": \"AAPL\", \"start_date\": \"2025-01-01\"},\n            \"extra_params\": {}\n        },\n        \"duration\": 565256375,\n        \"route\": \"/equity/price/historical\",\n        \"timestamp\": \"2025-01-15 11:28:57.149548\"\n    },\n    \"results_metadata\": {\n        \"...provider-specific metadata...\"\n    }\n}\n```\n\n**`metadata`** — always present (unless disabled):\n- `arguments` — the exact parameters used, split into provider choices,\n  standard params, and extra (provider-specific) params\n- `duration` — nanoseconds the request took\n- `route` — the API endpoint path\n- `timestamp` — when the request was made\n\n**`results_metadata`** — present when the provider returns contextual\ninformation about the data (e.g., FRED series metadata, CBOE options\nmetadata). Contents vary by endpoint and provider.\n\n### The `chart` Field\n\nWhen a tool is called with `chart: true` (where supported), the server\nreplaces the Plotly figure with an OpenBB Workspace artifact:\n\n```json\n\"chart\": {\n    \"type\": \"chart\",\n    \"name\": \"AAPL Close\",\n    \"description\": \"Output of the equity_price_historical tool.\",\n    \"uuid\": \"5b0e1c52-6f3e-4a51-9d6f-0f1b6a2f4c11\",\n    \"content\": [{\"date\": \"2025-01-02\", \"close\": 243.85}],\n    \"chart_params\": {\"chartType\": \"line\", \"xKey\": \"date\", \"yKey\": [\"close\"]}\n}\n```\n\n`content` holds the rows the chart is drawn from and `chart_params` says how:\n`chartType` is `line`, `bar`, or `scatter` with `xKey` and `yKey`, or `pie`\nor `donut` with `angleKey` and `calloutLabelKey`. Figures that cannot be\ndrawn that way (surfaces, candlesticks, heatmaps) come back as a `table`\nartifact of their data, without `chart_params`. Tools that return a figure\ndirectly return the artifact as their whole output. Present the artifact\nas it is; do not convert it or re-plot it. Not all endpoints support\ncharting.\n\n---\n\n## Provider Selection\n\n### How Providers Work\n\nEach data endpoint can have multiple provider sources (e.g., FMP, Yahoo Finance,\nPolygon). Providers differ in:\n\n- **Available parameters** — some providers offer extra filtering or options\n- **Returned fields** — column names and data granularity may differ\n- **Rate limits and authentication** — some providers require API keys\n- **Data coverage** — geographic markets, date ranges, asset types\n\n### Choosing a Provider\n\nWhen a tool's input schema includes `provider`, its enum lists all installed\nprovider sources for that endpoint. If there is only one provider, the\nparameter can be omitted — the sole provider is selected automatically.\nWhen multiple providers are available, pick one based on:\n\n1. **Check the enum** — only listed options work\n2. **Consider the data need** — different providers may have different fields\n3. **API key requirements** — some providers need credentials configured in\n   `~/.openbb_platform/user_settings.json`\n\nEndpoints that do not use the provider interface (basic GET/POST routes added\nvia `router.command(methods=[\"GET\"])` or raw FastAPI) have no `provider`\nparameter.\n\nIf a provider fails due to missing credentials, the error message will indicate\nan authentication issue.\n\n### Provider-Specific Parameters\n\nSome parameters only apply to certain providers. For example, the `source`\nparameter might only be available with the `intrinio` provider. Passing it to\n`fmp` generates a warning but does not cause an error.\n\n---\n\n## Working With Prompts\n\nThe server includes a prompt system for accessing documentation, usage guides,\nand analysis frameworks.\n\n### List Available Prompts\n\nCall `list_prompts` (no arguments):\n\n```json\n[\n    {\"name\": \"system_prompt\", \"description\": \"System prompt with guidance...\", \"arguments\": []},\n    {\"name\": \"analyze_stock\", \"description\": \"Analyze a stock.\", \"arguments\": [\n        {\"name\": \"symbol\", \"description\": \"Ticker.\", \"required\": true},\n        {\"name\": \"focus\", \"description\": \"Area.\", \"required\": false}\n    ]}\n]\n```\n\n### Get a Prompt\n\nCall `get_prompt` with the prompt `name` and any `arguments`:\n\n```json\n// Input\n{\"name\": \"analyze_stock\", \"arguments\": {\"symbol\": \"AAPL\"}}\n\n// Output - the rendered prompt\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"Analyze AAPL focusing on fundamentals.\"}\n    ]\n}\n```\n\nPass every argument value as a string (`{\"years\": \"10\"}`, not `{\"years\": 10}`);\nMCP prompt arguments are strings. Prompts without arguments return their\ncontent as-is. Omitted optional arguments fall back to their defaults, and a\nmissing required argument is an error. Skills are resources, not prompts; see\n[Working With Skills](#working-with-skills).\n\n### Prompt Categories by Tag\n\n| Tag | Source | Description |\n|---|---|---|\n| `system` | System prompt file | Server-wide context and instructions |\n| `server` | Server prompts JSON | Reusable analysis frameworks |\n| `route-specific` | Inline on API routes | Endpoint usage guides |\n\n---\n\n## Error Handling\n\n### Error Types\n\n| Scenario | What Happens |\n|---|---|\n| **Invalid parameters** | Error with HTTP 422 details explaining which parameter failed validation |\n| **Missing required parameter** | Error with HTTP 422 indicating the missing field |\n| **Provider authentication failure** | Error with HTTP 401/403 indicating credentials are missing or invalid |\n| **Provider rate limit** | Error with HTTP 429 or provider-specific rate limit message |\n| **No data available** | Successful response with `results: null` or `results: []` |\n| **Tool hidden by discovery** | Tool is not in the tool list; find it with `search_tools` and run it with `call_tool` |\n| **Tool outside the served categories** | \"Unknown tool\" error, by name, through `call_tool`, and through `run_pipeline` |\n| **Unknown tool name** | Standard MCP protocol error |\n| **Category not found** (discovery) | Error listing available categories |\n| **Connection failure** | Error with \"Request error: ...\" |\n\n### Interpreting Empty Results\n\nA response with `results: null` or `results: []` is **not an error** — it\nmeans the provider had no data matching the query. Common causes:\n\n- Date range with no trading days\n- Symbol not covered by the selected provider\n- Data not yet available for the requested period\n\nTry a different provider, adjust the date range, or verify the symbol format.\n\n### Reading Validation Errors\n\nValidation errors (HTTP 422) include detail about what went wrong:\n\n```\nHTTP error 422: Unprocessable Entity - {\"detail\": [{\"loc\": [\"query\", \"symbol\"], \"msg\": \"field required\", \"type\": \"value_error.missing\"}]}\n```\n\nThe `loc` field shows which parameter failed, and `msg` explains why.\n\n---\n\n## Practical Patterns\n\n### Fetching Time Series Data\n\n1. With discovery enabled, find the tool: `search_tools({\"query\": \"historical stock prices\"})`\n2. Call with date range:\n   ```json\n   {\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"start_date\": \"2025-01-01\", \"end_date\": \"2025-02-01\"}\n   ```\n3. Read `results` — each record has `date`, `open`, `high`, `low`, `close`,\n   `volume` (field names depend on provider)\n\n### Comparing Multiple Symbols\n\nPass comma-separated symbols:\n\n```json\n{\"symbol\": \"AAPL,MSFT,GOOG\", \"provider\": \"fmp\"}\n```\n\nResults will contain records for all symbols. Filter by the `symbol` field in\neach record if present, or by the ordering pattern.\n\n### Chaining Tool Calls\n\nUse the output of one tool as input to another:\n\n1. Get peers: `equity_compare_peers({\"symbol\": \"AAPL\", \"provider\": \"fmp\"})`\n2. Extract symbols from `results`\n3. Get quotes: `equity_price_quote({\"symbol\": \"AAPL,PEER1,PEER2\", \"provider\": \"fmp\"})`\n\nWhen a later tool needs the earlier tool's rows rather than a few values picked\nfrom them, use `run_pipeline` (below) so the rows never pass through the\nconversation.\n\n### Running Data-Processing Tools With `run_pipeline`\n\nTechnical, quantitative, and econometrics tools (`technical_rsi`,\n`technical_macd`, ...) take the rows to analyze as a `data` argument. Fetch and\nanalyze in one `run_pipeline` call instead of copying a price history into\ntheir arguments:\n\n```json\n{\n    \"steps\": [\n        {\n            \"id\": \"prices\",\n            \"tool\": \"equity_price_historical\",\n            \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\", \"start_date\": \"2025-01-01\"}\n        },\n        {\"id\": \"rsi\", \"tool\": \"technical_rsi\", \"arguments\": {\"length\": 14}, \"inputs\": {\"data\": \"prices\"}},\n        {\"id\": \"macd\", \"tool\": \"technical_macd\", \"inputs\": {\"data\": \"prices\"}}\n    ],\n    \"outputs\": [\"rsi\", \"macd\"]\n}\n```\n\n- Each step has a `tool`, its `arguments`, an optional `id`, and `inputs`, which\n  maps an argument name to the `id` of an earlier step. That step's `results`\n  fill the argument (its whole output when it has no `results`).\n- A step without an `id` is referred to by its position (`\"0\"`, `\"1\"`, ...).\n- `outputs` lists the steps to return; by default only the last step comes back.\n- The request is validated before any step runs. A failing step is reported as\n  `Step '<id>' (<tool>) failed: ...`.\n- With tool discovery, use `search_tools` to find the tools and their\n  parameters; `run_pipeline` can call tools hidden by discovery by name, but\n  not tools outside the categories the server serves.\n\n### Checking Data Coverage\n\nWhen unsure what providers are available for an endpoint, look at the tool's\ninput schema — the `provider` parameter's `enum` lists all installed options.\n\n### Using Charts\n\nPass `chart: true` to get a pre-built visualization:\n\n```json\n{\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"chart\": true}\n```\n\nThe `chart` field in the response holds a `chart` or `table` artifact (see\nThe `chart` Field). In `run_pipeline`, a step that returns an artifact passes\nits `content` rows to later steps.\n\n---\n\n## User Settings and Defaults\n\nThe server reads user settings from `~/.openbb_platform/user_settings.json`:\n\n### API Keys\n\nProvider credentials are stored under `credentials`:\n\n```json\n{\n    \"credentials\": {\n        \"fmp_api_key\": \"YOUR_KEY\",\n        \"polygon_api_key\": \"YOUR_KEY\"\n    }\n}\n```\n\nWithout the required API key, calls to that provider will fail with an\nauthentication error.\n\n### Default Provider\n\nSet a default provider per endpoint so it is preselected:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\"provider\": \"yfinance\"},\n            \"/economy/cpi\": {\"provider\": \"oecd\"}\n        }\n    }\n}\n```\n\nWhen multiple providers are available, this determines the default selection\nif the parameter is omitted.\n\n### Default Parameters\n\nIndividual parameters can be defaulted so they are applied when not explicitly\npassed:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\n                \"provider\": \"fmp\",\n                \"chart\": true,\n                \"chart_params\": {\n                    \"heikin_ashi\": true,\n                    \"indicators\": {\"sma\": {\"length\": [21, 50]}}\n                }\n            }\n        }\n    }\n}\n```\n\n### Output Preferences\n\nThe `output_type` preference controls how the Python Interface returns data.\nFor MCP, the server always returns the full OBBject JSON regardless of this\nsetting, but it is relevant for the Python Interface:\n\n| Output Type | Description |\n|---|---|\n| `OBBject` | Full response object (default) |\n| `dataframe` | Pandas DataFrame |\n| `numpy` | NumPy array |\n| `dict` | Python dictionary |\n| `polars` | Polars DataFrame |\n| `llm` | JSON-encoded string of results only |\n| `chart` | Chart object |\n\n### LLM Mode\n\nSetting `output_type` to `\"llm\"` in the Python Interface strips everything\nexcept `results` and returns it as a JSON string. This is optimized for token\nefficiency in LLM frameworks. In the REST API / MCP context, the full OBBject\nis always returned.\n\n---\n\n## Working With Skills\n\nSkills are MCP resources exposed at `skill://<name>/SKILL.md` URIs. Discover\nand read them via the standard MCP resource methods.\n\n### Discover Available Skills\n\nCall `list_resources()` (no arguments):\n\n```json\n[\n    {\"uri\": \"skill://develop_extension/SKILL.md\", \"name\": \"develop_extension\"},\n    {\"uri\": \"skill://build_workspace_app/SKILL.md\", \"name\": \"build_workspace_app\"},\n    {\"uri\": \"skill://configure_mcp_server/SKILL.md\", \"name\": \"configure_mcp_server\"},\n    {\"uri\": \"skill://work_with_server/SKILL.md\", \"name\": \"work_with_server\"},\n    {\"uri\": \"skill://use_openbb_cli/SKILL.md\", \"name\": \"use_openbb_cli\"}\n]\n```\n\n### Read a Skill\n\nCall `read_resource()` with the skill URI:\n\n```json\n// Input\n{\"uri\": \"skill://develop_extension/SKILL.md\"}\n\n// Output — full Markdown content of the skill guide\n```\n\n### Supporting Files\n\nSkill directories can contain additional supporting files (e.g. templates,\nexamples). Reference the skill manifest at `skill://<name>/_manifest` to\ndiscover any supporting files packaged alongside the main `SKILL.md`.\n\n---\n\n## Quick Reference\n\n### Admin Tools (Discovery)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `available_categories` | *(none)* | List of categories with subcategories and tool counts |\n| `available_tools` | `category`, `subcategory?` | List of tools with names and descriptions |\n| `search_tools` | `query` | Full definitions of the matching tools |\n| `call_tool` | `name`, `arguments?` | The tool's result |\n\n### Pipeline Tool (Always Listed)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `run_pipeline` | `steps`, `outputs?` | Outputs of the selected steps, keyed by step id |\n\n### OBBject Response Structure\n\n| Field | Always Present | Content |\n|---|---|---|\n| `id` | Yes | Request UUID |\n| `results` | Yes | Data payload (list, dict, string, or null) |\n| `provider` | Yes | Provider name or null |\n| `warnings` | Yes | Warning list or null |\n| `chart` | Yes | Chart artifact or null |\n| `extra` | Yes | Metadata dict (may be empty) |\n","frontmatter":{"name":"work_with_server","description":"This guide explains how to call tools, interpret responses, discover capabilities, use prompts, and handle errors when interacting with an OpenBB MCP server."},"isInternal":false,"tokens":6405,"sizeBytes":25620},{"name":"default_apps.json","path":"openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json","title":"Assets Skill","category":"anthropic-skill","format":"json","content":"# Assets Skill\n\nPath: `openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json)","tokens":300,"sizeBytes":285},{"name":"mock_openapi.json","path":"openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json","title":"Utils Skill","category":"anthropic-skill","format":"json","content":"# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json)","tokens":300,"sizeBytes":254},{"name":"mock_widgets.json","path":"openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json","title":"Utils Skill","category":"anthropic-skill","format":"json","content":"# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json)","tokens":300,"sizeBytes":254},{"name":"api-reference.md","path":"openbb_platform/extensions/technical/docs/api-reference.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/api-reference.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/api-reference.md)","tokens":300,"sizeBytes":231},{"name":"getting-started.md","path":"openbb_platform/extensions/technical/docs/getting-started.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/getting-started.md","title":"Docs Skill","category":"anthropic-skill","format":"markdown","content":"# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/getting-started.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/getting-started.md)","tokens":300,"sizeBytes":235},{"name":"01-indicators.md","path":"openbb_platform/extensions/technical/docs/user-guides/01-indicators.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/01-indicators.md","title":"User-guides Skill","category":"anthropic-skill","format":"markdown","content":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/01-indicators.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/01-indicators.md)","tokens":300,"sizeBytes":262},{"name":"02-signals.md","path":"openbb_platform/extensions/technical/docs/user-guides/02-signals.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/02-signals.md","title":"User-guides Skill","category":"anthropic-skill","format":"markdown","content":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/02-signals.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/02-signals.md)","tokens":300,"sizeBytes":256},{"name":"03-multi-symbol.md","path":"openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md","title":"User-guides Skill","category":"anthropic-skill","format":"markdown","content":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md)","tokens":300,"sizeBytes":266},{"name":"04-composition-and-discovery.md","path":"openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md","title":"User-guides Skill","category":"anthropic-skill","format":"markdown","content":"# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md)","tokens":300,"sizeBytes":292},{"name":"README.md","path":"openbb_platform/extensions/commodity/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/commodity/README.md","title":"commodity Documentation","category":"plugin-manifest","format":"markdown","content":"# Commodity Extension for OpenBB Platform\n\nThis extension provides a set of commands for commodity-related data.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-commodity\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","isInternal":false,"tokens":68,"sizeBytes":336},{"name":"README.md","path":"openbb_platform/extensions/crypto/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/crypto/README.md","title":"crypto Documentation","category":"plugin-manifest","format":"markdown","content":"# Crypto data extension for OpenBB Platform\n\nThis extension provides a set of commands for crypto data retrieval.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-crypto\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","isInternal":false,"tokens":68,"sizeBytes":334},{"name":"README.md","path":"openbb_platform/extensions/currency/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/currency/README.md","title":"currency Documentation","category":"plugin-manifest","format":"markdown","content":"# OpenBB Currency Extension\n\nThis extension provides currency exchange related data for the OpenBB Platform.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-currency\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n","isInternal":false,"tokens":66,"sizeBytes":331},{"name":"README.md","path":"openbb_platform/extensions/derivatives/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/derivatives/README.md","title":"derivatives Documentation","category":"plugin-manifest","format":"markdown","content":"# derivatives Documentation\n\nPath: `openbb_platform/extensions/derivatives/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/derivatives/README.md)","tokens":300,"sizeBytes":226},{"name":"README.md","path":"openbb_platform/extensions/devtools/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/devtools/README.md","title":"devtools Documentation","category":"plugin-manifest","format":"markdown","content":"# devtools Documentation\n\nPath: `openbb_platform/extensions/devtools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/devtools/README.md)","tokens":300,"sizeBytes":217},{"name":"README.md","path":"openbb_platform/extensions/econometrics/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/econometrics/README.md","title":"econometrics Documentation","category":"plugin-manifest","format":"markdown","content":"# econometrics Documentation\n\nPath: `openbb_platform/extensions/econometrics/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/econometrics/README.md)","tokens":300,"sizeBytes":229},{"name":"README.md","path":"openbb_platform/extensions/economy/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/economy/README.md","title":"economy Documentation","category":"plugin-manifest","format":"markdown","content":"# economy Documentation\n\nPath: `openbb_platform/extensions/economy/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/economy/README.md)","tokens":300,"sizeBytes":214},{"name":"README.md","path":"openbb_platform/extensions/equity/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/equity/README.md","title":"equity Documentation","category":"plugin-manifest","format":"markdown","content":"# equity Documentation\n\nPath: `openbb_platform/extensions/equity/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/equity/README.md)","tokens":300,"sizeBytes":211},{"name":"README.md","path":"openbb_platform/extensions/etf/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/etf/README.md","title":"etf Documentation","category":"plugin-manifest","format":"markdown","content":"# etf Documentation\n\nPath: `openbb_platform/extensions/etf/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/etf/README.md)","tokens":300,"sizeBytes":202},{"name":"README.md","path":"openbb_platform/extensions/fixedincome/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/fixedincome/README.md","title":"fixedincome Documentation","category":"plugin-manifest","format":"markdown","content":"# fixedincome Documentation\n\nPath: `openbb_platform/extensions/fixedincome/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/fixedincome/README.md)","tokens":300,"sizeBytes":226},{"name":"README.md","path":"openbb_platform/extensions/index/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/index/README.md","title":"index Documentation","category":"plugin-manifest","format":"markdown","content":"# index Documentation\n\nPath: `openbb_platform/extensions/index/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/index/README.md)","tokens":300,"sizeBytes":208},{"name":"README.md","path":"openbb_platform/extensions/mcp_server/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/README.md","title":"mcp_server Documentation","category":"plugin-manifest","format":"markdown","content":"# mcp_server Documentation\n\nPath: `openbb_platform/extensions/mcp_server/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/README.md)","tokens":300,"sizeBytes":223},{"name":"README.md","path":"openbb_platform/extensions/news/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/news/README.md","title":"news Documentation","category":"plugin-manifest","format":"markdown","content":"# news Documentation\n\nPath: `openbb_platform/extensions/news/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/news/README.md)","tokens":300,"sizeBytes":205},{"name":"README.md","path":"openbb_platform/extensions/platform_api/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/README.md","title":"platform_api Documentation","category":"plugin-manifest","format":"markdown","content":"# platform_api Documentation\n\nPath: `openbb_platform/extensions/platform_api/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/README.md)","tokens":300,"sizeBytes":229},{"name":"README.md","path":"openbb_platform/extensions/quantitative/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/quantitative/README.md","title":"quantitative Documentation","category":"plugin-manifest","format":"markdown","content":"# quantitative Documentation\n\nPath: `openbb_platform/extensions/quantitative/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/quantitative/README.md)","tokens":300,"sizeBytes":229},{"name":"README.md","path":"openbb_platform/extensions/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/README.md","title":"extensions Documentation","category":"plugin-manifest","format":"markdown","content":"# Extensions\n\nIn this folder you can find the extensions that were created or are supported by OpenBB.\n","isInternal":false,"tokens":21,"sizeBytes":103},{"name":"README.md","path":"openbb_platform/extensions/technical/docs/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/README.md","title":"docs Documentation","category":"plugin-manifest","format":"markdown","content":"# docs Documentation\n\nPath: `openbb_platform/extensions/technical/docs/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/README.md)","tokens":300,"sizeBytes":225},{"name":"README.md","path":"openbb_platform/extensions/technical/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/README.md","title":"technical Documentation","category":"plugin-manifest","format":"markdown","content":"# technical Documentation\n\nPath: `openbb_platform/extensions/technical/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/README.md)","tokens":300,"sizeBytes":220},{"name":"README.md","path":"openbb_platform/obbject_extensions/charting/README.md","rawUrl":"https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/obbject_extensions/charting/README.md","title":"charting Documentation","category":"plugin-manifest","format":"markdown","content":"# charting Documentation\n\nPath: `openbb_platform/obbject_extensions/charting/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/obbject_extensions/charting/README.md)","tokens":300,"sizeBytes":233}],"systemPromptSnippet":"<yakaai_skills repo=\"OpenBB-finance/OpenBB\">\n<!-- File: assets/extensions/obbject.json (Tokens: ~36 | Category: anthropic-skill) -->\n[\n    {\n        \"packageName\": \"openbb-charting\",\n        \"optional\": false,\n        \"description\": \"Create custom charts from OBBject data.\"\n    }\n]\n\n<!-- File: assets/extensions/provider.json (Tokens: ~2528 | Category: anthropic-skill) -->\n[\n    {\n        \"packageName\": \"openbb-bls\",\n        \"optional\": false,\n        \"reprName\": \"Bureau of Labor Statistics' (BLS) Public Data API\",\n        \"description\": \"The Bureau of Labor Statistics' (BLS) Public Data Application Programming Interface (API) gives the public access to economic data from all BLS programs. It is the Bureau's hope that talented developers and programmers will use the BLS Public Data API to create original, inventive applications with published BLS data.\",\n        \"credentials\": [\n            \"bls_api_key\"\n        ],\n        \"website\": \"https://www.bls.gov/developers/api_signature_v2.htm\",\n        \"instructions\": \"Sign up for a free API key here: https://data.bls.gov/registrationEngine/\"\n    },\n    {\n        \"packageName\": \"openbb-cboe\",\n        \"optional\": false,\n        \"reprName\": \"Chicago Board Options Exchange (CBOE)\",\n        \"description\": \"Cboe is the world's go-to derivatives and exchange network,\\ndelivering cutting-edge trading, clearing and investment solutions to people\\naround the world.\",\n        \"credentials\": [],\n        \"website\": \"https://www.cboe.com\"\n    },\n    {\n        \"packageName\": \"openbb-cftc\",\n        \"optional\": false,\n        \"reprName\": \"Commodity Futures Trading Commission (CFTC)\",\n        \"description\": \"The mission of the Commodity Futures Trading Commission (CFTC) is to promote the integrity,\\n    resilience, and vibrancy of the U.S. derivatives markets through sound regulation.\\n    Commitment of Traders reports are sourced from the CFTC Public Reporting API. Swap transaction\\n    and pricing data is sourced from the DTCC Public Price Dissemination service, the swap data\\n    repository that publishes CFTC-jurisdiction transactions. Overnight index swap curves and FX\\n    forward points are constructed from those executed transactions.\",\n        \"credentials\": [\n            \"cftc_app_token\"\n        ],\n        \"website\": \"https://cftc.gov/\",\n        \"instructions\": \"Credentials are not required, but your IP address may be subject to throttling limits\\n    on the Commitment of Traders endpoints. API requests made using an application token are not throttled.\\n    Create an account here: https://evergreen.data.socrata.com/signup\\n    and then generate the app_token by signing in with the credentials\\n    here: https://publicreporting.cftc.gov/profile/edit/developer_settings.\\n    The DTCC Public Price Dissemination endpoints are public and require no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-deribit\",\n        \"optional\": false,\n        \"reprName\": \"Deribit Public Data\",\n        \"description\": \"Deribit is a crypto derivatives exchange listing futures, perpetuals,\\noptions, and spot pairs. This extension covers its public market data API, which\\nneeds no credentials and carries no trading.\",\n        \"credentials\": [],\n        \"website\": \"https://deribit.com/\",\n        \"instructions\": \"This provider needs no credentials and is not meant for trading.\"\n    },\n    {\n        \"packageName\": \"openbb-ecb\",\n        \"optional\": false,\n        \"reprName\": \"European Central Bank (ECB)\",\n        \"description\": \"The ECB Data Portal provides access to all official ECB statistics\\nthrough the SDMX 2.1 REST API, plus euro reference exchange rates, key ECB interest\\nrates, the euro short-term rate (\\u20acSTR), MFI/bank interest rates, the statistical\\nrelease calendar, ECB releases & publications, and the Eurosystem list of eligible\\ncollateral assets.\",\n        \"credentials\": [],\n        \"website\": \"https://data.ecb.europa.eu\"\n    },\n    {\n        \"packageName\": \"openbb-famafrench\",\n        \"optional\": false,\n        \"reprName\": \"Fama-French Research Portfolios and Factors\",\n        \"description\": \"\\n    This provider implements the Fama-French research portfolios and factors data library,\\n    maintained and hosted by Kenneth R. French at Dartmouth College.\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html\"\n    },\n    {\n        \"packageName\": \"openbb-federal-reserve\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve (FED)\",\n        \"description\": \"Access data provided by the Federal Reserve System, the Central Bank of the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.federalreserve.gov/data.htm\"\n    },\n    {\n        \"packageName\": \"openbb-finra\",\n        \"optional\": false,\n        \"reprName\": \"Financial Industry Regulatory Authority (FINRA)\",\n        \"description\": \"FINRA Data provides centralized access to the abundance of data FINRA\\nmakes available to the public, media, researchers and member firms.\",\n        \"credentials\": [],\n        \"website\": \"https://www.finra.org/finra-data\",\n        \"instructions\": \"This provider needs no credentials.\"\n    },\n    {\n        \"packageName\": \"openbb-fred\",\n        \"optional\": false,\n        \"reprName\": \"Federal Reserve Economic Data | St. Louis FED (FRED)\",\n        \"description\": \"Federal Reserve Economic Data is a database maintained by the\\nResearch division of the Federal Reserve Bank of St. Louis that has more than\\n816,000 economic time series from various sources.\",\n        \"credentials\": [\n            \"fred_api_key\"\n        ],\n        \"deprecatedCredentials\": {\n            \"API_FRED_KEY\": \"fred_api_key\"\n        },\n        \"website\": \"https://fred.stlouisfed.org\",\n        \"instructions\": \"Go to: https://fred.stlouisfed.org\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827137-d143ba4c-72cb-467d-a7f4-5cc27c597aec.png)\\n\\nClick on, \\\"My Account\\\", create a new account or sign in with Google:\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827011-65cdd501-27e3-436f-bd9d-b0d8381d46a7.png)\\n\\nAfter completing the sign-up, go to \\\"My Account\\\", and select \\\"API Keys\\\". Then, click on, \\\"Request API Key\\\".\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207827577-c869f989-4ef4-4949-ab57-6f3931f2ae9d.png)\\n\\nFill in the box for information about the use-case for FRED, and by clicking, \\\"Request API key\\\", at the bottom of the page, the API key will be issued.\\n\\n![FRED](https://user-images.githubusercontent.com/46355364/207828032-0a32d3b8-1378-4db2-9064-aa1eb2111632.png)\"\n    },\n    {\n        \"packageName\": \"openbb-government-us\",\n        \"optional\": false,\n        \"reprName\": \"Congress.gov\",\n        \"description\": \"Legislative data from the U.S. Congress. Bills, bill metadata,\\nsummaries, bill text, amendments, enacted laws, calendars, mandated reports, and\\ncommittees are all sourced from keyless public data (GovInfo bulk data, the\\nGovInfo link service, and the unitedstates dataset). No API key is required for\\nthe 108th Congress onward; older Congresses predate the GovInfo bulk archives\\nand are served from the Congress.gov API, which needs a free API key.\",\n        \"credentials\": [\n            \"congress_gov_api_key\"\n        ],\n        \"website\": \"https://www.govinfo.gov\",\n        \"instructions\": \"Most endpoints use public, keyless data from GovInfo (bulk data\\nand the link service) and the unitedstates dataset, so no credentials are\\nrequired for the 108th Congress (2003) onward.\\n\\nGovInfo does not publish bulk archives for earlier Congresses. Those requests\\nfall back to the Congress.gov API, which reaches back to the 93rd Congress\\n(1973) and requires an API key. Sign up for a free key at\\nhttps://api.congress.gov/sign-up/ and set it as 'congress_gov_api_key'.\"\n    },\n    {\n        \"packageName\": \"openbb-imf\",\n        \"optional\": false,\n        \"reprName\": \"International Monetary Fund (IMF) Data APIs\",\n        \"description\": \"Access International Monetary Fund (IMF) data APIs.\",\n        \"credentials\": [],\n        \"website\": \"https://datahelp.imf.org/knowledgebase/articles/667681-using-json-restful-web-service\"\n    },\n    {\n        \"packageName\": \"openbb-jodi\",\n        \"optional\": false,\n        \"reprName\": \"Joint Organisations Data Initiative (JODI) World Databases\",\n        \"description\": \"The Joint Organisations Data Initiative (JODI) provides free monthly petroleum and natural gas supply, demand, and stock statistics, self-reported by over 100 countries, through the JODI-Oil and JODI-Gas World Databases.\",\n        \"credentials\": [],\n        \"website\": \"https://www.jodidata.org\"\n    },\n    {\n        \"packageName\": \"openbb-nasdaq\",\n        \"optional\": false,\n        \"reprName\": \"Nasdaq\",\n        \"description\": \"Positioned at the nexus of technology and the capital markets, Nasdaq\\nprovides premier platforms and services for global capital markets and beyond with\\nunmatched technology, insights and markets expertise.\",\n        \"credentials\": [],\n        \"website\": \"https://www.nasdaq.com\"\n    },\n    {\n        \"packageName\": \"openbb-oecd\",\n        \"optional\": false,\n        \"reprName\": \"Organization for Economic Co-operation and Development (OECD)\",\n        \"description\": \"Access OECD data via the SDMX REST API.\\nCovers all OECD dataflows including GDP, CPI, unemployment,\\ninterest rates, and hundreds more.\",\n        \"credentials\": [],\n        \"website\": \"https://data-explorer.oecd.org/\"\n    },\n    {\n        \"packageName\": \"openbb-sec\",\n        \"optional\": false,\n        \"reprName\": \"Securities and Exchange Commission (SEC)\",\n        \"description\": \"SEC is the public listings regulatory body for the United States.\",\n        \"credentials\": [],\n        \"website\": \"https://www.sec.gov/data\"\n    },\n    {\n        \"packageName\": \"openbb-tmx\",\n        \"optional\": false,\n        \"reprName\": \"TMX\",\n        \"description\": \"Unofficial TMX Data Provider Extension\\n    TMX Group Companies\\n        - Toronto Stock Exchange\\n        - TSX Venture Exchange\\n        - TSX Trust\\n        - Montr\\u00e9al Exchange\\n        - TSX Alpha Exchange\\n        - Shorcan\\n        - CDCC\\n        - CDS\\n        - TMX Datalinx\\n        - Trayport\\n    \",\n        \"credentials\": [],\n        \"website\": \"https://www.tmx.com\"\n    },\n    {\n        \"packageName\": \"openbb-us-eia\",\n        \"optional\": false,\n        \"reprName\": \"U.S. Energy Information Administration (EIA) Open Data and API\",\n        \"description\": \"The U.S. Energy Information Administration is committed to its free and open data by making it available through an Application Programming Interface (API) and its open data tools. See https://www.eia.gov/opendata/ for more information.\",\n        \"credentials\": [\n            \"eia_api_key\"\n        ],\n        \"website\": \"https://eia.gov/\",\n        \"instructions\": \"Credentials are required for functions calling the EIA's API.\\n    Register for a free key here: https://www.eia.gov/opendata/register.php\"\n    }\n]\n\n<!-- File: assets/extensions/router.json (Tokens: ~396 | Category: anthropic-skill) -->\n[\n    {\n        \"packageName\": \"openbb-commodity\",\n        \"optional\": true,\n        \"description\": \"Commodity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-crypto\",\n        \"optional\": true,\n        \"description\": \"Cryptocurrency market data.\"\n    },\n    {\n        \"packageName\": \"openbb-currency\",\n        \"optional\": true,\n        \"description\": \"Foreign exchange (FX) market data.\"\n    },\n    {\n        \"packageName\": \"openbb-derivatives\",\n        \"optional\": true,\n        \"description\": \"Derivatives market data.\"\n    },\n    {\n        \"packageName\": \"openbb-econometrics\",\n        \"optional\": true,\n        \"description\": \"Econometrics analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-economy\",\n        \"optional\": true,\n        \"description\": \"Economic data.\"\n    },\n    {\n        \"packageName\": \"openbb-equity\",\n        \"optional\": true,\n        \"description\": \"Equity market data.\"\n    },\n    {\n        \"packageName\": \"openbb-etf\",\n        \"optional\": true,\n        \"description\": \"Exchange Traded Funds market data.\"\n    },\n    {\n        \"packageName\": \"openbb-fixedincome\",\n        \"optional\": true,\n        \"description\": \"Fixed Income market data.\"\n    },\n    {\n        \"packageName\": \"openbb-index\",\n        \"optional\": true,\n        \"description\": \"Indices data.\"\n    },\n    {\n        \"packageName\": \"openbb-news\",\n        \"optional\": false,\n        \"description\": \"Financial market news data.\"\n    },\n    {\n        \"packageName\": \"openbb-quantitative\",\n        \"optional\": true,\n        \"description\": \"Quantitative analysis tools.\"\n    },\n    {\n        \"packageName\": \"openbb-technical\",\n        \"optional\": true,\n        \"description\": \"Technical Analysis tools.\"\n    }\n]\n\n<!-- File: openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/build_workspace_app/SKILL.md (Tokens: ~4665 | Category: anthropic-skill) -->\n---\nname: build_workspace_app\ndescription: This guide covers the full lifecycle of building, running, and serving a custom OpenBB Workspace application from an extension project scaffolded by `openbb-cookiecutter`. It assumes the project shell already exists (see the `develop_extension` skill for scaffolding instructions).\n---\n\n# Build and Run OpenBB Workspace Applications\n\nThis guide covers the full lifecycle of building, running, and serving a custom\nOpenBB Workspace application from an extension project scaffolded by\n`openbb-cookiecutter`. It assumes the project shell already exists\n(see the `develop_extension` skill for scaffolding instructions).\n\n---\n\n## Prerequisites\n\nEnsure the following packages are installed in the active Python environment:\n\n```\npip install openbb-core openbb-platform-api openbb-devtools\n```\n\n- `openbb-core` provides the Router, Provider, OBBject, and Fetcher base classes.\n- `openbb-platform-api` provides the `openbb-api` CLI for serving backends\n  and auto-generating `widgets.json` for OpenBB Workspace.\n- `openbb-devtools` provides `pytest`, cassette recording, and QA utilities.\n\nIf you also need the Python Interface wrapper (the `obb` object), install the\nmain package:\n\n```\npip install openbb --no-deps\n```\n\n---\n\n## Architecture Overview\n\nOpenBB is built on FastAPI and Pydantic. The application has two independent\ninterfaces that share core logic and models:\n\n- **Python Interface** — wraps installed routers into an `obb` Python package\n  with auto-generated docstrings and function signatures. Requires a build step\n  (`openbb-build`) to generate static assets.\n- **REST API** — a FastAPI instance with all installed routers available via\n  HTTP. Import it with `from openbb_core.api.rest_api import app` or launch\n  with `uvicorn openbb_core.api.rest_api:app`.\n\nThe application is the product of all installed extensions. With just `openbb-core`\nthere are no routers or endpoints — users compose their own combinations.\n\n### Key Classes\n\n| Class | Import | Purpose |\n|---|---|---|\n| `Router` | `openbb_core.app.router` | Subclass of `fastapi.APIRouter`; defines commands |\n| `OBBject` | `openbb_core.app.model.obbject` | Standard response object with results, provider, warnings, extra |\n| `Provider` | `openbb_core.provider.abstract.provider` | Registers fetchers for the provider interface |\n| `Fetcher` | `openbb_core.provider.abstract.fetcher` | TET pipeline: Transform query → Extract data → Transform data |\n| `QueryParams` | `openbb_core.provider.abstract.query_params` | Base class for input parameters |\n| `Data` | `openbb_core.provider.abstract.data` | Base class for output data schemas |\n\n---\n\n## Router Extensions (API Endpoints)\n\nRouter extensions are the user-facing endpoints that power the REST API, MCP,\nand Python Interface.\n\n### Creating a Router\n\n```python\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\", description=\"My custom extension.\")\n```\n\nThe top-level API path prefix is determined by the entry point name in\n`pyproject.toml`, not the `prefix` argument. Only use `prefix` for sub-routers.\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.routers.my_router:router\"\n```\n\nCommands appear at `/my_app/...` in the API and `obb.my_app.` in Python.\n\n### Provider Interface Endpoints\n\nConnect a router command to one or more provider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters and the `OBBject.from_query(Query(**locals()))` return are\nmandatory and must be used exactly as shown.\n\n### Basic GET Endpoint\n\n```python\n@router.command(methods=[\"GET\"])\nasync def hello() -> OBBject[str]:\n    \"\"\"OpenBB Hello World.\"\"\"\n    return OBBject(results=\"Hello from the extension!\")\n```\n\n### Basic POST Endpoint\n\n```python\nfrom openbb_core.provider.abstract.data import Data\n\n\n@router.command(methods=[\"POST\"])\nasync def process(data: Data, some_param: str) -> OBBject:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results=data.model_dump())\n```\n\n### Decorator Parameters\n\n`@router.command` accepts:\n- `model` — metamodel name linking to Provider fetchers\n- `methods` — list of HTTP methods, typically `[\"GET\"]` or `[\"POST\"]`\n- `examples` — list of `APIEx` or `PythonEx` for docs\n- `deprecated` — deprecation notice\n- `no_validate` — skip response validation, treat output as `Any`\n- `openapi_extra` — dictionary for inline `widget_config` or `mcp_config`\n\nFields of a response model marked\n`Field(json_schema_extra={\"exclude_from_api\": True})` are dropped from REST\nAPI responses and kept in the Python Interface, as `Chart.fig` is.\n\n### Using FastAPI APIRouter Directly\n\nAccess the underlying FastAPI router via `router._api_router`:\n\n```python\n@router._api_router.get(\"/also_empty\")\nasync def also_empty(param: str) -> str:\n    \"\"\"Also empty.\"\"\"\n    return \"Hello world!\"\n```\n\n---\n\n## From FastAPI — Converting Existing Apps\n\nAny existing FastAPI application can become an OpenBB extension without changing\ncode. Define the entry point in `pyproject.toml` pointed at the FastAPI or\nAPIRouter instance:\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_app = \"my_package.app:app\"\n```\n\nInstall the package and build static assets:\n\n```\npip install -e .\nopenbb-build\n```\n\nKnown limitations:\n- Authorization hooks are not injected into the Python Interface\n- Request-bound dependencies or those returning None are not injected\n- WebSockets are not supported in the Python Interface\n- Multi-method routes (GET + POST on same path) may not generate correctly\n\n---\n\n## Serving with openbb-api (Workspace Backends)\n\nThe `openbb-api` CLI converts a FastAPI instance into an OpenBB Workspace\nbackend with auto-generated widget definitions.\n\n### Basic Usage\n\n```\n# Start with default OpenBB extensions\nopenbb-api\n\n# Start with a custom FastAPI file\nopenbb-api --app ./my_app.py --host 0.0.0.0 --port 8005\n\n# Factory function pattern\nopenbb-api --app my_app.py:create_app --factory\n\n# Custom FastAPI instance name\nopenbb-api --app my_app.py --name my_app\n```\n\nDefaults are `--host 127.0.0.1 --port 6900`, falling back to the next\navailable port if already in use.\n\n### Key Arguments\n\n| Argument | Description |\n|---|---|\n| `--app` | Path to Python file with a FastAPI instance |\n| `--name` | Name of the FastAPI instance (default: `app`) |\n| `--factory` | Flag if the app name is a factory function |\n| `--editable` | Make `widgets.json` editable at runtime |\n| `--no-build` | Load existing `widgets.json` without checking for updates |\n| `--exclude` | JSON list of API paths to exclude from widgets |\n| `--widgets-json` | Custom path for `widgets.json` |\n| `--apps-json` | Custom path for `workspace_apps.json` |\n\nAll remaining arguments are passed to `uvicorn.run`.\n\n---\n\n## Inline Widget Definitions\n\nWidget properties can be defined inline in your code via `openapi_extra`:\n\n```python\n@app.get(\n    \"/some_endpoint\",\n    openapi_extra={\n        \"widget_config\": {\n            \"name\": \"Custom Widget Name\",\n            \"description\": \"Override docstring description\",\n        }\n    },\n)\nasync def some_endpoint():\n    \"\"\"Description from docstring.\"\"\"\n    pass\n```\n\n### Exclude an Endpoint from Widgets\n\n```python\n@app.get(\n    \"/internal_endpoint\",\n    openapi_extra={\"widget_config\": {\"exclude\": True}},\n)\nasync def internal_endpoint():\n    return [{\"label\": \"Choice 1\", \"value\": \"choice1\"}]\n```\n\n### Dropdown Parameters\n\nDropdowns are auto-generated from `Literal` types:\n\n```python\nfrom typing import Literal\n\n\n@app.get(\"/with_dropdown\")\nasync def with_dropdown(\n    choices: Literal[\"Choice 1\", \"Choice 2\", \"Choice 3\"] = \"Choice 3\",\n):\n    pass\n```\n\n### Column Definitions for Tables\n\nUse Pydantic response models to auto-generate table column definitions:\n\n```python\nimport datetime\nfrom pydantic import BaseModel, Field\n\n\nclass MyData(BaseModel):\n    date: datetime.date = Field(description=\"The date.\")\n    value: float = Field(description=\"The value.\")\n\n\n@app.get(\"/my_data\")\nasync def my_data() -> list[MyData]:\n    \"\"\"Widget with typed columns.\"\"\"\n    return [MyData(date=datetime.date.today(), value=42.0)]\n```\n\n### Widget Types by Return Type\n\n| Return Type | Widget Type |\n|---|---|\n| `list[dict]` or `list[BaseModel]` | Table (AgGrid) |\n| `str` | Markdown |\n| `dict` (with `widget_config.type=\"chart\"`) | Plotly Chart |\n| `MetricResponseModel` | Metric |\n| `PdfResponseModel` | PDF |\n\n### Response Models for Special Widgets\n\n```python\nfrom openbb_platform_api.response_models import MetricResponseModel, PdfResponseModel\n\n\n@app.get(\"/metric\", response_model=MetricResponseModel)\nasync def metric():\n    \"\"\"A metric widget.\"\"\"\n    return dict(label=\"Revenue\", value=12345, delta=5.67)\n\n\n@app.get(\"/pdf\", response_model=PdfResponseModel)\nasync def open_pdf(file_path: str):\n    \"\"\"Open a PDF document.\"\"\"\n    with open(file_path, \"rb\") as f:\n        return dict(content=f.read())\n```\n\n### Plotly Chart Widget\n\n```python\n@app.get(\n    \"/chart\",\n    openapi_extra={\"widget_config\": {\"type\": \"chart\"}},\n)\nasync def chart() -> dict:\n    \"\"\"A chart widget.\"\"\"\n    from plotly.graph_objs import Bar, Layout, Figure\n\n    fig = Figure(\n        data=[Bar(x=[\"A\", \"B\", \"C\"], y=[1, 2, 3])],\n        layout=Layout(title=\"My Chart\", template=\"plotly_dark\"),\n    )\n    return fig.to_plotly_json()\n```\n\n### JSON Schema Extra for Parameters\n\nAnnotate parameters with additional widget configuration:\n\n```python\nfrom typing import Annotated\nfrom fastapi import Query\n\nmy_param: Annotated[\n    str,\n    Query(\n        title=\"My Title\",\n        description=\"Detailed hover text\",\n        json_schema_extra={\n            \"x-widget_config\": {\"optionsEndpoint\": \"/my_choices_endpoint\"}\n        },\n    ),\n]\n```\n\n### Form Input Widget\n\nCreate an input form tied to a table:\n- GET endpoint defines `widget_config.form_endpoint` pointing to the POST route\n- POST route accepts a single Pydantic model argument\n\n---\n\n## OBBject Extensions (Result Post-Processing)\n\nExtend the `OBBject` response with custom methods, accessible in the Python Interface.\n\n### Class Accessor (Namespaced Methods)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self):\n        \"\"\"Return a summary.\"\"\"\n        return self._obbject.to_dataframe().describe()\n```\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_tools = \"my_package.obbject.my_ext:ext\"\n```\n\n### Callable Accessor (Property-Like)\n\n```python\next = Extension(name=\"to_csv\", description=\"Convert results to CSV.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject):\n    \"\"\"Convert to CSV string.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### OBBject Output Conversion Methods\n\nEvery `OBBject` has built-in conversion methods:\n- `to_df()` / `to_dataframe()` — Pandas DataFrame\n- `to_dict(orientation=...)` — Python dict\n- `to_numpy()` — NumPy array\n- `to_polars()` — Polars DataFrame (requires `polars` installed)\n- `model_dump()` — Complete object as dict\n- `model_dump_json()` — Serialized JSON string\n\n---\n\n## OBBject Plugins (Pre-Return Interceptors)\n\nPlugins execute before the response is returned, compatible with both REST API\nand Python Interface. They can conditionally alter the output of any command.\n\n**WARNING**: Plugins are considered potentially dangerous. The environment must\nbe explicitly configured to allow them.\n\nIn `system_settings.json`:\n\n```json\n{\n    \"allow_on_command_output\": true,\n    \"allow_mutable_extensions\": true\n}\n```\n\n### Plugin Configuration\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\nplugin = Extension(\n    name=\"my_plugin\",\n    description=\"Intercept output before return.\",\n    on_command_output=True,\n    command_output_paths=[\"/my_router/my_command\"],\n    immutable=False,\n    results_only=False,\n)\n```\n\nKey parameters:\n- `on_command_output=True` — required for plugins\n- `command_output_paths` — list of endpoint paths to intercept (None = all)\n- `immutable=False` — set to allow modifying the response object\n- `results_only=True` — receive only the `results` portion instead of full OBBject\n\n### Plugin Code\n\n```python\n@plugin.obbject_accessor\ndef my_plugin_func(obbject):\n    \"\"\"Modify or inspect the response before it returns.\"\"\"\n    # Modify obbject directly; do NOT return anything\n    pass\n```\n\n---\n\n## Charting Extensions (Views)\n\nAdd custom chart views to any router endpoint, activated when the user sets\n`chart=True`.\n\n### Structure\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Chart for my_command.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build chart with fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\nRegister in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_views:MyViews\"\n```\n\n### Kwargs Available in Views\n\n| Key | Content |\n|---|---|\n| `obbject_item` | Validated results object |\n| `charting_settings` | User charting preferences |\n| `standard_params` | Standard model parameters |\n| `extra_params` | Provider-specific parameters |\n| `provider` | Provider name used |\n| `extra` | Execution metadata |\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## HTTP Requests in Fetchers\n\nUse the built-in utilities instead of creating new clients from scratch.\n\n### Query String Helper\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"exclude_this_param\"])\n```\n\n### Synchronous Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\nresponse = make_request(url, headers=headers, params=params)\n```\n\n### Requests Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\n```\n\n### Asynchronous Requests (AIOHTTP)\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresponse_json = await amake_request(url)\n```\n\n### Multi-URL Async Requests\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nresults = await amake_requests([url1, url2, url3])\n```\n\n### Custom Response Callback\n\n```python\nfrom io import StringIO\nfrom pandas import read_csv\n\nresults = []\n\n\nasync def response_callback(response, _):\n    text = await response.text()\n    data = read_csv(StringIO(text), skiprows=2)\n    results.extend(data.to_dict(\"records\"))\n\n\nawait amake_requests([url1, url2], response_callback=response_callback)\n```\n\n### Async Session\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with await session.get(url) as response:\n        data = await response.json()\n```\n\n### Async Fetchers\n\nUse `aextract_data` instead of `extract_data` for async fetchers:\n\n```python\n@staticmethod\nasync def aextract_data(\n    query: MyQueryParams,\n    credentials: dict[str, str] | None,\n    **kwargs: Any,\n) -> list[dict]:\n    \"\"\"Async data extraction.\"\"\"\n    ...\n```\n\n---\n\n## Testing\n\n### Built-In Fetcher Test\n\nEvery Fetcher has a `.test()` method for quick validation:\n\n```python\nfrom my_package.providers.my_provider.models.my_model import MyFetcher\n\nfetcher = MyFetcher()\nfetcher.test({\"symbol\": \"AAPL\"}, {})  # Returns None on success\n```\n\n### Unit Tests with Cassettes\n\nInstall dev tools and use `pytest_recorder` for HTTP cassette recording:\n\n```\npip install openbb-devtools\npytest test_my_fetcher.py --record http\n```\n\nSubsequent test runs replay the recorded HTTP interactions.\n\n### Running Tests\n\n```\n# Unit tests only\npytest tests/ -m \"not integration\"\n\n# Integration tests only\npytest tests/ -m integration\n\n# All tests\npytest tests/\n```\n\n### Integration Testing\n\nFor API integration tests, start a local server first:\n\n```\nuvicorn openbb_core.api.rest_api:app --host 0.0.0.0 --port 8000 --reload\n```\n\n---\n\n## Install and Build\n\n### Development Install\n\nFrom the project root:\n\n```\nuv pip install -e . --group dev\n```\n\n### Build Python Interface\n\nAfter installing or removing extensions, regenerate static assets:\n\n```\nopenbb-build\n```\n\n### Verify in Python\n\n```python\nfrom openbb import obb\n# Your commands appear under obb.<router_name>.<command_name>()\n```\n\n### Serve as Workspace Backend\n\n```\nopenbb-api --app ./my_app.py --editable --host 0.0.0.0 --port 6900\n```\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me a Workspace application that does X\":\n\n1. **Scaffold** the project with `openbb-cookiecutter` (see `develop_extension` skill).\n2. **Remove** example files and clean up boilerplate.\n3. **Implement fetchers** for each data source in `providers/<name>/models/`.\n4. **Register fetchers** in `providers/<name>/__init__.py` via `fetcher_dict`.\n5. **Implement router commands** in `routers/<name>.py`.\n6. **Add widget config** via `openapi_extra` for Workspace-specific behavior.\n7. **Update `pyproject.toml`** entry points and dependencies.\n8. **Install** with `uv pip install -e . --group dev`.\n9. **Build** static assets with `openbb-build`.\n10. **Serve** with `openbb-api` and verify widgets in OpenBB Workspace.\n11. **Test** with `pytest` and fetcher `.test()` methods.\n\n<!-- File: openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/configure_mcp_server/SKILL.md (Tokens: ~7121 | Category: anthropic-skill) -->\n---\nname: configure_mcp_server\ndescription: This guide covers installation, configuration, authentication, tool discovery, prompt management, and client integration for `openbb-mcp-server`.\n---\n\n# Configure and Build the OpenBB MCP Server\n\nThis guide covers installation, configuration, authentication, tool discovery,\nprompt management, and client integration for `openbb-mcp-server`.\n\n---\n\n## Installation\n\n```\npip install openbb-mcp-server\n```\n\nThis installs the `openbb-mcp` CLI command. To include the default OpenBB\nextensions as tools, also install them:\n\n```\npip install openbb\n```\n\nOr install individual extensions:\n\n```\npip install openbb-equity openbb-economy\n```\n\nThe `cli` extra adds `openbb-cli` and its four dispatcher tools\n(`openbb_dispatch`, `openbb_batch_dispatch`, `openbb_list_commands`,\n`openbb_describe_command`); `enable_cli_tools=false` turns them off:\n\n```\npip install \"openbb-mcp-server[cli]\"\n```\n\n---\n\n## Starting the Server\n\n### Default (all installed OpenBB extensions)\n\n```\nopenbb-mcp\n```\n\nDefaults: `--host 127.0.0.1 --port 8001 --transport streamable-http`\n\n### With a Custom FastAPI Application\n\n```\n# File path with default instance name \"app\"\nopenbb-mcp --app ./my_app.py\n\n# Explicit instance name\nopenbb-mcp --app ./my_app.py --name my_app\n\n# Module import syntax\nopenbb-mcp --app my_package.app:my_app\n\n# Factory function pattern\nopenbb-mcp --app ./my_app.py:create_app --factory\n```\n\n### Transport Options\n\n| Transport | Flag | Use Case |\n|---|---|---|\n| `streamable-http` | `--transport streamable-http` | Default. HTTP-based, works with Cursor, VS Code |\n| `sse` | `--transport sse` | Legacy Server-Sent Events. Required by Cline |\n| `stdio` | `--transport stdio` | Standard I/O. Used by Claude Desktop |\n\n---\n\n## CLI Arguments\n\n| Argument | Description | Default |\n|---|---|---|\n| `--app <path>` | Path to FastAPI application file or `module:instance` (mutually exclusive with `--spec`) | OpenBB default app |\n| `--name <name>` | Name of the FastAPI instance or factory function | `app` |\n| `--factory` | Treat `--name` as a factory function | `false` |\n| `--spec <path>` | Path to an `openbb-cli` generated `.spec` file. Synthesizes a FastAPI proxy app whose routes forward to the spec's `base_url`; FastMCP exposes those routes as MCP tools. Mutually exclusive with `--app`. | None |\n| `--config-file <path>` | Path to an `openbb.toml` config file. Highest-priority TOML layer in the cascade. Also honored via `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` / `OPENBB_CONFIG`. | None |\n| `--host <host>` | Server host | `127.0.0.1` |\n| `--port <port>` | Server port | `8001` |\n| `--transport <type>` | `streamable-http`, `sse`, or `stdio` | `streamable-http` |\n| `--default-categories <csv>` | Categories served when tool discovery is off | `all` |\n| `--allowed-categories <csv>` | The only categories served, with or without discovery | All categories |\n| `--tool-discovery` | Hide the tools behind `available_categories`, `available_tools`, `search_tools`, and `call_tool` | Discovery disabled |\n| `--system-prompt <path>` | Path to a `.txt` system prompt file | None |\n| `--server-prompts <path>` | Path to a `.json` server prompts file | None |\n\nEvery other `--key value` is routed by name: an `MCPSettings` field sets that\nsetting (`--enable-cli-tools false`, `--server-auth '[\"user\", \"pass\"]'`),\n`--httpx-<option>` sets an option of the httpx client that calls the API\n(`--httpx-verify false`), and anything else is passed to uvicorn\n(`--log-level debug`).\n\n---\n\n## Spec-Driven Proxy Mode\n\nInstead of importing a FastAPI app in-process, the launcher can synthesize one\nfrom an `openbb-cli` generated `.spec` file. Each command in the spec becomes\na FastAPI route that proxies to the spec's recorded `base_url`. FastMCP walks\nthose routes and exposes them as MCP tools — same tool surface as a normal\n`--app` install, but the heavy `import openbb` lives on the upstream server.\n\n### Generating a spec\n\nThe spec is produced by `openbb-cli`:\n\n```bash\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n```\n\nThis fetches `/openapi.json` from the server and writes a precomputed `.spec`\nfile with every command's URL path, method, parameter schema, and a\n`content_sha256` integrity hash.\n\n### Single-spec launch\n\n```bash\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML (`--config-file openbb.toml`):\n\n```toml\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\n# Optional: override the spec's recorded base_url at deploy time.\nbase_url = \"https://upstream.example.com\"\n# Optional: pinned SHA-256. When set, the launcher recomputes the spec's\n# canonical-JSON hash and refuses to start unless it matches — protects\n# against silent file drift when the spec is fetched at deploy time.\ncontent_sha256 = \"abc123...\"\n\n[mcp.spec.headers]\n# Static headers injected on every proxied upstream request.\n# ``$VAR`` / ``${VAR}`` are substituted from the current environment;\n# entries with unresolved variables are skipped with a warning.\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\nX-API-Key     = \"$OPENBB_UPSTREAM_KEY\"\n```\n\n`--spec` and `--app` are mutually exclusive.\n\n### Multi-spec mounting\n\nEach `[mcp.spec.NAME]` subtable mounts an independent spec under its own\nprefix. FastMCP turns the union of all mounted routes into MCP tools; per-spec\nhooks fire on every tool dispatch into that spec's mount.\n\n```toml\n[mcp.spec.equity]\npath = \"/etc/openbb/equity.spec\"\nmount = \"/equity\"                          # optional; defaults to \"/<name>\"\ncontent_sha256 = \"abc...\"\n[mcp.spec.equity.headers]\nAuthorization = \"Bearer $EQUITY_TOKEN\"\n[mcp.spec.equity.auth]\nhooks = [\"my_pkg.auth:equity_token_check\"]\n[mcp.spec.equity.middleware]\nhooks = [\"my_pkg.middleware:equity_rate_limit\"]\n\n[mcp.spec.crypto]\npath = \"/etc/openbb/crypto.spec\"\n[mcp.spec.crypto.headers]\nX-API-Key = \"$CRYPTO_KEY\"\n```\n\n---\n\n## openbb.toml Cascade\n\nThe launcher reads the same layered TOML cascade openbb-core ships with,\nlowest priority first:\n\n```\npyproject.toml [tool.openbb] (so [tool.openbb.mcp]) → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD) → --config-file PATH (explicit)\n```\n\nEvery layer is optional, and `.openbb.toml` works in place of `openbb.toml`.\n`--config-file` (or `OPENBB_MCP_CONFIG` / `OPENBB_API_CONFIG` /\n`OPENBB_CONFIG`) is the highest-priority TOML layer. `[mcp]` values are\napplied like CLI flags: a CLI flag beats them, and they beat `OPENBB_MCP_*`\nenvironment variables. `[env]` entries never replace a variable already set\nin the shell or a `.env` file.\n\n### Top-level tables\n\n| Table | Purpose |\n|---|---|\n| `[mcp]` | Default values for any of the `--*` CLI flags (CLI wins). |\n| `[mcp.spec]` | Single-spec proxy mode (path, base_url, content_sha256, headers). |\n| `[mcp.spec.NAME]` | Multi-spec mount (path, base_url, content_sha256, headers, auth, middleware). |\n| `[mcp.auth]` | Top-level auth hook list. |\n| `[mcp.middleware]` | Top-level middleware hook list. |\n| `[env]` | Env vars pushed into `os.environ` before any heavy import runs. Existing shell env vars are NEVER clobbered. Values support `$VAR` / `${VAR}` substitution. |\n\n### Auth and middleware hooks\n\nBoth `[mcp.auth]` and `[mcp.middleware]` accept a `hooks` list of\n`module:attr` entry points. Each hook is a 2-arg async function\n`async def hook(request, call_next)` and is wrapped as a\n`Middleware(BaseHTTPMiddleware, dispatch=fn)` on the MCP server's ASGI stack.\n\n```toml\n[mcp.auth]\nhooks = [\"my_pkg.auth:bearer_token_check\"]\n\n[mcp.middleware]\nhooks = [\"my_pkg.middleware:rate_limit\", \"my_pkg.middleware:request_logger\"]\n```\n\nAuth hooks register before middleware hooks; within each list, registration\norder matches TOML order. Per-spec hooks (under `[mcp.spec.NAME.auth]` /\n`[mcp.spec.NAME.middleware]`) are scoped to that spec's mounted sub-app.\nThe top-level hooks wrap the HTTP transports (`streamable-http`, `sse`);\nthe `stdio` transport has no HTTP stack, so they do not run there.\n\n### Env injection\n\n```toml\n[env]\nOPENBB_MCP_HOST = \"0.0.0.0\"\n# Values support shell-style $VAR / ${VAR} substitution.\nOPENBB_GITHUB_TOKEN = \"$GITHUB_TOKEN\"\n```\n\nUseful in containers to inject `OPENBB_*` keys without shell exports. Any key\nalready present in the real environment is preserved.\n\n---\n\n## Configuration Precedence\n\nSettings are resolved in this order (highest priority first):\n\n1. **CLI arguments** — command-line flags\n2. **`[mcp]` table of the `openbb.toml` cascade** — applied like CLI flags; `--config-file PATH` > project `openbb.toml` > user-global `~/.openbb_platform/openbb.toml` > `[tool.openbb.mcp]` in `pyproject.toml`\n3. **Environment variables** — `OPENBB_MCP_` prefixed, including those set by `[env]`\n4. **JSON settings file** — `~/.openbb_platform/mcp_settings.json`, written with the defaults on first run\n5. **Defaults** — built-in MCPSettings defaults\n\n### Config File Example\n\nCreate `~/.openbb_platform/mcp_settings.json`:\n\n```json\n{\n    \"name\": \"My MCP Server\",\n    \"default_tool_categories\": [\"equity\", \"economy\"],\n    \"enable_tool_discovery\": false,\n    \"describe_responses\": false,\n    \"system_prompt_file\": \"/path/to/system_prompt.txt\",\n    \"server_prompts_file\": \"/path/to/prompts.json\"\n}\n```\n\n### Environment Variables\n\nAll settings map to `OPENBB_MCP_` prefixed environment variables:\n\n```\nOPENBB_MCP_NAME=\"My MCP Server\"\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ENABLE_TOOL_DISCOVERY=true\nOPENBB_MCP_SYSTEM_PROMPT_FILE=\"/path/to/prompt.txt\"\nOPENBB_MCP_SERVER_PROMPTS_FILE=\"/path/to/prompts.json\"\n```\n\n---\n\n## Settings Reference\n\n### Server Identity\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `name` | `OPENBB_MCP_NAME` | `str` | `\"OpenBB MCP\"` |\n| `description` | `OPENBB_MCP_DESCRIPTION` | `str` | Auto-generated |\n| `version` | `OPENBB_MCP_VERSION` | `str \\| None` | `None` |\n| `instructions` | `OPENBB_MCP_INSTRUCTIONS` | `str \\| None` | `None` (the system prompt, when one is loaded) |\n| `mask_error_details` | `OPENBB_MCP_MASK_ERROR_DETAILS` | `bool \\| None` | `None` |\n\n### Tool Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `default_tool_categories` | `OPENBB_MCP_DEFAULT_TOOL_CATEGORIES` | `list[str]` | `[\"all\"]` |\n| `allowed_tool_categories` | `OPENBB_MCP_ALLOWED_TOOL_CATEGORIES` | `list[str] \\| None` | `None` |\n| `enable_tool_discovery` | `OPENBB_MCP_ENABLE_TOOL_DISCOVERY` | `bool` | `false` |\n| `enable_cli_tools` | `OPENBB_MCP_ENABLE_CLI_TOOLS` | `bool` | `true` |\n| `list_page_size` | `OPENBB_MCP_LIST_PAGE_SIZE` | `int \\| None` | `None` |\n| `describe_responses` | `OPENBB_MCP_DESCRIBE_RESPONSES` | `bool` | `false` |\n| `api_prefix` | `OPENBB_MCP_API_PREFIX` | `str \\| None` | `None` |\n\n`enable_cli_tools` registers the `openbb-cli` dispatcher tools when\n`openbb-cli` is installed. `mask_error_details` hides exception details from\nclients.\n\n### Prompt Configuration\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `system_prompt_file` | `OPENBB_MCP_SYSTEM_PROMPT_FILE` | `str \\| None` | `None` |\n| `server_prompts_file` | `OPENBB_MCP_SERVER_PROMPTS_FILE` | `str \\| None` | `None` |\n| `default_skills_dir` | `OPENBB_MCP_DEFAULT_SKILLS_DIR` | `str \\| None` | Built-in skills dir |\n| `skills_reload` | `OPENBB_MCP_SKILLS_RELOAD` | `bool` | `false` |\n| `skills_providers` | `OPENBB_MCP_SKILLS_PROVIDERS` | `list[str] \\| None` | `None` |\n\n### HTTP Transport\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `uvicorn_config` | `OPENBB_MCP_UVICORN_CONFIG` | `dict` | `{\"host\": \"127.0.0.1\", \"port\": \"8001\"}` |\n| `httpx_client_kwargs` | `OPENBB_MCP_HTTPX_CLIENT_KWARGS` | `dict` | `{}` |\n\n`httpx_client_kwargs` configures the httpx client that sends tool calls to the\nAPI (headers, timeout, TLS verification).\n\n### Duplicate Handling\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `on_duplicate` | `OPENBB_MCP_ON_DUPLICATE` | `str \\| None` | `None` |\n\nOptions: `\"warn\"`, `\"error\"`, `\"replace\"`, `\"ignore\"`\n\n### Module Exclusion\n\n| Setting | Env Var | Type | Default |\n|---|---|---|---|\n| `module_exclusion_map` | `OPENBB_MCP_MODULE_EXCLUSION_MAP` | `dict \\| None` | `None` |\n\nMaps route path segments to Python modules: routes under a segment are hidden\nwhile its module is imported. `None` uses `{\"coverage\": \"openbb_core\"}`, which\nhides the platform's `coverage` routes; `{}` hides nothing. Data-processing\nextensions (`technical`, `quantitative`, `econometrics`) are exposed when\ninstalled. Pair them with `run_pipeline`, or leave them out with\n`allowed_tool_categories` (or, with tool discovery off,\n`default_tool_categories`).\n\n---\n\n## Authentication\n\nThree authentication modes are available.\n\n### Server-Side Authentication\n\nProtect incoming MCP requests with a Bearer token:\n\n```json\n{\n    \"server_auth\": [\"username\", \"password\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"username\", \"password\"]'\n```\n\nClients must include `Authorization: Bearer <base64(username:password)>` in\ntheir requests. The token is base64-encoded `username:password`.\n\n### Client-Side Authentication\n\nAuthenticate outbound requests to downstream services:\n\n```json\n{\n    \"client_auth\": [\"api_user\", \"api_key\"]\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_CLIENT_AUTH='[\"api_user\", \"api_key\"]'\n```\n\nThis passes `auth=(user, pass)` to the httpx client used for internal requests.\n\n### Programmatic Authentication\n\nWhen using the server as a library, pass a custom `AuthProvider` to\n`create_mcp_server()`:\n\n```python\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\nsettings = MCPSettings()\nmcp = create_mcp_server(settings, my_fastapi_app, auth=my_auth_provider)\n```\n\n---\n\n## Tool Discovery\n\nWhen `enable_tool_discovery` is `true`, the OpenBB tools are left out of the\ntool list and these tools are available to the agent instead:\n\n| Tool | Description |\n|---|---|\n| `available_categories` | Lists all tool categories with tool counts |\n| `available_tools` | Lists the tools in a category with one-sentence descriptions |\n| `search_tools` | Finds tools by a natural-language query and returns their full definitions |\n| `call_tool` | Runs any tool by name with its arguments |\n\nDiscovery holds no per-session state: every client sees the same tool list, and\na hidden tool can also be called directly by name. It behaves the same over every\ntransport and protocol version, so the server is safe for multi-user deployments.\n\n### Choosing the Served Categories\n\nUse `allowed_tool_categories` to serve only some categories, with or without\ndiscovery:\n\n```\nopenbb-mcp --allowed-categories equity,economy,crypto\n```\n\nRoutes in other categories are never registered: they can't be listed,\nsearched, or called, by name, through `call_tool`, or through `run_pipeline`.\n`None` or `all` serves every category.\n\nWithout discovery, `default_tool_categories` also limits the served tools:\n\n```\nopenbb-mcp --default-categories equity,economy\n```\n\nTools outside these categories are registered but disabled, so they are not\nlisted and calling them fails with \"Unknown tool\". A route's\n`mcp_config.enable` overrides this per tool. Nothing can re-enable a disabled\ntool at runtime. With discovery on, `default_tool_categories` is ignored and\nevery allowed tool is reachable through `search_tools` and `call_tool`.\n\n### Enabling Discovery\n\n```\nopenbb-mcp --tool-discovery\n```\n\nWithout discovery, all tools in `default_tool_categories` are listed and the\ndiscovery tools are not registered.\n\n---\n\n## Tool Naming Convention\n\nTools are named from their API route path after stripping the API prefix:\n\n| Route Path | Tool Name |\n|---|---|\n| `/equity/price/historical` | `equity_price_historical` |\n| `/economy/cpi` | `economy_cpi` |\n| `/my_app/process` | `my_app_process` |\n\nThe first path segment is the **category** and the second, on paths with three\nor more segments, the **subcategory**; otherwise the subcategory is\n`\"general\"`. `{placeholder}` segments are skipped, and a single-segment path\nrepeats its segment (`/hello` is `hello_hello`). When one path serves several\nmethods, the non-GET tools get a `_<method>` suffix (`demo_items` and\n`demo_items_post`). A route's `mcp_config.name` replaces the generated name.\n\n---\n\n## Prompt System\n\nThe server supports three layers of prompts, all accessible via the\n`list_prompts` and `get_prompt` tools. Bundled skills are served alongside them\nas resources.\n\n### 1. System Prompt (tag: `system`)\n\nA plain text file loaded once at startup. Also exposed as\n`resource://system_prompt`.\n\n```\nopenbb-mcp --system-prompt /path/to/system_prompt.txt\n```\n\n### 2. Server Prompts JSON (tag: `server`)\n\nA JSON file defining reusable prompts with optional arguments:\n\n```json\n[\n    {\n        \"name\": \"analyze_stock\",\n        \"description\": \"Framework for analyzing a stock.\",\n        \"content\": \"Analyze {symbol} focusing on {aspect}.\",\n        \"arguments\": [\n            {\n                \"name\": \"symbol\",\n                \"type\": \"str\",\n                \"description\": \"Ticker symbol\"\n            },\n            {\n                \"name\": \"aspect\",\n                \"type\": \"str\",\n                \"default\": \"fundamentals\",\n                \"description\": \"Analysis focus area\"\n            }\n        ],\n        \"tags\": [\"analysis\"]\n    }\n]\n```\n\nArgument types: `str`, `int`, `float`, `bool`, `list`, `dict`, `any`\n\nArguments with a `default` value are optional; those without are required.\nClients pass prompt arguments as strings (`{\"aspect\": \"valuation\"}`,\n`{\"years\": \"10\"}`).\n\n```\nopenbb-mcp --server-prompts /path/to/prompts.json\n```\n\n### 3. Inline Prompts (tag: route-specific)\n\nDefine prompts directly on FastAPI routes via `openapi_extra`:\n\n```python\n@router.command(\n    methods=[\"GET\"],\n    openapi_extra={\n        \"mcp_config\": {\n            \"prompts\": [\n                {\n                    \"name\": \"usage_guide\",\n                    \"description\": \"How to use this endpoint.\",\n                    \"content\": \"To analyze {symbol}, call this endpoint with...\",\n                }\n            ]\n        }\n    },\n)\nasync def my_endpoint(symbol: str) -> OBBject: ...\n```\n\nEach `{placeholder}` in `content` becomes an argument: a prompt argument of the\nsame name, else the endpoint parameter, else a required string. Arguments\nwithout a default are required. The rendered prompt starts with\n`Use the tool, <tool name>, to perform the following task.`, naming the\nroute's tool (including a name set with `mcp_config.name`), and the tool's\ndescription lists its prompts.\n\n### 4. Bundled Skills (Resources)\n\nSkill guides are exposed as MCP resources discoverable via `list_resources()`.\nEach skill is accessible at a `skill://<name>/SKILL.md` URI.\n\n```\n# Discover available skills\nlist_resources()  # returns skill://develop_extension/SKILL.md, etc.\n\n# Read a specific skill\nread_resource(\"skill://configure_mcp_server/SKILL.md\")\n```\n\nCustom skills directory:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=/path/to/my/skills\n```\n\nSet to empty string to disable bundled skills:\n\n```\nOPENBB_MCP_DEFAULT_SKILLS_DIR=\"\"\n```\n\n### Skills Reload\n\nEnable hot-reload of skill files without restarting the server (useful during development):\n\n```json\n{\n    \"skills_reload\": true\n}\n```\n\nOr via environment variable:\n\n```\nOPENBB_MCP_SKILLS_RELOAD=true\n```\n\n### Vendor Skills Providers\n\nLoad skill directories from well-known vendor locations (e.g. `~/.claude/skills/`).\nSets the `skills_providers` list in `mcp_settings.json`:\n\n```json\n{\n    \"skills_providers\": [\"claude\", \"cursor\"]\n}\n```\n\nOr via environment variable (comma-separated):\n\n```\nOPENBB_MCP_SKILLS_PROVIDERS=\"claude,cursor\"\n```\n\nSupported provider names:\n\n| Name | Default Directory |\n|---|---|\n| `claude` | `~/.claude/skills/` |\n| `cursor` | `~/.cursor/skills/` |\n| `vscode` / `copilot` | `~/.copilot/skills/` |\n| `codex` | `/etc/codex/skills/` + `~/.codex/skills/` |\n| `gemini` | `~/.gemini/skills/` |\n| `goose` | `~/.config/agents/skills/` |\n| `opencode` | `~/.config/opencode/skills/` |\n\n---\n\n## Inline MCP Configuration (MCPConfigModel)\n\nControl how individual routes appear in the MCP server via `openapi_extra`:\n\n```python\n@app.get(\n    \"/my_endpoint\",\n    openapi_extra={\n        \"mcp_config\": {\n            \"expose\": True,\n            \"mcp_type\": \"tool\",\n            \"methods\": [\"GET\"],\n            \"exclude_args\": [\"internal_param\"],\n            \"prompts\": []\n        }\n    },\n)\n```\n\nThe configuration is read from `openapi_extra[\"mcp_config\"]`, or from\n`openapi_extra[\"x-mcp\"]` when `mcp_config` is absent. An invalid configuration\nis logged and ignored for that route.\n\n### MCPConfigModel Fields\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `expose` | `bool \\| None` | `None` | Set `false` to hide route from MCP |\n| `mcp_type` | `str \\| None` | `None` | `\"tool\"`, `\"resource\"`, or `\"resource_template\"` |\n| `methods` | `list[str] \\| None` | `None` | The route's HTTP methods to serve; the others are left out (`\"*\"` for all) |\n| `exclude_args` | `list[str] \\| None` | `None` | Arguments left out of the tool schema; each needs a default, which then applies |\n| `name` | `str \\| None` | `None` | Tool name, replacing the one built from the path |\n| `tags` | `list[str] \\| None` | `None` | Tags added to the tool next to its category |\n| `enable` | `bool \\| None` | `None` | With discovery off, serve (`true`) or hide (`false`) the tool regardless of `default_tool_categories` |\n| `describe_responses` | `bool \\| None` | `None` | Keep (`true`) or cut (`false`) the response documentation in the tool description |\n| `mime_type` | `str \\| None` | `None` | MIME type of a route served as a resource |\n| `prompts` | `list[dict]` | `[]` | Inline prompt definitions |\n\n---\n\n## What Agents Get\n\nBeyond one tool per route, every server provides:\n\n- **`run_pipeline`** — chains tools on the server, feeding an earlier step's\n  `results` (or a chart artifact's rows) into a later step's argument, so\n  data-processing tools (`technical_*`, `quantitative_*`, `econometrics_*`)\n  receive price histories without them passing through the conversation. It\n  calls only tools the server serves.\n- **Parameter choices** — provider `choices` and widget `x-widget_config`\n  options become an `enum`, or a \"Valid values\" note in the description, and\n  an `optionsEndpoint` names the tool that lists the values. Options endpoints\n  hidden from the API schema have no tool to name.\n- **Chart artifacts** — a Plotly figure in a result (a figure route, or an\n  OBBject `chart` from `chart: true`) is replaced by an OpenBB Workspace\n  artifact: a `chart` artifact with rows and `chart_params` for line, bar,\n  scatter, pie, and donut figures, else a `table` artifact of the figure's\n  data.\n- **Prompt and resource tools** — `list_prompts`, `get_prompt`,\n  `list_resources`, and `read_resource` for clients without native prompt or\n  resource support.\n- **`openbb-cli` dispatcher tools** — with the `cli` extra and\n  `enable_cli_tools`.\n- **`install_skill`** — writes a skill into the bundled or a vendor skills\n  directory. Skill names are limited to lowercase letters, digits,\n  underscores, and hyphens, and file paths must stay inside the skill\n  directory, but any connected client can call it. When clients are\n  untrusted, keep the skills directories read-only for the server process or\n  require `server_auth`.\n\n---\n\n## Client Configuration Examples\n\n### Claude Desktop (stdio transport)\n\nIn Claude Desktop's MCP config file:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"--with\", \"openbb\",\n                \"openbb-mcp\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\nFor a custom app:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"command\": \"uvx\",\n            \"args\": [\n                \"--from\", \"openbb-mcp-server\",\n                \"openbb-mcp\",\n                \"--app\", \"./my_app.py\",\n                \"--transport\", \"stdio\"\n            ]\n        }\n    }\n}\n```\n\n### Cursor (streamable-http)\n\n1. Start the server: `openbb-mcp`\n2. In Cursor's `mcp.json`:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\"\n        }\n    }\n}\n```\n\n### VS Code (streamable-http)\n\n1. Enable MCP in VS Code settings (Settings → Chat → MCP)\n2. Start the server: `openbb-mcp`\n3. Open Command Palette → \"MCP: Add Server\" → HTTP\n4. Enter URL: `http://127.0.0.1:8001/mcp`\n\nFor the Cline VS Code extension, use `--transport sse`:\n\n```\nopenbb-mcp --transport sse\n```\n\n### With Authentication\n\nStart with server auth enabled:\n\n```\nopenbb-mcp --host 0.0.0.0 --port 8001\n```\n\nWith `mcp_settings.json`:\n\n```json\n{\n    \"server_auth\": [\"admin\", \"secretpass\"]\n}\n```\n\nClients include the Bearer token in their configuration:\n\n```json\n{\n    \"mcpServers\": {\n        \"openbb-mcp\": {\n            \"url\": \"http://localhost:8001/mcp\",\n            \"headers\": {\n                \"Authorization\": \"Bearer YWRtaW46c2VjcmV0cGFzcw==\"\n            }\n        }\n    }\n}\n```\n\nThe token value is `base64(\"admin:secretpass\")`.\n\n---\n\n## Advanced Configuration\n\n### Lists and Dicts in Environment Variables\n\nLists can be passed as comma-separated strings:\n\n```\nOPENBB_MCP_DEFAULT_TOOL_CATEGORIES=\"equity,economy,crypto\"\nOPENBB_MCP_ALLOWED_TOOL_CATEGORIES=\"equity,economy\"\n```\n\nDicts and tuples must be JSON-encoded strings:\n\n```\nOPENBB_MCP_SERVER_AUTH='[\"user\", \"pass\"]'\nOPENBB_MCP_UVICORN_CONFIG='{\"host\": \"0.0.0.0\", \"port\": 8001, \"log_level\": \"info\"}'\nOPENBB_MCP_HTTPX_CLIENT_KWARGS='{\"timeout\": 30, \"verify\": false}'\n```\n\n### SSL / HTTPS\n\nPass SSL config via Uvicorn:\n\n```\nopenbb-mcp --ssl-keyfile /path/to/key.pem --ssl-certfile /path/to/cert.pem\n```\n\nOr in the config file:\n\n```json\n{\n    \"uvicorn_config\": {\n        \"host\": \"0.0.0.0\",\n        \"port\": 443,\n        \"ssl_keyfile\": \"/path/to/key.pem\",\n        \"ssl_certfile\": \"/path/to/cert.pem\"\n    }\n}\n```\n\n### Using as a Library\n\n```python\nimport asyncio\nfrom fastapi import FastAPI\nfrom openbb_mcp_server.app.app import create_mcp_server\nfrom openbb_mcp_server.models.settings import MCPSettings\n\napp = FastAPI()\n\n\n@app.get(\"/hello\")\nasync def hello():\n    return \"Hello World\"\n\n\nsettings = MCPSettings(\n    name=\"My Custom MCP\",\n    default_tool_categories=[\"all\"],\n    enable_tool_discovery=False,\n)\n\nmcp = create_mcp_server(settings, app)\nmcp.run(transport=\"streamable-http\")\n```\n\n---\n\n## Workflow Summary\n\nTo configure and deploy an OpenBB MCP server:\n\n1. **Install**: `pip install openbb-mcp-server` (plus any desired OpenBB extensions).\n2. **Configure**: Create `~/.openbb_platform/mcp_settings.json` with desired settings.\n3. **Add prompts**: Write a system prompt file and/or server prompts JSON.\n4. **Start**: Run `openbb-mcp` with appropriate CLI flags.\n5. **Connect**: Configure your MCP client (Claude Desktop, Cursor, VS Code) with the server URL or stdio command.\n6. **Discover**: Use `available_categories`, `search_tools`, and `call_tool` to find and run tools.\n7. **Iterate**: Adjust settings, add inline `mcp_config` to routes, add skill files.\n\n<!-- File: openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/develop_extension/SKILL.md (Tokens: ~5019 | Category: anthropic-skill) -->\n---\nname: develop_extension\ndescription: This is a complete guide for creating a new OpenBB Platform extension from scratch. Follow every phase in order. When the user says \"build me an application that does X\", use this guide to scaffold, implement, install, and verify the extension.\n---\n\n# Build an OpenBB Platform Extension\n\nThis is a complete guide for creating a new OpenBB Platform extension from scratch.\nFollow every phase in order. When the user says \"build me an application that does X\",\nuse this guide to scaffold, implement, install, and verify the extension.\n\n---\n\n## Phase 1 — Scaffold the Project\n\nThe `openbb-cookiecutter` package must be installed in the active Python environment\nbefore scaffolding. Install it with:\n\n```\npip install openbb-cookiecutter\n```\n\nThen run the CLI to generate the project skeleton.\nAll variables have sensible defaults; override only what you need.\n\n### Template Variables\n\n| Variable | Default | Description |\n|---|---|---|\n| `full_name` | `\"Hello World\"` | Author name |\n| `email` | `\"hello@world.com\"` | Author email |\n| `project_name` | `\"OpenBB Python Extension Template\"` | Human-readable project name |\n| `project_tag` | derived from `project_name` | Hyphenated slug (used as directory name and package identifier) |\n| `package_name` | derived from `project_name` | Python package name (lower_snake_case) |\n| `provider_name` | derived from `project_name` | Provider identifier (lower_snake_case) |\n| `router_name` | derived from `project_name` | Router identifier (lower_snake_case) |\n| `obbject_name` | derived from `project_name` | OBBject accessor name (lower_snake_case) |\n\nAll derived values are automatically generated from `project_name` — you only need\nto supply `project_name` for most cases.\n\n### CLI Command\n\n```\nopenbb-cookiecutter \\\n  -o /path/to/output \\\n  --no-input \\\n  --extra-context project_name=\"My Extension Name\"\n```\n\nAdd more `--extra-context KEY=VALUE` pairs to override individual variables.\nUse `-f` to overwrite an existing directory.\n\n---\n\n## Phase 2 — Understand the Generated Structure\n\nAfter scaffolding, you get this tree (with template variables resolved):\n\n```\n<project_tag>/\n├── pyproject.toml          # Dependencies, entry points (CRITICAL)\n├── <package_name>/\n│   ├── providers/\n│   │   └── <provider_name>/\n│   │       ├── __init__.py     # Provider registration (fetcher_dict)\n│   │       ├── models/\n│   │       │   ├── example.py      # Custom-schema fetcher example\n│   │       │   └── ohlc_example.py # Standard-model fetcher example\n│   │       └── utils/\n│   │           └── helpers.py      # Shared utility functions\n│   ├── routers/\n│   │   ├── __init__.py\n│   │   ├── <router_name>.py        # Router commands (API endpoints)\n│   │   ├── <router_name>_views.py  # Chart views (optional)\n│   │   └── depends.py              # Dependency injection\n│   └── obbject/\n│       └── <obbject_name>/\n│           └── __init__.py         # OBBject accessors (optional)\n└── tests/\n    └── conftest.py\n```\n\n### The Four Plugin Types\n\nOpenBB discovers extensions via Python entry points in `pyproject.toml`.\nEach plugin type has its own entry point group:\n\n| Entry Point Group | What It Registers | Where It Lives |\n|---|---|---|\n| `openbb_provider_extension` | Data provider (fetchers) | `providers/<name>/__init__.py` |\n| `openbb_core_extension` | Router (API endpoints/commands) | `routers/<name>.py` |\n| `openbb_charting_extension` | Chart views | `routers/<name>_views.py` |\n| `openbb_obbject_extension` | Result post-processing accessors | `obbject/<name>/__init__.py` |\n\n---\n\n## Phase 3 — Implement the Data Provider\n\nThis is the core of any extension. A provider fetches data from an external source\nand returns it as typed Pydantic models.\n\n### The Three-Class Fetcher Pattern\n\nEvery data fetcher follows this structure:\n\n**Class 1 — QueryParams** (input schema):\n- Inherits from `openbb_core.provider.abstract.query_params.QueryParams`\n- Define all parameters the user can pass (e.g., `symbol`, `start_date`)\n- Use `pydantic.Field` for descriptions and defaults\n\n**Class 2 — Data** (output schema):\n- Inherits from `openbb_core.provider.abstract.data.Data`\n- Define all fields in the response (e.g., `open`, `high`, `low`, `close`, `volume`)\n- Use `pydantic.Field` for descriptions\n- Use `__alias_dict__` to map source field names to your schema names\n\n**Class 3 — Fetcher** (orchestrator):\n- Inherits from `Fetcher[YourQueryParams, list[YourData]]`\n- Has exactly three static methods:\n\n  1. `transform_query(params: dict) -> YourQueryParams`\n     Pre-process user input. Return a validated QueryParams instance.\n\n  2. `extract_data(query, credentials, **kwargs) -> list[dict]`\n     Make the actual HTTP request to the data source. Return raw data as dicts.\n     For async fetching, name it `aextract_data` instead.\n\n  3. `transform_data(query, data, **kwargs) -> list[YourData]`\n     Convert raw dicts into typed Data model instances.\n\n### Imports for Fetcher Files\n\n```python\nfrom typing import Any\nfrom openbb_core.provider.abstract.data import Data\nfrom openbb_core.provider.abstract.fetcher import Fetcher\nfrom openbb_core.provider.abstract.query_params import QueryParams\nfrom pydantic import Field\n```\n\n### HTTP Requests — Required Utilities\n\n**IMPORTANT:** All HTTP requests inside fetchers and utility helpers **must** use the built-in\nutilities from `openbb_core.provider.utils.helpers`. Do **not** create raw\n`requests`, `aiohttp`, or `httpx` clients from scratch. The built-in helpers\napply the user's configured HTTP settings (proxy, timeout, user-agent, etc.)\nfrom `system_settings.json` automatically.\n\n#### Building Query Strings\n\nConvert a QueryParams model to a URL query string, optionally excluding\nparameters that should not appear in the URL:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_querystring\n\nquery_string = get_querystring(query.model_dump(), [\"interval\", \"provider\"])\nurl = f\"https://api.example.com/data?{query_string}\"\n```\n\n`model_dump()` strips `None` values automatically. Pass parameter names to\nexclude in the second argument as a list (use `[]` if nothing to exclude).\n\n#### Synchronous Requests\n\nFor use inside `extract_data` (sync fetchers):\n\n```python\nfrom openbb_core.provider.utils.helpers import make_request\n\n# Returns a requests.Response object\nresponse = make_request(url, headers={\"Authorization\": f\"Bearer {api_key}\"})\ndata = response.json()\n```\n\nAll `requests.get`/`requests.post` keyword arguments are passed through.\n\nIf you need a session object for multiple requests:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nsession = get_requests_session()\nresponse = session.get(url)\n```\n\n#### Asynchronous Requests (Preferred)\n\nFor use inside `aextract_data` (async fetchers). **Always prefer async.**\n\n**Single URL** — returns parsed JSON by default:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_request\n\ndata = await amake_request(url)  # returns dict (parsed JSON)\n```\n\n**Multiple URLs** — downloads concurrently and returns a list:\n\n```python\nfrom openbb_core.provider.utils.helpers import amake_requests\n\nurls = [f\"https://api.example.com/data/{s}\" for s in symbols]\nall_data = await amake_requests(urls)  # returns list[dict]\n```\n\n**Custom response handling (e.g., CSV):** Both `amake_request` and\n`amake_requests` default to parsing JSON. For non-JSON content (CSV, text,\nbinary), pass a `response_callback`:\n\n```python\nfrom io import StringIO\nfrom typing import Any\nfrom pandas import read_csv\nfrom openbb_core.provider.utils.helpers import amake_request\n\nresults: list[dict] = []\n\n\nasync def csv_callback(response, _: Any):\n    \"\"\"Parse CSV response into list of dicts.\"\"\"\n    text = await response.text()\n    df = read_csv(StringIO(text))\n    results.extend(df.to_dict(\"records\"))\n\n\nawait amake_request(url, response_callback=csv_callback)\n# results now contains the parsed rows\n```\n\nFor CSV files with header rows to skip, pass `skiprows` to `read_csv`:\n\n```python\nasync def csv_callback(response, _: Any):\n    text = await response.text()\n    df = read_csv(StringIO(text), skiprows=3)\n    results.extend(df.to_dict(\"records\"))\n```\n\n**Async session object** — if you need a raw `aiohttp.ClientSession`:\n\n```python\nfrom openbb_core.provider.utils.helpers import get_async_requests_session\n\nasync with await get_async_requests_session() as session:\n    async with session.get(url) as response:\n        if response.status != 200:\n            raise OpenBBError(f\"Failed: {response.status} -> {response.reason}\")\n        data = await response.json()\n```\n\n#### Summary: Which Helper to Use\n\n| Scenario | Function | Module |\n|---|---|---|\n| Build a query string | `get_querystring()` | `openbb_core.provider.utils.helpers` |\n| Sync single request | `make_request()` | `openbb_core.provider.utils.helpers` |\n| Sync session | `get_requests_session()` | `openbb_core.provider.utils.helpers` |\n| Async single request (JSON) | `amake_request()` | `openbb_core.provider.utils.helpers` |\n| Async multiple URLs (JSON) | `amake_requests()` | `openbb_core.provider.utils.helpers` |\n| Async single/multi (CSV/text) | `amake_request()` + `response_callback` | `openbb_core.provider.utils.helpers` |\n| Async raw session | `get_async_requests_session()` | `openbb_core.provider.utils.helpers` |\n\n### Using Standard Models (Multi-Provider Endpoints)\n\nTo plug into existing endpoints that other providers already serve (like `EquityHistorical`),\ninherit from the standard query/data classes instead of the abstract base classes:\n\n```python\nfrom openbb_core.provider.standard_models.equity_historical import (\n    EquityHistoricalData,\n    EquityHistoricalQueryParams,\n)\n```\n\nThen add provider-specific fields as extra attributes.\nUse `__alias_dict__` to map the source's field names to the standard field names.\n\n### Registering Fetchers in the Provider\n\nIn `providers/<provider_name>/__init__.py`, create a `Provider` instance:\n\n```python\nfrom openbb_core.provider.abstract.provider import Provider\n\nmy_provider = Provider(\n    name=\"my_provider\",\n    description=\"Description of what this provider does.\",\n    # credentials=[\"api_key\"],  # Uncomment if an API key is needed\n    website=\"https://example.com\",\n    fetcher_dict={\n        \"MyCustomModel\": MyCustomFetcher,\n        \"EquityHistorical\": MyEquityHistoricalFetcher,  # Plugs into existing endpoint\n    },\n)\n```\n\nThe `fetcher_dict` keys are model names. When a key matches a standard model name\n(like `\"EquityHistorical\"`), this provider becomes selectable via the `provider`\nparameter on that existing endpoint.\n\nFor custom/new model names, you must also create a router command that references\nthat model name (see Phase 4).\n\n### Credentials\n\nIf your provider needs an API key:\n1. Add `credentials=[\"api_key\"]` to the Provider constructor\n2. Access it in `extract_data` via `credentials.get(\"<provider_name>_api_key\")` (the `Provider` prefixes each credential with its lower-cased `name`)\n3. Users configure it in their OpenBB user settings\n\n---\n\n## Phase 4 — Implement the Router\n\nThe router defines the API endpoints (commands) that users call.\n\n### Router Basics\n\n```python\nfrom openbb_core.app.router import Router\n\nrouter = Router(prefix=\"\")\n```\n\nThe top-level prefix is determined by the entry point name in `pyproject.toml`,\nnot by the `prefix` argument. Only set `prefix` for sub-routers.\n\n### Provider-Backed Command (Standard Pattern)\n\nThis is the most common pattern. It connects a router command to one or more\nprovider fetchers via the model name:\n\n```python\nfrom openbb_core.app.model.command_context import CommandContext\nfrom openbb_core.app.model.obbject import OBBject\nfrom openbb_core.app.provider_interface import (\n    ExtraParams,\n    ProviderChoices,\n    StandardParams,\n)\nfrom openbb_core.app.query import Query\nfrom pydantic import BaseModel\n\n\n@router.command(model=\"MyCustomModel\")\nasync def my_command(\n    cc: CommandContext,\n    provider_choices: ProviderChoices,\n    standard_params: StandardParams,\n    extra_params: ExtraParams,\n) -> OBBject[BaseModel]:\n    \"\"\"Description of this command.\"\"\"\n    return await OBBject.from_query(Query(**locals()))\n```\n\nThe four parameters (`cc`, `provider_choices`, `standard_params`, `extra_params`)\nand the `OBBject.from_query(Query(**locals()))` return pattern are mandatory and\nmust be used exactly as shown.\n\nThe `model=\"MyCustomModel\"` string must match a key in at least one provider's\n`fetcher_dict`.\n\n### Free-Form GET Endpoint\n\nFor endpoints that don't use the provider/fetcher system:\n\n```python\n@router.command(methods=[\"GET\"])\nasync def my_endpoint(symbol: str = \"AAPL\") -> OBBject[dict]:\n    \"\"\"Get some data directly.\"\"\"\n    # Make HTTP requests, compute, etc.\n    return OBBject(results={\"key\": \"value\"})\n```\n\n### Free-Form POST Endpoint\n\n```python\n@router.command(methods=[\"POST\"])\nasync def my_post_endpoint(\n    data: BaseModel,  # Body parameters\n    flag: bool = False,  # Query parameters\n) -> OBBject[dict]:\n    \"\"\"Process submitted data.\"\"\"\n    return OBBject(results={\"processed\": True})\n```\n\n### Dependency Injection\n\nUse `routers/depends.py` for shared dependencies:\n\n```python\nfrom typing import Annotated\nimport requests\nfrom fastapi import Depends\nfrom openbb_core.provider.utils.helpers import get_requests_session\n\nSession = Annotated[requests.Session, Depends(get_requests_session)]\n```\n\nThen use `session: Session` as a parameter in your router commands.\n\n### Adding Examples\n\n```python\nfrom openbb_core.app.model.example import APIEx, PythonEx\n\n@router.command(\n    model=\"MyModel\",\n    examples=[\n        PythonEx(\n            description=\"Get data for AAPL\",\n            code=[\"obb.my_router.my_command(symbol='AAPL')\"],\n        )\n    ],\n)\n```\n\n---\n\n## Phase 5 — Entry Points in pyproject.toml\n\nThis is **critical** — OpenBB discovers your code entirely through these entry points.\n\n### Provider Entry Point\n\n```toml\n[project.entry-points.\"openbb_provider_extension\"]\nmy_provider = \"my_package.providers.my_provider:my_provider_variable\"\n```\n\nThe variable (`my_provider_variable`) is the `Provider(...)` instance you created\nin Phase 3.\n\n### Router Entry Point\n\n```toml\n[project.entry-points.\"openbb_core_extension\"]\nmy_router = \"my_package.routers.my_router:router\"\n```\n\nThe entry point **name** (`my_router`) determines the API path prefix.\nFor example, `my_router` means endpoints appear under `/my_router/...`.\n\n### Charting Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_charting_extension\"]\nmy_router = \"my_package.routers.my_router_views:MyRouterViews\"\n```\n\n### OBBject Entry Point (Optional)\n\n```toml\n[project.entry-points.\"openbb_obbject_extension\"]\nmy_accessor = \"my_package.obbject.my_obbject:ext\"\nmy_namespace = \"my_package.obbject.my_obbject:class_ext\"\n```\n\n---\n\n## Phase 6 — Chart Views (Optional)\n\nIf you want to add charting support:\n\n```python\nfrom typing import TYPE_CHECKING, Any\n\nif TYPE_CHECKING:\n    from openbb_charting.core.openbb_figure import OpenBBFigure\n\n\nclass MyRouterViews:\n    \"\"\"Chart views for the router.\"\"\"\n\n    @staticmethod\n    def my_router_my_command(**kwargs) -> tuple[\"OpenBBFigure\", dict[str, Any]]:\n        \"\"\"Create a chart for my_command results.\"\"\"\n        from openbb_charting.core.openbb_figure import OpenBBFigure\n\n        data = kwargs[\"obbject_item\"]\n        fig = OpenBBFigure()\n        # Build your chart using fig.add_*() methods\n        content = fig.show(external=True).to_plotly_json()\n        return fig, content\n```\n\nMethod naming convention: `<router_name>_<command_name>` matching the route path\nin lower_snake_case.\n\n### Charting Engine, Hooks, and Backends\n\nEvery interface (Python, REST API, CLI, and MCP) resolves charts through\n`openbb_core.app.charting.ChartingManager`, so these seams apply everywhere:\n\n| Seam | Register under | Selected by |\n|---|---|---|\n| Replacement charting engine | `openbb_obbject_extension`, as an accessor named `charting` | the `charting_extension` system setting, to use an accessor with another name |\n| Lifecycle hooks | `openbb_charting_hooks`, as a `ChartingHook` subclass | always active; `routes` limits and `priority` orders them |\n| Rendering backend | `openbb_charting_backend` | the `charting_backend` system setting, by entry-point name (opt-in) |\n\nSet `charting_extension` and `charting_backend` under `[system]` in `openbb.toml`\nor in `~/.openbb_platform/system_settings.json`.\n\nA hook overrides any of `resolve_data`, `pre_figure`, `post_figure`, `pre_render`,\nand `post_render`. Each receives a `HookContext` and may mutate it in place or\nreturn a replacement:\n\n```python\nfrom openbb_core.app.charting.hooks import ChartingHook, HookContext\n\n\nclass Watermark(ChartingHook):\n    routes = (\"/equity/price/historical\",)\n\n    def post_figure(self, context: HookContext) -> None:\n        context.figure.add_annotation(text=\"My Firm\", showarrow=False)\n```\n\n```toml\n[project.entry-points.\"openbb_charting_hooks\"]\nwatermark = \"my_package.hooks:Watermark\"\n```\n\n---\n\n## Phase 7 — OBBject Accessors (Optional)\n\nResult post-processing extensions that add methods to the `OBBject` response.\n\n### Function Accessor (Property-Like)\n\n```python\nfrom openbb_core.app.model.extension import Extension\n\next = Extension(name=\"to_csv\", description=\"Convert results to CSV string.\")\n\n\n@ext.obbject_accessor\ndef to_csv(obbject, **kwargs) -> str:\n    \"\"\"Convert to CSV.\"\"\"\n    return obbject.to_dataframe().to_csv()\n```\n\n### Class Accessor (Namespaced Methods)\n\n```python\nclass_ext = Extension(name=\"my_tools\", description=\"Custom result tools.\")\n\n\n@class_ext.obbject_accessor\nclass MyTools:\n    def __init__(self, obbject):\n        self._obbject = obbject\n\n    def summary(self, **kwargs):\n        \"\"\"Return a summary.\"\"\"\n        df = self._obbject.to_dataframe()\n        return df.describe()\n```\n\n---\n\n## Phase 8 — Install, Build, and Test\n\n### Install in Development Mode\n\nFrom the generated project root directory:\n\n```\nuv pip install -e . --group dev\n```\n\nThis registers the entry points so OpenBB discovers your extension immediately.\n\n### Build Static Assets with `openbb-build`\n\n**CRITICAL:** After installing a new extension, or after making changes to any\nof the following, you **must** run `openbb-build` before using the Python\ninterface (`obb.<router>.<command>(...)`):\n\n- **Model definitions** — `QueryParams` or `Data` classes (field names, types,\n  defaults, descriptions)\n- **Provider registration** — changes to `fetcher_dict` keys, adding/removing\n  fetchers\n- **Router commands** — adding, removing, or renaming `@router.command()`\n  endpoints\n- **Entry points** — changes to `pyproject.toml` plugin entries\n- **Any importable item in the registration chain** — the `Provider(...)`\n  instance, router module, or model module paths\n\nYou do **not** need to re-run `openbb-build` when changing:\n\n- Logic inside `extract_data` / `aextract_data` / `transform_data` /\n  `transform_query` static methods (the Fetcher method bodies)\n- Utility/helper functions\n- Internal implementation details that don't affect the public schema\n\n```\nopenbb-build\n```\n\nThis regenerates the static assets (type stubs, package interface, provider\nmaps) that the Python interface relies on. Without this step, new or modified\ncommands will not appear on the `obb` object and calls will fail.\n\n**When running as an API server** (e.g., via `uvicorn` or the MCP server),\nstatic assets are **not used** — the API discovers extensions dynamically at\nstartup. You do not need to run `openbb-build` for API-only usage.\n\n### Verify Installation\n\nStart a Python session and check:\n\n```python\nfrom openbb import obb\n# Your new commands should appear:\n# obb.<router_name>.<command_name>(...)\n```\n\nOr start the API server and verify the new endpoints appear.\n\n### Run Tests\n\n```\npytest tests/ -v\n```\n\nThe generated `tests/conftest.py` sets `OPENBB_AUTO_BUILD=true` for proper\ntest environment setup.\n\n---\n\n## Workflow Summary\n\nWhen a user asks \"Build me an application that does X\":\n\n1. **Analyze** — Determine what data sources are needed, what endpoints to expose,\n   and whether to use standard models or custom schemas.\n2. **Scaffold** — Run `openbb-cookiecutter` with a meaningful `project_name`.\n3. **Delete examples** — Remove `example.py` and `ohlc_example.py` from the models\n   directory. Clean up the example router commands.\n4. **Implement models** — Create `QueryParams` + `Data` + `Fetcher` classes for each\n   data source in `providers/<name>/models/`.\n5. **Register fetchers** — Update `providers/<name>/__init__.py` with the `fetcher_dict`.\n6. **Implement router** — Create `@router.command(model=\"...\")` endpoints in\n   `routers/<name>.py`.\n7. **Update entry points** — Ensure `pyproject.toml` entry points match your actual\n   module paths and variable names.\n8. **Add dependencies** — Add any third-party packages to the `[project]`\n   `dependencies` list in `pyproject.toml`.\n9. **Install** — Run `uv pip install -e . --group dev` from the project root.\n10. **Build** — Run `openbb-build` to regenerate static assets for the Python\n    interface. Skip this step if only using the API server.\n11. **Test** — Verify the commands work, then write tests.\n\n<!-- File: openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/use_openbb_cli/SKILL.md (Tokens: ~3076 | Category: anthropic-skill) -->\n---\nname: use_openbb_cli\ndescription: This guide covers the `openbb` command-line surface — running commands, generating `.spec` files, dispatching against precomputed specs, and feeding spec output back into `openbb-mcp` for spec-driven proxy mode.\n---\n\n# Using the openbb-cli\n\n`openbb-cli` ships the `openbb` console-script. It is a self-contained\nfront-end for the same `obb` namespace the MCP server exposes, plus a build\ntool for generating reusable `.spec` files and full installable extension\npackages.\n\nThree console-scripts live on the v5 stack:\n\n| Command | Package | Purpose |\n|---|---|---|\n| `openbb` | `openbb-cli` | Run commands, REPL, generate `.spec` files. |\n| `openbb-mcp` | `openbb-mcp-server` | MCP server launcher (HTTP/SSE/stdio transports). |\n| `openbb-build` | `openbb-core` | Register a newly installed extension into the `obb` namespace. |\n\nThis guide focuses on `openbb`.\n\n---\n\n## Installation\n\n```\npip install openbb-cli\n```\n\nPulls `openbb-core[pandas]`, `prompt-toolkit`, `rich`, and `httpx`.\n\n---\n\n## Execution Modes\n\n`openbb` has six mutually-exclusive modes (the rest of the flags are shared\nacross modes).\n\n| Mode flag | Purpose |\n|---|---|\n| *(default)* | Single-shot: dispatch one dotted command and exit with NDJSON on stdout. |\n| `-i` / `--interactive` | Rich REPL with prompt-toolkit completion. |\n| `--batch` | Read NDJSON requests from stdin; emit NDJSON responses to stdout. |\n| `--generate-spec` | Fetch `openapi.json` from `--server` and write a precomputed `.spec` file to `--output`. |\n| `--generate-extension` | Build a full installable OpenBB Platform extension package from a `.spec` file. |\n| `--list-commands` / `--describe COMMAND[:PROVIDER]` | Print the command catalog or one command's schema as JSON. Needs `--spec` or `--server`. |\n\n### Single-shot\n\n```bash\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n```\n\nOutput is one dispatcher response as a JSON line: `{\"ok\": true, \"result\": ...}`\non success, where `result` holds the command's OBBject fields (`results`,\n`provider`, ...), or `{\"ok\": false, \"error\": {\"type\": ..., \"message\": ...}}` on\nfailure. Unset fields are left out.\nExit code is `0` on success, `1` on a dispatch error, `2` on a usage error.\n\n### Interactive REPL\n\n```bash\nopenbb -i\n```\n\nPrompt-toolkit completion against the resolved command catalog. Same flag\nsurface — `--server` / `--spec` / `--header` etc. work identically.\n\n### Batch (NDJSON in/out)\n\n```bash\necho '{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"AAPL\"},\"id\":\"a\"}\n{\"command\":\"equity.price.quote\",\"params\":{\"symbol\":\"MSFT\"},\"id\":\"b\"}' \\\n  | openbb --batch\n```\n\nEach input line is one `Request`; each output line is the matching\n`Response`. Use `--batch-concurrency N` (or `OPENBB_CLI_BATCH_CONCURRENCY`) to\ncontrol in-flight parallelism.\n\n---\n\n## Backend Selection\n\nThree backends share the dispatch surface; selection priority is\n`--spec` > `--server` > in-process `obb`.\n\n| Backend | Trigger | Behavior |\n|---|---|---|\n| Local | (default) | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. |\n| Remote HTTP | `--server URL` | `HttpDispatcher` proxies to an `openbb-platform-api` server. The heavy import lives on the server. |\n| Spec-driven | `--spec [NAME=]PATH` | Skips OpenAPI fetch + parse entirely. The spec carries the server URL it was generated against and the per-command HTTP methods. |\n\n```bash\nopenbb --server https://api.example.com economy.gdp\nopenbb --spec /etc/openbb/cli.spec economy.gdp\n```\n\n`--spec` is repeatable. Multiple `NAME=PATH` entries build a multi-spec\ndispatcher and the REPL routes by leading namespace\n(`congress.bill` → `congress` spec, `nyfed.markets.ambs` → `nyfed` spec).\nA single unnamed `--spec PATH` keeps the flat (unprefixed) command surface.\n\n---\n\n## `.spec` Files\n\nA `.spec` file is a precomputed JSON document encoding every command the\nserver exposes — URL path, HTTP method, parameter schema, response schema,\nand a `content_sha256` integrity hash over the canonical-JSON body.\n\n### Generating a spec\n\n```bash\n# From an openbb-platform-api server\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# From a server using a non-default OpenAPI path\nopenbb --generate-spec \\\n  --server https://markets.example.com \\\n  --openapi-path /static/docs/markets-api.yml \\\n  --output markets.spec\n\n# From a Socrata story (no OpenAPI involved — walks the story for datasetUid\n# entries and emits one router namespace per dataset)\nopenbb --generate-spec \\\n  --socrata-story https://data.example.gov/path/to/story.json \\\n  --output socrata.spec\n```\n\n### Using a spec\n\nAfter generation, every dispatch can use the spec to skip OpenAPI fetch +\nparse on every call:\n\n```bash\nopenbb --spec cli.spec economy.gdp\nopenbb --spec cli.spec --list-commands\nopenbb --spec cli.spec --describe equity.price.historical\nopenbb --spec cli.spec --describe equity.price.historical:yfinance\n```\n\nThe spec carries the upstream `base_url` it was generated against, so\n`--server` is not needed alongside `--spec`.\n\n### Filtering commands\n\n`--include` and `--exclude` are repeatable glob patterns over dotted command\nnames, applied by `--generate-extension` (`--generate-spec` always writes every\ncommand). When `--include` is supplied it takes priority — anything not\nmatching is dropped regardless of `--exclude`.\n\n```bash\nopenbb --generate-extension \\\n  --spec cli.spec \\\n  --output ./openbb-equity-subset \\\n  --include 'equity.*' \\\n  --exclude 'equity.fundamentals.*'\n```\n\n---\n\n## Headers and Query Parameters\n\nEvery dispatch (and the OpenAPI fetch during `--generate-spec`) carries the\nsame headers/query-params.\n\n```bash\n# Per-flag, repeatable\nopenbb --server https://api.example.com \\\n  -H 'Authorization: Bearer xxx' \\\n  -H 'X-Tenant: acme' \\\n  -Q 'api_key=secret' \\\n  equity.price.quote --symbol AAPL\n\n# From a JSON file (object of string values)\nopenbb --header-file ./headers.json --query-param-file ./query.json economy.gdp\n```\n\nEnv-var auto-loading: any `OPENBB_HTTP_QUERY_*` env var becomes a query param\n(`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). CLI flags beat env vars.\n\nFor multi-spec dispatchers, prefix a header or query parameter with a\nnamespace to send it to that spec only (`-H 'congress:X-API-Key: xxx'`,\n`-Q 'nyfed:format=json'`), or scope it in the TOML config\n(`[specs.<ns>.headers]`, `[specs.<ns>.query]`).\n\n---\n\n## Configuration File (`openbb.toml`)\n\nFlag defaults can be set via a layered TOML cascade — same pattern openbb-core\nuses. Layers (highest priority last):\n\n```\npyproject.toml [tool.openbb-cli]\n  → user-global ~/.openbb_platform/openbb.toml\n  → project openbb.toml (walking up from CWD)\n  → --config PATH (explicit)\n  → .env files\n  → real shell env vars\n```\n\nCLI flags always beat env vars; env vars beat TOML.\n\nThe CLI reads top-level keys of `openbb.toml` (and of `[tool.openbb-cli]` in\n`pyproject.toml`): `server`, `spec`, `openapi-path`, `header-file`,\n`query-param-file`, `auth-hook`, and the `[headers]`, `[query]`,\n`[specs.<ns>]`, and `[settings]` tables. Set the batch concurrency with\n`--batch-concurrency` or `OPENBB_CLI_BATCH_CONCURRENCY`.\n\n```toml\n# openbb.toml\nserver = \"https://api.example.com\"\n\n[headers]\nAuthorization = \"Bearer ...\"\n\n[specs.congress]\npath = \"/etc/openbb/congress.spec\"\n[specs.congress.headers]\n\"X-API-Key\" = \"...\"\n```\n\nHeader and query values are sent as written; they are not expanded from\n`$VAR` references.\n\n```bash\nopenbb --config /etc/openbb/openbb.toml --list-commands\nopenbb --print-config-template      # documented TOML template\nopenbb --show-config                # merged config as JSON\n```\n\n---\n\n## Building Extensions From a Spec\n\n`--generate-extension` builds a full installable OpenBB Platform extension\npackage from exactly one `.spec` file. The project is written to\n`<output>/<project-name>`; the command prints that path with its providers,\nrouters, and install commands.\n\n```bash\nopenbb --generate-extension \\\n  --spec congress.spec \\\n  --output ./extensions \\\n  --provider-name congress_gov \\\n  --project-name openbb-congress-gov \\\n  --package-name openbb_congress_gov\n\npip install -e ./extensions/openbb-congress-gov\nopenbb-build\n```\n\n`--project-name` defaults to `openbb-<provider-name>`, `--package-name` to\n`openbb_<provider-name>`, and `--provider-name` to the name of the `--output`\ndirectory. `--router-name` is accepted but not used by the generator yet.\n\nAfter registration the extension's commands are reachable under the printed\nrouters, as `obb.<router>.*` in Python and `<router>.*` from any backend.\n\n---\n\n## Spec → MCP Round-Trip\n\nThe same `.spec` file can drive the MCP server in spec-driven proxy mode —\nthe heavy `import openbb` lives on the upstream `openbb-platform-api`, the\nMCP launcher only knows the spec.\n\n```bash\n# 1. Generate the spec on the host that already has openbb installed.\nopenbb --generate-spec --server https://api.example.com --output cli.spec\n\n# 2. Hand off the spec to a lightweight MCP container.\nopenbb-mcp --spec /etc/openbb/cli.spec\n```\n\nOr via TOML:\n\n```toml\n# openbb.toml passed via `openbb-mcp --config-file openbb.toml`\n[mcp.spec]\npath = \"/etc/openbb/cli.spec\"\ncontent_sha256 = \"abc...\"\n\n[mcp.spec.headers]\nAuthorization = \"Bearer $OPENBB_UPSTREAM_TOKEN\"\n```\n\nThe MCP server synthesizes a FastAPI proxy app from the spec's commands;\nFastMCP exposes the routes as MCP tools; per-tool dispatch forwards to the\nspec's `base_url` over `aiohttp`. See `configure_mcp_server` for multi-spec\nmounts (`[mcp.spec.NAME]`), per-spec auth/middleware hooks, and the\n`content_sha256` provenance pin.\n\n---\n\n## The Same Commands as MCP Tools\n\nWhen `openbb-cli` is installed next to `openbb-mcp-server`, the MCP server\nexposes the CLI's dispatcher as tools, so an agent gets the same surface:\n\n| CLI | MCP tool |\n|---|---|\n| `openbb <command> --param value` | `openbb_dispatch` (`command`, `params`) |\n| `openbb --batch` | `openbb_batch_dispatch` (`requests`) |\n| `openbb --list-commands` | `openbb_list_commands` |\n| `openbb --describe <command>` | `openbb_describe_command` (`command`, optional `provider`) |\n\nEvery tool takes an optional `server_url`, the counterpart of `--server`, and\nfalls back to `OPENBB_SERVER_URL` like the CLI does.\n\n---\n\n## Useful Environment Variables\n\n| Variable | Purpose |\n|---|---|\n| `OPENBB_SERVER_URL` | Default `--server` URL. |\n| `OPENBB_SPEC_PATH` | Default `--spec` path. |\n| `OPENBB_HTTP_QUERY_*` | Auto-loaded as query parameters (`OPENBB_HTTP_QUERY_API_KEY=xxx` → `?api_key=xxx`). |\n| `OPENBB_CLI_CONFIG` | Default `--config` path. |\n| `OPENBB_CLI_ENV_FILE` | Default `--env-file` path. |\n| `OPENBB_CLI_BATCH_CONCURRENCY` | Default `--batch-concurrency`. |\n| `OPENBB_HEADER_FILE` / `OPENBB_QUERY_PARAM_FILE` | Default `--header-file` / `--query-param-file`. |\n\n`~/.openbb_platform/.env` is always loaded as a fallback `.env` source — real\nshell exports beat both.\n\n---\n\n## Quick Reference\n\n```bash\n# Discover commands (needs --server or --spec)\nopenbb --server URL --list-commands\nopenbb --server URL --describe equity.price.historical\n\n# One-shot dispatch\nopenbb equity.price.historical --symbol AAPL --provider yfinance\n\n# Batch (NDJSON)\ncat requests.ndjson | openbb --batch\n\n# Generate a precomputed spec from a server\nopenbb --generate-spec --server URL --output out.spec\n\n# Dispatch through a precomputed spec\nopenbb --spec out.spec equity.price.quote --symbol AAPL\n\n# Multi-spec routing by leading namespace\nopenbb --spec congress=congress.spec --spec nyfed=nyfed.spec congress.bill\n\n# Generate a full extension package from a spec\nopenbb --generate-extension --spec out.spec --output ./openbb-myprov\n\n# Inspect resolved config\nopenbb --show-config\nopenbb --print-config-template > openbb.toml\n```\n\n<!-- File: openbb_platform/extensions/mcp_server/openbb_mcp_server/skills/work_with_server/SKILL.md (Tokens: ~6405 | Category: anthropic-skill) -->\n---\nname: work_with_server\ndescription: This guide explains how to call tools, interpret responses, discover capabilities, use prompts, and handle errors when interacting with an OpenBB MCP server.\n---\n\n# Working With the OpenBB MCP Server\n\nThis guide explains how to call tools, interpret responses, discover\ncapabilities, use prompts, and handle errors when interacting with an OpenBB\nMCP server.\n\n---\n\n## Tool Discovery Workflow\n\nWhen the server runs with `--tool-discovery`, the OpenBB tools are left out of\nthe tool list, and a small set of discovery tools finds and runs them.\n\nDiscovery holds no per-session state — every client sees the same tool list, and\nnothing needs to be activated before a call.\n\n### Step 1 — List Categories\n\nCall `available_categories` (no arguments) to see what is installed:\n\n```json\n[\n    {\n        \"name\": \"equity\",\n        \"subcategories\": [\n            {\"name\": \"price\", \"tool_count\": 5},\n            {\"name\": \"fundamental\", \"tool_count\": 12}\n        ],\n        \"total_tools\": 17\n    },\n    {\n        \"name\": \"economy\",\n        \"subcategories\": [\n            {\"name\": \"general\", \"tool_count\": 3}\n        ],\n        \"total_tools\": 3\n    }\n]\n```\n\nEach category maps to a top-level API router. Subcategories are nested routers.\n\n### Step 2 — Browse Tools in a Category\n\nCall `available_tools` with a category name:\n\n```json\n// Input\n{\"category\": \"equity\", \"subcategory\": \"price\"}\n\n// Output\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"Get historical price data...\"},\n    {\"name\": \"equity_price_quote\", \"description\": \"Get current price quote...\"}\n]\n```\n\nEach entry has the tool's name and a one-sentence description. The\n`subcategory` argument is optional. Omit it to see all tools in the category.\n\n### Step 3 — Search for Tools\n\nCall `search_tools` with a natural-language query:\n\n```json\n// Input\n{\"query\": \"historical stock prices\"}\n\n// Output - full tool definitions, in the same format as list_tools\n[\n    {\"name\": \"equity_price_historical\", \"description\": \"...\", \"inputSchema\": {\"...\": \"...\"}}\n]\n```\n\nSearch covers the hidden OpenBB tools and returns at most five matches. Use\neach match's `inputSchema` to build the arguments.\n\n### Step 4 — Call a Tool\n\nCall `call_tool` with the tool name and its arguments:\n\n```json\n{\"name\": \"equity_price_historical\", \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"}}\n```\n\n`call_tool` runs the named tool and returns its result. A hidden tool can also\nbe called directly by name, with the same arguments.\n\n### When Discovery Is Disabled\n\nIf the server was started without `--tool-discovery`, the discovery tools are not\navailable. All tools in `default_tool_categories` are listed and called directly;\ntools outside them are not served, and calling one fails with \"Unknown tool\".\n\n---\n\n## openbb-cli Dispatcher Tools\n\nWhen the optional `openbb-mcp-server[cli]` extra is installed (it pulls in\n`openbb-cli`), the server also registers four first-class tools that wrap\n`openbb-cli`'s NDJSON dispatcher protocol. They give an agent the same\n`obb`-namespace command surface that the `openbb` CLI exposes — without\nshelling out or standing up a Python REPL.\n\n### `openbb_dispatch`\n\nExecute a single command and return its serialized result.\n\n```json\n{\n    \"name\": \"openbb_dispatch\",\n    \"arguments\": {\n        \"command\": \"equity.price.historical\",\n        \"params\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\"},\n        \"server_url\": null\n    }\n}\n```\n\n| Argument | Type | Notes |\n|---|---|---|\n| `command` | `string` | Dotted command path under the `obb` namespace (e.g. `equity.price.historical`). |\n| `params` | `dict \\| null` | Keyword arguments forwarded to the command. Defaults to `{}`. |\n| `server_url` | `string \\| null` | When set, dispatches against a remote `openbb-platform-api` server (HTTP). When omitted, falls back to `OPENBB_SERVER_URL`, then in-process local dispatch. |\n\nThe result is a dispatcher response. `ok` reports success, `result` holds the\ncommand's OBBject fields (`results`, `provider`, ...), and `error` carries the\n`type` and `message` of a failure instead of raising:\n\n```json\n{\n    \"id\": null,\n    \"ok\": true,\n    \"result\": {\"results\": [{\"date\": \"2025-01-02\", \"close\": 243.85}], \"provider\": \"yfinance\"},\n    \"error\": null\n}\n```\n\nRemote dispatch unwraps a single-row `results` list to the row itself.\n\n### `openbb_batch_dispatch`\n\nExecute many commands concurrently and return results in input order.\n\n```json\n{\n    \"name\": \"openbb_batch_dispatch\",\n    \"arguments\": {\n        \"requests\": [\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"AAPL\"}, \"id\": \"a\"},\n            {\"command\": \"equity.price.quote\", \"params\": {\"symbol\": \"MSFT\"}, \"id\": \"b\"}\n        ]\n    }\n}\n```\n\nEach entry needs `command` (dotted path); `params` and `id` are optional. The\n`id` is opaque correlation echoed back on the matching response. Failures\nsurface as response objects with `ok=False` and a structured `error` block —\nno exception bubbles up, errors are per-request.\n\n### `openbb_list_commands`\n\nList every command with a one-line description. Takes only the optional\n`server_url`. The response's `result` is a list of `{\"name\", \"description\"}`\nentries. Equivalent to `openbb --list-commands` on the CLI.\n\n### `openbb_describe_command`\n\nReturn one command's parameters and output schema.\n\n```json\n{\n    \"name\": \"openbb_describe_command\",\n    \"arguments\": {\"command\": \"equity.price.historical\", \"provider\": \"yfinance\"}\n}\n```\n\nThe response's `result` has the command `name`, its `parameters` (each with\n`name`, `in`, and `type`, plus `required`, `default`, `choices`, and `help` when\nset), and its `output_schema`. For a command with several providers, pass\n`provider` to get that provider's `parameters` and `output_schema`; without it,\n`result.providers` maps every provider to its own `parameters` and\n`output_schema`. Equivalent to `openbb --describe equity.price.historical`\n(`equity.price.historical:yfinance` for one provider) on the CLI.\n\n### Local vs remote dispatch\n\n| Mode | Trigger | Behavior |\n|---|---|---|\n| Local | No `server_url`, no `OPENBB_SERVER_URL` | `LocalDispatcher` resolves commands against the in-process `obb` namespace. Pays the heavy `import openbb` once at startup. Single-tenant. |\n| Remote | `server_url` arg or `OPENBB_SERVER_URL` env | `HttpDispatcher` proxies commands to a long-running `openbb-platform-api` server. Multi-tenant; the heavy import lives on the server. |\n\nMode is per-call — different requests in the same session can target different\nservers. Dispatchers are cached for the server's lifetime, so the `import openbb`\ncost (local) and the remote server's OpenAPI download are paid once per target.\n\n---\n\n## Calling Data Tools\n\n### Input Parameters\n\nEvery data tool has a JSON Schema describing its input. A typical tool schema:\n\n```json\n{\n    \"type\": \"object\",\n    \"properties\": {\n        \"symbol\": {\n            \"type\": \"string\",\n            \"description\": \"Symbol to get data for.\"\n        },\n        \"provider\": {\n            \"type\": \"string\",\n            \"enum\": [\"fmp\", \"polygon\", \"yfinance\"],\n            \"description\": \"The provider to use for the query.\"\n        },\n        \"start_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"Start date of the data.\"\n        },\n        \"end_date\": {\n            \"anyOf\": [{\"type\": \"string\", \"format\": \"date\"}, {\"type\": \"null\"}],\n            \"description\": \"End date of the data.\"\n        },\n        \"interval\": {\n            \"type\": \"string\",\n            \"default\": \"1d\",\n            \"description\": \"Time interval of the data.\"\n        }\n    },\n    \"required\": [\"symbol\"]\n}\n```\n\nKey rules:\n\n- **`provider`** — present only on endpoints that use the provider interface.\n  When listed, its enum shows available provider sources. If the endpoint has\n  only one provider, you can omit it and the sole provider is used\n  automatically. When multiple providers are available, select one from the\n  enum. Different providers may return different fields or support different\n  parameters. Endpoints that do not use the provider interface (basic GET/POST\n  routes) have no `provider` parameter at all.\n- **`symbol` formatting** — symbols are case-insensitive. Multiple symbols can\n  be comma-separated: `\"AAPL,MSFT,GOOG\"`.\n- **Dates** — always formatted as `YYYY-MM-DD` strings.\n- **Optional parameters** — have `anyOf` with a `null` type or a `default`\n  value. Omit them to use defaults.\n- **Provider-specific parameters** — some parameters are only relevant for\n  certain providers. The schema unions all of them; irrelevant ones are\n  silently ignored.\n- **Valid values** — never guess a value. A parameter with a fixed set of\n  values has an `enum`. When the values differ by provider, or several can be\n  comma-separated, the description lists them (`Valid values by provider:\n  ...`, `Valid values for provider oecd (comma-separate several): ...`). When\n  another tool lists them, the description names it and its arguments\n  (``Get the valid values from the `derivatives_options_strikes` tool with\n  symbol=<the symbol you pass here>.``) — call that tool first.\n\n### Example Tool Call\n\n```json\n{\n    \"name\": \"equity_price_historical\",\n    \"arguments\": {\n        \"symbol\": \"AAPL\",\n        \"provider\": \"fmp\",\n        \"start_date\": \"2025-01-01\",\n        \"end_date\": \"2025-02-01\",\n        \"interval\": \"1d\"\n    }\n}\n```\n\n---\n\n## Understanding the Response\n\nEvery OpenBB tool returns an **OBBject** — a standardized response envelope:\n\n```json\n{\n    \"id\": \"06520558-d54a-7e53-8000-7aafc8a42694\",\n    \"results\": [...],\n    \"provider\": \"fmp\",\n    \"warnings\": null,\n    \"chart\": null,\n    \"extra\": {\n        \"metadata\": {\n            \"arguments\": {...},\n            \"duration\": 565256375,\n            \"route\": \"/equity/price/historical\",\n            \"timestamp\": \"2025-01-15 11:28:57.149548\"\n        }\n    }\n}\n```\n\n### Response Fields\n\n| Field | Type | Description |\n|---|---|---|\n| `id` | `string` | UUID identifying this request |\n| `results` | `list[dict] \\| dict \\| string \\| null` | The actual data. Usually a list of records |\n| `provider` | `string \\| null` | Which provider fulfilled the request |\n| `warnings` | `list[object] \\| null` | Non-fatal warnings from the provider or platform |\n| `chart` | `object \\| null` | Chart artifact if `chart=true` was passed |\n| `extra` | `dict` | Execution metadata and results metadata |\n\n### The `results` Field\n\nThis is the primary data payload. Its structure depends on the endpoint:\n\n**Tabular data** — most common, a list of dictionaries (one per row):\n\n```json\n\"results\": [\n    {\"date\": \"2025-01-02\", \"open\": 150.0, \"high\": 155.0, \"low\": 149.0, \"close\": 153.5, \"volume\": 1000000},\n    {\"date\": \"2025-01-03\", \"open\": 153.0, \"high\": 157.0, \"low\": 152.0, \"close\": 156.2, \"volume\": 1200000}\n]\n```\n\n**Single record** — some endpoints return a single dict:\n\n```json\n\"results\": {\"symbol\": \"AAPL\", \"price\": 185.50, \"change\": 2.30, \"volume\": 45000000}\n```\n\n**Empty results** — when no data is available:\n\n```json\n\"results\": null\n```\n\nor\n\n```json\n\"results\": []\n```\n\n**Field names vary by provider** — different providers may return different\ncolumns for the same endpoint. Always check the keys in the returned records.\n\n### The `warnings` Field\n\nWarnings are non-fatal issues that occurred during execution:\n\n```json\n\"warnings\": [\n    {\n        \"category\": \"OpenBBWarning\",\n        \"message\": \"Parameter 'source' is not supported by fmp. Available for: intrinio.\"\n    }\n]\n```\n\nCommon warning scenarios:\n- Unknown parameters silently ignored by the provider\n- Partial data returned (fewer rows than requested)\n- Provider-specific data quality notes\n\nWhen `warnings` is `null`, no warnings were generated.\n\n### The `extra` Field\n\nContains execution metadata and optional results metadata:\n\n```json\n\"extra\": {\n    \"metadata\": {\n        \"arguments\": {\n            \"provider_choices\": {\"provider\": \"fmp\"},\n            \"standard_params\": {\"symbol\": \"AAPL\", \"start_date\": \"2025-01-01\"},\n            \"extra_params\": {}\n        },\n        \"duration\": 565256375,\n        \"route\": \"/equity/price/historical\",\n        \"timestamp\": \"2025-01-15 11:28:57.149548\"\n    },\n    \"results_metadata\": {\n        \"...provider-specific metadata...\"\n    }\n}\n```\n\n**`metadata`** — always present (unless disabled):\n- `arguments` — the exact parameters used, split into provider choices,\n  standard params, and extra (provider-specific) params\n- `duration` — nanoseconds the request took\n- `route` — the API endpoint path\n- `timestamp` — when the request was made\n\n**`results_metadata`** — present when the provider returns contextual\ninformation about the data (e.g., FRED series metadata, CBOE options\nmetadata). Contents vary by endpoint and provider.\n\n### The `chart` Field\n\nWhen a tool is called with `chart: true` (where supported), the server\nreplaces the Plotly figure with an OpenBB Workspace artifact:\n\n```json\n\"chart\": {\n    \"type\": \"chart\",\n    \"name\": \"AAPL Close\",\n    \"description\": \"Output of the equity_price_historical tool.\",\n    \"uuid\": \"5b0e1c52-6f3e-4a51-9d6f-0f1b6a2f4c11\",\n    \"content\": [{\"date\": \"2025-01-02\", \"close\": 243.85}],\n    \"chart_params\": {\"chartType\": \"line\", \"xKey\": \"date\", \"yKey\": [\"close\"]}\n}\n```\n\n`content` holds the rows the chart is drawn from and `chart_params` says how:\n`chartType` is `line`, `bar`, or `scatter` with `xKey` and `yKey`, or `pie`\nor `donut` with `angleKey` and `calloutLabelKey`. Figures that cannot be\ndrawn that way (surfaces, candlesticks, heatmaps) come back as a `table`\nartifact of their data, without `chart_params`. Tools that return a figure\ndirectly return the artifact as their whole output. Present the artifact\nas it is; do not convert it or re-plot it. Not all endpoints support\ncharting.\n\n---\n\n## Provider Selection\n\n### How Providers Work\n\nEach data endpoint can have multiple provider sources (e.g., FMP, Yahoo Finance,\nPolygon). Providers differ in:\n\n- **Available parameters** — some providers offer extra filtering or options\n- **Returned fields** — column names and data granularity may differ\n- **Rate limits and authentication** — some providers require API keys\n- **Data coverage** — geographic markets, date ranges, asset types\n\n### Choosing a Provider\n\nWhen a tool's input schema includes `provider`, its enum lists all installed\nprovider sources for that endpoint. If there is only one provider, the\nparameter can be omitted — the sole provider is selected automatically.\nWhen multiple providers are available, pick one based on:\n\n1. **Check the enum** — only listed options work\n2. **Consider the data need** — different providers may have different fields\n3. **API key requirements** — some providers need credentials configured in\n   `~/.openbb_platform/user_settings.json`\n\nEndpoints that do not use the provider interface (basic GET/POST routes added\nvia `router.command(methods=[\"GET\"])` or raw FastAPI) have no `provider`\nparameter.\n\nIf a provider fails due to missing credentials, the error message will indicate\nan authentication issue.\n\n### Provider-Specific Parameters\n\nSome parameters only apply to certain providers. For example, the `source`\nparameter might only be available with the `intrinio` provider. Passing it to\n`fmp` generates a warning but does not cause an error.\n\n---\n\n## Working With Prompts\n\nThe server includes a prompt system for accessing documentation, usage guides,\nand analysis frameworks.\n\n### List Available Prompts\n\nCall `list_prompts` (no arguments):\n\n```json\n[\n    {\"name\": \"system_prompt\", \"description\": \"System prompt with guidance...\", \"arguments\": []},\n    {\"name\": \"analyze_stock\", \"description\": \"Analyze a stock.\", \"arguments\": [\n        {\"name\": \"symbol\", \"description\": \"Ticker.\", \"required\": true},\n        {\"name\": \"focus\", \"description\": \"Area.\", \"required\": false}\n    ]}\n]\n```\n\n### Get a Prompt\n\nCall `get_prompt` with the prompt `name` and any `arguments`:\n\n```json\n// Input\n{\"name\": \"analyze_stock\", \"arguments\": {\"symbol\": \"AAPL\"}}\n\n// Output - the rendered prompt\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"Analyze AAPL focusing on fundamentals.\"}\n    ]\n}\n```\n\nPass every argument value as a string (`{\"years\": \"10\"}`, not `{\"years\": 10}`);\nMCP prompt arguments are strings. Prompts without arguments return their\ncontent as-is. Omitted optional arguments fall back to their defaults, and a\nmissing required argument is an error. Skills are resources, not prompts; see\n[Working With Skills](#working-with-skills).\n\n### Prompt Categories by Tag\n\n| Tag | Source | Description |\n|---|---|---|\n| `system` | System prompt file | Server-wide context and instructions |\n| `server` | Server prompts JSON | Reusable analysis frameworks |\n| `route-specific` | Inline on API routes | Endpoint usage guides |\n\n---\n\n## Error Handling\n\n### Error Types\n\n| Scenario | What Happens |\n|---|---|\n| **Invalid parameters** | Error with HTTP 422 details explaining which parameter failed validation |\n| **Missing required parameter** | Error with HTTP 422 indicating the missing field |\n| **Provider authentication failure** | Error with HTTP 401/403 indicating credentials are missing or invalid |\n| **Provider rate limit** | Error with HTTP 429 or provider-specific rate limit message |\n| **No data available** | Successful response with `results: null` or `results: []` |\n| **Tool hidden by discovery** | Tool is not in the tool list; find it with `search_tools` and run it with `call_tool` |\n| **Tool outside the served categories** | \"Unknown tool\" error, by name, through `call_tool`, and through `run_pipeline` |\n| **Unknown tool name** | Standard MCP protocol error |\n| **Category not found** (discovery) | Error listing available categories |\n| **Connection failure** | Error with \"Request error: ...\" |\n\n### Interpreting Empty Results\n\nA response with `results: null` or `results: []` is **not an error** — it\nmeans the provider had no data matching the query. Common causes:\n\n- Date range with no trading days\n- Symbol not covered by the selected provider\n- Data not yet available for the requested period\n\nTry a different provider, adjust the date range, or verify the symbol format.\n\n### Reading Validation Errors\n\nValidation errors (HTTP 422) include detail about what went wrong:\n\n```\nHTTP error 422: Unprocessable Entity - {\"detail\": [{\"loc\": [\"query\", \"symbol\"], \"msg\": \"field required\", \"type\": \"value_error.missing\"}]}\n```\n\nThe `loc` field shows which parameter failed, and `msg` explains why.\n\n---\n\n## Practical Patterns\n\n### Fetching Time Series Data\n\n1. With discovery enabled, find the tool: `search_tools({\"query\": \"historical stock prices\"})`\n2. Call with date range:\n   ```json\n   {\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"start_date\": \"2025-01-01\", \"end_date\": \"2025-02-01\"}\n   ```\n3. Read `results` — each record has `date`, `open`, `high`, `low`, `close`,\n   `volume` (field names depend on provider)\n\n### Comparing Multiple Symbols\n\nPass comma-separated symbols:\n\n```json\n{\"symbol\": \"AAPL,MSFT,GOOG\", \"provider\": \"fmp\"}\n```\n\nResults will contain records for all symbols. Filter by the `symbol` field in\neach record if present, or by the ordering pattern.\n\n### Chaining Tool Calls\n\nUse the output of one tool as input to another:\n\n1. Get peers: `equity_compare_peers({\"symbol\": \"AAPL\", \"provider\": \"fmp\"})`\n2. Extract symbols from `results`\n3. Get quotes: `equity_price_quote({\"symbol\": \"AAPL,PEER1,PEER2\", \"provider\": \"fmp\"})`\n\nWhen a later tool needs the earlier tool's rows rather than a few values picked\nfrom them, use `run_pipeline` (below) so the rows never pass through the\nconversation.\n\n### Running Data-Processing Tools With `run_pipeline`\n\nTechnical, quantitative, and econometrics tools (`technical_rsi`,\n`technical_macd`, ...) take the rows to analyze as a `data` argument. Fetch and\nanalyze in one `run_pipeline` call instead of copying a price history into\ntheir arguments:\n\n```json\n{\n    \"steps\": [\n        {\n            \"id\": \"prices\",\n            \"tool\": \"equity_price_historical\",\n            \"arguments\": {\"symbol\": \"AAPL\", \"provider\": \"yfinance\", \"start_date\": \"2025-01-01\"}\n        },\n        {\"id\": \"rsi\", \"tool\": \"technical_rsi\", \"arguments\": {\"length\": 14}, \"inputs\": {\"data\": \"prices\"}},\n        {\"id\": \"macd\", \"tool\": \"technical_macd\", \"inputs\": {\"data\": \"prices\"}}\n    ],\n    \"outputs\": [\"rsi\", \"macd\"]\n}\n```\n\n- Each step has a `tool`, its `arguments`, an optional `id`, and `inputs`, which\n  maps an argument name to the `id` of an earlier step. That step's `results`\n  fill the argument (its whole output when it has no `results`).\n- A step without an `id` is referred to by its position (`\"0\"`, `\"1\"`, ...).\n- `outputs` lists the steps to return; by default only the last step comes back.\n- The request is validated before any step runs. A failing step is reported as\n  `Step '<id>' (<tool>) failed: ...`.\n- With tool discovery, use `search_tools` to find the tools and their\n  parameters; `run_pipeline` can call tools hidden by discovery by name, but\n  not tools outside the categories the server serves.\n\n### Checking Data Coverage\n\nWhen unsure what providers are available for an endpoint, look at the tool's\ninput schema — the `provider` parameter's `enum` lists all installed options.\n\n### Using Charts\n\nPass `chart: true` to get a pre-built visualization:\n\n```json\n{\"symbol\": \"AAPL\", \"provider\": \"fmp\", \"chart\": true}\n```\n\nThe `chart` field in the response holds a `chart` or `table` artifact (see\nThe `chart` Field). In `run_pipeline`, a step that returns an artifact passes\nits `content` rows to later steps.\n\n---\n\n## User Settings and Defaults\n\nThe server reads user settings from `~/.openbb_platform/user_settings.json`:\n\n### API Keys\n\nProvider credentials are stored under `credentials`:\n\n```json\n{\n    \"credentials\": {\n        \"fmp_api_key\": \"YOUR_KEY\",\n        \"polygon_api_key\": \"YOUR_KEY\"\n    }\n}\n```\n\nWithout the required API key, calls to that provider will fail with an\nauthentication error.\n\n### Default Provider\n\nSet a default provider per endpoint so it is preselected:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\"provider\": \"yfinance\"},\n            \"/economy/cpi\": {\"provider\": \"oecd\"}\n        }\n    }\n}\n```\n\nWhen multiple providers are available, this determines the default selection\nif the parameter is omitted.\n\n### Default Parameters\n\nIndividual parameters can be defaulted so they are applied when not explicitly\npassed:\n\n```json\n{\n    \"defaults\": {\n        \"commands\": {\n            \"/equity/price/historical\": {\n                \"provider\": \"fmp\",\n                \"chart\": true,\n                \"chart_params\": {\n                    \"heikin_ashi\": true,\n                    \"indicators\": {\"sma\": {\"length\": [21, 50]}}\n                }\n            }\n        }\n    }\n}\n```\n\n### Output Preferences\n\nThe `output_type` preference controls how the Python Interface returns data.\nFor MCP, the server always returns the full OBBject JSON regardless of this\nsetting, but it is relevant for the Python Interface:\n\n| Output Type | Description |\n|---|---|\n| `OBBject` | Full response object (default) |\n| `dataframe` | Pandas DataFrame |\n| `numpy` | NumPy array |\n| `dict` | Python dictionary |\n| `polars` | Polars DataFrame |\n| `llm` | JSON-encoded string of results only |\n| `chart` | Chart object |\n\n### LLM Mode\n\nSetting `output_type` to `\"llm\"` in the Python Interface strips everything\nexcept `results` and returns it as a JSON string. This is optimized for token\nefficiency in LLM frameworks. In the REST API / MCP context, the full OBBject\nis always returned.\n\n---\n\n## Working With Skills\n\nSkills are MCP resources exposed at `skill://<name>/SKILL.md` URIs. Discover\nand read them via the standard MCP resource methods.\n\n### Discover Available Skills\n\nCall `list_resources()` (no arguments):\n\n```json\n[\n    {\"uri\": \"skill://develop_extension/SKILL.md\", \"name\": \"develop_extension\"},\n    {\"uri\": \"skill://build_workspace_app/SKILL.md\", \"name\": \"build_workspace_app\"},\n    {\"uri\": \"skill://configure_mcp_server/SKILL.md\", \"name\": \"configure_mcp_server\"},\n    {\"uri\": \"skill://work_with_server/SKILL.md\", \"name\": \"work_with_server\"},\n    {\"uri\": \"skill://use_openbb_cli/SKILL.md\", \"name\": \"use_openbb_cli\"}\n]\n```\n\n### Read a Skill\n\nCall `read_resource()` with the skill URI:\n\n```json\n// Input\n{\"uri\": \"skill://develop_extension/SKILL.md\"}\n\n// Output — full Markdown content of the skill guide\n```\n\n### Supporting Files\n\nSkill directories can contain additional supporting files (e.g. templates,\nexamples). Reference the skill manifest at `skill://<name>/_manifest` to\ndiscover any supporting files packaged alongside the main `SKILL.md`.\n\n---\n\n## Quick Reference\n\n### Admin Tools (Discovery)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `available_categories` | *(none)* | List of categories with subcategories and tool counts |\n| `available_tools` | `category`, `subcategory?` | List of tools with names and descriptions |\n| `search_tools` | `query` | Full definitions of the matching tools |\n| `call_tool` | `name`, `arguments?` | The tool's result |\n\n### Pipeline Tool (Always Listed)\n\n| Tool | Input | Returns |\n|---|---|---|\n| `run_pipeline` | `steps`, `outputs?` | Outputs of the selected steps, keyed by step id |\n\n### OBBject Response Structure\n\n| Field | Always Present | Content |\n|---|---|---|\n| `id` | Yes | Request UUID |\n| `results` | Yes | Data payload (list, dict, string, or null) |\n| `provider` | Yes | Provider name or null |\n| `warnings` | Yes | Warning list or null |\n| `chart` | Yes | Chart artifact or null |\n| `extra` | Yes | Metadata dict (may be empty) |\n\n<!-- File: openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Assets Skill\n\nPath: `openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/openbb_platform_api/assets/default_apps.json)\n\n<!-- File: openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_openapi.json)\n\n<!-- File: openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Utils Skill\n\nPath: `openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/platform_api/tests/utils/mock_widgets.json)\n\n<!-- File: openbb_platform/extensions/technical/docs/api-reference.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/api-reference.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/api-reference.md)\n\n<!-- File: openbb_platform/extensions/technical/docs/getting-started.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Docs Skill\n\nPath: `openbb_platform/extensions/technical/docs/getting-started.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/getting-started.md)\n\n<!-- File: openbb_platform/extensions/technical/docs/user-guides/01-indicators.md (Tokens: ~300 | Category: anthropic-skill) -->\n# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/01-indicators.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/01-indicators.md)\n\n<!-- File: openbb_platform/extensions/technical/docs/user-guides/02-signals.md (Tokens: ~300 | Category: anthropic-skill) -->\n# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/02-signals.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/02-signals.md)\n\n<!-- File: openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md (Tokens: ~300 | Category: anthropic-skill) -->\n# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/03-multi-symbol.md)\n\n<!-- File: openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md (Tokens: ~300 | Category: anthropic-skill) -->\n# User-guides Skill\n\nPath: `openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/technical/docs/user-guides/04-composition-and-discovery.md)\n\n<!-- File: openbb_platform/extensions/commodity/README.md (Tokens: ~68 | Category: plugin-manifest) -->\n# Commodity Extension for OpenBB Platform\n\nThis extension provides a set of commands for commodity-related data.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-commodity\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n\n<!-- File: openbb_platform/extensions/crypto/README.md (Tokens: ~68 | Category: plugin-manifest) -->\n# Crypto data extension for OpenBB Platform\n\nThis extension provides a set of commands for crypto data retrieval.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-crypto\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n\n<!-- File: openbb_platform/extensions/currency/README.md (Tokens: ~66 | Category: plugin-manifest) -->\n# OpenBB Currency Extension\n\nThis extension provides currency exchange related data for the OpenBB Platform.\n\n## Installation\n\nTo install the extension, run the following command in this folder:\n\n```bash\npip install openbb-currency\n```\n\nDocumentation available [here](https://docs.openbb.co/platform/developer_guide/contributing).\n\n<!-- File: openbb_platform/extensions/derivatives/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# derivatives Documentation\n\nPath: `openbb_platform/extensions/derivatives/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/derivatives/README.md)\n\n<!-- File: openbb_platform/extensions/devtools/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# devtools Documentation\n\nPath: `openbb_platform/extensions/devtools/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/devtools/README.md)\n\n<!-- File: openbb_platform/extensions/econometrics/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# econometrics Documentation\n\nPath: `openbb_platform/extensions/econometrics/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/econometrics/README.md)\n\n<!-- File: openbb_platform/extensions/economy/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# economy Documentation\n\nPath: `openbb_platform/extensions/economy/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/economy/README.md)\n\n<!-- File: openbb_platform/extensions/equity/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# equity Documentation\n\nPath: `openbb_platform/extensions/equity/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/equity/README.md)\n\n<!-- File: openbb_platform/extensions/etf/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# etf Documentation\n\nPath: `openbb_platform/extensions/etf/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/etf/README.md)\n\n<!-- File: openbb_platform/extensions/fixedincome/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# fixedincome Documentation\n\nPath: `openbb_platform/extensions/fixedincome/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/fixedincome/README.md)\n\n<!-- File: openbb_platform/extensions/index/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# index Documentation\n\nPath: `openbb_platform/extensions/index/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/index/README.md)\n\n<!-- File: openbb_platform/extensions/mcp_server/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# mcp_server Documentation\n\nPath: `openbb_platform/extensions/mcp_server/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/mcp_server/README.md)\n\n<!-- File: openbb_platform/extensions/news/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# news Documentation\n\nPath: `openbb_platform/extensions/news/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/OpenBB-finance/OpenBB/HEAD/openbb_platform/extensions/news/README.md)\n\n<!-- ... +6 more modular skills available via /api/skills/OpenBB-finance/OpenBB?path={path} -->\n</yakaai_skills>"}