Recipe · Intermediate · 15 min
MCP in Claude Code: connect Playwright, GitHub and your database
Add MCP servers to Claude Code with claude mcp add, share them in .mcp.json, control permissions and context, and avoid leaked secrets and injection.
Tested with Claude Code 2.1.316 · reviewed
The problem
Claude Code reads and edits your code, but it can't see your site in a browser, your GitHub issues or your database tables. You end up copying and pasting screenshots, errors and query results. MCP (Model Context Protocol) connects Claude Code to those real tools, but a careless setup fills your context, leaks tokens into the repository or opens the door to malicious instructions.
The solution
1. Understand the two kinds of server
- stdio (local): Claude Code launches a program on your machine. Everything after
--is the server's command, passed unchanged. - http (remote): you connect to a URL. This is the recommended option for cloud services. SSE is deprecated; use http when the service offers it.
# stdio
claude mcp add [options] <name> -- <command> [args...]
# http
claude mcp add --transport http <name> <url>
2. Pick the scope
| Scope | Loads in | Shared | Stored in |
|---|---|---|---|
local (default) | This project only | No | ~/.claude.json |
project | This project only | Yes, via git | .mcp.json at the root |
user | All your projects | No | ~/.claude.json |
Rule of thumb: project for what the whole team needs (no secrets inside), local for anything holding your credentials or only you use, user for personal tools you want everywhere. If a server exists in several scopes, local wins, then project, then user.
3. Playwright MCP: let Claude test your site in a browser
Microsoft's official server (package @playwright/mcp, requires Node.js 18 or newer). From your project root:
claude mcp add --scope project playwright -- npx @playwright/mcp@latest --headless --isolated
--headless runs the browser without a window and --isolated keeps the profile in memory only (no cookies left on disk). This creates or updates .mcp.json:
{
"mcpServers": {
"playwright": {
"type": "stdio",
"command": "npx",
"args": [
"@playwright/mcp@latest",
"--headless",
"--isolated"
],
"env": {}
}
}
}
Then ask things like: "Open http://localhost:8000, take a snapshot and tell me what errors show up in the console." It will use tools such as browser_navigate, browser_snapshot and browser_console_messages.
4. GitHub MCP in read-only mode, with the token kept out of the repo
GitHub's official server is remote (https://api.githubcopilot.com/mcp/). Adding /readonly to the end of the URL leaves only the read tools. In .mcp.json you can use ${VARIABLE}, so the token is never written to the file:
claude mcp add-json --scope project github '{"type":"http","url":"https://api.githubcopilot.com/mcp/readonly","headers":{"Authorization":"Bearer ${GITHUB_PAT}"}}'
Each person exports their own token before starting Claude Code (create a fine-grained token with the minimum permissions):
export GITHUB_PAT="your_token_here"
claude
Watch out: ${VARIABLE} expansion works in .mcp.json. With --scope local or user the text is stored as-is in ~/.claude.json and isn't expanded (we checked: the server answers "Authorization header is badly formatted").
5. A database with DBHub (try the sample data first)
DBHub (@bytebase/dbhub) talks to PostgreSQL, MySQL, SQL Server, MariaDB, Oracle and SQLite. To try it without touching your data, use its sample database:
claude mcp add --scope local db -- npx -y @bytebase/dbhub@latest --transport stdio --demo
For your real database, pass the connection string in the DSN variable and connect with a read-only database user. Use local scope, because the password is stored in your ~/.claude.json:
claude mcp add --scope local --env DSN="postgres://reader:password@localhost:5432/myapp" --transport stdio db \
-- npx -y @bytebase/dbhub@latest --transport stdio
DBHub also supports readonly = true and max_rows for its execute_sql tool in its TOML file (docs), but its own docs warn that this doesn't replace a low-privilege user.
6. Check status and authenticate with /mcp
Inside Claude Code type /mcp: you'll see each server and its status, you can sign in with OAuth to remote servers that require it, and you can disable the ones you aren't using without removing them. From the terminal:
claude mcp list # all servers and their health
claude mcp get playwright # details for one
claude mcp remove playwright -s project
The first time you open claude in a project with .mcp.json, it asks you to approve those servers. To be asked again: claude mcp reset-project-choices.
7. Permissions: approve only what you trust
MCP tools are named mcp__<server>__<tool>. In .claude/settings.json:
{
"permissions": {
"allow": [
"mcp__playwright__browser_navigate",
"mcp__playwright__browser_snapshot",
"mcp__github__get_*"
],
"deny": [
"mcp__playwright__browser_run_code_unsafe"
]
}
}
mcp__github__* covers every tool from the github server, and mcp__github__get_* only those starting with get_. A wildcard without a server, such as "mcp__*", approves nothing in allow (it does work in deny to block all MCP).
8. Watch your context
By default Claude Code uses tool search: at startup it loads only tool names and each server's instructions, and fetches the full definition when needed. Even so, each server adds names, instructions and long responses: Claude Code warns when an MCP tool returns more than 10,000 tokens and cuts off at 25,000 by default (MAX_MCP_OUTPUT_TOKENS). Disable in /mcp whatever you don't use for the current task and check usage with /context.
Check it
After step 3 (and step 4, without exporting GITHUB_PAT), at the project root:
claude mcp list
Real output with Claude Code 2.1.316 (trimmed to our servers):
Checking MCP server health…
playwright: npx @playwright/mcp@latest --headless --isolated - ⏸ Pending approval (run `claude` to approve)
github: https://api.githubcopilot.com/mcp/readonly (HTTP) - ⏸ Pending approval (run `claude` to approve)
[Contains warnings] Project config (shared via .mcp.json)
└ [Warning] [github] mcpServers.github: Missing environment variables: GITHUB_PAT
claude mcp get playwright
playwright:
Scope: Project config (shared via .mcp.json)
Status: ⏸ Pending approval (run `claude` to approve)
Type: stdio
Command: npx
Args: @playwright/mcp@latest --headless --isolated
Environment:
To remove this server, run: claude mcp remove playwright -s project
"Pending approval" is expected: open claude, approve the project servers, and /mcp should then show them connected. The GITHUB_PAT warning goes away once you export the variable.
Why it works
Claude Code acts as an MCP client: it starts each stdio server as a child process (or connects over HTTP), asks for its tool list and offers those tools to Claude with the mcp__<server>__ prefix. Every call goes through the same permission system as Bash or Edit. .mcp.json is a normal repository file, which is why it's good for sharing configuration and why it must never hold secrets. More in the official MCP docs for Claude Code and the permissions docs.
Common mistakes
claude mcp addsays "Added" but/mcpshows an error → the command doesn't validate credentials → check the token or URL and confirm in/mcpthat the server shows as connected.Missing environment variables: GITHUB_PAT→ you didn't export the variable in that terminal →export GITHUB_PAT=...and reopenclaude.- "Authorization header is badly formatted" with
--scope local→${GITHUB_PAT}isn't expanded outside.mcp.json→ use--scope projectwith the variable, or paste the token only inlocalscope. - The server added with
--envdoesn't start → the name sat right after--envand was read as anotherKEY=valuepair → put another option (for example--transport stdio) between--envand the name. - The session gets slow or runs out of context → too many servers or huge responses → disable servers in
/mcpand ask for bounded results (aLIMIT, a specific selector, etc.).
FAQ
Is it safe to connect any MCP server?
No. Only install servers you trust: a stdio server runs on your machine with your permissions. Also, servers that read external content (web pages, issues, database rows) can bring in prompt injection: text that tries to make Claude do something you didn't ask for. That's why read-only GitHub, low-privilege database users and reviewing every action before approving it are worth it.
Where do I put tokens and passwords?
Never inside .mcp.json. Write ${VARIABLE} in the file and set the variable in your terminal, or use local scope, which stores the config in your ~/.claude.json and never gets pushed to the repository.
What's the difference between local and user?
Both are stored in ~/.claude.json and aren't shared. local loads only in the project where you added it; user loads in all your projects.
Does Playwright MCP isolate dangerous sites?
No. Its README says it's not a security boundary, and browser_run_code_unsafe runs arbitrary JavaScript in the server process. Use it to test your own pages.
How do I turn a server off without removing it?
In /mcp, select it and disable it. Claude Code stores that choice per project in ~/.claude.json.