Skip to main content

MCP Server: Building Production‑Grade Model Context Protocol Servers

What Is an MCP Server

An MCP Server is a service that implements the Model Context Protocol (MCP), exposing tools, resources, and prompts that can be discovered and consumed by AI agents through a standardized JSON‑RPC 2.0 interface. The server acts as a bridge between an LLM‑powered agent and external systems—databases, APIs, file systems, or internal business services—translating the agent’s semantic requests into deterministic, executable operations.

In the MCP ecosystem, clients (Claude Desktop, Cursor, custom LangGraph agents, etc.) discover server capabilities via a handshake, then invoke tools (tools/call), read resources (resources/read), or fetch prompts (prompts/get). The server handles authentication, input validation, and business logic, returning structured results that the agent can use for further reasoning or final responses.

Key insight: An MCP server is not an agent. It is a tool provider. It does not decide which tool to call or in what order—that is the agent’s responsibility. The server simply serves the capabilities and executes them on demand.


Why MCP Servers Matter

MCP servers solve three fundamental problems that have plagued AI tool integration:

ProblemWithout MCPWith MCP
Tool standardizationEvery framework (LangChain, AutoGen, CrewAI) has its own tool format.One MCP server works with any MCP‑compatible client.
Tool discoveryHardcoded tool names; changes require code updates.Clients query tools/list at runtime; no hardcoding.
SecurityEach integration reinvents authentication.Standardized OAuth 2.1 + PKCE; predictable security model.
ReusabilityBuild the same tool for each agent.Build once, deploy once, connect from any client.

Practical impact: An enterprise that deploys a single MCP server for its internal CRM API makes that API accessible to every MCP‑compatible agent in the organisation—Claude Desktop, custom LangGraph agents, Cursor, and more—without rewriting integration code for each client.


MCP Architecture Overview

The MCP ecosystem has a clear separation of responsibilities:

Components:

  • Client – The AI agent or application that consumes server capabilities. Uses an MCP client SDK to discover and invoke tools.
  • Transport – The communication channel. stdio for local clients (Claude Desktop); Streamable HTTP (preferred for remote access); legacy HTTP/SSE is deprecated as of March 2025.
  • Authentication Layer – Validates OAuth 2.1 tokens or API keys before any capability is exposed.
  • Capability Registry – Stores registered tools, resources, and prompts with their schemas and metadata.
  • Handlers – Business logic that executes the requested operation (e.g., SELECT * FROM orders).
  • Backend Systems – The actual data sources or services the server integrates with.

MCP Server Responsibilities

