Local vs remote MCP servers: how each connects, step by step
Description and use
The author examined how local and remote MCP servers establish connections and handle authentication, detailing the lifecycle of a local server, the OAuth flow with PKCE, and token storage.
Problem
Understanding the differences in how local and remote MCP servers establish connections and handle authentication for AI tool calls.
Outcome
The author confirmed that local MCP servers run as child processes, reuse across calls, and terminate with the session, sourcing credentials from environment variables. Remote MCP servers use OAuth with PKCE, store tokens locally, and validate tokens per spec, with opaque tokens for the tested services.
Workflow
- Start Claude Code session
- Claude launches local MCP server as child process
- Reuse server for subsequent tool calls
- Stop server when session ends
- Observe server termination after crash
- For remote servers: send request without token → 401
- Read www‑authenticate header → sign‑in server URL
- Fetch sign‑in server public file → login and token URLs
- Claude identifies itself with client ID URL
- Generate PKCE verifier and challenge
- Open browser to sign‑in server login page with PKCE challenge
- User logs in, receives one‑time code via callback
- Claude exchanges code + verifier for access and refresh tokens
- Store tokens in ~/.claude/.credentials.json
- Use access token on every subsequent request
- Refresh token when access token expires
- Revoke tokens via sign‑in server revocation endpoint
Evidence
A session that called the tool twice started the server once.
Claude stops the server before exiting itself.
The spec says local (stdio) servers should take credentials from the environment instead of using OAuth.
Claude sends the code plus the verifier from step 5. The sign‑in server hashes the verifier and compares it with the challenge from step 6.
On my machine they're in ~/.claude/.credentials.json, an owner‑only file, with fields for each server: accessToken, refreshToken, expiresAt, clientId, issuer.
- Record date: 2026-10-05T21:40:50.920Z