Skip to main content
Glama

TypeScript MCP Server 보일러플레이트 (Vercel 배포용)

Model Context Protocol (MCP) 서버를 TypeScript로 개발하고 Vercel에 배포할 수 있는 보일러플레이트입니다. Next.js App Router 위에서 mcp-handlerStreamable HTTP 전송을 제공하며, 로컬 개발용 stdio 진입점도 함께 유지합니다.

📁 프로젝트 구조

typescript-mcp-server-boilerplate/
├── src/
│   ├── mcp/
│   │   └── tools.ts            # tool / resource / prompt 등록 (HTTP·stdio 공용)
│   ├── stdio.ts                # 로컬 stdio 진입점
│   └── app/
│       ├── api/mcp/route.ts    # Streamable HTTP 엔드포인트
│       ├── layout.tsx
│       └── page.tsx            # 엔드포인트 안내 랜딩 페이지
├── build/                      # stdio 빌드 결과물 (build:stdio 실행 시 생성)
├── next.config.ts
├── tsconfig.json               # Next.js용
├── tsconfig.stdio.json         # stdio 빌드 전용
└── .env.example

🚀 시작하기

1. 의존성 설치

npm install

2. 환경변수 설정

cp .env.example .env.local

.env.localHuggingFace 액세스 토큰을 넣습니다. HTTP로 연결할 때는 클라이언트가 x-hf-token 헤더로 토큰을 보낼 수 있으므로 이 값은 선택 사항(폴백)입니다.

3. 개발 서버 실행

npm run dev

MCP 엔드포인트: http://localhost:3000/api/mcp

4. MCP Inspector로 테스트

npx @modelcontextprotocol/inspector

인스펙터에서 전송 방식을 Streamable HTTP로 선택하고 위 URL을 입력합니다. generate-image를 테스트하려면 Headers에 x-hf-token을 추가하세요.

🔑 HF_TOKEN 주입 방식

generate-image 도구만 HuggingFace 토큰이 필요합니다. 토큰은 다음 우선순위로 결정됩니다.

  1. 요청의 x-hf-token 헤더 (클라이언트가 자기 토큰을 사용)

  2. 서버의 HF_TOKEN 환경변수 (헤더가 없을 때 폴백)

createMcpHandler는 tool 핸들러에 원본 요청 헤더를 노출하지 않기 때문에, 요청마다 핸들러를 생성해 헤더 값을 등록 클로저로 전달합니다.

// src/app/api/mcp/route.ts
const handler = async (request: Request) => {
    const hfToken = request.headers.get('x-hf-token') ?? process.env.HF_TOKEN

    return createMcpHandler(
        (server) => registerAll(server, { hfToken, transport: 'http' }),
        { serverInfo: SERVER_INFO }
    )(request)
}

export { handler as GET, handler as POST }

🔧 MCP 클라이언트 연결

로컬 개발 서버

./.cursor/mcp.json:

{
    "mcpServers": {
        "my-mcp-server": {
            "url": "http://localhost:3000/api/mcp",
            "headers": {
                "x-hf-token": "hf_xxxxxxxxxxxxxxxxxxxx"
            }
        }
    }
}

배포된 서버

{
    "mcpServers": {
        "my-mcp-server": {
            "url": "https://<your-app>.vercel.app/api/mcp",
            "headers": {
                "x-hf-token": "hf_xxxxxxxxxxxxxxxxxxxx"
            }
        }
    }
}

주의: .cursor/mcp.json은 git에 추적되는 파일입니다. 실제 토큰을 커밋하지 않도록 주의하세요.

Streamable HTTP를 지원하지 않는 stdio 전용 클라이언트는 mcp-remote로 연결할 수 있습니다.

{
    "mcpServers": {
        "my-mcp-server": {
            "command": "npx",
            "args": [
                "-y",
                "mcp-remote",
                "https://<your-app>.vercel.app/api/mcp",
                "--header",
                "x-hf-token:hf_xxxxxxxxxxxxxxxxxxxx"
            ]
        }
    }
}

☁️ Vercel 배포

npx vercel

