PF2e Party Read-Only MCP Companion — Full Implementation Plan

Goal

Enable party members to use Claude/Desktop (or other MCP-capable clients) as a read-only campaign companion for Age of Ashes notes.


Success Criteria

  • Party members can query campaign notes from their own clients.
  • Shared scope is restricted to player-safe notes only.
  • No write/delete capability from external clients.
  • Access is authenticated and encrypted.
  • Operations are easy to maintain in homelab.

Main Vault (private)
  /srv/docs/homelab-vault/pathfinder
          |
          | (sync/transform script, allowlist only)
          v
Shared Read-Only Dataset
  /srv/docs/homelab-vault/pathfinder_party_share
          |
          | (MCP server, read-only tools)
          v
Reverse Proxy + Auth + TLS
  (Caddy/Nginx/Cloudflare Tunnel)
          |
          v
Party clients (Claude Desktop, etc.)

Directory Model

Source (existing)

  • /srv/docs/homelab-vault/pathfinder

New shared dataset

  • /srv/docs/homelab-vault/pathfinder_party_share

Suggested structure in share

  • campaign/
    • 01_Sessions/CATCH UP.md
    • 01_Sessions/RECAP.md
    • selected session notes
  • characters/
    • character sheets, tactics cards, party-at-a-glance
  • denizens/
    • curated denizen files
  • rules/
    • player-safe quickrefs only
  • index/
    • README.md, What_to_Read_First.md

Explicitly exclude

  • Personal/private notes
  • GM-only spoilers
  • Secrets/twist notes
  • Anything under admin/internal operational paths

Sharing Policy

Implement allowlist sync, not denylist.

