Skip to main content
Glama
demo-ollama-organize.js11.4 kB
#!/usr/bin/env node /** * Ollama 기반 노트 정리 데모 * 실제 데이터를 생성하고 organize_notes 도구를 테스트합니다. */ const { spawn } = require('child_process'); const path = require('path'); const fs = require('fs'); // 색상 코드 const colors = { reset: '\x1b[0m', green: '\x1b[32m', red: '\x1b[31m', yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m', magenta: '\x1b[35m', }; function log(color, ...args) { console.log(color, ...args, colors.reset); } // MCP 클라이언트 class MCPClient { constructor() { this.requestId = 0; this.pendingRequests = new Map(); this.buffer = ''; } async start(vaultPath, indexPath) { const cliPath = path.join(__dirname, '../packages/mcp-server/dist/cli.js'); // 테스트 디렉토리 준비 if (fs.existsSync(vaultPath)) { fs.rmSync(vaultPath, { recursive: true }); } fs.mkdirSync(vaultPath, { recursive: true }); if (fs.existsSync(indexPath)) { fs.unlinkSync(indexPath); } this.process = spawn('node', [cliPath, '--vault', vaultPath, '--index', indexPath, '--timeout', '30000'], { stdio: ['pipe', 'pipe', 'pipe'], }); this.vaultPath = vaultPath; this.process.stdout.on('data', (data) => { this.buffer += data.toString(); this.processBuffer(); }); this.process.stderr.on('data', (data) => { // 로그는 stderr로 출력됨 }); // 초기화 await this.sendRequest('initialize', { protocolVersion: '2025-06-18', capabilities: {}, clientInfo: { name: 'demo-client', version: '1.0.0' }, }); await this.sendNotification('notifications/initialized', {}); } processBuffer() { const lines = this.buffer.split('\n'); this.buffer = lines.pop() || ''; for (const line of lines) { if (line.trim()) { try { const message = JSON.parse(line); if (message.id !== undefined && this.pendingRequests.has(message.id)) { const { resolve, reject } = this.pendingRequests.get(message.id); this.pendingRequests.delete(message.id); if (message.error) { reject(new Error(message.error.message)); } else { resolve(message.result); } } } catch (e) { // JSON 파싱 에러 무시 } } } } sendNotification(method, params) { const message = { jsonrpc: '2.0', method, params, }; this.process.stdin.write(JSON.stringify(message) + '\n'); } sendRequest(method, params) { return new Promise((resolve, reject) => { const id = this.requestId++; const message = { jsonrpc: '2.0', id, method, params, }; this.pendingRequests.set(id, { resolve, reject }); this.process.stdin.write(JSON.stringify(message) + '\n'); // 타임아웃 setTimeout(() => { if (this.pendingRequests.has(id)) { this.pendingRequests.delete(id); reject(new Error('Request timeout')); } }, 30000); }); } async callTool(name, args) { return this.sendRequest('tools/call', { name, arguments: args }); } async close() { this.process.kill(); } } // 샘플 노트 데이터 const sampleNotes = [ { title: '프로젝트 Alpha 킥오프 미팅', content: '새로운 프로젝트 Alpha의 킥오프 미팅을 진행했습니다.\n\n주요 논의 사항:\n- UI/UX 디자인 방향성\n- 백엔드 아키텍처 설계\n- 일정 및 마일스톤\n\n다음 미팅: 2주 후', category: 'Projects', tags: ['meeting', 'project-alpha', 'planning'], }, { title: 'UI 컴포넌트 디자인 가이드', content: '프로젝트에서 사용할 UI 컴포넌트 디자인 가이드라인입니다.\n\n- 버튼 스타일: Material Design 기반\n- 색상 팔레트: Blue (#2196F3), Green (#4CAF50)\n- 타이포그래피: Roboto 폰트 사용\n\n참고: [[design-system]]', category: 'Resources', tags: ['design', 'ui', 'components'], }, { title: 'TypeScript 베스트 프랙티스', content: 'TypeScript 개발 시 따라야 할 베스트 프랙티스 모음입니다.\n\n1. 명시적 타입 정의 사용\n2. any 타입 지양\n3. strict 모드 활성화\n4. 인터페이스보다 타입 별칭 선호\n\n관련: [[javascript-patterns]]', category: 'Resources', tags: ['typescript', 'programming', 'best-practices'], }, { title: '주간 회고', content: '이번 주 작업 회고입니다.\n\n잘한 점:\n- 프로젝트 Alpha 킥오프 성공\n- UI 컴포넌트 가이드 작성 완료\n\n개선할 점:\n- 코드 리뷰 시간 부족\n- 문서화 미흡\n\n다음 주 목표:\n- 백엔드 API 설계 완료\n- 프론트엔드 프로토타입 개발', category: 'Areas', tags: ['retrospective', 'weekly', 'reflection'], }, { title: 'API 설계 문서', content: '백엔드 API 설계 문서입니다.\n\n엔드포인트:\n- GET /api/users - 사용자 목록 조회\n- POST /api/users - 사용자 생성\n- PUT /api/users/:id - 사용자 수정\n- DELETE /api/users/:id - 사용자 삭제\n\n인증: JWT 토큰 사용\n\n관련: [[project-alpha]]', category: 'Projects', tags: ['api', 'backend', 'project-alpha'], }, { title: 'React Hooks 정리', content: 'React Hooks 사용법 정리입니다.\n\n주요 Hooks:\n- useState: 상태 관리\n- useEffect: 사이드 이펙트 처리\n- useContext: 컨텍스트 사용\n- useMemo: 메모이제이션\n- useCallback: 콜백 메모이제이션\n\n참고: [[react-patterns]]', category: 'Resources', tags: ['react', 'hooks', 'frontend'], }, { title: '장보기 목록', content: '주말에 장볼 것들:\n\n- 우유\n- 계란\n- 빵\n- 과일 (사과, 바나나)\n- 채소 (양파, 당근)\n\n예산: 5만원', category: 'Archives', tags: ['personal', 'shopping'], }, { title: 'Git 워크플로우', content: '팀에서 사용하는 Git 워크플로우입니다.\n\n브랜치 전략:\n- main: 프로덕션 코드\n- develop: 개발 중인 코드\n- feature/*: 기능 개발\n- hotfix/*: 긴급 수정\n\n커밋 메시지 규칙:\n- feat: 새 기능\n- fix: 버그 수정\n- docs: 문서 수정\n- refactor: 리팩토링', category: 'Resources', tags: ['git', 'workflow', 'development'], }, { title: '데이터베이스 스키마', content: '프로젝트 Alpha의 데이터베이스 스키마입니다.\n\nUsers 테이블:\n- id (PK)\n- email (UNIQUE)\n- name\n- created_at\n- updated_at\n\nPosts 테이블:\n- id (PK)\n- user_id (FK)\n- title\n- content\n- created_at\n\n관련: [[api-design]] [[project-alpha]]', category: 'Projects', tags: ['database', 'schema', 'project-alpha'], }, { title: '독서 노트: Clean Code', content: 'Clean Code 책을 읽고 정리한 내용입니다.\n\n핵심 원칙:\n- 의미 있는 이름 사용\n- 함수는 한 가지 일만\n- 주석보다 코드로 설명\n- 에러 처리 잘하기\n\n인상 깊은 구절:\n"나쁜 코드는 나쁜 코드를 부른다"\n\n적용할 점:\n- 함수 길이 줄이기\n- 변수명 개선', category: 'Resources', tags: ['book', 'clean-code', 'programming'], }, ]; async function runDemo() { log(colors.blue, '\n╔════════════════════════════════════════════════╗'); log(colors.blue, '║ Ollama 기반 노트 정리 데모 ║'); log(colors.blue, '╚════════════════════════════════════════════════╝\n'); const vaultPath = '/tmp/ollama-demo-vault'; const indexPath = '/tmp/ollama-demo-index.db'; const client = new MCPClient(); try { // 1. MCP 서버 시작 log(colors.yellow, '📡 MCP 서버 시작 중...'); await client.start(vaultPath, indexPath); log(colors.green, '✅ MCP 서버 시작 완료\n'); // 2. 샘플 노트 생성 log(colors.yellow, `📝 ${sampleNotes.length}개의 샘플 노트 생성 중...\n`); for (let i = 0; i < sampleNotes.length; i++) { const note = sampleNotes[i]; const result = await client.callTool('create_note', note); const uid = result._meta?.metadata?.id; log(colors.cyan, ` ${i + 1}. ${note.title} (${uid})`); } log(colors.green, '\n✅ 모든 노트 생성 완료\n'); // 3. 노트 목록 확인 log(colors.yellow, '📋 생성된 노트 목록:\n'); const listResult = await client.callTool('list_notes', { limit: 20 }); const notes = listResult._meta?.metadata?.notes || []; notes.forEach((note, i) => { log(colors.cyan, ` ${i + 1}. [${note.category}] ${note.title}`); log(colors.reset, ` 태그: ${note.tags.join(', ')}`); }); log(colors.green, `\n총 ${notes.length}개 노트\n`); // 4. organize_notes 실행 (dryRun) log(colors.blue, '\n' + '='.repeat(50)); log(colors.magenta, '\n🤖 Ollama를 통한 노트 정리 시작 (dryRun 모드)\n'); log(colors.blue, '='.repeat(50) + '\n'); const organizeResult = await client.callTool('organize_notes', { dryRun: true, limit: 10, }); if (organizeResult.content && organizeResult.content[0]) { log(colors.yellow, organizeResult.content[0].text); } log(colors.blue, '\n' + '='.repeat(50) + '\n'); // 5. 실제 적용 여부 확인 log(colors.yellow, '\n💡 실제로 변경사항을 적용하려면 dryRun: false로 설정하세요.\n'); log(colors.cyan, '예시:'); log(colors.reset, ' await client.callTool(\'organize_notes\', {'); log(colors.reset, ' dryRun: false,'); log(colors.reset, ' limit: 10'); log(colors.reset, ' });\n'); } catch (error) { log(colors.red, '\n❌ 오류 발생:', error.message); if (error.message.includes('Ollama')) { log(colors.yellow, '\n💡 Ollama가 실행 중이 아닙니다. 다음 명령어로 시작하세요:'); log(colors.cyan, ' ollama serve'); log(colors.yellow, '\n또는 Ollama를 설치하세요:'); log(colors.cyan, ' https://ollama.ai\n'); } } finally { await client.close(); log(colors.green, '✅ 데모 완료\n'); } } runDemo().catch(console.error);

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/inchan/memory-mcp'

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