Why a standard capability layer matters

AI tools like GitHub Copilot are powerful, but they hit a wall when they need private repository data, live documentation, or the ability to trigger real actions such as creating pull requests or driving a local application. There is no native channel for that context, and before the Model Context Protocol there was no common integration pattern either. Each AI tool demanded its own plugins and wiring.

MCP standardizes that gap with a familiar client-server architecture:

  • Host: The AI application you are using. GitHub Copilot in VS Code acts as a host and initiates connections.
  • Client: A component living inside the host. Each MCP server you connect to gets its own client instance that maintains the connection.
  • Server: Your custom piece that exposes tools, resources, and prompts to the host.

Once a server is registered with a host, the AI agent can immediately use whatever capabilities that server defines. This turns a generic assistant into one that knows about your games, your internal APIs, or your CI pipelines.

Anatomy of a learning project: a turn-based game server

To make these concepts tangible, I built a demo where you play Tic-Tac-Toe and Rock Paper Scissors against GitHub Copilot. The interesting part is that Copilot does not decide the moves itself. Instead, the MCP server does the actual game logic, and Copilot orchestrates calls to it based on your conversational prompts.

The project is a TypeScript monorepo with three parts:

  • A Next.js frontend that renders the game boards and handles your input.
  • API routes inside that Next.js app that manage game state.
  • An MCP server that receives Copilot's tool calls and translates them into API operations.
  • Shared libraries containing common types and game logic reused across those components.

The runtime flow looks like this:

  1. You register the MCP server in VS Code.
  2. You ask Copilot to play a game, or it discovers the available tools on its own.
  3. Copilot consults the language model and decides whether to invoke a tool.
  4. The MCP server executes that tool, which hits your local API and updates the game state.
  5. Copilot receives the result and continues the conversation.

For a local learning setup, the single-repository approach is useful because you can clone and run the whole system without complex dependency management. A production deployment would split that MCP server out into its own npm package or container image with its own versioning.

To register the server, create a .vscode/mcp.json file in your workspace:

{
  "servers": {
    "playwright": {
      "command": "npx",
      "args": [
        "@playwright/mcp@latest"
      ]
    },
    "turn-based-games": {
      "command": "node",
      "args": ["dist/index.js"],
      "cwd": "./mcp-server"
    }
  }
}

That configuration makes two servers available to Copilot: a Playwright MCP server executed as an npm package, and the turn-based games server running from your compiled TypeScript output.

Tools: the actions your AI can perform

Tools are the most direct way to let an AI do something. Each tool declaration includes a description and an input schema so the model knows exactly what you expect. The game server exposes handlers for these operations:

  • Analyze_game: read the current state of any active game
  • create_rock_paper_scissors_game: start a new Rock Paper Scissors match
  • create_tic_tac_toe_game: start a new Tic-Tac-Toe match
  • play_rock_paper_scissors: have the server pick a choice for Rock Paper Scissors
  • play_tic_tac_toe: have the server compute a move for Tic-Tac-Toe
  • wait_for_player_move: poll the endpoint until the human player has responded
{
  name: 'play_tic_tac_toe',
  description: 'Make an AI move in Tic-Tac-Toe game. IMPORTANT: After calling this tool when the game is still playing, you MUST call wait_for_player_move to continue the game flow.',
  inputSchema: {
    type: 'object',
    properties: {
      gameId: {
        type: 'string',
        description: 'The ID of the Tic-Tac-Toe game to play',
      },
    },
    required: ['gameId'],
  },
},

The clever separation here is that the LLM never computes a move. When Copilot calls play_tic_tac_toe, the MCP server runs a handler with the actual game logic, which can be a random response on easy difficulty or a more deliberate algorithm on hard. Tools are, in essence, reusable pieces of code that an AI invokes to take a concrete action.

Context resources

Where tools change state, resources provide context. They use URI-style identifiers so an AI can request a particular dataset in a predictable way. The game server implements these resource patterns:

  • game://tic-tac-toe to list all Tic-Tac-Toe games
  • game://tic-tac-toe/{Game-ID} to get one specific Tic-Tac-Toe state
  • game://rock-paper-scissors to list all Rock Paper Scissors games
  • game://rock-paper-scissors/{Game-ID} to get one specific Rock Paper Scissors state
async function readGameResource(uri) {
  const gameSession = await callBackendAPI(gameType, gameId);
  if (!gameSession) {
    throw new Error("Game not found");
  }
  return gameSession;
}

The MCP server has a method that watches for these URIs, translates them into a request to the local API, and returns the raw JSON response. That data then becomes part of the context for a subsequent tool call, such as deciding what move to make.

Reusable prompts

You already write prompts whenever you talk to an AI tool, but MCP servers can ship with predefined prompts that steer users toward effective use of your tools. On this game server, those prompts include strategy guides per difficulty level, explanations of the rules, and troubleshooting tips. Users pull them up with slash commands in VS Code, like typing /strategy to get advice for an optimal move in a specific scenario.

What carries over to production MCP servers

The demo patterns map directly onto real-world servers. The GitHub MCP server, for example, gives an agent access to issue and pull request data, Dependabot alerts, and actions like creating or updating those items. Playwright's MCP server lets an agent navigate, click, screenshot, and inspect rendered pages in a live browser. But the same core design applies to any custom internal API or database you want your AI to reach.

Before moving from demo to deployment, keep a few things in mind.

Security and trust. The local game server has no authentication, which is fine for this exercise. A server handling your actual data needs to validate identity, whatever the method: OAuth, a personal access token, or any other scheme that matches your infrastructure.

Also remember that every third-party MCP server you connect is now a dependency. Apply the same scrutiny you would to any other package: does the publisher look recognizable, can you audit the source code, and would you be comfortable reviewing changes upstream?

Broader spec features. Tools, resources, and prompts are the fundamental trio, but the MCP specification has added newer capabilities like sampling and elicitation. They were not part of this demo, but they open up interesting territory.

Language choice. Official SDKs exist for multiple languages, so you are not tied to TypeScript for your server. For this project, TypeScript was the natural pick because it unified the frontend, the API, and the server into one codebase. Your stack preference may point somewhere else, from Python to Go to Rust, and the protocol works just the same.

Key takeaways from building an MCP server

MCP gives you a standardized way to extend AI assistants across platforms like Visual Studio Code's Copilot. Before you start building, check what MCP servers already exist — if you recognize the publisher and can inspect the code, reuse it. When you do build your own, keep the scope tight and focus on one specific problem rather than a sprawling implementation.

The framework for any MCP server rests on three primitives: tools, resources, and prompts. Keeping these three building blocks distinct gives you a clear design path for whatever capabilities you expose to your AI tools.

The real value here isn't just novelty — it's removing the friction between your AI assistant and the systems you rely on day to day. Whether you're wiring up internal developer tooling, connecting to external APIs, or codifying custom workflows, MCP supplies a consistent foundation for extending AI in a structured way.

Where to go from here

  • Use the GitHub MCP server in your own environment or study it as a realistic implementation example.
  • Try the Playwright MCP server for UI testing workflows.
  • Build a small server against your own internal APIs — the turn-based-game-mcp example is a good starting point.
  • Experiment with custom prompts that encode your team's best practices.

The point of MCP is giving your AI assistant the specific capabilities your development environment needs. If you want a deeper look at one deployment, see our practical guide on using the GitHub MCP server.