Model Context Protocol (MCP) and Agentic Tooling Handbook
A production-ready handbook for beginner-to-intermediate engineers building secure, standardized, tool-enabled AI systems with MCP across Claude, Cursor, and custom hosts.
Table of Contents
This handbook is organized to explain the "why" first, then implementation details, then enterprise guardrails and anti-patterns.
Module 1: The AI Integration Problem and What MCP Solves
The N x M Problem
Most teams start with one AI model and one internal system. Then reality expands: another model, another knowledge source, another workflow. Without a standard, every model-to-system pairing needs custom glue code. If you have N models and M systems, your integration burden trends toward N multiplied by M.
Example: Claude, OpenAI, and a self-hosted model each need separate connectors for Slack, GitHub, Jira, Confluence, PostgreSQL, and internal APIs. This creates duplicated auth logic, inconsistent error handling, and expensive maintenance.
What is MCP?
Model Context Protocol (MCP), introduced by Anthropic, is a protocol standard that defines how AI hosts connect to external capabilities in a consistent way. It separates AI reasoning from enterprise integration details.
Instead of building one-off adapters for every host, teams expose capabilities through MCP servers. Any MCP-compatible host can consume those capabilities with the same protocol semantics.
The USB-C Analogy
USB-C standardized how laptops connect to peripherals. Before USB-C, each device needed a different cable and often a different port. MCP plays a similar role for AI integration: one protocol shape for many hosts and many tools.
Enterprise impact: your "Enterprise Postgres MCP Server" can be plugged into Claude Desktop, Cursor, or a custom internal chat application with minimal host-specific rewrite.
Module 2: MCP Architecture (Host, Client, Server)
The Host
The host is the AI application where users interact, such as Claude Desktop, Cursor, or your internal assistant portal.
The Client
The MCP client lives inside the host and manages protocol messaging, capability discovery, and request routing. Think of it as the protocol driver.
The Server
An MCP server is a lightweight program that exposes a focused capability surface. Example: a "billing-mcp" server that can retrieve invoice status and update payment reminders.
Capability Types
- Prompts: reusable prompt templates that guide host behavior.
- Resources: readable context objects (documents, policies, metadata).
- Tools: executable actions (query systems, trigger workflows, mutate state).
Module 3: The Rise of Agentic AI and Why Tools Matter
Chatbots vs Agents
Classic chatbots mostly generate text. Agentic systems can decide to invoke tools, gather evidence, and perform actions. This is the shift from "talking about work" to "doing work."
Why Tools Matter in Enterprise AI
Enterprise value appears when AI can securely interact with real systems: reading Jira incidents, querying production-safe datasets, creating PR comments, or triggering CI/CD checks. Without tools, enterprise AI stays informational and often shallow.
Tools are the AI system's eyes and hands. Resources provide read context. Tools perform constrained actions. Together, they deliver measurable operational outcomes.
Module 4: Building an Enterprise MCP Server
The snippet below uses the official Python MCP SDK pattern with FastMCP. It shows a foundational server structure suitable for local development via stdio.
# server.py
# Foundation MCP server with clear metadata and typed handlers.
from mcp.server.fastmcp import FastMCP
# The server name should be stable and descriptive for enterprise observability.
server = FastMCP(name="enterprise-context-server")
@server.prompt()
def summarize_incident_prompt(service_name: str) -> str:
# Reusable prompt template exposed as an MCP capability.
return (
"You are an SRE assistant. Summarize incident risk for service "
f"'{service_name}' in 5 bullet points with business impact."
)
if __name__ == "__main__":
# Local development transport. In production you can use HTTP/SSE patterns.
server.run()
Exposing a Resource (Mock Enterprise Wiki)
Use resources for read-only context objects. Keep them scoped and intentional.
# resource_example.py
# Expose internal wiki content as a resource URI.
from mcp.server.fastmcp import FastMCP
server = FastMCP(name="enterprise-wiki-server")
WIKI_PAGES = {
"incident-sev1-runbook": """
# Sev1 Incident Runbook
1. Acknowledge pager within 5 minutes.
2. Open incident bridge and assign commander.
3. Freeze risky deploys.
4. Communicate status every 15 minutes.
""".strip()
}
@server.resource("wiki://policies/{page_id}")
def get_wiki_page(page_id: str) -> str:
# Targeted lookup prevents exposing an entire content repository blindly.
if page_id not in WIKI_PAGES:
return "Page not found."
return WIKI_PAGES[page_id]
if __name__ == "__main__":
server.run()
Module 5: Defining and Using Tools via MCP
Tool Design Principle
Tools should represent precise business actions, not broad data dumps. The model chooses tools based on descriptions and input schemas. If your tool contract is vague, tool behavior becomes unreliable.
TypeScript Tool with Strict Parameter Schema
This snippet uses the official TypeScript MCP SDK with a strict parameter contract. The schema tells the model exactly what inputs are legal.
// billing-server.ts
// Enterprise MCP tool server with strict inputs and structured outputs.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Mock billing source. In production this could be a service call behind RBAC.
const BILLING_DB: Record<string, { status: "paid" | "past_due" | "on_hold"; balanceUsd: number }> = {
"CUST-1001": { status: "paid", balanceUsd: 0 },
"CUST-2009": { status: "past_due", balanceUsd: 1420.55 },
"CUST-9912": { status: "on_hold", balanceUsd: 350.0 }
};
const server = new McpServer({
name: "enterprise-billing-server",
version: "1.0.0"
});
server.tool(
"get_customer_billing_status",
{
customerId: z
.string()
.regex(/^CUST-\d{4}$/)
.describe("Enterprise customer ID in format CUST-1234"),
includeLedgerSummary: z
.boolean()
.default(false)
.describe("Include short ledger summary in response")
},
async ({ customerId, includeLedgerSummary }) => {
const record = BILLING_DB[customerId];
if (!record) {
// Structured error text helps the model reason correctly about failures.
return {
content: [
{
type: "text",
text: JSON.stringify({
ok: false,
error: "CUSTOMER_NOT_FOUND",
customerId
})
}
]
};
}
const result = {
ok: true,
customerId,
status: record.status,
balanceUsd: record.balanceUsd,
ledgerSummary: includeLedgerSummary
? "Last 3 invoices: INV-4432 paid, INV-4490 past_due, INV-4501 open"
: undefined
};
return {
content: [{ type: "text", text: JSON.stringify(result) }]
};
}
);
const transport = new StdioServerTransport();
await server.connect(transport);
Why JSON Schema Strictness Matters
- Reliability: the model knows exact parameter shape, constraints, and optional fields.
- Security: validation blocks malformed or unexpected input before business logic runs.
- Interoperability: consistent contracts improve behavior across Claude, Cursor, and custom hosts.
Execution Flow: How Tool Calls Actually Happen
- The user asks a question that requires external action or data.
- The host model decides a tool is needed based on tool description and schema.
- The MCP client packages the tool call request and routes it to the MCP server.
- The MCP server validates inputs, executes code, and returns structured output.
- The host model incorporates the tool result into its final response.
Module 6: Enterprise Security and Deployment Patterns
Security Boundaries
MCP creates a major enterprise security advantage: LLM providers do not need direct access to backend credentials. Secrets stay in your environment, and MCP servers execute behind your trust boundaries.
- Credential isolation: API keys, DB passwords, and tokens remain inside enterprise infrastructure.
- Least privilege: each MCP server can be scoped to minimal actions.
- Auditability: tool invocation logs can be routed to SIEM with user and correlation IDs.
Deployment Patterns
Local development: stdio transport is simple, fast, and ideal for developer workflows.
Production: MCP servers often run as dedicated services using HTTP/SSE, or as sidecars in Kubernetes where network policy and workload identity can be tightly controlled.
# k8s-sidecar-pattern.yaml
# App host and MCP server sidecar in one pod for low-latency local communication.
apiVersion: v1
kind: Pod
metadata:
name: enterprise-ai-host
spec:
containers:
- name: host-app
image: ghcr.io/example/internal-chat-host:1.2.0
env:
- name: MCP_BILLING_ENDPOINT
value: http://127.0.0.1:8081/sse
- name: billing-mcp-sidecar
image: ghcr.io/example/mcp-billing-server:1.0.0
ports:
- containerPort: 8081
securityContext:
runAsNonRoot: true
readOnlyRootFilesystem: true
// sse-server.ts
// Production-oriented transport example for remote host connectivity.
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const app = express();
const server = new McpServer({ name: "enterprise-mcp-sse", version: "1.0.0" });
app.get("/sse", async (_req, res) => {
const transport = new SSEServerTransport("/messages", res);
await server.connect(transport);
});
app.post("/messages", express.json(), async (req, res) => {
// Route incoming protocol messages to transport/session manager in real deployments.
res.status(202).json({ accepted: true });
});
app.listen(8081, () => {
console.log("MCP SSE server listening on :8081");
});
Module 7: Common Pitfalls and Anti-Patterns
1. Overloading the Tool Description
Mistake: writing vague descriptions like "handles billing stuff".
Why it fails: the model cannot reliably infer when to call the tool, causing missed calls or wrong calls.
Fix: describe trigger conditions, input expectations, and output semantics precisely.
2. Ignoring Error Handling
Mistake: returning plain success-like text even on failure, or throwing unstructured exceptions.
Why it fails: the model may interpret ambiguous failures as success and produce false confidence.
Fix: return structured error payloads with codes, user-safe messages, and remediation hints.
3. Broad Data Exposure
Mistake: exposing an entire database as a resource.
Why it fails: excessive data exposure increases privacy risk and allows accidental overreach.
Fix: expose narrow, permission-scoped tools such as get_customer_invoice(customerId) rather than raw table access.
Production Checklist
- Use strict schemas for all tool inputs and outputs.
- Implement authN/authZ at the MCP server boundary.
- Log all tool invocations with correlation IDs and redact sensitive fields.
- Apply least privilege to every backend credential and identity.
- Validate that the same MCP server works across at least two hosts.
If you adopt MCP as a protocol standard early, your AI integration layer remains portable, auditable, and secure as model providers and host frameworks evolve.