Getting Started with Model Context Protocol By the end of this lesson, students should be able to: Authentication in MCP servers. MCP servers handle their own authentication with external services – the host and client do not see API keys or credentials. The server is the trust boundary. Patterns: The server should fail loudly at startup if required credentials are missing – not silently at tool call time. Meaningful error responses. Tool handlers should return errors that Claude can communicate to the user: “ Returning the error as a TextContent block allows Claude to surface it to the user with context, rather than the session crashing on an unhandled exception. Production server patterns. Complete server checklist before deployment. A developer builds an MCP server wrapping an internal ticket system API. He implements: credential loading from environment variables, argument validation (ticket IDs must match a specific pattern), error responses as TextContent, startup validation that tests the API connection and exits with an error message if it fails, and request logging. He tests five tool calls in Claude Code – two error cases (invalid ticket ID, API timeout) and three happy path cases. All five behave correctly and return intelligible responses. He checks the deployment checklist: all items complete. Server goes to production. MCP servers that connect to internal APIs or databases have elevated trust within your infrastructure. Treat MCP server code with the same security review rigor as any code with database or API access. A vulnerability in an MCP server can be exploited via crafted Claude prompts that produce tool calls with malicious parameters – known as prompt injection. The tool argument validation in every handler is the defense against this attack vector. Log in and enroll to access lesson quizzes.
Lesson 5: Connecting Claude to External Services via MCP
Lesson Objectives
Lesson Content
os.environ.get("GITHUB_TOKEN"))python @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "search_github": try: results = github_client.search(arguments["query"]) return [types.TextContent(type="text", text=format_results(results))] except github.GithubException as e: return [types.TextContent( type="text", text=f"GitHub search failed: {e.status} {e.data.get('message', 'Unknown error')}" )] “Practical Example
Safety Notes