Music and Madness Sync Bridge
This project implements Stage 1 of your plan:
- FoundryVTT -> Notion Story Bible journal mirroring
- Manual-run sync commands
- MCP tool surface for Codex/LLM orchestration
- Full media copy to durable local storage
- Conflict tracking with manual resolution
- Non-destructive write scope lock to
Music and Madness Story Bible
Project layout
/Users/nicholasmcdowell/Documents/Codex Projects/Music and Madness/music-madness-sync-bridge/src- Bridge service, CLI, MCP server, Foundry/Notion adapters.
/Users/nicholasmcdowell/Documents/Codex Projects/Music and Madness/music-madness-sync-bridge/foundry-module/music-madness-bridge- Foundry module scaffold exposing authenticated journal read events.
/Users/nicholasmcdowell/Documents/Codex Projects/Music and Madness/music-madness-sync-bridge/data- Runtime state (
state/bridge.sqlite), media copies (media/), logs (logs/audit.log).
- Runtime state (
Definitions
- MCP: Model Context Protocol. Lets Codex call bridge tools like
sync.foundry_to_notion.apply. - Mirror block: Generated section under
## Foundry Mirrorin a Notion page. - Conflict: Source and target changed since last sync, requiring manual decision.
Setup
- Copy env template.
cd '/Users/nicholasmcdowell/Documents/Codex Projects/Music and Madness/music-madness-sync-bridge'
cp .env.example .env
- Fill
.envvalues:
- Foundry URL/tokens
- Notion key
- Story Bible scope database IDs
- Typecheck.
npm run typecheck
Operator commands
Preview changes (no writes):
npm run sync:preview
Apply mirror updates:
npm run sync:apply -- --include-media
List conflicts:
npm run sync:conflicts
Resolve one conflict:
npm run sync:resolve -- --conflict-id <id> --resolution manual_merge --notes "Reviewed and merged"
Health check:
tsx src/cli.ts health
MCP server
Run stdio MCP server:
npm run mcp:start
Exposed tools:
foundry.health_checkfoundry.list_journalsfoundry.get_journalfoundry.export_journal_mediasync.foundry_to_notion.previewsync.foundry_to_notion.applysync.diffsync.conflicts.listsync.conflicts.resolve
Guardrails enforced
- Only writes to Notion pages in
NOTION_ALLOWED_DATABASE_IDS. - Existing prose preserved by appending/updating generated mirror block.
- Legacy
Music and Madnesssection is untouched by default because database scope is explicit.
Known limitations in this initial implementation
- Foundry HTTP routes are expected at
/health,/journals,/journals/:id, etc. - The Foundry module included here is an authenticated socket/event scaffold. If you need direct HTTP routes from Foundry itself, run a small local proxy (or use an API module) that maps those routes to module events.
- Media is copied to local durable files. To render media directly inside cloud Notion reliably, set
MEDIA_PUBLIC_BASE_URLto a reachable static host.
Local test bridge API (optional)
If your Foundry module routes are not live yet, run the mock API that implements:
GET /healthGET /journalsGET /journals/:idGET /journals/:id/mediaGET /assets/:assetIdGET /changes?since=
Command:
npm run foundry:mock-api
Fixture data file:
/Users/nicholasmcdowell/Documents/Codex Projects/Music and Madness/music-madness-sync-bridge/fixtures/foundry-fixture.json
Real Foundry proxy (Socket.IO -> HTTP routes)
Use this when your Foundry world and music-madness-bridge module are running.
Why this exists
Your sync client expects HTTP routes (/journals, /health, etc.).
The Foundry module communicates via socket events in-world.
This proxy translates HTTP calls to socket event calls.
Start order
- Start Foundry and open the target world.
- Ensure module
music-madness-bridgeis enabled. - In Foundry world settings, set
Bridge Tokento the same value as.envFOUNDRY_BRIDGE_TOKEN. - Populate in
.env:FOUNDRY_SITE_URLFOUNDRY_WORLDFOUNDRY_SESSION_COOKIEFOUNDRY_BRIDGE_TOKEN
- Start proxy:
npm run foundry:proxy
- In another terminal, run bridge operations:
npm run sync:preview
npm run sync:apply -- --include-media
Session cookie note
FOUNDRY_SESSION_COOKIE is the value of the session cookie from a browser already logged into your Foundry world.
Treat it like a secret token.
Current limitation
GET /assets/:assetId returns 501 in socket-proxy mode unless your module adds binary asset streaming.
Media still syncs from journal-linked sourceUrl values where accessible.
Remote Foundry host (LAN)
If Foundry runs on another machine on your network, set:
FOUNDRY_SITE_URL=http://192.168.1.105:30000/
Keep the sync client pointed at your local proxy:
FOUNDRY_BASE_URL=http://127.0.0.1:8788
So the flow is:
- this laptop sync client -> local proxy (
127.0.0.1:8788) - local proxy -> remote Foundry (
192.168.1.105:30000)
You still need:
FOUNDRY_WORLDfor that remote Foundry worldFOUNDRY_SESSION_COOKIEfrom a browser logged into that remote Foundry- matching
FOUNDRY_BRIDGE_TOKENin Foundry module world settings
Remote diagnostic command
Run this before sync to verify remote Foundry + proxy health:
npm run health:remote
What it checks:
FOUNDRY_SITE_URLHTTP reachability- local proxy
/healthresponse and socket connection state - local proxy
/journalsroute response
Exit code is non-zero if any check fails.
Verbose diagnostics with remediation hints:
npm run health:remote -- --verbose
Doctor command
Validate your .env before running proxy or sync:
npm run doctor
What it validates:
- required env variables are present
- URL formats (
FOUNDRY_BASE_URL,FOUNDRY_SITE_URL) - Notion DB scope consistency (
NOTION_DEFAULT_TARGET_DB_IDis inNOTION_ALLOWED_DATABASE_IDS) - proxy port sanity
- basic shape checks for world slug and session cookie
Exit code is non-zero when required checks fail.
Remote module install (manifest URL)
For one-click Foundry install/update via URL, use the release workflow guide:
/Users/nicholasmcdowell/Documents/Codex Projects/Music and Madness/music-madness-sync-bridge/docs/foundry_remote_install.md
Quick command (example):
MODULE_VERSION=0.1.0 MODULE_URL=https://github.com/<you>/<repo> MODULE_MANIFEST_URL=https://raw.githubusercontent.com/<you>/<repo>/main/foundry-module/music-madness-bridge/module.json MODULE_DOWNLOAD_URL=https://github.com/<you>/<repo>/releases/download/v0.1.0/music-madness-bridge-v0.1.0.zip node scripts/release/prepare-foundry-module.mjs