import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { InferenceClient } from '@huggingface/inference'
import { z } from 'zod'
// Smithery 설정 스키마 (선택적)
export const configSchema = z.object({
hfToken: z.string().optional().describe('Hugging Face API Token (이미지 생성에 필요)')
})
// Smithery가 요구하는 createServer 함수
export default function createServer({ config }: { config: z.infer<typeof configSchema> }) {
// Hugging Face 클라이언트 생성 (토큰이 있는 경우에만)
const hfClient = config.hfToken ? new InferenceClient(config.hfToken) : null
// 서버 인스턴스 생성
const server = new McpServer({
name: 'typescript-mcp-server',
version: '1.0.0',
capabilities: {
tools: {},
resources: {},
prompts: {}
}
})
// 예시 도구: 인사하기
server.tool(
'greeting',
{
name: z.string().describe('인사할 사람의 이름'),
language: z
.enum(['ko', 'en'])
.optional()
.default('ko')
.describe('인사 언어 (기본값: ko)')
},
async ({ name, language }) => {
const greeting =
language === 'ko'
? `안녕하세요, ${name}님! 😊`
: `Hello, ${name}! 👋`
return {
content: [
{
type: 'text',
text: greeting
}
]
}
}
)
// 예시 도구: 계산기
server.tool(
'calculator',
{
operation: z
.enum(['add', 'subtract', 'multiply', 'divide'])
.describe('수행할 연산 (add, subtract, multiply, divide)'),
a: z.number().describe('첫 번째 숫자'),
b: z.number().describe('두 번째 숫자')
},
async ({ operation, a, b }) => {
// 연산 수행
let result: number
switch (operation) {
case 'add':
result = a + b
break
case 'subtract':
result = a - b
break
case 'multiply':
result = a * b
break
case 'divide':
if (b === 0) throw new Error('0으로 나눌 수 없습니다')
result = a / b
break
default:
throw new Error('지원하지 않는 연산입니다')
}
const operationSymbols = {
add: '+',
subtract: '-',
multiply: '×',
divide: '÷'
} as const
const operationSymbol =
operationSymbols[operation as keyof typeof operationSymbols]
return {
content: [
{
type: 'text',
text: `${a} ${operationSymbol} ${b} = ${result}`
}
]
}
}
)
// 예시 도구: 시간 조회
server.tool(
'get_time',
{
timeZone: z.string().describe('시간대')
},
async ({ timeZone }) => {
return {
content: [
{
type: 'text',
text: new Date().toLocaleString('ko-KR', {
timeZone
})
}
]
}
}
)
// 예시 리소스: 서버 정보
server.resource(
'server://info',
'server://info',
{
name: '서버 정보',
description: 'TypeScript MCP Server 보일러플레이트 정보',
mimeType: 'application/json'
},
async () => {
const serverInfo = {
name: 'typescript-mcp-server',
version: '1.0.0',
description: 'TypeScript MCP Server 보일러플레이트',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
nodeVersion: process.version,
platform: process.platform
}
return {
contents: [
{
uri: 'server://info',
mimeType: 'application/json',
text: JSON.stringify(serverInfo, null, 2)
}
]
}
}
)
// 이미지 생성 도구 (HF 토큰이 있는 경우에만 등록)
if (hfClient) {
server.tool(
'generate_image',
{
prompt: z.string().describe('생성할 이미지에 대한 설명 (프롬프트)')
},
async ({ prompt }) => {
const image = await hfClient.textToImage({
provider: 'auto',
model: 'black-forest-labs/FLUX.1-schnell',
inputs: prompt,
parameters: { num_inference_steps: 5 }
})
// Blob을 base64로 변환
const imageBlob = image as unknown as Blob
const arrayBuffer = await imageBlob.arrayBuffer()
const base64Data = Buffer.from(arrayBuffer).toString('base64')
return {
content: [
{
type: 'image',
data: base64Data,
mimeType: 'image/png',
annotations: {
audience: ['user'],
priority: 0.9
}
}
]
}
}
)
}
// 예시 프롬프트: 코드 리뷰
server.prompt(
'code_review',
'Request Code Review',
{
code: z.string().describe('The code to review')
},
async ({ code }) => {
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: `다음 코드를 분석하고 상세한 리뷰를 제공해주세요:\n\n1. 코드 품질 평가\n2. 개선 가능한 부분\n3. 모범 사례 권장사항\n4. 보안 고려사항\n\n리뷰할 코드:\n\n\`\`\`\n${code}\n\`\`\``
}
}
]
}
}
)
// Smithery는 server.server를 반환해야 함
return server.server
}