🎤 Speaking at PragVue 2025!

I'll be presenting "How to Structure Vue Projects" at the Czech Vue.js conference on September 23rd

Skip to content

What Is the Model Context Protocol (MCP)? How It Works

Published: at 

I did not see how powerful MCP was until I used Claude Code with the Playwright MCP. Playwright MCP lets an AI use a real browser. It can open a page, click buttons, fill forms, and take screenshots. I asked Claude to audit my site for SEO. It ran checks in a real browser, gave me the results, and sent screenshots. You can read more about how I use Claude Code for doing SEO audits. That was when I saw it. This was not just text prediction. This was an AI that can see and work with the web like a human tester.

What is MCP

MCP means Model Context Protocol. Before we define it, let us see how we got here.

How it started

User

LLM

In 2022 ChatGPT made AI open to everyone. You typed a question. It predicted the next tokens and sent back text. You could ask for your favorite author or create code.

The problem with plain LLMs

A plain LLM is a text generator.

You can read more in my post about LLM limits.

The first fix: tools

Yes

No

User

LLM

Needs Python?

Run code in Python sandbox

Execution result

When OpenAI added a Python sandbox, LLMs could run code and give exact results.

More tools mean more power

Python

Web search

No

User

LLM

Needs external tool?

Run code in Python sandbox

Execution result

Search the web for information

Search result

Web search gave live knowledge. Now the model could answer fresh questions.

Even more tools

Python

Web search

Google Calendar

No

User

LLM

Needs external tool?

Run code in Python sandbox

Execution result

Search the web for information

Search result

Check / update calendar events

Calendar data

Anthropic added more tools to Claude like Google Calendar and email. You can ask it what meetings you have next week and it tells you.

Comic showing multiple teams reinventing the wheel by building the same API connectors
If every team builds their own tool for connecting to email, calendar, and other APIs, everyone reinvents the wheel

The solution: a protocol

We need a standard. One tool for Google Calendar that any agent can use. In November the Model Context Protocol was released.

Definition

MCP is an open protocol that lets apps give context to LLMs in a standard way.

Think of USB-C. You plug in power, a display, or storage and it just works.

MCP does the same for AI with data sources and tools.

With MCP you can build agents and workflows without custom glue code.

Robot MCP Comic showing how MCP connects different tools and services
MCP acts as the universal connector for AI tools and services

How MCP Works (mental model)

At its core, MCP has three roles:

💡 Note

MCP takes some inspiration from the Language Server Protocol, which standardizes how to add support for programming languages across a whole ecosystem of development tools. In a similar way, MCP standardizes how to integrate additional context and tools into the ecosystem of AI applications.

The host embeds clients, and those clients connect to one or more servers. Your VS Code could have a Playwright MCP server for browser automation and another MCP server for your docs — all running at the same time.

User

Host UI
Claude Desktop, VS Code/Claude Code

MCP Client 1

MCP Client 2

MCP Client 3

MCP Server A

MCP Server B

MCP Server C


How MCP Connects: Transports

MCP uses JSON-RPC 2.0 for all messages and supports two main transport mechanisms:

📍 stdio (local)

  • Server runs as subprocess of the client
  • Messages flow through stdin/stdout pipes
  • No network latency - instant communication
  • Perfect for local tools and dev environments

🌐 Streamable HTTP (remote)

  • Single HTTP endpoint for all operations
  • POST for sending messages, GET for listening
  • Server-Sent Events (SSE) for streaming
  • Ideal for cloud-hosted MCP servers

Key points:

The transport choice depends on your use case: stdio for local tools with minimal latency, HTTP for remote services that multiple clients can connect to.

What servers can expose

An MCP server can offer any combination of three capabilities:

Tools: Functions the AI can call

// Simple calculator tool example
server.registerTool(
  "calculate",
  {
    title: "Calculator",
    description: "Perform mathematical calculations",
    inputSchema: {
      operation: z.enum(["add", "subtract", "multiply", "divide"]),
      a: z.number(),
      b: z.number()
    }
  },
  async ({ operation, a, b }) => {
    let result;
    switch (operation) {
      case "add": result = a + b; break;
      case "subtract": result = a - b; break;
      case "multiply": result = a * b; break;
      case "divide": result = b !== 0 ? a / b : "Error: Division by zero"; break;
    }
    
    return {
      content: [{
        type: "text",
        text: `${a} ${operation} ${b} = ${result}`
      }]
    };
  }
);

Resources: Context and data

server.registerResource(
  "app-config",
  "config://application",
  {
    title: "Application Configuration",
    description: "Current app settings and environment",
    mimeType: "application/json"
  },
  async (uri) => ({
    contents: [{
      uri: uri.href,
      text: JSON.stringify({
        environment: process.env.NODE_ENV,
        version: "1.0.0",
        features: {
          darkMode: true,
          analytics: false,
          beta: process.env.BETA === "true"
        }
      }, null, 2)
    }]
  })
);

