AI Agents & MCP
Wire an AI agent to trade on Native Core — the safety contract, the bundled MCP server, and driving the SDK directly.
The Native Core Python SDK is built to be driven by an AI agent, not just a person. This page is the agent-integration centerpiece: one safety contract, then two ways to connect — a bundled MCP server that needs no glue code, or the SDK called directly from your own agent loop.
Why Native Core is agent-friendly
The API and the SDK are shaped so an autonomous model can trade without ever holding a fund-moving key or parsing prose to know what happened.
Scoped, revocable keys. An agent trades with an API wallet — a protocol-level agent key scoped only to placing and cancelling orders. It can never deposit, withdraw, or move funds off Native. Create it in the web app, and revoke or rotate it there any time. A leaked API wallet key can trade the balance but cannot drain it. See getting-started.md for how to create one.
Reads on, writes gated. Market data, balances, and order status are always available. The order-placing and cancelling surface is turned on only by an explicit switch, so a read-only agent physically cannot place an order.
Outcomes are structured, not prose. Every trade response is decision-ready: the agent branches on a field (
submission_status,next_action,state,filled_qty) instead of reading a sentence. A business rejection is data in the response body, not an exception.Deterministic reconcile-by-cloid. Every order carries a client order id (
cloid) you can look up. That single primitive is what stops an agent from double-filling after a timeout — the core of the safety contract below.
The safety contract for agents
An agent that places money-moving orders must hold to a few rules. The SDK returns each outcome as structured fields so the agent branches on a field instead of remembering the rule.
Accepted is not filled. A raw
order()/market_order()returningsubmission_status: "accepted"means the transaction landed and executed — not that the order rested or filled (it also covers a benign IOC/FOK cancel). Read the order's status bycloid—wait_for_openfor a resting order, orinfo.reconcile_by_cloid(user, market, cloid)— for theoidand fill.Never resubmit an uncertain write. A wire timeout raises
SubmissionUncertain(carrying.cloidand.nonce) or returnssubmission_status: "timeout". The order may still be live. Reconcile bycloidand act on the result; resubmitting under a fresh nonce risks a double-fill. Only aRateLimitedrejection is safe to resend — it was never admitted.Survive a restart. Generate the
cloidyourself withExchange.random_cloid(), persist{intent, cloid}durably before callingorder(..., cloid=cloid), and on restart reconcile every persisted cloid before placing anything new. This is the idempotency-key pattern: an agent that crashes after sending but before recording an SDK-generated cloid cannot reconcile and may double-fill.Numbers are strings. Pass
szandlimit_pxasstrorDecimal, neverfloat. Size withinfo.min_order_size(market, price)and dry-run withbuild_order(...)(which signs and sends nothing) to avoid a precision or minimum-notional rejection. See decimals-units.md.
next_action(response) collapses any trade response into one verdict to branch on (it returns None for a non-trade response):
next_action
Situation
What the agent does
READ_ORDER_STATUS
accepted — landed & executed
Read the order's status once (wait_for_open / wait_for_order) for the oid and fill — the outcome is already settled
RECONCILE_BY_CLOID
timeout — indeterminate
reconcile_by_cloid; never resubmit
BACKOFF_AND_RETRY
RateLimited — never admitted
Sleep retry_after_ms, then resend the same order
FIX_AND_RESUBMIT
other rejection
Fix the input or account state, then submit fresh
as_problem_details(failure) is the one-envelope normalizer: it renders any failure — an exception, or a rejected / timeout body — into a single flat {type, title, retryable, next_action, cloids, …} shape, so an agent has one place to read regardless of how the outcome arrived.
The wire-level fields these helpers read (submission_status, error.code, orderStatus) are documented in post-trade.md and post-info.md.
Option A — the MCP server (no glue code)
The SDK ships an optional Model Context Protocol server that lets any MCP-speaking assistant (such as Claude Desktop) read the market and — when explicitly enabled — place orders through the SDK, with no glue code from you. Install it with the mcp extra:
Run it over stdio with the bundled console script, or as a module:
Two design rules make this surface safe for an autonomous model:
Read-only by default. The order-placing and cancelling tools are registered only when trading is turned on and a key and account are present. A read-only deployment does not register the write tools at all — it physically cannot place an order.
Credentials come from the environment, never as a model-visible argument. The model chooses actions; it never sees or sets the key.
Configuration
The server reads its configuration from these environment variables:
NATIVE_CORE_BUNDLE
Path to a connection bundle JSON ({network, accountAddress, agentPrivateKey}). Preferred — supplies everything on its own.
NATIVE_CORE_NETWORK
testnet (default) when not using a bundle.
NATIVE_CORE_BASE_URL
Endpoint URL, as an alternative to NATIVE_CORE_NETWORK.
NATIVE_CORE_ACCOUNT
Your account address (the owner main wallet) — needed for account-scoped reads and to register the write tools.
NATIVE_CORE_AGENT_KEY
The API wallet's private key, required for trading.
NATIVE_CORE_ENABLE_TRADING
Set to 1 (or true/yes/on) to register the write tools. Off by default: without it the server is read-only even if a key is present.
To connect it to Claude Desktop, add this to its MCP config, pointing NATIVE_CORE_BUNDLE at your bundle file:
Drop NATIVE_CORE_ENABLE_TRADING (or leave it unset) for a read-only assistant that can watch the market and your orders but cannot trade.
Tool inventory
Read tools — always registered:
whoami
The configured account, endpoint, and whether trading is enabled
list_markets
Tradable markets with their id and precision
get_orderbook
Top bids and asks for one market
get_balances
Spot balances for the configured account
list_open_orders
Open orders — one market, or all markets
get_order
Current state of one order by its cloid (poll a resting order)
get_fills
Recent executions, newest first
get_min_order_size
Smallest order size at a price that clears the minimum notional
reconcile_order
Did an order land? The uncertain / timeout recovery path
Write tools — registered only when NATIVE_CORE_ENABLE_TRADING is truthy and a key + account are present:
preview_order
Dry run: validate a limit order without placing it (nothing is sent)
place_limit_order
Place a limit order (gtc / ioc / fok / alo)
place_market_order
Place a market order with a slippage cap in basis points
cancel_order
Cancel one order by oid or cloid
cancel_all_orders
Cancel every open order in one market
Every result is normalized with the SDK's own helpers, so the assistant sees a decision-ready shape (state, filled_qty, next_action) rather than raw API JSON. The never-resubmit contract is enforced by shape: a timed-out or uncertain write comes back with next_action = RECONCILE_BY_CLOID and the cloid, and the model is pointed at reconcile_order — never told to resend. These tools are thin wrappers over the same SDK calls (get_order ≈ reconcile_by_cloid, get_min_order_size ≈ min_order_size, preview_order ≈ build_order).
Use a dedicated, limited API wallet here, never your main wallet. The MCP tools are convenience, not a security boundary: the real protection is that the API wallet is scoped and revocable in the app.
Option B — drive the SDK from your agent code
If your agent does its own tool-calling (not MCP), call the SDK directly and hand the model the same decision-ready fields. The pattern is place, then reconcile by cloid — and branch on the verdict, never resubmit on an uncertain one:
For an agent branching on raw responses, use next_action as the single switch, and generate the cloid up front so a crash between send and record is recoverable:
On restart, reconcile every persisted cloid with reconcile_by_cloid before placing anything new. Share one Exchange per API wallet across threads — the nonce is a per-instance, lock-guarded, monotonic counter, and two instances on the same key collide nonces. Deposits, withdrawals, and approveAgent stay in the web app, signed by the main wallet (transaction-signing.md).
The docs are agent-readable too
This documentation site is itself consumable by an agent — distinct from the trading MCP server above (that one places orders; this one answers questions about the docs).
Every page is Markdown. Append
.mdto any docs URL to fetch the raw Markdown of that page.llms.txtindex. A machine-readable index of the site an agent can crawl for the full page list.A read-only docs MCP endpoint at
https://docs.native.org/native-dev/~gitbook/mcp, exposingsearchDocumentationandgetPage— connect an agent to it for Q&A over these docs.
This endpoint only reads documentation; it holds no key and cannot trade. Use it for retrieval and grounding, and the native-core server above for trading.
Last updated