Skip to main content
Glama
README.md
# TypeScript MCP Server (Vercel 배포용)

Model Context Protocol (MCP) 서버 보일러플레이트입니다. Next.js App Router 위에서
[`mcp-handler`](https://github.com/vercel/mcp-handler)로 **Streamable HTTP** 엔드포인트를
제공하며, 같은 도구 정의를 재사용하는 **stdio** 진입점도 함께 유지합니다.

- HTTP 엔드포인트: `POST /api/mcp`
- stdio 진입점: `src/mcp/stdio.ts`
- 이미지 생성 토큰: `x-hf-token` 요청 헤더 우선, 없으면 `HF_TOKEN` 환경변수로 폴백

## 📁 프로젝트 구조

```
├── app/
│   ├── api/mcp/route.ts    # Streamable HTTP 엔드포인트
│   ├── layout.tsx          # 루트 레이아웃
│   ├── page.tsx            # 서버 안내 페이지
│   └── globals.css
├── src/mcp/
│   ├── metadata.ts         # 서버 이름/버전, 헤더 이름, 기능 목록 (단일 출처)
│   ├── server.ts           # registerAll() - 도구/리소스/프롬프트 등록
│   └── stdio.ts            # stdio 진입점
├── next.config.ts
├── tsconfig.json
└── .env.example
```

`app/api/mcp/route.ts`와 `src/mcp/stdio.ts`는 둘 다 `registerAll()`을 호출하므로
도구를 한 번만 정의하면 두 트랜스포트가 함께 갱신됩니다.

## 🚀 시작하기

```bash
npm install
npm run dev
```

`http://localhost:3000`에서 안내 페이지를, `http://localhost:3000/api/mcp`에서
MCP 엔드포인트를 확인할 수 있습니다.

### 스크립트

| 명령                | 설명                                       |
| ------------------- | ------------------------------------------ |
| `npm run dev`       | Next.js 개발 서버                          |
| `npm run build`     | 프로덕션 빌드                              |
| `npm run start`     | 빌드 결과 실행                             |
| `npm run stdio`     | stdio 트랜스포트로 MCP 서버 실행 (tsx)     |
| `npm run typecheck` | 타입 검사                                  |

## 🔑 HF_TOKEN 전달 방식

`generate-image` 도구는 HuggingFace Inference API를 사용합니다. 토큰은 다음 순서로 찾습니다.

1. 요청의 `x-hf-token` 헤더 (HTTP 트랜스포트)
2. 서버의 `HF_TOKEN` 환경변수

헤더를 쓰면 클라이언트마다 자기 토큰을 사용할 수 있어 서버에 비밀값을 두지 않아도 됩니다.
stdio로 실행할 때는 HTTP 요청이 없으므로 항상 `HF_TOKEN` 환경변수를 사용합니다.

구현은 `src/mcp/server.ts`의 `resolveHfToken()`에 있으며, MCP SDK v2가 도구 콜백의
두 번째 인자로 넘겨주는 컨텍스트에서 원본 `Request`를 꺼내 씁니다.

```ts
const resolveHfToken = (ctx: ServerContext): string | undefined =>
    ctx.http?.req?.headers.get(HF_TOKEN_HEADER)?.trim() || process.env.HF_TOKEN
```

로컬에서 환경변수를 쓰려면 `.env.example`을 `.env.local`로 복사한 뒤 값을 채우세요.

## ▲ Vercel 배포

1. 저장소를 Vercel에 연결하면 Next.js 프로젝트로 자동 인식됩니다.
2. (선택) 서버 기본 토큰을 두려면 프로젝트 설정 → Environment Variables에 `HF_TOKEN`을 추가합니다.
   클라이언트가 항상 `x-hf-token` 헤더를 보낸다면 설정하지 않아도 됩니다.
3. 배포 후 엔드포인트는 `https://seunghui-mcp-server.vercel.app/api/mcp` 입니다.

`app/api/mcp/route.ts`는 Node 런타임과 `maxDuration = 60`을 지정합니다. 이미지 생성처럼
오래 걸리는 도구가 있다면 플랜에 맞춰 값을 조정하세요.

> `mcp-handler` 2.x는 stateless로 동작하므로 Redis가 필요 없고, 레거시 HTTP+SSE
> 트랜스포트는 제공하지 않습니다. 엔드포인트 경로는 라우트 파일 위치로 결정됩니다.

## 🔧 MCP 클라이언트 연결

### HTTP (배포 후 또는 로컬 개발 서버)

`.cursor/mcp.json`:

```json
{
    "mcpServers": {
        "typescript-mcp-server": {
            "url": "https://seunghui-mcp-server.vercel.app/api/mcp",
            "headers": {
                "x-hf-token": "hf_xxxxxxxxxxxxxxxx"
            }
        }
    }
}
```

Streamable HTTP를 지원하지 않는 stdio 전용 클라이언트는
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote)를 경유할 수 있습니다.

