import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js'
import { z } from 'zod'
import { InferenceClient } from '@huggingface/inference'
// Export configSchema for Smithery
export const configSchema = z.object({
HF_TOKEN: z.string().describe('Hugging Face API Token for image generation')
})
// Greeting tool schema
const GreetingToolSchema = z.object({
name: z.string().describe('사용자의 이름'),
language: z.string().describe('인사말을 할 언어 (예: korean, english, japanese, chinese, spanish, french)')
})
// Calculator tool schema
const CalculatorToolSchema = z.object({
num1: z.number().describe('첫 번째 숫자'),
num2: z.number().describe('두 번째 숫자'),
operation: z.enum(['add', 'subtract', 'multiply', 'divide']).describe('연산자 (add: 덧셈, subtract: 뺄셈, multiply: 곱셈, divide: 나눗셈)')
})
// Time tool schema
const TimeToolSchema = z.object({
timezone: z.string().optional().describe('시간대 (예: Asia/Seoul, America/New_York, Europe/London). 입력하지 않으면 한국 시간대(Asia/Seoul)를 사용합니다.')
})
// Code review tool schema
const CodeReviewToolSchema = z.object({
code: z.string().describe('리뷰할 코드'),
language: z.string().optional().describe('코드 언어 (예: javascript, typescript, python, java, cpp, go, rust). 입력하지 않으면 자동으로 감지합니다.'),
reviewType: z.enum(['comprehensive', 'security', 'performance', 'readability', 'best_practices']).optional().describe('리뷰 유형 (comprehensive: 종합적, security: 보안, performance: 성능, readability: 가독성, best_practices: 모범사례). 기본값은 comprehensive입니다.')
})
// Image generation tool schema
const ImageGenerationToolSchema = z.object({
prompt: z.string().describe('이미지 생성을 위한 프롬프트')
})
// Greeting messages in different languages
const greetingMessages: Record<string, string> = {
korean: '안녕하세요',
english: 'Hello',
japanese: 'こんにちは',
chinese: '你好',
spanish: 'Hola',
french: 'Bonjour',
german: 'Hallo',
italian: 'Ciao',
portuguese: 'Olá',
russian: 'Привет'
}
// Calculator operations
const calculate = (num1: number, num2: number, operation: string): number => {
switch (operation) {
case 'add':
return num1 + num2
case 'subtract':
return num1 - num2
case 'multiply':
return num1 * num2
case 'divide':
if (num2 === 0) {
throw new Error('0으로 나눌 수 없습니다')
}
return num1 / num2
default:
throw new Error('지원하지 않는 연산입니다')
}
}
// Get current time in specified timezone
const getCurrentTime = (timezone: string = 'Asia/Seoul'): string => {
try {
const now = new Date()
const options: Intl.DateTimeFormatOptions = {
timeZone: timezone,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false
}
const formatter = new Intl.DateTimeFormat('ko-KR', options)
const timeString = formatter.format(now)
return `${timeString} (${timezone})`
} catch (error) {
throw new Error(`유효하지 않은 시간대입니다: ${timezone}`)
}
}
// Generate comprehensive code review prompt
const generateCodeReviewPrompt = (code: string, language?: string, reviewType: string = 'comprehensive'): string => {
const detectedLanguage = language || detectLanguage(code)
const reviewTypeKorean = {
comprehensive: '종합적',
security: '보안',
performance: '성능',
readability: '가독성',
best_practices: '모범사례'
}[reviewType] || '종합적'
const basePrompt = `다음 ${detectedLanguage} 코드에 대한 ${reviewTypeKorean} 코드 리뷰를 수행해주세요.
**리뷰할 코드:**
\`\`\`${detectedLanguage}
${code}
\`\`\`
**리뷰 가이드라인:**
1. **코드 품질 분석**
- 코드의 전반적인 구조와 설계 패턴 평가
- 함수/클래스의 단일 책임 원칙 준수 여부
- 코드의 모듈성과 재사용성
2. **가독성 및 유지보수성**
- 변수명, 함수명의 명확성
- 주석의 적절성과 필요성
- 코드 포맷팅과 들여쓰기 일관성
- 복잡도 분석 (순환 복잡도, 중첩 깊이)
3. **성능 최적화**
- 알고리즘 효율성 분석
- 불필요한 반복문이나 중복 계산 식별
- 메모리 사용량 최적화 가능성
- 비동기 처리의 적절성
4. **보안 취약점**
- 입력 검증 및 검증 부족
- SQL 인젝션, XSS 등 보안 취약점
- 민감한 정보 노출 위험
- 인증/인가 로직의 안전성
5. **에러 처리**
- 예외 처리의 완전성
- 에러 메시지의 명확성
- 로깅 및 디버깅 정보의 적절성
6. **모범 사례 준수**
- 해당 언어의 코딩 컨벤션 준수
- 디자인 패턴의 적절한 사용
- 테스트 가능한 코드 구조
**리뷰 결과 형식:**
- 🔴 **Critical Issues**: 즉시 수정이 필요한 심각한 문제
- 🟡 **Warning**: 개선이 권장되는 문제
- 🟢 **Suggestion**: 선택적 개선 사항
- 💡 **Best Practice**: 모범 사례 제안
각 항목에 대해 구체적인 코드 예시와 개선 방안을 제시해주세요.`
// 리뷰 유형별 추가 가이드라인
const specificGuidelines = {
security: `
**보안 특화 리뷰 항목:**
- 입력 검증 및 sanitization
- 인증/인가 메커니즘
- 데이터 암호화 및 보안 저장
- 로그 보안 및 민감 정보 노출 방지
- CSRF, XSS, SQL Injection 등 웹 보안 취약점
- API 보안 및 접근 제어`,
performance: `
**성능 특화 리뷰 항목:**
- 시간 복잡도 및 공간 복잡도 분석
- 데이터베이스 쿼리 최적화
- 캐싱 전략 및 메모리 관리
- 비동기 처리 및 병렬 처리
- 리소스 로딩 최적화
- 프로파일링 포인트 식별`,
readability: `
**가독성 특화 리뷰 항목:**
- 코드 구조 및 네이밍 컨벤션
- 주석의 품질과 적절성
- 함수/메서드 길이 및 복잡도
- 매직 넘버/문자열 식별
- 코드 중복 제거
- 일관성 있는 코딩 스타일`,
best_practices: `
**모범 사례 특화 리뷰 항목:**
- SOLID 원칙 준수
- 디자인 패턴 적용
- 테스트 가능한 코드 구조
- 의존성 주입 및 결합도 최소화
- 코드 재사용성 및 확장성
- 문서화 및 API 설계`
}
return basePrompt + (specificGuidelines[reviewType as keyof typeof specificGuidelines] || '')
}
// Detect programming language from code
const detectLanguage = (code: string): string => {
const codeLower = code.toLowerCase()
// TypeScript/JavaScript detection
if (codeLower.includes('interface ') || codeLower.includes('type ') || codeLower.includes('enum ') ||
codeLower.includes('const ') && codeLower.includes(': ')) {
return 'typescript'
}
if (codeLower.includes('function ') || codeLower.includes('const ') || codeLower.includes('let ') ||
codeLower.includes('var ') || codeLower.includes('=>')) {
return 'javascript'
}
// Python detection
if (codeLower.includes('def ') || codeLower.includes('import ') || codeLower.includes('from ') ||
codeLower.includes('class ') && codeLower.includes(':')) {
return 'python'
}
// Java detection
if (codeLower.includes('public class ') || codeLower.includes('private ') ||
codeLower.includes('public static void main') || codeLower.includes('System.out.println')) {
return 'java'
}
// C++ detection
if (codeLower.includes('#include') || codeLower.includes('std::') ||
codeLower.includes('cout') || codeLower.includes('cin')) {
return 'cpp'
}
// Go detection
if (codeLower.includes('package ') || codeLower.includes('func ') ||
codeLower.includes('import ') && codeLower.includes('"')) {
return 'go'
}
// Rust detection
if (codeLower.includes('fn ') || codeLower.includes('let ') && codeLower.includes(': ') ||
codeLower.includes('use ') || codeLower.includes('mod ')) {
return 'rust'
}
// Default fallback
return 'unknown'
}
// Generate image using Hugging Face Inference API
const generateImage = async (prompt: string, hfToken: string): Promise<string> => {
if (!hfToken) {
throw new Error('HF_TOKEN이 설정되지 않았습니다. Hugging Face API 토큰을 설정해주세요.')
}
const client = new InferenceClient(hfToken)
try {
const image = await client.textToImage({
provider: "fal-ai",
model: "black-forest-labs/FLUX.1-schnell",
inputs: prompt,
parameters: { num_inference_steps: 5 }
})
// Convert to base64
let base64: string
if (typeof image === 'string') {
// If it's already a base64 string, use it directly
base64 = image
} else if (image && typeof image === 'object' && 'arrayBuffer' in image) {
// If it's a Blob-like object
const arrayBuffer = await (image as any).arrayBuffer()
base64 = Buffer.from(arrayBuffer).toString('base64')
} else {
// If it's a Buffer or Uint8Array
base64 = Buffer.from(image as any).toString('base64')
}
return base64
} catch (error) {
throw new Error(`이미지 생성 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`)
}
}
// Export for Smithery
export default function createServer({ config }: { config?: any }) {
// Extract HF_TOKEN from config or fallback to environment variable
const hfToken = config?.HF_TOKEN || process.env.HF_TOKEN
// Create server instance inside createServer function
const server = new Server(
{
name: 'code-review-mcp-server',
version: '1.0.0',
},
{
capabilities: {
tools: {},
resources: {},
},
}
)
// Register tools list handler
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'greeting',
description: '사용자의 이름과 언어를 입력받아 해당 언어로 인사하는 도구',
inputSchema: {
type: 'object',
properties: {
name: {
type: 'string',
description: '사용자의 이름'
},
language: {
type: 'string',
description: '인사말을 할 언어 (korean, english, japanese, chinese, spanish, french, german, italian, portuguese, russian)',
enum: Object.keys(greetingMessages)
}
},
required: ['name', 'language']
}
},
{
name: 'calculator',
description: '두 숫자에 대한 사칙연산을 수행하는 계산기 도구',
inputSchema: {
type: 'object',
properties: {
num1: {
type: 'number',
description: '첫 번째 숫자'
},
num2: {
type: 'number',
description: '두 번째 숫자'
},
operation: {
type: 'string',
description: '연산자',
enum: ['add', 'subtract', 'multiply', 'divide']
}
},
required: ['num1', 'num2', 'operation']
}
},
{
name: 'current_time',
description: '현재 시간을 지정된 시간대에서 조회하는 도구. 시간대를 입력하지 않으면 한국 시간대(Asia/Seoul)를 사용합니다.',
inputSchema: {
type: 'object',
properties: {
timezone: {
type: 'string',
description: '시간대 (예: Asia/Seoul, America/New_York, Europe/London). 입력하지 않으면 한국 시간대를 사용합니다.'
}
},
required: []
}
},
{
name: 'code_review',
description: '사용자가 제공한 코드에 대한 상세한 코드 리뷰 프롬프트를 생성하는 도구',
inputSchema: {
type: 'object',
properties: {
code: {
type: 'string',
description: '리뷰할 코드'
},
language: {
type: 'string',
description: '코드 언어 (예: javascript, typescript, python, java, cpp, go, rust). 입력하지 않으면 자동으로 감지합니다.'
},
reviewType: {
type: 'string',
description: '리뷰 유형 (comprehensive: 종합적, security: 보안, performance: 성능, readability: 가독성, best_practices: 모범사례)',
enum: ['comprehensive', 'security', 'performance', 'readability', 'best_practices']
}
},
required: ['code']
}
},
{
name: 'generate_image',
description: '텍스트 프롬프트를 사용하여 이미지를 생성하는 도구',
inputSchema: {
type: 'object',
properties: {
prompt: {
type: 'string',
description: '이미지 생성을 위한 프롬프트'
}
},
required: ['prompt']
}
}
]
}
})
// Register resources list handler
server.setRequestHandler(ListResourcesRequestSchema, async () => {
return {
resources: [
{
uri: 'server://info',
name: '서버 정보',
description: '현재 MCP 서버의 정보를 반환합니다',
mimeType: 'application/json'
}
]
}
})
// Register resource read handler
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
if (request.params.uri === 'server://info') {
const serverInfo = {
name: 'code-review-mcp-server',
version: '1.0.0',
description: '코드 리뷰, 시간 조회, 계산기, 인사말, 이미지 생성 기능을 제공하는 MCP 서버',
capabilities: {
tools: ['greeting', 'calculator', 'current_time', 'code_review', 'generate_image'],
resources: ['server://info']
},
uptime: process.uptime(),
nodeVersion: process.version,
platform: process.platform,
architecture: process.arch,
memoryUsage: process.memoryUsage(),
timestamp: new Date().toISOString()
}
return {
contents: [
{
uri: 'server://info',
mimeType: 'application/json',
text: JSON.stringify(serverInfo, null, 2)
}
]
}
}
throw new Error(`알 수 없는 리소스: ${request.params.uri}`)
})
// Register tool call handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name === 'greeting') {
try {
const { name, language } = GreetingToolSchema.parse(request.params.arguments)
const greeting = greetingMessages[language.toLowerCase()] || greetingMessages.english
const message = `${greeting}, ${name}!`
return {
content: [
{
type: 'text',
text: message
}
]
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}
],
isError: true
}
}
}
if (request.params.name === 'calculator') {
try {
const { num1, num2, operation } = CalculatorToolSchema.parse(request.params.arguments)
const result = calculate(num1, num2, operation)
const operationSymbols = {
add: '+',
subtract: '-',
multiply: '*',
divide: '/'
}
const symbol = operationSymbols[operation]
const message = `${num1} ${symbol} ${num2} = ${result}`
return {
content: [
{
type: 'text',
text: message
}
]
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `계산 오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}
],
isError: true
}
}
}
if (request.params.name === 'current_time') {
try {
const { timezone } = TimeToolSchema.parse(request.params.arguments)
const currentTime = getCurrentTime(timezone)
const message = `현재 시간: ${currentTime}`
return {
content: [
{
type: 'text',
text: message
}
]
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `시간 조회 오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}
],
isError: true
}
}
}
if (request.params.name === 'code_review') {
try {
const { code, language, reviewType = 'comprehensive' } = CodeReviewToolSchema.parse(request.params.arguments)
const reviewPrompt = generateCodeReviewPrompt(code, language, reviewType)
return {
content: [
{
type: 'text',
text: reviewPrompt
}
]
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `코드 리뷰 프롬프트 생성 오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}
],
isError: true
}
}
}
if (request.params.name === 'generate_image') {
try {
const { prompt } = ImageGenerationToolSchema.parse(request.params.arguments)
const base64Image = await generateImage(prompt, hfToken)
return {
content: [
{
type: 'image',
data: base64Image,
mimeType: 'image/png',
annotations: {
audience: ['user'],
priority: 0.9
}
}
]
}
} catch (error) {
return {
content: [
{
type: 'text',
text: `이미지 생성 오류: ${error instanceof Error ? error.message : '알 수 없는 오류'}`
}
],
isError: true
}
}
}
throw new Error(`알 수 없는 도구: ${request.params.name}`)
})
return server
}
// Start server (for local development)
async function main() {
const server = createServer({ config: {} })
const transport = new StdioServerTransport()
await server.connect(transport)
console.error('Code Review MCP Server started')
}
// Only run main if this file is executed directly
if (process.argv[1] && process.argv[1].endsWith('index.js')) {
main().catch(console.error)
}