Skip to main content
Glama
index.ts23.2 kB
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) }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ciel240/class_study'

If you have feedback or need assistance with the MCP directory API, please join our Discord server