Prompts: Templates for interaction

server.registerPrompt(
  "code-review",
  {
    title: "Code Review",
    description: "Review code for quality and best practices",
    argsSchema: {
      language: z.enum(["javascript", "typescript", "python", "go"]),
      code: z.string(),
      focus: z.enum(["security", "performance", "readability", "all"]).default("all")
    }
  },
  ({ language, code, focus }) => ({
    messages: [{
      role: "user",
      content: {
        type: "text",
        text: [
          `Please review this ${language} code focusing on ${focus}:`,
          "",
          "```" + language,
          code,
          "```",
          "",
          "Provide feedback on:",
          focus === "all" 
            ? "- Security issues\n- Performance optimizations\n- Code readability\n- Best practices"
            : focus === "security" 
            ? "- Potential security vulnerabilities\n- Input validation\n- Authentication/authorization issues"
            : focus === "performance"
            ? "- Time complexity\n- Memory usage\n- Potential optimizations"
            : "- Variable naming\n- Code structure\n- Comments and documentation"
        ].join("\n")
      }
    }]
  })
);

What a Client can expose

An MCP client can provide capabilities that let servers interact with the world beyond their sandbox:

Roots: Filesystem boundaries

Sampling: Nested LLM calls

Elicitation: Asking users for input

Example: How we can use MCPS in Vscode

Your mcp.json could look like this:

{
  "servers": {
    "playwright": {
      "gallery": true,
      "command": "npx",
      "args": ["@playwright/mcp@latest"],
      "type": "stdio"
    },
    "deepwiki": {
      "type": "http",
      "url": "https://mcp.deepwiki.com/sse",
      "gallery": true
    }
  }
}

What MCP is not


Simple example of your first MCP Server

#!/usr/bin/env node
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "echo-onefile",
  version: "1.0.0",
});

server.tool(
  "echo",
  "Echo back the provided text",
  { text: z.string().min(1, "Text cannot be empty").describe("Text to echo back") },
  async ({ text }) => ({
    content: [{ type: "text", text }],
  })
);

const transport = new StdioServerTransport();

server
  .connect(transport)
  .then(() => console.error("Echo MCP server listening on stdio"))
  .catch((err) => {
    console.error(err);
    process.exit(1);
});

This example uses the official MCP SDK for TypeScript, which provides type-safe abstractions for building MCP servers.

The server exposes a single tool called “echo” that takes text input and returns it back. We’re using Zod for runtime schema validation, ensuring the input matches our expected structure with proper type safety and clear error messages.

Simple MCP Client Example

Here’s how to connect to an MCP server and use its capabilities:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

// Create a client that connects to your MCP server
async function connectToServer() {
  // Create transport - this runs your server as a subprocess
  const transport = new StdioClientTransport({
    command: "node",
    args: ["./echo-server.js"]
  });

  // Create and connect the client
  const client = new Client({
    name: "my-mcp-client",
    version: "1.0.0"
  });

  await client.connect(transport);
  
  return client;
}

// Use the server's capabilities
async function useServer() {
  const client = await connectToServer();
  
  // List available tools
  const tools = await client.listTools();
  console.log("Available tools:", tools);
  
  // Call a tool
  const result = await client.callTool({
    name: "echo",
    arguments: {
      text: "Hello from MCP client!"
    }
  });
  
  console.log("Tool result:", result.content);
  
  // List and read resources
  const resources = await client.listResources();
  for (const resource of resources) {
    const content = await client.readResource({
      uri: resource.uri
    });
    console.log(`Resource ${resource.name}:`, content);
  }
  
  // Get and execute a prompt
  const prompts = await client.listPrompts();
  if (prompts.length > 0) {
    const prompt = await client.getPrompt({
      name: prompts[0].name,
      arguments: {
        code: "console.log('test')",
        language: "javascript"
      }
    });
    console.log("Prompt messages:", prompt.messages);
  }
  
  // Clean up
  await client.close();
}

// Run the client
useServer().catch(console.error);

This client example shows how to:

Use it with Vscode

{
  "servers": {
    "echo": {
      "gallery": true,
      "type": "stdio",
      "command": "node",
      "args": ["--import", "tsx", "/absolute/path/echo-server.ts"]
    }
  }
}

Summary

This was just my starter post for MCP to give an overview. I will write more blog posts that will go in depth about the different topics.

💡 Note

If you need a TypeScript starter template for your next MCP server, you can use my mcp-server-starter-ts repository to get up and running quickly.

Stay Updated!

Subscribe to my newsletter for more TypeScript, Vue, and web dev insights directly in your inbox.

  • Background information about the articles
  • Weekly Summary of all the interesting blog posts that I read
  • Small tips and trick
Subscribe Now