refs #743: validate advertised ADB MCP tools at startup
This commit is contained in:
@@ -73,7 +73,8 @@ export BACKOFFICE_PRODUCT_NAME="Data & AI Backoffice"
|
|||||||
export BACKOFFICE_PRODUCT_TITLE="Data & AI Backoffice"
|
export BACKOFFICE_PRODUCT_TITLE="Data & AI Backoffice"
|
||||||
export BACKOFFICE_PRODUCT_DATA_LABEL="업무 데이터"
|
export BACKOFFICE_PRODUCT_DATA_LABEL="업무 데이터"
|
||||||
|
|
||||||
# 단일 Select AI 도구 호환 설정. 여러 Agent Tool을 쓸 때는 BACKOFFICE_MCP_TOOLS가 우선합니다.
|
# 단일 Select AI 도구 호환 설정. 여러 Tool을 쓸 때는 BACKOFFICE_MCP_TOOLS가 우선합니다.
|
||||||
|
# AGENT_TOOL targetName은 서버 시작 시 USER_AI_AGENT_TOOLS의 ENABLED 상태를 검증합니다.
|
||||||
export BACKOFFICE_MCP_PUBLIC_URL="https://example.com/mcp"
|
export BACKOFFICE_MCP_PUBLIC_URL="https://example.com/mcp"
|
||||||
export BACKOFFICE_MCP_SERVER_NAME="data-ai-backoffice"
|
export BACKOFFICE_MCP_SERVER_NAME="data-ai-backoffice"
|
||||||
export BACKOFFICE_MCP_TOOL_NAME="oracle.select_ai.data_text2sql"
|
export BACKOFFICE_MCP_TOOL_NAME="oracle.select_ai.data_text2sql"
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ BACKOFFICE_PRODUCT_DATA_LABEL='HMM HR 데이터'
|
|||||||
|
|
||||||
BACKOFFICE_MCP_PUBLIC_URL='https://hmm-backoffice.cloud-handson.com/mcp'
|
BACKOFFICE_MCP_PUBLIC_URL='https://hmm-backoffice.cloud-handson.com/mcp'
|
||||||
BACKOFFICE_MCP_SERVER_NAME='hmm-hr-backoffice'
|
BACKOFFICE_MCP_SERVER_NAME='hmm-hr-backoffice'
|
||||||
BACKOFFICE_MCP_TOOLS='[{"name":"resolve_hr_term","label":"HMM HR 용어 표준화","description":"휴가·근태 표현을 HMM 표준 용어와 코드로 변환합니다. 모호한 표현은 데이터 조회 전에 이 도구를 사용합니다.","argumentName":"term","argumentDescription":"확인할 휴가·근태 용어, 동의어 또는 코드입니다.","executionType":"AGENT_TOOL","targetName":"HMM_HR_TERM_RESOLVER","targetParameterName":"P_TERM"},{"name":"search_hr_data","label":"HMM HR 데이터 조회","description":"조직, 직원, 휴가 잔여·신청, 근태 데이터를 읽기 전용 Select AI로 조회합니다.","argumentName":"query","argumentDescription":"조직, 직원, 휴가 또는 근태에 대한 완전한 자연어 질문입니다.","executionType":"AGENT_TOOL","targetName":"HMM_HR_NORMALIZED_DATA_SEARCH","targetParameterName":"P_QUERY"},{"name":"search_hr_policy","label":"HMM HR 규정 검색","description":"HR 규정 PDF의 문서 메타데이터, Abstract, 관련 청크를 계층형 벡터 검색으로 조회합니다.","argumentName":"query","argumentDescription":"HR 규정에 대한 완전한 자연어 질문입니다.","executionType":"AGENT_TOOL","targetName":"HMM_HR_POLICY_SEARCH","targetParameterName":"P_QUERY"}]'
|
# AGENT_TOOL targetName must exist with STATUS=ENABLED in USER_AI_AGENT_TOOLS.
|
||||||
|
# The application fails startup before advertising an invalid configured contract.
|
||||||
|
BACKOFFICE_MCP_TOOLS='[{"name":"resolve_hr_term","label":"HMM HR 용어 표준화","description":"휴가·근태 표현을 HMM 표준 용어와 코드로 변환합니다. 모호한 표현은 데이터 조회 전에 이 도구를 사용합니다.","argumentName":"term","argumentDescription":"확인할 휴가·근태 용어, 동의어 또는 코드입니다.","executionType":"AGENT_TOOL","targetName":"HMM_HR_TERM_RESOLVER","targetParameterName":"P_TERM"},{"name":"search_hr_data","label":"HMM HR 데이터 조회","description":"조직, 직원, 휴가 잔여·신청, 근태 데이터를 읽기 전용 Select AI로 조회합니다.","argumentName":"query","argumentDescription":"조직, 직원, 휴가 또는 근태에 대한 완전한 자연어 질문입니다.","executionType":"SELECT_AI"},{"name":"search_hr_policy","label":"HMM HR 규정 검색","description":"HR 규정 PDF의 문서 메타데이터, Abstract, 관련 청크를 계층형 벡터 검색으로 조회합니다.","argumentName":"query","argumentDescription":"HR 규정에 대한 완전한 자연어 질문입니다.","executionType":"AGENT_TOOL","targetName":"HMM_HR_POLICY_SEARCH","targetParameterName":"P_QUERY"}]'
|
||||||
|
|
||||||
BACKOFFICE_MASKING_POLICIES='[{"objectName":"HMM_HR_EMPLOYEES","policyName":"HMM_EMPLOYEE_PII_REDACT"},{"objectName":"HMM_LEAVE_BALANCES","policyName":"HMM_LEAVE_BALANCE_REDACT"},{"objectName":"HMM_LEAVE_REQUESTS","policyName":"HMM_LEAVE_REQUEST_REDACT"},{"objectName":"HMM_ATTENDANCE_DAILY","policyName":"HMM_ATTENDANCE_REDACT"}]'
|
BACKOFFICE_MASKING_POLICIES='[{"objectName":"HMM_HR_EMPLOYEES","policyName":"HMM_EMPLOYEE_PII_REDACT"},{"objectName":"HMM_LEAVE_BALANCES","policyName":"HMM_LEAVE_BALANCE_REDACT"},{"objectName":"HMM_LEAVE_REQUESTS","policyName":"HMM_LEAVE_REQUEST_REDACT"},{"objectName":"HMM_ATTENDANCE_DAILY","policyName":"HMM_ATTENDANCE_REDACT"}]'
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# #743 MCP 광고 Agent Tool 시작 검증
|
||||||
|
|
||||||
|
## 문제
|
||||||
|
|
||||||
|
백오피스 MCP의 `tools/list`는 `BACKOFFICE_MCP_TOOLS`에 선언된 공개 계약을 광고한다.
|
||||||
|
`AGENT_TOOL`은 `targetName`을 `DBMS_CLOUD_AI_AGENT.RUN_TOOL`에 전달하지만, 현재는 서버
|
||||||
|
시작 시 해당 Tool이 ADB에 실제로 등록되어 있는지 확인하지 않는다. 따라서 Discovery는
|
||||||
|
성공하고 첫 `tools/call`에서만 실패할 수 있다.
|
||||||
|
|
||||||
|
## 목표
|
||||||
|
|
||||||
|
- 공개 Tool 이름과 입력 스키마는 배포 설정에서 안정적으로 관리한다.
|
||||||
|
- `AGENT_TOOL`의 내부 `targetName`은 현재 JDBC 실행 사용자의
|
||||||
|
`USER_AI_AGENT_TOOLS`에서 존재하고 `ENABLED` 상태여야 한다.
|
||||||
|
- 누락·비활성·메타데이터 조회 실패는 서버 시작을 중단한다.
|
||||||
|
- Java 자체 구현인 `SELECT_AI`는 ADB Agent Tool 검증 대상에서 제외한다.
|
||||||
|
|
||||||
|
## 처리 흐름
|
||||||
|
|
||||||
|
```text
|
||||||
|
Spring 설정 로드
|
||||||
|
→ EnvironmentMcpToolCatalog가 BACKOFFICE_MCP_TOOLS 검증
|
||||||
|
→ SmartInitializingSingleton이 AGENT_TOOL targetName 수집
|
||||||
|
→ USER_AI_AGENT_TOOLS 조회
|
||||||
|
→ 모두 ENABLED
|
||||||
|
├─ 예: MCP endpoint 기동 완료, tools/list 광고
|
||||||
|
└─ 아니오: 기동 실패, 외부에 불완전한 Tool 계약을 광고하지 않음
|
||||||
|
```
|
||||||
|
|
||||||
|
Discovery 요청 때마다 DB를 조회하지 않는다. ADB Agent Tool은 운영 설정이므로 시작 시 한 번
|
||||||
|
검증하고, 설정이나 DB Tool을 바꾼 뒤에는 애플리케이션을 재기동해 계약을 다시 확정한다.
|
||||||
|
|
||||||
|
## 실패 정책
|
||||||
|
|
||||||
|
누락된 Tool을 `tools/list`에서 자동 제외하지 않는다. 호출 가능한 Tool 집합이 환경에 따라
|
||||||
|
조용히 축소되면 Agent instruction과 실제 도구 목록이 어긋나기 때문이다. 설정에 선언된
|
||||||
|
`AGENT_TOOL`이 하나라도 누락되거나 `ENABLED`가 아니면 fail-closed로 기동을 실패시킨다.
|
||||||
|
|
||||||
|
오류에는 설정의 내부 Tool 이름과 상태만 포함한다. Bearer Token, Tool 입력, DB 접속정보는
|
||||||
|
로그에 기록하지 않는다.
|
||||||
|
|
||||||
|
## 구현 경계
|
||||||
|
|
||||||
|
- `EnvironmentMcpToolCatalog`: 공개 이름, 인자, 실행 유형, 내부 target 형식 검증
|
||||||
|
- `McpAgentToolStartupValidator`: DB 등록·상태 검증
|
||||||
|
- `McpSseService`: 검증 완료된 catalog를 `tools/list`로 광고하고 `tools/call`로 실행
|
||||||
|
- `JdbcHmmAiAgentToolRunner`: 검증된 `targetName`을 동일 JDBC 세션에서 실행
|
||||||
|
|
||||||
|
`USER_AI_AGENT_TOOLS`는 `RUN_TOOL`을 실행하는 기본 datasource 사용자 기준 View다. 다른
|
||||||
|
스키마의 Tool을 임의로 검색하거나 `ALL_*` 권한을 요구하지 않는다.
|
||||||
|
|
||||||
|
## 검증 기준
|
||||||
|
|
||||||
|
1. `AGENT_TOOL`이 모두 `ENABLED`면 검증이 통과한다.
|
||||||
|
2. Tool이 누락되면 누락된 이름을 포함해 실패한다.
|
||||||
|
3. Tool이 `DISABLED`면 이름과 상태를 포함해 실패한다.
|
||||||
|
4. 같은 ADB Tool을 여러 공개 Tool이 참조해도 한 번만 검증한다.
|
||||||
|
5. `SELECT_AI`만 구성되면 `USER_AI_AGENT_TOOLS`를 조회하지 않는다.
|
||||||
|
6. 기존 Maven 전체 테스트와 MCP discovery/call 테스트가 통과한다.
|
||||||
|
|
||||||
|
## 롤백
|
||||||
|
|
||||||
|
검증 컴포넌트와 테스트를 제거하면 기존 설정 기반 광고 방식으로 돌아간다. DB Tool,
|
||||||
|
프로필, VPD 정책과 운영 데이터는 이 변경에서 수정하지 않는다.
|
||||||
@@ -33,6 +33,12 @@ HMM 포털 로그인과 MCP 호출은 서로 다른 인증 수단을 사용한
|
|||||||
| `resolve_hr_term` | `term` | 휴가·근태 표현을 표준 용어와 코드로 변환 |
|
| `resolve_hr_term` | `term` | 휴가·근태 표현을 표준 용어와 코드로 변환 |
|
||||||
| `search_hr_policy` | `query` | HR 규정 PDF 지식 검색 |
|
| `search_hr_policy` | `query` | HR 규정 PDF 지식 검색 |
|
||||||
|
|
||||||
|
`tools/list`는 `BACKOFFICE_MCP_TOOLS`의 공개 이름·설명·입력 스키마를 광고한다.
|
||||||
|
`executionType=AGENT_TOOL`인 항목은 애플리케이션 시작 시 기본 datasource 사용자의
|
||||||
|
`USER_AI_AGENT_TOOLS`에서 `targetName`이 실제로 존재하고 `STATUS=ENABLED`인지 검증한다.
|
||||||
|
하나라도 누락되거나 비활성이면 서버는 기동을 실패하며 불완전한 Tool 목록을 광고하지 않는다.
|
||||||
|
`search_hr_data`처럼 `executionType=SELECT_AI`인 Java 자체 구현 Tool은 이 검증 대상이 아니다.
|
||||||
|
|
||||||
## 포털의 현재 공용 토큰 조합
|
## 포털의 현재 공용 토큰 조합
|
||||||
|
|
||||||
`config/vpd_token_presets.json`의 현재 조합은 다음과 같다.
|
`config/vpd_token_presets.json`의 현재 조합은 다음과 같다.
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package com.cloudhandson.vpdbackoffice.service;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/** Fails startup when an advertised ADB Agent Tool cannot actually be called. */
|
||||||
|
@Component
|
||||||
|
public class McpAgentToolStartupValidator implements SmartInitializingSingleton {
|
||||||
|
|
||||||
|
static final String TOOL_METADATA_SQL = """
|
||||||
|
SELECT TOOL_NAME, STATUS
|
||||||
|
FROM USER_AI_AGENT_TOOLS
|
||||||
|
""";
|
||||||
|
|
||||||
|
private static final Logger log =
|
||||||
|
LoggerFactory.getLogger(McpAgentToolStartupValidator.class);
|
||||||
|
|
||||||
|
private final McpToolCatalog toolCatalog;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
|
public McpAgentToolStartupValidator(
|
||||||
|
McpToolCatalog toolCatalog,
|
||||||
|
JdbcTemplate jdbcTemplate
|
||||||
|
) {
|
||||||
|
this.toolCatalog = toolCatalog;
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterSingletonsInstantiated() {
|
||||||
|
validateAdvertisedAgentTools();
|
||||||
|
}
|
||||||
|
|
||||||
|
void validateAdvertisedAgentTools() {
|
||||||
|
Set<String> requiredTargets = new TreeSet<>();
|
||||||
|
toolCatalog.tools().stream()
|
||||||
|
.filter(McpToolDefinition::agentTool)
|
||||||
|
.map(McpToolDefinition::targetName)
|
||||||
|
.map(value -> value.toUpperCase(Locale.ROOT))
|
||||||
|
.forEach(requiredTargets::add);
|
||||||
|
|
||||||
|
if (requiredTargets.isEmpty()) {
|
||||||
|
log.info("MCP startup validation skipped: no ADB Agent Tool is advertised");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, String> registered = loadRegisteredTools();
|
||||||
|
List<String> missing = new ArrayList<>();
|
||||||
|
List<String> notEnabled = new ArrayList<>();
|
||||||
|
for (String requiredTarget : requiredTargets) {
|
||||||
|
if (!registered.containsKey(requiredTarget)) {
|
||||||
|
missing.add(requiredTarget);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String status = registered.get(requiredTarget);
|
||||||
|
if (!"ENABLED".equals(status)) {
|
||||||
|
notEnabled.add(requiredTarget + "=" + status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missing.isEmpty() || !notEnabled.isEmpty()) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Advertised MCP Agent Tool validation failed: missing=" + missing
|
||||||
|
+ ", notEnabled=" + notEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Validated {} advertised ADB Agent Tool(s): {}",
|
||||||
|
requiredTargets.size(), requiredTargets);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, String> loadRegisteredTools() {
|
||||||
|
try {
|
||||||
|
Map<String, String> registered = new TreeMap<>();
|
||||||
|
for (Map<String, Object> row : jdbcTemplate.queryForList(TOOL_METADATA_SQL)) {
|
||||||
|
String toolName = normalized(row, "TOOL_NAME");
|
||||||
|
if (!toolName.isBlank()) {
|
||||||
|
String status = normalized(row, "STATUS");
|
||||||
|
registered.put(toolName, status.isBlank() ? "<NULL>" : status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return registered;
|
||||||
|
} catch (DataAccessException exception) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Unable to validate advertised MCP Agent Tools in USER_AI_AGENT_TOOLS",
|
||||||
|
exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalized(Map<String, Object> row, String column) {
|
||||||
|
Object value = row.get(column);
|
||||||
|
if (value == null) {
|
||||||
|
value = row.entrySet().stream()
|
||||||
|
.filter(entry -> column.equalsIgnoreCase(entry.getKey()))
|
||||||
|
.map(Map.Entry::getValue)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
return value == null ? "" : value.toString().trim().toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package com.cloudhandson.vpdbackoffice.service;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.dao.DataAccessResourceFailureException;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
|
class McpAgentToolStartupValidatorTest {
|
||||||
|
|
||||||
|
private final McpToolCatalog toolCatalog = mock(McpToolCatalog.class);
|
||||||
|
private final JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
|
||||||
|
private final McpAgentToolStartupValidator validator =
|
||||||
|
new McpAgentToolStartupValidator(toolCatalog, jdbcTemplate);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void acceptsEnabledAgentToolsAndChecksDuplicateTargetOnce() {
|
||||||
|
when(toolCatalog.tools()).thenReturn(List.of(
|
||||||
|
agentTool("resolve_hr_term", "HMM_HR_TERM_RESOLVER"),
|
||||||
|
agentTool("resolve_hr_alias", "HMM_HR_TERM_RESOLVER"),
|
||||||
|
agentTool("search_hr_policy", "HMM_HR_POLICY_SEARCH")
|
||||||
|
));
|
||||||
|
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
|
||||||
|
.thenReturn(List.of(
|
||||||
|
Map.of("TOOL_NAME", "HMM_HR_TERM_RESOLVER", "STATUS", "ENABLED"),
|
||||||
|
Map.of("TOOL_NAME", "HMM_HR_POLICY_SEARCH", "STATUS", "enabled")
|
||||||
|
));
|
||||||
|
|
||||||
|
validator.validateAdvertisedAgentTools();
|
||||||
|
|
||||||
|
verify(jdbcTemplate).queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsMissingAgentTool() {
|
||||||
|
when(toolCatalog.tools()).thenReturn(List.of(
|
||||||
|
agentTool("search_hr_policy", "HMM_HR_POLICY_SEARCH")
|
||||||
|
));
|
||||||
|
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
|
||||||
|
.thenReturn(List.of());
|
||||||
|
|
||||||
|
assertThatThrownBy(validator::validateAdvertisedAgentTools)
|
||||||
|
.isInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("missing=[HMM_HR_POLICY_SEARCH]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsDisabledAgentTool() {
|
||||||
|
when(toolCatalog.tools()).thenReturn(List.of(
|
||||||
|
agentTool("resolve_hr_term", "HMM_HR_TERM_RESOLVER")
|
||||||
|
));
|
||||||
|
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
|
||||||
|
.thenReturn(List.of(
|
||||||
|
Map.of("tool_name", "hmm_hr_term_resolver", "status", "DISABLED")
|
||||||
|
));
|
||||||
|
|
||||||
|
assertThatThrownBy(validator::validateAdvertisedAgentTools)
|
||||||
|
.isInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("notEnabled=[HMM_HR_TERM_RESOLVER=DISABLED]");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void skipsDatabaseLookupForApplicationNativeTools() {
|
||||||
|
when(toolCatalog.tools()).thenReturn(List.of(selectAiTool("search_hr_data")));
|
||||||
|
|
||||||
|
validator.validateAdvertisedAgentTools();
|
||||||
|
|
||||||
|
verify(jdbcTemplate, never())
|
||||||
|
.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsMetadataLookupFailure() {
|
||||||
|
when(toolCatalog.tools()).thenReturn(List.of(
|
||||||
|
agentTool("search_hr_policy", "HMM_HR_POLICY_SEARCH")
|
||||||
|
));
|
||||||
|
when(jdbcTemplate.queryForList(McpAgentToolStartupValidator.TOOL_METADATA_SQL))
|
||||||
|
.thenThrow(new DataAccessResourceFailureException("metadata unavailable"));
|
||||||
|
|
||||||
|
assertThatThrownBy(validator::validateAdvertisedAgentTools)
|
||||||
|
.isInstanceOf(IllegalStateException.class)
|
||||||
|
.hasMessageContaining("USER_AI_AGENT_TOOLS")
|
||||||
|
.hasCauseInstanceOf(DataAccessResourceFailureException.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private McpToolDefinition agentTool(String publicName, String targetName) {
|
||||||
|
return new McpToolDefinition(
|
||||||
|
publicName,
|
||||||
|
publicName,
|
||||||
|
"description",
|
||||||
|
"query",
|
||||||
|
"query description",
|
||||||
|
"AGENT_TOOL",
|
||||||
|
targetName,
|
||||||
|
"P_QUERY"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private McpToolDefinition selectAiTool(String publicName) {
|
||||||
|
return new McpToolDefinition(
|
||||||
|
publicName,
|
||||||
|
publicName,
|
||||||
|
"description",
|
||||||
|
"query",
|
||||||
|
"query description",
|
||||||
|
"SELECT_AI",
|
||||||
|
"",
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user