Skip to content

MCP Servers — Real-World Examples

MCP (Model Context Protocol) gives Claude access to external systems. Without MCP, Claude can only use built-in tools. With MCP, Claude can create GitHub issues, run browser tests, query your database, send Slack messages, and more.


Claude wants to create a GitHub issue
Claude calls: mcp__github__create_issue({ title: "...", body: "..." })
MCP client sends request to GitHub MCP server
GitHub MCP server calls GitHub REST API
Returns: { issue_number: 247, url: "https://github.com/..." }
Claude sees the result and continues

You configure the servers; Claude discovers what tools they provide automatically.


MCP Server 1: Playwright (Browser Automation)

Section titled “MCP Server 1: Playwright (Browser Automation)”

Use case: Claude can take screenshots, fill forms, click buttons, scrape pages.

Terminal window
claude mcp add playwright -- npx -y @playwright/mcp@latest

This writes to .mcp.json:

{
"mcpServers": {
"playwright": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@playwright/mcp@latest"]
}
}
}

You say:

Test the login flow — go to localhost:3000, log in with test@example.com / password123,
and verify the dashboard loads correctly

Claude does (using Playwright MCP tools):

mcp__playwright__navigate({ url: "http://localhost:3000" })
mcp__playwright__screenshot({ name: "homepage" })
mcp__playwright__fill({ selector: "#email", value: "test@example.com" })
mcp__playwright__fill({ selector: "#password", value: "password123" })
mcp__playwright__click({ selector: "[type=submit]" })
mcp__playwright__screenshot({ name: "after-login" })
mcp__playwright__assert({ selector: ".dashboard", state: "visible" })

Reports back with screenshots and pass/fail status.

You say:

Check the current pricing on our competitor's website and summarize it

Claude does:

mcp__playwright__navigate({ url: "https://competitor.com/pricing" })
mcp__playwright__get_text({ selector: ".pricing-table" })

You say:

The checkout button is invisible on mobile — screenshot it on a 375px viewport

Claude does:

mcp__playwright__set_viewport({ width: 375, height: 812 })
mcp__playwright__navigate({ url: "http://localhost:3000/checkout" })
mcp__playwright__screenshot({ name: "mobile-checkout", fullPage: true })

Use case: Claude can read/create issues, review PRs, manage repos.

Terminal window
claude mcp add --transport http github https://api.github.com/mcp

Authenticate:

Terminal window
/mcp
# Select "github" → Authenticate
# Follow OAuth flow in browser

You say:

Find all TODO comments in the codebase and create GitHub issues for each one

Claude does:

Grep("TODO:", "src/**/*.ts")
→ Finds: src/services/payment.ts:47: TODO: handle webhook retries
src/api/users.ts:23: TODO: add rate limiting
src/utils/email.ts:8: TODO: use queue instead of direct send
mcp__github__create_issue({
title: "Handle webhook retries in payment service",
body: "Located in src/services/payment.ts:47\n\nNeed to implement retry logic...",
labels: ["enhancement"]
})
# × 3 for each TODO

Real Example 2: Review a PR before merging

Section titled “Real Example 2: Review a PR before merging”

You say:

Review PR #247 — focus on security and missing tests

Claude does:

mcp__github__get_pull_request({ pull_number: 247 })
mcp__github__list_pull_request_files({ pull_number: 247 })
mcp__github__get_pull_request_diff({ pull_number: 247 })

Then reviews the diff and posts comments:

mcp__github__create_review({
pull_number: 247,
event: "REQUEST_CHANGES",
comments: [
{ path: "src/auth.ts", line: 34, body: "Missing auth check — any user can access this endpoint" },
{ path: "src/payment.ts", line: 78, body: "No test coverage for the refund path" }
]
})

You say:

Generate release notes for the v2.1.0 release based on merged PRs since v2.0.0

Claude does:

mcp__github__list_pull_requests({ state: "closed", base: "main" })
# Filters by merged_at date since v2.0.0 tag
mcp__github__get_pull_request({ pull_number: each })
# Builds categorized changelog: features, fixes, breaking changes

Use case: Claude can query your database directly — analyze data, write migrations, debug queries.

Terminal window
claude mcp add pg -- npx -y @modelcontextprotocol/server-postgres \
postgresql://localhost/myapp

Or with credentials:

{
"mcpServers": {
"db": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://user:pass@localhost/myapp"]
}
}
}

Real Example 1: Debug a production data issue

Section titled “Real Example 1: Debug a production data issue”

You say:

Users are complaining they can't see their recent orders. Debug the query.

Claude does:

mcp__db__query({ sql: "SELECT * FROM orders WHERE user_id = 'test-uuid' ORDER BY created_at DESC LIMIT 10" })
mcp__db__query({ sql: "EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 'test-uuid'" })

