
Give Your AI Agent an HPC Cluster Through MCP Tools
Wire a generative AI agent to an OpenFOAM cluster on AWS PCS so it can run real-time CFD simulations and act on the results in seconds.
Give Your AI Agent an HPC Cluster Through MCP Tools
Most agent demos stop at "the model called an API and got some JSON back." That's fine when the tool is a weather lookup. It falls apart the moment the agent needs to actually simulate something — run a fluid dynamics model, sweep an ensemble of scenarios, and come back with a physically grounded answer while a real-world operation is still in progress.
I wanted an agent that could do exactly that: take a proposed engineering plan, run it through a real computational fluid dynamics (CFD) solver on a proper HPC cluster, and hand back optimization and risk numbers fast enough to matter during a live, minute-by-minute operation. The interesting part isn't the physics — it's the plumbing. How do you give a language model a supercomputer without letting it anywhere near the supercomputer?
This post walks through the pattern I landed on: the agent only ever calls tools; the tools drive the cluster. It's a clean separation that keeps the model safe, the cluster fast, and the whole thing cheap when nobody's using it.
The problem with the obvious approach
The naive design is to let the agent SSH into a head node and run commands. Don't. An LLM with shell access to your HPC scheduler is a bad day waiting to happen — prompt injection, runaway job submission, and zero audit trail all live down that road.
The other trap is latency. If you back your simulations with AWS Batch , every job pays a cold-start tax — instance provisioning, container pull, container start, queue delay. That's 3–7 minutes of overhead per job before a single cell of your mesh is solved. Perfect for overnight batch runs. Useless when your operation updates every 60 seconds and you need a prediction before the next cycle begins.
Real-time simulation has two hard requirements that push you toward a persistent HPC cluster:
- Sub-10-second job start. Warm compute nodes, no container pull, no provisioning wait.
- A shared, fast filesystem. So each cycle can restart from the previous cycle's checkpoint instead of rebuilding state from scratch.
The architecture
Here's the shape of it. The agent lives in a container and speaks only to a GraphQL API. Behind that API, functions translate intent into scheduler commands. The cluster does the heavy lifting and writes results to object storage, which the agent reads back through another tool.
Plain text
Agent tool ──GraphQL──▶ API ──▶ Function ──(SSM RunShellScript)──▶ Login node ──sbatch──▶ SLURM│OpenFOAM runs on shared FS ◀────┘│results copied to object store ◀┘│Agent tool ──GraphQL──▶ API ──▶ Function ──(read object store)─────────┘
Every layer here maps to a managed AWS service:
Layer | Service | Why |
|---|---|---|
Cluster + scheduler | AWS Parallel Computing Service (PCS) | Managed SLURM ; persistent login node, auto-scaling compute |
Shared filesystem | FSx for Lustre | Sub-second checkpoint reads; auto-syncs to S3 |
Compute | Hpc7g (Graviton) with EFA | Cost-efficient arm64 cores with a low-latency Elastic Fabric Adapter interconnect for MPI |
Cluster access | AWS Systems Manager Run Command | Runs sbatch on the login node — no open SSH, full CloudTrail audit |
Agent-facing API | AWS AppSync | GraphQL front door; resolvers are functions, auth is scoped |
Solver | OpenFOAM | Open-source CFD, runs the actual simulation |
The key design decision: the agent's tools call GraphQL, and GraphQL resolvers — not the model — hold the credentials and the cluster commands. The model can ask for a simulation. It cannot decide how that simulation reaches the scheduler. That boundary is the whole security story.
Why MCP is the right seam
The Model Context Protocol (MCP) gives you a standard way to expose these capabilities as tools the model can discover and call. Instead of hard-coding one bespoke integration, you define a handful of well-named tools with typed inputs and let the agent compose them. In this system the tool surface looks like:
-
evaluate-treatment-plan— submit a plan as a 3-D CFD simulation, poll to completion, return optimization and risk metrics.
-
run-ensemble-simulation— fan one base plan out to ~10 concurrent geological-scenario simulations.
-
interpret-pressure-response— match an observed real-world pressure curve against the ensemble.
-
assess-screen-out-risk— score the risk of the operation failing and estimate lead time before it does.
Each tool is a thin GraphQL client. The agent reasons about which tool to call and what to pass; the resolver behind it does the privileged work. If you're running your agent on Amazon Bedrock AgentCore , these tools plug straight into its runtime, and the same MCP tool definitions work whether the model is Claude on Amazon Bedrock or something you host yourself.
The abstraction pays off in a second way: your tools return decision-ready data, not raw solver output.
getCfdResults reads a metrics.json from object storage — it never touches the cluster. The model sees clean numbers, not gigabytes of VTK field files.What actually happens on a submit
When
evaluate-treatment-plan fires, the resolver:- Generates a SLURM batch script for the requested case (steady-state or transient
pimpleFoam).
- Finds the running login node by EC2 tag.
- Runs
sbatchon it via SSMAWS-RunShellScript— no SSH session, everything logged.
- Stores the returned SLURM job ID on a DynamoDB record.
The tool then polls a
getCfdJobStatus operation, which walks squeue → scontrol → sacct and finally checks the S3 result prefix to map the job to a state. When results land, FSx for Lustre auto-exports the /fsx changes back to S3, and the results tool reads them directly.Here's the abbreviated SLURM script the resolver generates for a real-time restart cycle — note it restarts from the latest checkpoint rather than cold-starting the mesh:
Bash
#!/bin/bash#SBATCH --job-name=cfd-realtime#SBATCH --nodes=1#SBATCH --time=00:15:00#SBATCH --output=/fsx/simulations/%j/slurm-%j.outsource /opt/openfoam/etc/bashrccd /fsx/simulations/$SIM_ID# Restart from the most recent written time stepLATEST=$(foamListTimes -latestTime)cat > system/controlDict.overrides <<EOFstartFrom latestTime;startTime $LATEST;endTime $((LATEST + 600)); # predict 10 minutes aheadEOF# Solve — MPI ranks capped to match mesh size (see perf note below)mpirun -np 8 pimpleFoam -parallel# Compute decision metrics and push to object storagepython3 /fsx/scripts/calculate_metrics.py --sim-id $SIM_IDaws s3 cp results/ s3://$BUCKET/cfd-simulations/$SIM_ID/results/ --recursive
The performance surprise
The first version of this took ~18 minutes per simulation. That's not real-time by any definition. Profiling turned up a counterintuitive culprit: too much parallelism.
For a small mesh (a few thousand cells), running the solver across 64 MPI ranks meant each rank owned only ~60 cells — and the MPI communication overhead completely dominated the actual math. Capping ranks at 8 dropped the solver time from ~6 minutes to 3 seconds. Two other cuts — skipping VTK conversion (the metrics script reads OpenFOAM field files directly) and reducing write frequency — took the rest.
Phase | Before | After |
|---|---|---|
Solver ( pimpleFoam ) | ~6 min | ~3 s |
reconstructPar | ~5 min | ~50 s |
VTK conversion | ~3 min | skipped |
Total (warm node) | ~15 min | ~1.5 min |
The lesson generalizes well beyond CFD: match your parallelism to your problem size. More cores is not more speed once communication cost exceeds per-rank compute. Measure before you scale out.
Cost: know what's always-on before you commit
The economics are the part that makes this deployable — but only if you're honest about what runs at rest. The compute node group is configured
min=0, max=10 and scales to zero when idle, so the expensive hpc7g cores cost nothing between operations.- Cluster controller fee — charged for the managed SLURM controller whether or not a single compute node is running. It scales with controller size: Small is $0.5825/hour (
$425/month), Medium is $3.2579/hour ($2,346/month), Large is $6.5426/hour. A real-time demo like this one runs comfortably on a Small controller.
- Node management fee — $0.08 per instance-hour (standard tier) for every EC2 instance PCS launches in a node group. This does scale to zero with your compute.
So the real always-on floor for a persistent, scale-to-zero cluster is roughly:
Always-on component | Rough monthly cost |
|---|---|
PCS controller (Small) | ~$425 |
| FSx for Lustre (1.2 TB PERSISTENT_1) | ~$170 |
Login node ( t3.medium ) | ~$30 |
Call it ~$600/month at idle, before you run a single simulation. The
min=0 compute group and the $0.08/instance-hour node fee are what stay near zero between operations.That trade — a fixed monthly floor for the controller and shared filesystem, in exchange for sub-10-second job starts when warm — is what makes real-time, agent-driven simulation practical. You're paying to keep the scheduler and checkpoints warm, not the compute. Just don't budget it as "free when idle," and check the pricing page for your controller size and region before you commit.
Takeaways
- Never give the model the keys. The agent calls tools; the tools (GraphQL resolvers) hold the credentials and issue the cluster commands. SSM Run Command beats open SSH — it's auditable and needs no inbound ports.
- Persistent clusters beat Batch for real-time. Cold-start latency kills minute-by-minute loops. A warm PCS/SLURM cluster with FSx for Lustre restarts from checkpoints in seconds.
- MCP tools should return decisions, not data. Read results from object storage and hand the model clean metrics — keep the field files out of the context window.
- Right-size parallelism. For small meshes, fewer MPI ranks is dramatically faster. Profile before scaling out.
- Scale to zero, but budget the floor. A
min=0compute node group keeps expensive cores near zero when idle — but the PCS controller fee and FSx bill around the clock. Know your always-on floor (~$600/month here) before you commit.
If you're building agents that need to reach into serious compute — CFD, finite-element, Monte Carlo, genomics — this tool-in-front-of-a-cluster pattern is the one I'd reach for again.
Related reading
- Wiring CopilotKit to a Managed Bedrock AgentCore Harness — the front end for an agent like this one.
- Host Claude Code on Bedrock AgentCore with the AgentCoreApplication CDK Construct — deploying the agent runtime that hosts these tools.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article