LOOMAL
Claude Agent SDKTypeScript / Python

Email, vault, and 2FA
for Claude Agent SDK agents.

The Claude Agent SDK supports MCP servers natively. Install the Loomal MCP server and your Claude agent gets an addressable email identity, a credential vault, and TOTP — accessible through structured tools the model already knows how to call.

Send & receive emailEncrypted vaultTOTP / 2FA codesThreaded conversationsDKIM-signed outbound

Prerequisites

  • A Loomal API key (free at console.loomal.ai)
  • Node.js 20+ or Python 3.11+
  • @anthropic-ai/claude-agent-sdk installed
  • An Anthropic API key for the Claude model

The Claude Agent SDK is built around tool use, and MCP servers are how you give it tools without writing a tool layer yourself. Loomal ships an MCP server (@loomal/mcp) that exposes the full mail, vault, and TOTP surface as namespaced tools — mail.send, mail.list_messages, vault.get, vault.totp, and so on. Wire it in once and your agent can send email, read its inbox, store secrets, and pass 2FA challenges with no glue code.

This guide walks through the full setup: provisioning a Loomal identity, installing the MCP server, registering it with the Claude Agent SDK, and running an agent that uses email and vault operations end-to-end.

1. Provision a Loomal identity

Sign up at console.loomal.ai and create an identity. You'll get an API key in the form loid-... and a routable email address like agent-x8k2m@loomal.ai. The identity is the principal your agent will act as — every email it sends will be from this address, and every vault entry will be scoped to this identity.

Keep the API key in your local environment for development. In production, mint a separate identity per deployment so you can revoke them independently.

shell
export LOOMAL_API_KEY="loid-your-api-key"
export ANTHROPIC_API_KEY="sk-ant-..."

2. Install the Loomal MCP server

The MCP server runs as a subprocess of the Claude Agent SDK. You don't need to install it globally — npx will fetch the latest version on every run, which keeps tool schemas up to date as new primitives are added.

shell
npm install @anthropic-ai/claude-agent-sdk
npx -y @loomal/mcp --version  # warm the cache

3. Register the MCP server with the SDK

The Claude Agent SDK accepts MCP servers in its options. Each server is launched as a subprocess; the SDK handles the JSON-RPC handshake and exposes every tool the server advertises to the model.

Setting allowedTools is optional but recommended. By default the model can call any tool the MCP server provides; if you only want this agent sending email (not deleting messages, for example), restrict the surface explicitly.

agent.ts
import { query } from "@anthropic-ai/claude-agent-sdk";

const result = query({
  prompt: "Send a follow-up email to alice@example.com thanking her for the demo today.",
  options: {
    mcpServers: {
      loomal: {
        command: "npx",
        args: ["-y", "@loomal/mcp"],
        env: { LOOMAL_API_KEY: process.env.LOOMAL_API_KEY! },
      },
    },
    allowedTools: [
      "mcp__loomal__mail_send",
      "mcp__loomal__mail_list_messages",
      "mcp__loomal__mail_reply",
    ],
  },
});

for await (const message of result) {
  console.log(message);
}

4. Read inbound mail and reply in-thread

Replies happen in the same thread automatically — you don't manage In-Reply-To headers, and the model doesn't need to either. mail.list_messages with labels=unread gives the agent its work queue; mail.reply sends a response that lands in the right thread on the recipient's side.

support-loop.ts
const result = query({
  prompt: "Check unread support emails. Answer billing questions yourself, label anything else 'needs-human'.",
  options: {
    mcpServers: { loomal: { command: "npx", args: ["-y", "@loomal/mcp"], env: { LOOMAL_API_KEY: process.env.LOOMAL_API_KEY! } } },
    allowedTools: [
      "mcp__loomal__mail_list_messages",
      "mcp__loomal__mail_get_thread",
      "mcp__loomal__mail_reply",
      "mcp__loomal__mail_update_labels",
    ],
  },
});

5. Use the vault for credentials and 2FA

If your agent needs to log into a service that requires 2FA, store the TOTP secret in the vault once and call vault.totp(label) whenever a code is needed. The shared secret never returns in plaintext; the agent only sees the current six-digit code.

Store API keys for downstream services the same way. The vault is scoped to this identity, so revoking the identity revokes every credential in one operation — no manual cleanup.

login-flow.ts
// One-time setup (run from your dev box, not the agent):
//   curl -X POST https://api.loomal.ai/v0/vault \
//     -H "Authorization: Bearer $LOOMAL_API_KEY" \
//     -d '{"label": "crm-totp", "otpauth": "otpauth://totp/..."}'

// In the agent prompt, instruct the model to call vault.totp when needed:
const result = query({
  prompt: "Log into the CRM. The TOTP secret label is 'crm-totp'. Pull yesterday's leads.",
  options: {
    mcpServers: { loomal: { command: "npx", args: ["-y", "@loomal/mcp"], env: { LOOMAL_API_KEY: process.env.LOOMAL_API_KEY! } } },
    allowedTools: [
      "mcp__loomal__vault_get",
      "mcp__loomal__vault_totp",
      "mcp__loomal__mail_list_messages",
    ],
  },
});

Things to watch out for

MCP tool names are namespaced

The Claude Agent SDK prefixes MCP tools with mcp__<server>__<tool>. If you copy tool names from the Loomal docs (where they appear as mail.send), translate them to mcp__loomal__mail_send when listing allowedTools.

One identity per deployment

Don't share a single LOOMAL_API_KEY across staging and production agents. Mint separate identities — it makes revocation surgical and keeps audit trails clean.

FAQ

Does this work with the Python version of the Claude Agent SDK?

Yes. The MCP server is a subprocess; the SDK language doesn't matter. The Python SDK accepts the same MCP server config — command, args, env — under the equivalent options field.

Can I limit which tools the model can call?

Yes. Use allowedTools on the SDK options to whitelist the exact tool names. Anything not listed is hidden from the model. This is the right way to scope an agent — don't rely on prompt instructions alone.

What happens if the agent receives an attachment?

mail.list_messages and mail.get_message return attachment metadata; mail.get_attachment fetches the bytes. Allow mcp__loomal__mail_get_attachment if you want the model to read attachments. Otherwise it can still see filenames and content types.

Loomal primitives used

mail.sendmail.replymail.list_messagesvault.getvault.totp

Ship it.

Free tier, no card. 30 seconds to first email.

Last updated: 2026-04-14 · See also: AutoGen, Claude Desktop, CrewAI