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:
| Problem | Without MCP | With MCP |
|---|---|---|
| Tool standardization | Every framework (LangChain, AutoGen, CrewAI) has its own tool format. | One MCP server works with any MCP‑compatible client. |
| Tool discovery | Hardcoded tool names; changes require code updates. | Clients query tools/list at runtime; no hardcoding. |
| Security | Each integration reinvents authentication. | Standardized OAuth 2.1 + PKCE; predictable security model. |
| Reusability | Build 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.
stdiofor 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
| Responsibility | Description | Why It Matters |
|---|---|---|
| Capability exposure | Publish tools, resources, and prompts for client discovery. | Enables runtime discovery; no hardcoded client logic. |
| Tool registration | Register each tool with name, description, input/output schemas. | Clients understand what the tool does and how to call it. |
| Resource management | Serve read‑only data identified by URI templates (e.g., file:///docs/{path}). | Dynamic, parameterised data access. |
| Prompt management | Provide parameterised prompt templates for reusable interaction patterns. | Consistency across agent interactions. |
| Authentication enforcement | Reject unauthenticated requests; validate OAuth tokens per request. | Prevents unauthorised access to downstream systems. |
| Audit logging | Record every tool invocation, including identity, parameters, and outcome. | Compliance and debugging. |
MCP Server Lifecycle
Stage Details
| Stage | Purpose | Failure Mode |
|---|---|---|
| Startup | Load configuration, initialise backends, start transport. | Backend unreachable; port conflict. |
| Capability registration | Register tools/resources/prompts with schemas. | Duplicate registration; invalid schema. |
| Client connection | Perform handshake, negotiate protocol version. | Incompatible versions; failed auth. |
| Tool invocation | Execute handler, return structured result. | Timeout, backend error, invalid parameters. |
| Shutdown | Flush 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:
| Transport | Best For | Characteristics |
|---|---|---|
| stdio | Local clients (Claude Desktop, CLI tools) | Simplicity, isolation, no network exposure. |
| Streamable HTTP | Remote, multi‑tenant, cloud‑native | Stateless scaling, per‑request auth, recommended for production servers. |
| Legacy HTTP/SSE | Backward compatibility only | Deprecated in MCP 2025‑03‑26; avoid for new servers. |
| WebSocket | Real‑time bidirectional streaming | Higher 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 detailssearch_orders(customer_id: string, status: string)→ returns array of ordersupdate_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
- Keep schemas shallow – Avoid deeply nested objects; agents struggle to generate correct parameters for complex structures.
- Use enums for closed sets –
"enum": ["pending", "approved", "rejected"]guides the LLM toward valid values. - Provide sensible defaults – Reduces required parameter count.
- 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 documentation –
docs://api-reference - Parameterised data access –
logs://{service}/{date}where the handler fetches from external storage. - File system access –
file:///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
| Model | Description | Best For | Transport |
|---|---|---|---|
| Local | Runs on developer machine, started by client. | Individual developer tools, experimentation. | stdio |
| Team | Deployed within team/organisation network. | Shared internal tools (CRM, database queries). | Streamable HTTP |
| Enterprise | Production‑grade, multi‑tenant, high availability. | Company‑wide tool catalogue, compliance requirements. | Streamable HTTP |
| Cloud‑Native | Serverless, 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 distributionmcp.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
MCP Server Implementation with Popular SDKs
| SDK | Language | Strengths | Limitations | Best For |
|---|---|---|---|---|
| TypeScript SDK | TypeScript/Node.js | Reference implementation, most feature‑complete, strongly‑typed | JavaScript overhead | Cross‑platform, cloud‑native, community support |
| FastMCP (Python) | Python | High‑level abstractions, powers ~70% of all MCP servers | Less transport flexibility | Rapid prototyping, data‑science integrations |
| Python SDK | Python | Synchronous + asynchronous support, production‑ready | Less ergonomic than FastMCP | General Python development |
| Java SDK | Java | Enterprise‑grade, thread‑safe, asynchronous operations | Verbose, heavier | Enterprise Java shops, high‑throughput systems |
| Quarkus MCP | Java | Native executables, millisecond startup, ~30MB RAM, declarative @Tool annotation | Relatively new | Microservices, cloud‑native Java |
| Go SDK | Go | Lightweight, high concurrency | Smaller community | High‑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
-
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.
-
Schema first – Define clear input/output schemas for every tool before writing handlers. Use strict typing, documented error cases, and consistent naming.
-
Keep tools focused – Each tool should do one thing. “Get order” and “update order” are separate tools. Avoid “manage order” with a
modeparameter. -
Validate inputs twice – First against JSON Schema (structure/types), then against business rules (allowlisted values, range limits, permissions). Never trust the client.
-
Implement idempotency where possible – For tools that modify state, accept an
idempotency_keyparameter. Retries will not create duplicate side effects. -
Return structured errors – Avoid raw stack traces. Return error objects that clients can interpret and possibly handle automatically.
-
Enforce authentication at the transport boundary – Unauthenticated requests must never reach tool handlers. Implement OAuth 2.1 with PKCE for all remote servers.
-
Instrument everything – Add OpenTelemetry spans, structured logs, and metrics from day one. Retroactive instrumentation is painful.
-
Pin SDK versions – Specify exact SDK versions in
package.jsonorrequirements.txt. Review release notes before upgrading; SDK APIs have changed significantly over time. -
Document rate limits and costs – If a tool calls an external API with costs or limits, include that information in the tool description.
-
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.
-
Plan for versioning – Assume your tool schemas will change. Design for backward‑compatible evolution. When breaking changes are unavoidable, introduce
_v2tools rather than altering existing ones.
Common MCP Server Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Overloading a server | Too many tools confuse both clients and developers. | Split into multiple focused servers. |
| Poor schema design | LLM generates invalid parameters; tool fails silently. | Strict typing; validate; provide clear error messages. |
| No authentication | Server becomes open proxy to downstream systems. | Implement OAuth 2.1 + PKCE before production. |
| Static API keys | Cannot rotate, no per‑request identity, weak security. | Replace with OAuth 2.1 + short‑lived tokens. |
| Missing observability | Cannot debug latency issues or track usage. | Add OpenTelemetry instrumentation from day one. |
| Unbounded tool execution | Tool that calls an external API without timeouts can hang indefinitely. | Set timeouts on all backend calls. |
| Registration after connection | registerTool after connect() fails silently. | Register all capabilities before starting transport. |
| Ignoring protocol version negotiation | Older clients cannot connect. | Let SDK negotiate automatically; support multiple protocol versions. |
| Hardcoded secrets | Credentials leaked in source code or logs. | Use environment variables and secret stores. |
| Zero testing | Undiscovered 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
| Tool | Description | Input Schema | Backend |
|---|---|---|---|
search_kb | Search across all knowledge sources | query: string, max_results: int (default 10) | Confluence + SharePoint unified search |
get_document | Retrieve full document by ID | doc_id: string, source: enum("confluence","sharepoint","custom") | Source‑specific fetch |
get_policy | Retrieve HR/security policy by name | policy_name: string, version: optional string | SharePoint 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:
- MCP Basics – MCP Introduction (protocol fundamentals)
- MCP Clients – MCP Client (connecting agents to MCP servers)
- MCP Tools – MCP Tools (deep dive on tool implementation)
- MCP Resources & Prompts – MCP Resources and MCP Prompts
- MCP Security – MCP Security (comprehensive security guide)
- MCP Deployment – MCP Deployment (production operations)
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.