Allowlist examples

  • pathfinder/01_Campaigns/Age of Ashes/01_Sessions/CATCH UP.md
  • pathfinder/01_Campaigns/Age of Ashes/01_Sessions/RECAP.md
  • pathfinder/01_Campaigns/Age of Ashes/01_Sessions/* - AoA Session.md (optional)
  • pathfinder/01_Campaigns/Age of Ashes/Discord/exports/md/aoa-denizens/*.md
  • pathfinder/02_Characters/*/Character_Sheet.md
  • pathfinder/02_Characters/*/Tactics_Card.md
  • pathfinder/02_Characters/Party_At_A_Glance.md

Create policy file:

  • /srv/docs/homelab-vault/pathfinder_party_share/.share-policy.yaml

Sync Pipeline

Create script:

  • /home/zack/.openclaw/workspace/scripts/pf2e-share/sync_party_share.sh

Behavior:

  1. Build temp dir.
  2. Copy allowlisted files.
  3. Strip prohibited frontmatter/sections if needed.
  4. Write generated index pages.
  5. Atomic swap into pathfinder_party_share.
  6. Fix permissions read-only.

Permissions

  • owner root/service account
  • group read as needed
  • files 0644, dirs 0755
  • MCP service user has read-only access

MCP Server Choice

Option A (preferred for safety): Filesystem MCP server

  • Expose read/list/search tools only.
  • Point root to pathfinder_party_share.
  • No write/delete tools compiled/enabled.

Option B: Obsidian MCP server (cyanheads)

  • Use only if tool-level disable for write/delete is enforceable.
  • Still keep source as pathfinder_party_share, not main vault.

Service Deployment

Use systemd service (or Docker). Example systemd unit:

/etc/systemd/system/pf2e-mcp.service

[Unit]
Description=PF2e Party MCP (Read-only)
After=network.target
 
[Service]
User=pf2e-mcp
Group=pf2e-mcp
WorkingDirectory=/opt/pf2e-mcp
Environment=NODE_ENV=production
Environment=MCP_ROOT=/srv/docs/homelab-vault/pathfinder_party_share
ExecStart=/usr/bin/node /opt/pf2e-mcp/server.js
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadOnlyPaths=/srv/docs/homelab-vault/pathfinder_party_share
 
[Install]
WantedBy=multi-user.target

Secure Exposure

Reverse proxy requirements

  • HTTPS only
  • Auth required (per-user credentials/token)
  • Request logging + rate limiting
  • IP restriction optional (if party static IPs known)

Caddy example (concept)

pf2e-mcp.example.com {
  encode zstd gzip
  basicauth {
    user1 <hash>
    user2 <hash>
  }
 
  reverse_proxy 127.0.0.1:8787
}

(Use proper identity provider/API tokens if supported by chosen MCP stack.)


Client Onboarding (Party)

Provide one short guide per app:

  • Claude Desktop config snippet
  • Cursor/Windsurf optional
  • “Read-only campaign companion” usage examples:
    • “What happened last session?”
    • “Give me non-spoiler recap of Rova 10th.”
    • “Who are current named NPC allies?”

Document path:

  • /srv/docs/homelab-vault/homelab-docs/ideas/01_P2E_MCP/Party_Onboarding.md

Operational Runbook

Daily/weekly

  • Run share sync after session import pipeline.
  • Verify service healthy.
  • Verify latest CATCH UP/RECAP visible in MCP queries.

Incident response

  • Revoke compromised user credentials.
  • Rotate auth secrets.
  • Temporarily disable endpoint at proxy layer.

Automation Integration

After existing import pipeline completes (run_pipeline.sh), add:

  1. sync_party_share.sh
  2. optional systemctl reload pf2e-mcp (if needed)
  3. smoke test query

Also schedule a cron sync fallback (e.g. Fri morning after import cron).


Phase Plan

Phase 1 — Local prototype (1–2 hours)

  • Build share folder manually.
  • Run local MCP server bound to localhost.
  • Validate read/list/search only.

Phase 2 — Security hardening (1–2 hours)

  • Service account + systemd hardening.
  • Reverse proxy with auth + TLS.
  • Logs and rate limits.

Phase 3 — Party pilot (30–60 min)

  • Onboard 1 trusted player.
  • Validate query quality, no leakage.

Phase 4 — Rollout

  • Add remaining players.
  • Add feedback loop + iteration cadence.

Risks & Mitigations

  1. Leakage of private notes
    • Mitigation: strict allowlist sync + separate shared dataset.
  2. Write/delete abuse
    • Mitigation: read-only toolset + filesystem perms + hardened service.
  3. Auth token compromise
    • Mitigation: per-user credentials, revocation process, rotation.
  4. Availability issues
    • Mitigation: systemd restart policy + health checks + simple architecture.

Open Decisions

  1. Preferred auth model (basic auth vs API keys vs SSO).
  2. Which MCP server implementation to standardize on.
  3. Whether to include full session files or only curated recaps.
  4. Whether denizens should include all threads or curated subset.

Concrete Next Actions

  1. Approve share allowlist.
  2. I draft sync_party_share.sh + .share-policy.yaml.
  3. Deploy read-only MCP locally.
  4. Add reverse proxy + auth.
  5. Pilot with one party member.

Foundry-VTT-MCP Exposure Feasibility (Scoped)

Why this matters

If the GM already runs foundry-vtt-mcp, it can become a live-state data plane for the party app while Obsidian remains the curated narrative/source-of-truth plane.

Value add for the Pathfinder app

  • Pull live actor/combat state (initiative, HP, conditions) for “what’s true right now?” answers.
  • Pull journals/scenes where player-safe, reducing manual recap lag.
  • Cross-check app summaries against current Foundry state to reduce stale guidance.
  • Enable session-time utilities (initiative snapshot cards, status summaries, encounter context).

Security posture (non-negotiable)

Do not expose raw localhost service directly to the public internet.

Minimum safe baseline:

  1. Keep MCP service bound to localhost/private LAN.
  2. Publish only through a reverse proxy with TLS.
  3. Require strong auth at edge (OIDC/Access proxy preferred over static basic auth).
  4. Enforce per-user identity + revocation path.
  5. Add rate limiting + request logging.
  6. Restrict allowed tools to read-only where possible.

Preferred network pattern:

  • Best: private overlay (Tailscale/WireGuard) + no public ingress.
  • Acceptable: Cloudflare Access / Authelia-protected public endpoint.
Foundry VTT (GM server)
  └─ foundry-vtt-mcp (localhost)
       └─ reverse proxy + auth + TLS
            └─ Party app ingestion worker (read-only credentials)
 
Obsidian curated notes
  └─ pathfinder_party_share MCP (read-only)
       └─ same app retrieval layer
 
App retrieval/orchestration
  ├─ Live state queries => Foundry MCP
  ├─ Lore/recap/context => Notes MCP
  └─ Safety filter => player-facing response

Data governance

  • Tag all retrieved content with source=foundry-live or source=notes-curated.
  • Never present GM-only entities unless explicitly marked player-safe.
  • Cache short-lived live state (e.g., 15–60s TTL) to reduce load.
  • Keep write/mutate actions disabled for party-facing flows.

Integration phases for Foundry-VTT-MCP

  1. Recon
    • Enumerate available tools/resources from foundry-vtt-mcp.
    • Classify each as read-safe / sensitive / prohibited.
  2. Secure publish
    • Place behind authenticated TLS endpoint.
    • Confirm audit logging and credential rotation.
  3. Read-only app adapter
    • Build adapter with allowlisted tool calls only.
    • Add schema normalization into app domain objects.
  4. Spoiler safety layer
    • Denylist GM-only compendia/journals/scenes.
    • Add response guard that strips hidden/private fields.
  5. Pilot + validation
    • Run with one trusted player account.
    • Validate no spoiler leaks and acceptable latency.
  6. Rollout
    • Expand access, keep observability and revocation tested.

Go / No-Go gates

Go only if all are true:

  • Authenticated edge in place (no anonymous public access).
  • Read-only scoped credentials validated.
  • Tool allowlist documented and enforced.
  • Spoiler leak test pass.
  • Revocation drill tested.

No-Go if any are false.

Immediate next actions (Foundry track)

  1. Chosen model: Cloudflare Tunnel + Cloudflare Access in front of localhost foundry-vtt-mcp.
  2. Capture exact foundry-vtt-mcp tool list and permission behavior.
  3. Draft tool allowlist for party app adapter.
  4. Run a spoiler-safety red-team pass before player rollout.
  5. Execute 01_P2E_MCP/Phase_1_Checklist_Foundry_Exposure.md.