ResponsibilityDescriptionWhy It Matters
Capability exposurePublish tools, resources, and prompts for client discovery.Enables runtime discovery; no hardcoded client logic.
Tool registrationRegister each tool with name, description, input/output schemas.Clients understand what the tool does and how to call it.
Resource managementServe read‑only data identified by URI templates (e.g., file:///docs/{path}).Dynamic, parameterised data access.
Prompt managementProvide parameterised prompt templates for reusable interaction patterns.Consistency across agent interactions.
Authentication enforcementReject unauthenticated requests; validate OAuth tokens per request.Prevents unauthorised access to downstream systems.
Audit loggingRecord every tool invocation, including identity, parameters, and outcome.Compliance and debugging.

MCP Server Lifecycle

Stage Details

StagePurposeFailure Mode
StartupLoad configuration, initialise backends, start transport.Backend unreachable; port conflict.
Capability registrationRegister tools/resources/prompts with schemas.Duplicate registration; invalid schema.
Client connectionPerform handshake, negotiate protocol version.Incompatible versions; failed auth.
Tool invocationExecute handler, return structured result.Timeout, backend error, invalid parameters.
ShutdownFlush logs, close connections, exit cleanly.Unfinished operations interrupted.

MCP Server Components

Transport Layer

The transport determines how clients connect to the server. Choose based on deployment context:

TransportBest ForCharacteristics
stdioLocal clients (Claude Desktop, CLI tools)Simplicity, isolation, no network exposure.
Streamable HTTPRemote, multi‑tenant, cloud‑nativeStateless scaling, per‑request auth, recommended for production servers.
Legacy HTTP/SSEBackward compatibility onlyDeprecated in MCP 2025‑03‑26; avoid for new servers.
WebSocketReal‑time bidirectional streamingHigher complexity; use only when needed.

Best practice: Keep tool logic independent of transport. Implement handlers agnostically, then plug in the desired transport at the entrypoint.

Tool Registry

The registry holds metadata for every tool:

Each tool entry includes:

  • Name – Unique identifier (e.g., search_orders).
  • Description – Human‑readable, LLM‑friendly explanation.
  • Input schema – JSON Schema defining required and optional parameters.
  • Output schema – Structure of the returned data.
  • Authentication scope – Which OAuth scope grants access to this tool.

Resource Registry

Resources are read‑only data identified by URI templates. Example: file:///logs/{date}. When a client requests resources/read with URI file:///logs/2026-06-01, the handler fetches the corresponding data. Resources are ideal for exposing configuration, documentation, or any static/semi‑static content that agents may need without writing.

Prompt Registry

Prompts are parameterised templates that clients can retrieve and then use to format LLM requests. Example: analyze_logs(log_level="error", time_range="last hour") returns a prompt string with placeholders filled. This centralises prompt management and ensures consistency across agents.

Authentication Layer

The MCP specification made OAuth 2.1 mandatory for remote servers as of March 2025. Key requirements:

  • OAuth 2.1 with PKCE – Protects against authorization code interception.
  • Per‑request token validation – No session‑level caching; each tool invocation validates the token’s signature, issuer, audience, expiry, and required scope.
  • Short‑lived access tokens – Minutes, not hours. Refresh tokens rotate on each use.

Logging & Observability Layer

MCP servers ship with no built‑in observability. Without instrumentation, tool call latency, errors, and performance baselines remain invisible. Implement structured logging, OpenTelemetry metrics, and distributed tracing at the handler level. Tools like Elastic APM and Heimdall provide OpenTelemetry‑native instrumentation.


Building an MCP Server: Implementation Workflow

1. Define Tools

Start by listing the operations your server will expose. Each tool should have a single, focused responsibility. Avoid catch‑all “do everything” tools.

Example – order management tools:

  • get_order(order_id: string) → returns order details
  • search_orders(customer_id: string, status: string) → returns array of orders
  • update_order_status(order_id: string, new_status: string) → returns success/failure

Use clear, LLM‑friendly descriptions. The description is what guides the agent to choose the right tool.

2. Define Schemas

Schemas are not optional decorations; they are the contract that prevents ambiguity.

{
"name": "search_orders",
"description": "Search orders by customer ID and optional status filter",
"inputSchema": {
"type": "object",
"properties": {
"customer_id": { "type": "string", "description": "Customer identifier" },
"status": { "type": "string", "enum": ["pending", "shipped", "delivered", "cancelled"] }
},
"required": ["customer_id"]
}
}

Use strict typing, documented error cases, and consistent naming. In TypeScript, Zod is the recommended schema validation library.

3. Register Capabilities

Registration must complete before the transport is opened. In the TypeScript SDK, calling registerTool after connect() fails with “Cannot register capabilities after connecting to transport”. The exact registration API varies by SDK version (some use tool(), others registerTool()). Always verify against the current MCP documentation.

4. Implement Handlers

Handlers are pure functions: they receive validated parameters and return structured results. Do not embed transport‑specific logic (stdio writes, HTTP headers) inside handlers—keep them agnostic.

// Handler signature (simplified)
async function searchOrdersHandler(params: SearchOrdersParams): Promise< ToolResult> {
// 1. Validate business constraints (beyond schema)
if (params.limit && params.limit > 100) {
throw new Error("Limit cannot exceed 100");
}
// 2. Execute backend logic (DB query, API call)
const orders = await db.orders.find({ customerId: params.customer_id });
// 3. Return structured result
return { success: true, data: orders };
}

5. Add Authentication

For remote servers, implement OAuth 2.1 with PKCE. Validate the token on every invocation (not just at session establishment). Use a middleware pattern that checks token validity before dispatching to the handler. Never hardcode API keys in the server binary—use a secret store and environment variables.

6. Deploy

Package the server as a container (Alpine Linux for minimal footprint and security), deploy to Kubernetes with proper ingress and health checks, or use a serverless platform for scale‑to‑zero.


MCP Server Tool Management

Registration

Register each tool with its full metadata before the transport starts. Example pattern from the Java Quarkus MCP SDK: tools are defined using the @Tool annotation—no configuration files, no manual registration, no boilerplate. The @ToolArg annotation marks function parameters with descriptions, and JSON Schema generation, parameter validation, and error handling are automatic.

Schema Design Best Practices

  1. Keep schemas shallow – Avoid deeply nested objects; agents struggle to generate correct parameters for complex structures.
  2. Use enums for closed sets"enum": ["pending", "approved", "rejected"] guides the LLM toward valid values.
  3. Provide sensible defaults – Reduces required parameter count.
  4. Document error cases – Include in the description what happens for common failure modes (e.g., “Returns {error: ‘not_found’} if order doesn’t exist”).

Tool Versioning

When tool schemas evolve, use one of two strategies:

  • Backward‑compatible changes – Add optional fields; do not remove or rename existing fields. Update the description.
  • Breaking changes – Create a new tool version (get_order_v2). Keep both versions registered simultaneously until all clients migrate. Implement a deprecation warning in the response of the old version.

MCP Server Resource Management

Resources provide read‑only access to data via URI templates. Use them for:

  • Static documentationdocs://api-reference
  • Parameterised data accesslogs://{service}/{date} where the handler fetches from external storage.
  • File system accessfile:///var/data/{filename} (but always validate path traversal attempts; 82% of surveyed MCP servers are vulnerable to path traversal attacks).

Example resource handler (pseudocode):

registerResource({
uri: "logs://{service}/{date}",
handler: async (service, date) => {
// Validate service name against allowlist
if (!allowedServices.includes(service)) {
throw new Error("Invalid service");
}
const logs = await fetchLogs(service, date);
return { contents: [{ uri: `logs://${service}/${date}`, text: logs }] };
}
});

MCP Server Prompt Management

Prompts are parameterised templates that clients retrieve and then pass to LLMs. They centralise reusable interaction patterns.

Example – security incident prompt:

{
"name": "analyze_incident",
"description": "Analyze a security incident report",
"arguments": [
{ "name": "incident_type", "description": "Type of incident", "required": true },
{ "name": "severity", "description": "Severity level (low/medium/high/critical)", "required": true }
],
"template": "You are a security analyst. Analyze the following {{incident_type}} incident classified as {{severity}} severity. Provide: 1) Root cause analysis, 2) Impact assessment, 3) Recommended remediation steps."
}

Prompts are especially valuable in multi‑agent systems where different agents must follow consistent interaction patterns.


MCP Server Security

Security is the most critical—and most neglected—aspect of MCP server development. As of early 2026, only 8.5% of MCP servers use OAuth; 91.5% rely on static API keys, shared tokens, or no authentication at all. Among 2,614 implementations surveyed, 82% use file operations vulnerable to path traversal, and more than a third are susceptible to command injection.

Authentication Checklist

  • OAuth 2.1 with PKCE – Mandatory for all remote MCP servers per the March 2025 specification update. Anonymous connections must be rejected at the transport layer.
  • Per‑request token validation – Validate signature, issuer, audience, expiry, and required scope on every tools/call, not just at session establishment.
  • Short‑lived access tokens – Minutes, not hours. Refresh tokens rotate on each use.
  • No static API keys – They are difficult to rotate, impossible to scope per‑request, and provide no identity signal for audit logs.

Authorization & Tool Permissions

Each tool should require a specific OAuth scope. Clients request the minimum set of scopes needed. The server validates that the token includes the required scope for that specific tool before executing the handler.

Input Validation and Injection Defense

Validate all inputs twice: once against the JSON Schema (structure/type) and once against business rules (allowed values, range limits). Do not pass unvalidated parameters to shell commands, SQL queries, or system calls.

Secrets Management

Never hardcode secrets. Use environment variables and a secret store (HashiCorp Vault, AWS Secrets Manager). Inject secrets at deployment time, not at build time.

Supply Chain Security

Pin SDK versions in package.json or requirements.txt. Verify signatures on third‑party dependencies. Regularly scan for vulnerabilities.

Audit Logging

Log every tool invocation with:

  • Timestamp
  • Client/user identifier (from token claims)
  • Tool name and parameters (redact sensitive fields)
  • Outcome (success/error)
  • Duration
  • Request ID for correlation

Network Hardening

  • Run the server in an isolated network segment with egress controls.
  • Use mutual TLS (mTLS) between MCP clients, agents, and servers.
  • Implement rate limiting per client to prevent abuse.

MCP Server Scalability

Stateless Architecture

Design the server to be stateless where possible. Session state should not be stored on the server—use an external store (Redis, database) if needed. Stateless servers scale horizontally without sticky sessions.

Horizontal Scaling

For Streamable HTTP transport, multiple server instances behind a load balancer handle requests concurrently. Use readiness and liveness probes to ensure healthy instances.

Caching

Cache expensive or repetitive operations:

  • Read‑only resource caching – Resources that change infrequently (documentation, metadata) can be cached for minutes or hours.
  • Tool result caching – For idempotent read‑only tools (e.g., get_order), cache results with a short TTL (e.g., 30 seconds) to reduce backend load.

Connection Management

For stdio transport, one client connects to one server process. Scale by launching multiple server processes, each serving one client. For HTTP transport, a single server handles many concurrent clients using non‑blocking I/O (Node.js, Quarkus with Vert.x).

Deployment Diagram (Kubernetes)


MCP Server Deployment Models

ModelDescriptionBest ForTransport
LocalRuns on developer machine, started by client.Individual developer tools, experimentation.stdio
TeamDeployed within team/organisation network.Shared internal tools (CRM, database queries).Streamable HTTP
EnterpriseProduction‑grade, multi‑tenant, high availability.Company‑wide tool catalogue, compliance requirements.Streamable HTTP
Cloud‑NativeServerless, auto‑scaling, global distribution.Public APIs, high‑traffic services.Streamable HTTP

Kubernetes deployment (OCI OKE) pattern: Deploy both MCP server and client on OKE using Terraform, Docker, OCIR, and Kubernetes manifests. Expose the server via a Kubernetes LoadBalancer Service; configure the client with the public MCP URL.

Serverless alternative: Deploy MCP servers on platforms like Azion for low latency, high scalability, and robust security.


MCP Server Observability

MCP servers ship with no built‑in observability, leaving tool‑call latency, errors, and performance baselines invisible. Implement observability as a first‑class concern.

Logging

Use structured logging (JSON format) with fields:

  • level (info, warn, error)
  • timestamp (ISO 8601)
  • request_id (correlates across handler execution)
  • tool_name, duration_ms, success, error_message (if any)
  • client_id (from token)

Do not log sensitive parameters (passwords, API keys, PII). Redact or exclude.

Metrics

Export metrics via OpenTelemetry:

  • mcp.tool.calls.total – counter, tagged by tool name and status (success/error)
  • mcp.tool.duration – histogram, buckets for latency distribution
  • mcp.server.active_connections – gauge (for HTTP transport)
  • mcp.auth.failures – counter, tagged by failure reason

Use tools like the Observatory SDK, which adds comprehensive analytics and monitoring to MCP servers with 2–3 lines of code.

Distributed Tracing

Add OpenTelemetry tracing spans around each tool handler. Include:

  • Span name: tool.< tool_name>
  • Attributes: tool.name, client.id, parameters (redacted), result.size
  • Child spans for backend calls (database, API)

Platforms like Heimdall provide real‑time tracing, metrics, and insights into AI infrastructure, built on OpenTelemetry standards. Elastic APM allows the same Claude Desktop session that produced the traces to query them back through the Elastic Agent Builder MCP—the agent analyses its own tool‑call latency, identifies slow tools, and explains failures without leaving the chat.

Monitoring Dashboard

Build a dashboard (Grafana, Datadog) showing:

  • Tool success rate (target >99%)
  • P95 latency per tool
  • Error rate by tool and error type
  • Active client connections
  • Rate‑limited requests

SDKLanguageStrengthsLimitationsBest For
TypeScript SDKTypeScript/Node.jsReference implementation, most feature‑complete, strongly‑typedJavaScript overheadCross‑platform, cloud‑native, community support
FastMCP (Python)PythonHigh‑level abstractions, powers ~70% of all MCP serversLess transport flexibilityRapid prototyping, data‑science integrations
Python SDKPythonSynchronous + asynchronous support, production‑readyLess ergonomic than FastMCPGeneral Python development
Java SDKJavaEnterprise‑grade, thread‑safe, asynchronous operationsVerbose, heavierEnterprise Java shops, high‑throughput systems
Quarkus MCPJavaNative executables, millisecond startup, ~30MB RAM, declarative @Tool annotationRelatively newMicroservices, cloud‑native Java
Go SDKGoLightweight, high concurrencySmaller communityHigh‑performance systems

Selection guidance:

  • Python/FastMCP – Best for data‑oriented tools, quick iteration, and if you already have Python backends.
  • TypeScript/Node.js – Best for general‑purpose servers, especially if your tooling ecosystem is JavaScript‑centric.
  • Java/Quarkus – Best for enterprise Java shops, low‑memory/latency requirements, or deployment alongside existing JVM services.
  • Go – Best for extreme performance requirements.

MCP Server Best Practices

  1. Design each MCP server as a single, bounded context – Focus on one domain (e.g., “order management”) rather than a catch‑all host for disparate tools. This keeps toolsets manageable, documentation clear, and authorization boundaries well‑scoped.

  2. Schema first – Define clear input/output schemas for every tool before writing handlers. Use strict typing, documented error cases, and consistent naming.

  3. Keep tools focused – Each tool should do one thing. “Get order” and “update order” are separate tools. Avoid “manage order” with a mode parameter.

  4. Validate inputs twice – First against JSON Schema (structure/types), then against business rules (allowlisted values, range limits, permissions). Never trust the client.

  5. Implement idempotency where possible – For tools that modify state, accept an idempotency_key parameter. Retries will not create duplicate side effects.

  6. Return structured errors – Avoid raw stack traces. Return error objects that clients can interpret and possibly handle automatically.

  7. Enforce authentication at the transport boundary – Unauthenticated requests must never reach tool handlers. Implement OAuth 2.1 with PKCE for all remote servers.

  8. Instrument everything – Add OpenTelemetry spans, structured logs, and metrics from day one. Retroactive instrumentation is painful.

  9. Pin SDK versions – Specify exact SDK versions in package.json or requirements.txt. Review release notes before upgrading; SDK APIs have changed significantly over time.

  10. Document rate limits and costs – If a tool calls an external API with costs or limits, include that information in the tool description.

  11. Test with real agents – Unit tests verify handler logic, but integration tests with actual MCP clients catch protocol compliance issues. Tools like MCP‑Jest and MCP Testing automate this.

  12. Plan for versioning – Assume your tool schemas will change. Design for backward‑compatible evolution. When breaking changes are unavoidable, introduce _v2 tools rather than altering existing ones.


Common MCP Server Mistakes

MistakeConsequenceFix
Overloading a serverToo many tools confuse both clients and developers.Split into multiple focused servers.
Poor schema designLLM generates invalid parameters; tool fails silently.Strict typing; validate; provide clear error messages.
No authenticationServer becomes open proxy to downstream systems.Implement OAuth 2.1 + PKCE before production.
Static API keysCannot rotate, no per‑request identity, weak security.Replace with OAuth 2.1 + short‑lived tokens.
Missing observabilityCannot debug latency issues or track usage.Add OpenTelemetry instrumentation from day one.
Unbounded tool executionTool that calls an external API without timeouts can hang indefinitely.Set timeouts on all backend calls.
Registration after connectionregisterTool after connect() fails silently.Register all capabilities before starting transport.
Ignoring protocol version negotiationOlder clients cannot connect.Let SDK negotiate automatically; support multiple protocol versions.
Hardcoded secretsCredentials leaked in source code or logs.Use environment variables and secret stores.
Zero testingUndiscovered bugs surface in production.Unit test handlers; integration test with MCP clients.

Case Study: Enterprise Knowledge MCP Server

Scenario: A global enterprise deploys an MCP server that exposes its internal knowledge base (Confluence, SharePoint, and a custom document store) to AI agents. Employees use Claude Desktop and a custom LangGraph agent to query policies, technical documentation, and project status.

Architecture

Tool Catalog

ToolDescriptionInput SchemaBackend
search_kbSearch across all knowledge sourcesquery: string, max_results: int (default 10)Confluence + SharePoint unified search
get_documentRetrieve full document by IDdoc_id: string, source: enum("confluence","sharepoint","custom")Source‑specific fetch
get_policyRetrieve HR/security policy by namepolicy_name: string, version: optional stringSharePoint metadata + content

Resource Design

  • kb://search/{query}.json – Returns search results in structured JSON.
  • kb://document/{source}/{id} – Returns document content and metadata.
  • kb://policy/{name} – Returns latest policy version.

Security Model

  • OAuth 2.1 with PKCE – Token issued by corporate Identity Provider.
  • Per‑request validation – Every tool invocation validates token, scope, and expiry.
  • Scopes:
    • kb:read – Read‑only access to search and document retrieval.
    • kb:policy – Access to HR/security policies (more restricted).
  • Audit log – Every search query and document access logged with user ID, timestamp, and search terms (redacted for sensitive terms).

Deployment Strategy

  • Container: Alpine Linux (minimal footprint: ~50MB).
  • Platform: Kubernetes on AWS EKS with auto‑scaling.
  • Observability: OpenTelemetry collector exporting to Datadog. Dashboard shows search latency (p95 < 500ms), error rate (< 0.5%), and top search terms.
  • Caching: Redis cache for policy documents (TTL 1 hour) and frequent search results (TTL 5 minutes).

Testing

  • Unit tests: Mock backend services; test search ranking, error handling.
  • Integration tests: Run against test instances of Confluence/SharePoint; verify protocol compliance with MCP‑Jest.
  • Evals: LLM‑as‑judge evaluates that the search tool returns relevant results for a curated set of queries.

Results

After deployment, the Knowledge MCP server serves ~500 daily active users across Claude Desktop and LangGraph agents. P95 latency for search: 420ms. Tool success rate: 99.3%. Human escalation for document retrieval reduced by 72%.


FAQ

1. What is the difference between an MCP Server and an MCP Client?
An MCP server exposes tools, resources, and prompts. An MCP client (Claude Desktop, LangGraph agent, Cursor) connects to the server, discovers its capabilities, and invokes tools. The client is the agent; the server is the tool provider.

2. Can one server expose multiple tools?
Yes. A single MCP server can expose many tools, resources, and prompts. However, best practice is to design each server as a single bounded context (e.g., “order management” or “knowledge base”) to keep tool sets focused and authorization boundaries clear.

3. How should MCP servers scale?
For HTTP transport, deploy stateless server instances behind a load balancer. Horizontal scaling works naturally. For stdio transport, each client gets its own server process; scale by launching additional processes.

4. Should MCP servers be stateless?
Yes, for scalability. Session state should be stored externally (Redis, database). Stateless servers are easier to scale, deploy, and recover.

5. How do MCP servers handle authentication?
The MCP specification mandates OAuth 2.1 with PKCE for all remote servers (March 2025 update). Tokens must be validated per request, not just at session establishment.

6. What transport should I use for an MCP server?
Use stdio for local clients (Claude Desktop on your machine). Use Streamable HTTP for remote, multi‑tenant, cloud‑native deployments. Legacy HTTP/SSE is deprecated and should be avoided for new servers.

7. Can I build an MCP server in any language?
Officially supported SDKs exist for TypeScript, Python, and Java. Community SDKs are available for Go, C#, Rust, and others.

8. How do I test an MCP server?
Use tools like MCP‑Jest for protocol compliance testing, unit test handlers with mocked backends, and integration tests against real MCP clients. For LLM‑centric evaluation, use MCP Testing or @mcpjam/sdk with LLM‑as‑a‑judge.

9. How do MCP servers handle versioning?
The MCP protocol supports version negotiation. The server advertises supported protocol versions during the initialize handshake, and the client selects the highest mutually supported version. For tool schemas, use backward‑compatible changes (optional fields) and _v2 tools for breaking changes.

10. What is the difference between a tool and a resource in MCP?
A tool is an action the model can invoke that may have side effects (write operations, API calls, computations). A resource is read‑only data identified by a URI; the model can fetch it but not modify it.

11. Can one client connect to multiple MCP servers?
Yes. Clients can connect to any number of MCP servers simultaneously. Claude Desktop, for example, can have multiple MCP servers configured.

12. What is the typical performance of an MCP server?
The server itself adds minimal overhead (JSON‑RPC parsing, schema validation, handler dispatch). Most of the latency comes from backend operations (DB queries, API calls). Quarkus MCP servers can start in milliseconds and run on ~30MB of RAM.

13. How do I secure an MCP server against prompt injection?
Validate all tool parameters. Do not pass raw user input to system commands or SQL queries. Use allowlists for URI path components. The OWASP MCP Top 10 provides detailed guidance.

14. What is OAuth PKCE and why does MCP require it?
PKCE (Proof Key for Code Exchange) protects against authorization code interception attacks. An attacker who observes the redirect URI cannot exchange the code for a token without the code verifier. The MCP specification requires PKCE for all clients.

15. Can an MCP server call other MCP servers?
Not directly—an MCP server is a tool provider, not a client. However, a client (agent) can call server A, then call server B, orchestrating across multiple servers at the client level.


Continue Your Journey

Now that you understand how to build production‑grade MCP servers, explore the rest of the MCP ecosystem:

Or return to the Agent Learning Path to see where MCP fits in your overall agent engineering roadmap.


This article is part of the AgentDevPro Production Agent Engineering Handbook. Updated for Q2 2026.