my-mcp-server
TypeScript MCP Server 보일러플레이트 (Streamable HTTP / Vercel)
Next.js App Router + mcp-handler로 Model Context Protocol(MCP) 서버를 Streamable HTTP 엔드포인트로 제공하는 보일러플레이트입니다. 그대로 Vercel에 배포할 수 있습니다.
엔드포인트:
POST /api/mcp프로토콜: Streamable HTTP (stateless). 2026-07-28 스펙 네이티브 + 2025년대 클라이언트 폴백을 한 핸들러가 모두 처리합니다.
제공 기능: 도구 6개(
greet,calculator,get-time,geocode,get-weather,generate-image), 리소스server-info, 프롬프트code-review
프로젝트 구조
src/
app/
layout.tsx, page.tsx # 엔드포인트/도구 목록 안내 페이지
api/mcp/route.ts # MCP 핸들러 마운트
mcp/
config.ts # 서버 이름·버전·기능 목록
register.ts # registerAll(server): 도구·리소스·프롬프트 등록 집합
tools/ # 도구별 모듈
resources/server-info.ts
prompts/code-review.ts
lib/ # fetch 유틸, 날씨 코드 표, HF 토큰 해석
next.config.ts
tsconfig.json요청 1건마다 새 McpServer 인스턴스가 만들어지고 registerAll이 실행됩니다. 서버는 상태를 보관하지 않으므로 서버리스 환경에서 그대로 확장됩니다.
시작하기
npm install
npm run dev # http://localhost:3000MCP Inspector로 테스트
npm run inspect브라우저에서
http://127.0.0.1:6274접속Transport를 Streamable HTTP로 선택
URL에
http://localhost:3000/api/mcp입력Configuration에서 커스텀 헤더(
x-hf-token)를 추가한 뒤 Connect
curl로 빠르게 확인
curl -X POST http://localhost:3000/api/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"calculator","arguments":{"a":7,"b":6,"operator":"*"}}}'Stateless 서빙이므로 GET/DELETE(2025년대 세션 조작)는 405로 응답합니다. 정상 동작입니다.
HuggingFace 토큰: x-hf-token 헤더
generate-image 도구는 토큰을 다음 순서로 찾습니다.
요청의
x-hf-token헤더 — 클라이언트가 자기 토큰을 주입하는 기본 경로서버의
HF_TOKEN환경변수 — 폴백
구현은 src/mcp/lib/hf-token.ts의 resolveHfToken()이며, 도구 안에서는 ctx.http?.req로 원본 HTTP 요청에 접근합니다.
async ({ prompt }, ctx) => {
const token = resolveHfToken(ctx.http?.req)
// ...
}토큰이 둘 다 없으면 도구가 isError와 함께 설정 안내 메시지를 반환합니다. 토큰 값은 로그나 응답에 절대 포함하지 않습니다.
MCP 클라이언트 연결
./.cursor/mcp.json (또는 사용하는 클라이언트의 설정 파일):
{
"mcpServers": {
"my-mcp-server": {
"url": "http://localhost:3000/api/mcp",
"headers": {
"x-hf-token": "hf_..."
}
}
}
}배포 후에는 URL만 https://<project>.vercel.app/api/mcp로 바꾸면 됩니다.
stdio만 지원하는 클라이언트는 mcp-remote로 연결합니다.
{
"mcpServers": {
"my-mcp-server": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:3000/api/mcp"]
}
}
}Vercel 배포
GitHub 저장소를 Vercel 프로젝트에 연결하면
main푸시마다 자동 배포됩니다.빌드 설정은 기본값(
next build) 그대로 사용합니다. Fluid compute도 기본값을 사용합니다.환경변수
HF_TOKEN은 서버 측 폴백이 필요할 때만 등록합니다. 클라이언트가 항상x-hf-token을 보낸다면 등록하지 않아도 됩니다.src/app/api/mcp/route.ts에서 실행 환경을 지정합니다.
export const runtime = 'nodejs'
export const maxDuration = 60 // 이미지 생성 여유배포된 엔드포인트는 인증 없이 공개됩니다. 접근 제어가 필요하면 mcp-handler의 withMcpAuth + /.well-known/oauth-protected-resource 라우트로 OAuth를 추가하세요.
도구 추가하기
src/mcp/tools/에 모듈을 만들고 src/mcp/register.ts에 등록 함수를 추가합니다.
// src/mcp/tools/echo.ts
import type { McpServer } from '@modelcontextprotocol/server'
import { z } from 'zod'
export function registerEcho(server: McpServer) {
server.registerTool(
'echo',
{
description: '입력을 그대로 돌려줍니다.',
inputSchema: z.object({
message: z.string().describe('돌려줄 메시지')
}),
outputSchema: z.object({
message: z.string()
})
},
async ({ message }) => ({
content: [{ type: 'text' as const, text: message }],
structuredContent: { message }
})
)
}inputSchema/outputSchema/argsSchema는 원시 shape가 아니라 완전한 스키마(z.object({ ... }))를 넘깁니다. MCP SDK v2 규칙입니다.실패는 예외 대신
isError: true와 사용자용 메시지로 반환하는 편이 클라이언트 경험이 좋습니다.config.ts의TOOL_NAMES에도 이름을 추가하면server-info리소스와 안내 페이지에 함께 반영됩니다.
이미지 생성 도구 주의사항
Vercel 파일시스템은 읽기 전용이라 generate-image는 파일을 저장하지 않고 base64 image 콘텐츠만 반환합니다.
Cursor 채팅 UI는 MCP
image콘텐츠를 인라인 렌더링하지 않습니다. Inspector나 Claude Desktop 등에서 확인하세요.Vercel Functions 응답 본문 한도(약 4.5MB)를 고려해
num_inference_steps는 최대 10으로 제한되어 있습니다.영구 저장이 필요하면 Vercel Blob에 업로드하고 URL을 반환하도록 바꾸는 방식을 권장합니다.
스크립트
명령 | 설명 |
| 개발 서버 (http://localhost:3000) |
| 프로덕션 빌드 |
| 빌드 결과 실행 |
| 타입 검사 |
| MCP Inspector 실행 |
참고 자료
라이선스
MIT