my-mcp-server
TypeScript MCP Server 보일러플레이트 (Vercel 배포용)
Model Context Protocol (MCP) 서버를 Streamable HTTP 전송 방식으로 구현하고 Vercel에 배포할 수 있는 보일러플레이트입니다. Next.js App Router와 mcp-handler를 사용합니다.
📁 프로젝트 구조
typescript-mcp-server-boilerplate/
├── app/
│ ├── api/
│ │ └── mcp/
│ │ └── route.ts # MCP HTTP 엔드포인트 (POST /api/mcp)
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx # 엔드포인트 안내용 랜딩 페이지
├── src/
│ └── mcp/
│ └── server.ts # 도구·리소스·프롬프트 등록
├── next.config.ts
├── package.json
├── tsconfig.json
└── README.md🚀 시작하기
1. 의존성 설치
npm install2. 개발 서버 실행
npm run devMCP 엔드포인트가 http://localhost:3000/api/mcp에서 열립니다.
3. 동작 확인
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/list"}'또는 MCP Inspector로 확인할 수 있습니다:
npx @modelcontextprotocol/inspectorInspector 화면에서 전송 방식을 Streamable HTTP로, URL을 http://localhost:3000/api/mcp로 설정합니다.
🔑 HF_TOKEN 전달 방식
generate-image 도구는 HuggingFace Inference API를 사용하므로 토큰이 필요합니다. 토큰은 두 가지 경로로 전달할 수 있고, 요청 헤더가 환경변수보다 우선합니다.
x-hf-token요청 헤더 — 클라이언트가 자신의 토큰을 직접 전달합니다. 서버에 토큰을 저장하지 않아도 되고, 사용자별로 다른 토큰을 쓸 수 있습니다.HF_TOKEN환경변수 — 헤더가 없을 때 사용하는 서버 측 폴백입니다.
헤더로 받은 토큰은 해당 요청의 서버 인스턴스에만 주입되며 요청이 끝나면 사라집니다.
// app/api/mcp/route.ts
const hfToken = request.headers.get('x-hf-token')?.trim() || process.env.HF_TOKEN🔧 MCP 클라이언트 연결
Cursor
.cursor/mcp.json을 다음과 같이 작성합니다. command/args 대신 url과 headers를 사용합니다.
{
"mcpServers": {
"my-mcp-server": {
"url": "http://localhost:3000/api/mcp",
"headers": {
"x-hf-token": "hf_..."
}
}
}
}배포 후에는 url을 https://<your-deployment>.vercel.app/api/mcp로 바꿉니다.
⚠️
.cursor/mcp.json에는 토큰이 들어가므로.gitignore에 등록되어 있습니다. 커밋하지 마세요.
stdio 전용 클라이언트
HTTP 전송을 지원하지 않는 클라이언트는 mcp-remote를 사용합니다.
{
"mcpServers": {
"my-mcp-server": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"http://localhost:3000/api/mcp",
"--header",
"x-hf-token:hf_..."
]
}
}
}☁️ Vercel 배포
npx vercelHF_TOKEN환경변수 설정은 선택입니다. 클라이언트가x-hf-token헤더를 보내면 그 값이 사용됩니다. 헤더 없이 쓰는 클라이언트도 지원하려면 Vercel 프로젝트 환경변수에HF_TOKEN을 추가하세요.이미지 생성은 시간이 걸리므로 라우트에
maxDuration = 60이 설정되어 있습니다. 요금제에 따라 상한이 다릅니다.이미지는 base64 PNG로 응답에 담깁니다. Vercel 함수 응답 크기 상한(4.5MB)을 넘지 않도록 해상도와 스텝 수를 조절하세요.
🛠️ 개발 가이드
모든 도구·리소스·프롬프트는 src/mcp/server.ts의 registerMcpServer에서 등록합니다. 요청별 값(예: 헤더에서 읽은 토큰)은 deps 인자로 전달됩니다.
export type McpDeps = {
hfToken?: string
}
export function registerMcpServer(server: McpServer, deps: McpDeps): void {
// 여기에 도구를 등록합니다
}도구(Tool) 추가하기
registerTool에 Zod 스키마를 직접 정의해 등록합니다. outputSchema를 지정하면 structuredContent도 함께 반환해야 합니다.
server.registerTool(
'greet',
{
description: '이름과 언어를 입력하면 인사말을 반환합니다.',
inputSchema: z.object({
name: z.string().describe('인사할 사람의 이름'),
language: z
.enum(['ko', 'en'])
.optional()
.default('en')
.describe('인사 언어 (기본값: en)')
}),
outputSchema: z.object({
content: z.array(
z.object({
type: z.literal('text'),
text: z.string().describe('인사말')
})
)
})
},
async ({ name, language }) => {
const greeting =
language === 'ko' ? `안녕하세요, ${name}님!` : `Hello, ${name}!`
return {
content: [{ type: 'text', text: greeting }],
structuredContent: {
content: [{ type: 'text', text: greeting }]
}
}
}
)요청 헤더를 사용하는 도구
mcp-handler의 초기화 콜백은 서버 인스턴스만 받으므로, 헤더 값을 쓰려면 라우트에서 읽어 deps로 주입합니다. mcp-handler는 POST 요청마다 새 McpServer를 만들기 때문에 요청마다 핸들러를 생성해도 추가 비용이 없습니다.
// app/api/mcp/route.ts
async function handler(request: Request): Promise<Response> {
const hfToken =
request.headers.get('x-hf-token')?.trim() || process.env.HF_TOKEN
return createMcpHandler(
(server) => registerMcpServer(server, { hfToken }),
{ serverInfo: { ...SERVER_INFO } },
{ basePath: '/api', disableSse: true }
)(request)
}💡
basePath: '/api'에서 스트리머블 HTTP 엔드포인트/api/mcp가 파생됩니다. 라우트 파일 위치를 옮기면basePath도 함께 맞춰야 합니다.
리소스 추가하기
server.registerResource(
'server-info',
'info://server/info',
{
title: 'Server Info',
description: '서버의 기본 정보를 제공합니다.',
mimeType: 'text/plain'
},
async (uri) => ({
contents: [
{
uri: uri.href,
mimeType: 'text/plain',
text: JSON.stringify({ name: 'my-mcp-server' }, null, 2)
}
]
})
)📋 제공 도구
도구 | 설명 |
| 이름과 언어를 입력하면 인사말을 반환 |
| 두 숫자와 연산자로 사칙연산 수행 |
| 타임존 또는 도시명의 현재 시간 조회 |
| 도시명을 위도·경도와 타임존으로 변환 (Open-Meteo) |
| 좌표로 현재 날씨와 일별 예보 조회 (Open-Meteo) |
| 프롬프트로 이미지 생성 (HuggingFace FLUX.1-schnell) |
리소스 info://server/info와 프롬프트 code-review도 함께 제공됩니다.
📦 주요 의존성
next: App Router 기반 HTTP 서버
mcp-handler: MCP 서버를 웹 표준 요청 핸들러로 변환하는 Vercel 어댑터
@modelcontextprotocol/sdk: MCP 프로토콜 공식 SDK (
mcp-handler@1.x는1.26.0을 요구)zod: 도구 입출력 스키마 검증
@huggingface/inference: 이미지 생성
🔧 스크립트
npm run dev: 개발 서버 실행npm run build: 프로덕션 빌드 및 타입 검사npm start: 프로덕션 서버 실행
🔗 참고 자료
📄 라이선스
MIT