allowed at this block. Not a promise about the next one.
MCP Architecture for Agentic Finance. Agents act inside walls the chain enforces, and every refusal comes back with a reason.
An agent handed a wallet can enumerate its balances. It cannot enumerate what it is allowed to do with them. That is the whole problem, and the panel above is the whole proof: nine real assets on Base, four different shapes of rule, and no common way to ask.
| Asset | What it will answer |
|---|---|
| deSPXA · S&P 500, Centrifuge | erc1404 |
| USDC | blocklist pausable owned |
| tCOIN · USTBL | owned |
| PAXG · USOIL · NVDAX · wtCOIN · syrupUSDC | plain erc20 |
The rules exist. USDC can freeze you, Centrifuge runs a compliance hook, three of the nine have an owner who can act. What does not exist is a common way to ask about any of it, so for five of the nine the only way to find out is to try and read an opaque revert.
register(asset) staticcalls every interface we know how to ask about, with a gas cap on each, and records only what actually answered. Nobody submits a manifest, nobody is trusted, and a token that reverts or burns gas on its own compliance function is simply recorded as not answering. It cannot break its own registration.
// AssetRegistry.sol function _answers(address a, bytes memory data) internal view returns (bool) { (bool ok, bytes memory ret) = a.staticcall{gas: 60_000}(data); return ok && ret.length >= 32; }
Verdict{ ok, code, reason, checked }. The code is a stable enum. The reason is the asset's own sentence where it has one. And checked says which capabilities were actually consulted, because an agent has to be able to tell allowed from I could not find out. A thin mask is not a clean bill of health, and this is the difference between a tool a model can reason about and a boolean it has to trust.
allowed at this block. Not a promise about the next one.
recipient is on the blocklist, read from the asset itself.
refused by the compliance hook, with the hook's own wording.
the asset stopped answering. A refusal, never an optimistic yes.
Approving every transaction does not scale, and the deeper problem is that the person approving at three in the morning knows less about the trade than the agent does. Approval theatre is not safety.
So the human approves the walls: which assets, how much per action, how much in total, to whom, until when. The agent acts freely inside them. Stepping outside is not discouraged, it is impossible, because the executor holds the money and the only way out is act, which moves one asset to one recipient and nothing else. There is no generic call and no calldata a caller can shape.
The grantor turns a mandate off in the same block, without notice and without the agent's cooperation.
Every token can be withdrawn at any moment, live mandate or not. Nothing here can be locked against the person who funded it.
And act reverts with the same typed code, so a refused action tells the agent why instead of leaving it to guess.
Rised ships an MCP server. Point any MCP client at it and a model can ask a token what it will allow, before it proposes a transaction, in the vocabulary it already reasons in.
| Tool | What it answers |
|---|---|
rised_list_assets | Turn "the S&P token" into an address. |
rised_probe_asset | Which permission interfaces this token really implements, and what it stays silent about. |
rised_preflight | Would this transfer go through, with a stable code, a reason, and the block it is true at. |
rised_survey | Group the known assets by rule shape, to answer "which of these can freeze me". |
// what rised_preflight actually returns, live from Base { "ok": false, "code": "INSUFFICIENT_BALANCE", "reason": "sender holds less than the amount", "checked": ["balance"], "notChecked": ["erc1404", "allowlist", "freezable"], "atBlock": "50570985" }
notChecked is the part that matters. A model that only sees ok: true will treat silence as permission. A model that also sees which questions were never asked can say so, and can refuse to promise what it did not verify.
{
"mcpServers": {
"rised": { "command": "npx", "args": ["-y", "rised-mcp"] }
}
}The executor charges a fee on successful actions, immutable and capped at 0.25% when it is constructed. It has exactly one destination, written into the contract before the first action and never writable again, and it is paid inside the same transaction as the action. No emission, no schedule, no inflation. If no agent acts, nothing is collected, and that is correct rather than a bug.
| Contract | Address on Base |
|---|---|
| AssetRegistry | 0x5862AE09b055C61D810745CBbDE954BE57A2f3BB |
| Preflight | 0xDc3C7aedb1580AC5928db9931C9B2210a46e14Ec |
| Executor | 0xD6aCC729Ce294F5D7617139dBbf9DA59C9b3697C |
| Rised · $RISED | 0xb8D89c91B445A48Ee310247A2e63e0d7A6953643 |
| RisedLpManager | 0x8dBb194B376d694A93DBb755bdb6E7ea9c81813F |
| Pool | 0x29348f4f7e03e34e4e716a1a88e68d4491c4b9789d95d21c1a2963419e5f2708 |
It promises the state at the current block. Strictly better than a revert, and not a guarantee.
An asset with a bespoke, invisible rule is recorded as plain. Saying so through checked is the point.
For those, only balance, allowance and the move itself can be verified. That is the state of the market, not a design choice.