← Back to Home
🧰

Building Your First MCP Server

Expose a real tool the model can call — with input validation and a clear description.

11 min read📚DevOps Fundamentals

Let’s build. In this lesson you’ll create a minimal MCP server that exposes one tool, run it over stdio, and connect it to a host. We’ll use the official TypeScript SDK, but the concepts transfer to the Python SDK too.


🎯 Learning Objectives


Step 1 — Set Up the Project

Install the official MCP SDK and a schema validator:

bash
mkdir devops-mcp && cd devops-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod

zod lets us declare and validate the tool’s input schema — critical, because the model will send arguments and we must not trust them blindly.


Step 2 — Create the Server & a Tool

Here’s a complete, minimal server exposing one tool, get_service_status, that reports whether a named service is healthy:

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

// 1. Create the server
const server = new McpServer({
name: "devops-tools",
version: "1.0.0",
});

// 2. Register a tool: name, description, input schema, handler
server.tool(
"get_service_status",
"Check whether a named service is currently healthy",
{ service: z.string().describe("The service name, e.g. 'payments'") },
async ({ service }) => {
  // (in real life: query your monitoring / health endpoint)
  const healthy = await checkHealth(service);
  return {
    content: [
      {
        type: "text",
        text: healthy
          ? service + " is healthy ✅"
          : service + " is DOWN ❌",
      },
    ],
  };
}
);

// 3. Connect over stdio
const transport = new StdioServerTransport();
await server.connect(transport);

Three things to notice:

⚠ Validate and constrain every input

The model can call your tool with any arguments. The zod schema rejects malformed input, but you also enforce business rules in the handler — e.g. only allow known service names, never pass raw input into a shell command.


Step 3 — Run and Register It

An stdio server isn’t run directly by you — the host launches it as a subprocess. You tell the host how to start it via config. For a typical desktop host:

json
{
"mcpServers": {
  "devops-tools": {
    "command": "node",
    "args": ["/path/to/devops-mcp/server.js"]
  }
}
}

On start, the host runs that command, performs the initialize handshake, calls tools/list, and your get_service_status tool appears — ready for the model to call.


Step 4 — Test It

Before wiring it into a host, test with the MCP Inspector, an official dev tool that acts as a client:

bash
npx @modelcontextprotocol/inspector node server.js
bash — 80×24
student@devops:~$# In the Inspector UI: call get_service_status { service: 'payments' }

Seeing the tool listed and returning a result means your server is correct — now any MCP host can use it.

💡 Inspector first, host second

Always test a new server with the Inspector before connecting it to a real AI host. It isolates your server’s behaviour from the model’s decisions, so you debug one thing at a time.


🧪 Hands-on Lab

📝

Add a 'restart_service' Tool (Safely)

Sketch a second tool restart_service that takes a service name from an allowed list only, and returns a confirmation. Focus on the schema and the safety check.


🧠 Knowledge Check

Knowledge Check

Why is an input schema (e.g. with zod) important when defining an MCP tool?

Knowledge Check

How is a stdio MCP server typically started?


💼 Interview Preparation

Interview Q&A

What are the key things to get right when building an MCP tool?


Summary

You built a working MCP server, defined and validated a tool, ran it over stdio, and tested it with the Inspector. You now know how to give an AI safe, structured access to a system. Finally, let’s see how MCP is used in real DevOps workflows.

Up Next

Using MCP in DevOps Workflows

You've mastered this lesson. Continue your journey to becoming a DevOps Engineer.

Start Next Lesson