Skip to main content
Glama
README.md
# TypeScript MCP Server 보일러플레이트 (Vercel 배포용)

Model Context Protocol (MCP) 서버를 TypeScript로 개발하고 Vercel에 배포할 수 있는 보일러플레이트입니다.
Next.js App Router 위에서 [`mcp-handler`](https://github.com/vercel/mcp-handler)로 **Streamable 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. 의존성 설치

```bash
npm install
```

### 2. 환경변수 설정

```bash
cp .env.example .env.local
```

`.env.local`에 [HuggingFace 액세스 토큰](https://huggingface.co/settings/tokens)을 넣습니다. HTTP로 연결할 때는 클라이언트가 `x-hf-token` 헤더로 토큰을 보낼 수 있으므로 이 값은 선택 사항(폴백)입니다.

### 3. 개발 서버 실행

```bash
npm run dev
```

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

### 4. MCP Inspector로 테스트

```bash
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 핸들러에 원본 요청 헤더를 노출하지 않기 때문에, 요청마다 핸들러를 생성해 헤더 값을 등록 클로저로 전달합니다.

```ts
// 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`:

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

### 배포된 서버

```json
{
    "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`](https://www.npmjs.com/package/mcp-remote)로 연결할 수 있습니다.

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

## ☁️ Vercel 배포

```bash
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 없이 기존처럼 로컬 프로세스로 붙이려면:

```bash
npm run build:stdio
node build/stdio.js
```

```json
{
    "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.ts`의 `registerAll()` 안에서 등록합니다. HTTP 라우트와 stdio 진입점이 같은 함수를 호출하므로 한 곳만 수정하면 양쪽에 반영됩니다.

### Tool 추가하기

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

```ts
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에서는 환경변수로 주입할 수 있습니다.

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

### Resource 추가하기

```ts
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 양쪽 타입 검사           |

## 🔗 참고 자료

- [Model Context Protocol 공식 문서](https://modelcontextprotocol.io/)
- [Deploy MCP servers to Vercel](https://vercel.com/docs/mcp/deploy-mcp-servers-to-vercel)
- [vercel/mcp-handler](https://github.com/vercel/mcp-handler)
- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
- [Zod 문서](https://zod.dev/)

## 📄 라이선스

MIT