Skip to content

salon: delete the $-stripping MCP workaround, now that Fountain sends the substituted config #8

Description

@jhgaylor

Fountain shipped the fix this workaround compensates for, so apps/salon/server/sandbox.ts#unescaped can go.

Background

Salon declares its conversation-authenticated MCP server with Fountain's $$ escape (server/agents.ts:82):

headers: { Authorization: "Bearer $${FOUNTAIN_TOKEN}", "X-Fountain-Conversation-Id": "$${FOUNTAIN_CONVERSATION_ID}" }

Fountain wrote that config into the computer twice: substituted into the project .mcp.json ($${X}${X}, which the runtime then expands from its own environment), and raw on the ACP session. The session copy is the one Claude Code uses, so the runtime expanded the inner ${X} and left the leading $ standing, and the header arrived as Bearer $ftn_…. Salon dropped that $ on the way in.

BinaryBourbon/fountain#1404 fixed it: there is one substitution pass and one effective configuration now, and session/new gets the resolved document. The header arrives as Bearer ftn_….

What to change

  1. Delete unescaped()server/sandbox.ts:68.
  2. Drop its two call sitesserver/sandbox.ts:48 (the bearer key) and :50 (the conversation id). Both become the plain trimmed value.
  3. Update apps/salon/CLAUDE.md — the workaround is described at ~109-127 and again at ~374 ("fix there, then delete sandbox.ts#unescaped"). Note that line 127 says mcp.ts#unescaped; there is no unescaped in mcp.ts, so that reference is stale either way.

Do NOT remove the $$ escapes

server/agents.ts:82 should keep "Bearer $${FOUNTAIN_TOKEN}" exactly as it is. That is the correct pattern and the fix depends on it: Fountain's single pass turns $$ into $, leaving a literal ${FOUNTAIN_TOKEN} for the runtime to expand from the sandbox's process environment — which is what keeps a reattached or resumed turn on the current credential instead of one frozen into the stored agent document.

Deleting the escape as well would send an unresolved ${FOUNTAIN_TOKEN} that Fountain has no value for, or an eagerly-resolved token that goes stale. The workaround is wrong now; the escape is not.

Version requirement

The fix is live on the hosted instance (merged as 012d7ac5, deployed in sha-dc8bd31a, 2026-09-03), so this is safe against fountain.inevitable.fyi today.

It is not in a tagged release — v0.16.0 predates it. If any Salon deployment ever points at a pinned self-hosted Fountain at v0.16.0 or earlier, removing unescaped() breaks it until that instance takes the fix. Worth a glance at fountainUrl before merging.

Verifying

The unescaped() removal is safe on its own reading: a Fountain API key is ftn_… and a conversation id is a UUID, so neither legitimately begins with $. The helper only ever compensated for the bug.

Salon is the only app in this repo carrying it — grepped apps/ for unescaped and $${, and the other hits are unrelated (fountain-demos/src/roster.test.ts is about HTML quoting).

Refs: BinaryBourbon/fountain#1404, PR BinaryBourbon/fountain#1511.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions