@nabyltmt/teachmeto-mcp
v1.0.8
Published
Model Context Protocol server for TeachMeTo API - Access 76+ TeachMeTo tools via MCP
Maintainers
Readme
TeachMeTo MCP Full-Stack Application
A comprehensive three-tier application featuring Claude AI with full TeachMeTo platform integration:
- MCP Server: TeachMeTo API integration with OAuth 2.1 authentication (103 tools, 76 enabled by default)
- FastAPI Backend: Claude Sonnet 4 + MCP integration with LangSmith tracing
- Next.js Frontend: React chat interface with TeachMeTo phone/OTP authentication
Architecture Overview
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │ │ Backend │ │ MCP Server │
│ (Next.js) │ │ (FastAPI) │ │ (Node.js) │
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
│ • React Chat UI │ │ • Claude Sonnet │ │ • OAuth 2.1 │
│ • Phone/OTP Auth│◄──►│ • HTTP MCP │◄──►│ • Token Service │
│ • JWT Storage │ │ • LangSmith │ │ • 103 API Tools │
│ • TailwindCSS │ │ • FastAPI │ │ • Registry │
└─────────────────┘ └─────────────────┘ └─────────────────┘Key Features
MCP Server (Node.js/TypeScript)
- 103 TeachMeTo API Tools across 10 categories (76 enabled by default, 3 disabled for security)
- Registry-Based Architecture - Auto-discovery and registration with definitive operation type mapping
- OAuth 2.1 Authentication - Full MCP specification compliance
- User Delegation - Phone/OTP authentication with JWT tokens
- Rate Limiting - Configurable per-minute and burst limits
- Type-Safe - Full TypeScript with strict mode
Backend (Python/FastAPI)
- Claude Sonnet 4 - Latest AI model integration
- HTTP MCP Client - Direct communication with MCP server
- LangSmith Tracing - Full observability and debugging
- JWT Authentication - User-level permissions
- Conversation Memory - Track JWT across tool calls
Frontend (Next.js/React)
- Phone/OTP Authentication - Seamless TeachMeTo login
- Real-time Chat - Streaming responses from Claude
- State Management - Zustand for auth and conversation state
- Responsive Design - Mobile-first with Tailwind CSS
Authentication Flow
The application implements a complete authentication flow from frontend through to the TeachMeTo API:
┌─────────────┐
│ User │
└──────┬──────┘
│ 1. Enters phone number
▼
┌─────────────────────┐
│ Frontend (Next) │
│ ┌───────────────┐ │
│ │ AuthForm.tsx │ │ 2. POST /auth/request-otp
│ └───────┬───────┘ │ {phoneNumber: "+1..."}
└───────────┼─────────┘
▼
┌─────────────────────┐
│ Backend (FastAPI) │ 3. Forward to MCP Server
│ ┌───────────────┐ │ POST /tools/request_otp
│ │ main.py │ │
│ └───────┬───────┘ │
└───────────┼─────────┘
▼
┌─────────────────────┐
│ MCP Server │ 4. Call TeachMeTo API
│ ┌───────────────┐ │ POST /auth/request-otp
│ │ auth.ts │ │
│ └───────┬───────┘ │
└───────────┼─────────┘
▼
┌─────────────────────┐
│ TeachMeTo API │ 5. Send SMS with OTP
│ ┌───────────────┐ │
│ │ Send SMS │ │
│ └───────────────┘ │
└─────────────────────┘
User receives OTP: "366002"
┌─────────────┐
│ User │
└──────┬──────┘
│ 6. Enters OTP code
▼
┌─────────────────────┐
│ Frontend │ 7. POST /auth/verify-otp
│ ┌───────────────┐ │ {phoneNumber, OTP: "366002"}
│ │ AuthForm │ │
│ └───────┬───────┘ │
└───────────┼─────────┘
▼
┌─────────────────────┐
│ Backend │ 8. Forward to MCP
│ ┌───────────────┐ │ POST /tools/verify_otp
│ │ claude_service│ │
│ └───────┬───────┘ │
└───────────┼─────────┘
▼
┌─────────────────────┐
│ MCP Server │ 9. Verify with TeachMeTo
│ ┌───────────────┐ │ POST /auth/verify-otp
│ │ auth.ts │ │
│ └───────┬───────┘ │ Returns JWT token
└───────────┼─────────┘
│
│ 10. JWT: "eyJhbGc..."
▼
┌─────────────────────┐
│ Backend │ 11. Store JWT for session
│ ┌───────────────┐ │ Extract from response
│ │ Conversation │ │ Use for future calls
│ └───────┬───────┘ │
└───────────┼─────────┘
│ 12. Return to Frontend
▼
┌─────────────────────┐
│ Frontend │ 13. Store JWT in state
│ ┌───────────────┐ │ User is authenticated
│ │ useAuthStore │ │ Show chat interface
│ └───────────────┘ │
└─────────────────────┘
Subsequent Tool Calls:
┌─────────────┐
│ User │ "Get my profile"
└──────┬──────┘
▼
┌─────────────────────┐
│ Frontend │ POST /chat
│ │ {message: "Get my profile"}
└──────┬──────────────┘
▼
┌─────────────────────┐
│ Backend │ Claude decides: student_get_profile
│ ┌───────────────┐ │ POST /tools/student_get_profile
│ │ Claude+MCP │ │ Header: Authorization: Bearer JWT
│ └───────┬───────┘ │
└───────────┼─────────┘
▼
┌─────────────────────┐
│ MCP Server │ Validate JWT token
│ ┌───────────────┐ │ Extract userId from JWT
│ │ Registry │ │ Call TeachMeTo API
│ │ executeTool │ │ GET /student/profile
│ └───────┬───────┘ │ Header: Authorization: Bearer JWT
└───────────┼─────────┘
▼
┌─────────────────────┐
│ TeachMeTo API │ Verify JWT signature
│ ┌───────────────┐ │ Check user permissions
│ │ Profile Data │ │ Return user profile
│ └───────┬───────┘ │
└───────────┼─────────┘
│ {id, name, email, ...}
▼
[Returns through chain back to User]Key Points:
- User Delegation: Users authenticate with their own phone/OTP
- OAuth 2.1 Compliance: Full MCP specification-compliant authentication flow
- Two-Tier Tokens:
- TeachMeTo JWT: Long-lived (30 days) user identity token from TeachMeTo API
- MCP Access Token: Short-lived (15 minutes) OAuth 2.1 token signed with RS256
- Token Exchange: Backend exchanges TeachMeTo JWT for MCP access token per tool call
- Embedded JWT Pattern: MCP tokens contain embedded TeachMeTo JWT for API calls
- Bearer Authentication: All API calls use user's JWT (not admin keys)
- RS256 Signing: 2048-bit RSA asymmetric key signing for MCP tokens
- Stateless: No server-side token storage, all validation via signature verification
- Automatic Tool Selection: Claude decides which tools to call based on user intent
OAuth 2.1 Authentication Details
The application implements full MCP specification-compliant OAuth 2.1 authentication:
Token Exchange Flow
┌──────────────────────────────────────────────────────────────┐
│ Token Exchange Flow │
└──────────────────────────────────────────────────────────────┘
Backend Startup:
┌─────────────────────┐
│ Backend (FastAPI) │ 1. Register as OAuth client
│ HTTPMCPClient │ POST /oauth/register
└──────────┬──────────┘ {client_name: "TeachMeTo Backend"}
│
▼
┌─────────────────────┐
│ MCP Server │ 2. Return client credentials
│ OAuth Server │ {client_id, client_secret}
└─────────────────────┘
Per Tool Call:
┌─────────────────────┐
│ Backend │ 3. Exchange TeachMeTo JWT
│ exchange_token() │ POST /oauth/token
│ │ {
│ │ grant_type: "teachmeto_jwt",
│ │ teachmeto_jwt: "eyJhbGc...",
│ │ client_id: "...",
│ │ client_secret: "...",
│ │ resource: "teachmeto://api",
│ │ scope: "tools:read tools:write"
│ │ }
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ MCP Server │ 4. Validate TeachMeTo JWT signature
│ Token Service │ jwt.verify(teachmeto_jwt, PUBLIC_KEY)
│ │
│ │ 5. Create MCP access token payload:
│ │ {
│ │ iss: "http://localhost:3001",
│ │ sub: userId,
│ │ aud: ["teachmeto://api"],
│ │ client_id: "...",
│ │ exp: now + 900, // 15 minutes
│ │ iat: now,
│ │ embedded_jwt: "eyJhbGc...",
│ │ scope: "tools:read tools:write",
│ │ role: "student"
│ │ }
│ │
│ │ 6. Sign with RS256 private key
│ │ jwt.sign(payload, PRIVATE_KEY, RS256)
└──────────┬──────────┘
│
│ 7. Return MCP access token
▼
┌─────────────────────┐
│ Backend │ 8. Call tool with MCP token
│ call_tool() │ POST /tools/student_get_profile
│ │ Header: Authorization: Bearer <MCP_TOKEN>
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ MCP Server │ 9. Validate MCP token signature
│ executeTool() │ jwt.verify(mcp_token, PUBLIC_KEY)
│ │
│ │ 10. Extract embedded TeachMeTo JWT
│ │ Verify embedded JWT not expired
│ │
│ │ 11. Call TeachMeTo API
│ │ GET /student/profile
│ │ Header: Authorization: Bearer <TEACHMETO_JWT>
└─────────────────────┘Key Security Features
RS256 Asymmetric Signing:
- MCP server generates 2048-bit RSA key pair on startup
- Private key signs MCP tokens
- Public key verifies MCP tokens
- No shared secrets between components
Short-lived Tokens:
- MCP access tokens expire in 15 minutes (OAuth 2.1 best practice)
- TeachMeTo JWTs remain valid for 30 days
- Backend exchanges tokens per tool call to maintain security
Audience Binding (RFC 8707):
- MCP tokens bound to resource
teachmeto://api - Prevents token misuse across different resources
- Validated on every tool call
- MCP tokens bound to resource
Embedded JWT Verification:
- MCP token contains original TeachMeTo JWT
- Both tokens validated on each request
- TeachMeTo JWT used for actual API calls
- If either token invalid/expired, request rejected
OAuth 2.1 Compliance:
- Dynamic client registration (RFC 7591)
- Custom grant type
teachmeto_jwt - Bearer token authorization
- Proper error responses
Why This Architecture?
- MCP Specification Compliance: Follows MCP authorization spec exactly
- Security: Short-lived tokens limit exposure, RS256 prevents tampering
- Scalability: Stateless JWTs, no server-side session storage
- Flexibility: Supports multiple OAuth clients (future: mobile apps, CLI)
- API Compatibility: TeachMeTo API receives familiar JWT tokens
MCP Server Architecture
The MCP server uses a registry-based architecture that eliminates complex routing logic:
Registry System
// src/registry/tool-registry.ts
export class ToolRegistry {
private tools: Map<string, ToolMetadata> = new Map();
// Register a module with all its tools
registerModule(module: ToolModule): void {
const tools = module.createTools();
for (const tool of tools) {
this.tools.set(tool.name, {
tool,
handler: module.handler,
requiresAuth: module.requiresAuth,
category: module.category
});
}
}
// Direct lookup - no pattern matching
getTool(toolName: string): ToolMetadata | undefined {
return this.tools.get(toolName);
}
}Tool Module Pattern
Each tool category exports a module with metadata:
// src/tools/users.ts
export const userToolModule: ToolModule = {
createTools: createUserTools, // Returns Tool[] definitions
handler: handleUserTool, // Actual implementation
requiresAuth: true, // Requires JWT authentication
category: 'users' // Category for filtering
};Benefits
- No Routing Logic - Direct lookup by tool name
- Type-Safe - Full TypeScript inference
- Auto-Discovery - Modules self-register
- Easy Extension - Add new module to
src/registry/modules.ts - Category Filtering - Support for
DISABLED_TOOLSandDISABLED_CATEGORIES - Single Source of Truth - Tools in registry = tools exposed = tools executable
Tool Categories (103 total, 76 enabled by default)
- Authentication (10 tools): request_otp, verify_otp, OAuth management (3 OAuth tools disabled by default)
- Users (5 tools): Profile management for students and coaches (2 admin tools disabled by default)
- Bookings (18 tools): Lesson booking, scheduling, reviews
- Listings (7 tools): Search services by location, skill, rating
- Locations (18 tools): City/venue services, geographic data
- Payments (3 tools): Checkout, Stripe webhooks
- Subscriptions (16 tools): FFL, conversions, interventions
- Skills (3 tools): Skill lookup, badges
- Students (17 tools): Student-specific operations (profile, lessons, home screen)
- Admin (6 tools): System status, user enablement
Tool Filtering
The MCP server supports flexible tool filtering via environment variables for controlling which tools are available:
Environment Variables:
DISABLE_WRITE_OPERATIONS- When set totrue, disables all write operations (POST, PUT, DELETE, PATCH endpoints). Read operations (GET) remain available. Perfect for production environments where you want to limit data modifications.DISABLED_TOOLS- Comma-separated list of specific tool names to disable (e.g.,create_booking,cancel_booking)DISABLED_CATEGORIES- Comma-separated list of entire categories to disable (e.g.,admin,analytics)
Operation Types: Each tool is tagged as either:
read- GET operations that only retrieve data (safe to run anytime)write- POST/PUT/DELETE/PATCH operations that modify data (can be disabled withDISABLE_WRITE_OPERATIONS=true)
Examples:
# Conservative production setup - disable all writes
DISABLE_WRITE_OPERATIONS=true npm run dev
# Disable specific dangerous tools
DISABLED_TOOLS=cancel_booking,delete_user npm run dev
# Disable entire categories
DISABLED_CATEGORIES=admin,analytics npm run dev
# Combine multiple filters
DISABLE_WRITE_OPERATIONS=true DISABLED_CATEGORIES=admin npm run devDefault Configuration: By default, the following tools are disabled for security:
google_auth_student- OAuth integration (requires additional setup)google_auth_coach- OAuth integration (requires additional setup)google_auth_existing- OAuth integration (requires additional setup)list_users- Admin function (requires admin privileges)get_user- Admin function (requires admin privileges)
This results in 76/79 tools enabled out of the box. All existing tools default to read operations for backward compatibility.
Quick Start
📦 npm Installation (Recommended for MCP Clients)
The easiest way to use the TeachMeTo MCP server with Claude Desktop, Cursor, or other MCP clients:
# Test without installation
npx @nabyltmt/teachmeto-mcp
# Or install globally
npm install -g @nabyltmt/teachmeto-mcpConfiguration for Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"teachmeto": {
"command": "npx",
"args": ["-y", "@nabyltmt/teachmeto-mcp"],
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "-----BEGIN CERTIFICATE-----\\nMIIFeTCCA2GgAwIBAgIUfvXD0peT...\\n-----END CERTIFICATE-----",
"LOG_LEVEL": "info",
"RATE_LIMIT_RPM": "100",
"RATE_LIMIT_BURST": "10"
}
}
}
}Important Notes:
- Replace
TEACHMETO_JWT_PUBLIC_KEYwith your actual TeachMeTo JWT certificate - Use
\\n(double backslash) for newlines in JSON configuration - Restart Claude Desktop after configuration changes
- The
-yflag auto-confirms npx prompts
Available Environment Variables:
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| TEACHMETO_API_URL | ✅ Yes | - | TeachMeTo API base URL (https://api.teachme.to) |
| TEACHMETO_JWT_PUBLIC_KEY | ✅ Yes | - | JWT verification certificate (X.509 format) |
| LOG_LEVEL | No | info | Logging level: debug, info, warn, error |
| RATE_LIMIT_RPM | No | 100 | Requests per minute limit |
| RATE_LIMIT_BURST | No | 10 | Burst request limit (requests per second) |
| PORT | No | 3001 | HTTP server port (for HTTP mode) |
| NODE_ENV | No | development | Environment: development, production |
| DISABLE_WRITE_OPERATIONS | No | false | Set to true to disable all write operations (POST/PUT/DELETE/PATCH) |
| DISABLED_TOOLS | No | - | Comma-separated list of tool names to disable (e.g., cancel_booking,delete_user) |
| DISABLED_CATEGORIES | No | - | Comma-separated list of categories to disable (e.g., admin,analytics) |
Tool Filtering Examples:
// Conservative production setup - read-only mode
{
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "...",
"DISABLE_WRITE_OPERATIONS": "true"
}
}// Disable specific tools
{
"env": {
"DISABLED_TOOLS": "cancel_booking,delete_user,admin_disable_user"
}
}// Disable entire categories
{
"env": {
"DISABLED_CATEGORIES": "admin,analytics"
}
}See .env.example for complete configuration reference.
For Cursor, Cline, Continue, and other MCP clients, see the MCP Client Configuration section below.
🚀 AWS Lambda Deployment (Production)
Current Production Deployments (us-west-1):
- MCP Server:
mcp_http_serverLambda function with Function URL - Backend API:
mcp_demo_backendLambda function with Function URL - Frontend: Deployed to Vercel
See CLAUDE.md for detailed deployment URLs and troubleshooting
Prerequisites for Lambda Deployment
# 1. Install AWS CLI v2
# macOS: brew install awscli
# or download from: https://aws.amazon.com/cli/
# 2. Install Docker Desktop (for backend deployment)
# Download from: https://www.docker.com/products/docker-desktop/
# 3. Configure AWS credentials
aws configure
# Enter your AWS Access Key ID, Secret Access Key, and region (us-west-1)
# 4. Create environment files
cp .env.example .env
cp backend/.env.example backend/.env
# 5. Set required environment variables in .env files
# See "Environment Configuration" section belowUnified Deployment (Recommended)
The easiest way to deploy both MCP Server and Backend is using the unified deployment script:
# Make script executable (first time only)
chmod +x deploy-all.sh
# Run unified deployment
./deploy-all.sh
# The script will prompt you with options:
# 1) MCP Server only
# 2) Backend only
# 3) Both (MCP Server + Backend) [default]What the script does:
- ✅ Checks prerequisites (Docker running, AWS CLI configured)
- ✅ Builds and deploys MCP Server to Lambda (~52MB)
- ✅ Builds and deploys Backend to Lambda using Docker (~17MB)
- ✅ Automatically configures Backend with MCP Server URL
- ✅ Tests both deployments (/health endpoints)
- ✅ Displays deployment URLs and monitoring commands
Example output:
$ ./deploy-all.sh
========================================
TeachMeTo MCP - Full Deployment
========================================
✅ Docker is running
✅ AWS CLI found
✅ AWS credentials configured (Account: 771417188474)
What would you like to deploy?
1) MCP Server only
2) Backend only
3) Both (MCP Server + Backend)
Enter choice [1-3] (default: 3): 3
========================================
Deploying MCP Server to Lambda
========================================
[... MCP deployment logs ...]
========================================
Deploying Backend to Lambda
========================================
[... Backend deployment logs ...]
========================================
✅ Deployment Complete!
========================================
MCP Server URL:
https://oqderrsqrzi6curhigxchsqotm0lxxwa.lambda-url.us-west-1.on.aws/
Backend URL:
https://xxxxxx.lambda-url.us-west-1.on.aws/
📊 Monitoring Commands:
MCP Server Logs:
aws logs tail /aws/lambda/mcp_http_server --follow --region us-west-1
Backend Logs:
aws logs tail /aws/lambda/mcp_demo_backend --follow --region us-west-1
✅ All deployments successful!Troubleshooting Unified Deployment:
ResourceConflictException - Lambda update already in progress
- Wait 2-3 minutes for previous update to complete
- Run script again
Missing ANTHROPIC_API_KEY - Backend deployment fails
- Add
ANTHROPIC_API_KEY=sk-ant-api03-your-key-heretobackend/.env - Run script again
- Add
Docker not running - Backend deployment fails
- Start Docker Desktop
- Run script again
Deploy MCP Server to Lambda (Individual)
# Deploy MCP Server
./scripts/deploy-mcp-lambda.sh
# The script will:
# 1. Build TypeScript to JavaScript
# 2. Create deployment package (~52MB with node_modules)
# 3. Update Lambda function code
# 4. Update handler to build/lambda-handler.handler
# 5. Test deployment (/health and /tools endpoints)
# 6. Save deployment info to mcp-deployment-info.json
# Verify deployment
curl <MCP_SERVER_URL>/health
curl <MCP_SERVER_URL>/tools | jq '.tools | length'
# Should return: 103Deploy Backend to Lambda
# Deploy Backend (requires Docker)
./scripts/deploy-backend-lambda.sh
# The script will:
# 1. Build Docker image with AWS Lambda Python 3.11 base
# 2. Install dependencies with correct Linux architecture
# 3. Create deployment package (~17MB)
# 4. Update Lambda function code
# 5. Update environment variables (including MCP_SERVER_BASE_URL from mcp-deployment-info.json)
# 6. Test deployment (/health endpoint)
# 7. Save deployment info to backend-deployment-info.json
# Verify deployment
curl <BACKEND_URL>/health | jq
# Should return: {"status":"healthy","mcp_tools_available":103,"claude_model":"claude-sonnet-4-20250514"}Environment Configuration
Root .env (for MCP Server):
TEACHMETO_API_URL=https://api.teachme.to
TEACHMETO_API_KEY=""
TEACHMETO_JWT_PUBLIC_KEY='-----BEGIN CERTIFICATE-----
MIIFeTCCA2GgAwIBAgIUfvXD0peT8ijEJj6jt9HLxSgBC58wDQYJKoZIhvcNAQEL
[Your TeachMeTo JWT public key certificate - contact TeachMeTo team]
-----END CERTIFICATE-----'
MCP_SERVER_BASE_URL=http://localhost:3001
LOG_LEVEL=info
RATE_LIMIT_RPM=100
RATE_LIMIT_BURST=10
PORT=3001
NODE_ENV=development
# Tool Filtering (optional)
DISABLE_WRITE_OPERATIONS=false # Set to 'true' to disable all write operations (POST/PUT/DELETE/PATCH)
DISABLED_TOOLS= # Comma-separated list of tool names to disable (e.g., "create_booking,cancel_booking")
DISABLED_CATEGORIES= # Comma-separated list of categories to disable (e.g., "admin,analytics")backend/.env (for Backend):
ANTHROPIC_API_KEY=sk-ant-api03-your-anthropic-key-here
CLAUDE_MODEL=claude-sonnet-4-20250514
MCP_SERVER_PATH=../build/index.js
MCP_SERVER_BASE_URL=http://localhost:3001
TEACHMETO_API_URL=https://api.teachme.to
TEACHMETO_JWT_PUBLIC_KEY='-----BEGIN CERTIFICATE-----
[MUST be identical to MCP server certificate]
-----END CERTIFICATE-----'
SECRET_KEY=your_secret_key_here
CORS_ORIGINS=http://localhost:3000,http://localhost:3001
DEBUG=true
MAX_TOOL_ITERATIONS=20
# LangSmith Configuration (optional)
LANGCHAIN_TRACING_V2="true"
LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
LANGCHAIN_API_KEY="your_langsmith_key_here"
LANGCHAIN_PROJECT="mcp-demo"Critical Requirements:
TEACHMETO_JWT_PUBLIC_KEYmust be identical in both.envfiles- Must include
-----BEGIN CERTIFICATE-----and-----END CERTIFICATE-----headers - In Lambda, environment variables have escaped newlines (
\\n) which are automatically converted by the code
Troubleshooting Lambda Deployment
JWT Authentication Failures:
- Symptom: "Unknown tool" or "Authentication required" errors
- Cause: JWT public key mismatch or incorrect format
- Solution:
# 1. Verify both Lambda functions have identical JWT keys aws lambda get-function-configuration --function-name mcp_http_server --region us-west-1 --query 'Environment.Variables.TEACHMETO_JWT_PUBLIC_KEY' aws lambda get-function-configuration --function-name mcp_demo_backend --region us-west-1 --query 'Environment.Variables.TEACHMETO_JWT_PUBLIC_KEY' # 2. Check MCP server logs for JWT errors aws logs tail /aws/lambda/mcp_http_server --follow --region us-west-1 | grep JWT
Handler Path Issues:
- Symptom: "Cannot find module 'lambda-handler'"
- Cause: Lambda handler not set to
build/lambda-handler.handler - Solution:
aws lambda update-function-configuration \ --function-name mcp_http_server \ --handler build/lambda-handler.handler \ --region us-west-1
Registry Not Initialized:
- Symptom: "Unknown tool: student_get_profile"
- Cause: Old code without registry deployed
- Solution: Rebuild and redeploy with
./scripts/deploy-mcp-lambda.sh
Docker Issues (Backend):
# Check if Docker is running
docker ps
# Start Docker if not running (macOS)
open /Applications/Docker.app
# Test Docker with Lambda base image
docker run --rm public.ecr.aws/lambda/python:3.11 python --versionMonitoring Deployments:
# Monitor MCP Server logs
aws logs tail /aws/lambda/mcp_http_server --follow --region us-west-1
# Monitor Backend logs
aws logs tail /aws/lambda/mcp_demo_backend --follow --region us-west-1
# Check function status
aws lambda get-function --function-name mcp_http_server --region us-west-1
aws lambda get-function --function-name mcp_demo_backend --region us-west-1🏠 Local Development Setup
1. Prerequisites
- Node.js 18+
- Python 3.11+ (for backend)
- Poetry (Python package manager)
- TeachMeTo API credentials
- Claude API key (Anthropic)
- LangSmith API key (optional, for tracing)
2. Setup MCP Server
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Edit .env with your TeachMeTo credentials
# Build MCP server
npm run build
# Start MCP HTTP server (recommended for development)
npm run dev
# Or use the HTTP server for testing
node build/http-server.js
# Test locally
curl http://localhost:3001/health
curl http://localhost:3001/tools | jq '.tools | length'3. Setup Backend
cd backend
# Install dependencies with Poetry
poetry install
# Configure environment
cp .env.example .env
# Edit backend/.env with your API keys
# Start backend server
./start-server.sh
# Or manually:
poetry run uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
# Test locally
curl http://localhost:8000/health4. Setup Frontend (Optional)
cd frontend
# Install dependencies
npm install
# Configure environment
cp .env.local.example .env.local
# Edit .env.local with backend URL
# Start development server
npm run dev
# Open browser
open http://localhost:3000Development Commands
MCP Server (Root)
npm run build- Compile TypeScript to JavaScriptnpm run dev- Run MCP server in HTTP mode for developmentnpm start- Start compiled MCP server (stdio mode)npm test- Run Jest test suitenpm run type-check- TypeScript type checkingnpm run lint- Run ESLint
Backend (FastAPI)
cd backend && ./start-server.sh- Start backend with auto-setuppoetry install- Install Python dependenciespoetry run uvicorn app.main:app --reload- Start with hot reloadcurl http://localhost:8000/health- Health check
Frontend (Next.js)
cd frontend && npm run dev- Start development servernpm run build- Build for productionnpm run lint- ESLint check
API Endpoints
MCP Server (Port 3001)
Core Endpoints
GET /health- Health check with tool countGET /tools- List all available MCP toolsPOST /tools/{tool_name}- Execute a specific tool- Header:
Authorization: Bearer <MCP_ACCESS_TOKEN>(for authenticated tools) - Body: Tool-specific parameters
- Header:
OAuth 2.1 Endpoints
GET /.well-known/oauth-authorization-server- OAuth metadata discoveryPOST /oauth/register- Register OAuth client (RFC 7591)- Body:
{client_name, redirect_uris, grant_types, response_types} - Returns:
{client_id, client_secret, client_id_issued_at}
- Body:
POST /oauth/token- Token exchange endpoint- Body:
{grant_type: "teachmeto_jwt", teachmeto_jwt, client_id, client_secret, resource, scope} - Returns:
{access_token, token_type: "Bearer", expires_in: 900, scope, resource}
- Body:
Backend (Port 8000)
GET /health- Health check with MCP connection statusPOST /auth/request-otp- Request OTP for phone numberPOST /auth/verify-otp- Verify OTP and get JWT tokenPOST /chat- Send message to Claude with full tool accessGET /tools- List all available MCP tools
Testing
Test MCP Server
# Test health endpoint
curl http://localhost:3001/health
# Test tool listing
curl http://localhost:3001/tools | jq '.tools | length'
# Test OAuth metadata discovery
curl http://localhost:3001/.well-known/oauth-authorization-server | jq
# Test OAuth client registration
curl -X POST http://localhost:3001/oauth/register \
-H "Content-Type: application/json" \
-d '{
"client_name": "Test Client",
"grant_types": ["teachmeto_jwt"],
"response_types": ["code"]
}' | jq
# Test request_otp (no auth required)
curl -X POST http://localhost:3001/tools/request_otp \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+1234567890"}'
# Test token exchange (requires TeachMeTo JWT)
curl -X POST http://localhost:3001/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "teachmeto_jwt",
"teachmeto_jwt": "YOUR_TEACHMETO_JWT",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"resource": "teachmeto://api",
"scope": "tools:read tools:write"
}' | jq
# Test student_get_profile (requires MCP access token)
curl -X POST http://localhost:3001/tools/student_get_profile \
-H "Authorization: Bearer YOUR_MCP_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'Test Backend
# Test health endpoint
curl http://localhost:8000/health | jq
# Test OTP request
curl -X POST http://localhost:8000/auth/request-otp \
-H "Content-Type: application/json" \
-d '{"phoneNumber": "+1234567890"}' | jq
# Test chat endpoint (after authentication)
curl -X POST http://localhost:8000/chat \
-H "Content-Type: application/json" \
-d '{"message": "Get my profile"}' | jqProject Structure
tmt-api-mcp/
├── src/ # MCP Server source (TypeScript)
│ ├── core/ # Core MCP server logic
│ │ └── mcp-server.ts # Main MCP Core with registry
│ ├── registry/ # Registry-based architecture
│ │ ├── tool-registry.ts # Tool registry class
│ │ └── modules.ts # All tool modules list
│ ├── tools/ # Tool implementations
│ │ ├── auth.ts # Authentication (10 tools)
│ │ ├── users.ts # User management (5 tools)
│ │ ├── bookings.ts # Booking operations (18 tools)
│ │ ├── listings.ts # Listing search (3 tools)
│ │ ├── locations.ts # Location services (10 tools)
│ │ ├── payments.ts # Payment processing (3 tools)
│ │ ├── subscriptions.ts # Subscription management (16 tools)
│ │ ├── skills.ts # Skills (3 tools)
│ │ ├── admin.ts # Admin functions (2 tools)
│ │ └── analytics.ts # Analytics (10 tools)
│ ├── services/ # Core services
│ │ ├── api-client.ts # TeachMeTo API client
│ │ ├── auth.ts # Authentication service
│ │ ├── token-service.ts # MCP token management
│ │ └── mcp-auth-server.ts # OAuth 2.1 server
│ ├── types/ # TypeScript type definitions
│ ├── utils/ # Utility functions
│ ├── index.ts # stdio MCP server entry
│ ├── http-server.ts # HTTP MCP server entry
│ └── lambda-handler.ts # AWS Lambda handler
├── backend/ # FastAPI Backend (Python)
│ ├── app/
│ │ ├── main.py # FastAPI app with endpoints
│ │ ├── claude_service.py # Claude + MCP integration
│ │ ├── http_mcp_client.py # HTTP MCP client
│ │ ├── models.py # Pydantic models
│ │ └── config.py # Settings
│ ├── lambda_handler.py # AWS Lambda handler (Mangum)
│ └── scripts/
│ └── deploy-lambda.sh # Backend deployment script
├── frontend/ # Next.js Frontend (React)
│ ├── src/
│ │ ├── app/ # Next.js app directory
│ │ ├── components/ # React components
│ │ └── lib/ # Utilities and state
│ └── package.json
├── scripts/
│ ├── deploy-mcp-lambda.sh # MCP deployment script
│ └── deploy-backend-lambda.sh # Backend deployment script
├── .env # MCP Server environment
├── backend/.env # Backend environment
└── README.md # This fileMCP Client Configuration
The TeachMeTo MCP server can be integrated with any MCP-compatible client. Below are configuration instructions for popular clients.
Claude Code (Desktop App)
Claude Code supports MCP servers via claude_desktop_config.json:
Location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Configuration:
{
"mcpServers": {
"teachmeto": {
"command": "node",
"args": [
"/absolute/path/to/tmt-api-mcp/build/index.js"
],
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "-----BEGIN CERTIFICATE-----\nMIIFeTCCA2GgAwIBAgIUfvXD0peT...\n-----END CERTIFICATE-----",
"LOG_LEVEL": "info",
"RATE_LIMIT_RPM": "100",
"RATE_LIMIT_BURST": "10"
}
}
}
}Important:
- Replace
/absolute/path/to/tmt-api-mcpwith your actual project path - Ensure the project is built:
npm run build - Replace
TEACHMETO_JWT_PUBLIC_KEYwith your actual certificate (newlines as\n) - Restart Claude Desktop after configuration changes
Verification:
- Open Claude Desktop
- Look for "MCP" indicator in the UI
- Type: "List available TeachMeTo tools"
- Should see 76 tools (or 79 if you enabled all tools)
Cursor IDE
Cursor supports MCP servers via .cursor/mcp.json in your project directory:
Location: your-project/.cursor/mcp.json
Configuration:
{
"mcpServers": {
"teachmeto": {
"command": "node",
"args": [
"/absolute/path/to/tmt-api-mcp/build/index.js"
],
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "-----BEGIN CERTIFICATE-----\nMIIFeTCCA2GgAwIBAgIUfvXD0peT...\n-----END CERTIFICATE-----",
"LOG_LEVEL": "info"
}
}
}
}Important:
- Create
.cursordirectory in your project root if it doesn't exist - Use absolute paths for the MCP server
- Build the project first:
npm run build - Restart Cursor after configuration changes
Verification:
- Open Cursor
- Use the AI chat feature
- Type: "Use TeachMeTo MCP to get my profile"
- Should see MCP tools being called
Cline (VS Code Extension)
Cline (formerly Claude Dev) supports MCP servers via VS Code settings:
Location: .vscode/settings.json in your project directory
Configuration:
{
"cline.mcpServers": {
"teachmeto": {
"command": "node",
"args": [
"/absolute/path/to/tmt-api-mcp/build/index.js"
],
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "-----BEGIN CERTIFICATE-----\nMIIFeTCCA2GgAwIBAgIUfvXD0peT...\n-----END CERTIFICATE-----",
"LOG_LEVEL": "info"
}
}
}
}Important:
- Install the Cline extension from VS Code marketplace
- Build the MCP server:
npm run build - Reload VS Code window after configuration changes
Verification:
- Open Command Palette (Cmd+Shift+P / Ctrl+Shift+P)
- Run "Cline: Start New Task"
- Ask: "Show me TeachMeTo tools"
Continue (VS Code Extension)
Continue supports MCP servers via ~/.continue/config.json:
Location: ~/.continue/config.json
Configuration:
{
"mcpServers": [
{
"name": "teachmeto",
"command": "node",
"args": [
"/absolute/path/to/tmt-api-mcp/build/index.js"
],
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "-----BEGIN CERTIFICATE-----\nMIIFeTCCA2GgAwIBAgIUfvXD0peT...\n-----END CERTIFICATE-----",
"LOG_LEVEL": "info"
}
}
]
}Verification:
- Open Continue panel in VS Code
- Ask: "What TeachMeTo tools are available?"
HTTP Mode (Any MCP Client)
For clients that support HTTP-based MCP servers, you can run the server in HTTP mode:
Start HTTP Server:
# Development mode
npm run dev
# Production mode
node build/http-server.js
# Custom port
PORT=3001 node build/http-server.jsClient Configuration:
{
"mcpServers": {
"teachmeto": {
"url": "http://localhost:3001",
"transport": "http"
}
}
}Available HTTP Endpoints:
GET /health- Health checkGET /tools- List all toolsPOST /tools/{tool_name}- Execute toolGET /.well-known/oauth-authorization-server- OAuth metadataPOST /oauth/register- Register OAuth clientPOST /oauth/token- Token exchange
AWS Lambda Mode (Production)
For production deployments, use the deployed Lambda Function URL:
Client Configuration:
{
"mcpServers": {
"teachmeto": {
"url": "https://your-function-url.lambda-url.us-west-1.on.aws",
"transport": "http"
}
}
}Important:
- Lambda Function URL is public - ensure proper authentication
- All tools requiring authentication need valid JWT tokens
- See "AWS Lambda Deployment" section for deployment instructions
Environment Variables Reference
All MCP client configurations support these environment variables:
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| TEACHMETO_API_URL | Yes | - | TeachMeTo API base URL |
| TEACHMETO_JWT_PUBLIC_KEY | Yes | - | JWT verification certificate |
| LOG_LEVEL | No | info | Logging level (debug, info, warn, error) |
| RATE_LIMIT_RPM | No | 100 | Requests per minute limit |
| RATE_LIMIT_BURST | No | 10 | Burst request limit |
| DISABLE_WRITE_OPERATIONS | No | false | Disable all write operations |
| DISABLED_TOOLS | No | - | Comma-separated list of tools to disable |
| DISABLED_CATEGORIES | No | - | Comma-separated list of categories to disable |
Tool Filtering Examples
Conservative Production Setup:
{
"mcpServers": {
"teachmeto": {
"command": "node",
"args": ["/path/to/build/index.js"],
"env": {
"TEACHMETO_API_URL": "https://api.teachme.to",
"TEACHMETO_JWT_PUBLIC_KEY": "...",
"DISABLE_WRITE_OPERATIONS": "true"
}
}
}
}Disable Specific Tools:
{
"env": {
"DISABLED_TOOLS": "cancel_booking,delete_user,admin_disable_user"
}
}Disable Entire Categories:
{
"env": {
"DISABLED_CATEGORIES": "admin,analytics"
}
}Troubleshooting MCP Client Configuration
"Command not found" or "Cannot find module"
- Ensure you've run
npm run buildto compile TypeScript - Use absolute paths in
commandandargs - Verify Node.js is in your PATH:
which node
"Authentication failed" or "Invalid JWT"
- Verify
TEACHMETO_JWT_PUBLIC_KEYis correctly formatted - Ensure certificate includes
-----BEGIN CERTIFICATE-----headers - Check newlines are properly escaped as
\nin JSON
"No tools available" or tool count mismatch
- Check MCP server logs for registry initialization
- Verify environment variables are loaded correctly
- Test directly:
curl http://localhost:3001/tools | jq '.tools | length'
"Rate limit exceeded"
- Increase
RATE_LIMIT_RPMandRATE_LIMIT_BURSTvalues - Check for infinite loops in client configuration
- Monitor MCP server logs:
LOG_LEVEL=debug npm run dev
"Connection refused"
- Verify MCP server is running:
curl http://localhost:3001/health - Check port is not blocked by firewall
- Ensure correct URL/port in client configuration
MCP Inspector (Debugging)
For debugging MCP server integration, use the MCP Inspector:
# Install MCP Inspector globally (one time)
npm install -g @modelcontextprotocol/inspector
# Run with your MCP server
npx @modelcontextprotocol/inspector node build/index.js
# Or in HTTP mode
npm run mcp:inspectThe inspector provides:
- Interactive tool testing
- Request/response inspection
- Authentication flow debugging
- OAuth 2.1 flow visualization
Inspector URL: Opens automatically in browser at http://localhost:5173