또는 GitHub 저장소를 Vercel 프로젝트에 연결하면 됩니다. 추가 설정 없이 /api/mcp 라우트가 Vercel Function으로 배포됩니다.

  • 클라이언트가 항상 x-hf-token을 보낸다면 서버 환경변수는 필요 없습니다.

  • 폴백을 두려면 Vercel 프로젝트 Settings → Environment Variables에 HF_TOKEN을 추가하세요.

  • 이미지 생성은 시간이 걸리므로 라우트에 maxDuration = 60을 지정해 두었습니다.

  • Vercel Function의 응답 크기 제한(4.5MB)이 있으므로 num_inference_steps를 과도하게 올리지 마세요.

🖥️ stdio로 실행하기

HTTP 없이 기존처럼 로컬 프로세스로 붙이려면:

npm run build:stdio
node build/stdio.js
{
    "mcpServers": {
        "my-mcp-server": {
            "command": "node",
            "args": ["/ABSOLUTE/PATH/TO/YOUR/PROJECT/build/stdio.js"],
            "env": {
                "HF_TOKEN": "hf_xxxxxxxxxxxxxxxxxxxx"
            }
        }
    }
}

이 경우 헤더를 쓸 수 없으므로 HF_TOKEN 환경변수가 유일한 토큰 출처입니다.

🛠️ 개발 가이드

모든 tool / resource / prompt는 src/mcp/tools.tsregisterAll() 안에서 등록합니다. HTTP 라우트와 stdio 진입점이 같은 함수를 호출하므로 한 곳만 수정하면 양쪽에 반영됩니다.

Tool 추가하기

inputSchema에는 raw shape이 아니라 z.object()로 감싼 스키마를 넘깁니다 (MCP SDK v2 방식).

server.registerTool(
    'reverse-text',
    {
        description: '입력한 문자열을 뒤집어 반환합니다.',
        inputSchema: z.object({
            text: z.string().min(1).describe('뒤집을 문자열')
        }),
        outputSchema: textOutputSchema
    },
    async ({ text }) => textResult([...text].reverse().join(''))
)

외부 비밀값이 필요한 Tool

registerAll(server, options)options로 값을 받아 클로저에서 사용하면, HTTP에서는 요청 헤더로 stdio에서는 환경변수로 주입할 수 있습니다.

export type RegisterOptions = {
    hfToken?: string
    transport?: 'stdio' | 'http'
}

Resource 추가하기

server.registerResource(
    'app-settings',
    'config://settings',
    {
        title: '애플리케이션 설정',
        description: '애플리케이션의 현재 설정 정보',
        mimeType: 'application/json'
    },
    async (uri) => ({
        contents: [
            {
                uri: uri.href,
                mimeType: 'application/json',
                text: JSON.stringify({ theme: 'dark' }, null, 2)
            }
        ]
    })
)

🧰 제공 도구

이름

설명

greet

이름과 언어로 인사말 반환

calculator

두 숫자와 연산자로 사칙연산

get-time

타임존별 현재 시간

geocode

도시명을 위도/경도로 변환 (Open-Meteo)

get-weather

좌표로 현재 날씨 및 예보 (Open-Meteo)

generate-image

프롬프트로 이미지 생성 (HuggingFace FLUX.1-schnell)

Resource server://info, Prompt code-review도 함께 제공합니다.

📦 주요 의존성

  • @modelcontextprotocol/server: MCP TypeScript SDK v2

  • mcp-handler: MCP 서버를 웹 표준 HTTP 핸들러로 변환

  • next / react / react-dom: App Router 기반 호스팅

  • zod: 스키마 검증

  • @huggingface/inference: HuggingFace Inference API 클라이언트

🔧 스크립트

스크립트

설명

npm run dev

Next.js 개발 서버 실행

npm run build

Next.js 프로덕션 빌드

npm run start

프로덕션 서버 실행

npm run build:stdio

stdio 서버를 build/로 컴파일

npm run start:stdio

컴파일된 stdio 서버 실행

npm run typecheck

HTTP·stdio 양쪽 타입 검사

🔗 참고 자료

📄 라이선스

MIT