# Dewey MCP 서버 API 명세서 ## 개요 Dewey MCP (Model Context Protocol) 서버는 JSON-RPC 2.0 프로토콜을 기반으로 AI 장기기억 시스템의 기능을 제공합니다. - **프로토콜 버전**: 2024-11-05 - **서버 이름**: Dewey Memory Server - **서버 버전**: 0.0.1-SNAPSHOT - **Base URL**: `http://localhost:8080/mcp` - **아키텍처**: MVC (Model-View-Controller) 패턴 --- ## 아키텍처 ### MVC 구조 ``` Controller (McpController) ↓ API Service (McpService) ↓ Domain Service (MemoryService) ↓ Repository (MemoryRepository) ↓ Database (PostgreSQL / Redis) ``` ### 계층별 역할 - **Controller**: HTTP 요청 처리 및 라우팅 - **API Service**: Controller와 Domain Service 사이의 어댑터 - **Domain Service**: 비즈니스 로직 처리 - **Repository**: 데이터 접근 계층 - **Model**: 도메인 엔티티 및 DTO --- ## 엔드포인트 ### POST /mcp 모든 MCP 요청은 이 단일 엔드포인트로 처리됩니다. 요청 본문은 JSON-RPC 2.0 형식을 따릅니다. **요청 헤더:** ``` Content-Type: application/json ``` --- ## MCP 메서드 ### 1. initialize MCP 서버를 초기화하고 서버 정보 및 지원 기능을 반환합니다. #### 요청 ```json { "jsonrpc": "2.0", "id": "1", "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "client-name", "version": "1.0.0" } } } ``` #### 응답 ```json { "jsonrpc": "2.0", "id": "1", "result": { "protocolVersion": "2024-11-05", "capabilities": { "tools": {}, "resources": {}, "prompts": {} }, "serverInfo": { "name": "Dewey Memory Server", "version": "0.0.1-SNAPSHOT" } } } ``` **응답 DTO**: `McpInitializeResponse` --- ### 2. tools/list 사용 가능한 모든 도구 목록을 반환합니다. #### 요청 ```json { "jsonrpc": "2.0", "id": "2", "method": "tools/list" } ``` #### 응답 ```json { "jsonrpc": "2.0", "id": "2", "result": { "tools": [ { "name": "store_memory", "description": "메모리를 Redis에 임시 저장합니다", "inputSchema": { "type": "object", "properties": { "memory_text": { "type": "string", "description": "저장할 메모리 텍스트" }, "user_id": { "type": "string", "description": "사용자 ID" }, "importance": { "type": "integer", "description": "중요도 (1-5)", "minimum": 1, "maximum": 5 } }, "required": ["memory_text", "user_id"] } }, { "name": "retrieve_memory", "description": "사용자의 메모리를 조회합니다", "inputSchema": { "type": "object", "properties": { "user_id": { "type": "string", "description": "사용자 ID" }, "limit": { "type": "integer", "description": "조회할 메모리 개수", "default": 10 } }, "required": ["user_id"] } }, { "name": "search_memory", "description": "벡터 유사도 기반으로 메모리를 검색합니다", "inputSchema": { "type": "object", "properties": { "query": { "type": "string", "description": "검색 쿼리" }, "user_id": { "type": "string", "description": "사용자 ID (선택)" }, "limit": { "type": "integer", "description": "검색 결과 개수", "default": 5 } }, "required": ["query"] } } ] } } ``` **응답 DTO**: `McpToolResponse.ToolListResponse` --- ### 3. tools/call 특정 도구를 실행합니다. 도구 실행 결과에 따라 응답 내용이 달라집니다. #### 요청 형식 ```json { "jsonrpc": "2.0", "id": "3", "method": "tools/call", "params": { "arguments": { "name": "도구명", "arguments": { "도구별_파라미터": "값" } } } } ``` #### 응답 형식 ```json { "jsonrpc": "2.0", "id": "3", "result": { "isError": false, "content": "실행 결과 메시지", "parts": [], "metadata": { "tool": "도구명", "timestamp": 1701234567890 } } } ``` **응답 DTO**: `McpToolCallResponse` #### 도구별 사용 예시 ##### store_memory 메모리를 Redis에 임시 저장합니다. **요청:** ```json { "jsonrpc": "2.0", "id": "4", "method": "tools/call", "params": { "arguments": { "name": "store_memory", "arguments": { "memory_text": "오늘 사용자가 피자를 주문했습니다", "user_id": "user123", "importance": 4 } } } } ``` **성공 응답:** ```json { "jsonrpc": "2.0", "id": "4", "result": { "isError": false, "content": "Memory stored successfully", "parts": [], "metadata": { "tool": "store_memory", "timestamp": 1701234567890 } } } ``` **비즈니스 로직**: `MemoryService.storeTemporaryMemory()` 호출 --- ##### retrieve_memory 사용자의 영구 메모리를 PostgreSQL에서 조회합니다. **요청:** ```json { "jsonrpc": "2.0", "id": "5", "method": "tools/call", "params": { "arguments": { "name": "retrieve_memory", "arguments": { "user_id": "user123", "limit": 10 } } } } ``` **성공 응답:** ```json { "jsonrpc": "2.0", "id": "5", "result": { "isError": false, "content": "Retrieved 10 memories", "parts": [], "metadata": { "tool": "retrieve_memory", "timestamp": 1701234567890 } } } ``` **비즈니스 로직**: `MemoryService.getPermanentMemories()` 호출 --- ##### search_memory 벡터 유사도 기반으로 메모리를 검색합니다. **요청:** ```json { "jsonrpc": "2.0", "id": "6", "method": "tools/call", "params": { "arguments": { "name": "search_memory", "arguments": { "query": "커피", "user_id": "user123", "limit": 5 } } } } ``` **성공 응답:** ```json { "jsonrpc": "2.0", "id": "6", "result": { "isError": false, "content": "Found 3 matching memories", "parts": [], "metadata": { "tool": "search_memory", "timestamp": 1701234567890 } } } ``` **비즈니스 로직**: `MemoryService.searchMemoriesByVector()` 호출 **에러 응답 예시:** ```json { "jsonrpc": "2.0", "id": "6", "result": { "isError": true, "content": "Error: Unknown tool: invalid_tool", "parts": [], "metadata": { "tool": "invalid_tool", "timestamp": 1701234567890 } } } ``` --- ### 4. resources/list 사용 가능한 모든 리소스 목록을 반환합니다. #### 요청 ```json { "jsonrpc": "2.0", "id": "7", "method": "resources/list" } ``` #### 응답 ```json { "jsonrpc": "2.0", "id": "7", "result": { "resources": [ { "uri": "memory://recent", "name": "Recent Memories", "description": "최근 저장된 메모리 리소스", "mimeType": "application/json" }, { "uri": "memory://long-term", "name": "Long-term Memories", "description": "장기 저장된 메모리 리소스", "mimeType": "application/json" } ] } } ``` **응답 DTO**: `McpResourceResponse.ResourceListResponse` --- ### 5. resources/read 특정 리소스의 내용을 읽습니다. #### 요청 ```json { "jsonrpc": "2.0", "id": "8", "method": "resources/read", "params": { "uri": "memory://recent" } } ``` 또는 ```json { "jsonrpc": "2.0", "id": "8", "method": "resources/read", "params": { "arguments": { "uri": "memory://recent" } } } ``` #### 응답 ```json { "jsonrpc": "2.0", "id": "8", "result": { "uri": "memory://recent", "mimeType": "application/json", "text": "{\"message\": \"Resource content for memory://recent\"}", "metadata": { "uri": "memory://recent", "timestamp": 1701234567890 } } } ``` **응답 DTO**: `McpResourceResponse.ResourceReadResponse` --- ### 6. prompts/list 사용 가능한 모든 프롬프트 목록을 반환합니다. #### 요청 ```json { "jsonrpc": "2.0", "id": "9", "method": "prompts/list" } ``` #### 응답 ```json { "jsonrpc": "2.0", "id": "9", "result": { "prompts": [ { "name": "memory_summary", "description": "메모리 목록을 요약하는 프롬프트", "arguments": [ { "name": "memories", "description": "요약할 메모리 텍스트 목록", "required": true } ] }, { "name": "memory_search", "description": "메모리 검색을 위한 프롬프트", "arguments": [ { "name": "query", "description": "검색할 키워드", "required": true } ] } ] } } ``` **응답 DTO**: `McpPromptResponse.PromptListResponse` --- ### 7. prompts/get 특정 프롬프트를 가져옵니다. #### 요청 ```json { "jsonrpc": "2.0", "id": "10", "method": "prompts/get", "params": { "arguments": { "name": "memory_summary", "arguments": { "memories": ["메모리1", "메모리2", "메모리3"] } } } } ``` #### 응답 ```json { "jsonrpc": "2.0", "id": "10", "result": { "name": "memory_summary", "messages": [ { "role": "user", "content": "Prompt: memory_summary" } ], "arguments": { "memories": ["메모리1", "메모리2", "메모리3"] } } } ``` **응답 DTO**: `McpPromptResponse.PromptGetResponse` --- ## 에러 응답 에러가 발생한 경우 다음과 같은 형식으로 응답됩니다: ```json { "jsonrpc": "2.0", "id": "1", "error": { "code": -32603, "message": "Internal error", "data": null } } ``` ### 에러 코드 | 코드 | 의미 | 설명 | |------|------|------| | `-32600` | Invalid Request | 요청 형식이 잘못됨 | | `-32601` | Method not found | 존재하지 않는 메서드 호출 | | `-32602` | Invalid params | 파라미터가 잘못됨 | | `-32603` | Internal error | 서버 내부 오류 | | `-32700` | Parse error | JSON 파싱 오류 | **에러 처리**: `GlobalExceptionHandler`에서 전역 예외 처리 --- ## 사용 예시 ### cURL 예시 #### initialize ```bash curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "1", "method": "initialize", "params": { "protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": { "name": "test-client", "version": "1.0.0" } } }' ``` #### tools/list ```bash curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "2", "method": "tools/list" }' ``` #### tools/call - store_memory ```bash curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "3", "method": "tools/call", "params": { "arguments": { "name": "store_memory", "arguments": { "memory_text": "사용자는 파이썬을 좋아합니다", "user_id": "user123", "importance": 4 } } } }' ``` #### tools/call - retrieve_memory ```bash curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "4", "method": "tools/call", "params": { "arguments": { "name": "retrieve_memory", "arguments": { "user_id": "user123", "limit": 10 } } } }' ``` #### tools/call - search_memory ```bash curl -X POST http://localhost:8080/mcp \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": "5", "method": "tools/call", "params": { "arguments": { "name": "search_memory", "arguments": { "query": "커피", "user_id": "user123", "limit": 5 } } } }' ``` --- ## 데이터 모델 ### Memory (도메인 엔티티) PostgreSQL에 저장되는 영구 메모리 엔티티입니다. ```java @Entity @Table(name = "memories") public class Memory { private Long id; private String userId; private String memoryText; private Integer importance; // 1~5 private String tags; // JSON 형식 private LocalDateTime createdAt; private LocalDateTime updatedAt; } ``` ### TemporaryMemory (도메인 모델) Redis에 저장되는 임시 메모리 모델입니다. ```java public class TemporaryMemory { private String id; // Redis key private String userId; private String memoryText; private Integer importance; private LocalDateTime createdAt; private LocalDateTime expiresAt; // TTL 기반 } ``` ### MemoryResponse (응답 DTO) ```java public class MemoryResponse { private Long id; private String userId; private String memoryText; private Integer importance; private String tags; private LocalDateTime createdAt; private LocalDateTime updatedAt; } ``` --- ## 참고사항 ### 1. 요청/응답 형식 - 모든 요청은 `Content-Type: application/json` 헤더가 필요합니다. - `id` 필드는 요청과 응답을 매칭하기 위해 사용됩니다. - 모든 응답은 `jsonrpc: "2.0"` 필드를 포함합니다. ### 2. 아키텍처 - **MVC 패턴**을 따르며, 각 계층이 명확히 분리되어 있습니다. - **Controller**는 요청 처리만 담당합니다. - **API Service**는 Controller와 Domain Service 사이의 어댑터 역할을 합니다. - **Domain Service**는 비즈니스 로직을 처리합니다. - **Repository**는 데이터 접근만 담당합니다. ### 3. 데이터 저장 - **임시 메모리**: Redis에 저장 (TTL 3일) - **영구 메모리**: PostgreSQL에 저장 - **벡터 검색**: PostgreSQL의 pgvector 확장 사용 (향후 구현) ### 4. CORS 설정 - CORS는 모든 origin에서 허용되도록 설정되어 있습니다 (`/mcp/**`). - 프로덕션 환경에서는 특정 origin만 허용하도록 변경하는 것을 권장합니다. ### 5. 예외 처리 - 전역 예외 처리는 `GlobalExceptionHandler`에서 수행됩니다. - 유효성 검증 오류는 `MethodArgumentNotValidException`으로 처리됩니다. - 모든 예외는 JSON-RPC 2.0 에러 형식으로 변환됩니다. ### 6. 구현 상태 - ✅ 기본 MCP 프로토콜 구현 완료 - ✅ 도메인 모델 및 Repository 생성 완료 - ✅ MVC 구조 재설계 완료 - ⏳ Redis 연동 (TODO) - ⏳ 벡터 검색 구현 (TODO) - ⏳ 배치 처리 구현 (TODO) --- ## 버전 정보 - **문서 버전**: 2.0.0 - **최종 업데이트**: 2025-12-10 - **서버 버전**: 0.0.1-SNAPSHOT - **아키텍처**: MVC (Model-View-Controller) --- ## 관련 파일 구조 ``` src/main/java/com/pandol365/dewey/ ├── api/ │ ├── controller/ │ │ └── McpController.java # Controller 계층 │ ├── dto/ │ │ ├── request/ # 요청 DTO │ │ │ ├── McpJsonRpcRequest.java │ │ │ ├── MemoryStoreRequest.java │ │ │ └── MemorySearchRequest.java │ │ └── response/ # 응답 DTO (View) │ │ ├── McpJsonRpcResponse.java │ │ ├── McpInitializeResponse.java │ │ ├── McpToolResponse.java │ │ ├── McpResourceResponse.java │ │ ├── McpPromptResponse.java │ │ ├── McpToolCallResponse.java │ │ └── MemoryResponse.java │ └── service/ # API Service 계층 │ ├── McpService.java │ └── impl/ │ └── McpServiceImpl.java ├── domain/ │ └── memory/ │ ├── model/ # 도메인 모델 (Model) │ │ ├── Memory.java │ │ └── TemporaryMemory.java │ ├── repository/ # Repository 계층 │ │ └── MemoryRepository.java │ └── service/ # Domain Service 계층 │ ├── MemoryService.java │ └── impl/ │ └── MemoryServiceImpl.java └── exception/ └── GlobalExceptionHandler.java # 예외 처리 ```