Amanah MCP
Provides tools for interacting with Stellar (Soroban) smart contracts, enabling invoice-based USDC payments with escrow and policy enforcement.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Amanah MCPpropose payment for invoice.txt to supplier Batik Jaya"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Lumo
The agent can be tricked. The money cannot.
On-device SME treasury agent for USDC on Soroban. An untrusted local LLM only
reads invoices; a deterministic policy layer decides; two on-chain contracts
enforce. Funds in escrow can structurally reach only the bound supplier (on a
Shipped attestation) or return to the SME (on Failed, or on deadline with no
attestation) — so compromising the agent cannot move money to an attacker.
Demo persona: Bu Sari, owner of Sari Craft Export, a batik exporter in Yogyakarta, Indonesia. She pays overseas fabric suppliers in USDC and wants an agent that can read an invoice and propose a payment — without ever being able to send her money somewhere an attacker chose.
Architecture
The AI reads (untrusted); a deterministic policy decides; Soroban enforces. In
code that path is lumo/security (injection scan) -> lumo/llm (mock or llama,
extraction-only) -> lumo/policy (caps, allowlist) -> lumo/flow ->
lumo/chain/soroban_client (stellar CLI subprocess), with lumo/db as the
single audit chokepoint and two Soroban contracts as the on-chain gates: the
policy-signed account (__check_auth: per-tx cap + supplier allowlist) and the
attestation escrow (create_intent -> attest -> release | refund).
Trust boundary: the LLM is extraction-only and holds zero tools — it reads
invoice text and returns structured fields, nothing more. Every payment
decision is made by the deterministic Python policy layer (caps, supplier
registry, injection scanner), and every payment is enforced twice more
on-chain: the policy-signer smart account refuses an out-of-policy
transaction before it is ever submitted, and the escrow can only pay the one
supplier bound to that intent or refund the SME who funded it. A test suite
proves that even a fully compromised LLM (one that obeys attacker text in the
invoice) cannot move funds anywhere but the registered supplier or back to the
SME — see tests/test_t8_injection.py.
Money truth lives on-chain; agent-brain truth (suppliers, rules, intents,
audit) lives in SQLite; a request_hash (sha256 of the canonical intent JSON)
binds the two and is checked chain-side before any state is written locally.
Related MCP server: AgentPay
Prior art, honestly
On-chain spend caps and payee allowlists already exist — Coinbase Spend Permissions, Crossmint on Soroban, OpenZeppelin's Stellar smart accounts. Lumo does not claim to invent them. Its contribution is the combination: an attestation-gated escrow fused with the policy-signed account (others release on a human signature), aimed squarely at the compromised-agent and invoice-fraud / business-email-compromise threat that agent-payment guidance usually leaves to the operator — packaged full-stack with on-device inference for the cross-border SME vertical.
Repository layout
contracts/ Rust workspace (soroban-sdk 26, wasm32v1-none)
escrow/ conditional-release escrow (T1-T3, T5)
policy-account/ __check_auth policy-signer smart account (T4)
bindings/ frozen contract interface (escrow.json, policy_account.json)
lumo/ the Python agent (one package)
llm/ extraction-only providers: mock + llama-server
security/ injection scanner (NFKC + zero-width strip, patterns)
policy/ deterministic evaluate() — caps, registry, injection
db/ SQLite schema, migrations, repo (single audit chokepoint)
chain/ stellar CLI client, request_hash, chain-wins mapper
anchor/ mock_anchor.py — SEP-24-shaped, zero network
ui/ monitoring dashboard + wired testnet tester (/testnet)
site/ self-contained public landing page (landing_check.sh gate)
tests/ pytest: policy engine, injection, audit, db, chain client
acceptance/ acceptance.sh (gate runner) + t10_e2e.sh (local e2e)
scripts/ local_network / deploy_local / demo / deploy_testnet / testnet_serveContracts
lumo-escrow
Conditional-release escrow. One SME funds an Intent bound to one supplier; an
admin-registered oracle attests the outcome; funds settle only along the two
allowed paths.
Entrypoint | Effect |
| stores the admin |
| admin-gated oracle registry |
| pulls |
| oracle-only, |
| requires a |
| on |
A Shipped attestation always beats the deadline: once shipped, refund is
blocked and release stays valid.
lumo-policy-account
Deny-by-default policy-signer smart account (__check_auth). Only two
functions are allowlisted (transfer, create_intent); every invocation is
checked against a per-transaction cap and, for create_intent, an approved
supplier set. Anything else — wrong function, over cap, unapproved supplier,
bad signature — is a typed revert, never a silent pass-through.
cd contracts
cargo test --workspace # unit + revert tests, both crates
stellar contract build # -> target/wasm32v1-none/release/*.wasmThe Python agent
python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m lumo.cli --db /tmp/lumo.db init
.venv/bin/python -m lumo.cli propose tests/fixtures/invoices/clean_in_policy.txtpropose exits 0 and prints a tx plan for an in-policy invoice, or exits 2
with refusal codes (INJECTION_SUSPECTED, OVER_TX_CAP, UNKNOWN_SUPPLIER,
...) and proposes nothing on-chain. LUMO_PROVIDER=mock (default) never
touches a real model; point LUMO_PROVIDER=llama + LUMO_LLAMA_URL at a
local llama-server for the real extraction path (make live-check).
Try it on Stellar testnet (web)
Two web surfaces ship with the agent:
Landing —
site/index.html, a self-contained page (the only external request is Google Fonts).scripts/landing_check.shasserts it stays self-contained, links the live contracts, and leaks no secret key.Live testnet tester — a one-page tool that runs a real invoice against the deployed contracts and shows the actual
create_intent → attest → releasetransactions on Stellar Expert, or refuses a poisoned / over-cap invoice with the real policy codes and zero transactions.
scripts/testnet_serve.sh # one origin: / landing · /testnet tester · /dashboard monitor
scripts/testnet_smoke.sh # POSTs a clean invoice, asserts a real create_intent tx hashThe tester refers to three funded testnet keystore identities
(lumo-deployer, lumo-sme, lumo-supplier) by name only — it never
reads, prints, or exports a secret key. Its presets (clean / over-cap /
injection) are built from the live per-transaction cap and approved supplier
returned by /testnet/info, so a clean invoice settles on-chain and a tampered
one is refused before anything is signed. Nothing here touches mainnet or real
funds.
Integrate
Deeper walkthrough with every option: docs/integration.md.
Runnable versions of the snippets live in examples/.
Integrate in 5 lines
from lumo import LumoClient
client = LumoClient()
decision = client.propose(open("invoice.txt").read())
print(decision.decision, decision.codes)decision.decision is proposed, refused, or held; a proposal carries the
exact create_intent tx plan and an intent_id you can status() later.
Call from any language
Start the REST API (python -m lumo.api, default 127.0.0.1:8788) and use
plain HTTP — the full schema is served at /v1/openapi.json:
curl -X POST http://127.0.0.1:8788/v1/intents \
-H 'Content-Type: application/json' \
-d '{"invoice": "INVOICE INV-2026-0042\nFrom: CV Batik Nusantara\nAmount due: 1,250.00 USDC\n"}'Run it as a microservice (Docker)
Drop the trust layer into any stack as a container:
docker compose up --build # REST API on http://127.0.0.1:8788
# or:
docker build -t lumo . && docker run -p 8788:8788 lumoThe image ships the REST API plus the deterministic guard chain (injection
scan, per-tx cap, supplier allowlist, attestation gating) with the chain
adapter in mock mode — no keys, safe to publish. Callers get the payment
decision as a service and wire their own chain/signer. For real on-chain
settlement, extend the image with the stellar CLI and a mounted keystore,
then set LUMO_CHAIN_ADAPTER=soroban (see the Dockerfile header).
Use from any AI agent
python -m lumo.mcp is an MCP server over stdio exposing three tools:
lumo.propose_payment, lumo.get_status, lumo.attest. Point any
MCP-capable agent at that command and it can propose payments — while every
cap, registry, and injection guard still decides, not the agent.
Target any chain
Settlement is behind ChainAdapter / AnchorAdapter / AttestationSource
seams, selected by config:
Seam | Config key | Live | Roadmap |
Chain |
|
|
|
Anchor off-ramp |
|
|
|
Oracle |
|
|
|
Monitor it
Every decision, guard trip, and state change emits an event through one bus
(monitoring = true, on by default):
SDK:
client.on_event(print)·client.metrics()REST:
GET /v1/metrics(counters + gauges),POST /v1/webhooksregisters a URL that receives every event as JSONDashboard: the read-only monitoring UI at
http://127.0.0.1:8787/dashboardshows the intent timeline and live metrics (the same server serves the landing at/and the testnet tester at/testnet)
Pick a trust tier
Config.profile(name) returns a preset guard chain; everything else stays at
safe defaults and any field can be overridden per call:
Profile | Guards on | Extras |
| injection · policy · signer · attestation · k-of-n · cosign · proof-of-compute |
|
| injection · policy · signer · attestation | single oracle, no cosign |
| injection · policy | propose/refuse only, no release guards |
client = LumoClient(Config.profile("balanced"))Local demo
Requires Docker, the stellar CLI (major version pinned in
acceptance/lib.sh), and the Rust wasm32v1-none target. Everything below
runs on the local Stellar quickstart container — no testnet, no real funds.
scripts/demo.shThis is a narrated, timed walkthrough of the whole spine as Bu Sari would see it:
Seed — deploys the escrow + policy-account, registers the oracle, binds her suppliers, and starts a read-only UI at
http://127.0.0.1:8787.Injected refusal — a fake "our payment address has changed" email is proposed as an invoice. The injection scanner and policy layer refuse it before any transaction is proposed — exit code
2, no chain call.In-policy escrow — a legitimate invoice is proposed and escrowed on-chain, funds locked and structurally bound to that one supplier.
Attestation + release — the oracle attests
Shipped; the escrow releases to the supplier and nowhere else.MOCK cash-out —
lumo/anchor/mock_anchor.pyrecords aMOCK-<ulid>receipt. This is a stand-in for a real SEP-24 anchor off-ramp (structurally zero network calls) — never a real payout.Failure path — a second order is left unshipped past its deadline (a real few-second wait, no ledger time-travel) and refunds Bu Sari; the supplier never touches those funds.
Pace between steps is LUMO_DEMO_PACE seconds (default 2). The UI stays
up after the walkthrough finishes — Ctrl+C to stop it, then
scripts/local_network.sh down.
Acceptance gates
Each phase gate is one runnable command; acceptance/acceptance.sh (no
flags) re-runs all of them and requires the full T1–T10 matrix green with no
regression.
acceptance/acceptance.sh --gate P0 # T1-T3, T5 — escrow contract
acceptance/acceptance.sh --gate P1 # T4 — policy-signer __check_auth
acceptance/acceptance.sh --gate P2 # T6-T9 — policy engine, injection, audit (pytest)
acceptance/acceptance.sh --gate P3 # T10 — local e2e: happy + failure + MOCK cash-out
acceptance/acceptance.sh # full re-run, T1-T10make live-check runs the real-model extraction test against a local
llama-server; it is a human-triggered exit check, never part of a gate.
Testnet deployment
Deployed to the Stellar testnet (Test SDF Network ; September 2015) — a
valueless public test network. No mainnet asset is referenced and no real funds
move; the cash-out anchor stays structurally mocked (lumo/anchor/mock_anchor.py).
Artifact | Value |
Network |
|
| |
| |
Escrow admin + oracle |
|
Policy owner (SME ed25519, hex) |
|
Policy per-tx cap |
|
Test USDC SAC |
The USDC used here is a self-issued testnet asset (USDC:GC5U5EI2…), never a
mainnet asset. One full end-to-end smoke ran on-chain — create_intent →
attest(Shipped) → release — pulling 100 test USDC into escrow and settling it
to the bound supplier only:
The live tester (scripts/testnet_serve.sh) produces a fresh trail like this on
every clean run, and returns an empty transaction list on every refusal.
Re-deploying is a deliberate, human-run action — scripts/deploy_testnet.sh
prints the checklist and exits 1; no gate, script, or Makefile target
automates it.
Out of scope
Real anchor/off-ramp integration, mainnet deployment, and KYC/licensing are
outside this build — see scripts/deploy_testnet.sh for what a real (mainnet)
deploy would require.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/SyaugiAlkaf/lumo'
If you have feedback or need assistance with the MCP directory API, please join our Discord server