Expose an action to agents
Write an action once and expose it as an endpoint and an MCP tool with the same checks.
Preview
Status Status is where the work is: Planned (in a milestone, not started), Building (tickets in progress), Preview (usable, may change), then the stability it ships at.
Stability The promise an interface ships with.
- Stable Follows semantic versioning; breaking changes need a new major, 12 months of overlap and a codemod.
- Beta Usable in production; may change before it becomes stable.
- Experimental May change or go away; needs an explicit opt-in in main.yaml.
You will write one action, expose it over MCP and require a person's yes for a destructive one. It takes about 15 minutes.
You need
- A route or job that already does the work you want to expose.
1. Write the action
import { action, models, pay } from "mainsdk";
import { s } from "mainsdk/schema";
const { orders } = models({ orders: s.object({ paymentId: s.string() }) });
export const refundOrder = action({
name: "orders.refund",
intent: "Refund an order the signed-in user owns",
input: s.object({ orderId: s.string(), reason: s.string() }),
risk: "destructive",
mcp: true,
run: async ({ input, user }) => {
const order = await orders.for(user!).get(input.orderId);
return pay.refund(order!.paymentId, { reason: input.reason });
},
});2. Expose it
mcp:
expose: [orders.refund]
confirm: { write: true, destructive: true }import { mcpServer, type Action } from "mainsdk";
declare const refundOrder: Action<{ orderId: string; reason: string }, unknown>; // from app/actions/refund-order.ts
export const POST = mcpServer({ actions: [refundOrder] });Check it worked
An MCP client lists orders.refund. Calling it as an agent waits for a person's yes, because it is destructive.
Next
Was this page helpful?