BattleBots + MCP = Fun: Building Custom Tools in C# That Your IDE and Workflows Understand

BattleBots + MCP = Fun: Building Custom Tools in C# That Your IDE and Workflows Understand
Photo by Rubaitul Azad / Unsplash

Opening

AI copilots and IDE extensions are evolving fast, and Microsoft’s Model Context Protocol (MCP) is emerging as the glue that makes custom tools discoverable and usable by editors like VS Code and Visual Studio Insiders.

For this post, I’ll be using .NET Core 10 along with Visual Studio Insiders (which you can download here 👉 https://visualstudio.microsoft.com/insiders/).

And because I absolutely love BattleBots (yes, I’m one of those people who cheers every time Tombstone spins up), I thought it would be fun to wrap a simple BattleBots API in MCP and show how it can power workflows, copilots, and automation.

We’ll build something fun and practical:

  • A C# BattleBots API exposing competitors’ names.
  • Wrap it with an MCP server using the official Microsoft SDK.
  • Debug it with MCP Inspector.
  • Wire it into VS Code Insiders / Visual Studio Insiders.
  • And finally, automate it inside n8n.

If you’ve ever thought, “How do I bring my own APIs into AI-powered tools?” — this is your starting point.


Step 1: The BattleBots API

We’ll begin with a simple .NET 8 minimal API that returns a list of BattleBots competitors.

// Program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/battlebots", () =>
{
    return new[]
    {
        "Tombstone",
        "Witch Doctor",
        "Hydra",
        "End Game",
        "SawBlaze"
    };
});

app.Run();

Run it with:

dotnet run

Visit http://localhost:5000/battlebots and you’ll get back JSON like:

["Tombstone","Witch Doctor","Hydra","End Game","SawBlaze"]

That’s our foundation.


Step 2: Wrapping with an MCP Server

Now that we have our API, let’s expose it as an MCP Tool using Microsoft’s Model Context Protocol SDK.

The MCP project is slightly more involved than the API project because we want:

  1. A strongly typed MCP Server
  2. HTTP client resiliency (using IHttpClientFactory)
  3. A reusable tool class (BattleBotsTools)
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Server;
using System.ComponentModel;

var builder = WebApplication.CreateBuilder(args);

var apiBase = Environment.GetEnvironmentVariable("BATTLEBOTS_API_BASE") ?? "https://localhost:7000";

builder.Services
    .AddHttpClient("BattleBotsApi", client =>
    {
        client.BaseAddress = new Uri(apiBase);
        client.DefaultRequestHeaders.Accept.Add(
            new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) =>
            builder.Environment.IsDevelopment() &&
            apiBase.StartsWith("https://localhost", StringComparison.OrdinalIgnoreCase)
                ? true
                : errors == System.Net.Security.SslPolicyErrors.None;
        return handler;
    });

builder.Services.AddMcpServer()
    .WithHttpTransport()
    .WithToolsFromAssembly();

var app = builder.Build();
BattleBotsTools.Configure(app.Services);
app.MapMcp();
app.Run("http://localhost:3001");
[McpServerToolType]
public static class BattleBotsTools
{
    private static IHttpClientFactory _httpClientFactory = default!;

    public static void Configure(IServiceProvider services)
    {
        _httpClientFactory = services.GetRequiredService<IHttpClientFactory>();
    }

    [McpServerTool, Description("Return BattleBots competitor names.")]
    public static string ListBattleBots()
    {
        if (_httpClientFactory is null)
            throw new InvalidOperationException("Not configured. Call Configure(app.Services).");

        var client = _httpClientFactory.CreateClient("BattleBotsApi");
        var path = Environment.GetEnvironmentVariable("BATTLEBOTS_API_LIST_PATH") ?? "/battlebots";

        using var response = client.GetAsync(path).GetAwaiter().GetResult();
        response.EnsureSuccessStatusCode();
        return response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
    }
}

Why IHttpClientFactory?

  • Resiliency: DNS refresh, pooling, retries.
  • Configurable via env vars.
  • Extensible for logging/auth/policies.

The “Magic” of [McpServerTool]

That attribute is what generates the tool specification that MCP clients like Inspector, VS Code, and n8n use to discover/invoke your tool.

Tool spec example:

{
  "name": "list-battlebots",
  "description": "Return BattleBots competitor names.",
  "inputSchema": { "type": "object", "properties": {} },
  "outputSchema": { "type": "string" }
}

In VS Code Insiders, you’ll see it like this:

BattleBots (MCP Server)
└── Tools
    └── list-battlebots
        Description: Return BattleBots competitor names.
        Input: none
        Output: string

Now you can execute both projects and it will use the MCP server tool to proxy our API


Step 3: Inspect with MCP Inspector

Install & run:

npx @modelcontextprotocol/inspector
  • UI → http://localhost:6274
  • Proxy → port 6277

Or Docker:

docker run --rm --network host -p 6274:6274 -p 6277:6277 ghcr.io/modelcontextprotocol/inspector:latest

In the UI, connect via Streamable HTTPhttp://localhost:3001/.
You’ll see and run list-battlebots.


Step 4: Integrating with VS Code & Visual Studio Insiders

