MCP Servers
Model Context Protocol servers extend an agent session with tools it does not have natively. Configuring one is mostly unremarkable — a transport, a URL, some headers. The part that is not unremarkable is what happens when the credential in those headers has an expiry shorter than the session.
A static credential is resolved once and then held
Section titled “A static credential is resolved once and then held”This page describes Claude Code’s MCP client. Other clients read the same protocol but need not treat configuration the same way, and the behaviour below is the client’s, not the protocol’s.
A ${VAR} interpolation in a server’s configuration is expanded once and then held for the life of the session. This is observed rather than documented: a credential minted with a one-hour expiry worked, went stale mid-session, and every later call failed until the client was restarted. Nothing in the client’s documentation states the timing; what follows is inferred from that behaviour.
For a long-lived personal token that is fine. For a credential minted with a one-hour expiry — a GitHub App installation token, for instance — it is not: the value goes stale mid-session, every subsequent call fails, and recovering means restarting the client. On a shared, long-running connection that is the wrong failure to design in.
headersHelper runs fresh on every connection
Section titled “headersHelper runs fresh on every connection”headersHelper takes a script path rather than a value. The client runs that script on every connection — at session start and again on every reconnect — and does not cache the result. The script’s job is to produce the header value, which for a short-lived credential means minting a fresh one each time.
{ "type": "http", "url": "https://example.invalid/mcp/", "headersHelper": "<path>/auth-header.sh"}The expiry becomes invisible to the session. Any reconnect gets a new credential; nothing has to notice that the old one aged out.
The client’s own documentation is explicit about both halves, under Use dynamic headers for custom authentication: Claude Code “runs the helper fresh on each connection, at session start and on reconnect,” and “doesn’t cache the result, so your script is responsible for any token reuse” (Claude Code MCP documentation, read 2026-09-09).
This is not the OAuth refresh mechanism
Section titled “This is not the OAuth refresh mechanism”A 401 is handled for both, by different means, and the two are easy to collapse into one. For an OAuth session the client refreshes its stored token. For a headersHelper server the same documentation says that on a 401 Unauthorized or 403 Forbidden Claude Code “automatically re-runs the helper… reconnects with the fresh headers, and retries the call once.” So a helper-backed server is not unprotected against a mid-call expiry — it gets one retry with a freshly minted credential. What it does not get is a refreshed OAuth session, because there is no session to refresh.
Two adjacent mechanisms solve what looks like one problem, and reaching for the wrong one produces no error worth reading. The symptom is that the server works and then, after about an hour, stops — which sends the reader looking at the server, the network, or the endpoint rather than at the credential’s lifetime.
Keep the distinction: native OAuth refresh for OAuth sessions the client holds; headersHelper for anything you mint yourself.
Verifying an endpoint accepts a credential type
Section titled “Verifying an endpoint accepts a credential type”Vendor documentation enumerates what a service supports. It does not always enumerate everything a service accepts, and the difference matters when the type you need is one it does not mention.
The github/github-mcp-server README describes two ways to reach the remote endpoint — a one-click OAuth login and a personal access token in an Authorization header — and says nothing there about App installation tokens, which it covers only for the local server’s non-interactive deployment. Absent rather than refused, in a document that discusses the method elsewhere. That gap was closed by minting a token and sending one initialize handshake, which returned a valid response. One request settles what no amount of reading the page can.
Do that rather than inferring from the omission in either direction, and see Reading Evidence in a Codebase for why an absence is not a decision.
Status in this workspace
Section titled “Status in this workspace”The mechanism above is verified; the shared configuration is not yet in place. The handshake test was run against the hosted endpoint and succeeded. Adopting it for the workspace’s own GitHub MCP server is a separate change, deliberately held, and this page will say so until it lands.
Sources
Section titled “Sources”Verified 2026-09-09 against the hosted GitHub MCP endpoint.
Sourced: headersHelper’s call-on-every-connection behaviour and its 401 retry, from the Claude Code MCP documentation; the remote endpoint’s documented authentication methods, from the github/github-mcp-server README. Both read 2026-09-09 and dated because a vendor page is a record that moves.
Observed, not sourced: that a ${VAR} interpolation is expanded once and held. No document consulted states it; it is inferred from a one-hour credential going stale mid-session and recovering only on a client restart. The submission is /workbooks/notebooks/domain-ontology/streams/harry-tuttle/handback-shared-github-mcp-server.md. The App and endpoint identifiers, and the wrapper script, stay with that record rather than appearing here.
Copyright: © Arda Systems 2025-2026, All rights reserved