· 5 min read ·

Skills Are Prompts. MCP Tools Are Code. The Difference Matters.

Source: hackernews

A post by David Gasquez made the rounds on Hacker News recently, arguing that MCP is still his preferred way to extend AI assistants over skills. It landed 353 points and nearly 300 comments, which tells you the question is live and genuinely contested.

I have been building a Discord bot called Ralph that runs on exactly this stack, so I have opinions grounded in something more than preference. After building 53 MCP tools and five skill definitions in the same project, I think the debate is partly a category error. Skills and MCP are not the same kind of thing. They solve different problems, and treating them as alternatives to each other muddies the comparison.

What MCP Actually Is

Model Context Protocol was released by Anthropic in November 2024 as an open standard for connecting AI assistants to external systems. The spec is built on JSON-RPC 2.0 and defines three primitives: tools (functions the model can call), resources (data the model can read), and prompts (reusable templates). Transport is either stdio for local servers or HTTP with Server-Sent Events for remote ones.

In practice, a minimal MCP server in TypeScript looks like this:

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';

const server = new McpServer({ name: 'my-tools', version: '1.0.0' });

server.tool('get_logs', { lines: z.number().default(50) }, async ({ lines }) => {
  const output = await readLastNLines('/var/log/app.log', lines);
  return { content: [{ type: 'text', text: output }] };
});

const transport = new StdioServerTransport();
await server.connect(transport);

That tool can read a real file, call a real API, query a real database. It runs as a process with access to the filesystem, environment variables, and network. The model sees a schema, calls the tool, and gets back structured results.

Skills, by contrast, are markdown files. In Claude Code’s skill system, a skill is essentially a structured prompt that gets injected into the conversation when invoked. It can describe a workflow in detail, set up a persona, or orchestrate a sequence of actions, but it cannot perform I/O on its own. A skill that wants to restart a process still needs a tool to do the actual restarting.

The Architecture in Practice

In Ralph’s codebase, the split is clear in the directory structure. The src/mcp/ directory contains TypeScript that gets compiled and run as a real process. Tools in there do things like:

  • Execute code in a sandboxed environment (Node, Python, shell)
  • Read PM2 logs from disk
  • Make GitHub API calls to create issues or list pull requests
  • Query a conversation memory graph
  • Audit npm dependencies for vulnerabilities
  • Take screenshots of URLs via Puppeteer

None of that is possible from a markdown file. The restart_bot tool calls pm2 restart ralph. The eval_code tool spawns a subprocess. The http_request tool opens a network connection. These are capabilities, not descriptions of capabilities.

The skills/ directory contains a different kind of thing entirely. The main skill file for Ralph describes an autonomous engineering loop: orient, select a task, plan, implement, validate, update the board, commit. It is an orchestration document. It tells Claude how to sequence work and what tools to use in what order. It does not replace the tools; it coordinates them.

This is why the title of David’s post uses “still.” There has been pressure, especially in some Claude Code communities, to handle more things through skills because they are easier to write and maintain. No server to compile, no process to spawn, just a markdown file checked into the repo. That appeal is real. But it has a ceiling.

Where Each One Breaks Down

Skills break down the moment you need persistent state or real I/O. A skill can describe how to monitor a CI pipeline, but it cannot actually poll GitHub’s API every few minutes and post alerts. A skill can describe how to format a response, but it cannot check whether the format matches what a downstream API expects. The text of the skill lives in the conversation; the effect of the skill is limited to what the model can do with that text.

MCP tools break down at a different boundary. They add operational complexity. You need a server running, a build step, environment variables configured, a process manager keeping things alive. For simple extension cases, that overhead is not worth it. If all you want to do is remind Claude to write concise responses in a particular channel, a skill accomplishes that without any infrastructure. An MCP tool that just returns a string telling the model to be concise would be absurd.

The MCP server ecosystem has grown quickly, with official servers for filesystem access, Git, Postgres, Slack, Puppeteer, and dozens of community contributions. Each of these provides genuine capabilities the model could not otherwise have. The filesystem server lets Claude read and write files. The Postgres server lets it run queries. These are not things you can approximate with a well-written markdown file.

The Hacker News Reaction

The discussion on the post surfaced a real concern: MCP’s security model is still immature. An MCP server running locally with access to the filesystem and network is a significant trust boundary. There have already been documented prompt injection attacks targeting MCP servers, where malicious content in tool results attempts to redirect the model’s behavior. Skills, being inert text, do not have this attack surface.

This is a legitimate consideration, not just theoretical. If you are building tools that interact with untrusted external data, the MCP server that fetches that data becomes an injection vector. The protocol does not yet have a standardized capability negotiation or sandboxing model that would let a client constrain what a server can do at the protocol level.

What This Means for How You Design Extensions

The practical heuristic is straightforward. If the behavior you want requires network access, filesystem operations, subprocess execution, persistent state, or interaction with an external API, write an MCP tool. The overhead is worth it because the alternative is not a skill that does the same thing with less code; it is a skill that describes something the model will not actually be able to do.

If the behavior you want is about how the model should reason, respond, or sequence its actions, a skill is the right fit. Orchestration patterns, persona constraints, workflow descriptions, output formatting rules: these belong in skills or system prompts. They shape the model’s behavior without needing to perform any external operation.

Building Ralph with both has made this distinction concrete for me. The 53 MCP tools handle every interaction with the outside world: GitHub, Discord channel configuration, the kanbn task board, code execution, log reading, HTTP calls. The five skills describe how to work with those tools, what it means to be Ralph in a conversation, and how to sequence autonomous engineering tasks. Neither set could substitute for the other.

David’s preference for MCP is right for the use cases he is describing, and the Hacker News commenters who pushed back are also right that skills have a valid domain. The debate gets cleaner once you stop treating them as alternatives and start treating them as layers. MCP gives you capability. Skills give you behavior. You generally need both.

Was this interesting?