<?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: Flavio Sacca</title>
    <description>The latest articles on DEV Community by Flavio Sacca (@flavio_sacca_b0ab52158604).</description>
    <link>https://dev.to/flavio_sacca_b0ab52158604</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%2F4066428%2Fb5406485-e156-4ab9-8e7f-14c82fb5aac1.jpeg</url>
      <title>DEV Community: Flavio Sacca</title>
      <link>https://dev.to/flavio_sacca_b0ab52158604</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/flavio_sacca_b0ab52158604"/>
    <language>en</language>
    <item>
      <title>How I Built an AI Coach: Claude + Garmin, Yazio and FitDays via MCP, with a Git Repo as Memory</title>
      <dc:creator>Flavio Sacca</dc:creator>
      <pubDate>Tue, 06 Oct 2026 13:08:47 +0000</pubDate>
      <link>https://dev.to/flavio_sacca_b0ab52158604/how-i-built-an-ai-coach-claude-garmin-yazio-and-fitdays-via-mcp-with-a-git-repo-as-memory-47gl</link>
      <guid>https://dev.to/flavio_sacca_b0ab52158604/how-i-built-an-ai-coach-claude-garmin-yazio-and-fitdays-via-mcp-with-a-git-repo-as-memory-47gl</guid>
      <description>&lt;h2&gt;
  
  
  The starting point
&lt;/h2&gt;

&lt;p&gt;My Garmin watch knows how much I sleep and tracks all my health metrics, Yazio knows what and how much I eat and my FitDays scale knows how much I weigh and (more or less) how much lean mass I have.&lt;/p&gt;

&lt;p&gt;I had the data but nobody able to read it. Turning it into something useful (a meal plan, a training program, an answer to "why am I not improving?") takes someone who can cross-reference sleep, calories and weight and turn them into a decision: a nutritionist, a personal trainer. So I asked myself: what if an AI did that reading, with my real numbers, and answered my questions in real time?&lt;/p&gt;

&lt;p&gt;So I connected Claude to all three through MCP (Model Context Protocol) and gave it a clear role: nutritionist and personal trainer, with written rules, persistent memory in a git repo, and a set of commands for recurring tasks. The result is an AI coach that reads my real data, writes plans, syncs them to my Garmin calendar, and remembers why it made every decision.&lt;/p&gt;

&lt;p&gt;In this article you'll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how I wired up the MCP servers (with alternatives if you use MyFitnessPal or a Renpho scale);&lt;/li&gt;
&lt;li&gt;the &lt;code&gt;CLAUDE.md&lt;/code&gt; that turns a generic chatbot into a coach;&lt;/li&gt;
&lt;li&gt;how file-based memory works, and why it's the most important part;&lt;/li&gt;
&lt;li&gt;the slash commands I use every day and every week;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I'm Italian, so the original prompts and file names in the repo are in Italian. The snippets in this article are translated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The architecture: three sources of truth and a repo that remembers
&lt;/h2&gt;

&lt;p&gt;The system has two halves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first is the MCP servers, which give Claude read and write access to my data&lt;/li&gt;
&lt;li&gt;The second is a plain git repository that acts as brain and memory: Claude reads it at the start of every session and updates it at the end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each server owns one domain, and the coach must not mix them up:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Domain&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;What it provides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Activity and recovery&lt;/td&gt;
&lt;td&gt;Garmin&lt;/td&gt;
&lt;td&gt;workouts, sleep, HRV, resting heart rate, body battery, training status&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nutrition&lt;/td&gt;
&lt;td&gt;Yazio (or MyFitnessPal)&lt;/td&gt;
&lt;td&gt;calories and macros consumed, foods, water&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Weight and body composition&lt;/td&gt;
&lt;td&gt;FitDays (or Renpho)&lt;/td&gt;
&lt;td&gt;weight, body fat, lean mass&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The repo looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coach_planner/
├── CLAUDE.md              # role, rules, red flags: the "system prompt"
├── RIFERIMENTI.md         # (references) MCP server gotchas
├── .claude/commands/      # slash commands (/checkin, /settimana, ...)
├── profilo/               # (profile)
│   ├── anagrafica.md      # stable data, equipment, body measurements
│   ├── obiettivi.md       # goals: ACTIVE / ON HOLD / HISTORY
│   └── preferenze.md      # what I eat, how much time I have
├── piani/                 # (plans)
│   ├── dieta/CORRENTE.md          # current diet
│   └── allenamento/CORRENTE.md    # current training
├── memoria/               # (memory)
│   ├── DIARIO.md          # session log, append-only
│   ├── apprendimenti.md   # learnings: what works for me
│   └── decisioni.md       # decisions: why we chose what
└── dati/                  # append-only CSVs: weight, nutrition, workouts, weeks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using Markdown and CSV files inside git is deliberate: both Claude and I can read them, I can edit them by hand when needed and every session ends with a commit, so if the coach makes a bad call I have the full history of what it knew and why it decided what it did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the MCP servers
&lt;/h2&gt;