Create .mcp.json:

{
  "servers": {
    "battlebots": {
      "type": "http",
      "url": "http://localhost:3001/"
    }
  }
}

Put it in %USERPROFILE%\.mcp.json (global) or <SOLUTIONDIR>\.mcp.json (per repo).
Now in VS Code/Visual Studio Insiders → tools panel → run list-battlebots.

Also you can add it from the chat window using the + sign

Now we can start interacting with the tool.

Ok ok that is interesting, but how can you use the tool for more powerful integration? glad you asked.


Step 5: Automating with n8n

Example workflow:

  • Trigger: When chat message received
  • Ollama Chat Model (llama3.1:8b)
  • AI Agent orchestrates MCP calls
  • MCP battlebots tool fetches competitor names

You can download the file json definition here:

The MCP battlebots tool will have the following configuration

Note that since we are running n8n locally, it will need the host.docker.internal approach we did on the post https://darthseldon.net/n8n-workflows-ollama-ai-agents-docker-edition-host-your-own-ai-and-create-your-own-automation-army/

Finally we connect the chat model (llama3.1:8b in my case) and the magic that connects the AI to the tool, the prompt

Example 1: Filter by Letter

Here’s what the execution result looks like when the agent is asked to return only bots starting with “s”:

Example 2: Order & Count

We can also ask the agent to order the names alphabetically and count the characters in each name:

This demonstrates that the combination of Ollama + MCP tool + n8n isn’t limited to raw lookups — you can transform and enrich the data however you like. (RAG pattern)

In another post I will show you how can you use multiple tools and follow the RAG pattern (Retrieve Augment and Generate). This way you can use the battlebot api, use them to store the length in airtable or find information and "augment" the information about an specific bot.


Step 6: Architecture with C4

Here’s a C4 Container Diagram (PlantUML + C4 library), updated to show the IHttpClientFactory.

@startuml
!include <C4/C4_Container>

Person(dev, "Developer", "Builds & tests tools")
System_Ext(vscode, "VS Code Insiders", "MCP-aware editor")
System_Ext(inspector, "MCP Inspector", "Debug/inspect MCP tools")
System_Ext(n8n, "n8n Automation", "Schedules and orchestrates calls")

System_Boundary(battlebots, "BattleBots Tooling") {
  Container(api, "BattleBots API", "ASP.NET Core Minimal API", "GET /battlebots => JSON list of competitors")

  Container_Boundary(mcpSrv, "BattleBots MCP Server (ASP.NET Core + MCP SDK)") {
    Container(mcp, "MCP Server Host", "Microsoft MCP SDK", "Exposes tool: ListBattleBots")
    Container(httpFactory, "IHttpClientFactory", ".NET Built-in", "Creates resilient HttpClient instances")
  }
}

Rel(dev, api, "Calls directly for local testing", "HTTP")
Rel(mcp, api, "Fetches competitor names\nvia HttpClient created by IHttpClientFactory", "HTTP")
Rel(httpFactory, mcp, "Provides HttpClient", "DI")
Rel(inspector, mcp, "Connects via HTTP transport", "HTTP stream")
Rel(vscode, mcp, "Discovers & executes MCP tool", "MCP")
Rel(n8n, mcp, "Automates tool execution", "HTTP")

SHOW_LEGEND()
@enduml

Step 7: Sequence Diagram of n8n Execution

@startuml
title n8n + MCP + BattleBots — Request Flow

actor User as user
participant "AI Agent\n(n8n)" as agent
participant "Ollama\n(llama3.1:8b)" as llm
participant "MCP battlebots tool\n(httpStreamable)" as tool
participant "MCP Server Host\n(Microsoft MCP SDK)" as mcp
participant "IHttpClientFactory" as factory
participant "BattleBots API\n(ASP.NET Core Minimal API)" as api

user -> agent: "Who are the BattleBots competitors?"
agent -> llm: Analyze prompt / plan
llm -> tool: Invoke tool: list-battlebots
tool -> mcp: Connect (streamable HTTP)
mcp -> factory: CreateClient("BattleBotsApi")
factory --> mcp: HttpClient
mcp -> api: GET /battlebots
api --> mcp: 200 OK\n["Tombstone","Witch Doctor",...]
mcp --> tool: Stream tool result
tool --> llm: Tool output (competitor names)
llm --> agent: Draft final message
agent --> user: Answer with competitor list

@enduml

Conclusion

We’ve gone end-to-end:

  • Built a C# API
  • Wrapped it with an MCP server
  • Validated with MCP Inspector
  • Integrated into VS Code/Visual Studio Insiders
  • Automated with n8n

MCP isn’t just for copilots — it’s a universal protocol for turning APIs into discoverable tools.

Next time, we’ll explore making the tool smarter — maybe even adding OData filters so copilots can query bots directly.

Happy coding!!!


Code Repository

All the source code used in this post — including the BattleBots API, MCP server, and n8n workflow sample — is available on GitHub:

👉 https://github.com/jtenoriodseldon/mcpblogdemo

Clone the repo, run the API + MCP server locally, and import the sample workflow into n8n to get started right away.