Skip to content

UMBR is live on testnet. Private payments with test funds on Robinhood Chain and Tempo.

Open the app

Build

Private agents

Give an AI agent its own private balance with hard spending limits. Connect through the umbr-mcp server or call the SDK directly.

MCP server
umbr-mcp (preview)
Transport
stdio today
Limits
Per payment and per day
Networks
Testnets only

Preview

umbr-mcp and @umbr/sdk are not published yet, and both may change. Use testnets and test funds only.

Why agents need privacy#

An agent that pays for things leaves a trail. If it pays from a public wallet, anyone can read what it bought, from whom, how often and for how much, and from that infer what its owner is working on. A research agent paying for data sources gives away the research. A procurement agent gives away its suppliers and prices.

With UMBR, the agent holds a private balance and pays from it. Observers see pool interactions, not the vendor, the amount or the pattern. Hard limits sit next to the balance, so a confused or manipulated agent cannot spend beyond what you allowed.

Two ways in#

PathBest forStatus
MCP server (umbr-mcp)Agents in an MCP client: a desktop assistant, an IDE, an agent framework with MCP supportPreview, run locally
SDK (@umbr/sdk)Agents you write yourself, where your code decides when to payPreview

Both use the same engine: proofs are made on the machine the agent runs on, and the same limits apply.

Connect by URL#

There is no hosted UMBR MCP endpoint today. Today you run umbr-mcp locally, as shown in the next section. A remote endpoint over Streamable HTTP is planned. When it exists, adding it will look like any other remote MCP server, a config entry with a url:

Planned: remote config (placeholder URL)
{
	"mcpServers": {
		"umbr": {
			"url": "https://mcp.example/umbr"
		}
	}
}

A hosted server would hold the agent's key and make its proofs, which is a different trust model from running it yourself. We will document that trade-off when it ships rather than before.

The MCP server#

umbr-mcp runs as a local process over stdio. Your MCP client starts it with npx and passes its settings as environment variables:

MCP client config
{
	"mcpServers": {
		"umbr": {
			"command": "npx",
			"args": ["-y", "umbr-mcp"],
			"env": {
				"UMBR_NETWORK": "tempo-moderato",
				"UMBR_AGENT_KEY": "<read from your secret store>",
				"UMBR_LIMIT_PER_PAYMENT": "2.00",
				"UMBR_LIMIT_PER_DAY": "50.00"
			}
		}
	}
}
VariableMeaning
UMBR_NETWORKtempo-moderato or robinhood-testnet
UMBR_AGENT_KEYThe agent's own private key, injected from a secret store. Never paste a real key into a config file.
UMBR_LIMIT_PER_PAYMENTLargest single payment the server will make, as a decimal string
UMBR_LIMIT_PER_DAYTotal the server will spend in a rolling day

Fund the agent by sending test stablecoins to its public wallet and calling umbr_shield, or by sending privately to its umbr1q... address from your own balance.

Tools the server exposes#

ToolWhat it doesMoves funds
umbr_addressReturns the agent's private umbr1q... address.No
umbr_balanceSyncs and returns private balances by asset.No
umbr_shieldMoves public stablecoins from the agent's wallet into its private balance.Yes, within limits
umbr_sendSends a private payment to an UMBR address, with an optional memo.Yes, within limits
umbr_unshieldWithdraws to a public 0x address.Yes, within limits
umbr_create_linkCreates a claim link for someone without an UMBR address.Yes, within limits
umbr_payroll_runPays a list of recipients in one run.Yes, within limits
umbr_proveMakes a scoped proof with an audience and expiry.No
umbr_verifyChecks a proof link or JSON and returns the statement.No
umbr_limitsReports the limits and how much of today's allowance is left.No

Most MCP clients can require a human to approve each tool call. Keep approval on for every tool that moves funds until you trust the agent's behaviour.

An agent on the SDK#

If you write the agent loop yourself, call the SDK directly. Give the agent its own client with its own key and limits, and route anything over a limit back to a person:

