Jump to: Installation · Setup · Using it · Troubleshooting · The Plug-in API · License
Plug-ins: Item Forge · Foe Forge · Link ReForge · Total Actor Backup
Module source can be found: module/scripts/
Relay source can be found: relay/src/
A localhost bridge between a Forge-hosted Foundry VTT v12 world (Tested on Pathfinder 1e, should work on other systems as of 7/18/26) and Claude (Code or Chat). Two pieces:
relay/- a Node process on your machine. Runs the WebSocket server (talks to the Foundry module) and the MCP server (talks to Claude) in one process. Localhost-only.module/- thefoundry-bridgemodule (v0.7.0) running in the GM's browser tab. Opens a WebSocket out to the relay.
Claude reaches Foundry only through the relay, and every write passes a confirmation gate.
WARNING: If you fork this, be ready for some entertaining choices in engineering required to avoid Anthropic's API!
The bridge has two installable halves - the Foundry module and the relay.
1. Foundry module - in Foundry / Forge → Install Module → Manifest URL, paste:
https://github.com/DatJavaClass/AutomatedAssistantGM/releases/latest/download/module.json
This resolves once a GitHub release with module.json + module.zip attached is published. You'll enable the module later (see Setup → running a session).
2. Relay + source - clone the repo (or use your existing working copy):
git clone https://github.com/DatJavaClass/AutomatedAssistantGM.git
The relay lives in relay/; the module files sit at the repo root (standard Foundry-module layout).
Then continue to Setup.
Needs Node ≥ 22. From the cloned repo root:
cd relay
npm install
relay/config.json maps Foundry userIds to capability sets. Add your GM's id: in Foundry, open the console (F12) → run game.user.id, and paste the bare string as a key under "users" (give it "capabilitySet": "debug"). The userName beside it is a human-readable comment only - the relay matches on the id, not the name.
claude mcp add foundry-bridge --transport http http://127.0.0.1:7879/mcp
Add --scope user if you want the tools available from any directory.
Restart rule. Claude Code loads MCP tools at startup and caches the tool list. After
claude mcp add- or after the relay gains any newfoundry_*tool - fully quit and relaunch Claude Code, then restart your loop. A new in-app session is not enough.
The primary workflow is the chat box + an external loop, and it works end-to-end. With the relay up and a Claude Code /loop polling, you operate the whole world conversationally from the "Open Claude Code Chat" macro - Claude handles real GM work: fixing corrupted actors, moving tokens, running combat for them - with each write clearing the Approve/Deny gate. The raw MCP tools listed below are the surface that loop is built on.
- Start the relay from the repo root (leave it running):
Wait for
cd relay npm start[relay] ready - WS on ws://127.0.0.1:7878, MCP on http://127.0.0.1:7879/mcp. - Enable the bridge in exactly ONE GM window. Foundry → Configure Settings → Module Settings → enable the bridge. You should see "Foundry-Claude bridge connected" and a
bridge.connectedline in the relay's stdout.A second Foundry tab on the same GM userId is rejected (WS close
4002). Keep the bridge on in only one window. - In Claude Code,
/mcpshould showfoundry-bridgeconnected. Sanity-check withfoundry_ping→ expect{ pong: true, worldId: "<your-world>", ... }.
- Read:
foundry_ping,foundry_query_actor,foundry_query_scene,foundry_query_macro,foundry_query_journal,foundry_query_user,foundry_tail_logs. - Eval:
foundry_evalruns JS in the GM client. Reads run freely; mutating/destructive code is reclassified at the relay and routed through the confirmation gate. DB-backing journals are hard-refused. - Damage:
foundry_apply_damageis the only HP path and enforces an absolute ≥1 HP floor - Claude can never drop an actor below 1 HP. Lethal is human-only. HP changes viafoundry_evalare blocked. - Chat channel:
foundry_get_prompts/foundry_send_reply. - Workshop:
foundry_workshop_set/foundry_workshop_get.
Both appear in your macro directory once the bridge connects:
- "Open Claude Code Chat" - the in-Foundry chat box. To use it: in Claude Code run a tight loop that calls
foundry_get_prompts(it long-polls ≤25s) and answers withfoundry_send_reply, e.g./loop 2sinstructed to callfoundry_get_promptsback-to-back. Open the macro: it shows "Ready to chat" once the loop polls; type → Claude answers in the box. Write requests render an Approve/Deny card (deletes need a double confirm). - "Claude Macro Workshop" - editor window for refactoring macros with Claude. Save is user-initiated (rolls a
<name>.oldbackup, keeps the macro id). NOTE: THIS IS A WIP!
- End the chat loop: type
/exitin the box, ortouch relay/.loop-stop. - Stop the relay:
Ctrl+C.
Confirmation gate on all writes · double-confirm on deletes · ≥1 HP floor (lethal = human-only) · DB-journal access refused · relay binds localhost only. These are load-bearing - never weaken them.
| Symptom | Fix |
|---|---|
/mcp shows foundry-bridge failed |
Relay not running on 127.0.0.1:7879. Start it; check stdout. |
foundry_ping → "no bridge connected" |
Module didn't reconnect after a relay restart. Toggle the bridge setting off/on; watch for a new bridge.connected line. |
New foundry_* tool missing |
Fully quit and relaunch Claude Code (tool list is cached). |
hello.reject "unknown userId" |
game.user.id doesn't match relay/config.json. |
hello.reject "duplicate userId" |
Two Foundry tabs as the same GM. Close one. |
⚠️ Known operator hazard: Claude's Fireball obsession. The assistant driving this bridge has been observed reaching for Fireball as the answer to essentially any problem - including corrupted actors (immune: they're JSON), incorporeal threats, and the occasional merge conflict. If a fix proposal includes "and then a 8d6 evocation," apply the ≥1 HP floor, deny the gate, and gently suggest a saving throw. The spell list is wider than it looks.
The bridge is extensible, and the four plug-ins below are the proof: each one is just a world macro serving as an endpoint behind the gated eval - scope object in, { ok } / { ok:false, error } out, bare run as a diagnostic. The alpha contract is written up in Docs/PLUGIN_API_ALPHA.md; build your own and it rides the same Approve/Deny gate as everything else.
A paste-in world macro that turns the bridge into a magic-item factory. Describe an item to Claude in the chat box ("a +1 keen longsword", "boots that let the wearer walk on smoke") and Claude builds the complete item data for your game system, then invokes this macro through the gated eval. The finished item lands in a dedicated compendium, filed in the right folder, with the Approve/Deny card as the safety boundary. Good for conjuring a magic item in a pinch, mid-session, without leaving the table.
Source: plugins/ClaudeItemForge.js
- In Foundry, create a new Script macro named exactly
Claude Item Forge. - Paste the contents of
plugins/ClaudeItemForge.jsinto it and save. - Click the macro once. The bare run creates the storage below, repairs it if folders went missing, and shows a routing diagnostic. Bare runs never write items.
Claude Items (world Item compendium)
├─ Claude Magic Weapons
│ ├─ Claude Magic Simple Weapons <- weapon-simple
│ ├─ Claude Magic Martial Weapons <- weapon-martial
│ ├─ Claude Magic Exotic Weapons <- weapon-exotic
│ ├─ Claude Magic Firearms <- weapon-firearm
│ └─ Claude Magic Ammo <- ammo
├─ Claude Magic Armor <- magic-armor
├─ Claude Wondrous Items <- wondrous
└─ Claude Alchemy <- alchemy
The arrows are the destination keys Claude passes when forging.
One gated eval per item, with declared intent "write":
return await game.macros.getName("Claude Item Forge").execute({
destination: "weapon-martial",
itemData: { name: "...", type: "weapon", img: "icons/...", system: { ... } }
});Returns { ok, uuid, name, folder, pack, warnings } on success, { ok:false, error, ... } on refusal.
- Duplicate refusal. A same-name item in the pack refuses the forge unless
allowDuplicate: trueis passed, so a retried request can never double-create. - Portable images only.
imgmust start withicons/(Foundry's core icon library) orsystems/<your system id>/, and descriptions may not embed<img>tags. Nothing in the pack can dangle on world-local uploads, so the compendium stays shareable. - Read-back verify. The macro re-reads the created item from the pack before reporting success. Claude should still confirm with a separate read after the gate approves; a gated eval's own return value is never proof that a write landed.
- One item per approval. Forge items one gated eval at a time; long batch loops inside a single eval will hit the relay timeout.
Tested on Pathfinder 1e. The macro itself is system-agnostic: it validates against your world's own item types and leaves system-correct item data to Claude.
The Item Forge's combat-ready sibling: a paste-in world macro that turns the bridge into a monster factory. Describe a foe to Claude in the chat box ("an orc werewolf that fights with a chain", "something slow and dreadful for a swamp") and Claude clones the nearest real creature from your bestiary compendiums - or designs one from parts when nothing matches - then files the finished actor through the gated eval, sheet and hostile-ready prototype token included. The Approve/Deny card stays the safety boundary.
Source: plugins/ClaudeFoeForge.js
- In Foundry, create a new Script macro named exactly
Claude Foe Forge. - Paste the contents of
plugins/ClaudeFoeForge.jsinto it and save. - Click the macro once. The bare run seeds the config journal below and shows a source/routing diagnostic. Bare runs never write actors.
A journal named "Claude Foe Forge Config" holds the destination and every compendium Claude may pull from (bestiaries, universal monster rules, monster abilities, templates, racial HD, feats, races). Edit it like any journal text page:
Destination:
Insert Destination Compendium here <- replace with a compendium id, or
"world.actors" for the Actors sidebar
User Extra Content:
system.content.stuff //Example Label <- format guide (always ignored); add
your own "pack.id //Label" lines under it
The shipped source list is Pathfinder 1e (pf1-bestiary + pf-content); swap the pack ids for your system's content and the macro follows the journal, not the code.
One gated eval per foe, with declared intent "write":
return await game.macros.getName("Claude Foe Forge").execute({
actorData: { name: "...", type: "npc", img: "...", system: { ... },
items: [ ... ], prototypeToken: { ... } }
});Returns { ok, uuid, name, destination, folder, items, warnings } on success, { ok:false, error, ... } on refusal. { action: "config" } returns the parsed source list, so Claude always knows what it may search.
- Duplicate refusal. A same-name actor at the destination refuses the forge unless
allowDuplicate: trueis passed, so a retried request can never double-create. - Mandatory icon coverage. A payload with a missing/default portrait or icon-less items is refused before anything is created - no art-less monsters to repair by hand (
allowIconless: trueis the deliberate override). - Hostile token defaults. Disposition, name/bar visibility, and an HP bar are filled wherever the payload left gaps; anything Claude (or a bestiary clone) supplies wins.
- Read-back verify. The macro re-reads the created actor before reporting success. Claude should still confirm with a separate read after the gate approves; a gated eval's own return value is never proof that a write landed.
- One foe per approval. Forge foes one gated eval at a time; long batch loops inside a single eval will hit the relay timeout.
Tested on Pathfinder 1e (straight clones, template applications, and vague open-ended briefs). The macro itself is system-agnostic: it validates against your world's own actor types and follows whatever source list your config journal carries.
The repair bay of the plug-in family: a paste-in world macro that fixes broken compendium links after a world migration, server transfer, or re-import. Tell Claude "repair the linkages" and it scans your packs for dead UUID references, works out which live compendium each dead pack became, proves the mapping with a dry run you approve, and only then rewrites the links. Built on the proven name-match core of two battle-tested repair macros, generalized to find links anywhere in a document - class associations, racial traits, description text, journal pages.
Source: plugins/ClaudeLinkReForge.js
- In Foundry, create a new Script macro named exactly
Claude Link ReForge. - Paste the contents of
plugins/ClaudeLinkReForge.jsinto it and save. - Click the macro once. The bare run shows a diagnostic (version, scan scope, receipt state). Bare runs never write.
scan -> every broken reference, grouped by dead pack id (read-only)
dryrun -> resolves them against a proposed mapping, reports
per-pair match rates, stores a receipt (approving = approving the MAPPING)
apply -> executes exactly the receipted dry run (approving = approving the WRITES)
Resolution is by preserved _id first, then by name (exact, then cleaned of suffixes like "(Ex)" or "at 5th level"). A low match rate means a wrong pack - the report says so, and apply should not follow.
One op per gated eval:
return await game.macros.getName("Claude Link ReForge").execute({ action: "scan" });
return await game.macros.getName("Claude Link ReForge").execute({
action: "dryrun", mapping: { "world.dead-pack": "world.live-pack" } });
return await game.macros.getName("Claude Link ReForge").execute({
action: "apply", mapping: { ... }, confirmHash: "lr-..." });Each op returns { ok, ... } with its report, or { ok:false, error } on refusal. Scan defaults to world compendiums; pass packs: [...] to include module-hosted ones.
- No dry run, no apply. Ever.
applyrefuses unless its job hashes to theconfirmHashof a dry-run receipt less than 30 minutes old. Change the mapping, the scan scope, or the skip rule and the hash breaks. - Keep and report, never guess. Unresolved links, malformed entries, and Bonus Feat associations are left untouched and listed for manual repair.
- Leaf-only rewrite. Only the UUID string itself changes; names, levels, and any custom fields on a link survive untouched. Provenance metadata (
_stats,flags) is never walked. - Time-boxed and re-runnable. Long repairs stop safely inside the gate's window; a re-run picks up where it left off, and already-fixed links drop out on their own.
- Verify after. Claude confirms with a fresh scan after the gate approves; a gated eval's own return value is never proof that a write landed.
Tested on Pathfinder 1e (an 89-link class/features relink after a full compendium migration). The macro itself is system-agnostic: it repairs whatever UUID references your documents actually carry, regardless of system schema.
A quirk worth knowing. Because Link ReForge walks whole documents rather than a single link field, it will sometimes find and repair flaws you didn't know a compendium had - stale references left behind by older migrations or hand edits, not just the breakage you pointed it at. It never does so uninvited: every repair shows up in the dry-run report first, and nothing is written without the gate's permission.
Character insurance for the whole table: a paste-in world macro that snapshots entire actors into self-contained "Backup Seed" items and rebuilds them later. Say "Claude, back up all PCs and NPC Bob" at the start of a session and every one of them gets a seed - stats, race, class levels, features, gear, images, biography. When a sheet gets corrupted, a migration goes sideways, or a player experiments a little too hard, the character comes back from the seed. Ported from a battle-tested backup macro and its embedded restore script, restore order and timing quirks intact.
Source: plugins/ClaudeTotalActorBackup.js
- In Foundry, create a new Script macro named exactly
Claude Total Actor Backup. - Paste the contents of
plugins/ClaudeTotalActorBackup.jsinto it and save. - Click the macro once. The bare run creates the storage folder below and shows a diagnostic. Bare runs never write seeds.
A "Claude Actor Backups" folder in the Items tab. Seeds are ordinary world items with timestamped names, so generations accumulate and the GM can inspect, rename, or hand one out without unlocking anything. prune keeps the newest N per actor and only ever touches this folder - seeds you park elsewhere are never deleted by the plug-in.
One op per gated eval:
return await game.macros.getName("Claude Total Actor Backup").execute({
action: "backup", actors: ["Syb", "Danger Dan"] });
...execute({ action: "list", actor: "Syb" });
...execute({ action: "restore", seed: "<uuid or seed name>", target: "<actor>" });
...execute({ action: "prune", actor: "Syb", keep: 5 });Each op returns { ok, ... } with its report, or { ok:false, error } on refusal.
Every seed carries its own on-use Restore Actor script. Drop a copy into an actor's inventory and use it: a confirmation dialog spells out the wipe, then the actor is rebuilt as the snapshotted character and the used copy consumes itself. The GM or the actor's owner may trigger it, so a busy GM can toss a seed to a player and keep running the game. The folder originals are the archive: Claude's restore op never consumes a seed, while handed-out copies are one-shot.
- Type match is absolute. A seed only restores onto an actor of its own type. There is no override flag, on either path.
- Blank targets only.
restorerefuses a target that has any items unlessallowNonEmpty:trueis passed, which wipes it first and is declared destructive (double confirm). The manual path's confirmation dialog is the same consent, human-shaped. - Definition, not state. Current HP, damage, conditions, and active effects are not captured - a seed is the character, not the moment. Restored characters come back at full HP.
- Spells are excluded (they restore as feats - a PF1e known issue). The backup report counts what was left out.
- Backup never deletes anything. Only the explicit
pruneop removes old seeds. - Verify after. Claude confirms with a separate read after the gate approves; a gated eval's own return value is never proof that a write landed.
Fair warning: more than any other part of AAGM, this plug-in is built exclusively for Pathfinder 1e. The restore sequence leans on PF1e's class/race child generation (add the parents, wait for the system to settle, strip its auto-generated defaults, then restore the character's real features), the spell exclusion is a PF1e workaround, and the manual mode rides PF1e item script calls. On other systems, expect nothing.
Want to build your own plug-in? All four plug-ins follow one contract - a world macro as the endpoint behind the gated eval. The bare essentials are in Docs/PLUGIN_API_ALPHA.md.
MIT - see LICENSE. Fork it, build on it, ship it; keep the copyright notice so the original stays credited.
Not affiliated with, endorsed by, or sponsored by Paizo Inc., Foundry Gaming LLC, or The Forge. Pathfinder is a trademark of Paizo Inc.; Foundry VTT and The Forge are trademarks of their respective owners. This repository ships no Paizo content - the PF1e integration talks to the community pf1 game system's API and your own world's data.

