# Agents - MCP Server Onboarding and Credentials MCP failures hide behind green connection status and silent result-shape mismatches. Exercise the server process directly before and after you wire it into a client. Keep credentials in env entries that a secret runner or the OS keychain resolves, never in command argv. ## Key points ### Onboarding and smoke tests - A server that shows connected but registers zero tools crashed during lifespan setup. The process never reaches tool registration. - Test before you commit the config: run the package directly and read stderr for the crash reason. - `npx @modelcontextprotocol/[email protected]` config-file mode smoke-tests every server with `tools/list` plus read-only `tools/call`. - Scope servers by least privilege: global, project, and user-project scopes exist. A data-warehouse server should not pollute every session's tool namespace. - Chrome DevTools MCP attaches to an existing browser session and keeps cookies and auth state. Playwright MCP launches a fresh headless instance. - For Python servers, `uv tool install -e .` gives an isolated venv and a console script on PATH. Register it with `claude mcp add --scope user <name> -- <console-script>`. - MCP proxy middleware requires the upstream server command as its first positional argument. Zero args exit immediately as a JSON-RPC error. Expensive tool descriptions are the proxying use case. - Version-pin remote wrappers with open auth regressions (`mcp-remote` at `0.1.38`). Prefer official hosted OAuth endpoints over deprecated community servers. ### Credentials and OAuth - Serve credentials in `.mcp.json` as `env` entries with secret-manager references resolved via `op run`. Never use inline substitution. - Debug resolution first: `op read` of the exact reference must succeed, or the server fails on init with a JSON-RPC error. - A bearer token in process argv (an `sh -c exec` line with a `--header` flag) is visible to any process listing. - Keychain pattern: add a generic password once, load it with `-w`, and verify an entry by omitting `-w` and checking the exit code. - One adapter keys OAuth token stores by a hash of the server name. The URL is validated but never part of the key, so same-URL entries with different names get isolated stores. - Slack MCP OAuth differs per host: one client expects `callbackUrl`, another `redirectUri` without an auth field. Two hosts mean two token stores, so one can be authed while the other still prompts. - Check where a built-in client writes tokens before you abandon an adapter. One built-in writes plaintext tokens to disk. - A risk-free OAuth probe: point the OAuth dir at a temp directory and launch from a scratch directory with a project-level MCP config. - Account auth can succeed while every site read returns 403 "app is not installed". An empty accessible-resources list means the token binds no site resources. The fix is a fresh consent grant, not more retries. ### Silent result-shape failures - A Slack payload nests results in `content[].text`. A regex against escaped JSON reads as no results. - On Slack MCP searches, pass `include_context=false`, a concise format, and a small limit. Unrestricted results blow the context window. - Tool discovery needs server scope: a server field in the search, or an explicit connect first. - An adapter that skips re-prefixing tools already starting with the server name makes allowlist entries like `slack_slack_*` dead. Imported servers expose only namespace proxies unless you set `directTools`. - A headless print-mode session (`pi -p`) re-reads the MCP config. Intersect its tool list with the allowlist to verify entries without an interactive session. - When a result overflows to a one-line JSON file, page it: grep with a pattern like `.{1,300}` plus offset/limit. - Tool-schema `pattern` fields must be RE2-compatible: lookaround makes OpenAI's validator reject the whole request, and grammar-constrained backends reject it too. Compile them with Go's `regexp`, which is literally RE2. - A file-reading tool can silently regress to sample rows instead of full data. A CSV export restored the full dataset and removed a duplicated sheet. - Image-search servers can use tag-based matching, not semantic search. Abstract queries match unrelated tagged images, so use concrete noun phrases. ### Sharp edges in the mcp-go library - Tools registered with `WithInputSchema[T]()` carry their schema only in the raw JSON field. The structured fields stay zero-valued, so middleware that grafts onto them sees nothing. Unmarshal raw into structured before injection, then clear the raw field so serializers cannot double-encode. - mcp-go decodes the JSON-RPC request ID into `any` without type validation. An object or array ID becomes a map or slice, which is unhashable, so a sync-map store panics. Recovery middleware covers only tool and resource handlers. The SSE transport runs each message in a goroutine without recover, so one crafted `initialize` stops the process. ## How to apply 1. Run the server package directly and read stderr before you commit the config. 2. Smoke-test every server with `npx @modelcontextprotocol/[email protected]` config-file mode. 3. Serve credentials in `env` entries. Verify each reference first with `op read`. 4. Keep secrets out of argv. On macOS, use the Keychain: `security add-generic-password -a <name> -s <service> -w`. 5. Compile every tool-schema `pattern` with Go's `regexp` before shipping. ## Observations - [failure-mode] a connected server with zero registered tools crashed during lifespan setup #mcp (source: agent sessions, 2026-03) - [practice] secret-manager env entries keep MCP credentials out of process argv #security (source: agent sessions, 2026-08) - [security] lookaround in a tool pattern passed local tests and failed a hosted RE2 validator #tools (source: agent sessions, 2026-09) - [security] mcp-go panics on an unhashable JSON-RPC request ID, and one crafted initialize stops the process #security (source: agent sessions, 2026-10) ## Relations - relates_to [[resources/documentation/Agents/Agents - Guards, Hooks, and Security Boundaries]] (credential exposure in argv versus env entries) - relates_to [[resources/documentation/Agents/Agents - Gmail, Slack, and Google Docs Quirks]] (Slack response shapes and search limits) - relates_to [[resources/concepts/MCP Tool Optimization Framework]] (server scoping and tool namespace hygiene) - relates_to [[resources/documentation/Agents/pi - Extension Development]] (the pi-mcp-adapter that keys token stores by server name) ## Sources - Personal agent-session notes, March 2026 to October 2026