OpenWealth MCP
Three FastMCP servers that expose the OpenWealth APIs as MCP tools, so an AI assistant (Claude, Cursor, any MCP client) can read custody data, manage orders, and onboard customers through a typed, audited interface instead of raw HTTP.
| Server | API | Console script | Tools |
|---|---|---|---|
| Custody | Custody Services v3.2.0 — customers, accounts, positions, transactions | openwealth-custody-mcp |
10 (read-only) |
| Trading | Order Placement v3.0.1 — orders, executions, quotes, subscriptions | openwealth-trading-mcp |
18 (full lifecycle) |
| Customer | Customer Management v2.0.6 — customers, persons, contacts, addresses, documents, KYC | openwealth-customer-mcp |
26 (full lifecycle) |
All three ship in one Python package and speak MCP over stdio. OpenAPI specs are vendored from OpenWealth and served as sanitized MCP resources (server URLs and auth schemes are stripped before the spec is exposed to the LLM).
⚠️ Trading places real orders.
create_orderis a financially consequential tool. Read Safety model before pointing it at anything other than the sandbox.
Install
pip install openwealth-mcp
Requires Python ≥ 3.11 and a bearer token for an OpenWealth endpoint (sandbox or custodian).
To install from source for development:
git clone https://github.com/OpenWealth/OpenWealth-MCP.git
cd OpenWealth-MCP
pip install -e ".[dev]" # or: uv sync --extra dev
Quickstart
Configure each server separately — each needs its own base URL and token.
Claude Desktop / Claude Code (claude_desktop_config.json):
{
"mcpServers": {
"openwealth-custody": {
"command": "openwealth-custody-mcp",
"env": {
"OPENWEALTH_CUSTODY_BASE_URL": "https://api.openwealth.synpulse8.com/api/custody-services/v3",
"OPENWEALTH_BEARER_TOKEN": "<jwt>"
}
},
"openwealth-trading": {
"command": "openwealth-trading-mcp",
"env": {
"OPENWEALTH_TRADING_BASE_URL": "https://<host>/api/trading-services/v1",
"OPENWEALTH_BEARER_TOKEN": "<jwt>"
}
},
"openwealth-customer": {
"command": "openwealth-customer-mcp",
"env": {
"OPENWEALTH_CUSTOMER_MANAGEMENT_BASE_URL": "https://api.openwealth.synpulse8.com/api/customer-management/v2",
"OPENWEALTH_BEARER_TOKEN": "<jwt>"
}
}
}
}
Cursor (.cursor/mcp.json, or Settings → MCP for a global server):
{
"mcpServers": {
"openwealth-custody": {
"command": "openwealth-custody-mcp",
"env": {
"OPENWEALTH_CUSTODY_BASE_URL": "https://api.openwealth.synpulse8.com/api/custody-services/v3",
"OPENWEALTH_BEARER_TOKEN": "<jwt>"
}
},
"openwealth-trading": {
"command": "openwealth-trading-mcp",
"env": {
"OPENWEALTH_TRADING_BASE_URL": "https://<host>/api/trading-services/v1",
"OPENWEALTH_BEARER_TOKEN": "<jwt>"
}
},
"openwealth-customer": {
"command": "openwealth-customer-mcp",
"env": {
"OPENWEALTH_CUSTOMER_MANAGEMENT_BASE_URL": "https://api.openwealth.synpulse8.com/api/customer-management/v2",
"OPENWEALTH_BEARER_TOKEN": "<jwt>"
}
}
}
}
Then run MCP: Restart Servers from the command palette and check that the server shows as connected. Configure only the domain you have access to.
Sandbox JWTs often expire in about 60 seconds — refresh from the portal and
restart the server. Supply the token without the Bearer prefix; the
client adds it.
Full client wiring, WSL specifics and troubleshooting live in
docs/mcp-clients.md and
docs/local-dev.md.
Configuration
All settings are environment variables prefixed OPENWEALTH_. A .env file in
the working directory is loaded automatically (and is gitignored — never commit
one). Host and credentials are never hard-coded.
| Variable | Default | Description |
|---|---|---|
OPENWEALTH_CUSTODY_BASE_URL |
— | Custody API base URL including the path prefix, no trailing slash. Required when running the Custody server. https:// is added if the scheme is missing. |
OPENWEALTH_TRADING_BASE_URL |
— | Trading API base URL including the path prefix, no trailing slash. Required when running the Trading server. |
OPENWEALTH_CUSTOMER_MANAGEMENT_BASE_URL |
— | Customer Management API base URL including the path prefix, no trailing slash. Required when running the Customer Management server. |
OPENWEALTH_BEARER_TOKEN |
— | Access token without the Bearer prefix. Required unless OPENWEALTH_AUTH_HEADER is set. |
OPENWEALTH_AUTH_HEADER |
— | Full Authorization header value; overrides OPENWEALTH_BEARER_TOKEN. |
OPENWEALTH_CORRELATION_ID |
generated | Fixed correlation id; otherwise a UUID per request. |
OPENWEALTH_TIMEOUT_SECONDS |
120 |
Read/write timeout in seconds (max 300). |
OPENWEALTH_CONNECT_TIMEOUT_SECONDS |
10 |
Connect timeout in seconds (max 120). |
OPENWEALTH_MAX_RETRIES |
2 |
Retries for transient failures (0–5). See Safety model. |
OPENWEALTH_VERIFY_TLS |
true |
Set to false only against a sandbox with a self-signed certificate. |
OPENWEALTH_LOG_LEVEL |
INFO |
DEBUG, INFO, WARNING, or ERROR. JSON logs on stderr. |
OPENWEALTH_LOG_FILE |
— | Optional log file path (logs to both file and stderr). |
Startup fails fast if neither OPENWEALTH_BEARER_TOKEN nor OPENWEALTH_AUTH_HEADER is set.
The URL for each server is validated at startup — a missing URL prints a clear error and exits.
Transport is stdio only. HTTP transport and JWKS ingress are out of scope in favour of a smaller surface.
Tools
Every response is JSON with status_code, correlation_id and data
(plus next_cursor on paginated GETs). Errors return
{"error": true, "error_code": "...", "message": "...", "retryable": bool} — never a stack trace.
Custody — openwealth-custody-mcp
| Tool | operationId | Endpoint |
|---|---|---|
get_customers |
getCustomers |
GET /customers |
get_customers_by_customer_id |
getCustomersByCustomerId |
GET /customers/{customerId} |
get_customer_accounts_by_customer_id |
getCustomerAccountsByCustomerId |
GET /customers/{customerId}/accounts |
get_customer_account_by_id |
getCustomerAccountById |
GET /customers/{customerId}/accounts/{accountId} |
get_customer_position_by_customer_id |
getCustomerPositionByCustomerId |
GET /customers/{customerId}/positions |
get_customer_position_by_id |
getCustomerPositionById |
GET /customers/{customerId}/positions/{positionId} |
get_account_position_by_account_id |
getAccountPositionByAccountId |
GET /accounts/{accountId}/positions |
get_account_position_by_id |
getAccountPositionById |
GET /accounts/{accountId}/positions/{positionId} |
get_transaction_by_customer_id |
getTransactionByCustomerId |
GET /customers/{customerId}/transactions |
get_transaction_by_transaction_id |
getTransactionByTransactionId |
GET /customers/{customerId}/transactions/{transactionId} |
Listing positions and transactions requires a date (YYYY-MM-DD) — the
OpenAPI spec makes it mandatory.
Trading — openwealth-trading-mcp
| Tool | operationId | Endpoint |
|---|---|---|
list_customers |
listCustomers |
GET /customers |
get_customer |
getCustomer |
GET /customers/{customerId} |
list_accounts |
listAccounts |
GET /accounts |
get_account |
getAccount |
GET /accounts/{accountId} |
list_orders |
listOrders |
GET /orders |
get_order |
getOrder |
GET /orders/{orderId} |
create_order ⚠️ |
createOrder |
POST /orders |
cancel_order |
actionCancelOrder |
POST /orders/{orderId}/actions/cancel |
list_order_executions |
listOrderExecutions |
GET /orders/{orderId}/executions |
get_order_execution |
getOrderExecution |
GET /orders/{orderId}/executions/{executionId} |
list_order_states |
listOrderStates |
GET /orders/{orderId}/states |
create_quote |
createQuote |
POST /quotes |
list_event_subscriptions |
listEventSubscriptions |
GET /event-subscriptions |
get_event_subscription |
getEventSubscription |
GET /event-subscriptions/{eventSubscriptionId} |
create_event_subscription |
createEventSubscription |
POST /event-subscriptions |
update_event_subscription |
updateEventSubscription |
PUT /event-subscriptions/{eventSubscriptionId} |
delete_event_subscription |
deleteEventSubscription |
DELETE /event-subscriptions/{eventSubscriptionId} |
list_event_subscription_notifications |
listEventSubscriptionEventNotifications |
GET /event-subscriptions/{eventSubscriptionId}/event-notifications |
Customer Management — openwealth-customer-mcp
| Tool | operationId | Endpoint |
|---|---|---|
get_customers |
getCustomers |
GET /customers |
create_customer ⚠️ |
postCustomer |
POST /customer-details |
get_customer |
getCustomerByCustomerId |
GET /customers/{customerId} |
get_customer_details |
getCustomerDetailsByCustomerId |
GET /customers/{customerId}/customer-details |
get_persons |
getPersons |
GET /customers/{customerId}/persons |
create_person ⚠️ |
postPerson |
POST /customers/{customerId}/person-details |
get_person |
getPersonByPersonId |
GET /customers/{customerId}/persons/{personId} |
get_person_details |
getPersonDetailsByPersonId |
GET /customers/{customerId}/person-details/{personId} |
get_contacts |
getContactDetailsByCustomerId |
GET /customers/{customerId}/persons/{personId}/contacts |
create_contact |
postContactDetailsByCustomerId |
POST /customers/{customerId}/persons/{personId}/contacts |
get_contact |
getContactDetailsByContactDetailID |
GET /customers/{customerId}/persons/{personId}/contacts/{contactId} |
update_contact |
putContactDetailByContactDetailId |
PUT /customers/{customerId}/persons/{personId}/contacts/{contactId} |
delete_contact |
deleteContactDetailsByContactDetailID |
DELETE /customers/{customerId}/persons/{personId}/contacts/{contactId} |
get_addresses |
getAddressByCustomerId |
GET /customers/{customerId}/persons/{personId}/addresses |
create_address |
postAddressByCustomerId |
POST /customers/{customerId}/persons/{personId}/addresses |
get_address |
getAddressByAddressId |
GET /customers/{customerId}/persons/{personId}/addresses/{addressId} |
update_address |
putAddressByCustomerId |
PUT /customers/{customerId}/persons/{personId}/addresses/{addressId} |
get_documents |
getDocumentsByCustomerId |
GET /customers/{customerId}/documents |
create_document ⚠️ |
postDocumentByCustomerId |
POST /customers/{customerId}/document-details |
get_document |
getDocumentByDocumentId |
GET /customers/{customerId}/documents/{documentId} |
get_document_details |
getDocumentDetailsByDocumentId |
GET /customers/{customerId}/documents/{documentId}/document-details |
get_kyc |
getKycByCustomerId |
GET /customers/{customerId}/persons/{personId}/kyc |
create_kyc ⚠️ |
postKyc |
POST /customers/{customerId}/persons/{personId}/kyc |
create_prospect_precheck |
postProspect |
POST /prospect-precheck |
get_prospect_precheck |
getPreCheck |
GET /prospect-precheck/{temporaryId} |
get_status |
getStatusByTemporaryId |
GET /status/{temporaryId} |
⚠️ marks tools that create regulatory or compliance-significant entities (customer, person, document, kyc) — records that are hard to reverse and carry KYC/AML implications. Write tools on softer, mutable data (contact, address, prospect_precheck) are not marked because they can be freely updated or deleted without compliance impact. All Customer Management write tools are still never retried regardless of the ⚠️ marker — see Safety model.
MCP resources
| URI | Content |
|---|---|
openwealth://specs/custody |
Custody tool/endpoint table (Markdown) |
openwealth://specs/custody.yaml |
Custody OpenAPI schema reference (server URLs and auth stripped) |
openwealth://specs/trading |
Trading tool/endpoint table with financial-impact notes |
openwealth://specs/trading.yaml |
Trading OpenAPI schema reference (server URLs and auth stripped) |
openwealth://specs/customer |
Customer Management tool/endpoint table with write-impact notes |
openwealth://specs/customer.yaml |
Customer Management OpenAPI schema reference (server URLs and auth stripped) |
Safety model
create_orderis never retried. A duplicate order is a financial error, so a failed POST fails loudly rather than silently placing a second one.- Customer Management write tools are never retried.
create_customer,create_person,create_contact,create_address,create_document,create_kycandcreate_prospect_precheckare not retried to avoid duplicate records at the custody bank. - Idempotent writes are retried on 5xx:
cancel_order,create_quote,update_contact,update_address,PUTandDELETE. GETs retry on 408, 429 and 5xx with exponential backoff and ±20% jitter. TheRetry-Afterheader is honoured. - Tool annotations (
readOnlyHint,destructiveHint,idempotentHint) are set per tool, so MCP clients can require confirmation for writes. - Tokens are never tool arguments. They come from the environment only, and are never echoed in tool results or logs.
- Custody is read-only by design. Only the Trading and Customer servers can mutate state.
Always confirm order details with a human before calling create_order.
Development
With uv (recommended):
uv sync --extra dev
uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy
uv run pytest -q
With pip:
pip install -e ".[dev]"
ruff check src tests
ruff format --check src tests
mypy
pytest -q
The same four gates run in CI on every push and pull request across Python 3.11,
3.12 and 3.13. pre-commit install wires ruff and mypy into your commits.
See CONTRIBUTING.md for full contribution guidelines.
Documentation
| Where | What |
|---|---|
CHANGELOG.md |
Release history |
CONTRIBUTING.md |
Dev setup and contribution guidelines |
docs/mcp-clients.md |
MCP client configuration (Claude, Cursor, WSL) |
docs/local-dev.md |
Local development setup |
docs/release.md |
Release and publishing process |
specs/ |
Vendored OpenAPI definitions |
SECURITY.md |
Vulnerability reporting and secret handling |
Scope
Custody is read-only (GET). Trading covers the full order lifecycle (GET, POST, PUT, DELETE). Customer Management covers the full customer onboarding and lifecycle (GET, POST, PUT, DELETE). HTTP transport, JWKS ingress and a central MCP gateway are out of scope.
License
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openwealth_mcp-0.3.7.tar.gz.
File metadata
- Download URL: openwealth_mcp-0.3.7.tar.gz
- Upload date:
- Size: 210.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ad14f2dc37b01ccbcc64e1bc66c434f3d217f57dae212d811ff4de407411927b
|
|
| MD5 |
3e4c17da3301c222a63c634882107273
|
|
| BLAKE2b-256 |
12a4697bde3da5edce791f94059e3ab4774e16938c3e973532bb82708bb3a34f
|
Provenance
The following attestation bundles were made for openwealth_mcp-0.3.7.tar.gz:
Publisher:
publish.yml on OpenWealth/OpenWealth-MCP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openwealth_mcp-0.3.7.tar.gz -
Subject digest:
ad14f2dc37b01ccbcc64e1bc66c434f3d217f57dae212d811ff4de407411927b - Sigstore transparency entry: 2708320708
- Sigstore integration time:
-
Permalink:
OpenWealth/OpenWealth-MCP@d8780948902d8640f381db515b937b0db0044908 -
Branch / Tag:
refs/tags/v0.3.7 - Owner: https://github.com/OpenWealth
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d8780948902d8640f381db515b937b0db0044908 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file openwealth_mcp-0.3.7-py3-none-any.whl.
File metadata
- Download URL: openwealth_mcp-0.3.7-py3-none-any.whl
- Upload date:
- Size: 89.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2e43425b9314223b210b13408e6aff99b0180f3563faac83494e3b3a30d87f76
|
|
| MD5 |
7fdf35c5579d7abc10d24dec17498e01
|
|
| BLAKE2b-256 |
28c8757c30b90e5924c1299c376630d4a35606294e0a9f5a9e49a3a620146585
|
Provenance
The following attestation bundles were made for openwealth_mcp-0.3.7-py3-none-any.whl:
Publisher:
publish.yml on OpenWealth/OpenWealth-MCP
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
openwealth_mcp-0.3.7-py3-none-any.whl -
Subject digest:
2e43425b9314223b210b13408e6aff99b0180f3563faac83494e3b3a30d87f76 - Sigstore transparency entry: 2708320755
- Sigstore integration time:
-
Permalink:
OpenWealth/OpenWealth-MCP@d8780948902d8640f381db515b937b0db0044908 -
Branch / Tag:
refs/tags/v0.3.7 - Owner: https://github.com/OpenWealth
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d8780948902d8640f381db515b937b0db0044908 -
Trigger Event:
workflow_dispatch
-
Statement type: