Building your first MCP server: How to extend AI tools with custom capabilities

Learn Model Context Protocol by building a turn-based game server that shows how to extend GitHub Copilot with custom tools, resources, and prompts.

| 10 minutes

Have you ever worked with AI tools and wished they had access to some additional piece of context? Or wanted them to perform actions on your behalf? Think of those scenarios where you’re working with GitHub Copilot and you need it to check a GitHub Issue, run a Playwright test, or interact with an API. By default, these AI tools lack access to those external systems. But that’s where the Model Context Protocol (MCP) comes in. It’s a standardized way to extend AI tools with custom capabilities. 

I wanted to learn more about it by building something visual and interactive. So I created a turn-based game server that lets you play Tic-Tac-Toe and Rock Paper Scissors against Copilot using MCP.

In my latest Rubber Duck Thursdays live stream, I walked through the project, which has a few components, all written in TypeScript:

  • A Next.JS Web App and API, intended to run locally for demo/learning purposes
  • An MCP Server that uses the MCP TypeScript SDK
  • A shared library for common type definitions and components, which are reused across the web app, API, and MCP server

You can take a look at the code over at github-samples/turn-based-game-mcp. Here’s what I learned.

You can watch the full stream below 👇

Why MCP matters for developers

Even with powerful AI agents, we continue to run into limitations:

  • AI tools can’t natively access private data for retrieval augmented generation, like information from our GitHub repositories.
  • They don’t have access to the latest documentation or real-time data.
  • Agents can’t perform actions like creating pull requests, exploring the UI on your locally running application, or interact with your APIs.

To access external context and take action, we need to extend the capabilities of these AI tools. But before MCP, there was no standard approach to integrating with third-party tools and services. You’d potentially need different plugins and different integration patterns for whatever AI tool you were using. MCP changes that by providing one standard way to plug tools and capabilities into any tool that supports the Model Context Protocol.

MCP follows a client-server pattern that’s familiar to most developers:

  • Host: The AI tool you’re using, like GitHub Copilot in VS Code (you’ll notice that Copilot in VS Code has good support in the MCP feature support matrix). The host initiates the connection to your MCP server via a client.
  • Clients: Clients live inside the host application (for example, GitHub Copilot in VS Code), and have a 1:1 relationship with a server. When VS Code connects to a new MCP server (like GitHub, Playwright or the turn-based-game MCP server we’re talking about in this post), a new client is created to maintain the connection.
  • Server: Your custom MCP server that provides tools, resources, and prompts. In our case, we’re making an MCP server that provides capabilities for playing turn-based games! 

Building the turn-based game MCP server

For my learning project, I wanted something visual that would show the overall MCP interaction and could be reused when people are trying to explain it as part of a talk. So I built a web app with Tic-Tac-Toe and Rock Paper Scissors. But instead of the game having two people play locally (or online), or even a CPU in the backend, the opponent’s moves are orchestrated by an MCP server.

The architecture includes:

  • Next.js frontend: The game interface where I make moves
  • API routes (part of the Next.js implementation): Backend logic for game state management, called by the frontend and the MCP server.
  • MCP server: TypeScript server that handles AI game moves
  • Shared libraries: Common game logic used across components

Here’s how it works in practice:

  1. We register an MCP server in VS Code so that Copilot is aware of the new capabilities and tools.
  2. I interact with GitHub Copilot in VS Code. I can call tools explicitly, or Copilot can autodiscover them.
  3. Copilot calls the large language model. Based on the prompt context and the available tools, it may call the MCP server.
  4. The MCP server executes the requested tool (like making a move in the game) and returns results.
  5. Copilot uses those results to continue the conversation.

The magic step is when you register the MCP server with an MCP application host (in our example, GitHub Copilot in Visual Studio Code). Suddenly, your AI agent gains access to the capabilities that have been built into those servers.

A screenshot of an MCP server configuration with the turn-based-game MCP server running. It shows a popup listing several tools from the MCP server including analyze_game, create_rock_paper_scissors_game, create_tic_tac_toe_game, play_rock_paper_scissor, play_tic_tac_toe and wait_for_player_move.

Setting up the MCP server in VS Code

You can configure your MCP servers by creating a .vscode/mcp.json file. You can find more details about that on the Visual Studio Code docs.

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

The above configuration tells GitHub Copilot in VS Code that there are two MCP servers that we’d like to use:

  • A Playwright MCP server that is executed locally as an NPM package.
  • A turn-based-games MCP server that runs a server locally based on the compiled TypeScript code from our working directory.

For this implementation, I kept my turn-based-game MCP server architecture and logic relatively simple, with all components in a single repository. This monorepo approach bundles the web app, API, and MCP server together, making it straightforward to clone and run the entire system locally without complex dependency management or cross-repository setup. But for a more robust setup, you would likely distribute that MCP server either as a package (such as npm or a docker image), and have clear publishing and versioning processes around that. 

The three core building blocks of MCP

Through building this project, I familiarized myself with three fundamental MCP server concepts:

1. Tools: Actions your AI can take

Tools define what actions the MCP server can perform. In my game server, I specified tools like:

  • Analyze_game: Get the current state of any game
  • create_rock_paper_scissors_game: Start a new game of Rock Paper Scissors
  • create_tic_tac_toe_game: Start a new Tic-Tac-Toe game
  • play_rock_paper_scissors: Make an AI choice in Rock Paper Scissors
  • play_tic_tac_toe: Make an AI move in Tic-Tac-Toe
  • wait_for_player_move: Polls the endpoint until the player has made their move

Each tool has a clear description and input schema that tells the AI what parameters to provide:

{
  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'],
  },
},

