Simulate Your Cloud Architecture with an MCP Server Before You Deploy
Most capacity questions I get asked boil down to "what happens if traffic triples?" Usually the honest answer is "we'll find out in production." I build Cloud World Model (CWM), a simulator you can call over MCP, so an AI assistant can work through that question before deploying or changing infrastructure.
This post walks through one real run: connecting, describing a small AWS stack, running it at a baseline, spiking the traffic, and reading the results. Every number below came straight from the tool responses. I didn't round anything into a nicer story.
1. Connect your MCP client
The server is hosted and uses streamable HTTP. There's nothing to install locally.
{ "mcpServers": { "cloud-world-model": { "url": "https://www.cloudworldmodel.ai/mcp" } } } Nine tools work keyless: scenario.list, scenario.get, simulation.create, simulation.step, simulation.metrics, simulation.inject_traffic, simulation.inject_failure, simulation.recover_resource and simulation.delete. Keyless simulations are temporary demos, capped at 10 resources and 20 steps, and they expire. Adding an x-api-key header unlocks all 63 tools plus persistent simulations. Setup details for each client are in the MCP quickstart.
2. Describe the stack
I used a deliberately boring stack: an internal ALB, two m5.large Node app servers (2 workers, DB pool of 250 each), and one db.r5.large running MySQL 8.0 with max_connections at 500, all in us-east-2. Autoscaling is off, so the fleet stays at two.
// simulation.create (abridged) { "appWeight": "typical", "traffic": 200, "minInstances": 2, "maxInstances": 2, "resources": [ { "id": "alb", "type": "network", "characteristics": { "serviceFamily": "alb", "loadBalancerScheme": "internal" } }, { "id": "app1", "type": "compute", "characteristics": { "size": "m5.large", "workload": "crud-typical", "appRuntime": "node", "appWorkerCount": 2, "appDbPoolSize": 250, "autoscaling": false } }, { "id": "app2", "type": "compute", "characteristics": { "size": "m5.large", "workload": "crud-typical", "appRuntime": "node", "appWorkerCount": 2, "appDbPoolSize": 250, "autoscaling": false } }, { "id": "db", "type": "database", "characteristics": { "size": "db.r5.large", "databaseEngineIdentity": "mysql", "workloadDatabaseVersion": "8.0", "maxConnections": 500 } } ], "connections": [ { "sourceId": "alb", "targetId": "app1" }, { "sourceId": "alb", "targetId": "app2" }, { "sourceId": "app1", "targetId": "db" }, { "sourceId": "app2", "targetId": "db" } ] } In practice I didn't hand-write this. I described the stack to the assistant in a sentence and it built the call from the tool schema.
3. Baseline: 200 RPS
simulation.step with steps: 5. The engine (version 1.2.65) returned:
| Metric | 200 RPS |
|---|---|
| p50 / p95 / p99 latency | 4.52 / 15.01 / 87.10 ms |
| Error rate | 0% |
| App CPU (each node) | ~10% (100 RPS routed to each) |
| MySQL CPU | 18.6% |
| Modeled DB connections | 40 of 500 |
| Modeled cost | $0.76/hr |
The response also labels where each number comes from. This topology matches a load test we ran ourselves at 20–300 RPS, so the baseline evidence level is measured and the p95 range is 10.6–20.0 ms. That label means the model is fit to real k6 runs. It doesn't mean this simulation measured your system.
4. Spike: 600 RPS
simulation.inject_traffic with targetRps: 600, then another 5 steps:
| Metric | 200 RPS | 600 RPS |
|---|---|---|
| p50 / p95 / p99 latency | 4.52 / 15.01 / 87.10 ms | 14 / 38 / 422 ms |
| Error rate | 0% | 10% |
| Throughput (goodput) | 175 RPS | 540 RPS |
| App CPU (each node) | ~10% | 100%, status critical |
| MySQL CPU | 18.6% | 18.5% |
| Modeled DB connections | 40 / 500 | 108 / 500 |
| Error breakdown | none | cpuOverload: 10 |
| Modeled cost | $0.76/hr | $1.42/hr |
5. What the numbers mean
The app tier is the bottleneck in this model, not the database. Both app servers sit at 100% CPU and are marked degraded (still serving traffic). All of the 10% errors are attributed to CPU overload, and none to pool saturation or database pressure. MySQL CPU barely moves, and modeled connections stay at about a fifth of the limit. If I were planning for a 3× spike on this stack, I'd look at app capacity or autoscaling first. I wouldn't start with a bigger database.
The p99 jump from 87 ms to 422 ms matters more than the p95 change. That's the tail your slowest users feel.
Each step response is also explicit about what the model doesn't know, which is the part I care about most:
- Cost is a list-price model. The two m5.large nodes come out at $0.096/hr each and the db.r5.large MySQL at $0.24/hr, so $0.432/hr of the total is fixed. The rest is ALB and egress, which grow with traffic: about $0.33/hr at 200 RPS and $0.99/hr at 600 RPS. That's why the total goes from $0.76/hr to $1.42/hr. These are on-demand list prices for us-east-2, not your bill, so discounts, reservations and taxes aren't included.
- The spike was outside the fit. At 600 RPS the evidence level dropped from
measuredtoreference estimate,loadScopebecame "beyond measured load," and p99 was flagged as uncalibrated. Treat the spike numbers as directional. - No chaos/resilience score came back for this kind of run, so I'm not reporting one.
- The MySQL prepared-statement forecast returned
unavailablebecause I didn't supply its inputs (cap, schedule, starting counts). It doesn't fill in defaults for you.
6. Clean up
simulation.delete returned { "deleted": true }. Keyless demos expire on their own anyway, but deleting frees one of your two active slots.
Honest limits
These are modeled estimates, not measurements of your system. The engine is calibrated against a specific set of load tests, and outside those it extrapolates and tells you so. Cost figures use list prices, not your bill. I publish what's been validated and what hasn't on the accuracy page.
For an example of the model being wrong first and then checked against a holdout, I wrote up why adding app servers didn't raise our breach point. In that case it was MySQL's prepared-statement limit, which is a different bottleneck from the one in this run.
Try it
Add the config above to your MCP client and ask your assistant something like "Simulate an ALB with two m5.large servers and a db.r5.large MySQL at 200 RPS, then triple the traffic. What breaks first?" The MCP quickstart covers setup for each client.
If you run it against your own stack and the model gets something wrong, I'd like to hear about it in the comments.