Finds: missing index on user_id causing full table scan. Suggests and generates the migration.

Real Example 2: Analyze data before a migration

Section titled “Real Example 2: Analyze data before a migration”

You say:

I want to add a NOT NULL constraint to the email column — will it break anything?

Claude does:

mcp__db__query({ sql: "SELECT COUNT(*) FROM users WHERE email IS NULL" })
mcp__db__query({ sql: "SELECT id, created_at FROM users WHERE email IS NULL LIMIT 5" })

Reports: “23 rows have NULL email. Here’s how to handle them before adding the constraint.”

Real Example 3: Generate TypeScript types from schema

Section titled “Real Example 3: Generate TypeScript types from schema”

You say:

Generate TypeScript interfaces for all the database tables

Claude does:

mcp__db__query({ sql: "SELECT table_name, column_name, data_type, is_nullable FROM information_schema.columns WHERE table_schema = 'public' ORDER BY table_name, ordinal_position" })

Generates complete TypeScript interfaces matching the actual DB schema.


MCP Server 4: Filesystem (Extended Access)

Section titled “MCP Server 4: Filesystem (Extended Access)”

Use case: Grant Claude access to directories outside the current project.

Terminal window
claude mcp add fs -- npx -y @modelcontextprotocol/server-filesystem \
/Users/you/shared-lib \
/Users/you/design-tokens

You say:

Update our design tokens to match the shared design system in ~/design-tokens

Claude does:

mcp__fs__read_file({ path: "/Users/you/design-tokens/colors.json" })
mcp__fs__read_file({ path: "/Users/you/design-tokens/spacing.json" })
Read("src/styles/tokens.css")
Edit("src/styles/tokens.css", ...) ← updates to match shared tokens

Use case: Claude can send messages, search channels, notify the team.

Terminal window
claude mcp add --transport http slack https://slack.com/mcp
# Authenticate via /mcp → Authenticate

.claude/skills/deploy/SKILL.md with Slack:

# Deploy to $ARGUMENTS
...deploy steps...
## Notify team
After successful deployment:
mcp__slack__post_message({
channel: "#deployments",
text: "🚀 Deployed to $ARGUMENTS — commit $(git rev-parse --short HEAD) by $(git config user.name)"
})
When tests fail in CI, search #alerts in Slack for any related recent issues
mcp__slack__search({ query: "payment test failure", count: 5 })

Use case: Claude can read production errors directly.

Terminal window
claude mcp add --transport http sentry https://mcp.sentry.dev/mcp
# Authenticate via /mcp → Authenticate

You say:

What are the top 3 errors in production this week? Fix the most impactful one.

Claude does:

mcp__sentry__list_issues({ environment: "production", limit: 10, sort: "events" })
→ Returns: Error list with counts
mcp__sentry__get_issue({ issue_id: "PAYMENTS-4821" })
→ Returns: Full stack trace, affected users, example events
Read("src/services/payment.ts") ← find the relevant code
Edit("src/services/payment.ts", ...) ← fix the bug

For internal tools, databases, or APIs without an existing server.

Minimal Node.js MCP server:

my-mcp-server/index.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server({ name: "my-tools", version: "1.0.0" }, {
capabilities: { tools: {} }
});
server.setRequestHandler("tools/list", async () => ({
tools: [{
name: "get_feature_flags",
description: "Get all feature flags and their current values",
inputSchema: { type: "object", properties: {}, required: [] }
}]
}));
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "get_feature_flags") {
const flags = await db.query("SELECT * FROM feature_flags");
return { content: [{ type: "text", text: JSON.stringify(flags) }] };
}
});
const transport = new StdioServerTransport();
await server.connect(transport);

Register it:

{
"mcpServers": {
"my-tools": {
"type": "stdio",
"command": "node",
"args": ["my-mcp-server/index.js"]
}
}
}

Now Claude can call mcp__my-tools__get_feature_flags directly.


Control exactly which MCP tools Claude can use:

{
"permissions": {
"allow": [
"mcp__playwright__*", // all playwright tools
"mcp__github__get_*", // github read-only
"mcp__github__list_*",
"mcp__db__query" // db queries only
],
"deny": [
"mcp__github__delete_*", // no deleting
"mcp__db__execute", // no writes to db
"mcp__slack__*" // no slack in this project
]
}
}
  • Trick: “Connect what the team already uses — value on day one.”
  • Do: wire up one server your team actually needs (DB, GitHub, Slack).
  • They’ll trip on: hardcoding credentials — use env-var references in config.

Learning path:25-mcp-servers.md · Index · ➡ 27-plugins.md

Written by Fenil Patel