GitHub Copilot and the Large Language Model (LLM) aren’t calculating the actual game moves. When Copilot calls the play_tic_tac_toe tool, the MCP server executes a handler for that specific tool that runs the CPU game logic, like random moves for Tic Tac Toe in “easy” difficulty or a more optimal  algorithm for moves when using the “hard” difficulty.

In other words, tools are reusable pieces of software that can be called by the AI, often to take some form of action (like making a move in a turn-based game!).

2. Resources: Context your AI can access

Resources provide a way for the AI to gather context, and often have a URI-based identifier. For example, I implemented custom URI schemes like:

  • game://tic-tac-toe: List all Tic-Tac-Toe games
  • game://tic-tac-toe/{Game-ID}: Get state for a specific game of Tic Tac Toe
  • game://rock-paper-scissors: List all Rock Paper Scissors games
  • game://rock-paper-scissors/{Game-ID}: Get state for a specific game of Rock Paper Scissors

As the MCP resources docs explain, you can choose how these resources are passed. In our turn-based-game MCP server, there is a method that translates the resource URIs into an API call to the local API server and passes on the raw response, so that it can be used as context within a tool call (like playing a game).

async function readGameResource(uri) {
  const gameSession = await callBackendAPI(gameType, gameId);
  if (!gameSession) {
    throw new Error("Game not found");
  }
  return gameSession;
}

3. Prompts: Reusable guidance for users

The third concept is prompts. You’ll be very familiar with prompts and prompt crafting, as that’s the way that you interact with AI tools like GitHub Copilot. Your users could write their own prompts to use your tools, like “Play a game of Tic Tac Toe” or “Create a GitHub Issue for the work that we’ve just scoped out.”

But you may want to ship your MCP server with predefined prompts that help users get the most out of your tools. For example, the turn-based-game MCP comes with several prompts like:

  • Strategy guides for different difficulty levels
  • Game rules and optimal play instructions
  • Troubleshooting help for common scenarios

Your users can access these prompts via slash commands in VS Code. For example, when I typed /strategy, I could access the prompt asking for advice on optimal play for a given game or difficulty level.

Real-world applications and considerations

While my game demo is intentionally simple to help you learn some of these initial concepts, the patterns apply to other MCP servers:

  • GitHub MCP server: Get information from existing GitHub Issues or pull requests, list Dependabot alerts, or create and manage issues and pull requests, all based on the access you provide via OAuth (in the remote MCP server) or a Personal Access Token.
  • Playwright MCP server: Automatically navigate to specific pages in a browser, click and interact with the pages, capture screenshots, and check rendered content.
  • Custom API servers: Connect to your internal services, databases, or business logic.

Additional capabilities from the MCP specification

Tools, resources, and prompts are some of the most commonly used capabilities of the MCP specification. Recently, a number of additional capabilities including sampling and elicitation were added to the spec. I haven’t had a chance to add those yet, but perhaps they’ll feature as part of another stream!

Authentication and security

You may need to handle authentication and authorization for production MCP servers depending on the scenario. As an example, the GitHub MCP server supports OAuth flows for the remote MCP server and Personal Access Tokens in local and remote. This turn-based game MCP server is intended to be simple, and doesn’t include any auth requirements, but security should be a key consideration if you’re building your own MCP servers.

Trusting third-party MCP servers

You may not always need to create your own MCP server. For example, GitHub ships its own MCP server. Instead of creating your own version, why not make an open source contribution upstream to improve the experience for all?

Thought 💡: Make sure to do your due diligence on MCP servers before installing them, just like you would with any other dependency as part of your project’s supply chain. Do you recognise the publisher? Are you able to review (and contribute to) the code in an open source repository?

Language and SDK options

MCP provides SDKs for multiple languages, so you can build servers in whatever technology fits your stack, ranging from TypeScript to Python, Go to Rust and more. I chose TypeScript because I wanted my entire demo (frontend, backend, and MCP server) in one repository with shared code and a common language.

You can keep up to date with the evolution of the MCP specification on the Model Context Protocol documentation.

Take this with you

Here’s what you can learn from this exploration:

  • MCP standardizes AI tool extensibility across different platforms and applications (like Copilot in Visual Studio Code)
  • Reuse first by investigating what existing MCP servers are available. Review the MCP servers: Do you recognize the publisher and can you access the code?
  • Building your own? Start simple with focused servers that solve specific problems rather than trying to build everything at once
  • The three building blocks (tools, resources, prompts) provide a clear framework for designing the capabilities of your MCP server

MCP isn’t just about playing games with AI (though that was fun). It’s about breaking down the walls between your AI assistants and the systems they need to help you work effectively. 

Whether you’re building internal developer tools, integrating with external APIs, or creating custom workflows, MCP provides the foundation you need to extend your AI tools in consistent, powerful ways.

Next steps

Want to explore MCP further? Here are some practical starting points:

  • Check out the GitHub MCP server to use in your own workflows or learn more about a real-world MCP server implementation.
  • Try the Playwright MCP server for UI testing workflows.
  • Build a simple server for your internal APIs or development tools. You can check out the turn-based-game-mcp as an example.
  • Experiment with custom prompts that encode your team’s best practices.

The goal of MCP is to give your AI assistants the tools they need to be truly helpful in your specific development environment. So, which tool will you be using? What will you build?

Want a closer look? Check out our practical guide on how to use the GitHub MCP server >

Written by

Chris Reddington

Chris Reddington

@chrisreddington

Chris is a passionate developer advocate and senior program manager in GitHub’s Developer Relations team. He works with execs, engineering leads, and teams from the smallest of startups, established enterprises, open source communities and individual developers, helping them ❤️ GitHub and unlock their software engineering potential.

Related posts