# Stdio: Claude Code spawns the processClaude Code ──stdin/stdout──> MCP Server Process (local)# SSE: Claude Code connects over HTTPClaude Code ──HTTP POST──> MCP Server (remote) <──SSE──────
Playwright MCP 为 Claude 提供了一个真实的浏览器。不是无头爬虫——而是完整的 Chromium 实例,可以导航、点击、截图和检查。
1
2
3
4
5
# Add the server to your projectclaude mcp add playwright -- npx @anthropic-ai/mcp-playwright
# Verify it's registeredclaude mcp list
添加后,重启 Claude Code(或启动新会话)。你应该能在工具列表中看到 Playwright 工具。尝试一个简单的提示:
1
Navigate to https://news.ycombinator.com and tell me the top 3 stories
Claude 会调用 browser_navigate,然后调用 browser_snapshot 读取页面内容,并总结结果。每次工具首次调用时,你会收到权限提示。
以下是会话中实际的工具调用示例:
1
2
3
4
5
6
Claude wants to use mcp tool: mcp__playwright__browser_navigate
url: "https://news.ycombinator.com"
> Allow? (y/n/always)
Claude wants to use mcp tool: mcp__playwright__browser_snapshot
> Allow? (y/n/always)
filesystem MCP 服务器为 Claude 提供对项目根目录之外目录的受控访问。默认情况下,Claude Code 只能读写当前工作目录内的文件。filesystem MCP 扩展了这一能力——并带有防护措施。
1
2
3
4
5
6
7
# Allow Claude to read your SSH config and dotfiles (read-only)claude mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem \
/Users/you/.config \
/Users/you/.ssh/config
# The paths you pass are the allowed directories.# The server refuses access to anything outside them.
现在你可以问 Claude 类似这样的问题:
1
Read my SSH config and tell me which hosts I have configured
1
Check my git config in ~/.config/git/config and suggest improvements
GitHub MCP 服务器为 Claude 提供对 GitHub API 的直接访问——issues、PRs、repos 等。它比直接调用 gh CLI 更丰富,因为模型能获得结构化数据。
1
2
3
4
5
# You need a GitHub personal access tokenexportGITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxxxxxxxxxxx
# Add the serverclaude mcp add github -- npx -y @modelcontextprotocol/server-github
现在 Claude 可以:
1
List all open issues in this repo labeled "bug"
1
Show me the review comments on PR #42
1
Create a new issue titled "Fix login timeout" with the label "backend"
# For a stdio server, just run it and see if it startsnpx @anthropic-ai/mcp-playwright
# It should sit there waiting for input on stdin# If it crashes immediately, you have a dependency issue
// server.ts
import{McpServer}from'@modelcontextprotocol/sdk/server/mcp.js';import{StdioServerTransport}from'@modelcontextprotocol/sdk/server/stdio.js';import{z}from'zod';constAPI_BASE=process.env.DEPLOY_API_URL||'https://api.internal.company.com';constAPI_TOKEN=process.env.DEPLOY_API_TOKEN;if(!API_TOKEN){console.error('DEPLOY_API_TOKEN environment variable is required');process.exit(1);}asyncfunctionapiCall(path: string,method='GET',body?: unknown){constresponse=awaitfetch(`${API_BASE}${path}`,{method,headers:{'Authorization':`Bearer ${API_TOKEN}`,'Content-Type':'application/json',},body: body?JSON.stringify(body):undefined,});if(!response.ok){thrownewError(`API error ${response.status}: ${awaitresponse.text()}`);}returnresponse.json();}// Create the server
constserver=newMcpServer({name:'deploy-tools',version:'1.0.0',});// Tool 1: List deployments
server.tool('list_deployments','List recent deployments with their status',{environment: z.enum(['staging','production']).describe('Target environment'),limit: z.number().min(1).max(50).default(10).describe('Number of results'),},async({environment,limit})=>{constdata=awaitapiCall(`/deployments?env=${environment}&limit=${limit}`);constformatted=data.deployments.map((d: any)=>`[${d.status}] ${d.id} — ${d.service} @ ${d.version} (${d.deployed_at})`).join('\n');return{content:[{type:'text',text: formatted||'No deployments found.',}],};});// Tool 2: Get deployment details
server.tool('get_deployment','Get detailed information about a specific deployment',{deployment_id: z.string().describe('The deployment ID'),},async({deployment_id})=>{constdata=awaitapiCall(`/deployments/${deployment_id}`);return{content:[{type:'text',text: JSON.stringify(data,null,2),}],};});// Tool 3: Trigger rollback
server.tool('rollback_deployment','Roll back a deployment to a previous version. This is a destructive action.',{deployment_id: z.string().describe('The deployment ID to roll back'),target_version: z.string().describe('The version to roll back to'),reason: z.string().describe('Reason for the rollback'),},async({deployment_id,target_version,reason})=>{constdata=awaitapiCall(`/deployments/${deployment_id}/rollback`,'POST',{target_version,reason,});return{content:[{type:'text',text:`Rollback initiated: ${data.rollback_id}\nStatus: ${data.status}\nETA: ${data.estimated_completion}`,}],};});// Tool 4: Health check
server.tool('check_health','Check the health status of a service in an environment',{service: z.string().describe('Service name'),environment: z.enum(['staging','production']).describe('Target environment'),},async({service,environment})=>{constdata=awaitapiCall(`/health/${service}?env=${environment}`);return{content:[{type:'text',text:[`Service: ${data.service}`,`Environment: ${data.environment}`,`Status: ${data.status}`,`Uptime: ${data.uptime}`,`Last check: ${data.last_check}`,`Instances: ${data.healthy_instances}/${data.total_instances} healthy`,].join('\n'),}],};});// Start the server
consttransport=newStdioServerTransport();awaitserver.connect(transport);
# server.pyfrommcp.server.fastmcpimportFastMCPimporthttpximportosmcp=FastMCP("deploy-tools")API_BASE=os.environ.get("DEPLOY_API_URL","https://api.internal.company.com")API_TOKEN=os.environ["DEPLOY_API_TOKEN"]@mcp.tool()asyncdeflist_deployments(environment:str,limit:int=10)->str:"""List recent deployments with their status.
Args:
environment: Target environment (staging or production)
limit: Number of results to return (1-50)
"""asyncwithhttpx.AsyncClient()asclient:resp=awaitclient.get(f"{API_BASE}/deployments",params={"env":environment,"limit":limit},headers={"Authorization":f"Bearer {API_TOKEN}"},)resp.raise_for_status()data=resp.json()lines=[]fordindata["deployments"]:lines.append(f"[{d['status']}] {d['id']} — {d['service']} @ {d['version']}")return"\n".join(lines)or"No deployments found."@mcp.tool()asyncdefcheck_health(service:str,environment:str)->str:"""Check the health of a service in an environment.
Args:
service: Service name
environment: Target environment (staging or production)
"""asyncwithhttpx.AsyncClient()asclient:resp=awaitclient.get(f"{API_BASE}/health/{service}",params={"env":environment},headers={"Authorization":f"Bearer {API_TOKEN}"},)resp.raise_for_status()data=resp.json()return(f"Service: {data['service']}\n"f"Status: {data['status']}\n"f"Uptime: {data['uptime']}\n"f"Healthy: {data['healthy_instances']}/{data['total_instances']}")if__name__=="__main__":mcp.run(transport="stdio")
注册它:
1
claude mcp add deploy-tools -- python3 /path/to/server.py
I just deployed a change to the login page. Go to http://localhost:3000/login,
try logging in with test@example.com / password123, and tell me if the
redirect to /dashboard works correctly.
Claude 会导航、填写表单、点击提交、检查结果 URL 并报告。如果出错,它可以截图错误状态并读取控制台日志。
A user reported they can't see their orders. Their email is jane@example.com.
Check the users table for their account, then the orders table, and tell me
if there's a data issue.
Find all GitHub issues labeled "regression" that were opened this week,
then search Slack #incidents for any related discussion, and give me a
summary of what broke and whether it's been addressed.
# Add a stdio serverclaude mcp add SERVER_NAME -- COMMAND [ARGS...]# Add with environment variablesclaude mcp add SERVER_NAME -e KEY=VALUE -- COMMAND [ARGS...]# Add at a specific scopeclaude mcp add SERVER_NAME -s user -- COMMAND [ARGS...]# globalclaude mcp add SERVER_NAME -s project -- COMMAND [ARGS...]# project (default)# List all registered serversclaude mcp list
# Get details about a specific serverclaude mcp get SERVER_NAME
# Remove a serverclaude mcp remove SERVER_NAME