How MCP Servers Are Built in Phase 13: FastMCP, OAuth, and Registry Governance

Phase 13 of the "AI Engineering from Scratch" curriculum teaches you to build production-ready Model Context Protocol (MCP) servers using a three-layer stack: FastMCP for HTTP transport, OAuth 2.1 for scope enforcement, and Open Policy Agent (OPA) for governance, complete with a registry service that publishes capability manifests.

The Phase 13 capstone lesson in rohitg00/ai-engineering-from-scratch demonstrates how to construct enterprise-grade MCP servers located at phases/19-capstone-projects/13-mcp-server-with-registry/. Unlike basic toy implementations, this lesson focuses on a hardened architecture that separates read-only tools from destructive operations and exposes a discoverable registry endpoint.

The Three-Layer Architecture

The lesson divides the server into three tightly coupled components that together provide a complete governance framework.

FastMCP HTTP Transport

At the foundation sits a FastMCP server that exposes tools over a StreamableHTTP transport. This implementation runs as a stateless service capable of handling concurrent requests without maintaining persistent connections. The server architecture supports two distinct personas: a read-only server for safe data retrieval and a separate destructive server for mutating operations.

OAuth 2.1 Scope Enforcement

Every incoming request undergoes OAuth 2.1 validation before reaching the tool logic. The server extracts the scopes claim from the bearer token and verifies that the requested tool’s scope is present in the token. Destructive tools carry an additional requirement: the special approved:by:human scope must be present to indicate manual authorization for potentially dangerous operations.

OPA Policy Gate and Registry

An Open Policy Agent (OPA) layer acts as the final arbiter before execution. The policy_decide function evaluates requests against predefined security policies, blocking unsafe calls even if OAuth scopes are technically valid. A separate registry service periodically crawls server endpoints, validates the .well-known/mcp-capabilities manifests, and populates a central index for platform discovery.

Language Implementations

The repository provides complete implementations in both Python and TypeScript to illustrate the architecture across different runtime environments.

Python Implementation

The primary reference implementation resides in phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py. This file contains the MCPServer class, the policy_decide validation logic, and the dispatch method that routes approved calls to concrete tool implementations.


# Build a read-only MCP server (from phases/19-capstone-projects/13-mcp-server-with-registry/code/main.py)

def build_readonly_server() -> MCPServer:
    s = MCPServer(
        name="internal-readonly-mcp",
        url="https://mcp.internal/readonly",
        tools={
            "postgres:query:readonly": {
                "schema": {"type": "object", "properties": {"sql": {"type": "string"}}},
                "scope": "postgres:query:readonly",
            },
        },
    )
    return s

The policy_decide function implements the security gate:


# Policy decision function enforcing OAuth scopes and human approval

def policy_decide(server: MCPServer, tool: str, token: Token, args: dict) -> bool:
    if tool not in server.tools:
        raise PermissionError("Unknown tool")
    required = server.tools[tool]["scope"]
    if required not in token.scopes:
        raise PermissionError("Missing required scope")
    if tool.startswith("jira:create") and "approved:by:human" not in token.scopes:
        raise PermissionError("Destructive tool needs human approval")
    return True

TypeScript Implementation

For pedagogical contrast, the lesson includes a minimal hand-rolled version in phases/19-capstone-projects/13-mcp-server-with-registry/code/ts/src/index.ts. This implementation uses a custom JSON-RPC stub rather than an SDK, helping learners understand the underlying wire protocol.

// Minimal hand-rolled MCP entry point
process.stdout.write(
  "PHASE 19 LESSON 13 - internal MCP server (TypeScript, no SDK)\n"
);
// JSON-RPC request parsing and routing logic follows

Step-by-Step Server Construction

The Phase 13 lesson breaks down the construction process into six discrete stages documented in phases/19-capstone-projects/13-mcp-server-with-registry/docs/en.md.

  1. Scaffold the server — Instantiate the MCPServer class with a unique name, URL, and a dictionary mapping tool names to their JSON schemas.

  2. Define tools — Declare each tool with a strict JSON schema describing arguments and return types. Assign read-only tools (e.g., postgres:query:readonly) to the safe server and mutating tools (e.g., jira:create) to the destructive server.

  3. Add OAuth validation — Implement a helper to extract the scopes claim from the bearer token and verify the presence of the tool-specific scope before proceeding.

  4. Wrap the policy gate — Ensure the request dispatcher calls policy_decide before executing any tool logic. Rejected calls return a PermissionError immediately.

  5. Publish the capability manifest — Implement a GET /.well-known/mcp-capabilities endpoint that returns a JSON manifest listing all tools, required scopes, and transport URLs.

{
  "name": "internal-readonly-mcp",
  "transport": "StreamableHTTP",
  "tools": [
    {
      "name": "postgres:query:readonly",
      "scope": "postgres:query:readonly",
      "schema": { "type": "object", "properties": { "sql": { "type": "string" } } }
    }
  ]
}
  1. Run conformance tests — Execute the mcp-conformance-tests suite included in the lesson to verify StreamableHTTP compliance, scope enforcement, and OPA policy behavior.

Summary

  • Phase 13 teaches production-grade MCP server construction through the rohitg00/ai-engineering-from-scratch repository.
  • The architecture combines FastMCP for HTTP transport, OAuth 2.1 for authentication, and OPA for policy enforcement.
  • Destructive tools require the approved:by:human scope in addition to standard OAuth scopes.
  • Servers expose a .well-known/mcp-capabilities endpoint for registry discovery and governance.
  • Complete reference implementations are available in Python (main.py) and TypeScript (index.ts) within the phases/19-capstone-projects/13-mcp-server-with-registry/ directory.

Frequently Asked Questions

What transport protocol does the Phase 13 MCP server use?

The implementation uses StreamableHTTP as its transport protocol. This stateless HTTP-based approach allows the server to handle requests without maintaining persistent WebSocket connections, making it suitable for serverless and horizontally scalable deployments.

How does the server prevent unauthorized destructive operations?

The policy_decide function in main.py enforces a two-layer check. First, it verifies the OAuth 2.1 bearer token contains the specific scope required for the requested tool. Second, for destructive tools, it checks for the approved:by:human scope, ensuring that a human explicitly authorized the operation before execution proceeds.

Where is the registry capability documented?

The registry contract and governance architecture are documented in phases/19-capstone-projects/13-mcp-server-with-registry/docs/en.md. The final consolidated skill output, including production deployment guidance, appears in phases/19-capstone-projects/13-mcp-server-with-registry/outputs/skill-mcp-server.md.

Are there conformance tests for the implementation?

Yes. The lesson ships a reference test suite called mcp-conformance-tests that validates the StreamableHTTP transport, OAuth scope checking, and OPA policy gates. Passing this suite is required to complete the Phase 13 capstone and generate the verified skill artifact.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →