Asset Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Asset Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Read-only Python MCP server for aggregating personal assets across Binance, OKX, moomoo OpenD, Longbridge, IBKR, on-chain wallets, and manually configured accounts.
The server exposes normalized asset data to any MCP-compatible AI client. It does not trade, transfer, withdraw, or automate bank/Alipay access.
Arbitrum, Base, and Optimism.
>=3.10.accounts.
uv is only required for local development; end users can install withpip.
# 1. Install from PyPI
pip install asset-mcp
# 2. Create the starter config (~/.config/asset-mcp/config.local.yaml)
asset-mcp init
# 3. Edit the config: fill in credentials for at least one account
# and set its `enabled: true`. See "Configure" below for per-provider examples.
vim ~/.config/asset-mcp/config.local.yaml
# 4. Register the server with your MCP client
claude mcp add asset-mcp -- asset-mcp
# or
codex mcp add asset-mcp -- asset-mcp
# 5. Verify: ask your client to call the `health_check_sources` tool.
# Every enabled account should return `ok: true`.
The smallest working setup needs no API keys — a single manual cash account is enough to confirm the install:
baseCurrency: USD
rates:
USD: 1.0
CNY: 0.14
manual:
accounts:
- id: cash
label: Cash on hand
enabled: true
category: cash
assets:
- symbol: CNY
quantity: 10000
currency: CNYInstall from PyPI:
pip install asset-mcpThe base installation works with Binance, OKX, IBKR, on-chain wallets, and manual accounts. The moomoo and Longbridge SDKs are optional extras — install them only when needed:
pip install "asset-mcp[moomoo]"
pip install "asset-mcp[longbridge]"
pip install "asset-mcp[moomoo,longbridge]"Existing config files are never overwritten by asset-mcp init unless you pass --force:
asset-mcp init --forceFor local development, clone the repository and install dependencies with uv:
curl -LsSf https://astral.sh/uv/install.sh | sh # if uv is not installed
uv sync --extra dev --extra moomoo --extra longbridge
uv run asset-mcp init --path config.local.yamlIf you do not need moomoo or Longbridge support, omit those optional extras:
uv sync --extra devasset-mcp init writes the starter template to ~/.config/asset-mcp/config.local.yaml. Pass --path <file> to write the template anywhere other than the default location.
If you installed from PyPI, asset-mcp init writes the same template the repository ships as config.example.yaml. Refer to that file on GitHub (see Links below) for the complete set of supported fields and inline comments.
Keep real API keys and personal balances only in your local config file — never commit it. Each account id must be unique and stable; this id appears in MCP responses and is used for filtering.
Create a read-only API key in the Binance API management console. Keep Withdraw, Trade, Margin, and Futures permissions disabled.
exchanges:
binance:
accounts:
- id: binance-main
label: Binance Main
enabled: true
apiKey: "replace-with-read-only-key"
apiSecret: "replace-with-read-only-secret"
# environment: production # optionalCreate an OKX API key with Read permission only. The passphrase is the one you chose when generating the key.
exchanges:
okx:
accounts:
- id: okx-main
label: OKX Main
enabled: true
apiKey: "replace-with-read-only-key"
apiSecret: "replace-with-read-only-secret"
passphrase: "replace-with-passphrase"
# domain: https://www.okx.com # optionalInstall and start the Futu/moomoo OpenD client and log in before launching asset-mcp. Install the optional SDK with pip install "asset-mcp[moomoo]".
brokers:
moomoo:
accounts:
- id: moomoo-us
label: moomoo US
enabled: true
host: 127.0.0.1
port: 11111
trdMarket: US # US / HK / CN / SG
securityFirm: FUTUSECURITIES # or FUTUINC / MOOMOOSG / FUTUSG
# accountId: 12345678 # optional; only needed for multi-account setupsCreate a Longbridge OpenAPI application with read-only scopes and copy the App Key, App Secret, and Access Token. Install the optional SDK with pip install "asset-mcp[longbridge]".
brokers:
longbridge:
accounts:
- id: longbridge-main
label: Longbridge Main
enabled: true
appKey: "replace-with-app-key"
appSecret: "replace-with-app-secret"
accessToken: "replace-with-access-token"IBKR support uses Flex Web Service. In IBKR Client Portal, enable Flex Web Service, create an Activity Flex Query that outputs XML, and include at least Cash Report and Open Positions. Then configure the generated token and query ID under brokers.ibkr.accounts:
brokers:
ibkr:
accounts:
- id: ibkr-main
label: IBKR Main
enabled: true
token: "replace-with-flex-web-service-token"
queryId: "replace-with-flex-query-id"
baseUrl: https://ndcdyn.interactivebrokers.com/AccountManagement/FlexWebService
accountId: U1234567
version: 3
statementRetries: 3
statementRetryDelaySeconds: 5Leave accountId empty to import every account included in the Flex Query, or set it to keep only one account from a multi-account report. Flex Activity Statement data is report data rather than a real-time feed, so use it for periodic net-worth snapshots instead of active polling.
Configure public wallet addresses under onchain.accounts. The provider discovers assets held by each address. EVM chains query native balances plus a built-in mainstream ERC-20 token list, and any ERC-20 contracts explicitly configured under the address. Solana uses getTokenAccountsByOwner for SPL token accounts. Bitcoin and TRON use public address APIs. No private keys, seed phrases, trading, transfer, or approval operations are supported.
Supported built-in chains:
bitcoin / btcethereum / eth / 1solana / sol / 501bsc / bnb / 56tron / trxpolygon / matic / 137avalanche / avax / 43114arbitrum / 42161base / 8453optimism / op / 10Example on-chain wallet address config:
onchain:
accounts:
- id: onchain-wallet
label: On-chain Wallet
enabled: true
addresses:
- chain: bitcoin
address: "bc1..."
- chain: ethereum
address: "0x..."
tokens:
- symbol: CUSTOM
name: Custom ERC-20 Token
contractAddress: "0x..."
decimals: 18
coinGeckoId: ""
- chain: solana
address: "..."
- chain: bsc
address: "0x..."Each address can override rpcUrl or explorerApiUrl if you prefer your own node or paid provider over the default public endpoints. Native token prices are read from rates first, then CoinGecko. Solana SPL tokens without a Jupiter price are still returned with zero USD value.
For configured ERC-20 tokens, symbol, contractAddress, and decimals are required. name is optional. Add coinGeckoId for live USD pricing, or provide the token price under rates; if no price is available the token is still returned with zero USD value. If a Covalent indexer is enabled, configured ERC-20 contracts are queried in addition to the indexed inventory, skipping contracts already returned by the indexer.
For an Etherscan-like full token inventory instead of the built-in mainstream EVM token list, configure an optional indexer:
onchain:
indexer:
provider: covalent
apiKey: "replace-with-covalent-api-key"USD value for each manual asset is computed as quantity * rates[currency]. On-chain native tokens read rates first and fall back to CoinGecko.
rates:
USD: 1.0
CNY: 0.14
HKD: 0.128
manual:
accounts:
- id: alipay
label: Alipay
enabled: true
category: cash
assets:
- symbol: CNY
quantity: 25000
currency: CNY
- id: home
label: Primary residence
enabled: true
category: property
assets:
- symbol: USD
quantity: 500000
currency: USDasset-mcpBy default the server reads config in this order:
ASSET_MCP_CONFIGconfig.local.yaml in the current directory~/.config/asset-mcp/config.local.yamlTo use another path:
ASSET_MCP_CONFIG=/path/to/config.local.yaml asset-mcpUse stdio transport. Example client configuration:
{
"mcpServers": {
"asset-mcp": {
"command": "asset-mcp"
}
}
}If you prefer a non-default config path, pass an absolute path through ASSET_MCP_CONFIG.
get_net_worth: total net worth and grouped summaries.get_assets: normalized asset rows with optional filters.get_asset_dashboard_data: chart-ready grouping data.health_check_sources: per-account configuration and connection status.Provider calls are isolated with per-provider timeouts. When querying multiple providers, a slow or unavailable source is reported in providerErrors and the tool returns the data that was available with partial: true. SDK-backed providers that can block or emit native stdout run in a subprocess, so timed-out requests can be terminated without leaving the MCP server blocked or corrupting stdio transport.
providerErrors entries include stable machine-readable codes:
| code | Meaning | Retryable |
|---|---|---|
provider_timeout | The provider did not return before the per-provider timeout. | Yes |
provider_exception | The provider raised an exception. Raw errors are sanitized. | No |
provider_exited | A subprocess-isolated provider exited without returning a result. | Yes |
Example prompt after connecting the MCP server:
Use asset-mcp to summarize my net worth by account and asset category.To verify your setup end-to-end, ask the MCP client to call health_check_sources. Every enabled account should return ok: true; anything else points at a misconfiguration or unreachable provider.
Confirm the config file is in one of the three resolved locations (ASSET_MCP_CONFIG, the current directory, or ~/.config/asset-mcp/config.local.yaml) and that each account has enabled: true.
is not running, not logged in, or the port is blocked. Verify the daemon with nc -z 127.0.0.1 11111 and confirm you installed the SDK extras: pip install "asset-mcp[moomoo]".
more providers failed. provider_timeout is safe to retry; provider_exception usually means an expired or wrong credential; provider_exited indicates a subprocess-isolated provider crashed (often moomoo/Longbridge SDK issues).
provider likely wrote a banner or warning to stdout and corrupted the JSON-RPC stream. Run ASSET_MCP_CONFIG=config.local.yaml asset-mcp in a terminal and look for any unexpected output before the first JSON frame.
Verify the chain value matches one of the supported aliases, and that any custom ERC-20 entry has all three of symbol, contractAddress, and decimals.
config.local.yaml.possible.
scrape or automate those services.
private keys or seed phrases in the config.
src/asset_mcp/server.py: MCP stdio entry point and tool definitions.src/asset_mcp/service.py: application service orchestration.src/asset_mcp/domain/: normalized models plus aggregation and filteringlogic.
src/asset_mcp/config/: config models, YAML parsing, validation, andsecret redaction.
src/asset_mcp/providers/registry.py: source-to-provider factory registry.src/asset_mcp/providers/exchanges/: crypto exchange providers, currentlyBinance and OKX.
src/asset_mcp/providers/brokerages/: brokerage providers, currentlymoomoo, Longbridge, and IBKR.
src/asset_mcp/providers/onchain/: on-chain wallet provider andchain-specific package boundaries.
src/asset_mcp/providers/manual/: manually configured asset provider.tests/config/, tests/domain/, and tests/providers/: regression testsmatching the source layout.
Compatibility re-export modules are kept for older imports such as asset_mcp.models, asset_mcp.aggregation, and asset_mcp.providers.binance. New code should prefer the package paths above.
Install development dependencies before running tests:
uv sync --extra devThe regression tests use fake provider clients and inline sample config data. They do not require config.local.yaml, real API keys, running OpenD, broker SDK sessions, or outbound network access.
Run tests:
uv run pytestRun a syntax check with Python's compiler:
uv run python -m compileall src testsRun the server locally against the example config:
ASSET_MCP_CONFIG=config.example.yaml uv run asset-mcpBuild local package artifacts:
uv buildThis writes wheel and source distribution files under dist/.
pyproject.toml and the fallback__version__ value in src/asset_mcp/__init__.py.
uv run python -m compileall src tests and uv run pytest.uv build and inspect the source distribution for local-only filesbefore publishing.
the final artifacts.
src/asset_mcp/providers/exchanges/<platform>/.src/asset_mcp/providers/brokerages/<platform>/.
src/asset_mcp/providers/registry.py.src/asset_mcp/config/ when theprovider needs new YAML fields.
tests/providers/.intentionally change the public contract and update tests and docs together.
The MCP server uses stdio transport, so stdout is reserved for JSON-RPC protocol frames. Any banner, warning, permission table, progress line, or native SDK log written to stdout can corrupt the MCP stream and surface in clients as Transport closed.
When adding a new broker or exchange provider:
asset_mcp.providers.stdio.redirect_sdk_stdout().
print() and write directly to file descriptor 1from native code or background threads; contextlib.redirect_stdout() alone is not enough.
endpoints often emit permission tables even when account-balance endpoints are quiet.
capfd and os.write(1, ...) to proveprovider calls leave stdout empty.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.