11 min read
‧ 11 min read

Like a lot of projects on Metabase’s Cloud team, this one started as a boring corporate-IT problem that quietly turned into something I use every single day. We set out to consolidate a pile of AI logins behind one managed front door. We came out the other side running a self-hosted chat platform, with a couple of fixes upstreamed to the OSS project, and a Slack agent that answers questions about our infrastructure.
The original ask was modest and sane. AI tooling was sprawling everywhere: everyone had their own ChatGPT login, their own Anthropic key, a Claude Desktop here, a Cursor license there. Tokens for GitHub, Notion, and Slack were copy-pasted into a dozen different places on a dozen different laptops.
We wanted one place that:
That was the whole charter. Everything else in this post is stuff we piled on top.
I’d been a heavy MCP user locally for a while: Claude Code and Cursor wired up to GitHub, Notion, and Slack, plus the Anthropic memory and sequential-thinking servers, context7 docs, and many more. The thing that kept nagging me was that the interesting part was never any single model. It was the idea of a single frontend that could fan out to many models (or just point at OpenRouter), fork a conversation mid-stream to a different provider, share a prompt library across a team, and most importantly speak MCP, so we could plug our own tools into it.
So we drew up a short list of self-hostable, open-source chat frontends:
My evaluation criteria, in order of how much I actually cared:
The differences between the three chat frontends in feel were small. The differences in operability and MCP depth were not.
Thanks largely to the legwork our ops engineer put in standing each option up for real, we were able to compare the three chat frontend candidates.
Open WebUI got a genuine deployment and a couple of weeks of tire-kicking. It’s polished, with all the enterprise-y admin knobs you’d want. But the dealbreaker showed up fast: first-class support was effectively OpenAI-only. To get Anthropic working you installed a community “function,” literally a chunk of Python someone wrote to talk to the Anthropic API. For a tool we wanted to standardize the whole company on, “paste in a third-party script to reach one of your two primary providers” wasn’t a great place to start. MCP support at the time was similarly second-class.
LobeChat was fine. Nice enough to look at, but nothing about it pulled ahead on the things I cared about, and a few things (deployment story, MCP configuration) felt thinner.
LibreChat won on the criteria that mattered:
So LibreChat it was. We stood up the chat, wired in a dedicated service account for the GitHub integration, plugged in our provider keys, and we were off.
The first time I asked our LibreChat “what are the last ten PRs merged by the embedding team, and what are their product docs in Notion?”, it just went and did it. LibreChat chained the GitHub MCP and the Notion MCP together. I was genuinely a little stunned at how well it worked.
Over the following weeks we kept adding MCP servers: GitHub, Notion, Grafana, and eventually our own Metabase MCP server. Each MCP landed as a config change in the chart. Each one made the whole thing more useful, and the surprising part was how usable the combination became. At that point the frontend was almost incidental; the value was the graph of tools behind it.
The flip side of that is a lesson worth stating plainly: an MCP server is only as good as the API it wraps. The Notion MCP, for instance, is hamstrung because it still goes through Notion’s search under the hood, and Notion search is… Notion search. That pushed us toward setting up our own RAG and memory tooling instead of having every conversation re-search Notion from scratch.

Nobody should pretend that self-hosting a fast-moving, early-beta OSS AI product is smooth. A non-exhaustive list of what we hit, and how we worked around it:
The part that felt most like real engineering — as opposed to configuration and workarounds — was contributing fixes back upstream: we didn’t just consume an open-source project, we contributed back to it. To make it more interesting, our AI coding tools themselves struggled with some of the more cutting-edge issues around MCP auth that were simply too new to have any effective training.
Once we leaned hard on OAuth-based MCP servers, things got flaky in a way that was maddening to debug. One of our engineers dug in and found a real bug in how LibreChat handled OAuth refresh flows. We fixed it in our own deployment first, confirmed the servers that had been the main culprits went rock-solid, and then opened a fix upstream. The before/after on the previously-flaky servers was night and day.
Another bug fix we contributed: bringing the still-experimental memory feature online surfaced a bug I had to patch by hand in the database.
That’s the deal with running beta features in production: you become a contributor whether you planned to or not. The broader takeaway stuck with the whole team: vibe-coding and good OSS will get you to “working” eventually, but you still need to figure out which problems you’ll need to think through. Self-hosting forced us to actually understand the system, which paid off later.
By early this year, LibreChat had become part of how I actually worked. My day-to-day experience had gotten extremely smooth — mostly thanks to the shared RAG memory and the sheer volume of use. The natural next question: why does all this power have to live behind a web UI I have to go open?
So at the Cloud team’s offsite hackathon, I built LibreBot, a Slack agent on top of our LibreChat deployment. You DM it or @mention it, and it figures out which tool to reach for on its own across Linear, Metabase, Notion, GitHub, Grafana, Slack, and a web-search fallback.
A few design decisions that mattered:
Within days, people were using the Slack agent for things I hadn’t scripted: summarizing what a team merged yesterday, triaging an incident, doing a multi-week reliability deep-dive on one of our internal instances by correlating Grafana metrics, Loki logs, and Slack threads in a single conversation. A teammate’s separate hackathon project — a pricing knowledge-base agent — even folded in, so pricing questions were now just an @mention away.

The least glamorous work — the Helm chart, the GitOps flow, the RAG and pgvector deployment, the embeddings plumbing, the runbook, the upstream fixes — is what everything else got to stand on.