Skip to content

Production-ready OAuth-protected MCP server template for Claude and other MCP clients. Built with ktor-server-oauth and ktor-server-mcp.

License

Notifications You must be signed in to change notification settings

vctrl/ktor-oauth-mcp-sample

Repository files navigation

ktor-oauth-mcp-sample

A production-ready OAuth-protected MCP server for Claude and other MCP clients.

Built with ktor-server-mcp and ktor-server-oauth.

Works with Claude

This is a remote MCP server (HTTP/SSE transport, not stdio). It works out of the box with any MCP client that supports network-based servers:

  • Open registration - clients register automatically via OAuth 2.0 Dynamic Client Registration (public clients with PKCE)
  • Reverse proxy ready - designed to work behind nginx, Caddy, Traefik, etc. without modification

Adding to Claude

Once deployed with HTTPS (e.g., at https://mcp.example.com), add these connectors:

Claude Desktop / Web / Mobile (Pro, Max, Team, Enterprise):

  1. Go to Settings → Integrations
  2. Click "+ Add Custom Connector"
  3. Add each endpoint:
    • Name: Contact Sample → URL: https://mcp.example.com/contact
    • Name: Mom's Name Sample → URL: https://mcp.example.com/mom
  4. OAuth login prompts automatically on first use

Claude Code CLI:

claude mcp add --transport http contact https://mcp.example.com/contact
claude mcp add --transport http mom https://mcp.example.com/mom

Then use /mcp inside Claude Code to authenticate.

SSL Required

MCP clients like Claude require HTTPS. Options:

  1. Reverse proxy (recommended) - terminate SSL at nginx/Caddy/Traefik, proxy to this server on HTTP
  2. Ktor SSL - configure directly in application.conf:
    ktor {
        deployment {
            sslPort = 8443
            ssl {
                keyStore = /path/to/keystore.jks
                keyAlias = mykey
                keyStorePassword = changeit
                privateKeyPassword = changeit
            }
        }
    }

Quick Start

Both options start the server at http://localhost:8080 and work identically behind a reverse proxy with SSL for production use with Claude.

Gradle

./gradlew run

Docker

docker compose -f deployment/docker-compose.yml up

See deployment/README.md for more deployment options.

Features

  • OAuth 2.0 with dynamic client registration (RFC 7591)
  • Provision flow for collecting user credentials during auth
  • Session-bound storage tied to OAuth tokens
  • MCP tools with access to user session data

How It Works

  1. MCP client connects and triggers OAuth flow
  2. User completes the provision form (e.g., enters their name/contact info)
  3. Data is stored in a session bound to the OAuth token (or as encrypted JWT claims)
  4. MCP tools access session data via call.sessions.get<T>() or JWT claims

Included Examples

This sample includes two MCP endpoints demonstrating different patterns:

/contact - Session Storage

Uses bearer-bound sessions to store contact information.

Tools:

  • greet_me - Returns a personalized greeting
  • my_contact_info - Returns stored contact info (text or JSON)
  • update_contact - Update email, phone, or company
  • send_test_email - Simulates sending email to stored address

/mom - Encrypted JWT Claims

Stores data as encrypted claims in the JWT token itself.

Tools:

  • your_moms_name - Returns the name stored in encrypted JWT claim

Customization

  • Edit Application.kt to add your own tools and endpoints
  • Modify templates in src/main/resources/templates/ for provision forms
  • Choose between session storage or encrypted JWT claims based on your needs

Project Structure

src/main/kotlin/com/example/
└── Application.kt           # Server, routes, and tools

src/main/resources/templates/
├── provision.html           # Contact info provision form
└── mom-provision.html       # Mom's name provision form

License

Apache 2.0

About

Production-ready OAuth-protected MCP server template for Claude and other MCP clients. Built with ktor-server-oauth and ktor-server-mcp.

Topics

Resources

License

Stars

Watchers

Forks