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

> Learn how to build MCP servers in Phase 13 of AI Engineering from Scratch. Explore FastMCP, OAuth, and OPA for robust HTTP transport, scope enforcement, and governance with a registry service.

- Repository: [Rohit Ghumare/ai-engineering-from-scratch](https://github.com/rohitg00/ai-engineering-from-scratch)
- Tags: deep-dive
- Published: 2026-06-07

---

**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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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.

```python

# 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:

```python

# 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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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.

```typescript
// 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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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.

```json
{
  "name": "internal-readonly-mcp",
  "transport": "StreamableHTTP",
  "tools": [
    {
      "name": "postgres:query:readonly",
      "scope": "postgres:query:readonly",
      "schema": { "type": "object", "properties": { "sql": { "type": "string" } } }
    }
  ]
}

```

6. **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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/main.py)) and **TypeScript** ([`index.ts`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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`](https://github.com/rohitg00/ai-engineering-from-scratch/blob/main/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.