-
Notifications
You must be signed in to change notification settings - Fork 9
feat: Add Vercel serverless deployment support #42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds Vercel serverless deployment support to the MCP TypeScript template, enabling deployment to Vercel's serverless platform alongside the existing Express server implementation.
Key Changes
- Introduced serverless function handlers in
/apidirectory for MCP protocol handling and server information endpoints - Created shared library code in
/libfor configuration, logging, and utilities that can be reused across both Express and serverless implementations - Added Vercel-specific configuration and comprehensive deployment documentation
Reviewed Changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| api/mcp.ts | Main MCP protocol handler for Vercel serverless, includes session management and request routing |
| api/index.ts | Server information endpoint returning metadata about the MCP server |
| lib/config.ts | Shared environment configuration with Zod validation |
| lib/logger.ts | Shared structured logging setup with Pino |
| lib/utils.ts | Shared MCP helper utilities for formatting responses |
| vercel.json | Vercel platform configuration for serverless functions, rewrites, and runtime settings |
| package.json | Updated build scripts for Vercel deployment, added Vercel dependencies, included lib/ in build |
| tsconfig.json | Expanded to include api/ and lib/ directories, changed from noEmit to outDir configuration |
| .gitignore | Added .vercel directory exclusion |
| README.vercel.md | Comprehensive deployment guide with architecture details and production recommendations |
| VERCEL_GUIDE.md | Quick reference guide for Vercel serverless deployment |
| const expressReq = { | ||
| body: req.body, | ||
| headers: req.headers, | ||
| method: req.method, | ||
| }; | ||
|
|
||
| const expressRes = { | ||
| status: (code: number) => { | ||
| res.status(code); | ||
| return expressRes; | ||
| }, | ||
| setHeader: (name: string, value: string) => { | ||
| res.setHeader(name, value); | ||
| return expressRes; | ||
| }, | ||
| json: (data: any) => { | ||
| res.json(data); | ||
| }, | ||
| send: (data: any) => { | ||
| res.send(data); | ||
| }, | ||
| end: () => { | ||
| res.end(); | ||
| }, | ||
| }; | ||
|
|
||
| await transport.handleRequest(expressReq as any, expressRes as any, req.body); | ||
| return; | ||
| } | ||
|
|
||
| // Handle existing session requests | ||
| if (sessionId && transports[sessionId]) { | ||
| const transport = transports[sessionId]; | ||
|
|
||
| const expressReq = { | ||
| body: req.body, | ||
| headers: req.headers, | ||
| method: req.method, | ||
| }; | ||
|
|
||
| const expressRes = { | ||
| status: (code: number) => { | ||
| res.status(code); | ||
| return expressRes; | ||
| }, | ||
| setHeader: (name: string, value: string) => { | ||
| res.setHeader(name, value); | ||
| return expressRes; | ||
| }, | ||
| json: (data: any) => { | ||
| res.json(data); | ||
| }, | ||
| send: (data: any) => { | ||
| res.send(data); | ||
| }, | ||
| end: () => { | ||
| res.end(); | ||
| }, | ||
| }; |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code duplication: The Express request/response adapter logic is duplicated between lines 82-106 and 116-140. Consider extracting this into a reusable function to reduce duplication and improve maintainability.
Example:
function createExpressAdapter(res: VercelResponse) {
return {
status: (code: number) => {
res.status(code);
return expressRes;
},
setHeader: (name: string, value: string) => {
res.setHeader(name, value);
return expressRes;
},
json: (data: any) => {
res.json(data);
},
send: (data: any) => {
res.send(data);
},
end: () => {
res.end();
},
};
}| ) { | ||
| // Set CORS headers for API access | ||
| res.setHeader("Access-Control-Allow-Credentials", "true"); | ||
| res.setHeader("Access-Control-Allow-Origin", "*"); |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting Access-Control-Allow-Credentials: true with Access-Control-Allow-Origin: * is not allowed by the CORS specification and will cause browsers to reject the response. If you need credentials, specify an explicit origin instead of *, or remove the credentials header if not needed.
See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
| res.setHeader("Access-Control-Allow-Origin", "*"); | |
| res.setHeader("Access-Control-Allow-Origin", req.headers.origin || ""); |
| try { | ||
| config = configSchema.parse(process.env); | ||
| } catch (error) { | ||
| console.error("❌ Invalid environment configuration:", error); |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the project's coding guidelines, console.error should not be used. Use structured logging with the logger from logger.ts instead. However, in this specific case where the config is being initialized and the logger depends on the config, this creates a circular dependency. Consider restructuring to use a basic logger initialization that doesn't depend on config, or document this as an acceptable exception.
| "scripts": { | ||
| "build": "vite build", | ||
| "dev": "node --watch src/index.ts", | ||
| "build": "tsc", |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The build command was changed from vite build to tsc, but vite is still listed as a devDependency and not removed. If Vite is no longer needed for the build process, consider removing it from dependencies to reduce package size. If it's still used elsewhere, this is fine.
| "buildCommand": "npm run build", | ||
| "outputDirectory": "dist", |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The buildCommand and outputDirectory configuration may conflict with Vercel's automatic TypeScript compilation for serverless functions. Vercel automatically compiles TypeScript files in the api/ directory - you typically don't need these settings for serverless functions. Consider removing these lines or documenting why they're needed if there's a specific use case.
| "buildCommand": "npm run build", | |
| "outputDirectory": "dist", |
| import { logger } from "../logger.js"; | ||
| import { getConfig } from "../config.js"; |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The imports are pointing to incorrect paths. The logger and config modules are in the lib/ directory, not at the root level. These imports should be:
import { logger } from "../lib/logger.js";import { getConfig } from "../lib/config.js";
| import { logger } from "../logger.js"; | |
| import { getConfig } from "../config.js"; | |
| import { logger } from "../lib/logger.js"; | |
| import { getConfig } from "../lib/config.js"; |
| json: (data: any) => { | ||
| res.json(data); | ||
| }, | ||
| send: (data: any) => { |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The use of any type for the data parameter in the json and send methods reduces type safety. Consider using unknown or a more specific type to maintain better type checking.
| json: (data: any) => { | |
| res.json(data); | |
| }, | |
| send: (data: any) => { | |
| json: (data: unknown) => { | |
| res.json(data); | |
| }, | |
| send: (data: unknown) => { |
| "pino": "^10.1.0", | ||
| "pino-pretty": "^13.1.2" | ||
| "pino-pretty": "^13.1.2", | ||
| "zod": "^3.24.1" |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] zod has been moved from devDependencies to dependencies. This is correct since it's now used at runtime in lib/config.ts for environment variable validation. However, it was previously only in devDependencies, which means the original src/config.ts also uses it at runtime. Consider also checking if zod should have been in dependencies all along in the original Express version.
|
|
||
| ### Option 2: GitHub Integration | ||
| 1. Connect repository to Vercel | ||
| 2. Select the `vercel-serverless` branch |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation references a vercel-serverless branch. Ensure this branch name is correct and will exist when this is merged, or update it to reference the appropriate branch (e.g., main if merging there).
| 2. Select the `vercel-serverless` branch | |
| 2. Select the `main` branch |
| "lint": "eslint src/ api/", | ||
| "lint:fix": "eslint src/ api/ --fix", | ||
| "format": "prettier --write '{src,api}/**/*.ts'", | ||
| "format:check": "prettier --check '{src,api}/**/*.ts'", |
Copilot
AI
Nov 20, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The lint and format scripts should include the lib/ directory since new shared code has been added there. Update these scripts to:
"lint": "eslint src/ api/ lib/""lint:fix": "eslint src/ api/ lib/ --fix""format": "prettier --write '{src,api,lib}/**/*.ts'""format:check": "prettier --check '{src,api,lib}/**/*.ts'"
| "lint": "eslint src/ api/", | |
| "lint:fix": "eslint src/ api/ --fix", | |
| "format": "prettier --write '{src,api}/**/*.ts'", | |
| "format:check": "prettier --check '{src,api}/**/*.ts'", | |
| "lint": "eslint src/ api/ lib/", | |
| "lint:fix": "eslint src/ api/ lib/ --fix", | |
| "format": "prettier --write '{src,api,lib}/**/*.ts'", | |
| "format:check": "prettier --check '{src,api,lib}/**/*.ts'", |
Overview
This PR adds Vercel serverless deployment support to the MCP TypeScript template, enabling easy deployment to Vercel's edge network without managing infrastructure.
What's New
🚀 Serverless Architecture
/api/mcp.tsfor MCP protocol handling/api/index.tsfor server information endpoint📦 Shared Libraries
/libdirectory for reuse across serverless functionslib/config.ts- Environment configurationlib/logger.ts- Structured logging with Pinolib/utils.ts- MCP helper utilities⚙️ Configuration
vercel.jsonwith optimized serverless settingspackage.jsonwith Vercel dependencies and scriptstsconfig.jsonto support multi-directory structure.gitignorefor Vercel-specific files📚 Documentation
README.vercel.md- Comprehensive deployment guideVERCEL_GUIDE.md- Quick reference for developersKey Features
✅ One-click deployment to Vercel
✅ Automatic scaling based on demand
✅ Zero infrastructure management
✅ Compatible with existing Express version
✅ TypeScript with full type safety
✅ Development server with hot reload
✅ Production-optimized builds
Architecture
Usage
Local Development
Deploy to Vercel
Or use the one-click deploy button!
Important Notes
The current implementation uses in-memory session storage, which is not production-ready for serverless:
For production, implement persistent storage using:
See
README.vercel.mdfor implementation examples.💡 Benefits Over Express
🔄 Backwards Compatible
The original Express server in
src/index.tsremains functional. You can still:Testing
Test the deployed serverless functions:
Files Changed
New Files
api/index.ts- Server info endpointapi/mcp.ts- MCP protocol handlerlib/config.ts- Shared configurationlib/logger.ts- Shared logginglib/utils.ts- Shared utilitiesvercel.json- Vercel platform configREADME.vercel.md- Deployment documentationVERCEL_GUIDE.md- Quick reference guideModified Files
package.json- Added Vercel dependencies and scriptstsconfig.json- Updated for multi-directory structure.gitignore- Added.verceldirectoryNext Steps
After merging:
Documentation
README.vercel.mdVERCEL_GUIDE.mdQuestions?
Feel free to ask questions or request changes. This implementation prioritizes:
Ready to deploy! 🚀