add game query plan MCP tool

This commit is contained in:
devmrko
2026-07-27 14:23:17 +09:00
parent beaef2d171
commit 10581dc605

View File

@@ -16,6 +16,7 @@ public class McpSseService {
private static final String SELECT_AI_TOOL_PATH = "/mcp (tools/call)"; private static final String SELECT_AI_TOOL_PATH = "/mcp (tools/call)";
private static final String GAME_CATALOG_TOOL = "oracle.select_ai.game_catalog_resolve"; private static final String GAME_CATALOG_TOOL = "oracle.select_ai.game_catalog_resolve";
private static final String GAME_QUERY_PLAN_TOOL = "oracle.select_ai.game_query_plan";
private final SelectAiService selectAiService; private final SelectAiService selectAiService;
private final ObjectMapper objectMapper; private final ObjectMapper objectMapper;
@@ -81,7 +82,7 @@ public class McpSseService {
public List<McpToolView> registeredTools() { public List<McpToolView> registeredTools() {
return List.of( return List.of(
selectAiQueryView(), selectAiShowpromptView(), qaVectorSearchView(), selectAiQueryView(), selectAiShowpromptView(), qaVectorSearchView(),
qaVectorStoreView(), fewShotNl2SqlView(), gameScopeView(), gameCatalogView()); qaVectorStoreView(), fewShotNl2SqlView(), gameScopeView(), gameCatalogView(), gameQueryPlanView());
} }
private ObjectNode initializeResult(String contextPath) { private ObjectNode initializeResult(String contextPath) {
@@ -107,6 +108,7 @@ public class McpSseService {
tools.add(toolDefinition(fewShotNl2SqlView())); tools.add(toolDefinition(fewShotNl2SqlView()));
tools.add(toolDefinition(gameScopeView())); tools.add(toolDefinition(gameScopeView()));
tools.add(toolDefinition(gameCatalogView())); tools.add(toolDefinition(gameCatalogView()));
tools.add(toolDefinition(gameQueryPlanView()));
result.set("tools", tools); result.set("tools", tools);
return result; return result;
} }
@@ -122,7 +124,7 @@ public class McpSseService {
ArrayNode required = objectMapper.createArrayNode(); ArrayNode required = objectMapper.createArrayNode();
if (qaVectorSearchToolName().equals(toolView.name()) || gameScopeToolName().equals(toolView.name()) if (qaVectorSearchToolName().equals(toolView.name()) || gameScopeToolName().equals(toolView.name())
|| GAME_CATALOG_TOOL.equals(toolView.name())) { || GAME_CATALOG_TOOL.equals(toolView.name()) || GAME_QUERY_PLAN_TOOL.equals(toolView.name())) {
addStringProperty(properties, "question", "few-shot 예제 SQL을 찾을 현재 질문입니다.", 4000); addStringProperty(properties, "question", "few-shot 예제 SQL을 찾을 현재 질문입니다.", 4000);
ObjectNode topK = properties.putObject("topK"); ObjectNode topK = properties.putObject("topK");
topK.put("type", "integer"); topK.put("type", "integer");
@@ -168,8 +170,9 @@ public class McpSseService {
boolean fewShotNl2SqlTool = fewShotNl2SqlToolName().equals(calledToolName); boolean fewShotNl2SqlTool = fewShotNl2SqlToolName().equals(calledToolName);
boolean gameScopeTool = gameScopeToolName().equals(calledToolName); boolean gameScopeTool = gameScopeToolName().equals(calledToolName);
boolean gameCatalogTool = GAME_CATALOG_TOOL.equals(calledToolName); boolean gameCatalogTool = GAME_CATALOG_TOOL.equals(calledToolName);
boolean gameQueryPlanTool = GAME_QUERY_PLAN_TOOL.equals(calledToolName);
if (!queryTool && !showpromptTool && !qaVectorSearchTool && !qaVectorStoreTool && !fewShotNl2SqlTool if (!queryTool && !showpromptTool && !qaVectorSearchTool && !qaVectorStoreTool && !fewShotNl2SqlTool
&& !gameScopeTool && !gameCatalogTool) { && !gameScopeTool && !gameCatalogTool && !gameQueryPlanTool) {
throw new AppException("등록되지 않은 MCP tool입니다: " + calledToolName); throw new AppException("등록되지 않은 MCP tool입니다: " + calledToolName);
} }
@@ -196,6 +199,9 @@ public class McpSseService {
} else if (gameCatalogTool) { } else if (gameCatalogTool) {
response = gameCatalogVectorResponse(gameCatalogVectorService.resolve( response = gameCatalogVectorResponse(gameCatalogVectorService.resolve(
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5))); token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
} else if (gameQueryPlanTool) {
response = gameQueryPlanResponse(gameCatalogVectorService.resolve(
token, arguments.path("question").asText(""), arguments.path("topK").asInt(5)));
} else { } else {
String prompt = arguments.path("prompt").asText(""); String prompt = arguments.path("prompt").asText("");
response = queryTool response = queryTool
@@ -362,6 +368,24 @@ public class McpSseService {
-1L, "게임 카탈로그 범위 확인", SELECT_AI_TOOL_PATH); -1L, "게임 카탈로그 범위 확인", SELECT_AI_TOOL_PATH);
} }
private McpToolView gameQueryPlanView() {
return new McpToolView(GAME_QUERY_PLAN_TOOL,
"질문에서 게임 범위와 실행 모드를 판정합니다. SQL은 실행하지 않습니다.",
-1L, "게임 질의 계획", SELECT_AI_TOOL_PATH);
}
private ObjectNode gameQueryPlanResponse(GameCatalogVectorService.Resolution resolution) {
ObjectNode response = gameCatalogVectorResponse(resolution);
String mode = switch (resolution.scopeType()) {
case "SINGLE_GAME" -> "SINGLE";
case "MULTI_GAME" -> "FAN_OUT";
case "ALL_GAMES" -> "GROUPED";
default -> "BLOCKED";
};
response.put("executionMode", mode);
return response;
}
private String selectAiProfile() { private String selectAiProfile() {
BackofficeProperties.SelectAi selectAi = properties == null ? null : properties.selectAi(); BackofficeProperties.SelectAi selectAi = properties == null ? null : properties.selectAi();
if (selectAi == null || selectAi.profile() == null || selectAi.profile().isBlank()) { if (selectAi == null || selectAi.profile() == null || selectAi.profile().isBlank()) {