&lt;p&gt;All three servers I use are community projects built on unofficial or reverse-engineered APIs. They work well, but they can break overnight: keep that in mind before building anything critical on top of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Garmin
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/Taxuspt/garmin_mcp" rel="noopener noreferrer"&gt;Taxuspt/garmin_mcp&lt;/a&gt; exposes 110+ tools: activities, sleep, HRV, weight and, most importantly, creating and scheduling workouts on the Garmin calendar. You authenticate once from the terminal, and the OAuth tokens are stored in &lt;code&gt;~/.garminconnect&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tokens last about six months. When they expire, the fix is to re-run the auth with &lt;code&gt;--force-reauth&lt;/code&gt;, not to retry the call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Yazio
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/fliptheweb/yazio-mcp" rel="noopener noreferrer"&gt;fliptheweb/yazio-mcp&lt;/a&gt; runs with &lt;code&gt;npx&lt;/code&gt; and reads daily summaries, consumed items, goals and water intake. It can also add and remove foods from your diary.&lt;/p&gt;

&lt;h3&gt;
  
  
  FitDays
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/roquerodrigo/fitdays-mcp-server" rel="noopener noreferrer"&gt;roquerodrigo/fitdays-mcp-server&lt;/a&gt; is read-only: weight history, latest measurement, users and devices on the account. It requires Node.js 22+.&lt;/p&gt;

&lt;h3&gt;
  
  
  The configuration
&lt;/h3&gt;

&lt;p&gt;In the project root I keep a &lt;code&gt;.mcp.json&lt;/code&gt; that reads credentials from environment variables, so they never end up in the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"garmin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uvx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3.12"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"--from"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
               &lt;/span&gt;&lt;span class="s2"&gt;"git+https://github.com/Taxuspt/garmin_mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"garmin-mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"yazio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yazio-mcp"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"YAZIO_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${YAZIO_USERNAME}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"YAZIO_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${YAZIO_PASSWORD}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fitdays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fitdays-mcp-server"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FITDAYS_EMAIL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${FITDAYS_EMAIL}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"FITDAYS_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${FITDAYS_PASSWORD}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Garmin server needs no credentials in the config: it uses the saved tokens. If its 110 tools flood the context, the &lt;code&gt;GARMIN_ENABLED_TOOLS&lt;/code&gt; variable lets you pass an allowlist of just the tools you need.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternatives: MyFitnessPal and Renpho
&lt;/h3&gt;

&lt;p&gt;If you use different apps, the system works the same way: only the tool names referenced in &lt;code&gt;CLAUDE.md&lt;/code&gt; and the commands change.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MyFitnessPal&lt;/strong&gt; → &lt;a href="https://github.com/AdamWalt/myfitnesspal-mcp-python" rel="noopener noreferrer"&gt;AdamWalt/myfitnesspal-mcp-python&lt;/a&gt;. Reads and writes diary, measurements, exercises and water. One advantage over Yazio: it has &lt;code&gt;mfp_set_goals&lt;/code&gt;, so the coach can update calorie and macro targets on its own. Requires Python 3.10–3.12 and preferably authenticates using cookies from a logged-in browser session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Renpho&lt;/strong&gt; → &lt;a href="https://github.com/StartupBros-com/renpho-mcp-server" rel="noopener noreferrer"&gt;StartupBros-com/renpho-mcp-server&lt;/a&gt;. Full body composition, trends over N days, and diagnostic tools for measurements that don't sync. It works with the Renpho Health app (not the legacy one) and you install it by cloning the repo and building it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  CLAUDE.md: from chatbot to coach
&lt;/h2&gt;

