<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Johan Eklund</title>
    <description>The latest articles on DEV Community by Johan Eklund (@jagduvi1).</description>
    <link>https://dev.to/jagduvi1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3862377%2F9d657778-74f3-441f-8ab0-47d7b18822cf.png</url>
      <title>DEV Community: Johan Eklund</title>
      <link>https://dev.to/jagduvi1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jagduvi1"/>
    <language>en</language>
    <item>
      <title>What MCP Is Actually For, Explained With 200 Bottles of Wine</title>
      <dc:creator>Johan Eklund</dc:creator>
      <pubDate>Mon, 27 Jul 2026 20:05:13 +0000</pubDate>
      <link>https://dev.to/jagduvi1/what-mcp-is-actually-for-explained-with-200-bottles-of-wine-4i81</link>
      <guid>https://dev.to/jagduvi1/what-mcp-is-actually-for-explained-with-200-bottles-of-wine-4i81</guid>
      <description>&lt;p&gt;Last month a national magazine wrote about people using Claude as a sommelier. The writer had inherited a cupboard of old bottles from a great-aunt, photographed the labels, and asked Claude what she had. It told her the 1982 Meursault was oxidised and past saving, and that the 1972 Bordeaux came from one of the worst vintages of the decade. Both answers were correct. The piece was still, fairly, a bit sniffy about the whole exercise.&lt;/p&gt;

