17 KiB
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 서버를 초기화하고 서버 정보 및 지원 기능을 반환합니다.
요청
{
"jsonrpc": "2.0",
"id": "1",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "client-name",
"version": "1.0.0"
}
}
}
응답
{
"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
사용 가능한 모든 도구 목록을 반환합니다.
요청
{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/list"
}
응답
{
"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
특정 도구를 실행합니다. 도구 실행 결과에 따라 응답 내용이 달라집니다.
요청 형식
{
"jsonrpc": "2.0",
"id": "3",
"method": "tools/call",
"params": {
"arguments": {
"name": "도구명",
"arguments": {
"도구별_파라미터": "값"
}
}
}
}
응답 형식
{
"jsonrpc": "2.0",
"id": "3",
"result": {
"isError": false,
"content": "실행 결과 메시지",
"parts": [],
"metadata": {
"tool": "도구명",
"timestamp": 1701234567890
}
}
}
응답 DTO: McpToolCallResponse
도구별 사용 예시
store_memory
메모리를 Redis에 임시 저장합니다.
요청:
{
"jsonrpc": "2.0",
"id": "4",
"method": "tools/call",
"params": {
"arguments": {
"name": "store_memory",
"arguments": {
"memory_text": "오늘 사용자가 피자를 주문했습니다",
"user_id": "user123",
"importance": 4
}
}
}
}
성공 응답:
{
"jsonrpc": "2.0",
"id": "4",
"result": {
"isError": false,
"content": "Memory stored successfully",
"parts": [],
"metadata": {
"tool": "store_memory",
"timestamp": 1701234567890
}
}
}
비즈니스 로직: MemoryService.storeTemporaryMemory() 호출
retrieve_memory
사용자의 영구 메모리를 PostgreSQL에서 조회합니다.
요청:
{
"jsonrpc": "2.0",
"id": "5",
"method": "tools/call",
"params": {
"arguments": {
"name": "retrieve_memory",
"arguments": {
"user_id": "user123",
"limit": 10
}
}
}
}
성공 응답:
{
"jsonrpc": "2.0",
"id": "5",
"result": {
"isError": false,
"content": "Retrieved 10 memories",
"parts": [],
"metadata": {
"tool": "retrieve_memory",
"timestamp": 1701234567890
}
}
}
비즈니스 로직: MemoryService.getPermanentMemories() 호출
search_memory
벡터 유사도 기반으로 메모리를 검색합니다.
요청:
{
"jsonrpc": "2.0",
"id": "6",
"method": "tools/call",
"params": {
"arguments": {
"name": "search_memory",
"arguments": {
"query": "커피",
"user_id": "user123",
"limit": 5
}
}
}
}
성공 응답:
{
"jsonrpc": "2.0",
"id": "6",
"result": {
"isError": false,
"content": "Found 3 matching memories",
"parts": [],
"metadata": {
"tool": "search_memory",
"timestamp": 1701234567890
}
}
}
비즈니스 로직: MemoryService.searchMemoriesByVector() 호출
에러 응답 예시:
{
"jsonrpc": "2.0",
"id": "6",
"result": {
"isError": true,
"content": "Error: Unknown tool: invalid_tool",
"parts": [],
"metadata": {
"tool": "invalid_tool",
"timestamp": 1701234567890
}
}
}
4. resources/list
사용 가능한 모든 리소스 목록을 반환합니다.
요청
{
"jsonrpc": "2.0",
"id": "7",
"method": "resources/list"
}
응답
{
"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
특정 리소스의 내용을 읽습니다.
요청
{
"jsonrpc": "2.0",
"id": "8",
"method": "resources/read",
"params": {
"uri": "memory://recent"
}
}
또는
{
"jsonrpc": "2.0",
"id": "8",
"method": "resources/read",
"params": {
"arguments": {
"uri": "memory://recent"
}
}
}
응답
{
"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
사용 가능한 모든 프롬프트 목록을 반환합니다.
요청
{
"jsonrpc": "2.0",
"id": "9",
"method": "prompts/list"
}
응답
{
"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
특정 프롬프트를 가져옵니다.
요청
{
"jsonrpc": "2.0",
"id": "10",
"method": "prompts/get",
"params": {
"arguments": {
"name": "memory_summary",
"arguments": {
"memories": ["메모리1", "메모리2", "메모리3"]
}
}
}
}
응답
{
"jsonrpc": "2.0",
"id": "10",
"result": {
"name": "memory_summary",
"messages": [
{
"role": "user",
"content": "Prompt: memory_summary"
}
],
"arguments": {
"memories": ["메모리1", "메모리2", "메모리3"]
}
}
}
응답 DTO: McpPromptResponse.PromptGetResponse
에러 응답
에러가 발생한 경우 다음과 같은 형식으로 응답됩니다:
{
"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
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
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
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
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
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에 저장되는 영구 메모리 엔티티입니다.
@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에 저장되는 임시 메모리 모델입니다.
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)
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 # 예외 처리