MCP Base코드 구현

This commit is contained in:
mskim 2025-12-10 11:47:51 +09:00
parent b2239cde5f
commit 29f381ae81
5 changed files with 133 additions and 0 deletions

View File

@ -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 요청을 처리하는 통합 엔드포인트

View File

@ -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);
}
/** /**
* 유효성 검증 예외 처리 * 유효성 검증 예외 처리
*/ */

14
test_mcp.json Normal file
View File

@ -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"
}
}
}

16
test_store_memory.json Normal file
View File

@ -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
}
}
}
}

6
test_tools_list.json Normal file
View File

@ -0,0 +1,6 @@
{
"jsonrpc": "2.0",
"id": "2",
"method": "tools/list"
}