From b2239cde5f81a35db43ab84e1a560dfd006a7d39 Mon Sep 17 00:00:00 2001 From: mskim Date: Wed, 10 Dec 2025 11:09:17 +0900 Subject: [PATCH] =?UTF-8?q?MVC=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dewey/api/controller/McpController.java | 124 ++++++++++++++---- 1 file changed, 96 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/pandol365/dewey/api/controller/McpController.java b/src/main/java/com/pandol365/dewey/api/controller/McpController.java index 82cdcb4..9ec22ca 100644 --- a/src/main/java/com/pandol365/dewey/api/controller/McpController.java +++ b/src/main/java/com/pandol365/dewey/api/controller/McpController.java @@ -1,40 +1,108 @@ package com.pandol365.dewey.api.controller; -import com.pandol365.dewey.api.response.McpHealthResponse; -import com.pandol365.dewey.api.response.McpInfoResponse; +import com.pandol365.dewey.api.dto.request.McpJsonRpcRequest; +import com.pandol365.dewey.api.dto.response.McpJsonRpcResponse; +import com.pandol365.dewey.api.service.McpService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; -import java.time.LocalDateTime; +import java.util.Map; +/** + * MCP (Model Context Protocol) 서버 컨트롤러 + * MVC 패턴의 Controller 계층 + * JSON-RPC 2.0 기반 엔드포인트 제공 + */ +@Slf4j @RestController -@RequestMapping("/api/mcp") +@RequestMapping("/mcp") @RequiredArgsConstructor public class McpController { - - @GetMapping("/health") - public ResponseEntity health() { - return ResponseEntity.ok( - McpHealthResponse.builder() - .status("UP") - .timestamp(LocalDateTime.now()) - .build() - ); + + private final McpService mcpService; + + /** + * MCP JSON-RPC 2.0 엔드포인트 + * 모든 MCP 요청을 처리하는 통합 엔드포인트 + */ + @PostMapping + public ResponseEntity handleMcpRequest(@RequestBody McpJsonRpcRequest request) { + log.info("MCP 요청 수신: method={}, id={}", request.getMethod(), request.getId()); + + try { + Object result = processRequest(request); + + McpJsonRpcResponse response = McpJsonRpcResponse.builder() + .jsonrpc("2.0") + .id(request.getId()) + .result(result) + .build(); + + return ResponseEntity.ok(response); + + } catch (Exception e) { + log.error("MCP 요청 처리 중 오류 발생", e); + + McpJsonRpcResponse.McpError error = McpJsonRpcResponse.McpError.builder() + .code(-32603) // Internal error + .message(e.getMessage()) + .data(null) + .build(); + + McpJsonRpcResponse response = McpJsonRpcResponse.builder() + .jsonrpc("2.0") + .id(request.getId()) + .error(error) + .build(); + + return ResponseEntity.ok(response); + } } - - @GetMapping("/info") - public ResponseEntity info() { - return ResponseEntity.ok( - McpInfoResponse.builder() - .name("Dewey MCP API") - .version("0.0.1-SNAPSHOT") - .description("Model Context Protocol API for Dewey") - .build() - ); + + /** + * 요청 메서드에 따라 적절한 서비스 메서드 호출 + */ + @SuppressWarnings("unchecked") + private Object processRequest(McpJsonRpcRequest request) { + String method = request.getMethod(); + McpJsonRpcRequest.McpParams params = request.getParams(); + + return switch (method) { + case "initialize" -> mcpService.initialize(params); + case "tools/list" -> mcpService.listTools(); + case "tools/call" -> { + Object args = params != null ? params.getArguments() : null; + if (args instanceof Map) { + Map callArgs = (Map) args; + String toolName = callArgs.get("name") != null ? callArgs.get("name").toString() : null; + Object toolArguments = callArgs.get("arguments"); + yield mcpService.callTool(toolName, toolArguments); + } + yield mcpService.callTool(null, null); + } + case "resources/list" -> mcpService.listResources(); + case "resources/read" -> { + String uri = params != null ? params.getUri() : null; + if (uri == null && params != null && params.getArguments() instanceof Map) { + Map args = (Map) params.getArguments(); + uri = args.get("uri") != null ? args.get("uri").toString() : null; + } + yield mcpService.readResource(uri); + } + case "prompts/list" -> mcpService.listPrompts(); + case "prompts/get" -> { + Object args = params != null ? params.getArguments() : null; + if (args instanceof Map) { + Map promptArgs = (Map) args; + String promptName = promptArgs.get("name") != null ? promptArgs.get("name").toString() : null; + Object promptArguments = promptArgs.get("arguments"); + yield mcpService.getPrompt(promptName, promptArguments); + } + yield mcpService.getPrompt(null, null); + } + default -> throw new IllegalArgumentException("Unknown method: " + method); + }; } } - -