# Composable portfolios — agent entry Non-production demo of Solana portfolios composed from registered yield pools. Portfolio NAV is operator-attested (not on-chain proven). Redemption times are estimates, not guarantees. Legs may settle across CCTP. ## How to talk to the system - Production REST / MCP: `https://api-production-17bd.up.railway.app` - MCP JSON-RPC: `POST /mcp` with `initialize`, `tools/list`, `tools/call` - Do not scrape the website. Call `list_portfolios` / `get_portfolio`. - Books are identified by shares mint **or** symbol (`sDM6`) **or** name. - Intent schema: `GET /v1/schemas/portfolio-intent` - Aliases: `create` / `create_portfolio` → `build_create_portfolio`; `simulate` → `simulate_portfolio` ## Create a portfolio (agents) Agents can create books. The API returns unsigned transactions. You never hold a key. 1. `list_pools` — use only `status: active` pools. `poolId` may be the ticker (`pMSOL`) or `pMSOL@solana`. 2. `simulate_portfolio` (alias `simulate`) with an intent. `targets[].weightBps` must sum to **exactly 10000**. Missing fees, bands, name, symbol, and metadata default to demo-safe values (deposit/withdraw 10/10, idle 500, drift 200, symbol `sAGENT`). 3. Fix every `errors[]` entry. Read `warnings[]`. 4. `build_create_portfolio` (aliases `create`, `create_portfolio`) with the same intent plus `creator` (the paying pubkey). `curator` defaults to `creator`. 5. Return the unsigned transactions. The creator signs through `init_custody`. Stop at the first operator step (`init_vault_atas` keeper, `whitelist_custodian` governance). Do not tell the user the book is live, and do not `activate_portfolio`, until the custodian is whitelisted. 6. `verify_portfolio` / `get_portfolio` until `state == live`. Example: ``` { "name": "Gold tilt", "symbol": "sGOLD", "creator": "", "targets": [ { "poolId": "pXAUT", "weightBps": 3000 }, { "poolId": "pMSOL", "weightBps": 7000 } ] } ``` ## Deposit (agents) 1. `list_portfolios` — pick a live `symbol` (e.g. `sDM6`) 2. `get_portfolio` with `{ "mint": "sDM6" }` — confirm `state` is `live` 3. `build_deposit` with `{ "mint": "sDM6", "user": "", "amount": "1000000", "minShares": "1" }` (`amount` is USDC × 1e6; 1 USDC = `1000000`) 4. Return the unsigned transactions. The agent never holds a key. The user signs. ## Withdraw `build_withdraw` (alias `withdraw`) with `{ "mint": "sDM6", "user": "", "shares": "1000000", "minAmountOut": "1" }`. ## Read - `list_pools` / `get_pool` / `get_pool_history` — `poolId` may be `pXAUT` or `pXAUT@ethereum` - `list_portfolios` / `get_portfolio` / `get_portfolio_history` — prices, targets, marked allocations, flows - Aliases: `deposit`, `withdraw`, `list_books`, `get_book`, `create`, `create_portfolio`, `simulate` ## Rules agents must not violate 1. Prefer `simulate_portfolio` / `simulate_rebalance` before any build. 2. Never invent pool IDs — call `list_pools` and use returned `poolId` values. 3. Weights must sum to exactly 10000 bps; omit a pool rather than weight it 0. 4. Creation returns unsigned transactions. The creator signs creator/anyone steps; operator steps (`keeper_processor`, `governance`) are not signed by the user. v1 ceiling is 32 legs. Books wider than 8 legs insert `create_portfolio_page` (create) or `propose_targets_page` (rebalance) — one transaction per 8-leg page. Live `maxLegs` is on `GET /health`. Idle is a cash floor, not an 11th weight. 5. Under the demo profile, first deposits need governance to whitelist the depositor. 6. Curator rebalances are timelocked: announce → wait `effectiveAt` → apply. 7. Do not claim trustless NAV or guaranteed redemption times. 8. `build_refresh_nav` is permissionless `crank_nav`. Do not tell the user to `crank_pool` a vault whose oracle is the publisher bot — that fails InvalidOracle. ## MCP tools (map 1:1 to REST) | Tool | REST | |---|---| | list_pools | GET /v1/pools | | list_portfolios | GET /v1/portfolios | | get_portfolio | GET /v1/portfolios/{mint} | | simulate_portfolio | POST /v1/portfolios/simulate | | build_create_portfolio | POST /v1/portfolios/build | | verify_portfolio | POST /v1/portfolios/verify | | get_deployment | GET /v1/deployments/{id} | | rebuild_deployment | POST /v1/deployments/{id}/rebuild | | get_pool | GET /v1/pools/{poolId} | | get_pool_history | GET /v1/pools/{poolId}/history | | get_portfolio_history | GET /v1/portfolios/{mint}/history | | build_deposit | POST /v1/portfolios/{mint}/deposit | | build_withdraw | POST /v1/portfolios/{mint}/withdraw | | build_refresh_nav | POST /v1/portfolios/{mint}/refresh-nav | | list_withdrawals | GET /v1/portfolios/{mint}/withdrawals | | get_withdrawal | GET /v1/withdrawals/{request} | | simulate_rebalance | POST /v1/portfolios/{mint}/rebalance/simulate | | build_propose_targets | POST /v1/portfolios/{mint}/rebalance | | build_apply_targets | POST /v1/portfolios/{mint}/rebalance/apply | | build_cancel_targets | POST /v1/portfolios/{mint}/rebalance/cancel | | build_transfer_curator | POST /v1/portfolios/{mint}/curator | | build_accept_curator | POST /v1/portfolios/{mint}/curator/accept | | build_cancel_curator | POST /v1/portfolios/{mint}/curator/cancel | | build_propose_fee_recipient | POST /v1/portfolios/{mint}/fee-recipient | | build_accept_fee_recipient | POST /v1/portfolios/{mint}/fee-recipient/accept | ## Minimal create path 1. `list_pools` → pick two Active pools 2. `simulate_portfolio` with targets summing to 10000 bps 3. Fix every `errors[]` entry; read `warnings[]` 4. `build_create_portfolio` with the same intent + `creator` 5. Present unsigned txs. Creator steps include `init_custody` and come before `init_vault_atas` (keeper) and `whitelist_custodian` (governance). Stop at the first operator-only step — do not activate before whitelist. 6. `verify_portfolio` / `get_portfolio` until `state == live` ## Fee stream Creator fee share is set at creation. The fee recipient can change only after the portfolio is Live, via a two-step propose/accept.