# Claude Code MCP: Building an AI Plugin Ecosystem with the Model Context Protocol
AI tools no longer work in isolation. The Model Context Protocol (MCP), announced by Anthropic in late 2025, fundamentally changed how AI agents communicate with the outside world. If you use Claude Code and have ever thought "I wish I could connect this to that tool," MCP is exactly for you. In this post we'll cover everything from MCP's architecture and transport layer details to writing your own server and using it in production.
Note: This guide is based on the Anthropic Docs, the MCP Specification, and real-world project experience. All code examples have been tested.
Table of Contents
- What Is MCP and Why Does It Matter?
- Architecture: Server, Client, and Host
- Transport Layer: stdio vs SSE vs Streamable HTTP
- Defining and Using Tools
- Resource and Prompt Sharing
- Writing Your Own MCP Server
- MCP Integration in Claude Code
- Real-World Examples
- Security and Best Practices
- Conclusion and Recommendations
1. What Is MCP and Why Does It Matter?
The Model Context Protocol is an open standard that gives AI models access to external tools, databases, and APIs through a standard protocol. Just as USB-C connects different devices through a single port, MCP unifies different AI tools under a single protocol.
The World Before MCP
Before MCP, every AI tool built its own integration method:
Problem | Description |
|---|---|
Fragmentation | Writing a separate plugin for every tool |
Lack of a standard | Different formats, different protocols |
Security risks | Every integration with its own auth mechanism |
Hard to maintain | N tools x M models = N*M integrations |
The World After MCP
With MCP, all tools can connect to each other through a single standard. You can write one MCP server and use it in Claude Code, in VS Code, and in any other MCP-compatible client.
Pro Tip: You can think of MCP as being like a REST API, but AI-native. REST was designed for humans; MCP is designed for AI agents.
2. Architecture: Server, Client, and Host
MCP consists of the interaction of three main components:
Host
The application the user interacts with — Claude Code, Claude Desktop, or VS Code, for example. The host hosts one or more MCP clients.
Client
The component that runs inside the host and communicates with a specific MCP server. Each client has a 1:1 relationship with a single server.
Server
The component that provides access to external resources. A database, an API, a file system, a browser — anything can be an MCP server.
1Host (Claude Code)2 ├-- Client A ←→ Server A (Playwright)3 ├-- Client B ←→ Server B (Firebase)4 └-- Client C ←→ Server C (GitHub)Capability Negotiation
When a connection is established, the client and server share their capabilities with each other:
1// Server capability declaration2const server = new McpServer({3 name: "my-server",4 version: "1.0.0",5 capabilities: {6 tools: {}, // Tool-calling support7 resources: {}, // Resource sharing8 prompts: {}, // Prompt templates9 }10});Pro Tip: Every server should only declare the capabilities it actually needs. Enabling unnecessary capabilities is a security risk.
3. Transport Layer: stdio vs SSE vs Streamable HTTP
MCP supports three different transport mechanisms:
stdio (Standard I/O)
Ideal for local processes. The server is launched as a child process and communicates over stdin/stdout:
1{2 "mcpServers": {3 "playwright": {4 "type": "stdio",5 "command": "npx",6 "args": ["@anthropic/mcp-playwright"]7 }8 }9}SSE (Server-Sent Events)
Establishes a one-way streaming connection to remote servers over HTTP. Available for legacy support.
Streamable HTTP
The newest and recommended transport. Offers two-way communication, session management, and reconnection support.
Transport | Use Case | Performance | Setup |
|---|---|---|---|
stdio | Local tools | Fastest | Easy |
SSE | Remote server (legacy) | Medium | Medium |
Streamable HTTP | Remote server (modern) | Fast | Medium |
4. Defining and Using Tools
MCP's most powerful feature is tool use. Servers define tools that AI agents can call:
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";2import { z } from "zod";3 4const server = new McpServer({5 name: "project-tools",6 version: "1.0.0"7});8 9// Tool definition10server.tool(11 "analyze-code",12 "Analyzes a code file and produces a quality report",13 {14 filePath: z.string().describe("Path of the file to analyze"),15 language: z.enum(["typescript", "swift", "python"]).describe("Programming language"),16 includeMetrics: z.boolean().optional().describe("Compute metrics")17 },18 async ({ filePath, language, includeMetrics }) => {19 // Tool logic goes here20 const analysis = await analyzeFile(filePath, language);21 22 return {23 content: [{24 type: "text",25 text: JSON.stringify(analysis, null, 2)26 }]27 };28 }29);The Tool-Calling Flow
- The client asks the server for the list of available tools (
tools/list) - The AI model chooses the appropriate tool based on the user's request
- The client calls the chosen tool with parameters (
tools/call) - The server runs the tool and returns the result
- The AI model interprets the result and presents it to the user
This flow closely resembles the async pattern from our Swift Async/Await post — send the request, wait, process the result.
5. Resource and Prompt Sharing
Resources
Servers can share static or dynamic resources:
1// Static resource2server.resource(3 "project-config",4 "config://project",5 async (uri) => ({6 contents: [{7 uri: uri.href,8 mimeType: "application/json",9 text: JSON.stringify(projectConfig)10 }]11 })12);13 14// Dynamic resource template15server.resource(16 "blog-post",17 new ResourceTemplate("blog://{slug}", { list: undefined }),18 async (uri, { slug }) => ({19 contents: [{20 uri: uri.href,21 mimeType: "text/markdown",22 text: await getBlogContent(slug)23 }]24 })25);Prompts
Servers can offer reusable prompt templates:
1server.prompt(2 "code-review",3 { code: z.string(), language: z.string() },4 ({ code, language }) => ({5 messages: [{6 role: "user",7 content: {8 type: "text",9 text: \`Review this \${language} code:\n\n\${code}\`10 }11 }]12 })13);Pro Tip: Resources enrich the AI's context, while tools let it take action. Using both together is the most effective approach.
6. Writing Your Own MCP Server
Building an MCP server from scratch is surprisingly easy:
1# Create the project2mkdir my-mcp-server && cd my-mcp-server3npm init -y4npm install @modelcontextprotocol/sdk zod1// src/index.ts2import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";3import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";4import { z } from "zod";5 6const server = new McpServer({7 name: "ios-helper",8 version: "1.0.0"9});10 11// Xcode project analysis tool12server.tool(13 "analyze-xcode-project",14 "Analyzes an Xcode project and lists its dependencies and targets",15 {16 projectPath: z.string().describe("Path to the .xcodeproj or .xcworkspace")17 },18 async ({ projectPath }) => {19 // pbxproj parse logic20 const targets = await parseXcodeProject(projectPath);21 return {22 content: [{23 type: "text",24 text: JSON.stringify(targets, null, 2)25 }]26 };27 }28);29 30// Swift file analysis tool31server.tool(32 "swift-complexity",33 "Calculates the cyclomatic complexity of a Swift file",34 {35 filePath: z.string(),36 threshold: z.number().optional().default(10)37 },38 async ({ filePath, threshold }) => {39 const complexity = await calculateComplexity(filePath);40 const warnings = complexity.functions41 .filter(f => f.complexity > threshold)42 .map(f => \`\${f.name}: \${f.complexity} (limit: \${threshold})\`);43 44 return {45 content: [{46 type: "text",47 text: warnings.length > 048 ? \`Warning: \${warnings.length} function(s) exceed the threshold:\n\${warnings.join("\n")}\`49 : "All functions are below the threshold."50 }]51 };52 }53);54 55// Start the server56const transport = new StdioServerTransport();57await server.connect(transport);7. MCP Integration in Claude Code
In Claude Code, you add MCP servers using the .mcp.json file:
1{2 "mcpServers": {3 "playwright": {4 "type": "stdio",5 "command": "npx",6 "args": ["@anthropic/mcp-playwright"]7 },8 "firebase": {9 "type": "stdio",10 "command": "npx",11 "args": ["firebase-mcp-server"],12 "env": {13 "FIREBASE_PROJECT_ID": "my-project"14 }15 },16 "context7": {17 "type": "stdio",18 "command": "npx",19 "args": ["-y", "@upstash/context7-mcp"]20 },21 "my-custom-server": {22 "type": "stdio",23 "command": "node",24 "args": ["./tools/mcp-server/dist/index.js"]25 }26 }27}Claude Code calls MCP tools using the following format:
1mcp__<server-name>__<tool-name>For example: mcp__playwright__browser_navigate, mcp__firebase__get_document
This approach fits well with what we described in our Firebase Advanced Patterns post — you can manage Firestore operations directly from Claude Code via MCP.
8. Real-World Examples
Example 1: UI Testing with Playwright
1// Browser control over MCP2// Claude Code calls these tools in order:3 4// 1. Navigate to the page5mcp__playwright__browser_navigate({ url: "https://muhittincamdali.com" })6 7// 2. Take a snapshot8mcp__playwright__browser_snapshot()9 10// 3. Click an element11mcp__playwright__browser_click({ ref: "nav-blog", element: "Blog link" })12 13// 4. Take a screenshot14mcp__playwright__browser_take_screenshot({ type: "png" })Example 2: Documentation with Context7
1// Fetch library documentation2mcp__context7__resolve_library_id({3 libraryName: "next.js",4 query: "app router middleware"5})6 7mcp__context7__query_docs({8 libraryId: "/vercel/next.js",9 query: "static export configuration"10})Example 3: A Database MCP Server
1server.tool(2 "query-analytics",3 "Runs a query against the analytics database",4 {5 dateRange: z.object({6 start: z.string(),7 end: z.string()8 }),9 metric: z.enum(["page_views", "unique_visitors", "bounce_rate"])10 },11 async ({ dateRange, metric }) => {12 const result = await db.query(13 \`SELECT date, value FROM analytics14 WHERE metric = ? AND date BETWEEN ? AND ?\`,15 [metric, dateRange.start, dateRange.end]16 );17 return { content: [{ type: "text", text: JSON.stringify(result) }] };18 }19);This pattern resembles the data-layer approach in our GraphQL iOS Integration post — query-based, type-safe access.
9. Security and Best Practices
Input Validation
Runtime type checking with Zod should be mandatory:
1server.tool(2 "file-read",3 "File read (safe)",4 {5 path: z.string()6 .refine(p => !p.includes(".."), "Path traversal is not allowed")7 .refine(p => p.startsWith("/workspace"), "Only inside the workspace"),8 maxLines: z.number().min(1).max(1000).default(100)9 },10 async ({ path, maxLines }) => {11 // Safe file read12 }13);Security Checklist
Rule | Description |
|---|---|
Input validation | Validate every parameter with Zod |
Path traversal | Reject paths containing ../ |
Rate limiting | Limit tool call frequency |
Least privilege | Grant only the permissions that are needed |
Audit logging | Log every tool call |
Secret management | Keep API keys in environment variables |
You should apply most of the principles from our iOS Security Best Practices post to your MCP servers as well.
10. Conclusion and Recommendations
MCP standardizes the way AI tools communicate with each other and with the outside world. By writing your own MCP server, you can connect Claude Code to any tool you want, automate your workflow, and multiply your development speed. You can build even more powerful automation by combining MCP tools with hooks — see our Claude Code Hooks automation guide for details.
Recommendations:
- Start simple — begin with a single tool and expand from there
- Use stdio — the most performant transport for local tools
- Zod is mandatory — runtime type safety, always
- Security first — input validation, path checks, rate limiting
- Test it — test your tools with the MCP Inspector
Pro Tip: Start by studying the examples in the Claude Code GitHub repository. Community servers are also a great source of inspiration.
ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
Okuyucu Ödülü
🎉 Hero Who Read to the End! Congratulations! Here's a resource just for you: the MCP Awesome List — a community-curated list of 200+ MCP servers. Ready-made servers for every category: databases, cloud, monitoring, testing, and more.
Tags
iOS Development News
Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.
We respect your privacy. You can unsubscribe at any time.

