Skip to main content
Glama
nickp27

Webull AU MCP

by nickp27
README.md
# Webull AU OpenAPI MCP

A slim MCP server for the **Webull Australia** OpenAPI. It exposes exactly what
the AU platform supports — **US stock and ETF** market data and trading — and
nothing else. Region is hardcoded to `au`; there is no multi-region, options,
futures, crypto, event-contract, combo, strategy or algo code.

Built on `fastmcp` and the official `webull-openapi-python-sdk` (≥ 2.0.12, which
is the first line to bundle AU production endpoints).

> For reference only; not investment advice. Trading involves risk. Review every
> order before confirming.

## Install

```bash
cd webull-au-mcp
uv sync            # or: pip install -e .
```

## Configure

```bash
webull-au-mcp init          # writes .env
# edit .env → set WEBULL_APP_KEY / WEBULL_APP_SECRET
webull-au-mcp status        # sanity-check config
```

For a zero-setup trial, use a public **UAT** test account from the
[AU SDK docs](https://developer.webull.com.au/apis/docs/sdk) (no application, no 2FA):

```
WEBULL_ENVIRONMENT=uat
WEBULL_APP_KEY=c8d29a52a41c03cb76ebe1f680bada3c
WEBULL_APP_SECRET=40424c891fca1aa68c0aeef862de2ee7
WEBULL_ACCOUNT_ID=1234113618547593216
```

### Configuration

| Variable | Description | Default |
| --- | --- | --- |
| `WEBULL_APP_KEY` | App key (required) | — |
| `WEBULL_APP_SECRET` | App secret (required) | — |
| `WEBULL_ENVIRONMENT` | `uat` (sandbox, no 2FA) or `prod` (live) | `uat` |
| `WEBULL_ACCOUNT_ID` | Default account for tools that need one | — |
| `WEBULL_TOOLSETS` | `account`, `market-data`, `instrument`, `trading` (comma-sep) | `account,market-data,instrument` |
| `WEBULL_MAX_ORDER_NOTIONAL_USD` | Max single-order value (USD) | `10000` |
| `WEBULL_MAX_ORDER_QUANTITY` | Max single-order share quantity | `1000` |
| `WEBULL_SYMBOL_WHITELIST` | Allowed symbols (comma-sep); empty = any | — |
| `WEBULL_TOKEN_DIR` | 2FA token store directory | `./conf/` |
| `WEBULL_AUDIT_LOG_FILE` | Append order audit records here | stderr only |
| `WEBULL_LOG_LEVEL` | SDK log level | `WARNING` |

**Trading is off by default.** The default toolset is read-only. Add `trading`
to `WEBULL_TOOLSETS` to enable order placement, ideally alongside a tight
`WEBULL_SYMBOL_WHITELIST` and notional/quantity caps.

## Authenticate (prod only)

UAT test tokens need no 2FA. For a live account:

```bash
webull-au-mcp auth      # approve the request in your Webull app
webull-au-mcp serve
```

## Client configuration

```json
{
  "mcpServers": {
    "webull-au": {
      "command": "uvx",
      "args": ["--from", "/path/to/webull-au-mcp", "webull-au-mcp", "serve"],
      "env": {
        "WEBULL_APP_KEY": "your_app_key",
        "WEBULL_APP_SECRET": "your_app_secret",
        "WEBULL_ENVIRONMENT": "prod",
        "WEBULL_TOOLSETS": "account,market-data,instrument,trading",
        "WEBULL_SYMBOL_WHITELIST": "AAPL,MSFT,VOO",
        "WEBULL_MAX_ORDER_NOTIONAL_USD": "5000"
      }
    }
  }
}
```

Pass credentials via the client's `env` field rather than a `.env` file in your
workspace, so the model can't read them.

## Docker

The image runs the server over **HTTP transport** (a long-running networked
endpoint at `http://<host>:8000/mcp`), which suits a homelab far better than
stdio.

```bash
cp .env.example .env      # fill in credentials
docker compose up -d --build
```

For a **prod** account, cache the 2FA token once (interactive) before starting —
the token persists in the `./conf` volume:

```bash
docker compose run --rm webull-au-mcp auth
docker compose up -d
```

Point an HTTP-capable MCP client at `http://<homelab-ip>:8000/mcp`. For a local
**stdio** client instead of the networked service, run the image directly:

```bash
docker run -i --rm --env-file .env -e WEBULL_MCP_TRANSPORT=stdio webull-au-mcp:latest
```

| Variable | Description | Default (image) |
| --- | --- | --- |
| `WEBULL_MCP_TRANSPORT` | `stdio`, `http`, `sse`, `streamable-http` | `http` |
| `WEBULL_MCP_HOST` | Bind address (http transports) | `0.0.0.0` |
| `WEBULL_MCP_PORT` | Bind port (http transports) | `8000` |
| `WEBULL_MCP_PATH` | MCP endpoint path (http transports) | `/mcp` |

## Tools

**Account** (`account`): `get_account_list`, `get_account_balance`, `get_account_positions`

**Instrument** (`instrument`): `get_instrument`

**Market data** (`market-data`): `get_stock_snapshot`, `get_stock_quotes`,
`get_stock_tick`, `get_stock_bars`

**Trading** (`trading`): `preview_stock_order`, `place_stock_order`,
`replace_stock_order`, `cancel_order`, `get_open_orders`, `get_order_history`,
`get_order_detail`

Order tools hardcode the AU invariants (`market=US`, `instrument_type=EQUITY`,
`entrust_type=QTY`, `combo_type=NORMAL`) and auto-generate the `client_order_id`,
so the model only supplies symbol, side, quantity, order type and price. Every
order passes quantity, notional and whitelist guards before submission; rejected
orders never reach the broker. `place_stock_order` returns the `client_order_id`
you need for `replace`/`cancel`/`get_order_detail`.

Order types: `MARKET`, `LIMIT`, `STOP_LOSS`, `STOP_LOSS_LIMIT`. Time in force:
`DAY`, `GTC`.

## CLI

```
webull-au-mcp init [--env-file PATH] [--force]
webull-au-mcp auth [--env-file PATH]
webull-au-mcp serve [--env-file PATH]
webull-au-mcp status [--env-file PATH]
webull-au-mcp tools [--env-file PATH]
```

## AU-specific notes

- **SDK ≥ 2.0.12 is required.** Earlier releases (including the 2.0.5 the
  multi-region upstream pins) only bundle `us`/`hk`/`jp` endpoints, so an `au`
  client fails region resolution. UAT hosts are injected explicitly by this
  server regardless.
- The upstream server also registers screener/watchlist/fundamental tools. The
  AU API reference do