While Claude Skills offer incredible flexibility for agent-driven workflows, sometimes you need absolute, programmatic control over a task. This is where MCP (Model Context Protocol) Tools excel. MCP Tools are a powerful alternative to Skills, designed for when you need a well-defined, unambiguous API for the AI to call.
This lesson demonstrates how to build a custom compress MCP tool from scratch. You'll learn how to convert a standard CLI script into a tool with a strict input schema using Zod, providing clear instructions for the AI on how to format arguments. We'll then contrast this with the more abstract, natural-language approach of a Skill to help you understand the ideal use case for each.
mcpez library and Zod to define a tool's name, description, and a precise input schema.claude mcp add command to make your new tool available to the agent within your project.This lesson clarifies the distinction between Claude Code's programmatic MCP Tools and its natural language-based Skills. MCP Tools are ideal for single-purpose, precise automations where you need absolute control. They are defined in code with a strict input schema (using Zod) that acts as a rigid API for the AI to call. Skills, in contrast, are defined in Markdown and provide a more abstract context, giving the agent greater flexibility to reason about and execute complex, multi-step plans. The video demonstrates this by building a compress MCP tool, showing how to define its schema and descriptions to guide the AI in generating the correct arguments. This makes MCP Tools the right choice for well-defined, repeatable tasks, while Skills are better for workflows that require more dynamic, agent-driven problem-solving.
compress @index.ts
# Create and navigate to the mcp directory
take .claude/mcp
# Install the mcp-easy helper library
bun i mcpez
# Install type definitions for a dependency
bun i -D @types/tar-fs
# Install a dependency for the tool
bun i tar-fs
# Add the MCP server to the project configuration
claude mcp add tools --scope project -- bun run ./claude/mcp/index.ts
The core of the lesson is the MCP tool definition in index.ts. It uses the tool function from mcpez to define the tool's name, description, Zod schema for inputs, and the callback function that executes the logic.
import { tool, z } from "mcpez";
// ... other imports for the compress function
async function compress(outputFileName: string, filesToCompress: string[]) {
// ... implementation for compressing files
}
tool("compress", {
description: "Compress a file or directory using tar and gzip",
inputSchema: {
outputFileName: z.string().describe("Relative path to the output file ending in .tar.gz"),
filesToCompress: z.array(z.string()).describe("Relative paths to the files to compress"),
},
async ({ outputFileName, filesToCompress }) => {
const result = await compress(outputFileName, filesToCompress);
if (result.success) {
return {
content: [
{
type: "text",
text: `Compressed ${result.sizeInMB} MB to ${result.outputPath}`,
},
],
};
} else {
return {
content: [
{
type: "text",
text: `Error: ${result.error}`,
},
],
};
}
},
});
[00:00] If we create an mcp, which I'll go ahead and make in a clod mcp directory. So we'll create this and navigate into it. And I'm going to initialize this with bun and install a project called mcp-ez, which is a tool I put together for spinning up mcps very easily. And then I'm going to open up this index.ts. So if I go into our compress script, just copy and paste everything into here, close this, close this, make sure we don't call compress manually.
[00:29] That way we just have a compress function we can use later. And I'll install the types for this. So button id types tar fs. Then from here I'm going to convert this into an mcp by importing tool off of mcp-easy. And then I need to convert these arguments into arguments passed into our function rather than parsed off of argv.
[00:54] So now our compress function can take a file name and files to compress, and then instead of exiting the process we'll return an object and we'll return a success object with some data on it. So our function has some inputs and some outputs. Alright, so now to set up the MCP, we'll set up a tool, add caller tool, compress, make sure and import Zod from MCPEasy, and then accept some of cursor's suggestions here. So it looks fine, and I'll explain it all a second. Let's rename these to camel case, camel case, We don't need that.
[01:33] And then refactor return to match what a tool needs to return. Where essentially the key here is that our MCP has this input schema which will pass to our compress function, get a result back, and say successful or unsuccessful. Then to make sure this gets called and has the correct extensions and everything we're gonna set up some descriptions. So it knows to call compress when we need to compress a file or a directory and the output file name will end in tar.gz and the files will be an array of strings relative to the files we need to compress. So now I need to go in and delete our skills for compress.
[02:12] So remove this directory, probably need to remove the compress skill from here, and remove any sort of knowledge about a compress script, just so it's not influenced by that at all. And then we'll use the command line and the command to add this MCP is clod mcp add. We're going to call this set of MCP tools, just tools for now, and we'll scope it to a project. And then the command we want to run is bun run in the current directory, clod mcp-index.ts. And this will go ahead and configure in our current project this .mcpjson file, which means that once we fire up Clod in here and we check our mcp configuration, it looks like I was in the wrong directory when I ran that command, So I'll go ahead and move this file over to the root of the project.
[03:04] So if we check the root again we now should have... There's our mcp file. So exit out, clear this out, fire up Clod. It found a new MCP server. So we're going to go ahead and allow this.
[03:18] If we check MCP we'll now see tools is added. We'll try and reconnect. Then I guess I'll check this command real quick. Bun run clod mcp index.ts. It looks like I forgot to install the packages required.
[03:34] So back in a clod, back in an mcp, bun install, tar fs, close this out. Let's try and reconnect this one more time. And now we're back in business. So now we're connected, we'll check on our tools, and we see if we view the tools we'll see a compress tool. Alright so hit an escape to go back, and now if I just want to compress our index.ts file, Now this will prompt us for tool use.
[04:01] It was able to detect that our MCP had a tool for compressing files, and it'll ask for permissions to use this tool. So I'll go ahead and allow this, and now we've successfully compressed a file using an MCP tool. Now you'll notice the difference here is that in our MCP tool we are limited to the name of the tool, the description of the tool, and input schema as the context that Claude knows about. So even though this is very similar to a skill in the fact that this is a tool an MCP could decide to call later. You're completely missing out on the fact that in a skill file, skill files have context that's loaded dynamically once the skill tool is called.
[04:45] So if you want this to be a more complex series of steps where the agent can decide which tools to call in what order based on whatever conditions and you're handing those decisions over the agent and allowing it to infer what to do based on all this loaded context With an MCP tool, if you want that level of complexity, you'd have to make a lot of round trips where, based on some condition, you send back a prompt which would then call another MCP tool which would send back a prompt. And it gets very unwieldy very quickly compared to a plain text description of a series of steps or a series of tools where you hand the power over to the agent. So when it comes to deciding between an MCP tool or a skill, you end up deciding, do I want absolute control over what happens when the tool is called, where I have full programmatic instructions, and you end up writing a lot of code and trying to architect these things to work perfectly, or do you want to step back into the abstraction of English, where maybe if you didn't account for a specific scenario, the agent could decide what to do for you.
[05:55] So in general, that's why there's so much hype around skills, is that they're much, much simpler to write up because it doesn't require much development or thought behind how everything links together. Rather you define an intent of the goal that the skill should achieve and the abilities the skill should have and you let the agent take care of all of the hard work in the moment rather than you trying to anticipate and guess what might happen in the moment.