ANNA
- −1 IdentityEntra ID, profile, history, memories. Everything is per user.
- −2 ClaudeTwo backends behind one interface: Claude Code streaming as a subprocess, or the Anthropic API.
- −3 MCP connectorsLoket for leave, ClockWise for hours, plus ANNA's own tools for memory, reminders and documents.
- −4 OperationsHealth checks, a log analyser, alerts before tokens expire, an admin portal.
What I Built
ANNA is Anamata’s AI assistant in Microsoft Teams. The idea is simple: the repetitive parts of the working day, like requesting leave, logging hours or finding the right policy document, shouldn’t need a separate app each. People message ANNA in Teams, and it routes the request to the right system through Claude and MCP. I own it end to end, from the first commit to production, and work out the direction together with the rest of the team.
It’s about 37,000 lines of Python, organised into a service layer, a command registry and two Claude backends.
Message flow starts at the Microsoft Bot Framework SDK. When someone messages ANNA, the bot resolves their identity through Entra ID, loads their profile, conversation history and memories, and builds a system prompt for their role and the active persona. Personas are markdown files that shape Claude’s behaviour: a default generalist, a developer mode and a support mode for non-technical users.
Everything is per user. Memory, uploaded documents and connector access are all scoped to the person asking. Connector secrets are stored encrypted, and connectors that need a login go through an OAuth flow where ANNA keeps the token, not the user’s chat. In group chats ANNA needs an @mention and leaves personal memory and history out of the context, and the personal commands only work in a direct message.
Claude integration runs behind a common interface with two backends. The CLI backend runs Claude Code as a subprocess with streaming output, passing the user’s identity to the MCP layer so every tool call is scoped to that user. The API backend calls the Anthropic SDK directly. A streaming orchestrator turns Claude’s chunked output into a single Teams message that updates as the answer is written.
Connectors are MCP servers. ANNA has its own MCP tools for memory, reminders, profiles and documents, and reaches other systems through a connector registry, for example Loket for leave balances and ClockWise for logging hours. Adding a system means registering a connector and granting access, not changing ANNA’s core.
Running it is as much of the work as building it. Health checks and a log analyser run alongside the bot, tokens that are about to expire raise an alert before they break anything, and an admin portal handles users, documents, connectors and maintenance mode.
Key Decisions
Teams as the place, Claude as the brain. People already live in Teams. Putting the assistant in the channel they have open, rather than behind yet another web app, removes the onboarding step entirely. The Bot Framework SDK handles authentication, group chat semantics and Adaptive Cards out of the box.
MCP as the integration layer. Each connected system is an MCP server, and ANNA discovers its tools at startup. Connecting a new system is mostly configuration, and the same servers work in local development with the Claude CLI.
Privacy by design, not by policy. Rather than a multi-tenant query layer, each request carries the user’s identity down to the tools, and every query is scoped to it. Group chats get no personal context at all. It’s simple, auditable, and it means one person’s request never touches another person’s data.
Honest about what it is. ANNA introduces itself as an AI assistant, not a colleague. That’s EU AI Act transparency, and it’s also just how you earn the trust to be handed real work.
Outcome
ANNA runs in production, and it runs at Anamata first, so new features are tried on our own work before a customer sees them. It’s on v1.24 after 55 releases, with about 2,500 tests behind it, and Prometheus metrics feed a Grafana dashboard for response times, token usage, active users and errors.
Source code is not public: the repository is company-internal.