```json
{
    "mcpServers": {
        "typescript-mcp-server": {
            "command": "npx",
            "args": [
                "-y",
                "mcp-remote",
                "https://seunghui-mcp-server.vercel.app/api/mcp",
                "--header",
                "x-hf-token:hf_xxxxxxxxxxxxxxxx"
            ]
        }
    }
}
```

### stdio (로컬)

```json
{
    "mcpServers": {
        "typescript-mcp-server": {
            "command": "npx",
            "args": ["tsx", "src/mcp/stdio.ts"],
            "cwd": "/ABSOLUTE/PATH/TO/YOUR/PROJECT",
            "env": { "HF_TOKEN": "hf_xxxxxxxxxxxxxxxx" }
        }
    }
}
```

> `.cursor/mcp.json`은 토큰을 담을 수 있어 `.gitignore`에 포함되어 있습니다.

## 🧪 MCP Inspector로 테스트

```bash
npm run dev
npx @modelcontextprotocol/inspector
```

1. `http://127.0.0.1:6274` 접속
2. 왼쪽 드롭다운에서 **Streamable HTTP** 선택
3. URL에 `http://localhost:3000/api/mcp` 입력
4. Configuration에서 커스텀 헤더 `x-hf-token`을 추가하면 `generate-image` 테스트 가능
5. **Connect** → **List Tools**

## 🛠️ 도구 추가하기

`src/mcp/server.ts`의 `registerAll()` 안에 등록하면 HTTP와 stdio 양쪽에 동시에 반영됩니다.
MCP SDK v2는 `inputSchema`/`outputSchema`에 Zod 객체 스키마를 그대로 받습니다.

```ts
server.registerTool(
    'roll-dice',
    {
        description: 'N면체 주사위를 굴립니다.',
        inputSchema: z.object({
            sides: z.number().int().min(2).describe('주사위 면 수')
        })
    },
    async ({ sides }) => {
        const value = 1 + Math.floor(Math.random() * sides)
        return { content: [{ type: 'text', text: `🎲 ${value}` }] }
    }
)
```

HTTP 요청 정보가 필요하면 두 번째 인자 `ctx`에서 `ctx.http?.req`로 접근합니다.
stdio 실행 시에는 `ctx.http`가 `undefined`이므로 항상 옵셔널 체이닝으로 다루세요.

새 도구를 추가하면 `src/mcp/metadata.ts`의 `TOOLS` 목록에도 넣어 주세요.
`server-info` 리소스 텍스트와 안내 페이지가 이 목록을 함께 사용합니다.

## 📦 주요 의존성

- **mcp-handler**: MCP 서버를 Web 표준 `Request` 핸들러로 변환 (Vercel 어댑터)
- **@modelcontextprotocol/server**: MCP TypeScript SDK v2 서버 패키지
- **next / react**: HTTP 라우트 호스팅
- **zod**: 도구 입출력 스키마
- **@huggingface/inference**: 이미지 생성
- **tsx**: stdio 진입점 실행

## 🔗 참고 자료

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

## 📄 라이선스

MIT