Skip to content

ADR-071: Profile embedding across agent contexts via Sync MCP

Status: Accepted Date: 2026-05-23 Context: The Sync judgment profile (stored in player_decision_profiles) was being read by exactly one client: Hermes, via the SKILL.md + MCP server combination. Every other context where CoachJ does decision work — Claude.ai conversations, Claude Code sessions in this and other repos, web tools, future agents — was operating without his profile loaded. This was discovered when a design conversation in Claude Code revealed that recommendations would have changed substantially if the profile had been available (overconfidence flag, political attention, eliminative reasoning style, authenticity gap in values-culture contexts).

The MCP server at /api/agent/v1/mcp already exists (built with the agent-connector brief, ADR-039 adjacent). The gap was: tool naming used dot-notation (sync.get_profile) which Claude.ai rejects (pattern ^[a-zA-Z0-9_-]{1,64}$), the SKILL.md was being managed per-Hermes-install rather than as a cross-client artifact, and no convention existed for how to load the profile in non-Hermes contexts.

At a glance

What it decides: The Sync MCP server is the one canonical way to load a user's judgment profile into any agent context — Hermes, Claude.ai, Claude Code, future tools — not just the dedicated Hermes surface.

  • One endpoint, any clienthttps://pulse.beaconinfra.xyz/api/agent/v1/mcp over JSON-RPC, Bearer-authed with a sak_ key; already deployed, no per-client build.
  • Tool names use underscores, not dotssync_get_profile etc., a breaking change forced by Claude.ai's validation pattern (Hermes installs on SKILL.md ≤ v3.4.0 must reinstall v3.5.0).
  • Static paste rejected as the default — pasting personal_context freezes the profile at paste-time and loses signal as games accrue; MCP fetches live. Read-path work does not gate on ADR-039's write path.
  • Watch: Claude Code has no MCP registration yet, so it uses a refreshable memory snapshot (user_coachj_sync_profile.md); per-conversation staleness and cross-project profile scoping remain open.
flowchart LR
    P[("player_decision_profiles")] --> M["Sync MCP server<br/>/api/agent/v1/mcp<br/>sync_get_profile · sync_get_recent_decisions"]
    K["sak_ API key (Bearer)"] -.-> M
    M --> H["Hermes"]
    M --> C["Claude.ai Projects"]
    M --> X["Claude Code & future agents"]

One MCP primitive fans the live profile out to every agent surface, authenticated by a single (or per-client) SAK key.

Decision

The Sync MCP server is the canonical primitive for profile embedding across any agent context. Three concrete components:

  1. MCP server endpoint: https://pulse.beaconinfra.xyz/api/agent/v1/mcp — JSON-RPC over HTTP POST, Bearer auth via sak_ API key. Already deployed; no client-specific build.

  2. Tool naming: Underscores, not dots. Renamed sync.get_profilesync_get_profile, sync.get_recent_decisionssync_get_recent_decisions, sync.submit_feedbacksync_submit_feedback. Required for Claude.ai compatibility and is friendlier across all MCP clients regardless of their specific validation rules.

  3. Multi-client support via SAK keys: A single sak_ API key can authenticate calls from any number of agent clients (Hermes, Claude.ai Projects, future tools). Users can also mint per-client keys with descriptive names for separate revocation and per-client last_used_at tracking. Both patterns are supported; users can pick based on their key-management preferences.

Fallback for clients without MCP support: Paste the personal_context field plus the agent_context_block directly into custom instructions. Trade-off: the profile freezes at paste-time and doesn't update as games are played. Use this only when MCP isn't available (no current client requires it).

For Claude Code sessions specifically: Until the Sync MCP server is registered in Claude Code's MCP config, save a refreshable profile snapshot to memory (user_coachj_sync_profile.md). Refresh trigger: significant change in games_analyzed. Direct DB query path is documented in the memory file for ~30-second refresh.

Rationale

Why MCP over alternatives:

  • Static custom instructions paste: Works for any client but freezes the profile. CoachJ is at 83 games and counting — the profile is actively growing and stale snapshots would lose signal value monthly.
  • Per-client API integrations: Doesn't scale. Every new agent platform (Lindy, custom GPTs, future tools) would need its own bespoke integration.
  • REST API direct calls in client prompts: This was the v1 SKILL.md approach (raw curl examples) and failed in production — security scanners flagged the skill as DANGEROUS and tool-use models treated curl examples as flavor-text rather than callable tools. MCP first-class tools are the proven fix (see CHANGELOG entry for SKILL.md v2.0.0).

Why tool-name sanitization is a decision worth ADR'ing: The dot-to-underscore rename is a breaking change for any existing Hermes installs using the old SKILL.md (v3.4.0 and earlier). Worth documenting that the change was forced by a real client constraint, not arbitrary preference — so future contributors understand why the inconsistent-looking naming exists.

