feat: Add Vercel serverless deployment support#42
feat: Add Vercel serverless deployment support#42nickytonline wants to merge 12 commits intomainfrom
Conversation
There was a problem hiding this comment.
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(); | ||
| }, | ||
| }; |
There was a problem hiding this comment.
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", "*"); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
[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", |
There was a problem hiding this comment.
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"; |
There was a problem hiding this comment.
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) => { |
There was a problem hiding this comment.
[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" |
There was a problem hiding this comment.
[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 |
There was a problem hiding this comment.
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'", |
There was a problem hiding this comment.
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! 🚀