&lt;p&gt;Connecting the data is the easy part. Without instructions, Claude with access to the apps is a great analyst that tells you generic things. &lt;code&gt;CLAUDE.md&lt;/code&gt; is where I tell it who it is, what it can do and, above all, what it must not do. Here are the parts that made the difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  An opening ritual and a closing one
&lt;/h3&gt;

&lt;p&gt;Every session starts by reading the profile, the current plans, the last lines of the log and all the learnings. And it always ends the same way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Session close — ALWAYS&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Max 8 lines in &lt;span class="sb"&gt;`memoria/DIARIO.md`&lt;/span&gt;.
&lt;span class="p"&gt;2.&lt;/span&gt; Plan changed → archive to &lt;span class="sb"&gt;`archivio/YYYY-MM-DD-*.md`&lt;/span&gt;, rewrite &lt;span class="sb"&gt;`CORRENTE.md`&lt;/span&gt;,
   rationale in &lt;span class="sb"&gt;`memoria/decisioni.md`&lt;/span&gt;.
&lt;span class="p"&gt;3.&lt;/span&gt; New data → append to &lt;span class="sb"&gt;`dati/*.csv`&lt;/span&gt; (never rewrite, never duplicate dates).
&lt;span class="p"&gt;4.&lt;/span&gt; &lt;span class="sb"&gt;`git add -A &amp;amp;&amp;amp; git commit -m "session YYYY-MM-DD: &amp;lt;what&amp;gt;"`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what makes the memory reliable: it doesn't depend on Claude "remembering" to save, it's a mandatory step.&lt;/p&gt;

&lt;h3&gt;
  
  
  A coach that lasts years, not a workout generator
&lt;/h3&gt;

&lt;p&gt;My favorite section is about goals. One primary goal at a time, each with a measurable exit criterion, and transitions handled explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; One primary goal at a time. If I ask for a new one while one is active,
  stop: either we close the old one or the new one goes ON HOLD. Don't stack them.
&lt;span class="p"&gt;-&lt;/span&gt; Flag conflicts. Calorie deficit + maximum strength gain, or high-volume
  running + aggressive hypertrophy, don't coexist. Tell me the cost, propose a
  sequence, not a silent compromise.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this rule, an LLM tends to please you: ask for strength and fat loss and it hands you a plan that does both badly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writes always require confirmation
&lt;/h3&gt;

&lt;p&gt;The coach can create workouts on Garmin and log foods on Yazio, but never on its own initiative. It first shows exactly what it's about to write, then waits for my OK. If there are five operations, it lists them all and asks for a single confirmation, so a session doesn't turn into a barrage of "yes".&lt;/p&gt;

&lt;h3&gt;
  
  
  Non-negotiable rules and red flags
&lt;/h3&gt;

&lt;p&gt;There are numbers the coach can't cross, whatever I ask: maximum deficit of 20–25% of TDEE, an absolute calorie floor, protein between 1.6 and 2.2 g/kg, a deload every 4–6 weeks. TDEE must be recalculated from real data (average calories vs. weight change) as soon as there are two weeks of history, not estimated with a formula.&lt;/p&gt;

&lt;p&gt;Then there's a list of &lt;strong&gt;red flags&lt;/strong&gt; that stop everything and send me to a doctor: chest pain, dizziness during exercise, HRV dropping and resting heart rate rising for more than 10 days, under 5 hours of sleep on average, weight loss that's too fast, signs of a disordered relationship with food. The coach is not a doctor, and it knows it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to read the scale
&lt;/h3&gt;

&lt;p&gt;I weigh myself every morning, and that's a risk: a coach that comments on every weigh-in will drive you crazy. So the rule is strict: never comment on a single weigh-in, reason only on 7-day moving averages, compare averages with averages. And never use weight as a motivational lever, in either direction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory: four files with four different jobs
&lt;/h2&gt;