Why multi-client support via shared keys works: The agent_api_keys table has no per-user uniqueness constraint on the key — the server only validates that the presented key is unrevoked and looks up the associated user_id. This means one key works across N clients with no server-side change. The name column was added during initial implementation precisely so users can label keys by client when they want separate keys.

Alternatives Considered

  • Build a different protocol or wrapper (gRPC, custom JSON, etc.) instead of MCP: Rejected. MCP is the emerging standard for agent tool access; rolling our own would require client-side adapter work that MCP-native clients (Claude.ai, Claude Code, Cursor) get for free.

  • Require one key per client (no shared-key support): Rejected. The current schema supports multi-key trivially; forcing the per-client pattern only on UX is friction without benefit. Users who want separate keys can mint them.

  • Mandate static custom-instructions paste for non-Hermes clients (no MCP): Rejected. Defeats the whole "agent that knows current me" thesis if profiles freeze at paste-time.

  • Wait until ADR-039 (decision capture primitive) ships before broader profile embedding: Rejected. ADR-039 is about capturing decisions from non-game contexts (write path). This ADR is about reading the existing profile from non-Hermes contexts (read path). They're independent — the read-path work doesn't gate on the write-path work.

Discussion

The gap this ADR closes was invisible until a design conversation in Claude Code revealed how much the profile would have changed recommendations. Until that moment, the assumption had been "profile lives in Hermes, that's where it gets used." The realization: any context where CoachJ is doing decision-style work could benefit from the profile, not just the dedicated decision-advice surface of Hermes. This generalizes the original framing of the Sync judgment layer.

The MCP server already existed (built with the agent-connector brief), so the work to enable this was almost entirely (a) naming-sanitization to unblock Claude.ai and (b) documentation of the install path. The actual capability was present months ago — what was missing was the conceptual frame that says "the MCP server isn't just for Hermes; it's for any agent surface."

This conversation also surfaced an interesting interaction with ADR-039: if profile reads are now first-class across contexts, the bar for profile writes (capturing decisions from non-game contexts) rises proportionally. A profile that informs more surfaces deserves more capture surfaces. ADR-039 stays Proposed; this ADR doesn't change its priority but does sharpen its value.

Outstanding profile-embedding questions deferred to future work:

  • Adaptive response depth in SKILL.md: Whether the agent should infer exploratory-vs-decisive context and adjust response depth, or whether the user should set it explicitly. Open question (audit item 72), exploration prompt drafted but not committed.
  • Per-conversation profile staleness handling: MCP fetches the live profile per call. But within a long Claude.ai conversation, the profile is read once at start. Future question: should re-reads happen on conversation milestones? Open.
  • Cross-project profile awareness: CoachJ runs multiple projects (Mystery Maker, Cold Case Files). Whether a project-tagged profile context is useful is open (memory: user_parallel_projects.md).

Consequences

  • Breaking change for Hermes: Existing Hermes installs using SKILL.md v3.4.0 or earlier will get "unknown tool" errors when the server rejects dotted tool names. SKILL.md v3.5.0 needs to be reinstalled. CHANGELOG entry communicates this.
  • Documentation surface: /docs/agent-skill page on the marketing site and docs/orchestrator/setup.md both updated to reflect new tool names.
  • Memory hygiene: Future Claude Code sessions should refresh user_coachj_sync_profile.md when CoachJ's games_analyzed has grown materially. Query is in the memory file; takes ~30 seconds.
  • First-class read surface: The agent_context_block is now the canonical way for any agent to embody CoachJ's reasoning, not just Hermes. Other Beacon team members can follow the same pattern (mint a SAK key, register MCP server in their preferred client).
  • Adjacent work that this unblocks:
  • The autonomous-twin product vision (audit item 79) becomes architecturally feasible — a profile-loaded agent in Claude Code can read the codebase, ADRs, and current product state, and reason as CoachJ would about what to work on next.
  • Cross-project memory (memory: user_parallel_projects.md) becomes more interesting — if any project's Claude session has profile-aware reasoning, the question of which projects share which profile context gets sharper.

Key files: - src/lib/agent/mcp.ts — MCP server implementation; tool names normalized to underscores - public/agent-skill/SKILL.md v3.5.0 — canonical skill manifest with updated tool names - src/app/docs/agent-skill/page.tsx — public install guide - docs/orchestrator/setup.md — orchestrator setup guide - memory/user_coachj_sync_profile.md — refreshable profile snapshot for Claude Code sessions - memory/reference_coachj_skill_md.md — pointers to where CoachJ's specific skill version lives - docs/research/audits/conversation-decisions-audit.md — source item 73 + MCP discovery from items 64