vctrl/ktor-oauth-mcp-sample
If you are the rightful owner of ktor-oauth-mcp-sample and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A production-ready OAuth-protected MCP server for Claude and other MCP clients, built with ktor-server-mcp and ktor-server-oauth.
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):
- Go to Settings → Integrations
- Click "+ Add Custom Connector"
- Add each endpoint:
- Name:
Contact Sample→ URL:https://mcp.example.com/contact - Name:
Mom's Name Sample→ URL:https://mcp.example.com/mom
- Name:
- 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:
- Reverse proxy (recommended) - terminate SSL at nginx/Caddy/Traefik, proxy to this server on HTTP
- 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 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
- MCP client connects and triggers OAuth flow
- User completes the provision form (e.g., enters their name/contact info)
- Data is stored in a session bound to the OAuth token (or as encrypted JWT claims)
- 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 greetingmy_contact_info- Returns stored contact info (text or JSON)update_contact- Update email, phone, or companysend_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.ktto 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