Using AI to play BitBurner - Part 1: a swing and a miss.
I thought it might be fun to let a large language model hack around a network and try to steal lots of money from people. Luckily it wasn't a real network, and there was no real money involved.
This post is one of a series describing my experiment playing BitBurner with Claude Sonnet 4.
It started off, as so many of these silly ideas do, from a place of curiosity.
When I’m not working at my day job, I like to keep busy by sharpening my skills. Sometimes that’s the ever-changing world of infrastructure, sometimes it’s software development, and sometimes it’s computer gaming. I love to play simulation games and some of my firm favourites include the “Cities: Skylines” franchise, anything in any way related to “Total Annihilation”, and more recently, hacking games.
BitBurner is a particularly interesting game. At its heart, it’s deceptively simple. You start off with your own computer, and use it to hack into servers on a virtual internet. You extract money from those servers by “weakening” security and then “hacking” to gain root access, which allows you to extract the money. You then “grow” the server to increase the amount of money available from it, and rinse and repeat until you have ridiculous amounts of money. Every successful hack or grow results in the server’s owner increasing its security level, and so you end up in a loop which is generally summarised as the “weaken-hack-grow” loop. There are a few nuances around using programs to break through ports on firewalls, joining factions and maintaining reputation, buying hardware upgrades, and personal stats which add a small amount of complexity.
The interesting part of the game is that the objective of the game seems almost to be to not actually play it. BitBurner relies very heavily on automation through the use of a built-in scripting environment that allows you to write JavaScript or TypeScript to interact with the game’s internal API, and the only realistic chance you have of “finishing” the game is to automate your way to success. It would take too long to play manually.
The game does a reasonable job of teaching you how its mechanics work as part of the tutorial, but once you’ve understood the weaken-hack-grow loop you’re on your own. It’s down to players to devise their own strategies and then write scripts to carry out these strategies, and there’s an entire community of people who claim to have written “the” script that will win BitBurner. Some of them are awesome and do win BitBurner. Some of them are terrible, and some of them don’t even run properly. If you want to play just by downloading someone’s scripts and watching the money stack up, it’s almost trivially easy to win.
But how do you get scripts into the game? You can copy and paste script after script into the game’s built-in editor (it’s VS Code!), but this gets tedious quickly if you find a whole set of scripts that need to be saved into the game one-by-one. Luckily, BitBurner has a tricky up its sleeve in the form of a WebSocket API that exposes a handful of JSON-RPC methods that allow you to interact with the file system of your virtual computer inside the game (read the API spec). BitBurner can connect out to a WebSocket server and then the server can call back to BitBurner to do some file management.
The community have written plenty of different servers for BitBurner to connect to, including an extension for VS Code, but I wanted to do something a little more unusual. I wanted to get an LLM to play BitBurner. And that websocket API was a route into the game from the outside world. It was time to write an MCP server…
Wait. What is MCP?
MCP, Model Context Protocol, is an open standard originally introduced by Anthropic to allow AI systems (and in particular large language models or LLMs) to interact with other external systems. Developers can create tools that conform to the MCP spec, and then AI models can call to those tools to share data and consume services. MCP defines a mechanism for tool discovery (a bit like OpenAPI), and a mechanism to invoke those tools.
Let’s say you’re writing a customer service chatbot and you want to let an LLM find out the status of an order for some products that a customer has placed; you could set up a REST API and try to poke your LLM of choice to get it to talk through that REST API. It might work, assuming the LLM is capable of forming a valid request to your REST API, and then consuming the response - however, it’s much easier, and much more robust, to author an MCP server and have your LLM interact with customer data through a tool provided via MCP.
There are SDKs to write MCP servers in a variety of programming languages and on a variety of frameworks. I’m a .NET guy, and there is a preview SDK available for authoring MCP servers and clients [https://learn.microsoft.com/en-us/dotnet/ai/quickstarts/build-mcp-server]. I considered writing an MCP server for BitBurner, but then I had a better idea.
Can Claude write an MCP server?
Of course it can.
I think Claude is a fantastic product. It seems to have a good grasp of C# and .NET, and is useful in a consultative rule when I’m architecting or writing software. I prefer it over GPT models because it seems to write better code, seems to have a better grasp of how to work according to many different architectures, and is reasonably up-to-date with some of the more exotic parts of the Microsoft developer ecosystem (including Microsoft Orleans, which will be the subject of at least one future blog post!). So I wondered whether Claude could write its own MCP server to play BitBurner.
As much as I think Claude is fantastic, like with any use of an AI tool that can write code and develop software (or most applications of generative AI in general), you can’t rely on it to deliver what you want in just one iteration. Right now, I believe that even the best LLMs that have been tuned for software development are only as good as a mid-level developer. They’re good at automating the boilerplate stuff, and given a robust spec can generally deliver something usable, but they’re not good at being left unwatched to write entire applications. They hallucinate, they write code that doesn’t compile, and they rely on assumptions that may or may not turn out to be true. As fallible as they can be, they’re also good fun to watch, and so I decided on a little experiment.
I would get Claude to do all of the work to be able to play BitBurner by itself. I wouldn’t write a single line of code, I’d just guide it through the process until it could play.
The first step in the process was to understand how much Claude knew about BitBurner, and it turns out that Claude is already well versed in the game. I asked it to summarise how the game works:
Yes, I’m familiar with Bitburner! It’s an incremental/idle game with a cyberpunk theme where you play as a hacker. The core gameplay involves writing actual JavaScript code to automate various hacking activities, managing servers, and growing your virtual empire. Key elements include:
- Writing scripts to automate hacking targets for money
- Managing your own network of servers
- Joining factions and completing their requirements
- Augmentations that provide permanent upgrades
- Various mini-games and mechanics like the stock market, gangs, and corporations
The game is particularly interesting because it actually teaches programming concepts - you write real JavaScript to control your character’s actions, optimize your hacking strategies, and automate complex workflows. It starts simple but can get quite sophisticated as you progress.
So we were off to a great start. Claude knew the basic premise of the game, and knew that it can be automated with JavaScript. I asked if it knew of any existing MCP servers that would let it play BitBurner and after about 5 minutes it confirmed that it couldn’t find any evidence of any. Now we’re trailblazing…
Next up was to understand whther Claude knew about BitBurner’s websocket capabilities. It did, and correctly identified JSON-RPC 2.0 as a mechanism to interact with BitBurner from the outside. So I asked it a fairly meaty question.
Do you think you could write the MCP server?
Absolutely! Given my C# background and your preference for working backward from end goals, this would be a perfect project. The technical foundation is solid - Bitburner’s WebSocket API using JSON-RPC 2.0 aligns well with MCP’s architecture.
Here’s how I’d approach it:
End Goal: An MCP server that lets you interact with Bitburner through natural language, handling script deployment, file management, and game state monitoring.
Technical Stack: I’d use the official MCP TypeScript SDK since it’s the most mature, but could also implement it in C# using the official MCP C# SDK that Microsoft collaborates on.
Core Implementation:
- WebSocket bridge to connect to Bitburner’s localhost:12525 API
- MCP tool definitions for common operations (deploy script, check RAM, get server list, etc.)
- State synchronization through Bitburner’s file system for real-time data
- Script templates for common automation patterns
Simple starting point: Let’s begin with basic file operations - push/pull scripts, check RAM usage, and deploy simple automation. Once that works, we can expand to more sophisticated features.
The beauty is that Bitburner’s community would likely be very receptive to this, since they’re already using external tools like the VS Code extension.
Would you like me to start with a basic implementation? I could create the initial MCP server structure and show you how it would connect to Bitburner’s API. What specific Bitburner operations would be most valuable to you for AI assistance?
My reply was short. I told it “You decide. I want you to able to play the game.”
It “decided”, and a few minutes later we had an MVP. It was then that things started to get really odd.
First up, Claude spat out a load of TypeScript and then told me it had produced an extremely capable MCP server:
Core Game Capabilities:
- Server Discovery: Scan and map the entire network
- Server Analysis: Get detailed info on any server (RAM, money, security, etc.)
- File Management: Create, read, update, and delete scripts
- Script Execution: Run and kill scripts with threading control
- Player Status: Check my character’s stats, money, and progress
- Hacking Operations: Actually hack servers for money
Key Features:
- WebSocket Integration: Connects directly to Bitburner’s Remote API
- Smart Scripting: Uses NetScript bridge scripts to access game data that isn’t directly available via the Remote API
- Error Handling: Robust connection management and timeout handling
- Real-time Operations: Can monitor running scripts and server states
How it works: The server acts as a bridge between you (through MCP) and Bitburner’s game engine. When you ask me to do something in the game, I’ll use these tools to:
- Analyze the current game state
- Create and deploy NetScript automation
- Execute hacking operations
- Monitor and manage ongoing activities
To set this up, you’d need to:
- Install the MCP TypeScript SDK and WebSocket dependencies
- Enable Bitburner’s Remote API (in game settings)
- Configure your AI client to use this MCP server
The eagle eyed reader will notice two big problems in this response.
I had a read through the TypeScript file it had generated. I’m not a TypeScript expert, but I know enough to be able to read and lightly edit TypeScript files. The structure of the script looked decent at first glance, but it was actually a bit of a mess.
-
Claude had written some lovely looking code to handle connecting out to BitBurner’s WebSocket server but the “Robust connection management and timeout handling” was entirely missing from the script, not least because it’s impossible for the MCP server to handle a connection to a server that doesn’t actually exist. BitBurner has to connect out to a server, not the other way around! There used to be a BitBurner API that accepted incoming remote connections but this has been deprecated in more recent versions of the game. Claude was relying on old knowledge.
-
Claude had invented some entirely new API methods in the BitBurner API - there were definitions to call methods such as
getRunningScripts
,runScript
, andkillScript
. The BitBurner API just doesn’t offer these methods. However, the Netscript API (which you call from the scripts running inside the game) offers these functions. Claude had totally confused the BitBurner API and the Netscript API, and was trying to call Netscript methods via a WebSocket. Was this a hallucination, or dodgy reference documentation? -
Claude had written a God class! Just one class was responsible for everything from receiving requests via Stdin from the MCP client, building and sharing a tool spec with the client, initiating the connection outbound to the non-existent BitBurner API server. This God class contained 19 methods spanning almost a thousand lines of beautifully formatted but totally non-functional code.
-
The whole thing would just explode if there were any errors thanks to the very final line of code in the script:
server.run().catch(console.error);
A first attempt that was simultaneously brilliant and appalling.
In the next post, I’ll explain how I worked with Claude to iterate over multiple versions of the MCP server to get Claude connected to BitBurner. And in the final post, I’ll look at the technical and ethical concerns raised by this experiment, and discuss how this kind of work is dangerously close to something much more ominous…