Model Context Protocol – Advanced Patterns for Production By the end of this lesson, students should be able to: Testing strategy for MCP servers. Three testing layers for production MCP servers: Unit tests: Test tool handler logic independently of the MCP protocol layer. Mock the external dependencies (API calls, database queries) and verify handler behavior for valid inputs, invalid inputs, and error cases. Integration tests: Test the full MCP server using the SDK's test client utilities. Verify tool discovery (list_tools returns expected tools), tool execution (call_tool returns expected results for representative inputs), and error handling (call_tool returns correct errors for invalid inputs). End-to-end tests: Connect a real Claude Code or test MCP client to the server and verify behavior from the AI perspective – "Can Claude successfully use this tool to accomplish the task it was designed for?" The end-to-end test is where prompt injection vulnerabilities and tool description quality issues surface. Observability for MCP servers. Minimum production observability: import logging, time logger = logging.getLogger(__name__) @app.call_tool() Versioning and updates. MCP servers do not have a built-in versioning mechanism – clients connect and get the current tool list. For breaking changes (removing a tool, changing a tool's parameter schema): Maintenance workflow. Regular maintenance tasks for production MCP servers: Enroll to continue this lesson. The preview above shows the lesson objectives and opening lesson content. Enroll to view the full lesson, complete the practice work, and take the lesson quiz.
Lesson 5: Production Deployment – Testing, Monitoring, and Maintenance
Lesson Objectives
Lesson Content
async def call_tool(name: str, arguments: dict):
start = time.time()
try:
result = await _execute_tool(name, arguments)
logger.info({
"event": "tool_call",
"tool": name,
"latency_ms": int((time.time() – start) * 1000),
"result": "success"
})
return result
except Exception as e:
logger.error({
"event": "tool_call",
"tool": name,
"latency_ms": int((time.time() – start) * 1000),
"result": "error",
"error": str(e)
})
return [types.TextContent(type="text", text=f"Tool error: {str(e)}")]
