BuildAgent World

Agent
World

A pixel-art town that runs on my real MCP stack. Each NPC is a service I use — Asana, Notion, n8n, Linear, Arc, Google Drive, Excalidraw, Granola, Claude Code — and when a task lands or a workflow fires, the matching NPC wakes up and walks to its quarter.

The whole town9 agent types

  • Phaser 3
  • TypeScript
  • WebSocket
  • Express
  • Canvas 2D
  • Vite
Specialized Agents
0
Procedural Buildings
0
Agent.ts Lines
0
State Machine States
0

Live dashboard

The town, running

Click to interact.

The mapping

9 sources → 9 archetypes

Every tool in my stack maps to one NPC archetype. Real activity lands — a task, a page, a workflow run — and the matching agent wakes up and walks to its quarter.

  • Asana

    Gildenläufer

    guild runner

    task events

  • Notion

    Bibliothekar

    librarian

    page events

  • n8n

    Ingenieur

    engineer

    scenario runs

  • Claude Code

    Magier

    mage

    thinking / response

  • Google Drive

    Schatzwächter

    treasure keeper

    files

  • Excalidraw

    Kartograph

    cartographer

    sketches

  • Linear

    Stratege

    strategist

    issues

  • Arc

    Späher

    scout

    tabs, bookmarks

  • Granola

    Wirt

    innkeeper

    meeting notes

How it's wired

From MCP to NPC

The town sits on top of my live MCP and automation stack. Work that happens in a tool runs through the adapter layer and shows up in the world as an NPC reaction.

  1. MCP servers + Claude Code

    notion, asana, make, second-brain — the layer where I actually work.

  2. Activity lands in external tools

    A new Asana task, a Notion page edit, a Drive upload, a Claude Code session.

  3. AdapterManager polls every 30s

    Express + ws. Up to 6 live integrations (Asana, Notion, n8n, Linear, plus file watchers for Excalidraw and Claude Code). Mocks fall back when env tokens are missing.

  4. WebSocket broadcast to the game

    Typed events only — source, type, data, timestamp. No game logic on the server.

  5. EventDispatcher wakes up an NPC

    SOURCE_TO_AGENT maps the event to an idle agent of matching type. assignTask() fires.

State machine

Four states per agent

Every agent cycles through idle, alert, walking, and working — a real event turns into a roughly five-second scene you can watch. All four transitions sit in one Agent.ts class, 429 lines, no framework.

  1. idle

  2. alert

  3. walking

  4. working

Agent.tsts
type AgentState = 'idle' | 'walking' | 'working' | 'alert'; // Event arrives → alert → walk → work → back to idleassignTask(taskName: string, duration: number, destX: number, destY: number) {  this.pendingTask = { name: taskName, duration };  this.showAlert();                                 // state: 'alert'  setTimeout(() => this.walkTo(destX, destY), 600); // state: 'walking'  // On arrival:  startWork()     → state: 'working' + progress bar + bubble  // On finish:   completeWork()  → state: 'idle'    + green particle burst}

Decisions

Calls I'd defend

  • Phaser 3 over raw Canvas

    It ships a sprite system, physics, and scene management. On raw Canvas I'd have rebuilt half of that myself.

  • The agents walk before they work

    An event could flip a tile straight to "done" — but then there's nothing to see. The detour makes an Asana card closing something you watch happen, not a number that ticks up.

  • EventDispatcher is one source-to-archetype dictionary

    9 sources map to 9 archetypes in one dictionary. The game scene never hears Asana or Notion — only Gildenläufer and Magier — so a new tool is one adapter and one line in the map.

  • Polling by default, webhooks when I want them

    AdapterManager polls every 30s, so the server runs locally without tunnels. A POST /api/webhook endpoint gives n8n near-instant NPC reactions when I want them.