&lt;p&gt;My app (&lt;a href="https://cellarion.app" rel="noopener noreferrer"&gt;https://cellarion.app&lt;/a&gt;) got a passing mention in it, in scare quotes, which is how I ended up thinking about why that experiment was doomed from the start.&lt;/p&gt;

&lt;p&gt;Claude did nothing wrong there. It gave good general answers. The problem is that "is this specific bottle worth opening" is not a general question, and no amount of world knowledge gets you to it. That gap is the entire reason MCP exists, and wine turns out to be an unusually clear way to see it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap
&lt;/h2&gt;

&lt;p&gt;A language model knows an enormous amount about wine in general. It knows that white Burgundy from the early eighties is a lottery, that 1972 was a washout in Bordeaux, that Nebbiolo needs time. All of that is in the training data, freely available, and genuinely useful.&lt;/p&gt;

&lt;p&gt;What it does not know is what is in your cellar.&lt;/p&gt;

&lt;p&gt;So the questions you actually have are the ones it cannot answer. Which of my bottles is closest to falling off. What should I open tonight given I'm cooking lamb. Am I drinking faster than I'm buying. Every one of those depends entirely on private state, and the general knowledge is nearly worthless without it.&lt;/p&gt;

&lt;p&gt;This is not a wine problem. It's the shape of most useful questions. Your codebase, your tickets, your customers, your homelab, your library. The model knows the domain and knows nothing about your instance of it.&lt;/p&gt;

&lt;p&gt;MCP is the boring plumbing that closes that gap. That's it. It is not a way to make the model smarter. It's a way to let it see your stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually built
&lt;/h2&gt;

&lt;p&gt;Cellarion is a wine cellar app. Free, open source, and I built it with friends after another service made it unreasonably hard to get my own data back out.&lt;/p&gt;

&lt;p&gt;The MCP server exposes around 60 tools over a remote endpoint, covering the things you'd expect: search the cellar, get a bottle, place it in a rack, mark it consumed, look up a wine in the shared registry, record a tasting note. Reads mostly, writes where they make sense.&lt;/p&gt;

&lt;p&gt;The interesting part is not the tool list. It's what happens when a model can chain them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four examples, described rather than pasted
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"What should I open tonight?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sounds trivial. It isn't. Answering it properly means knowing what's currently in the cellar, which of those bottles are inside their drinking window, whether anything is already open and needs finishing, and ideally what I've been drinking lately so it doesn't suggest the same Chianti for the fourth time. That's several tools and a judgement call. The model does the chaining itself; I just ask the question in the way I'd ask a person.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What goes with lamb?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The generic answer to this is free everywhere on the internet and completely useless, because it recommends wines I don't own. The useful version matches the dish against what's physically on my racks right now. Same question, entirely different answer, and the difference is access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Am I in trouble?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Buying pace versus drinking pace, bottles clustered at the end of their window, wines I have too many of. This is the one that genuinely changed my behaviour, and it's the one that would be impossible without state. It's also, I suspect, the version of this that matters in every other domain. The model is good at noticing the pattern once it can see the data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Here's what we drank last night."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the one I didn't expect to work at all. After a party I photograph the empty bottles standing on the kitchen counter and hand the picture over. Claude reads the labels, matches them against what's actually in my cellar, and marks those bottles as consumed. It lists what it's about to do before it writes anything, which given the circumstances is the correct order of events.&lt;/p&gt;

&lt;p&gt;Fifteen minutes of admin that I would simply never have done, turned into one message and a photograph taken while clearing up.&lt;/p&gt;

&lt;p&gt;It's worth noticing that this is the same input as the magazine experiment. A photograph of some bottles. In that case the model could only offer general commentary, because there was nothing behind the picture. Here the photograph resolves against real inventory and turns into an action. Identical input, completely different value, and the only thing that changed is that it can see my data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The things I didn't expect
&lt;/h2&gt;

&lt;p&gt;Some of this was surprising enough that it's the real reason I'm writing this up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your data quality becomes the model's problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I had a resolution function for matching a wine the user names against the shared registry. It worked acceptably for humans, who tolerate a bad first result and try again. It worked badly for a model, which takes the first plausible answer and proceeds confidently.&lt;/p&gt;

&lt;p&gt;The specific failure: producers whose name had been typed into the wine name field as well as the producer field. A human skims past the duplication. The matcher scored those entries wrong and the model built on the bad match without hesitating. The bug had been sitting there for months, invisible, because every human user had been silently working around it.&lt;/p&gt;

&lt;p&gt;Exposing a database to a model is a data quality audit you didn't ask for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writes are a different category from reads.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reads are forgiving. If the model fetches the wrong bottle, you notice and correct it. Writes are not, especially when they touch data other people see.&lt;/p&gt;

&lt;p&gt;Cellarion has drinking windows that are shared across every user. If a wine is marked as peaking between 2028 and 2034, everyone sees that. Letting a model write to shared reference data unsupervised is a bad idea no matter how good it is, so the workflow is that it proposes values and I confirm before anything is written. Never once regretted it.&lt;/p&gt;

&lt;p&gt;If you build one of these, split your tools by blast radius rather than by feature area, and put the friction where the damage would be permanent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;More tools is not straightforwardly better.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's a point where adding tools starts costing you accuracy, because tool selection is itself a hard problem and the model has to pick correctly before it can do anything useful. Clear names help. Descriptions that say when &lt;em&gt;not&lt;/em&gt; to use a tool help more than descriptions that say what it does. Annotations marking which tools are read-only and which are destructive help the client behave sensibly.&lt;/p&gt;

&lt;p&gt;I don't have a clean number for where the ceiling is. I do know that "expose everything" is not a strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's a fresh look at your own API.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every place my tools were confusingly named or badly scoped, the model found it by using them wrong. It can't ask a colleague what the field means. It reads the description and commits. That is a rather unforgiving reviewer and it improved the design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it doesn't help
&lt;/h2&gt;

&lt;p&gt;It doesn't taste anything. It has no idea whether your specific bottle was stored badly, which is exactly why the magazine experiment went the way it did. Photographs of a dead cupboard are the hardest possible input, because there's nothing to check the answer against.&lt;/p&gt;

&lt;p&gt;And I'd say this about the tool generally: it's a multiplier on what you bring. I get good code out of Claude because I've written code long enough to see the mistake immediately. I get good wine answers because I know enough to argue back. Ask it about something you can't evaluate and it sounds precisely as confident as it does when it's right. That's the part worth being careful about, and it doesn't go away when you add MCP. It just gets faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're thinking about building one
&lt;/h2&gt;

&lt;p&gt;Pick the thing where your private state matters more than world knowledge does. That's the whole heuristic. If the general answer is already good enough, MCP adds nothing but latency. If the general answer is useless without knowing your specifics, that's where it earns its place.&lt;/p&gt;

&lt;p&gt;For me that turned out to be two hundred bottles in my basement. It'll be something else for you.&lt;/p&gt;

&lt;p&gt;The code is open source if you want to see how the server is put together: &lt;a href="https://github.com/jagduvi1/Cellarion" rel="noopener noreferrer"&gt;github.com/jagduvi1/Cellarion&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Built an 80,000-Line Open Source Wine Cellar App in 6 Weeks with Claude</title>
      <dc:creator>Johan Eklund</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:51:05 +0000</pubDate>
      <link>https://dev.to/jagduvi1/how-i-built-an-80000-line-open-source-wine-cellar-app-in-6-weeks-with-claude-3fd</link>
      <guid>https://dev.to/jagduvi1/how-i-built-an-80000-line-open-source-wine-cellar-app-in-6-weeks-with-claude-3fd</guid>
      <description>&lt;p&gt;I'm a wine collector. I'm also a developer. And like most developers, when I couldn't find an app that did what I wanted, I built my own.&lt;/p&gt;

&lt;p&gt;The result is &lt;strong&gt;Cellarion&lt;/strong&gt; — a full-featured, self-hosted wine cellar management app (&lt;a href="https://cellarion.app" rel="noopener noreferrer"&gt;https://cellarion.app&lt;/a&gt;). Free, open source, and built almost entirely with Claude as my coding partner.&lt;/p&gt;

&lt;p&gt;In about 6 weeks, we went from zero to a production app with 80,000+ lines of code, 40 database models, 47 API routes, 290+ commits, and real users. Here's how.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I tried a few wine cellar apps. They all had the same issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your data was locked in. Exporting was either impossible or deliberately painful.&lt;/li&gt;
&lt;li&gt;Features were paywalled — sometimes basic ones.&lt;/li&gt;
&lt;li&gt;They didn't let me visualize my actual cellar layout.&lt;/li&gt;
&lt;li&gt;No community-driven wine database. Every user entering the same wines manually.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I opened a terminal and started building.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;p&gt;I went with a classic MERN stack, containerized with Docker Compose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MongoDB&lt;/strong&gt; + Mongoose for data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Express.js&lt;/strong&gt; for the API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React 19&lt;/strong&gt; for the frontend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meilisearch&lt;/strong&gt; for fuzzy wine search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose&lt;/strong&gt; to tie it all together&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;rembg&lt;/strong&gt; (Python/Flask) for automatic wine label background removal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing exotic. Proven tools that work well together and are easy to self-host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter Claude
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. I didn't build this alone. Claude was my development partner from day one — not as a code generator I copy-paste from, but as a genuine collaborator.&lt;/p&gt;

&lt;h3&gt;
  
  
  What that actually looks like
&lt;/h3&gt;

&lt;p&gt;A typical session: I describe what I want at a high level. Claude reads the existing codebase, understands the patterns, and implements it — backend route, frontend page, database model, tests. Then I review, test, and we iterate.&lt;/p&gt;

&lt;p&gt;Some examples from the last few weeks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I want users to be able to share their cellar with friends and family"&lt;/strong&gt;&lt;br&gt;
Claude implemented the full sharing system — invite by email, role-based permissions (viewer/editor/admin), pending share resolution when someone registers, notifications, and the UI for managing it. Across maybe 15 files. In one session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"The wine detail pages need structured data for Google"&lt;/strong&gt;&lt;br&gt;
Claude added JSON-LD schema markup, Open Graph meta tags, a server-side pre-rendering route for crawlers, and a sitemap generator. When Google Search Console flagged an issue with Product schema on wines without reviews, Claude diagnosed it and fixed it the same day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"I want a 3D view of the cellar"&lt;/strong&gt;&lt;br&gt;
This one was fun. Claude built an interactive 3D cellar visualization where you can explore your racks, see bottles placed in their slots, and navigate around. Using Three.js and React Three Fiber, integrated with the existing rack and bottle data.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Claude is actually good at
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Understanding context.&lt;/strong&gt; It reads your codebase and follows your patterns. My API routes all follow the same structure, and Claude picks that up immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-stack thinking.&lt;/strong&gt; It doesn't just write a backend endpoint — it writes the model, the route, the API client function, the React page, and the tests. All consistent with the existing code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The boring stuff.&lt;/strong&gt; GDPR compliance, data export endpoints, audit logging, rate limiting, input validation. The stuff you know you need but really don't want to write. Claude does it thoroughly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iteration speed.&lt;/strong&gt; I can describe a feature, get a working implementation, test it, and ship it in a single session. That feedback loop is incredible for a solo/small team.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What it's not
&lt;/h3&gt;

&lt;p&gt;Claude isn't magic. I still need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Know what I want.&lt;/strong&gt; Claude is a great builder, but you need to be the architect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review everything.&lt;/strong&gt; It makes mistakes. It sometimes over-engineers. It occasionally misses edge cases that only a domain expert would catch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make product decisions.&lt;/strong&gt; Which features matter? What's the right UX? What should be free vs. paid? That's all human judgment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test in production.&lt;/strong&gt; Docker, real data, real users — no AI replaces that.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Going Open Source
&lt;/h2&gt;

&lt;p&gt;This was a deliberate choice. If your biggest complaint about existing apps is data lock-in, you'd better put your money where your mouth is.&lt;/p&gt;

&lt;p&gt;Cellarion is AGPL-3.0. You can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-host it on your own server&lt;/li&gt;
&lt;li&gt;Fork it and modify it&lt;/li&gt;
&lt;li&gt;Contribute features back&lt;/li&gt;
&lt;li&gt;Export all your data whenever you want&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The hosted version at &lt;a href="https://cellarion.app" rel="noopener noreferrer"&gt;cellarion.app&lt;/a&gt; is there for people who don't want to run Docker on a VPS, but the code is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the App
&lt;/h2&gt;

&lt;p&gt;After 6 weeks of building, here's what Cellarion can do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cellars &amp;amp; Racks&lt;/strong&gt; — Create cellars with customizable rack layouts that match your real setup. Place bottles in specific slots.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3D Cellar View&lt;/strong&gt; — Explore your cellar in an interactive 3D environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Wine Database&lt;/strong&gt; — Search a community-maintained database instead of entering wine details manually every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Drink Windows&lt;/strong&gt; — Maturity tracking so you know when a wine is ready to drink, at its peak, or past it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Statistics&lt;/strong&gt; — See your collection broken down by region, grape, country, value, and more. Including a world map.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cellar Sharing&lt;/strong&gt; — Invite family or friends to view or manage your cellar together.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Label Scanning&lt;/strong&gt; — Take a photo of a wine label and let AI identify the wine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full Data Export&lt;/strong&gt; — One click and you have all your data. No lock-in, ever.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GDPR Compliant&lt;/strong&gt; — Proper consent management, data portability, and right to deletion.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Time to build&lt;/td&gt;
&lt;td&gt;~6 weeks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lines of code&lt;/td&gt;
&lt;td&gt;80,000+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Commits&lt;/td&gt;
&lt;td&gt;290+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database models&lt;/td&gt;
&lt;td&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API routes&lt;/td&gt;
&lt;td&gt;47&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend tests&lt;/td&gt;
&lt;td&gt;266&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend tests&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;AGPL-3.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hosted version:&lt;/strong&gt; &lt;a href="https://cellarion.app" rel="noopener noreferrer"&gt;cellarion.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/jagduvi1/Cellarion" rel="noopener noreferrer"&gt;github.com/jagduvi1/Cellarion&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-host:&lt;/strong&gt; &lt;code&gt;docker-compose up --build&lt;/code&gt; and you're running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're into wine, give it a try. If you're a developer, check out the code. And if you find something missing, open an issue — or better yet, a PR.&lt;/p&gt;

&lt;p&gt;I'd love to hear what you think.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>webdev</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
