import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'
import { InferenceClient } from '@huggingface/inference'
// 선택적 설정 스키마 - Hugging Face API 키가 필요한 경우
export const configSchema = z.object({
huggingFaceToken: z.string().optional().describe('Hugging Face API 토큰 (이미지 생성 기능에 필요)')
})
// 언어별 인사말 정의
const greetings: Record<string, string> = {
korean: '안녕하세요',
english: 'Hello',
japanese: 'こんにちは',
chinese: '你好',
spanish: 'Hola',
french: 'Bonjour',
german: 'Hallo',
italian: 'Ciao',
portuguese: 'Olá',
russian: 'Привет'
}
// 가짜 서버 정보 데이터
const fakeServerInfo = {
serverName: 'MyApp Production Server',
hostname: 'prod-server-01.example.com',
ipAddress: '192.168.1.100',
port: 8080,
status: 'running',
uptime: '45 days, 12 hours, 30 minutes',
cpu: {
model: 'Intel Xeon E5-2680 v4',
cores: 16,
usage: '23.5%'
},
memory: {
total: '64 GB',
used: '42 GB',
free: '22 GB',
usage: '65.6%'
},
disk: {
total: '1 TB',
used: '650 GB',
free: '350 GB',
usage: '65%'
},
os: {
name: 'Ubuntu',
version: '22.04 LTS',
kernel: '5.15.0-91-generic'
},
services: [
{ name: 'nginx', status: 'active', port: 80 },
{ name: 'postgresql', status: 'active', port: 5432 },
{ name: 'redis', status: 'active', port: 6379 },
{ name: 'nodejs', status: 'active', port: 3000 }
],
lastUpdated: new Date().toISOString()
}
// Smithery 배포용 createServer 함수 - default export 필수
export default function createServer({ config }: { config: z.infer<typeof configSchema> }) {
// Create server instance
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0',
capabilities: {
tools: {},
resources: {},
prompts: {}
}
})
// greeting 도구 등록
server.registerTool(
'greeting',
{
title: 'Greeting Tool',
description: '사용자의 이름과 언어를 입력받아 해당 언어로 인사말을 반환합니다.',
inputSchema: {
name: z.string().describe('인사할 사람의 이름'),
language: z.enum([
'korean', 'english', 'japanese', 'chinese',
'spanish', 'french', 'german', 'italian',
'portuguese', 'russian'
]).describe('인사말 언어 (korean, english, japanese, chinese, spanish, french, german, italian, portuguese, russian)')
},
outputSchema: {
greeting: z.string()
}
},
async ({ name, language }) => {
const greetingPhrase = greetings[language] || 'Hello'
const message = `${greetingPhrase}, ${name}!`
const output = { greeting: message }
return {
content: [{ type: 'text', text: message }],
structuredContent: output
}
}
)
// calc 도구 등록
server.registerTool(
'calc',
{
title: 'Calculator Tool',
description: '두 개의 숫자와 연산자를 입력받아 계산 결과를 반환합니다.',
inputSchema: {
num1: z.number().describe('첫 번째 숫자'),
num2: z.number().describe('두 번째 숫자'),
operator: z.enum(['+', '-', '*', '/']).describe('연산자 (+, -, *, /)')
},
outputSchema: {
result: z.number(),
expression: z.string()
}
},
async ({ num1, num2, operator }) => {
let result: number
switch (operator) {
case '+':
result = num1 + num2
break
case '-':
result = num1 - num2
break
case '*':
result = num1 * num2
break
case '/':
if (num2 === 0) {
return {
content: [{ type: 'text', text: '오류: 0으로 나눌 수 없습니다.' }],
isError: true
}
}
result = num1 / num2
break
}
const expression = `${num1} ${operator} ${num2} = ${result}`
const output = { result, expression }
return {
content: [{ type: 'text', text: expression }],
structuredContent: output
}
}
)
// real_time 도구 등록
server.registerTool(
'real_time',
{
title: 'Real Time Tool',
description: '현재 시간과 타임존 정보를 반환합니다.',
inputSchema: {
timezone: z.string().optional().describe('IANA 타임존 (예: Asia/Seoul, America/New_York). 미입력시 시스템 타임존 사용')
},
outputSchema: {
currentTime: z.string(),
timezone: z.string(),
timestamp: z.number()
}
},
async ({ timezone }) => {
const now = new Date()
const timestamp = now.getTime()
// 시스템 타임존 또는 지정된 타임존 사용
const tz = timezone || Intl.DateTimeFormat().resolvedOptions().timeZone
// 지정된 타임존으로 시간 포맷팅
const options: Intl.DateTimeFormatOptions = {
timeZone: tz,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}
let formattedTime: string
try {
formattedTime = new Intl.DateTimeFormat('ko-KR', options).format(now)
} catch (error) {
return {
content: [{ type: 'text', text: `오류: 잘못된 타임존입니다 - ${tz}` }],
isError: true
}
}
const message = `현재 시간: ${formattedTime} (${tz})`
const output = {
currentTime: formattedTime,
timezone: tz,
timestamp
}
return {
content: [{ type: 'text', text: message }],
structuredContent: output
}
}
)
// server-info 리소스 등록
server.registerResource(
'server-info',
'server://info',
{
title: 'Server Information',
description: '가짜 서버 정보를 반환합니다.',
mimeType: 'application/json'
},
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: 'application/json',
text: JSON.stringify(fakeServerInfo, null, 2)
}]
})
)
// code_review 프롬프트 등록
server.registerPrompt(
'code_review',
{
title: 'Code Review Prompt',
description: '코드를 입력받아 코드 리뷰를 수행하는 프롬프트입니다.',
argsSchema: {
code: z.string().describe('리뷰할 코드'),
language: z.string().optional().describe('프로그래밍 언어 (예: javascript, python, java)'),
focus: z.enum(['security', 'performance', 'readability', 'all']).optional().describe('리뷰 초점 영역')
}
},
({ code, language, focus }) => {
const lang = language || '알 수 없음'
const reviewFocus = focus || 'all'
let focusInstruction = ''
switch (reviewFocus) {
case 'security':
focusInstruction = '보안 취약점과 잠재적인 보안 문제에 집중해주세요.'
break
case 'performance':
focusInstruction = '성능 최적화와 효율성에 집중해주세요.'
break
case 'readability':
focusInstruction = '코드 가독성, 네이밍 컨벤션, 코드 구조에 집중해주세요.'
break
case 'all':
focusInstruction = '보안, 성능, 가독성, 베스트 프랙티스 등 모든 측면에서 리뷰해주세요.'
break
}
return {
messages: [
{
role: 'user',
content: {
type: 'text',
text: `다음 코드를 리뷰해주세요.
## 프로그래밍 언어
${lang}
## 리뷰 초점
${focusInstruction}
## 리뷰 항목
다음 항목들을 확인하고 피드백을 제공해주세요:
1. **버그 및 오류**: 잠재적인 버그나 런타임 오류
2. **보안**: 보안 취약점 (SQL 인젝션, XSS 등)
3. **성능**: 성능 개선 가능한 부분
4. **가독성**: 코드 가독성 및 유지보수성
5. **베스트 프랙티스**: 해당 언어의 베스트 프랙티스 준수 여부
6. **개선 제안**: 구체적인 개선 방안
## 리뷰할 코드
\`\`\`${lang}
${code}
\`\`\`
상세하고 건설적인 피드백을 한국어로 제공해주세요.`
}
}
]
}
}
)
// generate_image 도구 등록
server.registerTool(
'generate_image',
{
title: 'Image Generation Tool',
description: '텍스트 프롬프트를 입력받아 AI 이미지를 생성합니다. (FLUX.1-schnell 모델 사용)',
inputSchema: {
prompt: z.string().describe('이미지 생성을 위한 텍스트 프롬프트 (영어 권장)')
}
},
async ({ prompt }) => {
try {
// 설정에서 토큰 가져오기 또는 환경 변수에서 가져오기
const token = config?.huggingFaceToken || process.env.HF_TOKEN
if (!token) {
return {
content: [{ type: 'text', text: '오류: Hugging Face API 토큰이 설정되지 않았습니다. configSchema에서 huggingFaceToken을 설정하거나 HF_TOKEN 환경 변수를 설정해주세요.' }],
isError: true
}
}
// Hugging Face Inference Client 초기화
const client = new InferenceClient(token)
// 이미지 생성
const image = await client.textToImage({
provider: 'auto',
model: 'black-forest-labs/FLUX.1-schnell',
inputs: prompt,
parameters: { num_inference_steps: 5 }
})
// Blob을 ArrayBuffer로 변환 후 Base64 인코딩
let base64Data: string
if (image instanceof Blob) {
const arrayBuffer = await image.arrayBuffer()
base64Data = Buffer.from(arrayBuffer).toString('base64')
} else if (typeof image === 'string') {
// 이미 base64 문자열인 경우
base64Data = image
} else {
// 기타 경우 (Buffer 등)
base64Data = Buffer.from(image as ArrayBuffer).toString('base64')
}
return {
content: [
{
type: 'image',
data: base64Data,
mimeType: 'image/png',
annotations: {
audience: ['user'],
priority: 0.9
}
}
]
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '알 수 없는 오류'
return {
content: [{ type: 'text', text: `이미지 생성 오류: ${errorMessage}` }],
isError: true
}
}
}
)
// Smithery는 server.server를 반환해야 함
return server.server
}