&lt;p&gt;The weak spot of any "AI coach" is that every conversation starts from scratch. I solved it with Markdown files split by purpose, because a single notes file becomes unreadable within weeks.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memoria/DIARIO.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;what happened in each session&lt;/td&gt;
&lt;td&gt;append-only, max 8 lines per session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memoria/apprendimenti.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;what works for me&lt;/td&gt;
&lt;td&gt;read in full every session; disproven hypotheses get struck through, not deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;memoria/decisioni.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;why we chose what, and the rejected alternatives&lt;/td&gt;
&lt;td&gt;never delete old decisions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dati/*.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;time series of weight, nutrition, workouts, weeks&lt;/td&gt;
&lt;td&gt;append-only, no duplicate dates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The split between log and learnings is the heart of the system: the log is a chronicle, the learnings are knowledge; a pattern is promoted from the log to the learnings only when it has repeated at least three times, and when a hypothesis is disproven it stays visible, struck through, with the new one below it.&lt;/p&gt;

&lt;p&gt;The decisions file also records the &lt;strong&gt;rejected alternatives&lt;/strong&gt;. It sounds like bureaucracy, but it's what stops the coach from re-proposing, two months later, the same idea you had already ruled out, without knowing why.&lt;/p&gt;

&lt;p&gt;The goals file (&lt;code&gt;obiettivi.md&lt;/code&gt;) is also built to last:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ACTIVE&lt;/strong&gt;: a single goal, with an exit criterion and a review date. If it's empty, the coach refuses to produce plans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ON HOLD&lt;/strong&gt;: what I want but am not pursuing now, with the reason and the prerequisite to unlock it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HISTORY&lt;/strong&gt;: every closed goal, with its outcome and why it ended. Never deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the CSVs mean Claude doesn't have to call the MCP servers for every calculation. For planning it works on local data; it only queries the servers when it needs fresh data. Over time, when the log exceeds 200 lines, the weekly review command archives old entries and leaves a monthly summary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The commands: the whole workflow in eight files
&lt;/h2&gt;

&lt;p&gt;Recurring operations live in &lt;code&gt;.claude/commands/&lt;/code&gt;, one Markdown file per command. That way I don't rewrite the prompt for a weekly review every time, and the coach always does it the same way. (Command names are in Italian; the translation is in brackets.)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;When&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/obiettivo&lt;/code&gt; (goal)&lt;/td&gt;
&lt;td&gt;when direction changes&lt;/td&gt;
&lt;td&gt;orientation session: 90 days of data, max 5 questions, reality check. You leave with a written goal, not a workout plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/nuovo-piano&lt;/code&gt; (new plan)&lt;/td&gt;
&lt;td&gt;after &lt;code&gt;/obiettivo&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;diet, training types, calendar for the whole mesocycle, sync to Garmin&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/checkin&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;every morning&lt;/td&gt;
&lt;td&gt;weight, yesterday's nutrition, sleep and HRV: 3 numbers, 1 deviation, 1 action. Max 10 lines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/settimana&lt;/code&gt; (week)&lt;/td&gt;
&lt;td&gt;every week&lt;/td&gt;
&lt;td&gt;real TDEE, red flag and goal check, at most one parameter changed, report in Markdown and PDF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/spesa&lt;/code&gt; (groceries)&lt;/td&gt;
&lt;td&gt;when needed&lt;/td&gt;
&lt;td&gt;shopping list for a typical week, grouped by aisle, with a PDF to use at the supermarket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/viaggio&lt;/code&gt; (trip)&lt;/td&gt;
&lt;td&gt;holidays, surprises, illness&lt;/td&gt;
&lt;td&gt;temporary adjustment without touching the current plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/analisi&lt;/code&gt; (audit)&lt;/td&gt;
&lt;td&gt;after manual edits&lt;/td&gt;
&lt;td&gt;audit of the existing plan against today's preferences and goal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/consolida&lt;/code&gt; (consolidate)&lt;/td&gt;
&lt;td&gt;when memory grows&lt;/td&gt;
&lt;td&gt;compacts the log and promotes recurring patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/nuovo-piano&lt;/code&gt;: three separate confirmations
&lt;/h3&gt;

&lt;p&gt;The most complex command is also the one with the most checkpoints. First it shows the diet numbers (TDEE, target, macros) and waits for an OK. Then it proposes training &lt;strong&gt;types&lt;/strong&gt;, not the actual program yet, each with one line of reasoning tied to the goal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;For each proposed type, say in one line why I recommend it for THIS goal
(e.g. "weights 3x/week: preserve lean mass during the deficit" /
"Z2 running 2x/week: aerobic base without interfering with strength recovery").
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only then does it write the plans, generate a menu with 3–4 interchangeable options per meal, and propose the calendar for the entire mesocycle. Then, if the user accepts, it syncs and schedules the workouts to Garmin. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;/settimana&lt;/code&gt;: one parameter at a time
&lt;/h3&gt;

&lt;p&gt;The weekly review recalculates TDEE from real data with a simple formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;real TDEE = average kcal − (weight change × 7700 / days)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it decides, in this order: red flags, goal status against the exit criterion, any correction. The rule is to change &lt;strong&gt;at most one parameter at a time&lt;/strong&gt;, with the rationale written in &lt;code&gt;decisioni.md&lt;/code&gt;. If you change calories and volume together and things improve, you don't know what worked.&lt;/p&gt;

&lt;p&gt;If the exit criterion has already been met, the coach must not keep going on autopilot: it sends me back to &lt;code&gt;/obiettivo&lt;/code&gt; to close the goal and decide the next phase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prompt design lessons
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Separate the sources of truth.&lt;/strong&gt; Weight from FitDays, food from Yazio, activity from Garmin. Garmin and Yazio both know something about weight and calories, and without an explicit rule the model picks whichever source it happens to hit. If two sources disagree, it must say so instead of picking silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write numbers, not adjectives.&lt;/strong&gt; "Easy run" meant 170 bpm to me. "Z2 run, HR &amp;lt; 145, max 40 minutes" didn't. That applies to Claude as much as to me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make the model stop.&lt;/strong&gt; The most useful commands have points where Claude must stop and ask: types before the program, calendar before the sync, report before changes in &lt;code&gt;/analisi&lt;/code&gt;. Without them, an LLM tends to produce everything in one go and get you to accept the whole package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Give it permission to say no.&lt;/strong&gt; Calorie floor, red flags, missing goal: there are situations where the right answer is not producing a plan. It has to be written explicitly, otherwise the model always finds a way to please you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Remove the noise from answers.&lt;/strong&gt; "No repeated disclaimers, no 'consult a professional' in every answer" sounds like asking for less caution. It's the opposite: with precisely defined red flags, the warning comes when it's needed and doesn't get lost among a hundred generic ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limits
&lt;/h2&gt;

&lt;p&gt;A few honest caveats before you copy the project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's not a doctor.&lt;/strong&gt; &lt;code&gt;CLAUDE.md&lt;/code&gt; says so explicitly: no diagnoses, no blood tests, no medication. If you have a medical condition, the coach is for organizing, not deciding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The APIs are unofficial.&lt;/strong&gt; Yazio, FitDays, Renpho and MyFitnessPal don't offer public APIs: the servers rely on reverse engineering and can stop working after an app update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This is health data.&lt;/strong&gt; Weight, body composition, sleep, heart rate. Keep credentials in environment variables and &lt;code&gt;.env&lt;/code&gt; in &lt;code&gt;.gitignore&lt;/code&gt;, and think twice before making a repo with your data public.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bioimpedance is imprecise.&lt;/strong&gt; The absolute body-fat value from a home scale is worth little; the trend over weeks is worth a lot more. The coach is instructed to reason that way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have ideas to improve the commands or just want the source code of the repo, let me know in the comments.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>claude</category>
      <category>agents</category>
    </item>
    <item>
      <title>Building an enterprise Claude Code marketplace</title>
      <dc:creator>Flavio Sacca</dc:creator>
      <pubDate>Fri, 18 Sep 2026 12:46:46 +0000</pubDate>
      <link>https://dev.to/flavio_sacca_b0ab52158604/building-an-enterprise-claude-code-marketplace-5d3j</link>
      <guid>https://dev.to/flavio_sacca_b0ab52158604/building-an-enterprise-claude-code-marketplace-5d3j</guid>
      <description>&lt;p&gt;If you're rolling out Claude Code across a company, at some point you’ll eventually hit the same question I did: how do you distribute &lt;em&gt;internally built&lt;/em&gt; artifacts (skills, commands, subagents and MCPs) to every developer, without asking each person to hand-configure things one by one and, maybe, forcing them to use only some tool?&lt;/p&gt;

&lt;p&gt;The answer turned out to be simpler than expected: &lt;strong&gt;Claude Code plugin marketplaces&lt;/strong&gt;, hosted on a regular Git repo (GitLab, GitHub…). Here's what I learned setting one up, including where I initially got the mental model wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The building block: a plugin marketplace
&lt;/h2&gt;

&lt;p&gt;A marketplace is just a Git repo with one manifest file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-marketplace/
└── .claude-plugin/
    └── marketplace.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acme-internal-marketplace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACME Platform Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform-team@acme.example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acme-sentry-http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./plugins/acme-sentry-http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sentry integration via remote MCP server"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyone on the team runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;/plugin marketplace add &amp;lt;your-repo-url&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/plugin install acme-sentry-http
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and that's it: &lt;strong&gt;one command installs everything the plugin bundles&lt;/strong&gt;, whether that's an MCP server, a skill, a command or a subagent. There's no separate "install the skill" vs "install the agent" step.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A quick note&lt;/em&gt;: your own marketplace doesn't have to contain only internally-built artifacts. A plugin entry's source can point anywhere: a subfolder of your repo or straight at someone else's GitHub repo, so you can re-list a good third-party plugin under your own governance umbrella instead of telling people to go find it themselves. &lt;/p&gt;

&lt;p&gt;Say your team likes “ponytail”, an unofficial but well-maintained plugin: you'd vet it once, pin it to a commit and add it to your marketplace.json.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acme-internal-marketplace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"owner"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACME Platform Team"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"platform-team@acme.example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acme-sentry-http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./plugins/acme-sentry-http"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acme-internal-crm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./plugins/acme-internal-crm"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ponytail"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"repo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dietrichgebert/ponytail"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Third-party plugin, vetted and pinned by the Platform Team — not maintained by ACME."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The four things a plugin can bundle
&lt;/h2&gt;

&lt;p&gt;This was the part that took me a minute to get straight, because they all superficially "teach Claude how to do something", but the trigger mechanism and execution context are&lt;br&gt;
genuinely different for each.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;Runs where&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always connected once plugin is enabled&lt;/td&gt;
&lt;td&gt;External process/API&lt;/td&gt;
&lt;td&gt;Giving Claude access to a system (CRM, monitoring, fitness data...)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User types &lt;code&gt;/name&lt;/code&gt; explicitly&lt;/td&gt;
&lt;td&gt;Main conversation&lt;/td&gt;
&lt;td&gt;A prompt you'd otherwise retype constantly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skill&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Claude decides it's relevant, based on the task&lt;/td&gt;
&lt;td&gt;Main conversation&lt;/td&gt;
&lt;td&gt;Domain knowledge / house conventions Claude should apply without being asked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;User or Claude invokes it explicitly or automatically&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Isolated context&lt;/strong&gt;, returns only the final result&lt;/td&gt;
&lt;td&gt;Heavy, multi-step work you don't want cluttering the main thread&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Focus on - Agents
&lt;/h2&gt;

&lt;p&gt;The one that surprised me most is the &lt;strong&gt;agent&lt;/strong&gt;, because it's the only one of the four&lt;br&gt;
that runs in a separate context window. If a task needs to crawl through dozens of files&lt;br&gt;
or call a tool a hundred times, doing it as an agent keeps that noise out of your main&lt;br&gt;
conversation, so you can keep only a clean summary instead of the full trace.&lt;/p&gt;

&lt;p&gt;A minimal agent looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;garmin-analyst&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Analyzes training load, HRV and sleep trends from Garmin data. Use PROACTIVELY when the user asks about recovery or overtraining.&lt;/span&gt;
&lt;span class="na"&gt;tools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mcp__garmin&lt;/span&gt;
&lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sonnet&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

You are a sports physiology specialist. When given Garmin data (HRV, training load, sleep), interpret trends cautiously, flag overtraining patterns, and never give direct medical advice.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Focus on - MCP servers
&lt;/h2&gt;

&lt;p&gt;I ended up needing three distinct&lt;br&gt;
patterns:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Remote HTTP, SaaS-hosted (e.g. Sentry)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sentry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.sentry.dev/mcp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No secrets in the file — either the remote server handles OAuth itself, or you&lt;br&gt;
interpolate an env var for headers (&lt;code&gt;"Authorization": "Bearer ${SENTRY_MCP_TOKEN}"&lt;/code&gt;). You should never hardcode a token in something that lives in a shared repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Stdio, published npm/pip package&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-filesystem@1.2.3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${WORKSPACE_DIR:-.}"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember to always &lt;strong&gt;pin the version.&lt;/strong&gt; &lt;code&gt;npx -y package-name&lt;/code&gt; with no version means every user auto-pulls whatever gets published next, including a compromised release if that&lt;br&gt;
ever happens upstream.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stdio, internally built binary&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"acme-crm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${CLAUDE_PLUGIN_ROOT}/bin/acme-crm-mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"--config"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${CLAUDE_PLUGIN_ROOT}/config/crm-config.json"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ACME_CRM_API_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://crm-internal.acme.example.com/api"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;${CLAUDE_PLUGIN_ROOT}&lt;/code&gt; resolves to wherever the plugin got installed, so the path&lt;br&gt;
works on any machine. Ship the compiled binary inside the plugin for zero-setup onboarding, or point to a pre-installed binary on PATH if it's already distributed some other way (apt/brew/choco/provisioning script): the trade-off is repo size and zero-config vs. syncing two release pipelines.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enterprise governance
&lt;/h2&gt;

&lt;p&gt;Everything above is opt-in (you make things available, users choose) and doesn’t lock out other sources (users can bypass you repo and install an artifact from any repo).&lt;/p&gt;

&lt;p&gt;If you need to actually force the issue instead there are two separate knobs, and it's worth knowing them in order because they lock down two different layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Restricting MCP servers.&lt;/strong&gt; &lt;em&gt;allowedMcpServers&lt;/em&gt; + &lt;em&gt;allowManagedMcpServersOnly: true&lt;/em&gt; in managed settings restricts which MCP servers are allowed to run at all, regardless of where they'd otherwise come from (plugin, project .mcp.json, or a user's own config). This is the narrower lock: it doesn't touch what plugins, skills or commands someone installs, it only decides whether the MCP connections inside them are permitted to actually start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowedMcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sentry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acme-crm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"filesystem"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowManagedMcpServersOnly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Restricting plugins/marketplaces.&lt;/strong&gt; &lt;em&gt;strictKnownMarketplaces&lt;/em&gt; in managed settings restricts which marketplaces Claude Code will even talk to. Set this and any /plugin marketplace add pointing somewhere not on your list (including Anthropic's own default marketplace if not specified) is rejected outright. &lt;/p&gt;

&lt;p&gt;Nothing outside your approved marketplace(s) is even visible to /plugin, let alone installable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extraKnownMarketplaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"acme-internal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://gitlab.acme.example.com/platform/claude-marketplace.git"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"strictKnownMarketplaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enabledPlugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"acme-sentry-http@acme-internal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"acme-internal-crm@acme-internal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stack the two and you get the pairing you actually want for governance: MCP servers restricted (only connections coming from your approved plugins are allowed to run) and plugins restricted (only your marketplace is even reachable) while still leaving you room to fold in vetted external tools under your own review, instead of forcing everything to be built in-house. &lt;/p&gt;

&lt;p&gt;Users get /plugin, see your curated list, and that's the whole universe: nothing else shows up, and nothing else installs even if they try.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;If you're standardizing Claude Code across a team, a single Git repo with a &lt;code&gt;marketplace.json&lt;/code&gt; gets you a long way: internal + curated external skills, commands, subagents and MCP connections, all installable with the same two commands, versioned and reviewable like any other code. &lt;/p&gt;

&lt;p&gt;The main things worth getting right up front are version-pinning anything that comes from outside your org, and keeping credentials out of the manifests entirely.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
