<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agent Development on Code Plato</title><link>https://CodePlato3721.github.io/categories/agent-development/</link><description>Recent content in Agent Development on Code Plato</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sun, 13 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://CodePlato3721.github.io/categories/agent-development/index.xml" rel="self" type="application/rss+xml"/><item><title>A Concise Guide to Writing CLAUDE.md</title><link>https://CodePlato3721.github.io/post/a-concise-guide-to-writing-claude-md/</link><pubDate>Sun, 13 Sep 2026 00:00:00 +0000</pubDate><guid>https://CodePlato3721.github.io/post/a-concise-guide-to-writing-claude-md/</guid><description>&lt;img src="https://pub-deacd49348914a49b1254b01f351ef0d.r2.dev/2026/09/a-concise-guide-to-writing-claude-md/en/banner.png" alt="Featured image of post A Concise Guide to Writing CLAUDE.md" /&gt;&lt;p&gt;This guide applies equally to &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/p&gt;&#10;&lt;h2 id="what-claudemd-is-for"&gt;What CLAUDE.md Is For&#10;&lt;/h2&gt;&lt;p&gt;First, let&amp;rsquo;s understand what CLAUDE.md is for. A model is like a newborn. Every time you talk to it, it has no idea who you are, who it is, or what you&amp;rsquo;ve said before. It learns all of this from the context you pass it. Of course, Claude Code comes with some preset context, so it knows it&amp;rsquo;s Claude Code, a tool that helps you write code.&#10;So at the start of every new session, it knows nothing about your project. The reason it still works reasonably well is that Claude Code&amp;rsquo;s own preset context teaches it what to do to understand a project before touching it.&#10;That&amp;rsquo;s why it can seem to understand your project quite well, yet still frequently reinvents the wheel. It has a methodology for quickly getting up to speed, but it still can&amp;rsquo;t know every detail of your project. And you wouldn&amp;rsquo;t want it to try — that would burn far too many tokens.&#10;This is where someone needs to use short, plain language to tell it about the project&amp;rsquo;s background, architecture, and so on. Doing this has two benefits:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;It won&amp;rsquo;t need to traverse your entire project, saving tokens&lt;/li&gt;&#10;&lt;li&gt;It won&amp;rsquo;t do things that obviously go against your codebase&amp;rsquo;s intent, or reinvent the wheel&#10;This short text is what goes into &lt;code&gt;CLAUDE.md&lt;/code&gt;, or &lt;code&gt;AGENTS.md&lt;/code&gt;. This guide applies equally to &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="writing-principles"&gt;Writing Principles&#10;&lt;/h2&gt;&lt;p&gt;A good CLAUDE.md should read like:&#10;&lt;strong&gt;The shortest onboarding guide for a new hire:&lt;/strong&gt; Imagine you&amp;rsquo;re onboarding a new hire for a task. Try to convey just enough knowledge, in the fewest words possible, for them to start working without wrecking your codebase.&#10;&lt;strong&gt;Wording as tight as a résumé:&lt;/strong&gt; A while back I was job hunting, and I learned from an HR contact how to write a résumé. I racked my brain compressing my 4-page résumé down to 2 pages, cutting almost every redundant word, tightening every sentence until it couldn&amp;rsquo;t be trimmed further. You should write &lt;code&gt;CLAUDE.md&lt;/code&gt; the same way.&#10;&lt;strong&gt;Highly abstract knowledge:&lt;/strong&gt; You don&amp;rsquo;t need to tell &lt;code&gt;Claude Code&lt;/code&gt; or &lt;code&gt;Codex CLI&lt;/code&gt; how many spaces to indent, or which line of which file has the database connection. Leave a lot of that to hooks — file names and line numbers change all the time. What you should tell the model are highly abstract design principles.&#10;Generally speaking, a good &lt;code&gt;CLAUDE.md&lt;/code&gt; should be under 200 lines.&lt;/p&gt;&#10;&lt;h2 id="types"&gt;Types&#10;&lt;/h2&gt;&lt;p&gt;Including the &lt;code&gt;CLAUDE.md&lt;/code&gt; at your project root, there are actually 3 kinds of &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — global scope. Used by all of your projects.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;./CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — project scope.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;./subdirectory/CLAUDE.md&lt;/code&gt;&lt;/strong&gt; — subdirectories can have their own &lt;code&gt;CLAUDE.md&lt;/code&gt; too.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="framework"&gt;Framework&#10;&lt;/h2&gt;&lt;p&gt;There&amp;rsquo;s no strict, perfect framework for &lt;code&gt;CLAUDE.md&lt;/code&gt;. But drawing on some good examples of &lt;code&gt;CLAUDE.md&lt;/code&gt; and related articles, I&amp;rsquo;ve distilled a reasonably sound structure:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;One-line introduction&lt;/li&gt;&#10;&lt;li&gt;Architecture&lt;/li&gt;&#10;&lt;li&gt;Tech stack&lt;/li&gt;&#10;&lt;li&gt;Commands&lt;/li&gt;&#10;&lt;li&gt;Conventions&lt;/li&gt;&#10;&lt;li&gt;Boundaries&lt;/li&gt;&#10;&lt;li&gt;Domain doc map&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="one-line-introduction"&gt;One-Line Introduction&#10;&lt;/h3&gt;&lt;p&gt;A brief description of what the project is and roughly what it does. Example:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;This is a multi-agent orchestration framework for coordinating parallel Claude Code sub-agents, automating dev/QA workflows on a FastAPI + React codebase.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="architecture"&gt;Architecture&#10;&lt;/h3&gt;&lt;p&gt;A brief description of the project&amp;rsquo;s architecture, for example:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Architecture&#10;- Keep controllers thin — business logic lives in `app/Services/`&#10;- Database access only goes through `app/Repositories/`. No direct Eloquent usage in controllers.&#10;- API resources under `app/Http/Resources/` are responsible for shaping every JSON response.&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="tech-stack"&gt;Tech Stack&#10;&lt;/h3&gt;&lt;p&gt;A brief description of the project&amp;rsquo;s tech stack, like:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Tech Stack&#10;- FastAPI, Python 3.11&#10;- PostgreSQL 15 (async SQLAlchemy 2.0)&#10;- Celery + Redis for background jobs&#10;- Poetry for dependency management&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="commands"&gt;Commands&#10;&lt;/h3&gt;&lt;p&gt;Common commands for the project, for example:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Commands&#10;- Dev server: `uvicorn app.main:app --reload`&#10;- Run tests: `pytest -x -v`&#10;- DB migrations: `alembic upgrade head`&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="conventions"&gt;Conventions&#10;&lt;/h3&gt;&lt;p&gt;Abstract conventions that a linter can&amp;rsquo;t capture, like:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Conventions&#10;- Boolean variables/properties are prefixed with `is`, `has`, or `should`&#10;- All datetimes are stored and passed as UTC&#10;- Event names follow the `domain.action` naming format&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="boundaries"&gt;Boundaries&#10;&lt;/h3&gt;&lt;p&gt;Boundaries on which files can be modified:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Boundaries&#10;- `legacy/` — old payment system, emergency bug fixes only, no new patterns or refactors&#10;- `src/generated/` — auto-generated by Prisma/GraphQL&#10;- `vendor/`, `third_party/` — third-party code, fix issues by upgrading the dependency version, don&amp;#39;t edit directly&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="domain-doc-map"&gt;Domain Doc Map&#10;&lt;/h3&gt;&lt;p&gt;A mapping between domain terms and documentation:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;## Domain Doc Map&#10;| Mentions | Read |&#10;|---|---|&#10;| billing, stripe, payment, subscription, invoice | docs/billing.md |&#10;| auth, login, session, oauth, jwt | docs/auth.md |&#10;| migration, schema, drizzle, kysely | docs/db-migrations.md |&#10;| feature flag, rollout, kill switch | docs/feature-flags.md |&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Putting these examples together, a good &lt;code&gt;CLAUDE.md&lt;/code&gt; should look something like this:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;This is a multi-agent orchestration framework for coordinating parallel Claude Code sub-agents, automating dev/QA workflows on a FastAPI + React codebase.&#10;&#10;## Architecture&#10;- Keep controllers thin — business logic lives in `app/Services/`&#10;- Database access only goes through `app/Repositories/`. No direct Eloquent usage in controllers.&#10;- API resources under `app/Http/Resources/` are responsible for shaping every JSON response.&#10;&#10;## Tech Stack&#10;- FastAPI, Python 3.11&#10;- PostgreSQL 15 (async SQLAlchemy 2.0)&#10;- Celery + Redis for background jobs&#10;- Poetry for dependency management&#10;&#10;## Commands&#10;- Dev server: `uvicorn app.main:app --reload`&#10;- Run tests: `pytest -x -v`&#10;- DB migrations: `alembic upgrade head`&#10;&#10;## Conventions&#10;- Boolean variables/properties are prefixed with `is`, `has`, or `should`&#10;- All datetimes are stored and passed as UTC&#10;- Event names follow the `domain.action` naming format&#10;&#10;## Boundaries&#10;- `legacy/` — old payment system, emergency bug fixes only, no new patterns or refactors&#10;- `src/generated/` — auto-generated by Prisma/GraphQL&#10;- `vendor/`, `third_party/` — third-party code, fix issues by upgrading the dependency version, don&amp;#39;t edit directly&#10;&#10;## Domain Doc Map&#10;| Mentions | Read |&#10;|---|---|&#10;| billing, stripe, payment, subscription, invoice | docs/billing.md |&#10;| auth, login, session, oauth, jwt | docs/auth.md |&#10;| migration, schema, drizzle, kysely | docs/db-migrations.md |&#10;| feature flag, rollout, kill switch | docs/feature-flags.md |&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="should-you-write-a-never-section"&gt;Should You Write a &amp;ldquo;Never&amp;rdquo; Section?&#10;&lt;/h3&gt;&lt;p&gt;You may have seen a section in other guides called &amp;ldquo;Never,&amp;rdquo; used to record past mistakes. Sounds good in theory. But I don&amp;rsquo;t recommend adding this section. That&amp;rsquo;s because a &amp;ldquo;Never&amp;rdquo; section has &lt;strong&gt;only the motivation to add, never the motivation to remove&lt;/strong&gt;. It just keeps growing — even Claude Code itself will add to it, and no one ever trims it. Over time it turns into a &lt;strong&gt;graveyard of historical incidents&lt;/strong&gt;.&#10;What I&amp;rsquo;d suggest instead: when a problem comes up, flip the failure mode around and write it as a positive rule. For instance, instead of &amp;ldquo;Never do synchronous database writes in &lt;code&gt;webhook/stripe.ts&lt;/code&gt;,&amp;rdquo; write &amp;ldquo;Database operations only happen inside &lt;code&gt;repository/&lt;/code&gt;.&amp;rdquo;&lt;/p&gt;&#10;&lt;h2 id="trimming"&gt;Trimming&#10;&lt;/h2&gt;&lt;p&gt;Even if you follow the framework above, for a large project, a long-lived one, or one that already has a &lt;code&gt;CLAUDE.md&lt;/code&gt;, you&amp;rsquo;ll still find it hard to keep the file under 200 lines. That&amp;rsquo;s when you move to the trimming step.&#10;Trimming means moving things out of &lt;code&gt;CLAUDE.md&lt;/code&gt;. Here are the destinations and methods, in order:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Custom sub-agents: &lt;code&gt;.claude/agents&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Rules folder: &lt;code&gt;.claude/rules&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Subdirectory CLAUDE.md: &lt;code&gt;./subdirectory/CLAUDE.md&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Fixed workflows: &lt;code&gt;.claude/skills/&lt;/code&gt;&lt;/li&gt;&#10;&lt;li&gt;Other docs: &lt;code&gt;docs/&lt;/code&gt;&#10;Notice these are numbered. That&amp;rsquo;s because extraction has a priority order — from specific to abstract, from precise to general.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="custom-sub-agents"&gt;Custom Sub-agents&#10;&lt;/h3&gt;&lt;p&gt;Move all guidance related to sub-agents into &lt;code&gt;.claude/agents&lt;/code&gt;. For example:&#10;You could define an agent dedicated to running integration tests, named &lt;code&gt;integration-tester.md&lt;/code&gt;, with content like:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;---&#10;name: integration-tester&#10;description: Runs integration tests&#10;tools: Bash&#10;model: sonnet&#10;---&#10;&#10;You run and diagnose integration tests. You should follow these steps.....&#10;&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="rules-folder"&gt;Rules Folder&#10;&lt;/h3&gt;&lt;p&gt;Specific rule files can live in &lt;code&gt;.claude/rules&lt;/code&gt;. There are two kinds: path-scoped and non-path-scoped. Non-path-scoped rules carry the same priority as &lt;code&gt;CLAUDE.md&lt;/code&gt;, for example:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;# Security Rules&#10;&#10;- All user input is validated at the API boundary&#10;- Secrets and API keys are read from environment variables only&#10;- SQL queries always use parameterized statements&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Path-scoped rules only load when the given path condition is met, for example:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;---&#10;paths:&#10; - &amp;#34;tests/**/*.py&amp;#34;&#10; - &amp;#34;**/*.spec.ts&amp;#34;&#10;---&#10;&#10;# Unit Testing Rules&#10;&#10;- One assertion concept per test&#10;- Test names describe behavior, not implementation&#10;....&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Rules vs. the domain doc map:&lt;/strong&gt; You might wonder: &amp;ldquo;Don&amp;rsquo;t rule files and the domain doc map end up defining the same thing twice?&amp;rdquo; Yes, that overlap can happen. For instance, you might specify that files under &lt;code&gt;billings/&lt;/code&gt; should follow the billing docs, and also have a row in the domain doc map like &lt;code&gt;| billings | docs/billings.md |&lt;/code&gt; — which is indeed redundant.&#10;The fix is to split documentation into &lt;strong&gt;rules&lt;/strong&gt; and &lt;strong&gt;background&lt;/strong&gt;. Rule files are highly enforceable and belong in &lt;code&gt;.claude/rules&lt;/code&gt;. Background files are comparatively looser and belong in &lt;code&gt;docs/&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="subdirectory-claudemd"&gt;Subdirectory CLAUDE.md&#10;&lt;/h3&gt;&lt;p&gt;Rules specific to certain subdirectories can move into those directories — common cases are &lt;code&gt;sql/&lt;/code&gt;, &lt;code&gt;domains/&lt;/code&gt;, &lt;code&gt;adapters/&lt;/code&gt; folders. A &lt;code&gt;CLAUDE.md&lt;/code&gt; in these directories doesn&amp;rsquo;t need to follow the standard framework, but it should still stay short.&lt;/p&gt;&#10;&lt;h3 id="fixed-workflows"&gt;Fixed Workflows&#10;&lt;/h3&gt;&lt;p&gt;If there are fixed, sequential operations, turn them into a project-level skill and put it under &lt;code&gt;.claude/skills/&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h3 id="other-docs"&gt;Other Docs&#10;&lt;/h3&gt;&lt;p&gt;Everything else goes into the &lt;code&gt;docs/&lt;/code&gt; directory. This directory holds more general documentation — things like a more detailed &lt;code&gt;architecture.md&lt;/code&gt;, or ADRs (Architecture Decision Records) under &lt;code&gt;docs/adr/&lt;/code&gt;, e.g. &lt;code&gt;docs/adr/0001-migrate-to-drizzle.md&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="getting-started"&gt;Getting Started&#10;&lt;/h2&gt;&lt;p&gt;If you don&amp;rsquo;t yet have a &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt;, start with these steps:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Run &lt;code&gt;/init&lt;/code&gt; to generate a first draft&lt;/li&gt;&#10;&lt;li&gt;Revise the draft following the methods above&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;Also remember to run &lt;code&gt;/doctor&lt;/code&gt; periodically going forward to keep optimizing &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="references"&gt;References&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://www.humanlayer.dev/blog/writing-a-good-claude-md" target="_blank" rel="noopener"&#10; &gt;https://www.humanlayer.dev/blog/writing-a-good-claude-md&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://chipp.ai/engineering/claude-md-architecture" target="_blank" rel="noopener"&#10; &gt;https://chipp.ai/engineering/claude-md-architecture&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://dev.to/nishilbhave/claudemd-best-practices-the-complete-2026-guide-435j" target="_blank" rel="noopener"&#10; &gt;https://dev.to/nishilbhave/claudemd-best-practices-the-complete-2026-guide-435j&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a class="link" href="https://medium.com/@arad.haghi/best-practices-for-structuring-claude-code-projects-1b7144a683bc" target="_blank" rel="noopener"&#10; &gt;https://medium.com/@arad.haghi/best-practices-for-structuring-claude-code-projects-1b7144a683bc&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>When You Don't Need MCP</title><link>https://CodePlato3721.github.io/post/when-you-don-t-need-mcp/</link><pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate><guid>https://CodePlato3721.github.io/post/when-you-don-t-need-mcp/</guid><description>&lt;img src="https://pub-deacd49348914a49b1254b01f351ef0d.r2.dev/2026/09/when-you-don-t-need-mcp/en/banner.png" alt="Featured image of post When You Don't Need MCP" /&gt;&lt;h2 id="the-problem"&gt;The Problem&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;Job postings keep mentioning MCP, as if everyone doing agent development has to know it&lt;/li&gt;&#10;&lt;li&gt;Some people say MCP is too heavyweight and hardly anyone actually uses it&lt;/li&gt;&#10;&lt;li&gt;Meanwhile plenty of tutorials say a unified interface via MCP is great&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Most tutorials you&amp;rsquo;ll come across explain what MCP is and why you should use it. After all that explanation, it&amp;rsquo;s still hard to get an intuitive feel for the trade-offs. So today I&amp;rsquo;ll flip the question around: when do you &lt;em&gt;not&lt;/em&gt; need MCP? That&amp;rsquo;s a better way to build intuition about it.&lt;/p&gt;&#10;&lt;h2 id="what-is-mcp"&gt;What Is MCP&#10;&lt;/h2&gt;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt; is an open protocol launched by Anthropic that lets AI applications (agents like Claude Code, Claude Desktop, OpenClaw) discover and call external tools, and read external resources, in a &lt;strong&gt;unified way&lt;/strong&gt;.&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;p&gt;That&amp;rsquo;s the textbook definition. In practice, you can think of MCP as a kind of resource exposed to an agent. Before MCP existed, if you wanted an AI application to connect to services like Google Drive, GitHub, or Slack, &lt;strong&gt;every single AI application&lt;/strong&gt; had to write its own integration code for every external service.&#10;MCP is essentially a &amp;ldquo;standard socket&amp;rdquo; defined for that connection.&lt;/p&gt;&#10;&lt;h2 id="what-youd-use-instead-of-mcp"&gt;What You&amp;rsquo;d Use Instead of MCP&#10;&lt;/h2&gt;&lt;p&gt;If you skip MCP, you still have plenty of other options. The two most important ones:&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Function calling:&lt;/strong&gt; OpenAI introduced function calling in 2023. It&amp;rsquo;s actually simple — you pass a function signature to the LLM first.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-arduino" data-lang="arduino"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Get the current weather for a specified city&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;}},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once the LLM knows a tool exists, if it decides during execution that it needs to call this external tool, the result&amp;rsquo;s &lt;code&gt;content&lt;/code&gt; will include an extra &lt;code&gt;tool_use&lt;/code&gt; object, and &lt;code&gt;stop_reason&lt;/code&gt; will also be set to &lt;code&gt;tool_use&lt;/code&gt;. Like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;content&amp;#34;&lt;/span&gt;: [&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;tool_use&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;new york&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ],&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;stop_reason&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;tool_use&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you write the code yourself to actually implement the function call.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; response.stop_reason &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tool_use&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tool_use &lt;span style="color:#f92672"&gt;=&lt;/span&gt; response.content[&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... call the weather-lookup function&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In real-world work it&amp;rsquo;s obviously less crude than this — you&amp;rsquo;d use a framework like LangChain.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;CLI:&lt;/strong&gt; This is really just another form of &lt;strong&gt;function calling&lt;/strong&gt; — except you expose exactly one tool, and you tell the LLM upfront that it&amp;rsquo;s a bash shell, so it can write whatever command it wants. The advantage is you don&amp;rsquo;t need to tell the LLM what bash can do or how to use it — the LLM has already read enough material to know how to write bash commands on its own.&lt;/p&gt;&#10;&lt;p&gt;But using a &lt;strong&gt;CLI&lt;/strong&gt; comes with real risk, since permission control is hard to get right.&lt;/p&gt;&#10;&lt;h2 id="from-the-models-perspective"&gt;From the Model&amp;rsquo;s Perspective&#10;&lt;/h2&gt;&lt;p&gt;First, you need to understand one thing: the model has no idea whether you&amp;rsquo;re calling it through MCP or through function calling.&#10;For example, when you register a weather-lookup tool, if you register it via MCP, here&amp;rsquo;s what the model sees in its tools list:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Get the current weather for a specified city&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you use function calling instead, here&amp;rsquo;s what the model sees:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;get_weather&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Get the current weather for a specified city&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;input_schema&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;properties&amp;#34;&lt;/span&gt;: {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;city&amp;#34;&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s play a little game: can you spot the difference between these two JSON blocks? The answer is there isn&amp;rsquo;t one. So there&amp;rsquo;s no need to agonize over whether MCP versus function calling affects the model somehow. As far as the model is concerned, there&amp;rsquo;s no difference at all.&lt;/p&gt;&#10;&lt;h3 id="the-downsides-of-mcp"&gt;The Downsides of MCP&#10;&lt;/h3&gt;&lt;p&gt;If MCP is so great, why not just use it everywhere? Because MCP does have real downsides:&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Large context overhead:&lt;/strong&gt; Once you connect to an MCP server, the &lt;strong&gt;full&lt;/strong&gt; schema (name, parameters, description) for &lt;em&gt;every&lt;/em&gt; tool that server exposes gets stuffed into the model&amp;rsquo;s context window up front — whether or not this particular conversation ever needs it.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Server resource consumption:&lt;/strong&gt; An MCP server is something that runs continuously and has to stay alive. The more MCP servers you spin up, the more CPU and memory they eat on your machine.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Operational burden:&lt;/strong&gt; Since an MCP server is a separate process, you&amp;rsquo;re on the hook for restarting it when it crashes, sequencing startup order across multiple servers, and maintaining internal state over time.&lt;/p&gt;&#10;&lt;h3 id="the-upsides-of-mcp"&gt;The Upsides of MCP&#10;&lt;/h3&gt;&lt;p&gt;MCP sounds pretty rough so far. So why do so many companies keep using it anyway? Because despite its downsides, its biggest strength is unifying the integration interface across your project&amp;rsquo;s resources. A unified interface brings two benefits on its own:&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Cleaner calling code:&lt;/strong&gt; People who don&amp;rsquo;t build software won&amp;rsquo;t really feel this benefit, but anyone who does knows immediately how good a unified interface is. Your thousands of lines of legacy spaghetti code can suddenly collapse into a dozen lines. The more interfaces you have, the more bugs get buried, and buried deeper — maintenance becomes endless.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Easier third-party integration:&lt;/strong&gt; Once every third-party resource is packaged as MCP, integrating them no longer takes a lot of custom effort. There&amp;rsquo;s nothing technically clever about this — it&amp;rsquo;s the same idea as standardizing currency or units of measurement. What matters is that someone has to actually go do it.&lt;/p&gt;&#10;&lt;h2 id="conclusion"&gt;Conclusion&#10;&lt;/h2&gt;&lt;p&gt;If your situation matches one of the following:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;You want to use a tool built by a third party&lt;/li&gt;&#10;&lt;li&gt;You&amp;rsquo;re using a tool shared across multiple platforms on your team&lt;/li&gt;&#10;&lt;li&gt;You need to separate permissions across multiple roles&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;then MCP is worth considering. Otherwise, a lighter-weight approach — like plain function calling — is enough.&lt;/p&gt;&#10;</description></item></channel></rss>