agent.ts
import { createUmbrClient, networks, UmbrError } from "@umbr/sdk";

const agent = createUmbrClient({
	network: networks.tempoModerato,
	signer: process.env.UMBR_AGENT_KEY, // the agent's own key, never your treasury key
	relayer: "default",                 // the agent's public wallet stays out of view
	limits: { perPayment: "2.00", perDay: "50.00" },
});

export async function payVendor(vendorAddress: string, amount: string, memo: string) {
	try {
		return await agent.send({ to: vendorAddress, asset: "pathUSD", amount, memo });
	} catch (err) {
		if (err instanceof UmbrError && err.code === "LIMIT_EXCEEDED") {
			return { needsApproval: true, amount }; // hand it back to a human
		}
		throw err;
	}
}

Spending limits#

  • Per payment caps any single send, unshield, claim link or payroll run.
  • Per day caps the total over a rolling 24 hours.
  • Limits are checked before any proof is made. A call over a limit throws LIMIT_EXCEEDED and nothing is signed or submitted.
  • Limits live in the client, not the pool contract. They stop the agent's own code and prompts; they do not stop someone who has the key and uses different software. The real ceiling is the balance you give the agent.

Fund it like a wallet, not a bank account

Keep only what the agent needs for a few days in its private balance and top it up. A small balance is the limit that holds even if everything else fails.

Some APIs charge per request using HTTP status 402 Payment Required. An UMBR agent can answer that from its private balance. This is an integration pattern, not a shipped feature: neither the SDK nor umbr-mcp handles 402 for you yet, and the header name below is an example.

A paid request, privately
  1. Request

    The agent calls the API as usual.
  2. 402 response

    The server replies 402 with the asset, the amount and an UMBR address to pay.
  3. Private payment

    The agent sends the amount from its private balance, within its limits.
  4. Retry with proof

    The agent makes a payment proof addressed to that server and retries with it in a header.
  5. Server verifies

    The server checks the proof with verifyProof, confirms the amount and audience, and serves the response.
paid-fetch.ts (pattern)
import { verifyProof } from "@umbr/sdk";

// Integration pattern, not a shipped API.
async function paidFetch(url: string, init: RequestInit = {}) {
	const first = await fetch(url, init);
	if (first.status !== 402) return first;

	// The server says what it wants: asset, amount and an UMBR address.
	const { asset, amount, payTo } = await first.json();

	const { paymentId } = await agent.send({ to: payTo, asset, amount, memo: url });
	const proof = await agent.prove({
		claim: "payment",
		payment: paymentId,
		reveal: "exact",
		audience: new URL(url).host,
		expiresIn: "1d",
	});

	return fetch(url, {
		...init,
		headers: { ...init.headers, "x-umbr-payment": proof.link },
	});
}

// On the server: const r = await verifyProof(req.headers["x-umbr-payment"]);
// then check r.valid, !r.expired, r.audience and r.statement before serving.

The server should also remember which payments it has already accepted, so one proof cannot buy two responses.

Pay only for results that pass

To pay x402 tools through an escrow that checks each result first, fund the agent's Assay balance from your private balance.

Keys and custody#

  • The agent's key is the agent's money. Generate a fresh key for each agent; never reuse yours.
  • Load it from a secret manager at start-up. Do not commit it, log it or put it in a prompt.
  • The key never leaves the process running umbr-mcp or the SDK. No UMBR service holds it, which also means no one can recover it for you.
  • Back up the note store (./.umbr by default in Node) and the key together. To retire an agent, unshield or send its balance back to you, then destroy the key.

What it does not do#

  • It does not judge whether a payment is a good idea. Limits bound the damage; they do not supply judgement.
  • It does not hide the agent's IP address or timing from the RPC provider or the relayer.
  • Shielding and unshielding are public. An agent that shields right before each payment gets little privacy.
  • There is no hosted MCP endpoint and no built-in 402 handling yet.
  • It runs on testnets only. Nothing here should hold real funds. See Product status.