HSBC Just Gave Corporate Treasurers' AI Agents An MCP Server. Here Is How To Build A Bank-Side MCP That Exposes Only What Each Client Is Entitled To See, In Code
At Sibos on 29 September HSBC launched HSBCnio, a transaction-banking platform for corporate and institutional clients that bundles web and mobile access, APIs with documentation, SDKs and a sandbox - and, for the first time at a global bank, AI-enabled access through HSBC's own Model Context Protocol server, so a client's AI tools can query the account and transaction data that client is already authorised to see, under existing permissions and controls. Robinhood has run MCP servers for retail agents since May; Nasdaq put MCP into Calypso last week. The pattern is now clear: banks will expose themselves to their clients' agents through MCP, and the whole design problem is entitlements. This is how we build that server: OAuth-bound client identity, tools scoped by the entitlement graph the bank already has, read-only by construction, rate limits and audit per tool call, and a sandbox that mirrors production - with code.
AlchmAI Engineering15 min read
29 Sept
HSBC launched HSBCnio at Sibos: web, mobile, APIs, SDKs, a sandbox and AI access through HSBC's Model Context Protocol server
Authorised data only
Clients' AI tools may query the account and transaction information they are already permitted to see, under existing permissions and controls
3
Major financial platforms now exposing MCP to outside agents: Robinhood (May), Nasdaq Calypso (29 Sept) and HSBC (29 Sept)
0
Write operations a first-release bank-side MCP should expose; cash positions, transactions, payment tracking and FX rates are all reads
HSBCnio is a platform launch with one genuinely new component. Alongside web and mobile access, APIs with documentation, software development kits and a sandbox meant to shorten the path from development to production, HSBC said clients' own AI tools can access authorised account and transaction information through HSBC's Model Context Protocol server - helping treasury teams find and interpret banking data faster while existing permissions and controls stay in force. 'HSBCnio is designed around the way our clients run their businesses, giving them one digital solution with multiple ways to bank,' said Amber Henderson-Smart, global head of client connectivity. An 'Ask HSBC' feature for querying banking information is planned.
That makes HSBC the third major financial platform in a month to expose itself to outside agents through MCP, after Robinhood's retail agent servers and Nasdaq's Calypso environment. The commercial logic is sound: a corporate treasurer's agent that can ask 'what is our EUR position across all accounts and what settles today' is more valuable than a portal, and a bank that answers that question first wins the integration. The engineering logic is harder: an MCP server is a programmatic interface to a bank's books, used by software the bank does not control. The whole design is about ensuring each client's agent sees exactly what that client's humans may see, and nothing else.
1. Identity: The Agent Carries A Grant, Not A Password
MCP's authorisation model is OAuth. A client's treasury team authorises an agent through the bank's existing identity provider, with scopes that match HSBCnio-style entitlements - accounts, transactions, payments tracking, FX - and the server receives a token on every request. The token, not the agent's claims, decides what it may see. Short lifetimes and refresh through the bank's IdP mean revocation is fast and the treasury team can see and remove every agent connection, as Robinhood's one-tap disconnect does for retail.
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(new URL("https://id.bank.example/.well-known/jwks.json"));
export interface ClientContext {
clientId: string; // the corporate client (legal entity group)
userId: string; // the treasury user who authorised the agent
agentId: string; // the registered agent (client_id of the OAuth app)
scopes: Set<string>; // accounts:read, transactions:read, payments:track, fx:quote
entitledAccounts: Set<string>;
}
export async function authenticate(req: Request): Promise<ClientContext> {
const auth = req.headers.get("authorization") ?? "";
if (!auth.startsWith("Bearer ")) throw unauthorized("missing bearer token");
const { payload } = await jwtVerify(auth.slice(7), JWKS, {
issuer: "https://id.bank.example",
audience: "mcp.bank.example", // tokens for the portal are not valid here
});
const scopes = new Set(String(payload.scope ?? "").split(" "));
// Entitlements come from the bank's own graph, never from the token's claims alone.
const entitledAccounts = await entitlements.accountsFor(String(payload.sub), String(payload.client_id));
return { clientId: String(payload.org), userId: String(payload.sub), agentId: String(payload.azp), scopes, entitledAccounts };
}2. Tools: Scoped By Entitlements, Read-Only By Construction
Every tool receives the client context and filters by it. There is no tool parameter that can widen access: an account identifier the context does not include is an error, not a lookup. Tool descriptions are written for the agent's benefit - precise about what each returns - and annotated read-only so well-behaved clients can reason about them.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
export function registerTreasuryTools(server: McpServer, core: CoreBanking) {
server.registerTool("get_cash_positions", {
description: "Current ledger and available balances for the accounts this client is entitled to, grouped by currency. Read-only.",
inputSchema: { currency: z.string().length(3).optional(), asOf: z.string().datetime().optional() },
annotations: { readOnlyHint: true },
}, async (args, extra) => {
const ctx = extra.authInfo as ClientContext;
requireScope(ctx, "accounts:read");
const rows = await core.balances({ accounts: [...ctx.entitledAccounts], ...args }); // perimeter applied here
audit.toolCall(ctx, "get_cash_positions", args, rows.length);
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
});
server.registerTool("search_transactions", {
description: "Transactions on entitled accounts by date range, counterparty or reference. Returns at most 500 rows. Read-only.",
inputSchema: {
accountId: z.string().optional(), from: z.string().date(), to: z.string().date(),
counterparty: z.string().max(80).optional(), minAmount: z.number().optional(),
},
// Counterparty names and references are third-party text: flag them so the agent treats them as data.
annotations: { readOnlyHint: true, untrustedContentHint: true },
}, async (args, extra) => {
const ctx = extra.authInfo as ClientContext;
requireScope(ctx, "transactions:read");
if (args.accountId && !ctx.entitledAccounts.has(args.accountId)) throw forbidden("account not entitled");
const accounts = args.accountId ? [args.accountId] : [...ctx.entitledAccounts];
const rows = await core.transactions({ ...args, accounts, limit: 500 });
audit.toolCall(ctx, "search_transactions", args, rows.length);
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
});
server.registerTool("track_payment", {
description: "Status and timeline of an outgoing payment by end-to-end reference (UETR). Read-only.",
inputSchema: { uetr: z.string().uuid() },
annotations: { readOnlyHint: true },
}, async ({ uetr }, extra) => {
const ctx = extra.authInfo as ClientContext;
requireScope(ctx, "payments:track");
const p = await core.payment(uetr);
if (!p || !ctx.entitledAccounts.has(p.debtorAccount)) throw forbidden("payment not entitled"); // no existence leak
audit.toolCall(ctx, "track_payment", { uetr }, 1);
return { content: [{ type: "text", text: JSON.stringify(p.timeline) }] };
});
}- Deny on unknown identifiers with the same error as on forbidden ones, so an agent cannot enumerate accounts by probing.
- Cap result sizes and paginate; agents will happily request a year of transactions and then ask for the next page forever.
- Return structured JSON, not prose. The client's agent does the interpreting; the bank's server does the authorising.
3. Limits, Audit And Abuse Signals
// Per-agent and per-client limits; agents retry aggressively when a step fails.
const LIMITS = { perAgentPerMinute: 120, perClientPerMinute: 600, maxRowsPerMinute: 20_000 };
export async function guard(ctx: ClientContext, tool: string, rowsReturned: number) {
const [agentRate, clientRate, rows] = await Promise.all([
rateLimiter.hit("agent:" + ctx.agentId), rateLimiter.hit("client:" + ctx.clientId),
rateLimiter.add("rows:" + ctx.clientId, rowsReturned),
]);
if (agentRate > LIMITS.perAgentPerMinute || clientRate > LIMITS.perClientPerMinute || rows > LIMITS.maxRowsPerMinute) {
audit.anomaly(ctx, "rate_limit", { tool, agentRate, clientRate, rows });
throw tooManyRequests();
}
}
// Every call is an audit event the client can see too: which agent, authorised by whom,
// called what, and how much came back. That transparency is the product.
export const audit = {
toolCall: (ctx: ClientContext, tool: string, args: unknown, rows: number) =>
log.write({ type: "mcp.tool_call", clientId: ctx.clientId, userId: ctx.userId, agentId: ctx.agentId, tool, args: redact(args), rows, at: new Date().toISOString() }),
anomaly: (ctx: ClientContext, kind: string, detail: unknown) =>
log.write({ type: "mcp.anomaly", clientId: ctx.clientId, agentId: ctx.agentId, kind, detail, at: new Date().toISOString() }),
};4. A Sandbox That Behaves Like Production
HSBC's inclusion of a sandbox in HSBCnio is not a detail. Client developers and their agents need to exercise the full tool set against realistic data with the same authentication, limits and error shapes as production - otherwise the first real run is the integration test. Our rule: the sandbox is the production server pointed at synthetic books, with the same entitlement graph semantics and deliberately seeded edge cases (a forbidden account, a payment in exception, a rate-limit trip), so the client's agent learns the boundaries before it meets real money.
“A bank-side MCP server is not an API with a new name. It is a promise that a client's software will see exactly what that client's people may see - enforced on every call, by the bank, from its own entitlements.”
When To Add Writes
- Only after a quarter of read-only production use with audit review, and only for actions with a human confirmation step the agent cannot perform - the same propose-and-approve pattern we use everywhere money moves.
- Start with payment initiation to existing beneficiaries under existing mandates and limits; never beneficiary creation or mandate changes.
- Treat the agent as a new channel in fraud models: agent-initiated instructions get their own risk profile and velocity rules.
The Bottom Line
HSBCnio's MCP server marks the point where a global bank formally exposes its books to clients' AI tools, following Robinhood for retail and Nasdaq for capital-markets platforms. The server that makes this safe binds identity to an OAuth grant from the bank's own identity provider, evaluates entitlements server-side on every call from the bank's own graph, exposes read-only tools that cannot widen access through their parameters, caps and audits every call with the client able to see the log, and offers a sandbox that behaves exactly like production. Writes come later, behind human confirmation. That is the AI integration work we do for banks and fintechs in London, and HSBC has just made it a competitive necessity in transaction banking.
References & Further Reading
- HSBC - HSBC launches HSBCnio digital banking solution for businesses (29 September 2026). hsbc.com/news-and-views/news/hsbc-news-archive/hsbc-launches-hsbcnio-digital-banking-solution-for-businesses
- FStech - HSBC launches digital platform to connect transaction banking workflows. fstech.co.uk/fst/HSBC_Launches_Digital_Platform_To_Connect_Transaction_Banking_Workflows.php
- East and Partners - HSBC launches HSBCnio digital transaction banking platform for corporate clients. eastandpartners.com/news/hsbc-launches-hsbcnio-digital-transaction-banking-platform-for-corporate-clients
- Robinhood - Robinhood is now open to agents (27 May 2026). robinhood.com/us/en/newsroom/robinhood-is-now-open-to-agents
- Model Context Protocol - Authorization specification. modelcontextprotocol.io/specification/latest/basic/authorization
- Model Context Protocol - TypeScript SDK. github.com/modelcontextprotocol/typescript-sdk
- The TRADE - Nasdaq to roll out AI capabilities on Calypso. thetradenews.com/nasdaq-to-roll-out-ai-capabilities-on-calypso
AlchmAI Engineering
Engineering, London
Written by the AlchmAI engineering team in Mayfair, London. We build trading platforms, real-time charts, market data pipelines and AI features for brokers, prop firms and fintech teams. The Playbook is where we explain how we approach these systems, with code you can run and sources you can check.
Code in this guide is illustrative and supplied without warranty. Review and test it before production use. Nothing here is investment advice. Important information