Skills vs. MCP tools for AI agents: When to use which LogRocket's advisory article compares AI agent Skills and MCP tools along axes of auditability and flexibility, arguing that MCP tools suit deterministic, traceable tasks while Skills handle open-ended interpretation. The piece builds a changelog generator both ways, noting that the MCP 2026-07-28 specification removed session overhead, making MCP tools lighter and more competitive with Skills. Advisory boards aren’t only for executives. Join the LogRocket Content Advisory Board today → Most guidance on this choice hands you a feature table comparing setup, execution model, latency, and portability, as though Skills and MCP tools were competing technologies racing on the same track. A more useful frame sets them along two axes, auditability and flexibility, where auditability is how well you can inspect and predict what the agent will actually do, and flexibility is how much open-ended range it has to interpret a situation and act. Nearly everything that lands in the feature table, from how a capability scales as you add more to how it fails and how far you can trust its output unwatched, follows from where it sits between those two poles. MCP tools sit toward the auditable end, because a tool exposes fixed input and output schemas that leave the agent only one real decision: which tool to call and when; over a deterministic call you can trace and reproduce. Skills sit toward the flexible end, where a Skill injects natural-language instructions the agent reads at runtime and decides for itself which guidance applies, when, and how to carry it out. That interpretive room is the whole point, since it absorbs the kind of judgment a fixed schema cannot express, and it is also the whole cost, since interpreted instructions can be misread and two runs over the same input can diverge. Neither end is better in the abstract, since an MCP tool buys auditability at the expense of range while a Skill buys range at the expense of auditability, so the choice comes down to how much interpretation the task genuinely needs and how much unwatched trust you can extend to the result. Rather than argue that abstractly, this article builds the same capability twice, once as an MCP tool and once as a Skill, and reads the difference off what each one actually produced against the same repository. An MCP server exposes its capabilities through three primitives https://blog.logrocket.com/understanding-anthropic-model-context-protocol-mcp/ , and standing a first one up in Node.js https://blog.logrocket.com/how-to-build-mcp-server-nodejs/ takes a few dozen lines. Skills are simpler still: a folder with a SKILL.md https://blog.logrocket.com/skills-github-copilot/ whose frontmatter tells the agent when the instructions inside it A Skill reaches the agent through progressive disclosure. Only its name and description load at startup, so dozens can sit installed without crowding the context window, and the body of the SKILL.md enters context only once a request matches that description. A Skill can therefore carry effectively unbounded guidance while costing almost nothing idle, on the condition that the agent recognizes when to trigger it. MCP’s 2026-07-28 https://blog.modelcontextprotocol.io/posts/2026-07-28/ specification is what moved the tradeoff. The earlier protocol opened every connection with an initialize handshake and pinned the client to one server instance through an Mcp-Session-Id header carried on every later request, state that had to survive across calls. The revision retires both, so each request now describes itself and lands on any instance behind an ordinary load balancer. That removed overhead is precisely the weight that once counted against MCP and made a Skill feel like the lighter thing to reach for. The capability is a changelog generator. Given two points in a repository’s history, it produces an account of what changed between them. The auditable half of that job is reading the commits, a deterministic operation with a fixed shape, and that half is what an MCP tool models cleanly. The July 2026 SDK ships as split packages rather than the earlier monolith, so the server pulls its core from @modelcontextprotocol/server https://www.npmjs.com/package/@modelcontextprotocol/server , its transport from @modelcontextprotocol/node , and an Express adapter from @modelcontextprotocol/express , all at 2.0.0 , alongside zod for the schemas.Reading git history is its own module, and a few of its decisions matter to how trustworthy the tool ends up being. Commit messages are unstructured text that can contain newlines, commas, and anything else a developer types, so splitting git log output on ordinary delimiters is fragile. Passing explicit control characters as field and record separators sidesteps that, since a unit separator between fields and a record separator between commits will not appear in a commit message by accident. js const FIELD = "\x1f"; const RECORD = "\x1e"; const FORMAT = "%H", "%h", "%an", "%aI", "%s", "%b" .join FIELD + RECORD; const REF PATTERN = /^ A-Za-z0-9. \/^~- +$/; function assertRef ref: string, label: string : void { if REF PATTERN.test ref { throw new Error Invalid ${label} ref: ${JSON.stringify ref } ; } } The assertRef guard is the second decision. Both refs flow into a git invocation, and while the call uses execFile rather than a shell and so is already free of shell interpretation, rejecting anything that is not a plausible ref token keeps a stray argument from ever being read as a flag. With those two guards in place, the read itself is a single git log over the from..to range, parsed back into typed records. export async function getCommits opts: GetCommitsOptions : Promise