MCP Base코드 구현
This commit is contained in:
parent
b2239cde5f
commit
29f381ae81
|
|
@ -8,6 +8,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,6 +24,33 @@ public class McpController {
|
||||||
|
|
||||||
private final McpService mcpService;
|
private final McpService mcpService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 헬스 체크 엔드포인트 (GET 요청 처리)
|
||||||
|
* Cursor나 다른 클라이언트가 서버 상태를 확인하기 위해 사용
|
||||||
|
*/
|
||||||
|
@GetMapping(produces = {"application/json", "text/plain", "*/*"})
|
||||||
|
public ResponseEntity<Map<String, Object>> healthCheck() {
|
||||||
|
log.info("헬스 체크 요청 수신");
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("status", "ok");
|
||||||
|
response.put("service", "Dewey MCP Server");
|
||||||
|
response.put("version", "0.0.1-SNAPSHOT");
|
||||||
|
response.put("protocol", "JSON-RPC 2.0");
|
||||||
|
response.put("endpoint", "/mcp");
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OPTIONS 요청 처리 (CORS preflight)
|
||||||
|
*/
|
||||||
|
@RequestMapping(method = RequestMethod.OPTIONS)
|
||||||
|
public ResponseEntity<Void> options() {
|
||||||
|
log.info("OPTIONS 요청 수신 (CORS preflight)");
|
||||||
|
return ResponseEntity.ok().build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MCP JSON-RPC 2.0 엔드포인트
|
* MCP JSON-RPC 2.0 엔드포인트
|
||||||
* 모든 MCP 요청을 처리하는 통합 엔드포인트
|
* 모든 MCP 요청을 처리하는 통합 엔드포인트
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,10 @@ import com.pandol365.dewey.api.dto.response.McpJsonRpcResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||||
import org.springframework.validation.FieldError;
|
import org.springframework.validation.FieldError;
|
||||||
|
import org.springframework.web.HttpMediaTypeNotAcceptableException;
|
||||||
|
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
@ -19,6 +22,72 @@ import java.util.Map;
|
||||||
@RestControllerAdvice
|
@RestControllerAdvice
|
||||||
public class GlobalExceptionHandler {
|
public class GlobalExceptionHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP 메서드가 지원되지 않는 경우 처리
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||||
|
public ResponseEntity<Map<String, Object>> handleMethodNotSupported(
|
||||||
|
HttpRequestMethodNotSupportedException ex) {
|
||||||
|
log.warn("지원되지 않는 HTTP 메서드: {}", ex.getMethod());
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("error", "Method not allowed");
|
||||||
|
response.put("message", ex.getMessage());
|
||||||
|
if (ex.getSupportedMethods() != null) {
|
||||||
|
response.put("supportedMethods", ex.getSupportedMethods());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON 파싱 오류 처리
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||||
|
public ResponseEntity<McpJsonRpcResponse> handleJsonParseException(
|
||||||
|
HttpMessageNotReadableException ex) {
|
||||||
|
log.warn("JSON 파싱 오류: {}", ex.getMessage());
|
||||||
|
|
||||||
|
// 원인 예외 메시지 추출
|
||||||
|
String errorMessage = "Invalid JSON format";
|
||||||
|
if (ex.getCause() != null) {
|
||||||
|
errorMessage = ex.getCause().getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
McpJsonRpcResponse.McpError error = McpJsonRpcResponse.McpError.builder()
|
||||||
|
.code(-32700) // Parse error
|
||||||
|
.message(errorMessage)
|
||||||
|
.data(null)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
McpJsonRpcResponse response = McpJsonRpcResponse.builder()
|
||||||
|
.jsonrpc("2.0")
|
||||||
|
.error(error)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return ResponseEntity.badRequest().body(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 미디어 타입이 허용되지 않는 경우 처리
|
||||||
|
*/
|
||||||
|
@ExceptionHandler(HttpMediaTypeNotAcceptableException.class)
|
||||||
|
public ResponseEntity<Map<String, Object>> handleMediaTypeNotAcceptable(
|
||||||
|
HttpMediaTypeNotAcceptableException ex) {
|
||||||
|
log.warn("허용되지 않는 미디어 타입: {}", ex.getMessage());
|
||||||
|
|
||||||
|
Map<String, Object> response = new HashMap<>();
|
||||||
|
response.put("status", "ok");
|
||||||
|
response.put("service", "Dewey MCP Server");
|
||||||
|
response.put("version", "0.0.1-SNAPSHOT");
|
||||||
|
response.put("protocol", "JSON-RPC 2.0");
|
||||||
|
response.put("endpoint", "/mcp");
|
||||||
|
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(response);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 유효성 검증 예외 처리
|
* 유효성 검증 예외 처리
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "1",
|
||||||
|
"method": "initialize",
|
||||||
|
"params": {
|
||||||
|
"protocolVersion": "2024-11-05",
|
||||||
|
"capabilities": {},
|
||||||
|
"clientInfo": {
|
||||||
|
"name": "test-client",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "3",
|
||||||
|
"method": "tools/call",
|
||||||
|
"params": {
|
||||||
|
"arguments": {
|
||||||
|
"name": "store_memory",
|
||||||
|
"arguments": {
|
||||||
|
"user_id": "test_user",
|
||||||
|
"memory_text": "테스트 메모리입니다",
|
||||||
|
"importance": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"id": "2",
|
||||||
|
"method": "tools/list"
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue