From d118f071204e75037aa9d6ec68db9bc3d47f0014 Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 12 Nov 2025 09:54:27 -0800 Subject: [PATCH 1/2] chore: removes old mcp code Signed-off-by: Anthony D. Mays --- .../fullstack_demo/mcp-server/.env.example | 11 - .../fullstack_demo/mcp-server/.gitignore | 26 - .../mcp-server/AUTHENTICATION.md | 123 -- .../fullstack_demo/mcp-server/DEPLOYMENT.md | 124 -- .../fullstack_demo/mcp-server/README.md | 167 -- .../mcp-server/SESSION_AUTHENTICATION.md | 159 -- .../mcp-server/claude-desktop-config.json | 14 - .../mcp-server/generate-api-key.mjs | 15 - .../fullstack_demo/mcp-server/mcp-config.json | 14 - .../mcp-server/package-lock.json | 1658 ----------------- .../fullstack_demo/mcp-server/package.json | 28 - .../mcp-server/src/api-client.ts | 147 -- .../mcp-server/src/auth/session-manager.ts | 226 --- .../fullstack_demo/mcp-server/src/index.ts | 643 ------- .../mcp-server/src/types/todo.ts | 23 - .../mcp-server/test-connection.mjs | 57 - .../fullstack_demo/mcp-server/test-fetch.mjs | 30 - .../fullstack_demo/mcp-server/test.mjs | 36 - .../fullstack_demo/mcp-server/tsconfig.json | 20 - .../src/app/api/mcp/debug/route.ts | 14 - .../src/app/api/mcp/session/route.ts | 52 - .../src/app/api/mcp/todos/[id]/route.ts | 50 - .../src/app/api/mcp/todos/route.ts | 55 - .../fullstack_demo/src/middleware.ts | 4 +- .../fullstack_demo/src/util/mcp-auth.ts | 50 - 25 files changed, 1 insertion(+), 3745 deletions(-) delete mode 100644 lib/javascript/fullstack_demo/mcp-server/.env.example delete mode 100644 lib/javascript/fullstack_demo/mcp-server/.gitignore delete mode 100644 lib/javascript/fullstack_demo/mcp-server/AUTHENTICATION.md delete mode 100644 lib/javascript/fullstack_demo/mcp-server/DEPLOYMENT.md delete mode 100644 lib/javascript/fullstack_demo/mcp-server/README.md delete mode 100644 lib/javascript/fullstack_demo/mcp-server/SESSION_AUTHENTICATION.md delete mode 100644 lib/javascript/fullstack_demo/mcp-server/claude-desktop-config.json delete mode 100644 lib/javascript/fullstack_demo/mcp-server/generate-api-key.mjs delete mode 100644 lib/javascript/fullstack_demo/mcp-server/mcp-config.json delete mode 100644 lib/javascript/fullstack_demo/mcp-server/package-lock.json delete mode 100644 lib/javascript/fullstack_demo/mcp-server/package.json delete mode 100644 lib/javascript/fullstack_demo/mcp-server/src/api-client.ts delete mode 100644 lib/javascript/fullstack_demo/mcp-server/src/auth/session-manager.ts delete mode 100644 lib/javascript/fullstack_demo/mcp-server/src/index.ts delete mode 100644 lib/javascript/fullstack_demo/mcp-server/src/types/todo.ts delete mode 100644 lib/javascript/fullstack_demo/mcp-server/test-connection.mjs delete mode 100644 lib/javascript/fullstack_demo/mcp-server/test-fetch.mjs delete mode 100644 lib/javascript/fullstack_demo/mcp-server/test.mjs delete mode 100644 lib/javascript/fullstack_demo/mcp-server/tsconfig.json delete mode 100644 lib/javascript/fullstack_demo/src/app/api/mcp/debug/route.ts delete mode 100644 lib/javascript/fullstack_demo/src/app/api/mcp/session/route.ts delete mode 100644 lib/javascript/fullstack_demo/src/app/api/mcp/todos/[id]/route.ts delete mode 100644 lib/javascript/fullstack_demo/src/app/api/mcp/todos/route.ts delete mode 100644 lib/javascript/fullstack_demo/src/util/mcp-auth.ts diff --git a/lib/javascript/fullstack_demo/mcp-server/.env.example b/lib/javascript/fullstack_demo/mcp-server/.env.example deleted file mode 100644 index c20966a59..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -# Environment Configuration -TODO_API_BASE_URL=http://localhost:3000/api/mcp - -# Clerk Authentication (Required for session-based auth) -CLERK_SECRET_KEY=your-clerk-secret-key-here - -# Optional: Database connection for direct DB access -DATABASE_URL=your_database_url_here - -# Note: API keys are no longer used for authentication -# Users authenticate using Clerk session tokens from the web app diff --git a/lib/javascript/fullstack_demo/mcp-server/.gitignore b/lib/javascript/fullstack_demo/mcp-server/.gitignore deleted file mode 100644 index 2b4735b1f..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -# Dependencies -node_modules/ -npm-debug.log* - -# Build output -dist/ -*.tsbuildinfo - -# Environment files -.env -.env.local -.env.production - -# IDE files -.vscode/ -.idea/ -*.swp -*.swo - -# OS files -.DS_Store -Thumbs.db - -# Logs -logs/ -*.log diff --git a/lib/javascript/fullstack_demo/mcp-server/AUTHENTICATION.md b/lib/javascript/fullstack_demo/mcp-server/AUTHENTICATION.md deleted file mode 100644 index 074d2e64c..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/AUTHENTICATION.md +++ /dev/null @@ -1,123 +0,0 @@ -# Authentication Setup for Claude Desktop - -## Quick Setup Guide - -### 1. Configure Your Todo App - -Add these environment variables to your todo app's `.env` file: - -```bash -# Add to /lib/javascript/fullstack_demo/.env -MCP_API_KEY=your-secret-mcp-api-key-here -MCP_DEFAULT_USER_ID=your-user-id-here -``` - -**Generate a secure API key:** -```bash -# Generate a random API key -node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" -``` - -### 2. Configure MCP Server - -Create/update the MCP server's `.env` file: - -```bash -# /lib/javascript/fullstack_demo/mcp-server/.env -TODO_API_BASE_URL=http://localhost:3000/api/mcp -MCP_API_KEY=your-secret-mcp-api-key-here -``` - -**Use the same API key in both files!** - -### 3. Configure Claude Desktop - -Update your Claude Desktop config file: - -**Location:** `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) - -```json -{ - "mcpServers": { - "todo-mcp-server": { - "command": "node", - "args": ["/full/path/to/your/mcp-server/dist/index.js"], - "env": { - "TODO_API_BASE_URL": "http://localhost:3000/api/mcp", - "MCP_API_KEY": "your-secret-mcp-api-key-here" - } - } - } -} -``` - -### 4. Build and Test - -1. **Start your todo app:** - ```bash - cd /lib/javascript/fullstack_demo - npm run dev - ``` - -2. **Build the MCP server:** - ```bash - cd /lib/javascript/fullstack_demo/mcp-server - npm run build - ``` - -3. **Restart Claude Desktop** to pick up the new configuration - -### 5. Test Authentication - -In Claude Desktop, try: -``` -Can you show me my todos? -``` - -If authentication works, you should see your todos or an empty list. - -## Troubleshooting - -### "Unauthorized - Invalid API Key" -- Check that the API key is the same in both `.env` files -- Ensure the MCP server environment variables are set correctly -- Restart Claude Desktop after config changes - -### "Cannot connect to API" -- Verify your todo app is running on the correct port -- Check that the `TODO_API_BASE_URL` points to the MCP endpoints (`/api/mcp`) -- Ensure firewalls aren't blocking the connection - -### "User not found" errors -- Set `MCP_DEFAULT_USER_ID` to a valid user ID from your system -- For development, you can use any string like "test-user" - -## Security Notes - -⚠️ **Important for Production:** - -1. **Keep API keys secret** - Never commit them to version control -2. **Use environment variables** - Don't hardcode keys in your config -3. **Rotate keys regularly** - Generate new API keys periodically -4. **Limit scope** - Consider implementing user-specific API keys -5. **Use HTTPS** - In production, always use HTTPS for API communication - -## Alternative: User-Specific Authentication - -For multiple users, you can extend the authentication to map API keys to specific users: - -```typescript -// In your API route -const userKeyMap = { - 'api-key-1': 'user-1', - 'api-key-2': 'user-2', - // etc. -}; - -function authenticateApiKey(request: Request): string | null { - const apiKey = request.headers.get('X-API-Key'); - return userKeyMap[apiKey] || null; -} -``` - -This allows each Claude Desktop user to have their own API key and access their own todos. diff --git a/lib/javascript/fullstack_demo/mcp-server/DEPLOYMENT.md b/lib/javascript/fullstack_demo/mcp-server/DEPLOYMENT.md deleted file mode 100644 index b0be9dd96..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/DEPLOYMENT.md +++ /dev/null @@ -1,124 +0,0 @@ -# Todo MCP Server Deployment Guide - -## Quick Start - -1. **Build the server**: - ```bash - cd mcp-server - npm install - npm run build - ``` - -2. **Configure environment**: - ```bash - cp .env.example .env - # Edit .env with your configuration - ``` - -3. **Test the server**: - ```bash - node test.mjs - ``` - -## MCP Client Configuration - -### Claude Desktop - -Add this to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): - -```json -{ - "mcpServers": { - "todo-mcp-server": { - "command": "node", - "args": ["/path/to/your/mcp-server/dist/index.js"], - "env": { - "TODO_API_BASE_URL": "http://localhost:3000/api" - } - } - } -} -``` - -### Other MCP Clients - -For other MCP-compatible clients, use the stdio transport with: -- **Command**: `node` -- **Args**: `["/path/to/mcp-server/dist/index.js"]` -- **Environment**: Set `TODO_API_BASE_URL` to your API base URL - -## Environment Variables - -| Variable | Description | Default | Required | -| ------------------- | ----------------------------------- | --------------------------- | -------- | -| `TODO_API_BASE_URL` | Base URL of the Todo API | `http://localhost:3000/api` | Yes | -| `CLERK_SECRET_KEY` | Clerk secret key for authentication | - | Optional | - -## Usage Examples - -Once configured with an MCP client, you can use these tools: - -### Get All Todos -``` -Can you show me all my todos? -``` - -### Create a Todo -``` -Create a new todo: "Learn about MCP servers" -``` - -### Update a Todo -``` -Update todo ID 1 to mark it as completed -``` - -### Get Statistics -``` -Show me my todo statistics -``` - -## Troubleshooting - -### Common Issues - -1. **Module not found errors**: - - Ensure you've run `npm run build` - - Check that the path in your MCP client config is correct - -2. **API connection errors**: - - Verify the Todo app is running on the correct port - - Check the `TODO_API_BASE_URL` environment variable - - Ensure the API endpoints are accessible - -3. **Authentication errors**: - - For production use, configure proper authentication - - Set the `CLERK_SECRET_KEY` if using Clerk authentication - -### Debug Mode - -Run the server in development mode to see detailed logs: -```bash -npm run dev -``` - -## Security Considerations - -⚠️ **Important**: This MCP server is currently configured for development use. For production deployment: - -1. **Authentication**: Implement proper API authentication -2. **HTTPS**: Use HTTPS for API communication -3. **Rate Limiting**: Implement rate limiting to prevent abuse -4. **Input Validation**: Add additional input validation and sanitization -5. **Error Handling**: Avoid exposing sensitive information in error messages - -## API Compatibility - -This MCP server is designed to work with the Todo app's REST API: - -- `GET /api/todos` - Get all todos -- `POST /api/todos` - Create a new todo -- `PATCH /api/todos/{id}` - Update a todo -- `DELETE /api/todos/{id}` - Delete a todo - -Ensure your Todo app implements these endpoints for full compatibility. diff --git a/lib/javascript/fullstack_demo/mcp-server/README.md b/lib/javascript/fullstack_demo/mcp-server/README.md deleted file mode 100644 index 47836dbb3..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/README.md +++ /dev/null @@ -1,167 +0,0 @@ -# Todo MCP Server - -A Model Context Protocol (MCP) server that provides tools for interacting with the Todo application API. - -## Features - -This MCP server provides the following tools: - -- **get_todos**: Retrieve all todos for the authenticated user -- **create_todo**: Create a new todo item -- **update_todo**: Update an existing todo item (text and/or completion status) -- **delete_todo**: Delete a todo item -- **toggle_todo**: Toggle the completion status of a todo item -- **get_todo_stats**: Get statistics about todos (total, completed, pending, completion rate) - -## Setup - -1. Install dependencies: - ```bash - npm install - ``` - -2. Copy the environment file and configure it: - ```bash - cp .env.example .env - ``` - -3. Update the `.env` file with your configuration: - ``` - TODO_API_BASE_URL=http://localhost:3000/api - CLERK_SECRET_KEY=your_clerk_secret_key_here - ``` - -## Development - -Build the project: -```bash -npm run build -``` - -Run in development mode: -```bash -npm run dev -``` - -Run the compiled server: -```bash -npm start -``` - -## Usage - -This MCP server is designed to be used with MCP-compatible clients. The server communicates via stdio. - -### Tool Examples - -1. **Get all todos**: - ```json - { - "name": "get_todos", - "arguments": {} - } - ``` - -2. **Create a new todo**: - ```json - { - "name": "create_todo", - "arguments": { - "text": "Learn about MCP servers", - "completed": false - } - } - ``` - -3. **Update a todo**: - ```json - { - "name": "update_todo", - "arguments": { - "id": 1, - "text": "Updated todo text", - "completed": true - } - } - ``` - -4. **Delete a todo**: - ```json - { - "name": "delete_todo", - "arguments": { - "id": 1 - } - } - ``` - -5. **Toggle todo completion**: - ```json - { - "name": "toggle_todo", - "arguments": { - "id": 1 - } - } - ``` - -6. **Get todo statistics**: - ```json - { - "name": "get_todo_stats", - "arguments": {} - } - ``` - -## Authentication - -The MCP server interacts with the Todo API, which uses Clerk for authentication. In a production setup, you would need to handle authentication properly by either: - -1. Using API keys or service accounts -2. Implementing session-based authentication -3. Using OAuth flows - -Currently, the server assumes the API is accessible without authentication for demonstration purposes. - -## API Integration - -The server communicates with the Todo app's REST API endpoints: - -- `GET /api/todos` - Get all todos -- `POST /api/todos` - Create a new todo -- `PATCH /api/todos/{id}` - Update a todo -- `DELETE /api/todos/{id}` - Delete a todo - -## Error Handling - -The server includes comprehensive error handling for: -- API request failures -- Network issues -- Invalid todo IDs -- Malformed requests - -All errors are returned as structured responses with descriptive messages. - -## Troubleshooting - -### "fetch is not defined" Error - -If you see `fetch is not defined` errors in Claude Desktop, this means you're running an older version of Node.js. The MCP server now includes a `node-fetch` polyfill to fix this. - -**Solution:** -1. Rebuild the MCP server: `npm run build` -2. Restart Claude Desktop completely -3. The error should be resolved - -### Other Common Issues - -- Ensure all dependencies are correctly installed -- Check your `.env` configuration -- Verify the Todo API is running and accessible - -## Contributing - -1. Make your changes -2. Run `npm run build` to ensure compilation -3. Test with your MCP client -4. Submit a pull request diff --git a/lib/javascript/fullstack_demo/mcp-server/SESSION_AUTHENTICATION.md b/lib/javascript/fullstack_demo/mcp-server/SESSION_AUTHENTICATION.md deleted file mode 100644 index ba8e0d86c..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/SESSION_AUTHENTICATION.md +++ /dev/null @@ -1,159 +0,0 @@ -# Session-Based Authentication Setup for Claude Desktop - -## Quick Setup Guide - -This MCP server now uses **session-based authentication** with Clerk instead of API keys. Users can log into the web app and use their session tokens for secure, personalized access. - -### 1. Configure Your Todo App - -Add these environment variables to your todo app's `.env` file: - -```bash -# Add to /lib/javascript/fullstack_demo/.env -CLERK_SECRET_KEY=your-clerk-secret-key-here -``` - -**Your Clerk secret key can be found in your Clerk Dashboard.** - -### 2. Configure MCP Server - -Create/update the MCP server's `.env` file: - -```bash -# /lib/javascript/fullstack_demo/mcp-server/.env -TODO_API_BASE_URL=http://localhost:3000/api/mcp -CLERK_SECRET_KEY=your-clerk-secret-key-here -``` - -**Use the same Clerk secret key in both files!** - -### 3. Configure Claude Desktop - -Update your Claude Desktop config file: - -**Location:** `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) - -```json -{ - "mcpServers": { - "todo-mcp-server": { - "command": "node", - "args": ["/full/path/to/your/mcp-server/dist/index.js"], - "env": { - "TODO_API_BASE_URL": "http://localhost:3000/api/mcp", - "CLERK_SECRET_KEY": "your-clerk-secret-key-here" - } - } - } -} -``` - -### 4. Build and Test - -1. **Start your todo app:** - ```bash - cd /lib/javascript/fullstack_demo - npm run dev - ``` - -2. **Build the MCP server:** - ```bash - cd /lib/javascript/fullstack_demo/mcp-server - npm run build - ``` - -3. **Restart Claude Desktop** to pick up the new configuration - -## How to Use Session Authentication - -### Step 1: Get Your Session Token - -1. **Open the todo app** in your browser: `http://localhost:3000` -2. **Sign in** with your Clerk account -3. **Get your session token** using one of these methods: - - **Method A: API Endpoint (Easiest)** - - Visit: `http://localhost:3000/api/mcp/session` - - Copy the `sessionToken` from the response - - **Method B: Browser DevTools** - - Open Developer Tools (F12) - - Go to Application/Storage tab - - Find the `__session` cookie and copy its value - -### Step 2: Login in Claude Desktop - -In Claude Desktop, use the login tool: - -``` -Please login to the todo app using my session token: sess_2h9j8k7l6m5n4o3p2q1r0s9t8u7v6w5x4y3z -``` - -### Step 3: Start Managing Todos - -Once logged in, you can use all todo commands: - -``` -Can you show me my todos? -Create a todo: "Buy groceries" -Mark todo 1 as completed -Get my todo statistics -``` - -## Available Tools - -### Authentication Tools -- `login` - Login with your session token -- `logout` - Logout and clear your session -- `get_auth_status` - Check your current authentication status -- `get_login_url` - Get instructions for logging in - -### Todo Management Tools -- `get_todos` - Retrieve all your todos -- `create_todo` - Create a new todo item -- `update_todo` - Update an existing todo -- `delete_todo` - Delete a todo item -- `toggle_todo` - Toggle completion status -- `get_todo_stats` - Get statistics about your todos - -## Troubleshooting - -### "Authentication required. Please use the login tool first." -- You need to login first using your session token -- Get a fresh session token from the web app -- Use the `login` tool with your session token - -### "Login failed. Invalid session token or session has expired." -- Your session token may have expired -- Get a new session token from the web app -- Make sure you copied the complete token - -### "Cannot connect to API" -- Verify your todo app is running on the correct port -- Check that the `TODO_API_BASE_URL` points to the MCP endpoints (`/api/mcp`) -- Ensure firewalls aren't blocking the connection - -### Session expires quickly -- Session tokens have built-in expiration for security -- Simply get a new token when yours expires -- The MCP server will remember your login until you restart it - -## Security Notes - -✅ **Improved Security:** - -1. **User-specific access** - Each user only sees their own todos -2. **Session-based auth** - More secure than shared API keys -3. **Automatic expiration** - Sessions expire automatically -4. **No shared secrets** - Each user has their own session -5. **Clerk integration** - Benefits from Clerk's security features - -## Multiple Users - -Each Claude Desktop user can: -1. Sign into the web app with their own account -2. Get their own session token -3. Access only their own todos -4. Have independent authentication status - -This provides true multi-user support with proper data isolation! diff --git a/lib/javascript/fullstack_demo/mcp-server/claude-desktop-config.json b/lib/javascript/fullstack_demo/mcp-server/claude-desktop-config.json deleted file mode 100644 index 04c51a10e..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/claude-desktop-config.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "mcpServers": { - "todo-mcp-server": { - "command": "node", - "args": [ - "/your/path/here/code-society-25-2/lib/javascript/fullstack_demo/mcp-server/dist/index.js" - ], - "env": { - "TODO_API_BASE_URL": "http://localhost:3000/api/mcp", - "MCP_API_KEY": "your_api_key_here" - } - } - } -} \ No newline at end of file diff --git a/lib/javascript/fullstack_demo/mcp-server/generate-api-key.mjs b/lib/javascript/fullstack_demo/mcp-server/generate-api-key.mjs deleted file mode 100644 index 7cbedf5d8..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/generate-api-key.mjs +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env node - -import { randomBytes } from 'crypto'; - -function generateApiKey() { - return randomBytes(32).toString('hex'); -} - -console.log('🔑 Generated MCP API Key:'); -console.log(generateApiKey()); -console.log('\n📝 Add this to both:'); -console.log(' 1. Your todo app .env file (MCP_API_KEY=...)'); -console.log(' 2. Your MCP server .env file (MCP_API_KEY=...)'); -console.log(' 3. Your Claude Desktop config (MCP_API_KEY: "...")'); -console.log('\n🔒 Keep this key secret and secure!'); diff --git a/lib/javascript/fullstack_demo/mcp-server/mcp-config.json b/lib/javascript/fullstack_demo/mcp-server/mcp-config.json deleted file mode 100644 index d13780a43..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/mcp-config.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "mcpServers": { - "todo-mcp-server": { - "command": "node", - "args": [ - "dist/index.js" - ], - "cwd": "/path/to/your/mcp-server", - "env": { - "TODO_API_BASE_URL": "http://localhost:3000/api" - } - } - } -} \ No newline at end of file diff --git a/lib/javascript/fullstack_demo/mcp-server/package-lock.json b/lib/javascript/fullstack_demo/mcp-server/package-lock.json deleted file mode 100644 index 73b8b867e..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/package-lock.json +++ /dev/null @@ -1,1658 +0,0 @@ -{ - "name": "todo-mcp-server", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "todo-mcp-server", - "version": "1.0.0", - "dependencies": { - "@clerk/clerk-sdk-node": "^4.13.23", - "@modelcontextprotocol/sdk": "^0.6.0", - "@types/node": "^20.0.0", - "@types/node-fetch": "^2.6.12", - "dotenv": "^16.4.5", - "node-fetch": "^3.3.2" - }, - "devDependencies": { - "tsx": "^4.7.0", - "typescript": "^5.3.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@clerk/backend": { - "version": "0.38.15", - "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-0.38.15.tgz", - "integrity": "sha512-zmd0jPyb1iALlmyzyRbgujQXrGqw8sf+VpFjm5GkndpBeq5+9+oH7QgMaFEmWi9oxvTd2sZ+EN+QT4+OXPUnGA==", - "license": "MIT", - "dependencies": { - "@clerk/shared": "1.4.2", - "@clerk/types": "3.65.5", - "@peculiar/webcrypto": "1.4.1", - "@types/node": "16.18.6", - "cookie": "0.5.0", - "deepmerge": "4.2.2", - "node-fetch-native": "1.0.1", - "snakecase-keys": "5.4.4", - "tslib": "2.4.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@clerk/backend/node_modules/@types/node": { - "version": "16.18.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.6.tgz", - "integrity": "sha512-vmYJF0REqDyyU0gviezF/KHq/fYaUbFhkcNbQCuPGFQj6VTbXuHZoxs/Y7mutWe73C8AC6l9fFu8mSYiBAqkGA==", - "license": "MIT" - }, - "node_modules/@clerk/backend/node_modules/snakecase-keys": { - "version": "5.4.4", - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.4.4.tgz", - "integrity": "sha512-YTywJG93yxwHLgrYLZjlC75moVEX04LZM4FHfihjHe1FCXm+QaLOFfSf535aXOAd0ArVQMWUAe8ZPm4VtWyXaA==", - "license": "MIT", - "dependencies": { - "map-obj": "^4.1.0", - "snake-case": "^3.0.4", - "type-fest": "^2.5.2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@clerk/clerk-sdk-node": { - "version": "4.13.23", - "resolved": "https://registry.npmjs.org/@clerk/clerk-sdk-node/-/clerk-sdk-node-4.13.23.tgz", - "integrity": "sha512-D3/O5zLqOtIcoNjA5tza1WK+o3Bs20DcmFc2z8swv7p8p5i5fj5Q1rEZl31bnz+fEQGo2YtTYZyG0zY/HkioQw==", - "license": "MIT", - "dependencies": { - "@clerk/backend": "0.38.15", - "@clerk/shared": "1.4.2", - "@clerk/types": "3.65.5", - "@types/cookies": "0.7.7", - "@types/express": "4.17.14", - "@types/node-fetch": "2.6.2", - "camelcase-keys": "6.2.2", - "snakecase-keys": "3.2.1", - "tslib": "2.4.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@clerk/clerk-sdk-node/node_modules/@types/node-fetch": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.2.tgz", - "integrity": "sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, - "node_modules/@clerk/clerk-sdk-node/node_modules/form-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.3.tgz", - "integrity": "sha512-q5YBMeWy6E2Un0nMGWMgI65MAKtaylxfNJGJxpGh45YDciZB4epbWpaAfImil6CPAPTYB4sh0URQNDRIZG5F2w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.35" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@clerk/shared": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-1.4.2.tgz", - "integrity": "sha512-R+OkzCtnNU7sn/F6dBfdY5lKs84TN785VZdBBefmyr7zsXcFEqbCcfQzyvgtIS28Ln5SifFEBoAyYR334IXO8w==", - "license": "MIT", - "dependencies": { - "glob-to-regexp": "0.4.1", - "js-cookie": "3.0.1", - "swr": "2.2.0" - }, - "peerDependencies": { - "react": ">=16" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } - } - }, - "node_modules/@clerk/types": { - "version": "3.65.5", - "resolved": "https://registry.npmjs.org/@clerk/types/-/types-3.65.5.tgz", - "integrity": "sha512-RGO8v2a52Ybo1jwVj42UWT8VKyxAk/qOxrkA3VNIYBNEajPSmZNa9r9MTgqSgZRyz1XTlQHdVb7UK7q78yAGfA==", - "license": "MIT", - "dependencies": { - "csstype": "3.1.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.6.tgz", - "integrity": "sha512-ShbM/3XxwuxjFiuVBHA+d3j5dyac0aEVVq1oluIDf71hUw0aRF59dV/efUsIwFnR6m8JNM2FjZOzmaZ8yG61kw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.6.tgz", - "integrity": "sha512-S8ToEOVfg++AU/bHwdksHNnyLyVM+eMVAOf6yRKFitnwnbwwPNqKr3srzFRe7nzV69RQKb5DgchIX5pt3L53xg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.6.tgz", - "integrity": "sha512-hd5zdUarsK6strW+3Wxi5qWws+rJhCCbMiC9QZyzoxfk5uHRIE8T287giQxzVpEvCwuJ9Qjg6bEjcRJcgfLqoA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.6.tgz", - "integrity": "sha512-0Z7KpHSr3VBIO9A/1wcT3NTy7EB4oNC4upJ5ye3R7taCc2GUdeynSLArnon5G8scPwaU866d3H4BCrE5xLW25A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.6.tgz", - "integrity": "sha512-FFCssz3XBavjxcFxKsGy2DYK5VSvJqa6y5HXljKzhRZ87LvEi13brPrf/wdyl/BbpbMKJNOr1Sd0jtW4Ge1pAA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.6.tgz", - "integrity": "sha512-GfXs5kry/TkGM2vKqK2oyiLFygJRqKVhawu3+DOCk7OxLy/6jYkWXhlHwOoTb0WqGnWGAS7sooxbZowy+pK9Yg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.6.tgz", - "integrity": "sha512-aoLF2c3OvDn2XDTRvn8hN6DRzVVpDlj2B/F66clWd/FHLiHaG3aVZjxQX2DYphA5y/evbdGvC6Us13tvyt4pWg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.6.tgz", - "integrity": "sha512-2SkqTjTSo2dYi/jzFbU9Plt1vk0+nNg8YC8rOXXea+iA3hfNJWebKYPs3xnOUf9+ZWhKAaxnQNUf2X9LOpeiMQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.6.tgz", - "integrity": "sha512-SZHQlzvqv4Du5PrKE2faN0qlbsaW/3QQfUUc6yO2EjFcA83xnwm91UbEEVx4ApZ9Z5oG8Bxz4qPE+HFwtVcfyw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.6.tgz", - "integrity": "sha512-b967hU0gqKd9Drsh/UuAm21Khpoh6mPBSgz8mKRq4P5mVK8bpA+hQzmm/ZwGVULSNBzKdZPQBRT3+WuVavcWsQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.6.tgz", - "integrity": "sha512-aHWdQ2AAltRkLPOsKdi3xv0mZ8fUGPdlKEjIEhxCPm5yKEThcUjHpWB1idN74lfXGnZ5SULQSgtr5Qos5B0bPw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.6.tgz", - "integrity": "sha512-VgKCsHdXRSQ7E1+QXGdRPlQ/e08bN6WMQb27/TMfV+vPjjTImuT9PmLXupRlC90S1JeNNW5lzkAEO/McKeJ2yg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.6.tgz", - "integrity": "sha512-WViNlpivRKT9/py3kCmkHnn44GkGXVdXfdc4drNmRl15zVQ2+D2uFwdlGh6IuK5AAnGTo2qPB1Djppj+t78rzw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.6.tgz", - "integrity": "sha512-wyYKZ9NTdmAMb5730I38lBqVu6cKl4ZfYXIs31Baf8aoOtB4xSGi3THmDYt4BTFHk7/EcVixkOV2uZfwU3Q2Jw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.6.tgz", - "integrity": "sha512-KZh7bAGGcrinEj4qzilJ4hqTY3Dg2U82c8bv+e1xqNqZCrCyc+TL9AUEn5WGKDzm3CfC5RODE/qc96OcbIe33w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.6.tgz", - "integrity": "sha512-9N1LsTwAuE9oj6lHMyyAM+ucxGiVnEqUdp4v7IaMmrwb06ZTEVCIs3oPPplVsnjPfyjmxwHxHMF8b6vzUVAUGw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.6.tgz", - "integrity": "sha512-A6bJB41b4lKFWRKNrWoP2LHsjVzNiaurf7wyj/XtFNTsnPuxwEBWHLty+ZE0dWBKuSK1fvKgrKaNjBS7qbFKig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.6.tgz", - "integrity": "sha512-IjA+DcwoVpjEvyxZddDqBY+uJ2Snc6duLpjmkXm/v4xuS3H+3FkLZlDm9ZsAbF9rsfP3zeA0/ArNDORZgrxR/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.6.tgz", - "integrity": "sha512-dUXuZr5WenIDlMHdMkvDc1FAu4xdWixTCRgP7RQLBOkkGgwuuzaGSYcOpW4jFxzpzL1ejb8yF620UxAqnBrR9g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.6.tgz", - "integrity": "sha512-l8ZCvXP0tbTJ3iaqdNf3pjaOSd5ex/e6/omLIQCVBLmHTlfXW3zAxQ4fnDmPLOB1x9xrcSi/xtCWFwCZRIaEwg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.6.tgz", - "integrity": "sha512-hKrmDa0aOFOr71KQ/19JC7az1P0GWtCN1t2ahYAf4O007DHZt/dW8ym5+CUdJhQ/qkZmI1HAF8KkJbEFtCL7gw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.6.tgz", - "integrity": "sha512-+SqBcAWoB1fYKmpWoQP4pGtx+pUUC//RNYhFdbcSA16617cchuryuhOCRpPsjCblKukAckWsV+aQ3UKT/RMPcA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.6.tgz", - "integrity": "sha512-dyCGxv1/Br7MiSC42qinGL8KkG4kX0pEsdb0+TKhmJZgCUDBGmyo1/ArCjNGiOLiIAgdbWgmWgib4HoCi5t7kA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.6.tgz", - "integrity": "sha512-42QOgcZeZOvXfsCBJF5Afw73t4veOId//XD3i+/9gSkhSV6Gk3VPlWncctI+JcOyERv85FUo7RxuxGy+z8A43Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.6.tgz", - "integrity": "sha512-4AWhgXmDuYN7rJI6ORB+uU9DHLq/erBbuMoAuB4VWJTu5KtCgcKYPynF0YI1VkBNuEfjNlLrFr9KZPJzrtLkrQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.6.tgz", - "integrity": "sha512-NgJPHHbEpLQgDH2MjQu90pzW/5vvXIZ7KOnPyNBm92A6WgZ/7b6fJyUBjoumLqeOQQGqY2QjQxRo97ah4Sj0cA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.1.tgz", - "integrity": "sha512-OkVXMix3EIbB5Z6yife2XTrSlOnVvCLR1Kg91I4pYFEsV9RbnoyQVScXCuVhGaZHOnTZgso8lMQN1Po2TadGKQ==", - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "raw-body": "^3.0.0", - "zod": "^3.23.8" - } - }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.15.tgz", - "integrity": "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==", - "license": "MIT", - "dependencies": { - "asn1js": "^3.0.5", - "pvtsutils": "^1.3.6", - "tslib": "^2.8.1" - } - }, - "node_modules/@peculiar/asn1-schema/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/@peculiar/json-schema": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", - "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@peculiar/webcrypto": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.1.tgz", - "integrity": "sha512-eK4C6WTNYxoI7JOabMoZICiyqRRtJB220bh0Mbj5RwRycleZf9BPyZoxsTvpP0FpmVS2aS13NKOuh5/tN3sIRw==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.0", - "@peculiar/json-schema": "^1.1.12", - "pvtsutils": "^1.3.2", - "tslib": "^2.4.1", - "webcrypto-core": "^1.7.4" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookies": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz", - "integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==", - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.14", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.14.tgz", - "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.18", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "license": "MIT" - }, - "node_modules/@types/keygrip": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.6.tgz", - "integrity": "sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==", - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.7.tgz", - "integrity": "sha512-1GM9z6BJOv86qkPvzh2i6VW5+VVrXxCLknfmTkWEqz+6DqosiY28XUWCTmBcJ0ACzKqx/iwdIREfo1fwExIlkA==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/asn1js": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.6.tgz", - "integrity": "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==", - "license": "BSD-3-Clause", - "dependencies": { - "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/asn1js/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "license": "MIT", - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==", - "license": "MIT" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.6", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.6.tgz", - "integrity": "sha512-GVuzuUwtdsghE3ocJ9Bs8PNoF13HNQ5TXbEi2AhvVb8xU1Iwt9Fos9FEamfoee+u/TOsn7GUWc04lz46n2bbTg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.6", - "@esbuild/android-arm": "0.25.6", - "@esbuild/android-arm64": "0.25.6", - "@esbuild/android-x64": "0.25.6", - "@esbuild/darwin-arm64": "0.25.6", - "@esbuild/darwin-x64": "0.25.6", - "@esbuild/freebsd-arm64": "0.25.6", - "@esbuild/freebsd-x64": "0.25.6", - "@esbuild/linux-arm": "0.25.6", - "@esbuild/linux-arm64": "0.25.6", - "@esbuild/linux-ia32": "0.25.6", - "@esbuild/linux-loong64": "0.25.6", - "@esbuild/linux-mips64el": "0.25.6", - "@esbuild/linux-ppc64": "0.25.6", - "@esbuild/linux-riscv64": "0.25.6", - "@esbuild/linux-s390x": "0.25.6", - "@esbuild/linux-x64": "0.25.6", - "@esbuild/netbsd-arm64": "0.25.6", - "@esbuild/netbsd-x64": "0.25.6", - "@esbuild/openbsd-arm64": "0.25.6", - "@esbuild/openbsd-x64": "0.25.6", - "@esbuild/openharmony-arm64": "0.25.6", - "@esbuild/sunos-x64": "0.25.6", - "@esbuild/win32-arm64": "0.25.6", - "@esbuild/win32-ia32": "0.25.6", - "@esbuild/win32-x64": "0.25.6" - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "license": "BSD-2-Clause" - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/js-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", - "integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT", - "peer": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/node-fetch-native": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.0.1.tgz", - "integrity": "sha512-VzW+TAk2wE4X9maiKMlT+GsPU4OMmR1U9CrHSmd3DFLn2IcZ9VJ6M6BBugGfYUnPCLSYxXdZy17M0BEJyhUTwg==", - "license": "MIT" - }, - "node_modules/pvtsutils": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.6.tgz", - "integrity": "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.8.1" - } - }, - "node_modules/pvtsutils/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/quick-lru": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", - "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/snake-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", - "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/snakecase-keys": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-3.2.1.tgz", - "integrity": "sha512-CjU5pyRfwOtaOITYv5C8DzpZ8XA/ieRsDpr93HI2r6e3YInC6moZpSQbmUtg8cTk58tq2x3jcG2gv+p1IZGmMA==", - "license": "MIT", - "dependencies": { - "map-obj": "^4.1.0", - "to-snake-case": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/swr": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.0.tgz", - "integrity": "sha512-AjqHOv2lAhkuUdIiBu9xbuettzAzWXmCEcLONNKJRba87WAefz8Ca9d6ds/SzrPc235n1IxWYdhJ2zF3MNUaoQ==", - "license": "MIT", - "dependencies": { - "use-sync-external-store": "^1.2.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/to-no-case": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/to-no-case/-/to-no-case-1.0.2.tgz", - "integrity": "sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg==", - "license": "MIT" - }, - "node_modules/to-snake-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-snake-case/-/to-snake-case-1.0.0.tgz", - "integrity": "sha512-joRpzBAk1Bhi2eGEYBjukEWHOe/IvclOkiJl3DtA91jV6NwQ3MwXA4FHYeqk8BNp/D8bmi9tcNbRu/SozP0jbQ==", - "license": "MIT", - "dependencies": { - "to-space-case": "^1.0.0" - } - }, - "node_modules/to-space-case": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-space-case/-/to-space-case-1.0.0.tgz", - "integrity": "sha512-rLdvwXZ39VOn1IxGL3V6ZstoTbwLRckQmn/U8ZDLuWwIXNpuZDhQ3AiRUlhTbOXFVE9C+dR51wM0CBDhk31VcA==", - "license": "MIT", - "dependencies": { - "to-no-case": "^1.0.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.20.3", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz", - "integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webcrypto-core": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.8.1.tgz", - "integrity": "sha512-P+x1MvlNCXlKbLSOY4cYrdreqPG5hbzkmawbcXLKN/mf6DZW0SdNNkZ+sjwsqVkI4A4Ko2sPZmkZtCKY58w83A==", - "license": "MIT", - "dependencies": { - "@peculiar/asn1-schema": "^2.3.13", - "@peculiar/json-schema": "^1.1.12", - "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.7.0" - } - }, - "node_modules/webcrypto-core/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/lib/javascript/fullstack_demo/mcp-server/package.json b/lib/javascript/fullstack_demo/mcp-server/package.json deleted file mode 100644 index 1c31b4eec..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/package.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "name": "todo-mcp-server", - "version": "1.0.0", - "description": "MCP server for Todo App integration", - "type": "module", - "main": "dist/index.js", - "scripts": { - "build": "tsc", - "dev": "tsx src/index.ts", - "start": "node dist/index.js", - "clean": "rm -rf dist" - }, - "dependencies": { - "@clerk/clerk-sdk-node": "^4.13.23", - "@modelcontextprotocol/sdk": "^0.6.0", - "@types/node": "^20.0.0", - "@types/node-fetch": "^2.6.12", - "dotenv": "^16.4.5", - "node-fetch": "^3.3.2" - }, - "devDependencies": { - "tsx": "^4.7.0", - "typescript": "^5.3.0" - }, - "engines": { - "node": ">=18.0.0" - } -} diff --git a/lib/javascript/fullstack_demo/mcp-server/src/api-client.ts b/lib/javascript/fullstack_demo/mcp-server/src/api-client.ts deleted file mode 100644 index 6e5922367..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/src/api-client.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { Todo } from './types/todo.js'; -// @ts-ignore - Using node-fetch for compatibility -import fetch from 'node-fetch'; - -export class TodoApiClient { - private baseUrl: string; - private sessionToken?: string; - - constructor(baseUrl: string, _deprecated_apiKey?: string) { - this.baseUrl = baseUrl; - // Note: API key parameter is deprecated in favor of session-based auth - } - - /** - * Set session token for authentication - */ - setSessionToken(sessionToken: string): void { - this.sessionToken = sessionToken; - console.debug('[API Client] Session token updated'); - } - - /** - * Clear session token - */ - clearSessionToken(): void { - this.sessionToken = undefined; - console.debug('[API Client] Session token cleared'); - } - - /** - * Check if client is authenticated - */ - isAuthenticated(): boolean { - return !!this.sessionToken; - } - - private async makeRequest( - endpoint: string, - options: RequestInit = {}, - ): Promise { - const url = `${this.baseUrl}${endpoint}`; - - const headers: Record = { - 'Content-Type': 'application/json', - }; - - // Use session token for authentication - if (this.sessionToken) { - headers['Authorization'] = `Bearer ${this.sessionToken}`; - console.error(`[API] Using session token authentication`); - } else { - console.error(`[API] Warning: No authentication token available`); - } - - console.error(`[API] ${options.method || 'GET'} ${url}`); - if (options.body) { - console.error(`[API] Body:`, options.body); - } - - const response = await (fetch as any)(url, { - ...options, - headers: { - ...headers, - ...options.headers, - }, - }); - - console.error(`[API] Response: ${response.status} ${response.statusText}`); - - if (!response.ok) { - const errorText = await response.text(); - console.error(`[API] Error response:`, errorText); - throw new Error( - `API request failed: ${response.status} ${response.statusText} - ${errorText}`, - ); - } - - // Handle empty responses (like DELETE operations) - if ( - response.status === 204 || - response.headers.get('content-length') === '0' - ) { - return null; - } - - return await response.json(); - } - - async getTodos(): Promise { - const todos = await this.makeRequest('/todos'); - return todos || []; - } - - async createTodo(text: string, completed = false): Promise { - const todo = { text, completed }; - return await this.makeRequest('/todos', { - method: 'POST', - body: JSON.stringify(todo), - }); - } - - async updateTodo( - id: number, - updates: Partial>, - ): Promise { - const updateData = { id, ...updates }; - return await this.makeRequest(`/todos/${id}`, { - method: 'PATCH', - body: JSON.stringify(updateData), - }); - } - - async deleteTodo(id: number): Promise { - await this.makeRequest(`/todos/${id}`, { - method: 'DELETE', - }); - } - - async toggleTodo(id: number): Promise { - // First get the current todo to know its current state - const todos = await this.getTodos(); - const todo = todos.find((t) => t.id === id); - - if (!todo) { - throw new Error(`Todo with ID ${id} not found`); - } - - return await this.updateTodo(id, { completed: !todo.completed }); - } - - async getTodoStats() { - const todos = await this.getTodos(); - - return { - total: todos.length, - completed: todos.filter((t) => t.completed).length, - pending: todos.filter((t) => !t.completed).length, - completionRate: - todos.length > 0 - ? ( - (todos.filter((t) => t.completed).length / todos.length) * - 100 - ).toFixed(1) + '%' - : '0%', - }; - } -} diff --git a/lib/javascript/fullstack_demo/mcp-server/src/auth/session-manager.ts b/lib/javascript/fullstack_demo/mcp-server/src/auth/session-manager.ts deleted file mode 100644 index 77a47f211..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/src/auth/session-manager.ts +++ /dev/null @@ -1,226 +0,0 @@ -import { createClerkClient } from '@clerk/clerk-sdk-node'; -import fs from 'fs/promises'; -import os from 'os'; -import path from 'path'; - -export interface UserSession { - userId: string; - sessionToken: string; - email?: string; - firstName?: string; - lastName?: string; - expiresAt: string; - createdAt: string; -} - -export class SessionManager { - private sessionsFile: string; - private clerk: ReturnType | null = null; - - constructor() { - // Store sessions in a file in the user's home directory - const homeDir = process.env.HOME || process.env.USERPROFILE || os.homedir(); - this.sessionsFile = path.join(homeDir, '.mcp-todo-sessions.json'); - - console.log(`[Session Manager] Sessions file path: ${this.sessionsFile}`); - - // Initialize Clerk if secret key is available - if (process.env.CLERK_SECRET_KEY) { - this.clerk = createClerkClient({ - secretKey: process.env.CLERK_SECRET_KEY, - }); - } - } - - /** - * Load sessions from file - */ - private async loadSessions(): Promise> { - try { - const data = await fs.readFile(this.sessionsFile, 'utf8'); - return JSON.parse(data); - } catch (error) { - // File doesn't exist or is invalid, return empty object - return {}; - } - } - - /** - * Save sessions to file - */ - private async saveSessions( - sessions: Record, - ): Promise { - await fs.writeFile(this.sessionsFile, JSON.stringify(sessions, null, 2)); - } - - /** - * Store a user session - */ - async storeSession(sessionToken: string): Promise { - if (!this.clerk) { - throw new Error( - 'Clerk is not initialized. Please provide CLERK_SECRET_KEY.', - ); - } - - try { - const decodedToken = await this.clerk.verifyToken(sessionToken); - - if (!decodedToken.sid) { - console.warn( - '[Session Manager] Invalid session token, no session ID found.', - ); - return null; - } - - // Get the actual session using the session ID from the token - const sessionData = await this.clerk.sessions.getSession( - decodedToken.sid, - ); - - if (!sessionData || sessionData.status !== 'active') { - console.warn( - `[Session Manager] Session not found or inactive for token: ${sessionToken}`, - ); - return null; - } - - // Get user details - const user = await this.clerk.users.getUser(sessionData.userId); - - const userSession: UserSession = { - userId: sessionData.userId, - sessionToken, - email: user.emailAddresses[0]?.emailAddress, - firstName: user.firstName || '', - lastName: user.lastName || '', - expiresAt: new Date(sessionData.expireAt).toISOString(), - createdAt: new Date().toISOString(), - }; - - // Load existing sessions - const sessions = await this.loadSessions(); - - // Store session with userId as key - sessions[sessionData.userId] = userSession; - - // Clean up expired sessions - await this.cleanupExpiredSessions(sessions); - - // Save updated sessions - await this.saveSessions(sessions); - - console.log( - `[Session Manager] Stored session for user: ${sessionData.userId}`, - ); - return userSession; - } catch (error) { - console.error('[Session Manager] Error storing session:', error); - return null; - } - } - - /** - * Get active session for current user (latest session) - */ - async getCurrentSession(): Promise { - const sessions = await this.loadSessions(); - - // Get all non-expired sessions - const activeSessions = Object.values(sessions).filter( - (session) => new Date(session.expiresAt) > new Date(), - ); - - if (activeSessions.length === 0) { - return null; - } - - // Return the most recent session - const latestSession = activeSessions.sort( - (a, b) => - new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), - )[0]; - - // Validate session is still active with Clerk - if (this.clerk) { - try { - // Extract session ID from the stored token - const decodedToken = JSON.parse( - Buffer.from( - latestSession.sessionToken.split('.')[1], - 'base64', - ).toString(), - ); - - if (!decodedToken.sid) { - await this.removeSession(latestSession.userId); - return null; - } - - const session = await this.clerk.sessions.getSession(decodedToken.sid); - if (!session || session.status !== 'active') { - await this.removeSession(latestSession.userId); - return null; - } - } catch (error) { - console.debug('[Session Manager] Session validation failed:', error); - await this.removeSession(latestSession.userId); - return null; - } - } - - return latestSession; - } - - /** - * Remove a user session - */ - async removeSession(userId: string): Promise { - const sessions = await this.loadSessions(); - delete sessions[userId]; - await this.saveSessions(sessions); - console.log(`[Session Manager] Removed session for user: ${userId}`); - } - - /** - * Remove all sessions (logout all users) - */ - async clearAllSessions(): Promise { - await this.saveSessions({}); - console.log('[Session Manager] Cleared all sessions'); - } - - /** - * Clean up expired sessions - */ - private async cleanupExpiredSessions( - sessions: Record, - ): Promise { - const now = new Date(); - let cleaned = false; - - for (const [userId, session] of Object.entries(sessions)) { - if (new Date(session.expiresAt) <= now) { - delete sessions[userId]; - cleaned = true; - } - } - - if (cleaned) { - console.log('[Session Manager] Cleaned up expired sessions'); - } - } - - /** - * List all active sessions - */ - async listActiveSessions(): Promise { - const sessions = await this.loadSessions(); - const now = new Date(); - - return Object.values(sessions).filter( - (session) => new Date(session.expiresAt) > now, - ); - } -} diff --git a/lib/javascript/fullstack_demo/mcp-server/src/index.ts b/lib/javascript/fullstack_demo/mcp-server/src/index.ts deleted file mode 100644 index 1a5ebf72b..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/src/index.ts +++ /dev/null @@ -1,643 +0,0 @@ -#!/usr/bin/env node - -import { Server } from '@modelcontextprotocol/sdk/server/index.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { - CallToolRequest, - CallToolRequestSchema, - ListToolsRequestSchema, -} from '@modelcontextprotocol/sdk/types.js'; -import dotenv from 'dotenv'; -import { TodoApiClient } from './api-client.js'; -import { SessionManager, UserSession } from './auth/session-manager.js'; -import { CreateTodoRequest, Todo, UpdateTodoRequest } from './types/todo.js'; - -// Load environment variables -dotenv.config(); - -class TodoMCPServer { - private server: Server; - private apiClient: TodoApiClient; - private sessionManager: SessionManager; - - constructor() { - const apiBaseUrl = - process.env.TODO_API_BASE_URL || 'http://localhost:3000/api/mcp'; - - this.apiClient = new TodoApiClient(apiBaseUrl); - this.sessionManager = new SessionManager(); - - this.server = new Server( - { - name: 'todo-mcp-server', - version: '1.0.0', - }, - { - capabilities: { - tools: {}, - }, - }, - ); - - this.setupToolHandlers(); - this.setupErrorHandling(); - - // Initialize with existing session if available - this.initializeSession(); - } - - /** - * Initialize with existing session on startup - */ - private async initializeSession(): Promise { - try { - const session = await this.sessionManager.getCurrentSession(); - if (session) { - this.apiClient.setSessionToken(session.sessionToken); - console.debug(`[MCP] Restored session for user: ${session.email}`); - } - } catch (error) { - console.debug('[MCP] No existing session to restore'); - } - } - - private setupErrorHandling(): void { - this.server.onerror = (error: Error) => { - console.debug('[MCP Error]', error); - }; - - process.on('SIGINT', async () => { - await this.server.close(); - process.exit(0); - }); - } - - private setupToolHandlers(): void { - this.server.setRequestHandler(ListToolsRequestSchema, async () => { - return { - tools: [ - { - name: 'get_todos', - description: 'Retrieve all todos for the authenticated user', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - { - name: 'create_todo', - description: 'Create a new todo item', - inputSchema: { - type: 'object', - properties: { - text: { - type: 'string', - description: 'The text content of the todo item', - }, - completed: { - type: 'boolean', - description: 'Whether the todo is completed (default: false)', - default: false, - }, - }, - required: ['text'], - }, - }, - { - name: 'update_todo', - description: 'Update an existing todo item', - inputSchema: { - type: 'object', - properties: { - id: { - type: 'number', - description: 'The ID of the todo to update', - }, - text: { - type: 'string', - description: 'The new text content of the todo item', - }, - completed: { - type: 'boolean', - description: 'Whether the todo is completed', - }, - }, - required: ['id'], - }, - }, - { - name: 'delete_todo', - description: 'Delete a todo item', - inputSchema: { - type: 'object', - properties: { - id: { - type: 'number', - description: 'The ID of the todo to delete', - }, - }, - required: ['id'], - }, - }, - { - name: 'toggle_todo', - description: 'Toggle the completion status of a todo item', - inputSchema: { - type: 'object', - properties: { - id: { - type: 'number', - description: 'The ID of the todo to toggle', - }, - }, - required: ['id'], - }, - }, - { - name: 'get_todo_stats', - description: - 'Get statistics about todos (total, completed, pending)', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - { - name: 'login', - description: - 'Login with your session token to authenticate with the todo app', - inputSchema: { - type: 'object', - properties: { - sessionToken: { - type: 'string', - description: 'Your Clerk session token from the todo app', - }, - }, - required: ['sessionToken'], - }, - }, - { - name: 'logout', - description: 'Logout and clear your session', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - { - name: 'get_auth_status', - description: 'Check your current authentication status', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - { - name: 'get_login_url', - description: - 'Get the URL to login to the todo app and obtain a session token', - inputSchema: { - type: 'object', - properties: {}, - required: [], - }, - }, - ], - }; - }); - - this.server.setRequestHandler( - CallToolRequestSchema, - async (request: CallToolRequest) => { - const { name, arguments: args } = request.params; - - try { - switch (name) { - case 'login': - if ( - !args || - typeof args !== 'object' || - !('sessionToken' in args) - ) { - throw new Error('Missing required parameter: sessionToken'); - } - return await this.login(args.sessionToken as string); - - case 'logout': - return await this.logout(); - - case 'get_auth_status': - return await this.getAuthStatus(); - - case 'get_login_url': - return await this.getLoginUrl(); - - case 'get_todos': - return await this.getTodos(); - - case 'create_todo': - if (!args || typeof args !== 'object' || !('text' in args)) { - throw new Error('Missing required parameter: text'); - } - return await this.createTodo( - args as unknown as CreateTodoRequest, - ); - - case 'update_todo': - if (!args || typeof args !== 'object' || !('id' in args)) { - throw new Error('Missing required parameter: id'); - } - return await this.updateTodo( - args as unknown as UpdateTodoRequest, - ); - - case 'delete_todo': - if (!args || typeof args !== 'object' || !('id' in args)) { - throw new Error('Missing required parameter: id'); - } - return await this.deleteTodo(args.id as number); - - case 'toggle_todo': - if (!args || typeof args !== 'object' || !('id' in args)) { - throw new Error('Missing required parameter: id'); - } - return await this.toggleTodo(args.id as number); - - case 'get_todo_stats': - return await this.getTodoStats(); - - default: - throw new Error(`Unknown tool: ${name}`); - } - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error: ${error instanceof Error ? error.message : String(error)}`, - }, - ], - isError: true, - }; - } - }, - ); - } - - private async getTodos() { - const session = await this.requireAuth(); - console.debug(`[MCP] Getting todos for user: ${session.email}...`); - - const todos = await this.apiClient.getTodos(); - console.debug(`[MCP] Retrieved ${todos.length} todos`); - - return { - content: [ - { - type: 'text', - text: JSON.stringify(todos, null, 2), - }, - ], - }; - } - - private async createTodo(args: CreateTodoRequest) { - const session = await this.requireAuth(); - console.debug( - `[MCP] Creating todo for user ${session.email}: "${args.text}"`, - ); - - const result = await this.apiClient.createTodo(args.text, args.completed); - console.debug(`[MCP] Todo created with ID: ${result}`); - - return { - content: [ - { - type: 'text', - text: `Todo created successfully with ID: ${result}`, - }, - ], - }; - } - - private async updateTodo(args: UpdateTodoRequest) { - const session = await this.requireAuth(); - - const updates: Partial> = {}; - - if (args.text !== undefined) { - updates.text = args.text; - } - - if (args.completed !== undefined) { - updates.completed = args.completed; - } - - console.debug(`[MCP] Updating todo ${args.id} for user ${session.email}`); - const result = await this.apiClient.updateTodo(args.id, updates); - - return { - content: [ - { - type: 'text', - text: `Todo updated successfully: ${JSON.stringify(result, null, 2)}`, - }, - ], - }; - } - - private async deleteTodo(id: number) { - const session = await this.requireAuth(); - console.debug(`[MCP] Deleting todo ${id} for user ${session.email}`); - - await this.apiClient.deleteTodo(id); - - return { - content: [ - { - type: 'text', - text: `Todo with ID ${id} deleted successfully`, - }, - ], - }; - } - - private async toggleTodo(id: number) { - const session = await this.requireAuth(); - console.debug(`[MCP] Toggling todo ${id} for user ${session.email}`); - - const result = await this.apiClient.toggleTodo(id); - - return { - content: [ - { - type: 'text', - text: `Todo toggled successfully: ${JSON.stringify(result, null, 2)}`, - }, - ], - }; - } - - private async getTodoStats() { - const session = await this.requireAuth(); - console.debug(`[MCP] Getting todo stats for user ${session.email}`); - - const stats = await this.apiClient.getTodoStats(); - - return { - content: [ - { - type: 'text', - text: JSON.stringify(stats, null, 2), - }, - ], - }; - } - - /** - * Check if user is authenticated and get session - */ - private async getActiveSession(): Promise { - return await this.sessionManager.getCurrentSession(); - } - - /** - * Require authentication for protected operations - */ - private async requireAuth(): Promise { - const session = await this.getActiveSession(); - if (!session) { - throw new Error( - 'Authentication required. Please use the "login" tool first.', - ); - } - return session; - } - - /** - * Login with session token - */ - private async login(sessionToken: string) { - try { - const session = await this.sessionManager.storeSession(sessionToken); - - if (!session) { - return { - content: [ - { - type: 'text', - text: 'Login failed. Invalid session token or session has expired.', - }, - ], - isError: true, - }; - } - - // Set session token on API client for immediate use - this.apiClient.setSessionToken(sessionToken); - - return { - content: [ - { - type: 'text', - text: `Successfully logged in as ${session.firstName} ${session.lastName} (${session.email}) - -You can now use all todo management commands: -- get_todos -- create_todo -- update_todo -- delete_todo -- toggle_todo -- get_todo_stats`, - }, - ], - }; - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Login failed: ${error instanceof Error ? error.message : String(error)}`, - }, - ], - isError: true, - }; - } - } - - /** - * Logout current user - */ - private async logout() { - try { - const session = await this.getActiveSession(); - - if (!session) { - return { - content: [ - { - type: 'text', - text: 'No active session found. You are already logged out.', - }, - ], - }; - } - - await this.sessionManager.removeSession(session.userId); - - // Clear session token from API client - this.apiClient.clearSessionToken(); - - return { - content: [ - { - type: 'text', - text: `Successfully logged out ${session.firstName} ${session.lastName}`, - }, - ], - }; - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Logout failed: ${error instanceof Error ? error.message : String(error)}`, - }, - ], - isError: true, - }; - } - } - - /** - * Get authentication status - */ - private async getAuthStatus() { - try { - const session = await this.getActiveSession(); - - if (!session) { - return { - content: [ - { - type: 'text', - text: JSON.stringify( - { - authenticated: false, - message: - 'Not logged in. Use the "login" tool with your session token.', - }, - null, - 2, - ), - }, - ], - }; - } - - const status = { - authenticated: true, - userId: session.userId, - email: session.email, - name: `${session.firstName} ${session.lastName}`.trim(), - expiresAt: session.expiresAt, - sessionAge: Math.floor( - (new Date().getTime() - new Date(session.createdAt).getTime()) / - 1000 / - 60, - ), // minutes - }; - - return { - content: [ - { - type: 'text', - text: JSON.stringify(status, null, 2), - }, - ], - }; - } catch (error) { - return { - content: [ - { - type: 'text', - text: `Error checking auth status: ${error instanceof Error ? error.message : String(error)}`, - }, - ], - isError: true, - }; - } - } - - /** - * Get login URL and instructions - */ - private async getLoginUrl() { - const baseUrl = - process.env.TODO_API_BASE_URL?.replace('/api/mcp', '') || - 'http://localhost:3000'; - - return { - content: [ - { - type: 'text', - text: `To login to the Todo app: - -**Method 1: Using the Session API (Recommended)** -1. Open your browser and go to: ${baseUrl} -2. Sign in with your Clerk account -3. Visit: ${baseUrl}/api/mcp/session -4. Copy the sessionToken from the response -5. Use the "login" tool with that token - -**Method 2: Using Browser DevTools** -1. Open your browser and go to: ${baseUrl} -2. Sign in with your Clerk account -3. Open the browser's developer tools (F12) -4. Go to the Application/Storage tab -5. Find the "__session" cookie and copy its value -6. Use the "login" tool with that session token - -**Example:** -login with sessionToken: "sess_2h..." - -The session token will look like: sess_xxxxxxxxxxxxxxxxxxxxxxxxxx - -After logging in successfully, you'll be able to use all todo management tools with your personal account.`, - }, - ], - }; - } - - async run(): Promise { - const transport = new StdioServerTransport(); - await this.server.connect(transport); - console.log('Todo MCP server running on stdio'); - console.debug( - `API Base URL: ${process.env.TODO_API_BASE_URL || 'http://localhost:3000/api/mcp'}`, - ); - console.debug( - `Clerk Secret Key configured: ${process.env.CLERK_SECRET_KEY ? 'Yes' : 'No'}`, - ); - - // Check if user has an active session - try { - const session = await this.getActiveSession(); - if (session) { - console.debug(`Active session found for: ${session.email}`); - } else { - console.debug('No active session. User will need to login.'); - } - } catch (error) { - console.debug('Session check failed'); - } - } -} - -const server = new TodoMCPServer(); -server.run().catch(console.error); diff --git a/lib/javascript/fullstack_demo/mcp-server/src/types/todo.ts b/lib/javascript/fullstack_demo/mcp-server/src/types/todo.ts deleted file mode 100644 index 1bba23ee8..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/src/types/todo.ts +++ /dev/null @@ -1,23 +0,0 @@ -export interface Todo { - id: number; - text: string; - completed: boolean; -} - -export interface CreateTodoRequest { - text: string; - completed?: boolean; -} - -export interface UpdateTodoRequest { - id: number; - text?: string; - completed?: boolean; -} - -export interface TodoStats { - total: number; - completed: number; - pending: number; - completionRate: string; -} diff --git a/lib/javascript/fullstack_demo/mcp-server/test-connection.mjs b/lib/javascript/fullstack_demo/mcp-server/test-connection.mjs deleted file mode 100644 index 47e94c885..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/test-connection.mjs +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env node - -import dotenv from 'dotenv'; -import { TodoApiClient } from './dist/api-client.js'; - -dotenv.config(); - -async function testApiConnection() { - console.log('🧪 Testing MCP API Connection...\n'); - - const apiBaseUrl = - process.env.TODO_API_BASE_URL || 'http://localhost:3000/api/mcp'; - const apiKey = process.env.MCP_API_KEY; - - console.log(`📡 API Base URL: ${apiBaseUrl}`); - console.log(`🔑 API Key configured: ${apiKey ? 'Yes' : 'No'}`); - - if (!apiKey) { - console.log( - '❌ No API key found. Please set MCP_API_KEY in your .env file', - ); - console.log('💡 Run: node generate-api-key.mjs to generate one'); - return; - } - - const client = new TodoApiClient(apiBaseUrl, apiKey); - - try { - console.log('\n🔍 Testing GET /todos...'); - const todos = await client.getTodos(); - console.log(`✅ Success! Retrieved ${todos.length} todos:`, todos); - - console.log('\n🔍 Testing POST /todos...'); - const newTodoId = await client.createTodo( - 'Test todo from MCP client', - false, - ); - console.log(`✅ Success! Created todo with ID: ${newTodoId}`); - - console.log('\n🔍 Testing GET /todos again...'); - const updatedTodos = await client.getTodos(); - console.log(`✅ Success! Now have ${updatedTodos.length} todos`); - - console.log( - '\n🎉 All tests passed! Your MCP server should work with Claude Desktop.', - ); - } catch (error) { - console.error('\n❌ Connection test failed:', error.message); - console.log('\n🔧 Troubleshooting steps:'); - console.log('1. Make sure your todo app is running: npm run dev'); - console.log('2. Check that the API key matches in both .env files'); - console.log('3. Verify the API URL is correct'); - console.log('4. Check the server logs for more details'); - } -} - -testApiConnection(); diff --git a/lib/javascript/fullstack_demo/mcp-server/test-fetch.mjs b/lib/javascript/fullstack_demo/mcp-server/test-fetch.mjs deleted file mode 100644 index a5967c6e2..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/test-fetch.mjs +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env node - -// Test that fetch works with our MCP server -import { TodoApiClient } from './dist/api-client.js'; - -async function testFetch() { - console.log('🧪 Testing fetch functionality...\n'); - - try { - const client = new TodoApiClient( - 'http://localhost:3000/api/mcp', - 'test-key', - ); - console.log('✅ TodoApiClient created successfully'); - console.log('✅ fetch polyfill loaded correctly'); - - console.log( - '\n📡 If your todo server is running on port 3000, the MCP server should work in Claude Desktop now!', - ); - console.log('\n🔧 Steps to test in Claude Desktop:'); - console.log(' 1. Make sure your todo app is running on port 3000'); - console.log(' 2. Restart Claude Desktop completely'); - console.log(' 3. Ask: "Can you show me my todos?"'); - } catch (error) { - console.error('❌ Error testing fetch:', error.message); - process.exit(1); - } -} - -testFetch(); diff --git a/lib/javascript/fullstack_demo/mcp-server/test.mjs b/lib/javascript/fullstack_demo/mcp-server/test.mjs deleted file mode 100644 index 2573f4cf9..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/test.mjs +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env node - -import { TodoApiClient } from './dist/api-client.js'; - -async function testMCPServer() { - console.log('Testing Todo MCP Server...\n'); - - // Mock API client for testing (since we don't have the real API running) - const apiBaseUrl = 'http://localhost:3000/api'; - const client = new TodoApiClient(apiBaseUrl); - - try { - console.log('✅ MCP Server built successfully'); - console.log('✅ All imports resolved correctly'); - console.log('✅ TypeScript compilation passed'); - - console.log('\n📝 Available MCP Tools:'); - console.log(' - get_todos: Retrieve all todos'); - console.log(' - create_todo: Create a new todo'); - console.log(' - update_todo: Update an existing todo'); - console.log(' - delete_todo: Delete a todo'); - console.log(' - toggle_todo: Toggle todo completion'); - console.log(' - get_todo_stats: Get todo statistics'); - - console.log('\n🚀 MCP Server is ready to use!'); - console.log('\nTo start the server:'); - console.log(' npm start'); - console.log('\nTo run in development mode:'); - console.log(' npm run dev'); - } catch (error) { - console.error('❌ Test failed:', error); - process.exit(1); - } -} - -testMCPServer(); diff --git a/lib/javascript/fullstack_demo/mcp-server/tsconfig.json b/lib/javascript/fullstack_demo/mcp-server/tsconfig.json deleted file mode 100644 index 8056a3d01..000000000 --- a/lib/javascript/fullstack_demo/mcp-server/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "node", - "outDir": "./dist", - "rootDir": "./src", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "resolveJsonModule": true, - "allowSyntheticDefaultImports": true - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} diff --git a/lib/javascript/fullstack_demo/src/app/api/mcp/debug/route.ts b/lib/javascript/fullstack_demo/src/app/api/mcp/debug/route.ts deleted file mode 100644 index 71cc73adf..000000000 --- a/lib/javascript/fullstack_demo/src/app/api/mcp/debug/route.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { NextResponse } from 'next/server'; - -export async function GET() { - console.log('[DEBUG] /api/mcp/debug route called'); - - return NextResponse.json({ - message: 'MCP debug route is working!', - timestamp: new Date().toISOString(), - env: { - MCP_API_KEY_SET: !!process.env.MCP_API_KEY, - MCP_DEFAULT_USER_ID: process.env.MCP_DEFAULT_USER_ID, - }, - }); -} diff --git a/lib/javascript/fullstack_demo/src/app/api/mcp/session/route.ts b/lib/javascript/fullstack_demo/src/app/api/mcp/session/route.ts deleted file mode 100644 index 13e6f308d..000000000 --- a/lib/javascript/fullstack_demo/src/app/api/mcp/session/route.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { auth, clerkClient } from '@clerk/nextjs/server'; -import { NextResponse } from 'next/server'; - -/** - * Get session token for MCP authentication - */ -export async function GET() { - console.log('[MCP Auth] GET /api/mcp/session called'); - - try { - const { userId, sessionId, getToken } = await auth(); - - if (!userId || !sessionId) { - console.log('[MCP Auth] No active session found'); - return new Response('Unauthorized - No active session', { status: 401 }); - } - - // Get the session token - const sessionToken = await getToken(); - - if (!sessionToken) { - console.log('[MCP Auth] Could not retrieve session token'); - return new Response('Unauthorized - Could not retrieve session token', { - status: 401, - }); - } - - console.log(`[MCP Auth] Session token generated for user: ${userId}`); - - // Get user details for display - const client = await clerkClient(); - const user = await client.users.getUser(userId); - - return NextResponse.json({ - sessionToken, - userId, - sessionId, - user: { - email: user.emailAddresses[0]?.emailAddress, - firstName: user.firstName, - lastName: user.lastName, - }, - instructions: { - usage: 'Copy the sessionToken and use it with the MCP login tool', - example: `login with sessionToken: "${sessionToken.substring(0, 20)}..."`, - }, - }); - } catch (error) { - console.error('[MCP Auth] Error generating session token:', error); - return new Response('Internal Server Error', { status: 500 }); - } -} diff --git a/lib/javascript/fullstack_demo/src/app/api/mcp/todos/[id]/route.ts b/lib/javascript/fullstack_demo/src/app/api/mcp/todos/[id]/route.ts deleted file mode 100644 index b18bd521b..000000000 --- a/lib/javascript/fullstack_demo/src/app/api/mcp/todos/[id]/route.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { createTodoRepository } from '@/repositories'; -import { authenticateRequest } from '@/util/mcp-auth'; -import { NextResponse } from 'next/server'; - -const todoRepository = createTodoRepository(); - -/** - * Delete a todo for MCP client - */ -export async function DELETE( - request: Request, - { params }: { params: Promise<{ id: string }> }, -) { - const userId = await authenticateRequest(request); - - if (!userId) { - return new Response('Unauthorized - Invalid API Key', { status: 401 }); - } - - const { id } = await params; - - try { - await todoRepository.delete(Number(id), userId); - return new Response('No content', { status: 200 }); - } catch (error) { - console.error('Error deleting todo:', error); - return new Response('Internal Server Error', { status: 500 }); - } -} - -/** - * Update a todo for MCP client - */ -export async function PATCH(request: Request) { - const userId = await authenticateRequest(request); - - if (!userId) { - return new Response('Unauthorized - Invalid API Key', { status: 401 }); - } - - const todo = await request.json(); - - try { - const updatedTodo = await todoRepository.patch(todo, userId); - return NextResponse.json(updatedTodo); - } catch (error) { - console.error('Error updating todo:', error); - return new Response('Internal Server Error', { status: 500 }); - } -} diff --git a/lib/javascript/fullstack_demo/src/app/api/mcp/todos/route.ts b/lib/javascript/fullstack_demo/src/app/api/mcp/todos/route.ts deleted file mode 100644 index 035a2b713..000000000 --- a/lib/javascript/fullstack_demo/src/app/api/mcp/todos/route.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { createTodoRepository } from '@/repositories'; -import { authenticateRequest } from '@/util/mcp-auth'; -import { NextResponse } from 'next/server'; - -const todoRepository = createTodoRepository(); - -/** - * Retrieve all todos for MCP client - */ -export async function GET(request: Request) { - console.log('[MCP API] GET /api/mcp/todos called'); - - const userId = await authenticateRequest(request); - - if (!userId) { - console.log('[MCP API] Authentication failed'); - return new Response('Unauthorized - Invalid API Key', { status: 401 }); - } - - console.log(`[MCP API] Authenticated as user: ${userId}`); - - try { - const todos = await todoRepository.getAll(userId); - console.log(`[MCP API] Retrieved ${todos?.length || 0} todos`); - return NextResponse.json(todos || []); - } catch (error) { - console.error('[MCP API] Error fetching todos:', error); - return new Response('Internal Server Error', { status: 500 }); - } -} - -/** - * Create a new todo for MCP client - */ -export async function POST(request: Request) { - console.log('[MCP API] POST /api/mcp/todos called'); - - const userId = await authenticateRequest(request); - - if (!userId) { - console.log('[MCP API] Authentication failed'); - return new Response('Unauthorized - Invalid API Key', { status: 401 }); - } - - const todo = await request.json(); - console.log(`[MCP API] Creating todo for user ${userId}:`, todo); - - try { - const id = await todoRepository.create(todo, userId); - return NextResponse.json(id); - } catch (error) { - console.error('Error creating todo:', error); - return new Response('Internal Server Error', { status: 500 }); - } -} diff --git a/lib/javascript/fullstack_demo/src/middleware.ts b/lib/javascript/fullstack_demo/src/middleware.ts index 93e5061dc..5a8c9a643 100644 --- a/lib/javascript/fullstack_demo/src/middleware.ts +++ b/lib/javascript/fullstack_demo/src/middleware.ts @@ -4,14 +4,12 @@ import { v4 as uuidv4 } from 'uuid'; const CORRELATION_ID_HEADER = 'x-correlation-id'; const isProtectedRoute = createRouteMatcher(['/(.*)']); -const isMcpApiRoute = createRouteMatcher(['/api/mcp/(.*)']); export default clerkMiddleware(async (auth, req) => { const correlationId = uuidv4(); req.headers.set(CORRELATION_ID_HEADER, correlationId); - // Skip Clerk protection for MCP API routes (they have their own API key auth) - if (isProtectedRoute(req) && !isMcpApiRoute(req)) { + if (isProtectedRoute(req)) { await auth.protect(); } diff --git a/lib/javascript/fullstack_demo/src/util/mcp-auth.ts b/lib/javascript/fullstack_demo/src/util/mcp-auth.ts deleted file mode 100644 index 16c74388d..000000000 --- a/lib/javascript/fullstack_demo/src/util/mcp-auth.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { clerkClient, verifyToken } from '@clerk/nextjs/server'; - -/** - * Authenticate MCP requests using session tokens - */ -export async function authenticateRequest( - request: Request, -): Promise { - console.debug('[MCP Auth] Attempting session token authentication...'); - - // Extract Bearer token from Authorization header - const authHeader = request.headers.get('Authorization'); - if (!authHeader?.startsWith('Bearer ')) { - console.debug('[MCP Auth] No Bearer token found in Authorization header'); - return null; - } - - const sessionToken = authHeader.substring(7); - if (!sessionToken) { - console.debug('[MCP Auth] Empty session token'); - return null; - } - - try { - // Verify the session token using Clerk - const { sid } = await verifyToken( - request.headers.get('Authorization')?.replace('Bearer ', '') || '', - { - secretKey: process.env.CLERK_SECRET_KEY, - }, - ); - - // Get session by token using Clerk client - const client = await clerkClient(); - const session = await client.sessions.getSession(sid); - - if (!session || !session.userId || session.status !== 'active') { - console.debug('[MCP Auth] Invalid or inactive session'); - return null; - } - - console.debug( - `[MCP Auth] Session authentication successful for user: ${session.userId}`, - ); - return session.userId; - } catch (error) { - console.debug('[MCP Auth] Session validation failed:', error); - return null; - } -} From 1885bc68da1be6668ded3198ee67eed294ba0b40 Mon Sep 17 00:00:00 2001 From: "Anthony D. Mays" Date: Wed, 12 Nov 2025 19:23:15 -0800 Subject: [PATCH 2/2] chore: rewrites mcp server functionality using clerk mcp tools Signed-off-by: Anthony D. Mays --- lib/javascript/fullstack_demo/README.md | 17 + .../fullstack_demo/docs/MCP_SERVER_SETUP.md | 285 +++++++++++ .../fullstack_demo/package-lock.json | 474 +++++++++++++----- lib/javascript/fullstack_demo/package.json | 13 +- .../scripts/generate-mcp-config.js | 99 ++++ .../fullstack_demo/scripts/setup-https.sh | 68 +++ .../oauth-authorization-server/route.ts | 9 + .../oauth-protected-resource/mcp/route.ts | 11 + .../src/app/[transport]/route.ts | 163 ++++++ .../fullstack_demo/src/middleware.ts | 11 + .../repositories/postgres-todo-repository.ts | 1 + 11 files changed, 1014 insertions(+), 137 deletions(-) create mode 100644 lib/javascript/fullstack_demo/docs/MCP_SERVER_SETUP.md create mode 100755 lib/javascript/fullstack_demo/scripts/generate-mcp-config.js create mode 100644 lib/javascript/fullstack_demo/scripts/setup-https.sh create mode 100644 lib/javascript/fullstack_demo/src/app/.well-known/oauth-authorization-server/route.ts create mode 100644 lib/javascript/fullstack_demo/src/app/.well-known/oauth-protected-resource/mcp/route.ts create mode 100644 lib/javascript/fullstack_demo/src/app/[transport]/route.ts diff --git a/lib/javascript/fullstack_demo/README.md b/lib/javascript/fullstack_demo/README.md index ec957c2d3..f39c10fca 100644 --- a/lib/javascript/fullstack_demo/README.md +++ b/lib/javascript/fullstack_demo/README.md @@ -14,6 +14,23 @@ npm run db:start npm run dev ``` +## MCP Server - AI Agent Integration + +This app includes a Model Context Protocol (MCP) server that allows AI agents to interact with your todos through natural language commands. + +### Quick Setup + +1. Enable Dynamic Client Registration in [Clerk Dashboard](https://dashboard.clerk.com/) → OAuth Applications +2. Start the dev server: `npm run dev` +3. Connect your AI agent: + ```bash + npx -y mcp-remote http://localhost:3000/mcp + ``` + +Your AI agent can now manage todos through natural language! Try: "Show me my todos" or "Create a todo to buy groceries" + +📚 **Full Documentation**: See [docs/MCP_QUICKSTART.md](./docs/MCP_QUICKSTART.md) and [docs/MCP_SERVER_SETUP.md](./docs/MCP_SERVER_SETUP.md) + ## Architecture The app is built using the React library running on the NextJS framework. The homepage is located at [src/app/page.tsx](./src/app/page.tsx) and contains the main `TodoApp` component. diff --git a/lib/javascript/fullstack_demo/docs/MCP_SERVER_SETUP.md b/lib/javascript/fullstack_demo/docs/MCP_SERVER_SETUP.md new file mode 100644 index 000000000..c9094b489 --- /dev/null +++ b/lib/javascript/fullstack_demo/docs/MCP_SERVER_SETUP.md @@ -0,0 +1,285 @@ +# MCP Server Setup for fullstack_demo + +This document explains how to use the Model Context Protocol (MCP) server that has been integrated into the fullstack_demo application. This allows AI agents like Claude Desktop to securely interact with your todos through a standardized protocol. + +## What is MCP? + +Model Context Protocol (MCP) is a standardized way for AI assistants to securely connect to external tools and services. This implementation allows Claude Desktop and other MCP-compatible AI agents to: +- View all your todos +- Create new todos +- Update existing todos +- Delete todos +- Access your Clerk user data + +All operations are authenticated using Clerk OAuth tokens, ensuring secure access to your data. + +## Architecture + +The MCP server is built using: +- **@vercel/mcp-adapter**: Handles the core MCP protocol logic +- **@clerk/mcp-tools**: Integrates Clerk OAuth authentication with MCP +- **Next.js App Router**: Provides the API endpoints + +### Key Components + +1. **MCP Route Handler** (`src/app/[transport]/route.ts`) + - Defines MCP tools for todo operations + - Supports both `/mcp` (HTTP) and `/sse` (Server-Sent Events) transports + - Authenticates requests using Clerk OAuth tokens + +2. **OAuth Metadata Endpoints** + - `/.well-known/oauth-protected-resource/mcp` - Resource metadata + - `/.well-known/oauth-authorization-server` - Authorization server metadata (backward compatibility) + +3. **Middleware** (`src/middleware.ts`) + - Updated to allow public access to `.well-known` endpoints + - Maintains authentication for all other routes + +## Setup Instructions + +### 1. Enable Dynamic Client Registration in Clerk + +**IMPORTANT**: Before the MCP server can work, you must enable dynamic client registration in your Clerk Dashboard. + +1. Go to the [Clerk Dashboard](https://dashboard.clerk.com/) +2. Navigate to **Configure** → **OAuth Applications** +3. Toggle on **Dynamic client registration** + +This allows MCP-compatible clients to automatically register themselves during the OAuth flow. + +### 2. Start the Development Server + +```bash +cd /Users/anthonymays/source/forks/code-society-25-2/lib/javascript/fullstack_demo +npm run dev +``` + +The server will start on `http://localhost:3000` (or your configured port). + +### 3. Connect AI Agents to Your MCP Server + +#### Recommended Method: Using mcp-remote + +The easiest way to connect AI agents to your MCP server is using the `mcp-remote` utility: + +```bash +npx -y mcp-remote http://localhost:3000/mcp +``` + +This command: +- Automatically configures the MCP client +- Handles OAuth authentication with Clerk +- Works with any MCP-compatible AI agent +- Requires no manual configuration file editing + +#### Alternative Method: Manual Configuration + +If you prefer to manually configure your AI agent: + +1. Locate your Claude Desktop configuration file: + - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` + - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` + +2. Add your MCP server configuration: + +```json +{ + "mcpServers": { + "fullstack_demo_todos": { + "url": "http://localhost:3000/mcp", + "transport": "streamable-http", + "auth": { + "type": "oauth2", + "authorizationUrl": "http://localhost:3000/.well-known/oauth-authorization-server" + } + } + } +} +``` + +**Note**: The `tokenUrl` is automatically discovered from the authorization server metadata. + +3. Restart your AI agent + +4. When the agent tries to use the MCP server, you'll be prompted to authenticate via Clerk OAuth. + +### 4. Production Deployment + +When deploying to production: + +1. Connect to your production MCP server using `mcp-remote`: + ```bash + npx -y mcp-remote https://your-production-domain.com/mcp + ``` + + Or update your AI agent's configuration manually: + ```json + { + "mcpServers": { + "fullstack_demo_todos": { + "url": "https://your-production-domain.com/mcp", + "transport": "streamable-http", + "auth": { + "type": "oauth2", + "authorizationUrl": "https://your-production-domain.com/.well-known/oauth-authorization-server" + } + } + } + } + ``` + +2. Ensure your production environment has all required environment variables set: + - `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` + - `CLERK_SECRET_KEY` + - Database connection strings + - Any other app-specific variables + +## Available MCP Tools + +### 1. `get-todos` +Gets all todos for the authenticated user. + +**Parameters**: None + +**Example Response**: +```json +[ + { + "id": 1, + "text": "Buy groceries", + "completed": false + }, + { + "id": 2, + "text": "Finish homework", + "completed": true + } +] +``` + +### 2. `create-todo` +Creates a new todo for the authenticated user. + +**Parameters**: +- `text` (string, required): The text content of the todo +- `completed` (boolean, optional): Whether the todo is completed (defaults to false) + +**Example Response**: +```json +{ + "id": 1234567890, + "text": "New todo item", + "completed": false +} +``` + +### 3. `update-todo` +Updates an existing todo for the authenticated user. + +**Parameters**: +- `id` (number, required): The ID of the todo to update +- `text` (string, optional): The new text content of the todo +- `completed` (boolean, optional): Whether the todo is completed + +**Example Response**: +```json +{ + "id": 1234567890, + "text": "Updated todo text", + "completed": true +} +``` + +### 4. `delete-todo` +Deletes a todo for the authenticated user. + +**Parameters**: +- `id` (number, required): The ID of the todo to delete + +**Example Response**: +```json +{ + "success": true, + "deletedId": 1234567890 +} +``` + +### 5. `get-clerk-user-data` +Gets data about the Clerk user that authorized the request. + +**Parameters**: None + +**Example Response**: +```json +{ + "id": "user_xxxxxxxxxxxxx", + "firstName": "John", + "lastName": "Doe", + "emailAddresses": [...], + ... +} +``` + +## Using the MCP Server with Claude + +Once configured, you can interact with your todos naturally through Claude: + +**Example Prompts**: +- "Show me all my todos" +- "Create a new todo to buy milk" +- "Mark todo #123 as completed" +- "Delete the todo about buying groceries" +- "What todos do I have pending?" + +Claude will use the appropriate MCP tools to interact with your todo list securely. + +## Security + +- All MCP requests are authenticated using Clerk OAuth tokens +- The middleware ensures `.well-known` endpoints are publicly accessible for OAuth discovery +- All other routes remain protected by Clerk authentication +- Each user can only access their own todos (enforced by the todoRepository) + +## Troubleshooting + +### MCP Server Not Connecting + +1. Verify the development server is running +2. Check that dynamic client registration is enabled in Clerk Dashboard +3. Ensure the URLs in Claude Desktop config match your server URLs +4. Check browser/Claude console for error messages + +### Authentication Errors + +1. Verify your Clerk API keys are set correctly in `.env` +2. Ensure the Clerk Frontend API URL in Claude config is correct +3. Try re-authenticating by disconnecting and reconnecting the MCP server in Claude + +### Tool Execution Errors + +1. Check the Next.js server logs for detailed error messages +2. Verify the database is accessible and properly configured +3. Ensure the authenticated user has permission to perform the action + +## Files Created/Modified + +### New Files +- `src/app/[transport]/route.ts` - Main MCP server handler +- `src/app/.well-known/oauth-protected-resource/mcp/route.ts` - OAuth resource metadata +- `src/app/.well-known/oauth-authorization-server/route.ts` - OAuth server metadata + +### Modified Files +- `src/middleware.ts` - Updated to allow public access to `.well-known` endpoints +- `package.json` - Added MCP dependencies + +### Dependencies Added +- `@vercel/mcp-adapter` - MCP protocol handler +- `@clerk/mcp-tools` - Clerk OAuth integration for MCP + +## Additional Resources + +- [Clerk MCP Documentation](https://clerk.com/docs/nextjs/guides/development/mcp/build-mcp-server) +- [MCP Specification](https://modelcontextprotocol.io/) +- [Clerk OAuth Documentation](https://clerk.com/docs/guides/development/verifying-oauth-access-tokens) +- [Vercel MCP Adapter](https://github.com/vercel/mcp-adapter) +- [Example Repository](https://github.com/clerk/mcp-nextjs-example) diff --git a/lib/javascript/fullstack_demo/package-lock.json b/lib/javascript/fullstack_demo/package-lock.json index 20a413e95..861a1b9b9 100644 --- a/lib/javascript/fullstack_demo/package-lock.json +++ b/lib/javascript/fullstack_demo/package-lock.json @@ -10,15 +10,19 @@ "hasInstallScript": true, "dependencies": { "@clerk/clerk-sdk-node": "^4.13.23", - "@clerk/nextjs": "^6.9.2", + "@clerk/mcp-tools": "^0.3.1", + "@clerk/nextjs": "^6.35.1", "@modelcontextprotocol/sdk": "^1.20.0", "@prisma/client": "^6.9.0", "@upstash/redis": "^1.34.3", + "@vercel/mcp-adapter": "^1.0.0", + "dotenv": "^17.2.3", "lowdb": "^7.0.1", "lucide-react": "^0.468.0", "next": "^14.2.22", "uuid": "^11.0.5", - "winston": "^3.17.0" + "winston": "^3.17.0", + "zod": "^3.25.76" }, "devDependencies": { "@clerk/testing": "^1.7.5", @@ -418,6 +422,7 @@ "version": "1.34.0", "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-1.34.0.tgz", "integrity": "sha512-9rZ8hQJVpX5KX2bEpiuVXfpjhojQCiqCWADJDdCI0PCeKxn58Ep0JPYiIcczg4VKUc3a7jve9vXylykG2XajLQ==", + "dev": true, "license": "MIT", "dependencies": { "@clerk/shared": "^3.9.5", @@ -438,51 +443,21 @@ } } }, - "node_modules/@clerk/backend/node_modules/@clerk/shared": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.9.5.tgz", - "integrity": "sha512-KeIug5qV4LnzZD+16SLkJvdONPs2HQ7I1A7jbHYOGB37vQrQrus64Wu5XeNzbWFTN1Z5fAPSGuja8MfT2cBT4A==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@clerk/types": "^4.59.3", - "dequal": "2.0.3", - "glob-to-regexp": "0.4.1", - "js-cookie": "3.0.5", - "std-env": "^3.9.0", - "swr": "^2.3.3" - }, - "engines": { - "node": ">=18.17.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } - } - }, "node_modules/@clerk/backend/node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, "license": "0BSD" }, "node_modules/@clerk/clerk-react": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.21.0.tgz", - "integrity": "sha512-WGIYKeXA/cpvWj2NiMsWYJfRq4Npy6bM9ZrcTENXVox9jpk6iGggoX9zFJG9NBx5LDHBV2kgvpRdhnF/cnrJ+w==", + "version": "5.55.0", + "resolved": "https://registry.npmjs.org/@clerk/clerk-react/-/clerk-react-5.55.0.tgz", + "integrity": "sha512-CWLV0f87utJY2tS40n7imCPJHP2+DPwhoZhCRS2dtYd/c29kLAR5rgydBAy4KkvzZLCPG3Hyt4Vlw1EuMLTq1A==", "license": "MIT", "dependencies": { - "@clerk/shared": "^2.20.4", - "@clerk/types": "^4.40.0", - "tslib": "2.4.1" + "@clerk/shared": "^3.34.0", + "tslib": "2.8.1" }, "engines": { "node": ">=18.17.0" @@ -492,6 +467,12 @@ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" } }, + "node_modules/@clerk/clerk-react/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@clerk/clerk-sdk-node": { "version": "4.13.23", "resolved": "https://registry.npmjs.org/@clerk/clerk-sdk-node/-/clerk-sdk-node-4.13.23.tgz", @@ -644,42 +625,88 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@clerk/mcp-tools": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@clerk/mcp-tools/-/mcp-tools-0.3.1.tgz", + "integrity": "sha512-c/7bmT9KxA3CYVhCS08RGmVwjU8+zJ6ZCuIA5KpcJfE5xh8XLKKvZzOD4mP0GgvUGjcnT+IViaqBRlRKAyE5rw==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.17.0" + }, + "peerDependencies": { + "better-sqlite3": "^8.7.0", + "pg": "^8.11.0", + "redis": "^4.0.0" + }, + "peerDependenciesMeta": { + "better-sqlite3": { + "optional": true + }, + "pg": { + "optional": true + }, + "redis": { + "optional": true + } + } + }, "node_modules/@clerk/nextjs": { - "version": "6.9.2", - "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.9.2.tgz", - "integrity": "sha512-1OoG+UGmU2kVPirC6Rh1My/cerwoxxSUxBJWJMvEbyXfM2hT0fqEl7L+BoTGSpJcJlGBw5e7tvtgHIqA4cUfZw==", + "version": "6.35.1", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-6.35.1.tgz", + "integrity": "sha512-J398mgB/qTmhGOdCQ76YnwDftiI3ry01TJOP82yL+dsNKCDlC4G/ocv3fa4iJ1kw9h6NaUXdEXt0VJQjs12XkA==", "license": "MIT", "dependencies": { - "@clerk/backend": "^1.21.2", - "@clerk/clerk-react": "^5.20.2", - "@clerk/shared": "^2.20.2", - "@clerk/types": "^4.39.4", - "crypto-js": "4.2.0", + "@clerk/backend": "^2.22.0", + "@clerk/clerk-react": "^5.55.0", + "@clerk/shared": "^3.34.0", + "@clerk/types": "^4.100.0", "server-only": "0.0.1", - "tslib": "2.4.1" + "tslib": "2.8.1" }, "engines": { "node": ">=18.17.0" }, "peerDependencies": { - "next": "^13.5.4 || ^14.0.3 || ^15.0.0", + "next": "^13.5.7 || ^14.2.25 || ^15.2.3 || ^16", "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" } }, + "node_modules/@clerk/nextjs/node_modules/@clerk/backend": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.22.0.tgz", + "integrity": "sha512-d/PWhQ9l531SJArX3vYXzAMPLYkkSmfVOl6/MxuiCWTUzx8J8ZIxXT/duXQNM6jM8NRfyQbnt+kLPoE+L0zBlg==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.34.0", + "@clerk/types": "^4.100.0", + "cookie": "1.0.2", + "standardwebhooks": "^1.0.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/nextjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/@clerk/shared": { - "version": "2.20.4", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-2.20.4.tgz", - "integrity": "sha512-1ndGEO+NejIMFkl47DCeSpVv3nmKh9BHD6wt2Sl3X1wv7sj3eWzSVC14Exkag7D8Og2VcN4LXOFLErsCXHS+YQ==", + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.34.0.tgz", + "integrity": "sha512-ZuruC+VtvejdnNj+e1sI5JK8tRdOc9wsMWyLNeumXrsZGh92DHTT+FGxCSDPJcmro0iIYw8ZiZqjHT9Fb2gMTg==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@clerk/types": "^4.40.0", + "csstype": "3.1.3", "dequal": "2.0.3", "glob-to-regexp": "0.4.1", "js-cookie": "3.0.5", - "std-env": "^3.7.0", - "swr": "^2.2.0" + "std-env": "^3.9.0", + "swr": "2.3.4" }, "engines": { "node": ">=18.17.0" @@ -725,44 +752,26 @@ } } }, - "node_modules/@clerk/testing/node_modules/@clerk/shared": { - "version": "3.9.5", - "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.9.5.tgz", - "integrity": "sha512-KeIug5qV4LnzZD+16SLkJvdONPs2HQ7I1A7jbHYOGB37vQrQrus64Wu5XeNzbWFTN1Z5fAPSGuja8MfT2cBT4A==", + "node_modules/@clerk/testing/node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@clerk/types": "^4.59.3", - "dequal": "2.0.3", - "glob-to-regexp": "0.4.1", - "js-cookie": "3.0.5", - "std-env": "^3.9.0", - "swr": "^2.3.3" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=18.17.0" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + "node": ">=12" }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "funding": { + "url": "https://dotenvx.com" } }, "node_modules/@clerk/types": { - "version": "4.59.3", - "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.59.3.tgz", - "integrity": "sha512-xwOO/hfABzbFr3f1RaVXHsDDQ0+jYpm84GiaUDxo+mLsYUgD9f2GmGjKkgWybXzvsBsgZlycSwRXkeDD6utFqg==", + "version": "4.100.0", + "resolved": "https://registry.npmjs.org/@clerk/types/-/types-4.100.0.tgz", + "integrity": "sha512-QT9GIO3bDL4R+ojAtc8XeJc/ehkdcF/+6PN3tcRWwGe4eMbX2TQ54/mhXJiY01DhMwBnF5cgK3O/z3D+34B07Q==", "license": "MIT", "dependencies": { - "csstype": "3.1.3" + "@clerk/shared": "^3.34.0" }, "engines": { "node": ">=18.17.0" @@ -1614,9 +1623,9 @@ } }, "node_modules/@next/env": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.22.tgz", - "integrity": "sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.33.tgz", + "integrity": "sha512-CgVHNZ1fRIlxkLhIX22flAZI/HmpDaZ8vwyJ/B0SDPTBuLZ1PJ+DWMjCHhqnExfmSQzA/PbZi8OAc7PAq2w9IA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1630,9 +1639,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.22.tgz", - "integrity": "sha512-HUaLiehovgnqY4TMBZJ3pDaOsTE1spIXeR10pWgdQVPYqDGQmHJBj3h3V6yC0uuo/RoY2GC0YBFRkOX3dI9WVQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", + "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", "cpu": [ "arm64" ], @@ -1646,9 +1655,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.22.tgz", - "integrity": "sha512-ApVDANousaAGrosWvxoGdLT0uvLBUC+srqOcpXuyfglA40cP2LBFaGmBjhgpxYk5z4xmunzqQvcIgXawTzo2uQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", "cpu": [ "x64" ], @@ -1662,9 +1671,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.22.tgz", - "integrity": "sha512-3O2J99Bk9aM+d4CGn9eEayJXHuH9QLx0BctvWyuUGtJ3/mH6lkfAPRI4FidmHMBQBB4UcvLMfNf8vF0NZT7iKw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", "cpu": [ "arm64" ], @@ -1678,9 +1687,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.22.tgz", - "integrity": "sha512-H/hqfRz75yy60y5Eg7DxYfbmHMjv60Dsa6IWHzpJSz4MRkZNy5eDnEW9wyts9bkxwbOVZNPHeb3NkqanP+nGPg==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", "cpu": [ "arm64" ], @@ -1694,9 +1703,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.22.tgz", - "integrity": "sha512-LckLwlCLcGR1hlI5eiJymR8zSHPsuruuwaZ3H2uudr25+Dpzo6cRFjp/3OR5UYJt8LSwlXv9mmY4oI2QynwpqQ==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", "cpu": [ "x64" ], @@ -1710,9 +1719,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.22.tgz", - "integrity": "sha512-qGUutzmh0PoFU0fCSu0XYpOfT7ydBZgDfcETIeft46abPqP+dmePhwRGLhFKwZWxNWQCPprH26TjaTxM0Nv8mw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", "cpu": [ "x64" ], @@ -1726,9 +1735,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.22.tgz", - "integrity": "sha512-K6MwucMWmIvMb9GlvT0haYsfIPxfQD8yXqxwFy4uLFMeXIb2TcVYQimxkaFZv86I7sn1NOZnpOaVk5eaxThGIw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", "cpu": [ "arm64" ], @@ -1742,9 +1751,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.22.tgz", - "integrity": "sha512-5IhDDTPEbzPR31ZzqHe90LnNe7BlJUZvC4sA1thPJV6oN5WmtWjZ0bOYfNsyZx00FJt7gggNs6SrsX0UEIcIpA==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", "cpu": [ "ia32" ], @@ -1758,9 +1767,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.22.tgz", - "integrity": "sha512-nvRaB1PyG4scn9/qNzlkwEwLzuoPH3Gjp7Q/pLuwUgOTt1oPMlnCI3A3rgkt+eZnU71emOiEv/mR201HoURPGg==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", "cpu": [ "x64" ], @@ -1969,6 +1978,71 @@ "@prisma/debug": "6.9.0" } }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", + "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", + "license": "MIT", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/client/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", + "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", + "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", + "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", + "license": "MIT", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.29.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz", @@ -2249,6 +2323,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -2901,6 +2981,31 @@ "crypto-js": "^4.2.0" } }, + "node_modules/@vercel/mcp-adapter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@vercel/mcp-adapter/-/mcp-adapter-1.0.0.tgz", + "integrity": "sha512-o/QA4LhYCJvuL7/i8CfKPpOm29u25/L4m1LWH44EQZ8cfYJlc4B7Bu+YzDHl23m8A3hDxCJQmlgZEGmsPAQ0vQ==", + "license": "Apache-2.0", + "dependencies": { + "mcp-handler": "^1.0.0" + }, + "bin": { + "create-mcp-route": "dist/cli/index.js", + "mcp-adapter": "dist/cli/index.js" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.12.0", + "next": ">=13.0.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": false + }, + "next": { + "optional": true + } + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -4168,6 +4273,15 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/color": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -4757,10 +4871,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.7", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", - "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", - "dev": true, + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", "license": "BSD-2-Clause", "engines": { "node": ">=12" @@ -5934,6 +6047,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -6212,6 +6331,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -8004,6 +8132,55 @@ "node": ">= 0.4" } }, + "node_modules/mcp-handler": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mcp-handler/-/mcp-handler-1.0.3.tgz", + "integrity": "sha512-AQ8I1a6wyfK0hIHeGqvjXqKHGvABDvw5dvwJvMjUiuKAUDgFsFtPExqThFOE/boOTbBKXn7fPSJ94soT0GjL+A==", + "license": "Apache-2.0", + "dependencies": { + "chalk": "^5.3.0", + "commander": "^11.1.0", + "redis": "^4.6.0" + }, + "bin": { + "create-mcp-route": "dist/cli/index.js", + "mcp-adapter": "dist/cli/index.js", + "mcp-handler": "dist/cli/index.js" + }, + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.17.2", + "next": ">=13.0.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": false + }, + "next": { + "optional": true + } + } + }, + "node_modules/mcp-handler/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/mcp-handler/node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -8173,12 +8350,12 @@ } }, "node_modules/next": { - "version": "14.2.22", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.22.tgz", - "integrity": "sha512-Ps2caobQ9hlEhscLPiPm3J3SYhfwfpMqzsoCMZGWxt9jBRK9hoBZj2A37i8joKhsyth2EuVKDVJCTF5/H4iEDw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.33.tgz", + "integrity": "sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==", "license": "MIT", "dependencies": { - "@next/env": "14.2.22", + "@next/env": "14.2.33", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -8193,15 +8370,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.22", - "@next/swc-darwin-x64": "14.2.22", - "@next/swc-linux-arm64-gnu": "14.2.22", - "@next/swc-linux-arm64-musl": "14.2.22", - "@next/swc-linux-x64-gnu": "14.2.22", - "@next/swc-linux-x64-musl": "14.2.22", - "@next/swc-win32-arm64-msvc": "14.2.22", - "@next/swc-win32-ia32-msvc": "14.2.22", - "@next/swc-win32-x64-msvc": "14.2.22" + "@next/swc-darwin-arm64": "14.2.33", + "@next/swc-darwin-x64": "14.2.33", + "@next/swc-linux-arm64-gnu": "14.2.33", + "@next/swc-linux-arm64-musl": "14.2.33", + "@next/swc-linux-x64-gnu": "14.2.33", + "@next/swc-linux-x64-musl": "14.2.33", + "@next/swc-win32-arm64-msvc": "14.2.33", + "@next/swc-win32-ia32-msvc": "14.2.33", + "@next/swc-win32-x64-msvc": "14.2.33" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -9257,6 +9434,23 @@ "node": ">=8.10.0" } }, + "node_modules/redis": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", + "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", + "license": "MIT", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.6.1", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.7", + "@redis/search": "1.2.0", + "@redis/time-series": "1.1.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.7.tgz", @@ -9860,6 +10054,7 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-8.0.1.tgz", "integrity": "sha512-Sj51kE1zC7zh6TDlNNz0/Jn1n5HiHdoQErxO8jLtnyrkJW/M5PrI7x05uDgY3BO7OUQYKCvmeMurW6BPUdwEOw==", + "dev": true, "license": "MIT", "dependencies": { "map-obj": "^4.1.0", @@ -9921,6 +10116,16 @@ "dev": true, "license": "MIT" }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -10273,9 +10478,9 @@ } }, "node_modules/swr": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", - "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz", + "integrity": "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==", "license": "MIT", "dependencies": { "dequal": "^2.0.3", @@ -11232,6 +11437,7 @@ "version": "4.41.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" diff --git a/lib/javascript/fullstack_demo/package.json b/lib/javascript/fullstack_demo/package.json index 391ac8bf7..a899a459c 100644 --- a/lib/javascript/fullstack_demo/package.json +++ b/lib/javascript/fullstack_demo/package.json @@ -17,19 +17,26 @@ "cy:run:e2e": "cypress run --e2e", "db:start": "docker-compose -f ./db/docker-compose.yml up -d", "db:stop": "docker-compose -f ./db/docker-compose.yml down", - "db:seed": "tsx prisma/seed.ts" + "db:seed": "tsx prisma/seed.ts", + "mcp:config": "node scripts/generate-mcp-config.js", + "tunnel": "cloudflared tunnel --url http://localhost:3000", + "tunnel:ngrok": "ngrok http 3000" }, "dependencies": { "@clerk/clerk-sdk-node": "^4.13.23", - "@clerk/nextjs": "^6.9.2", + "@clerk/mcp-tools": "^0.3.1", + "@clerk/nextjs": "^6.35.1", "@modelcontextprotocol/sdk": "^1.20.0", "@prisma/client": "^6.9.0", "@upstash/redis": "^1.34.3", + "@vercel/mcp-adapter": "^1.0.0", + "dotenv": "^17.2.3", "lowdb": "^7.0.1", "lucide-react": "^0.468.0", "next": "^14.2.22", "uuid": "^11.0.5", - "winston": "^3.17.0" + "winston": "^3.17.0", + "zod": "^3.25.76" }, "devDependencies": { "@clerk/testing": "^1.7.5", diff --git a/lib/javascript/fullstack_demo/scripts/generate-mcp-config.js b/lib/javascript/fullstack_demo/scripts/generate-mcp-config.js new file mode 100755 index 000000000..21dd65186 --- /dev/null +++ b/lib/javascript/fullstack_demo/scripts/generate-mcp-config.js @@ -0,0 +1,99 @@ +#!/usr/bin/env node + +/** + * Helper script to extract Clerk domain from environment variables + * and generate MCP configuration files for different clients. + */ + +import dotenv from 'dotenv'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Load .env file +dotenv.config({ path: path.join(__dirname, '..', '.env') }); + +const publishableKey = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY; + +if (!publishableKey) { + console.error( + '❌ Error: NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY not found in .env file', + ); + process.exit(1); +} + +// Extract domain from publishable key +// Format: pk_test_$ +const parts = publishableKey.split('_'); +if (parts.length < 3) { + console.error('❌ Error: Invalid Clerk publishable key format'); + process.exit(1); +} + +const encodedDomain = parts[2].replace(/\$+$/, ''); +let clerkDomain; + +try { + clerkDomain = Buffer.from(encodedDomain, 'base64') + .toString('utf-8') + .replace(/\$+$/, ''); +} catch (error) { + console.error('❌ Error: Could not decode Clerk domain from publishable key'); + process.exit(1); +} + +console.log('✅ Clerk Configuration Detected\n'); +console.log(`Clerk Domain: ${clerkDomain}`); +console.log(`Authorization Server: https://${clerkDomain}\n`); + +console.log('💡 Recommended: Use mcp-remote for easy setup:'); +console.log(' npx -y mcp-remote http://localhost:3000/mcp\n'); +console.log('⚠️ Note: Claude.ai (web) requires HTTPS. See HTTPS_SETUP.md.\n'); + +// Generate Goose config +const gooseConfig = { + mcpServers: { + fullstack_demo_todos: { + url: 'http://localhost:3000/mcp', + transport: 'streamable-http', + description: 'Todo management for fullstack_demo app', + oauth: { + authorizationServer: `https://${clerkDomain}`, + authorizationEndpoint: `https://${clerkDomain}/oauth/authorize`, + tokenEndpoint: `https://${clerkDomain}/oauth/token`, + registrationEndpoint: `https://${clerkDomain}/oauth/register`, + }, + }, + }, +}; + +console.log('📝 Goose Configuration:'); +console.log(JSON.stringify(gooseConfig, null, 2)); + +// Write to file +const docsDir = path.join(__dirname, '..', 'docs'); +fs.writeFileSync( + path.join(docsDir, 'goose_config.generated.json'), + JSON.stringify(gooseConfig, null, 2), +); + +console.log('\n✅ Configuration file generated:'); +console.log(' - docs/goose_config.generated.json'); +console.log('\n📋 Next Steps:'); +console.log( + ' 1. Ensure Dynamic Client Registration is enabled in Clerk Dashboard', +); +console.log(' 2. Start the dev server: npm run dev'); +console.log( + ' 3. Connect using mcp-remote: npx -y mcp-remote http://localhost:3000/mcp', +); +console.log('\n Alternative: Manual Configuration'); +console.log( + ' - For Claude.ai: Settings → Connectors → Add custom connector (requires HTTPS)', +); +console.log( + ' - For other agents: Copy docs/goose_config.generated.json to your client config', +); diff --git a/lib/javascript/fullstack_demo/scripts/setup-https.sh b/lib/javascript/fullstack_demo/scripts/setup-https.sh new file mode 100644 index 000000000..e7bc77cbd --- /dev/null +++ b/lib/javascript/fullstack_demo/scripts/setup-https.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# Quick setup script for exposing MCP server over HTTPS + +set -e + +echo "🔧 MCP Server HTTPS Setup" +echo "==========================" +echo "" + +# Check if ngrok is installed +if command -v ngrok &> /dev/null; then + echo "✅ ngrok is installed" +else + echo "❌ ngrok is not installed" + echo "" + echo "Install options:" + echo " macOS: brew install ngrok" + echo " Other: https://ngrok.com/download" + echo "" + exit 1 +fi + +# Check if authenticated +if ngrok config check &> /dev/null; then + echo "✅ ngrok is configured" +else + echo "⚠️ ngrok is not authenticated" + echo "" + echo "To authenticate:" + echo " 1. Sign up at https://ngrok.com" + echo " 2. Get your auth token from the dashboard" + echo " 3. Run: ngrok config add-authtoken YOUR_AUTH_TOKEN" + echo "" + read -p "Press Enter after authenticating, or Ctrl+C to exit..." +fi + +# Check if dev server is running +if curl -s http://localhost:3000 > /dev/null 2>&1; then + echo "✅ Dev server is running on http://localhost:3000" +else + echo "⚠️ Dev server is not running" + echo "" + echo "Start it with: npm run dev" + echo "" + read -p "Press Enter after starting the dev server, or Ctrl+C to exit..." +fi + +echo "" +echo "🚀 Starting ngrok tunnel..." +echo "" +echo "This will:" +echo " 1. Create an HTTPS tunnel to localhost:3000" +echo " 2. Display the public HTTPS URL" +echo "" +echo "Next steps after tunnel starts:" +echo " 1. Copy the HTTPS URL (e.g., https://abc123.ngrok-free.app)" +echo " 2. Go to Clerk Dashboard → OAuth Applications" +echo " 3. Add redirect URL: https://YOUR-URL.ngrok-free.app/*" +echo " 4. Go to claude.ai → Settings → Connectors" +echo " 5. Add custom connector: https://YOUR-URL.ngrok-free.app/mcp" +echo "" +echo "Press Ctrl+C to stop the tunnel" +echo "" +sleep 2 + +# Start ngrok +ngrok http 3000 diff --git a/lib/javascript/fullstack_demo/src/app/.well-known/oauth-authorization-server/route.ts b/lib/javascript/fullstack_demo/src/app/.well-known/oauth-authorization-server/route.ts new file mode 100644 index 000000000..b84b9adea --- /dev/null +++ b/lib/javascript/fullstack_demo/src/app/.well-known/oauth-authorization-server/route.ts @@ -0,0 +1,9 @@ +import { + authServerMetadataHandlerClerk, + metadataCorsOptionsRequestHandler, +} from '@clerk/mcp-tools/next'; + +const handler = authServerMetadataHandlerClerk(); +const corsHandler = metadataCorsOptionsRequestHandler(); + +export { handler as GET, corsHandler as OPTIONS }; diff --git a/lib/javascript/fullstack_demo/src/app/.well-known/oauth-protected-resource/mcp/route.ts b/lib/javascript/fullstack_demo/src/app/.well-known/oauth-protected-resource/mcp/route.ts new file mode 100644 index 000000000..acabb44fd --- /dev/null +++ b/lib/javascript/fullstack_demo/src/app/.well-known/oauth-protected-resource/mcp/route.ts @@ -0,0 +1,11 @@ +import { + metadataCorsOptionsRequestHandler, + protectedResourceHandlerClerk, +} from '@clerk/mcp-tools/next'; + +const handler = protectedResourceHandlerClerk({ + scopes: ['read:todos', 'write:todos'], +}); +const corsHandler = metadataCorsOptionsRequestHandler(); + +export { handler as GET, corsHandler as OPTIONS }; diff --git a/lib/javascript/fullstack_demo/src/app/[transport]/route.ts b/lib/javascript/fullstack_demo/src/app/[transport]/route.ts new file mode 100644 index 000000000..01bc43739 --- /dev/null +++ b/lib/javascript/fullstack_demo/src/app/[transport]/route.ts @@ -0,0 +1,163 @@ +import { Todo } from '@/models'; +import { createTodoRepository } from '@/repositories'; +import { verifyClerkToken } from '@clerk/mcp-tools/next'; +import { auth, clerkClient } from '@clerk/nextjs/server'; +import { createMcpHandler, withMcpAuth } from '@vercel/mcp-adapter'; +import { z } from 'zod'; + +const clerk = await clerkClient(); +const todoRepository = createTodoRepository(); + +const handler = createMcpHandler((server) => { + // Get all todos for the authenticated user + server.tool( + 'get-todos', + 'Gets all todos for the authenticated user', + {}, + async (_, extra) => { + const userId = extra?.authInfo?.extra?.userId as string; + const todos = await todoRepository.getAll(userId); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(todos, null, 2), + }, + ], + }; + }, + ); + + // Create a new todo + server.tool( + 'create-todo', + 'Creates a new todo for the authenticated user', + { + text: z.string().describe('The text content of the todo'), + completed: z + .boolean() + .optional() + .describe( + 'Whether the todo is completed (optional, defaults to false)', + ), + }, + async (args, extra) => { + const userId = extra?.authInfo?.extra?.userId as string; + const todo: Todo = { + id: Date.now(), + text: args.text as string, + completed: (args.completed as boolean) || false, + }; + + const id = await todoRepository.create(todo, userId); + + return { + content: [ + { + type: 'text', + text: JSON.stringify({ ...todo, id }, null, 2), + }, + ], + }; + }, + ); + + // Update a todo + server.tool( + 'update-todo', + 'Updates an existing todo for the authenticated user', + { + id: z.number().int().describe('The ID of the todo to update'), + text: z + .string() + .optional() + .describe('The new text content of the todo (optional)'), + completed: z + .boolean() + .optional() + .describe('Whether the todo is completed (optional)'), + }, + async (args, extra) => { + const userId = extra?.authInfo?.extra?.userId as string; + const updates: Partial = { id: args.id as number }; + + if (args.text !== undefined) { + updates.text = args.text as string; + } + if (args.completed !== undefined) { + updates.completed = args.completed as boolean; + } + + const updatedTodo = await todoRepository.patch(updates, userId); + + return { + content: [ + { + type: 'text', + text: JSON.stringify(updatedTodo, null, 2), + }, + ], + }; + }, + ); + + // Delete a todo + server.tool( + 'delete-todo', + 'Deletes a todo for the authenticated user', + { + id: z.number().int().describe('The ID of the todo to delete'), + }, + async (args, extra) => { + const userId = extra?.authInfo?.extra?.userId as string; + await todoRepository.delete(args.id as number, userId); + + return { + content: [ + { + type: 'text', + text: JSON.stringify( + { success: true, deletedId: args.id }, + null, + 2, + ), + }, + ], + }; + }, + ); + + // Get user data (example from Clerk guide) + server.tool( + 'get-clerk-user-data', + 'Gets data about the Clerk user that authorized this request', + {}, + async (_, extra) => { + const userId = extra?.authInfo?.extra?.userId as string; + const userData = await clerk.users.getUser(userId); + + return { + content: [{ type: 'text', text: JSON.stringify(userData) }], + }; + }, + ); +}); + +const authHandler = withMcpAuth( + handler, + async (req, token) => { + // For MCP OAuth flow, we need to verify the token directly + // The route is public, so auth() won't have context + const clerkAuth = await auth({ + acceptsToken: 'oauth_token', + }); + return verifyClerkToken(clerkAuth, token); + }, + { + required: true, + resourceMetadataPath: '/.well-known/oauth-protected-resource/mcp', + }, +); + +export { authHandler as GET, authHandler as POST }; diff --git a/lib/javascript/fullstack_demo/src/middleware.ts b/lib/javascript/fullstack_demo/src/middleware.ts index 5a8c9a643..df07353ed 100644 --- a/lib/javascript/fullstack_demo/src/middleware.ts +++ b/lib/javascript/fullstack_demo/src/middleware.ts @@ -3,12 +3,23 @@ import { NextResponse } from 'next/server'; import { v4 as uuidv4 } from 'uuid'; const CORRELATION_ID_HEADER = 'x-correlation-id'; +const isPublicRoute = createRouteMatcher([ + '/.well-known/oauth-authorization-server(.*)', + '/.well-known/oauth-protected-resource(.*)', +]); const isProtectedRoute = createRouteMatcher(['/(.*)']); export default clerkMiddleware(async (auth, req) => { const correlationId = uuidv4(); req.headers.set(CORRELATION_ID_HEADER, correlationId); + // Allow public access to .well-known endpoints + if (isPublicRoute(req)) { + const response = NextResponse.next(); + response.headers.set(CORRELATION_ID_HEADER, correlationId); + return response; + } + if (isProtectedRoute(req)) { await auth.protect(); } diff --git a/lib/javascript/fullstack_demo/src/repositories/postgres-todo-repository.ts b/lib/javascript/fullstack_demo/src/repositories/postgres-todo-repository.ts index d507782c6..a43527dbb 100644 --- a/lib/javascript/fullstack_demo/src/repositories/postgres-todo-repository.ts +++ b/lib/javascript/fullstack_demo/src/repositories/postgres-todo-repository.ts @@ -18,6 +18,7 @@ export class PostgresTodoRepository implements TodoRepository { } async create(todo: Todo, userId: string): Promise { + console.log('Creating todo for user:', userId); const dbTodo = await this.client.todos.create({ data: { text: todo.text,