대화 저장 MCP작성완료
This commit is contained in:
parent
ec0c8c55ba
commit
9d4906a141
|
|
@ -0,0 +1,297 @@
|
||||||
|
# Dewey MCP 대화 기록 저장 프롬프트
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
당신은 Dewey MCP (Model Context Protocol) 서버에 연결되어 있습니다. 사용자와의 대화가 완료되면 **항상** MCP 도구를 사용하여 대화 기록을 저장해야 합니다.
|
||||||
|
|
||||||
|
## MCP 도구 호출 규칙
|
||||||
|
|
||||||
|
## 📖 데이터 읽기 (Retrieve) - AI 버튜버 방송 기준
|
||||||
|
|
||||||
|
### 언제 과거 데이터를 읽어와야 하는가?
|
||||||
|
|
||||||
|
**방송 시작 시 (세션 초기화):**
|
||||||
|
- 방송이 시작되거나 새로운 대화 세션이 시작될 때
|
||||||
|
- 사용자와 처음 대화를 시작하기 전
|
||||||
|
- 이전 방송/대화의 맥락을 이어가야 할 때
|
||||||
|
|
||||||
|
**사용자 질문/요청 시:**
|
||||||
|
- 사용자가 "이전에 말한", "저번에", "예전에" 같은 과거 언급 표현 사용 시
|
||||||
|
- 사용자가 특정 주제나 약속을 다시 언급할 때
|
||||||
|
- 사용자의 선호도나 설정을 확인해야 할 때
|
||||||
|
- 맥락이 필요한 질문이 들어올 때 (예: "그거 어떻게 됐어?", "그때 말한 거 기억나?")
|
||||||
|
|
||||||
|
**주제 전환 시:**
|
||||||
|
- 대화 주제가 바뀔 때 관련 과거 정보 확인
|
||||||
|
- 사용자가 이전에 언급한 내용과 연관된 질문을 할 때
|
||||||
|
- 연속적인 대화 흐름을 유지해야 할 때
|
||||||
|
|
||||||
|
**맥락 부족 감지 시:**
|
||||||
|
- 사용자의 질문이 모호하거나 맥락이 필요할 때
|
||||||
|
- 사용자의 의도를 파악하기 위해 과거 대화가 필요할 때
|
||||||
|
- 개인화된 응답을 위해 사용자 정보가 필요할 때
|
||||||
|
|
||||||
|
### 어떤 도구를 사용해야 하는가?
|
||||||
|
|
||||||
|
**1. `retrieve_conversation` - 과거 대화 조회**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "retrieve_conversation",
|
||||||
|
"arguments": {
|
||||||
|
"user_id": "cursor-user",
|
||||||
|
"limit": 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **사용 시점**: 방송 시작 시, 최근 대화 맥락이 필요할 때
|
||||||
|
- **용도**: 최근 대화 기록을 시간순으로 조회하여 대화 흐름 파악
|
||||||
|
|
||||||
|
**2. `retrieve_memory` - 사용자 메모리 조회**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "retrieve_memory",
|
||||||
|
"arguments": {
|
||||||
|
"user_id": "cursor-user",
|
||||||
|
"limit": 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **사용 시점**: 사용자의 선호도, 약속, 중요한 정보를 확인해야 할 때
|
||||||
|
- **용도**: 저장된 메모리(선호사항, 약속, 중요 사실) 조회
|
||||||
|
|
||||||
|
**3. `search_memory` - 벡터 검색으로 관련 메모리 찾기**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "search_memory",
|
||||||
|
"arguments": {
|
||||||
|
"query": "검색 키워드",
|
||||||
|
"user_id": "cursor-user",
|
||||||
|
"limit": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- **사용 시점**: 특정 주제나 키워드와 관련된 과거 정보를 찾아야 할 때
|
||||||
|
- **용도**: 의미 기반 검색으로 관련 메모리 찾기
|
||||||
|
|
||||||
|
### 데이터 읽기 우선순위
|
||||||
|
|
||||||
|
1. **방송 시작 시**: `retrieve_conversation` (최근 10개) → `retrieve_memory` (최근 10개)
|
||||||
|
2. **과거 언급 감지 시**: `search_memory` (관련 키워드로 검색)
|
||||||
|
3. **선호도/약속 확인 시**: `retrieve_memory` (중요도 높은 메모리 우선)
|
||||||
|
4. **맥락 부족 시**: `retrieve_conversation` (최근 대화 확인)
|
||||||
|
|
||||||
|
### 데이터 읽기 예시
|
||||||
|
|
||||||
|
**시나리오 1: 방송 시작**
|
||||||
|
```
|
||||||
|
[방송 시작]
|
||||||
|
→ retrieve_conversation: 최근 대화 40개 조회
|
||||||
|
→ retrieve_memory: 최근 메모리 10개 조회
|
||||||
|
→ 조회된 정보를 바탕으로 개인화된 인사 및 대화 시작
|
||||||
|
```
|
||||||
|
|
||||||
|
**시나리오 2: 과거 언급 감지**
|
||||||
|
```
|
||||||
|
사용자: "저번에 말한 그 프로젝트 어떻게 됐어?"
|
||||||
|
→ search_memory: "프로젝트" 키워드로 검색
|
||||||
|
→ 관련 메모리 확인 후 답변
|
||||||
|
```
|
||||||
|
|
||||||
|
**시나리오 3: 선호도 확인**
|
||||||
|
```
|
||||||
|
사용자: "내가 좋아하는 음식 뭐였지?"
|
||||||
|
→ retrieve_memory: 사용자 메모리 조회
|
||||||
|
→ 선호도 정보 확인 후 답변
|
||||||
|
```
|
||||||
|
|
||||||
|
**시나리오 4: 맥락 부족**
|
||||||
|
```
|
||||||
|
사용자: "그거 해줘"
|
||||||
|
→ retrieve_conversation: 최근 대화 확인
|
||||||
|
→ 맥락 파악 후 답변
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💾 데이터 저장 (Store)
|
||||||
|
|
||||||
|
### 1. 언제 호출해야 하는가?
|
||||||
|
|
||||||
|
**대화 완료 시점에 항상 호출:**
|
||||||
|
- 사용자의 질문에 대한 답변을 완료한 후
|
||||||
|
- 사용자가 새로운 주제로 전환하기 전
|
||||||
|
- 대화 세션이 종료되기 직전
|
||||||
|
- 중요한 정보나 약속, 선호사항이 언급된 경우 즉시 저장
|
||||||
|
|
||||||
|
### 2. 어떤 도구를 사용해야 하는가?
|
||||||
|
|
||||||
|
**`store_memory` 도구를 사용하여 대화 기록 저장:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "store_memory",
|
||||||
|
"arguments": {
|
||||||
|
"memory_text": "대화 내용 요약",
|
||||||
|
"user_id": "cursor-user",
|
||||||
|
"importance": 1-5 (중요도)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 어떻게 호출해야 하는가?
|
||||||
|
|
||||||
|
#### 대화 기록 저장 형식
|
||||||
|
|
||||||
|
1. **대화 요약 작성:**
|
||||||
|
- 사용자의 질문/요청 내용
|
||||||
|
- AI의 답변/제공한 정보
|
||||||
|
- 중요한 사실, 약속, 선호사항이 있다면 명시
|
||||||
|
|
||||||
|
2. **중요도 설정:**
|
||||||
|
- `5`: 매우 중요한 약속, 선호사항, 중요한 사실
|
||||||
|
- `4`: 중요한 정보나 결정사항
|
||||||
|
- `3`: 일반적인 대화 내용 (기본값)
|
||||||
|
- `2`: 간단한 질의응답
|
||||||
|
- `1`: 단순 인사나 불필요한 내용 (저장 생략 가능)
|
||||||
|
|
||||||
|
3. **저장 예시:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
사용자: "서울 날씨를 검색해줄래?"
|
||||||
|
AI: "서울 날씨는 맑고 기온 5도입니다..."
|
||||||
|
→ 저장: "사용자가 서울 날씨를 검색 요청. 현재 맑고 기온 5도, 체감온도 3도, 낮 최고 11도 예상."
|
||||||
|
```
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
사용자: "MCP 서버 테스트를 진행했어"
|
||||||
|
AI: "테스트 결과를 확인했습니다..."
|
||||||
|
→ 저장: "MCP 서버 테스트 진행. content 배열 형식 수정 완료 및 정상 동작 확인."
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 저장하지 않아도 되는 경우
|
||||||
|
|
||||||
|
- 단순 인사 ("안녕하세요", "감사합니다" 등)
|
||||||
|
- 명확히 불필요하다고 판단되는 대화
|
||||||
|
- 중요도가 1 이하인 경우
|
||||||
|
|
||||||
|
## 구현 예시
|
||||||
|
|
||||||
|
### 대화 완료 후 자동 저장 패턴
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[사용자 질문에 답변 완료]
|
||||||
|
|
||||||
|
→ 대화 기록 저장:
|
||||||
|
- memory_text: "사용자 질문: [질문 내용]. AI 답변: [답변 요약]. 중요 정보: [있는 경우]"
|
||||||
|
- user_id: "cursor-user" (또는 요청에서 제공된 user_id)
|
||||||
|
- importance: [적절한 중요도 1-5]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 주의사항
|
||||||
|
|
||||||
|
1. **항상 대화 완료 후 저장**: 사용자의 질문에 답변을 완료하면 반드시 `store_memory`를 호출하세요.
|
||||||
|
|
||||||
|
2. **요약 작성**: 전체 대화를 그대로 저장하지 말고, 핵심 내용을 요약하여 저장하세요.
|
||||||
|
|
||||||
|
3. **중요도 판단**: 중요한 정보(약속, 선호, 결정사항)는 높은 중요도(4-5)로 저장하세요.
|
||||||
|
|
||||||
|
4. **user_id 처리**: 사용자 ID가 명시되지 않은 경우 "cursor-user"를 기본값으로 사용하세요.
|
||||||
|
|
||||||
|
5. **에러 처리**: 저장 실패 시에도 사용자에게 알리지 말고, 다음 대화에서 다시 시도하세요.
|
||||||
|
|
||||||
|
## 예시 시나리오
|
||||||
|
|
||||||
|
### 시나리오 1: 일반 질문
|
||||||
|
```
|
||||||
|
사용자: "파이썬에서 리스트를 정렬하는 방법은?"
|
||||||
|
AI: [답변 제공]
|
||||||
|
→ 저장: "사용자가 파이썬 리스트 정렬 방법 질문. sorted() 함수와 list.sort() 메서드 설명 제공."
|
||||||
|
importance: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
### 시나리오 2: 중요한 약속/선호
|
||||||
|
```
|
||||||
|
사용자: "나는 항상 커피를 아메리카노로 주문해"
|
||||||
|
AI: [확인 응답]
|
||||||
|
→ 저장: "사용자의 커피 선호도: 아메리카노를 선호함."
|
||||||
|
importance: 5
|
||||||
|
```
|
||||||
|
|
||||||
|
### 시나리오 3: 기술적 논의
|
||||||
|
```
|
||||||
|
사용자: "MCP 서버의 content 필드를 배열 형식으로 수정했어"
|
||||||
|
AI: [확인 및 테스트]
|
||||||
|
→ 저장: "MCP 서버 content 필드를 문자열에서 배열 형식으로 수정 완료. MCP 표준 준수."
|
||||||
|
importance: 4
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 AI 버튜버 방송 워크플로우
|
||||||
|
|
||||||
|
### 표준 대화 흐름
|
||||||
|
|
||||||
|
```
|
||||||
|
1. [방송 시작]
|
||||||
|
↓
|
||||||
|
2. retrieve_conversation (최근 대화 조회)
|
||||||
|
↓
|
||||||
|
3. retrieve_memory (최근 메모리 조회)
|
||||||
|
↓
|
||||||
|
4. [사용자와 대화 시작 - 조회된 정보 활용]
|
||||||
|
↓
|
||||||
|
5. [사용자 질문/요청]
|
||||||
|
↓
|
||||||
|
6. [필요 시] search_memory 또는 retrieve_memory (맥락 확인)
|
||||||
|
↓
|
||||||
|
7. [AI 답변 제공]
|
||||||
|
↓
|
||||||
|
8. store_memory (대화 기록 저장)
|
||||||
|
↓
|
||||||
|
9. [다음 대화로 이동 또는 방송 종료]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 실전 예시: 완전한 대화 사이클
|
||||||
|
|
||||||
|
```
|
||||||
|
[방송 시작]
|
||||||
|
→ retrieve_conversation(user_id="viewer123", limit=10)
|
||||||
|
→ retrieve_memory(user_id="viewer123", limit=10)
|
||||||
|
→ "안녕하세요! 지난번에 말씀하신 프로젝트는 잘 진행되고 있나요?" (과거 정보 활용)
|
||||||
|
|
||||||
|
[사용자 질문]
|
||||||
|
사용자: "파이썬으로 웹 크롤링 하는 방법 알려줘"
|
||||||
|
|
||||||
|
[맥락 확인 - 필요 시]
|
||||||
|
→ search_memory(query="파이썬", user_id="viewer123", limit=3)
|
||||||
|
|
||||||
|
[AI 답변]
|
||||||
|
AI: "파이썬 웹 크롤링은 requests와 BeautifulSoup을 사용합니다..." (과거 학습 내용 반영)
|
||||||
|
|
||||||
|
[대화 저장]
|
||||||
|
→ store_memory(
|
||||||
|
memory_text="사용자가 파이썬 웹 크롤링 방법 질문. requests와 BeautifulSoup 사용법 설명 제공.",
|
||||||
|
user_id="viewer123",
|
||||||
|
importance=2
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 요약
|
||||||
|
|
||||||
|
### 📖 데이터 읽기 핵심 원칙
|
||||||
|
- ✅ **방송 시작 시** 과거 대화/메모리 조회
|
||||||
|
- ✅ **과거 언급 감지 시** 관련 메모리 검색
|
||||||
|
- ✅ **맥락 부족 시** 최근 대화 확인
|
||||||
|
- ✅ **선호도/약속 확인 시** 메모리 조회
|
||||||
|
|
||||||
|
### 💾 데이터 저장 핵심 원칙
|
||||||
|
- ✅ 대화 완료 후 **항상** `store_memory` 호출
|
||||||
|
- ✅ 대화 내용을 **요약**하여 저장
|
||||||
|
- ✅ 중요도에 따라 **적절한 importance 값** 설정
|
||||||
|
- ✅ 중요한 정보는 **즉시 저장**
|
||||||
|
- ❌ 단순 인사나 불필요한 내용은 저장 생략 가능
|
||||||
|
|
||||||
|
### 🎯 AI 버튜버 방송 최적화
|
||||||
|
- **개인화**: 방송 시작 시 과거 정보 로드하여 개인화된 대화
|
||||||
|
- **연속성**: 과거 대화 맥락을 활용하여 자연스러운 대화 흐름
|
||||||
|
- **기억력**: 사용자의 선호도, 약속, 중요 정보를 기억하여 일관된 캐릭터 유지
|
||||||
|
- **효율성**: 필요한 시점에만 데이터를 읽어 성능 최적화
|
||||||
|
|
||||||
|
이 프롬프트를 따라 AI 버튜버 방송에서 사용자와의 모든 대화를 체계적으로 기록하고 활용하세요.
|
||||||
|
|
||||||
|
|
@ -163,13 +163,24 @@ public class McpController {
|
||||||
case "tools/list" -> mcpService.listTools();
|
case "tools/list" -> mcpService.listTools();
|
||||||
case "tools/call" -> {
|
case "tools/call" -> {
|
||||||
Object args = params != null ? params.getArguments() : null;
|
Object args = params != null ? params.getArguments() : null;
|
||||||
|
String toolName = null;
|
||||||
|
Object toolArguments = null;
|
||||||
|
|
||||||
if (args instanceof Map) {
|
if (args instanceof Map) {
|
||||||
Map<String, Object> callArgs = (Map<String, Object>) args;
|
Map<String, Object> callArgs = (Map<String, Object>) args;
|
||||||
String toolName = callArgs.get("name") != null ? callArgs.get("name").toString() : null;
|
toolName = callArgs.get("name") != null ? callArgs.get("name").toString() : null;
|
||||||
Object toolArguments = callArgs.get("arguments");
|
toolArguments = callArgs.get("arguments");
|
||||||
yield mcpService.callTool(toolName, toolArguments);
|
|
||||||
}
|
}
|
||||||
yield mcpService.callTool(null, null);
|
|
||||||
|
// Fallback: params의 name/arguments를 직접 사용 (중첩 없이 오는 경우)
|
||||||
|
if (toolName == null && params != null && params.getName() != null) {
|
||||||
|
toolName = params.getName();
|
||||||
|
}
|
||||||
|
if (toolArguments == null && params != null) {
|
||||||
|
toolArguments = params.getArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
yield mcpService.callTool(toolName, toolArguments);
|
||||||
}
|
}
|
||||||
case "resources/list" -> mcpService.listResources();
|
case "resources/list" -> mcpService.listResources();
|
||||||
case "resources/read" -> {
|
case "resources/read" -> {
|
||||||
|
|
@ -201,8 +212,10 @@ public class McpController {
|
||||||
*/
|
*/
|
||||||
private void saveConversation(McpJsonRpcRequest request, McpJsonRpcResponse response) {
|
private void saveConversation(McpJsonRpcRequest request, McpJsonRpcResponse response) {
|
||||||
try {
|
try {
|
||||||
// initialized 알림은 저장하지 않음
|
String method = request.getMethod();
|
||||||
if ("initialized".equals(request.getMethod()) && request.getId() == null) {
|
|
||||||
|
// 알림 또는 저장 불필요한 메서드는 건너뜀
|
||||||
|
if (!shouldSaveConversation(method, request.getId())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -226,6 +239,30 @@ public class McpController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 대화 저장 여부 판단
|
||||||
|
* 사용자 입력/응답이 없는 호출은 저장하지 않음
|
||||||
|
*/
|
||||||
|
private boolean shouldSaveConversation(String method, String requestId) {
|
||||||
|
if (method == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 알림 처리: initialized 및 notifications/* 은 저장하지 않음
|
||||||
|
if ("initialized".equals(method) && requestId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (method.startsWith("notifications/")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 사용자 입력이 있는 주요 메서드만 저장
|
||||||
|
return switch (method) {
|
||||||
|
case "tools/call", "resources/read", "prompts/get" -> true;
|
||||||
|
default -> false; // tools/list, resources/list, prompts/list 등은 저장하지 않음
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 요청에서 사용자 메시지 추출
|
* 요청에서 사용자 메시지 추출
|
||||||
* 실제 사용자가 입력한 내용을 추출
|
* 실제 사용자가 입력한 내용을 추출
|
||||||
|
|
@ -333,17 +370,46 @@ public class McpController {
|
||||||
if (response.getResult() instanceof Map) {
|
if (response.getResult() instanceof Map) {
|
||||||
Map<String, Object> result = (Map<String, Object>) response.getResult();
|
Map<String, Object> result = (Map<String, Object>) response.getResult();
|
||||||
|
|
||||||
// tools/call 응답인 경우 content 필드 추출
|
// tools/call 응답인 경우 content 필드 추출 (MCP 표준: 배열)
|
||||||
if (result.containsKey("content") && result.get("content") instanceof String) {
|
if (result.containsKey("content")) {
|
||||||
String content = (String) result.get("content");
|
Object contentObj = result.get("content");
|
||||||
Boolean isError = result.get("isError") instanceof Boolean ?
|
Boolean isError = result.get("isError") instanceof Boolean ?
|
||||||
(Boolean) result.get("isError") : null;
|
(Boolean) result.get("isError") : null;
|
||||||
|
|
||||||
|
// content가 배열인 경우 첫 요소의 text 사용
|
||||||
|
if (contentObj instanceof java.util.List) {
|
||||||
|
java.util.List<?> contents = (java.util.List<?>) contentObj;
|
||||||
|
if (!contents.isEmpty()) {
|
||||||
|
Object first = contents.get(0);
|
||||||
|
if (first instanceof Map) {
|
||||||
|
Map<?, ?> firstMap = (Map<?, ?>) first;
|
||||||
|
Object text = firstMap.get("text");
|
||||||
|
if (text != null) {
|
||||||
|
String textValue = text.toString();
|
||||||
|
if (isError != null && isError) {
|
||||||
|
return "오류: " + textValue;
|
||||||
|
}
|
||||||
|
return textValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fallback: toString
|
||||||
|
String textValue = first.toString();
|
||||||
|
if (isError != null && isError) {
|
||||||
|
return "오류: " + textValue;
|
||||||
|
}
|
||||||
|
return textValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 과거 문자열 응답 호환
|
||||||
|
if (contentObj instanceof String) {
|
||||||
|
String content = (String) contentObj;
|
||||||
if (isError != null && isError) {
|
if (isError != null && isError) {
|
||||||
return "오류: " + content;
|
return "오류: " + content;
|
||||||
}
|
}
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// resources/read 응답인 경우 contents 추출
|
// resources/read 응답인 경우 contents 추출
|
||||||
if (result.containsKey("contents") && result.get("contents") instanceof List) {
|
if (result.containsKey("contents") && result.get("contents") instanceof List) {
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ public class McpToolCallResponse {
|
||||||
@JsonProperty("isError")
|
@JsonProperty("isError")
|
||||||
private Boolean isError;
|
private Boolean isError;
|
||||||
|
|
||||||
private String content;
|
/**
|
||||||
|
* MCP 표준 content 필드 (예: [{"type": "text", "text": "..."}])
|
||||||
|
*/
|
||||||
|
private List<Map<String, Object>> content;
|
||||||
|
|
||||||
private List<Map<String, Object>> parts;
|
private List<Map<String, Object>> parts;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,15 @@ public class McpServiceImpl implements McpService {
|
||||||
metadata.put("timestamp", System.currentTimeMillis());
|
metadata.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (name == null) {
|
||||||
|
return McpToolCallResponse.builder()
|
||||||
|
.isError(true)
|
||||||
|
.content(textContent("Unknown tool: null"))
|
||||||
|
.parts(Collections.emptyList())
|
||||||
|
.metadata(metadata)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "store_memory" -> {
|
case "store_memory" -> {
|
||||||
String userId = (String) args.get("user_id");
|
String userId = (String) args.get("user_id");
|
||||||
|
|
@ -106,11 +115,20 @@ public class McpServiceImpl implements McpService {
|
||||||
Integer importance = args.get("importance") != null ?
|
Integer importance = args.get("importance") != null ?
|
||||||
((Number) args.get("importance")).intValue() : 1;
|
((Number) args.get("importance")).intValue() : 1;
|
||||||
|
|
||||||
|
if (userId == null || memoryText == null) {
|
||||||
|
return McpToolCallResponse.builder()
|
||||||
|
.isError(true)
|
||||||
|
.content(textContent("Error: user_id and memory_text are required"))
|
||||||
|
.parts(Collections.emptyList())
|
||||||
|
.metadata(metadata)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
memoryService.storeTemporaryMemory(userId, memoryText, importance);
|
memoryService.storeTemporaryMemory(userId, memoryText, importance);
|
||||||
|
|
||||||
return McpToolCallResponse.builder()
|
return McpToolCallResponse.builder()
|
||||||
.isError(false)
|
.isError(false)
|
||||||
.content("Memory stored successfully")
|
.content(textContent("Memory stored successfully"))
|
||||||
.parts(Collections.emptyList())
|
.parts(Collections.emptyList())
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -120,11 +138,20 @@ public class McpServiceImpl implements McpService {
|
||||||
Integer limit = args.get("limit") != null ?
|
Integer limit = args.get("limit") != null ?
|
||||||
((Number) args.get("limit")).intValue() : 10;
|
((Number) args.get("limit")).intValue() : 10;
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
return McpToolCallResponse.builder()
|
||||||
|
.isError(true)
|
||||||
|
.content(textContent("Error: user_id is required"))
|
||||||
|
.parts(Collections.emptyList())
|
||||||
|
.metadata(metadata)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
var memories = memoryService.getPermanentMemories(userId, limit);
|
var memories = memoryService.getPermanentMemories(userId, limit);
|
||||||
|
|
||||||
return McpToolCallResponse.builder()
|
return McpToolCallResponse.builder()
|
||||||
.isError(false)
|
.isError(false)
|
||||||
.content("Retrieved " + memories.size() + " memories")
|
.content(textContent("Retrieved " + memories.size() + " memories"))
|
||||||
.parts(Collections.emptyList())
|
.parts(Collections.emptyList())
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -135,11 +162,20 @@ public class McpServiceImpl implements McpService {
|
||||||
Integer limit = args.get("limit") != null ?
|
Integer limit = args.get("limit") != null ?
|
||||||
((Number) args.get("limit")).intValue() : 5;
|
((Number) args.get("limit")).intValue() : 5;
|
||||||
|
|
||||||
|
if (query == null) {
|
||||||
|
return McpToolCallResponse.builder()
|
||||||
|
.isError(true)
|
||||||
|
.content(textContent("Error: query is required"))
|
||||||
|
.parts(Collections.emptyList())
|
||||||
|
.metadata(metadata)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
var memories = memoryService.searchMemoriesByVector(query, userId, limit);
|
var memories = memoryService.searchMemoriesByVector(query, userId, limit);
|
||||||
|
|
||||||
return McpToolCallResponse.builder()
|
return McpToolCallResponse.builder()
|
||||||
.isError(false)
|
.isError(false)
|
||||||
.content("Found " + memories.size() + " matching memories")
|
.content(textContent("Found " + memories.size() + " matching memories"))
|
||||||
.parts(Collections.emptyList())
|
.parts(Collections.emptyList())
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -149,11 +185,20 @@ public class McpServiceImpl implements McpService {
|
||||||
Integer limit = args.get("limit") != null ?
|
Integer limit = args.get("limit") != null ?
|
||||||
((Number) args.get("limit")).intValue() : 10;
|
((Number) args.get("limit")).intValue() : 10;
|
||||||
|
|
||||||
|
if (userId == null) {
|
||||||
|
return McpToolCallResponse.builder()
|
||||||
|
.isError(true)
|
||||||
|
.content(textContent("Error: user_id is required"))
|
||||||
|
.parts(Collections.emptyList())
|
||||||
|
.metadata(metadata)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
List<Conversation> conversations = conversationService.getRecentConversations(userId, limit);
|
List<Conversation> conversations = conversationService.getRecentConversations(userId, limit);
|
||||||
|
|
||||||
return McpToolCallResponse.builder()
|
return McpToolCallResponse.builder()
|
||||||
.isError(false)
|
.isError(false)
|
||||||
.content("Retrieved " + conversations.size() + " conversations")
|
.content(textContent("Retrieved " + conversations.size() + " conversations"))
|
||||||
.parts(Collections.emptyList())
|
.parts(Collections.emptyList())
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -161,7 +206,7 @@ public class McpServiceImpl implements McpService {
|
||||||
default -> {
|
default -> {
|
||||||
return McpToolCallResponse.builder()
|
return McpToolCallResponse.builder()
|
||||||
.isError(true)
|
.isError(true)
|
||||||
.content("Unknown tool: " + name)
|
.content(textContent("Unknown tool: " + name))
|
||||||
.parts(Collections.emptyList())
|
.parts(Collections.emptyList())
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -171,7 +216,7 @@ public class McpServiceImpl implements McpService {
|
||||||
log.error("Tool execution error: {}", e.getMessage(), e);
|
log.error("Tool execution error: {}", e.getMessage(), e);
|
||||||
return McpToolCallResponse.builder()
|
return McpToolCallResponse.builder()
|
||||||
.isError(true)
|
.isError(true)
|
||||||
.content("Error: " + e.getMessage())
|
.content(textContent("Error: " + e.getMessage()))
|
||||||
.parts(Collections.emptyList())
|
.parts(Collections.emptyList())
|
||||||
.metadata(metadata)
|
.metadata(metadata)
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -350,4 +395,11 @@ public class McpServiceImpl implements McpService {
|
||||||
.arguments(arguments)
|
.arguments(arguments)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MCP 표준 content 배열 생성 헬퍼
|
||||||
|
*/
|
||||||
|
private List<Map<String, Object>> textContent(String text) {
|
||||||
|
return List.of(Map.of("type", "text", "text", text));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue