Let's Connect

Close-up of a circuit board representing tools wired into an AI assistant

Claude can reason about your order system all day, but it can't actually look up an order unless you give it a way to call your code. The usual fix is bespoke: a custom HTTP shim per app, prompt-stuffing, sometimes fine-tuning. To connect Claude to your tools with MCP you skip all of that. You write a small MCP server that exposes the tool, then register that server in your MCP client. You define a tool with a name, a description, and an input schema; you run the server over a transport the client understands; you point the client at it. From then on Claude can call your function whenever a conversation needs it. If you want the conceptual tour of what an MCP server actually is, I wrote that separately in what is an MCP server. Here we build one.

What does "connecting a tool" actually mean?

MCP (the Model Context Protocol) is an open standard from Anthropic for wiring AI assistants to external tools, data, and systems. An MCP server exposes three kinds of things to a client: tools (actions the model can call), resources (data it can read), and prompts (reusable templates). The payoff is the part people miss at first: you write one server, and every MCP-compatible client can use it. Claude Desktop, Claude Code, a handful of IDEs — same server, no per-app integration.

For most "connect Claude to my thing" tasks, you only care about tools. A tool is the model-facing version of one of your functions. The model never runs your code; it emits a request to call the tool, your server executes it, and the result goes back into the conversation.

How do I define a tool?

Every MCP tool has exactly three things that matter:

  • name — a stable identifier the model uses to call it, like get_order_status.
  • description — plain text the model reads to decide when to call this tool. Write it about WHEN to use it, not just what it does.
  • input_schema — a JSON Schema describing the arguments. This is how the model knows what to pass and how your server validates what it gets.

The description is the part people underinvest in. The model picks tools by reading descriptions, so "Get order status" is weaker than "Look up the current shipping status of a customer order. Call this when the user asks where an order is, whether it shipped, or for a tracking number." Say when, and the model triggers it at the right moments instead of guessing. Here is a complete server in TypeScript that exposes one tool.

server.ts — an MCP server exposing one tool
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: "orders", version: "1.0.0" });

server.registerTool(
  "get_order_status",
  {
    // The model reads this to decide WHEN to call the tool — be prescriptive.
    description:
      "Look up the current shipping status of a customer order. " +
      "Call this when the user asks where an order is, whether it " +
      "shipped, or for a tracking number.",
    inputSchema: {
      order_id: z.string().describe("The order ID, e.g. ORD-10482"),
    },
  },
  async ({ order_id }) => {
    const status = await lookupOrder(order_id); // your real logic
    return { content: [{ type: "text", text: JSON.stringify(status) }] };
  },
);

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

The SDK turns the Zod schema into the JSON Schema that goes on the wire — you do not hand-write the `input_schema` object yourself. There are official MCP SDKs for TypeScript and Python; the Python shape is the same three fields wrapped in a decorator.

server.py — the same tool in Python
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("orders")

@mcp.tool()
def get_order_status(order_id: str) -> str:
    """Look up the current shipping status of a customer order.

    Call this when the user asks where an order is, whether it
    shipped, or for a tracking number.
    """
    return lookup_order(order_id)  # your real logic

if __name__ == "__main__":
    mcp.run()  # stdio transport by default
A developer's screen showing terminal output and code side by side
A local MCP server is just a process your client launches over stdio — run it, register it, and Claude can call it.

How does the server run — stdio or HTTP?

MCP defines two transports, and the choice is about where the server lives, not what it does. A local server runs over stdio: the client launches your server as a subprocess and talks to it over stdin/stdout. This is the default for anything personal — it runs on your machine, starts when the client starts, and needs no network setup. A shared server runs over Streamable HTTP: you host it somewhere reachable and clients connect by URL. Use HTTP when more than one person or machine needs the same tool, or when the tool has to live next to a database or internal API it can reach but your laptop cannot.

Write the server once. stdio when it is yours alone, Streamable HTTP when it is shared. The tool definition does not change — only how the client reaches it.Md Raihan Hasan

How do I register it so Claude can call it?

The client needs to know how to reach your server. For a stdio server you give it a command to launch; for an HTTP server you give it a URL. Here is the config block for a local stdio server — the client runs the command, speaks MCP over the subprocess, and surfaces your tool to the model.

mcp config — register a local (stdio) server
{
  "mcpServers": {
    "orders": {
      "command": "node",
      "args": ["/abs/path/to/server.js"]
    }
  }
}

A remote server is the same shape with `url` instead of `command`:

mcp config — register a remote (Streamable HTTP) server
{
  "mcpServers": {
    "orders": {
      "url": "https://tools.example.com/mcp"
    }
  }
}

In Claude Code you do not even hand-edit JSON for the common case — `claude mcp add orders -- node /abs/path/to/server.js` writes the entry for you, and `claude mcp list` shows what is wired up. Whichever client you use, once the server is registered the model sees your tool's name and description, decides on its own when a request needs it, and your server does the work.

Where this fits next to the API

If you have used raw tool use over the Claude API, MCP will feel familiar — the tool shape is the same name/description/input_schema triple, and the call/result handshake mirrors the API's `tool_use` and `tool_result` blocks. The difference is reuse. With the API you build the tool loop into one application. With MCP you build the tool once as a server and every MCP client gets it for free. For Laravel and PHP folks who want the API-side version first, I cover that in building AI features with the Anthropic API. And if your tool is a WordPress site rather than your own code, the registration mechanics are the same — I walk through that case in configuring an MCP server for WordPress.

The honest summary: connecting Claude to your own tool is a small server and a config block, not a project. Start with stdio and one tool with a description that says when to call it. Get the model triggering it correctly in a real conversation, then add tools or move to Streamable HTTP when you actually need to share it. The standard does the integration work so you do not have to — which is the entire point of MCP. Ship the one-tool version today; you can always grow it.