Guides / AI agents

Give your AI agent an inbox (MCP)

Agents are good at acting and bad at knowing. Ask Claude what your vendors invoiced this month and it has nothing, because that information lives in the one feed no agent framework talks about: email. This guide fixes that in about ten minutes.

MCP in one paragraph

MCP (Model Context Protocol) is an open standard that lets AI applications connect to external tools and data. A server exposes named tools with typed schemas; a client (Claude Desktop, Claude Code, Cursor, or your own agent loop) discovers those tools and calls them mid-conversation. If you have used function calling, MCP is that, standardized across vendors and moved out of your prompt.

The architecture decision that matters

You could hand an agent raw emails and let it parse each one in-context. It works, and it costs you twice: the agent burns tokens re-deriving the same structure on every read, and a language model reading a marketing email will occasionally invent a number. The better shape does extraction before the agent, once, at ingest, against a schema:

Email arrives          (newsletter, invoice, alert)
      ↓
Structured JSON        (extracted against your schema, guarded)
      ↓
MCP server             (list, search, filter, group, aggregate)
      ↓
Agent                  (reads data, not documents)

Every Inbin application is an MCP server out of the box, so the rest of this guide uses it. The architecture transfers if you build your own.

Step 1: an inbox that emits structured events

Create an inbox and declare what every email should become. One address, one schema:

import { Inbin } from "@inbin/core";
const inbin = new Inbin({ apiKey: process.env.INBIN_API_KEY });

const inbox = await inbin.inboxes.create({ name: "invoices" });
// -> invoices-b81f22@in.inbin.dev

await inbin.schemas.put({
  extract: {
    invoice: {
      type: "object",
      items: {
        vendor: { type: "string", required: true },
        invoice_number: { type: "string", required: true },
        amount_due_usd: { type: "number", required: true },
        due_date: { type: "string", required: true },
      },
    },
  },
  hallucination_guard: true,
});

Auto-forward your invoice emails to that address (every mail provider can forward by filter), or subscribe it to the feeds you care about. Each message becomes a typed event.

Step 2: connect the agent

For clients that speak Streamable HTTP, it is one config block. Claude Desktop and Cursor (mcpServers in their respective config files):

{
  "mcpServers": {
    "inbin": {
      "url": "https://api.inbin.dev/mcp",
      "headers": { "Authorization": "Bearer ink_live_..." }
    }
  }
}

Claude Code, as a one-liner:

claude mcp add --transport http inbin https://api.inbin.dev/mcp \
  --header "Authorization: Bearer ink_live_..."

Stdio-only clients use the tiny proxy instead: npx @inbin/mcp with INBIN_API_KEY set. The server is also listed in the MCP Registry as io.github.yaotsakpo/inbin.

What the agent can do

Six tools, discovered automatically:

list_inboxesWhat feeds exist in the application
list_eventsThe event stream, filterable by inbox and status
get_eventOne event: parsed JSON plus delivery history
search_eventsSubstring search over sender, subject, id
query_eventsFilter, sort, group and aggregate over extracted fields, server-side
describe_schemaThe current extraction schema (agents read this first, unprompted)

Step 3: ask questions instead of writing glue

With the server connected, this is a conversation, not a program. Ask “what did we get invoiced this month, by vendor?” and the agent calls query_events:

{
  "where": [{ "field": "received_at", "op": "gte", "value": "2026-07-01" }],
  "group_by": "vendor",
  "aggregate": [{ "fn": "sum", "field": "amount_due_usd", "as": "total" }]
}

The aggregation runs server-side, so the agent never pages through fifty emails in-context: one tool call, a handful of rows back, nothing for the model to mis-copy. The same pattern answers “which flight deals under $300 appeared this week”, “list urgent support tickets”, or “how many shipments are still in transit”, depending on what your schemas extract.

Why the guard matters double for agents

The extraction step uses an LLM, and the agent consuming it is an LLM. Stack two and errors compound: an invented invoice amount at ingest becomes a confidently wrong answer at query time, with no human in between. The hallucination guard enforces that every extracted value literally appears in the source email, or the field comes back null. The agent’s floor is “I don’t have that value”, never “here is a plausible number”. If you build your own pipeline, build that check; it is one substring match per field.

What to subscribe the inbox to

The oldest protocol on the internet turns out to be a good context feed. Create an inbox on the free beta, or read the MCP section of the API docs for the wire-level detail.