AI integration
MCP server
Connect an AI agent to your workspace's records through MCP tools: every API read, and with a read-and-write key, adding counterparties, invoices, milestones and payee links.
Vestiarion runs a remote MCP server. Connect an AI agent to it with a workspace API key, and the agent can answer questions from your workspace's own records: which payments are held and why, whether the ledger is intact, what is due to contractors.
Each tool is one of the API's operations. A tool call runs that operation with your key and returns its JSON exactly as the API does. With a read-only key, an agent connected here can read and explain, and nothing more. With a read-and-write key, it can also add counterparties, invoices and milestones, which the agent in Vestiarion then decides as it decides any other, and make payee links. No tool verifies work, approves or pays.
The server
https://www.vestiarion.xyz/api/mcp- Transport: Streamable HTTP. The server keeps no session, so a client sends every message as a
POST. AGETor aDELETEwith a valid key answers405: there is no event stream to open and no session to end. - Protocol versions: the 2026-07-28 revision of MCP, and clients on the earlier revisions, such as 2025-06-18 and 2025-11-25, at the same URL.
- Surface: tools only. The server has no resources and no prompts.
Authentication
Send a workspace API key in the Authorization header, exactly as for /api/v1:
Authorization: Bearer <key>The key is checked before any MCP message is read:
- A missing, malformed, unknown or revoked key answers
401with the API's own error body and aWWW-Authenticate: Bearer realm="vestiarion"header. - A key without the
readscope answers403, witherror="insufficient_scope"inWWW-Authenticate. Every key issued today has that scope. - The header is the only place a key is read. A key in the query string or in an MCP message's
_metais ignored: without the header, the request answers401. - If the key cannot be looked up at all, the answer is
500, not401: retry later, and keep the key. See When authentication fails.
Every tool call reads the key's own workspace and no other. Asking get_counterparty for another workspace's counterparty answers not_found, as the API does.
Tools
One tool per API operation. Its name is the operation's id in snake case, its arguments are the operation's parameters, or for a write the fields of its request body and an optional idempotencyKey, with their types, allowed values and bounds, and its description, which is what the agent reads, is the operation's summary and description from its reference page. A read's tool is marked read-only and idempotent. The four write tools are marked as adding records: not read-only, and not idempotent.
create_counterparty,create_invoiceandcreate_milestoneare not destructive. A repeat adds another record, unless it passes the sameidempotencyKey.create_payee_linkis marked destructive, because a new link revokes the payee's unused one. It takes noidempotencyKey: the link is in its answer only, so a repeat makes a new link.
| Tool | What it answers | Reference |
|---|---|---|
get_status | Get workspace status | GET /api/v1/status |
list_ledger_entriesArguments: limit, cursor, domain, actor | List ledger entries | GET /api/v1/ledger |
verify_ledger | Verify the ledger | GET /api/v1/ledger/verify |
list_invoicesArguments: limit, cursor, direction, status, counterpartyId | List invoices | GET /api/v1/invoices |
create_invoiceArguments: direction, counterpartyId, amount, currency, dueDate, memo, poReference, goodsReceived, earlyPayDiscount, idempotencyKey | Add an invoice | POST /api/v1/invoices |
list_counterpartiesArguments: limit, cursor, role, riskLevel | List counterparties | GET /api/v1/counterparties |
get_counterpartyArguments: id | Get a counterparty | GET /api/v1/counterparties/{id} |
create_counterpartyArguments: name, role, address, chain, jurisdiction, paymentLimit, noticeEmail, idempotencyKey | Add a counterparty | POST /api/v1/counterparties |
create_payee_linkArguments: counterpartyId | Create a payee link | POST /api/v1/payee-links |
list_milestonesArguments: limit, cursor, status, contractorId | List milestones | GET /api/v1/milestones |
create_milestoneArguments: contractorId, title, amount, verificationSource, idempotencyKey | Add a milestone | POST /api/v1/milestones |
get_treasury | Get the treasury | GET /api/v1/treasury |
get_insights | Get insights | GET /api/v1/insights |
What a call returns:
- A success is the API's JSON, as text and as structured content.
- An API error is a tool error (
isError) whose text is the API's error body, such as a400 invalid_requestfor a cursor from another endpoint, or a404 not_foundfor an unknown id. The agent reads the message and corrects its call. A read-only key calling a write tool gets the API's403 forbiddenthe same way. The error codes are on Errors. - A collection is one page, 50 items unless the agent passes
limit, up to 200. The agent passespage.nextCursorback ascursorfor the next page, as described in Pagination. - Nothing is cut. A tool returns the whole response,
get_insightsincluded, as the API documents it.
Setup
Keep the key in the VESTIARION_API_KEY environment variable. None of the blocks below writes the key itself into a file.
Claude Code
claude mcp add --transport http vestiarion https://www.vestiarion.xyz/api/mcp \
--header "Authorization: Bearer $VESTIARION_API_KEY"Your shell puts the key into the command, and Claude Code saves the header in its own configuration, ~/.claude.json. To share the server with a project instead, without the key, add it to .mcp.json at the project root. Claude Code expands ${VESTIARION_API_KEY} from the environment when it starts:
{
"mcpServers": {
"vestiarion": {
"type": "http",
"url": "https://www.vestiarion.xyz/api/mcp",
"headers": {
"Authorization": "Bearer ${VESTIARION_API_KEY}"
}
}
}
}Cursor
Add the server to .cursor/mcp.json in your project, or to ~/.cursor/mcp.json for every project. Cursor resolves ${env:VESTIARION_API_KEY} from the environment:
{
"mcpServers": {
"vestiarion": {
"url": "https://www.vestiarion.xyz/api/mcp",
"headers": {
"Authorization": "Bearer ${env:VESTIARION_API_KEY}"
}
}
}
}Codex
Add the server to ~/.codex/config.toml, or to .codex/config.toml in a trusted project. bearer_token_env_var names the variable Codex reads the key from and sends as Authorization: Bearer:
[mcp_servers.vestiarion]
url = "https://www.vestiarion.xyz/api/mcp"
bearer_token_env_var = "VESTIARION_API_KEY"Clients that only start local servers
A client that runs MCP servers as local commands over stdio can reach this one through mcp-remote, which relays between stdio and the remote server:
{
"mcpServers": {
"vestiarion": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://www.vestiarion.xyz/api/mcp",
"--header",
"Authorization: Bearer ${VESTIARION_API_KEY}"
]
}
}
}mcp-remote replaces ${VESTIARION_API_KEY} from its own environment, so the key never appears in the arguments. If your client does not pass VESTIARION_API_KEY on to the servers it starts, set the variable in that server's env entry.
Some clients do not escape spaces inside args when they start npx; the mcp-remote README names Cursor, the Codex CLI and Claude Desktop on Windows. There, write the header as Authorization:${VESTIARION_AUTH_HEADER}, with no spaces, and set VESTIARION_AUTH_HEADER to Bearer followed by the key.
Questions to ask
Once the server is connected, ask in plain language. The agent picks the tools:
- "Is our ledger intact, and are any payments held? Why was each one held?" uses
verify_ledger, thenlist_invoicesandlist_milestoneswithstatus: "held", whose entries carry the agent's reasoning. - "Which contractor milestones are verified but not paid yet?" uses
list_milestones. - "Which counterparties are high risk, and what did their latest screening find?" uses
list_counterpartieswithriskLevel: "high", thenget_counterparty. - "How much do we hold in reserve, and what does the latest liquidity forecast say?" uses
get_treasury. - "Are payments on this workspace live or simulated?" uses
get_status. - With a read-and-write key, "Add Quill Studio's invoice for 420 USDC against PO-4012, due October 31; the work was delivered" uses
list_counterpartiesto find Quill Studio, thencreate_invoice. - With a read-and-write key, "Pay Mona 150 USDC for https://github.com/acme/app/pull/42 once it is merged" uses
list_counterpartiesto find Mona, thencreate_milestone.
What it does not do
- It does not decide. Approving or rejecting a payment, confirming an address, pausing the agent, and managing members, API keys or webhooks are a person's decisions, made in the console. No tool can make them, and the write tools only add records.
- It has no OAuth. The server authenticates with a key in a header and has no authorization server, so it cannot be added as a claude.ai web connector, which needs OAuth. Use a client that sends a header, like the ones above.