Synchronize and manage FoundryVTT journal entries within a Notion Story Bible.
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 writes under
Music and Madness Story Biblewith a top-levelJournalswiki tree
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. - Journals wiki tree: A top-level Story Bible child page named
Journals, with sub-pages matching Foundry folders. - 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 root page ID (
NOTION_STORY_BIBLE_PAGE_ID)
- Typecheck.
npm run typecheck
Operator commands
Preview changes (no writes):
npm run sync:preview
sync:preview auto-starts the local proxy when FOUNDRY_BASE_URL is local (127.0.0.1 / localhost / ::1) and FOUNDRY_AUTO_PROXY=1.
Preview/write targets are wiki pages under Journals, not Notion databases.
Apply mirror updates:
npm run sync:apply
sync:apply has the same auto-proxy behavior and stops the proxy afterward only if this command started it.
When creating a page, it mirrors Foundry folder structure under the Journals wiki tree.
Media download/linking is enabled by default; disable with --no-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
Session cookie note
`FOUNDRY_S
Tools (9)
foundry.health_checkChecks the health status of the FoundryVTT connection.foundry.list_journalsLists available journal entries from FoundryVTT.foundry.get_journalRetrieves a specific journal entry by ID.foundry.export_journal_mediaExports media assets associated with a journal entry.sync.foundry_to_notion.previewPreviews changes between FoundryVTT and Notion without applying them.sync.foundry_to_notion.applyApplies mirror updates from FoundryVTT to the Notion Story Bible.sync.diffShows the differences between source and target.sync.conflicts.listLists current synchronization conflicts.sync.conflicts.resolveResolves a specific synchronization conflict.Environment Variables
FOUNDRY_BASE_URLrequiredThe base URL for the FoundryVTT instance.NOTION_API_KEYrequiredThe API key for Notion integration.NOTION_STORY_BIBLE_PAGE_IDrequiredThe root page ID for the Story Bible in Notion.FOUNDRY_BRIDGE_TOKENrequiredAuthentication token for the bridge module.Configuration
{"mcpServers": {"music-madness-sync": {"command": "node", "args": ["/path/to/music-madness-sync-bridge/dist/index.js"], "env": {"FOUNDRY_BASE_URL": "...", "NOTION_API_KEY": "...", "NOTION_STORY_BIBLE_PAGE_ID": "..."}}}}