Update project functionality and configuration
This commit is contained in:
18
potal/agent-test-backend/bin/main/application.yml
Normal file
18
potal/agent-test-backend/bin/main/application.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
server:
|
||||
port: ${AGENT_TEST_PORT:7070}
|
||||
|
||||
agent-test:
|
||||
mcp:
|
||||
endpoint-url: ${MCP_ENDPOINT_URL:http://localhost:8080/mcp}
|
||||
protocol-version: ${MCP_PROTOCOL_VERSION:2025-11-25}
|
||||
tool-server:
|
||||
manifest-url: ${TOOL_MANIFEST_URL:http://localhost:9092/tool-manifest}
|
||||
api-key: ${TOOL_SERVER_API_KEY:tool-server-key}
|
||||
portal:
|
||||
registry-revision: ${PORTAL_REGISTRY_REVISION:1}
|
||||
route-key: ${PORTAL_ROUTE_KEY:cus}
|
||||
tool-service-domain: ${TOOL_SERVICE_DOMAIN:http://localhost:9092}
|
||||
|
||||
logging:
|
||||
level:
|
||||
com.example.agenttest: INFO
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
625
potal/agent-test-backend/bin/main/static/index.html
Normal file
625
potal/agent-test-backend/bin/main/static/index.html
Normal file
@@ -0,0 +1,625 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>AX HUB Portal PoC</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f4f6f9;
|
||||
--surface: #ffffff;
|
||||
--line: #d7dde8;
|
||||
--text: #172033;
|
||||
--muted: #667085;
|
||||
--primary: #1d5fd1;
|
||||
--primary-dark: #164aa5;
|
||||
--soft: #eef3fb;
|
||||
--good: #087443;
|
||||
--bad: #b42318;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: "Segoe UI", "Noto Sans KR", Arial, sans-serif;
|
||||
}
|
||||
|
||||
header {
|
||||
background: #162033;
|
||||
color: #fff;
|
||||
padding: 18px 28px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.header-meta {
|
||||
color: #cbd5e1;
|
||||
font-size: 13px;
|
||||
text-align: right;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
main {
|
||||
width: min(1320px, calc(100% - 32px));
|
||||
margin: 18px auto 28px;
|
||||
display: grid;
|
||||
grid-template-columns: 360px minmax(0, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
section {
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 0 12px;
|
||||
font-size: 17px;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 16px 0 8px;
|
||||
font-size: 14px;
|
||||
color: var(--muted);
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.two-col {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.item {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.kv {
|
||||
display: grid;
|
||||
grid-template-columns: 110px minmax(0, 1fr);
|
||||
gap: 5px 10px;
|
||||
font-size: 13px;
|
||||
color: var(--muted);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: #e7f6ee;
|
||||
color: var(--good);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin: 10px 0 6px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
input,
|
||||
textarea {
|
||||
width: 100%;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
padding: 10px;
|
||||
font: inherit;
|
||||
background: #fff;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
min-height: 92px;
|
||||
font-family: Consolas, "Courier New", monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
min-height: 104px;
|
||||
font-family: "Segoe UI", "Noto Sans KR", Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
min-height: 38px;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
button.secondary {
|
||||
background: #344054;
|
||||
}
|
||||
|
||||
button.secondary:hover {
|
||||
background: #202939;
|
||||
}
|
||||
|
||||
button.soft {
|
||||
background: var(--soft);
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
button.soft:hover {
|
||||
background: #dfe8f6;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 0;
|
||||
min-height: 300px;
|
||||
padding: 14px;
|
||||
overflow: auto;
|
||||
border-radius: 8px;
|
||||
background: #101828;
|
||||
color: #e5e7eb;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.answer {
|
||||
margin-top: 12px;
|
||||
padding: 14px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 8px;
|
||||
background: #fbfcff;
|
||||
min-height: 80px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.manual-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
header,
|
||||
main,
|
||||
.two-col,
|
||||
.manual-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
header {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.header-meta {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<div>
|
||||
<h1>AX HUB Portal PoC</h1>
|
||||
<div class="header-meta" style="text-align:left">Hardcoded Registry + Agent Backend + MCP Tool Call</div>
|
||||
</div>
|
||||
<div class="header-meta" id="config">Loading configuration...</div>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<div class="stack">
|
||||
<section>
|
||||
<h2>Portal Registry</h2>
|
||||
<p class="header-meta" style="text-align:left;color:var(--muted)">필요할 때 MCP가 Registry를 다시 확인하도록 revision을 갱신합니다.</p>
|
||||
<input id="routeKey" value="cus" type="hidden">
|
||||
<div class="actions">
|
||||
<button onclick="bumpRevision()">Portal Revision 올리기</button>
|
||||
<button onclick="openNewBundle()">+ Tool Server 추가</button>
|
||||
</div>
|
||||
<details id="bundleEditor" style="margin-top:14px">
|
||||
<summary><strong>Tool Server 설정</strong></summary>
|
||||
<div style="margin-top:12px">
|
||||
<label for="bundleId">Bundle ID</label>
|
||||
<input id="bundleId" placeholder="business-tools">
|
||||
<label for="toolServerName">Tool Server 이름</label>
|
||||
<input id="toolServerName" placeholder="Business Tool Server">
|
||||
<label for="manifestUrl">Manifest URL</label>
|
||||
<input id="manifestUrl" placeholder="http://localhost:9090/tool-manifest">
|
||||
<label for="baseUrl">실행 Base URL</label>
|
||||
<input id="baseUrl" placeholder="http://localhost:9090">
|
||||
<label for="namePrefix">Tool name prefix</label>
|
||||
<input id="namePrefix" placeholder="business.">
|
||||
<label><input id="bundleEnabled" type="checkbox" checked style="width:auto"> 활성화</label>
|
||||
<details style="margin-top:12px">
|
||||
<summary>고급 설정</summary>
|
||||
<label for="pollInterval">Manifest 조회 주기(초)</label>
|
||||
<input id="pollInterval" type="number" min="5" value="60">
|
||||
<label for="apiKey">Tool Server API Key (입력 전용)</label>
|
||||
<input id="apiKey" type="password" autocomplete="new-password" placeholder="기존 Key를 유지하려면 비워두세요">
|
||||
<label for="toolEndpoints">Tool 실행 경로 매핑(JSON)</label>
|
||||
<textarea id="toolEndpoints">{}</textarea>
|
||||
</details>
|
||||
<div class="actions">
|
||||
<button onclick="saveBundle()">저장</button>
|
||||
<button class="soft" onclick="closeBundleEditor()">취소</button>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<details>
|
||||
<summary><strong>MCP 연결 테스트</strong></summary>
|
||||
<label for="mcpToolServerFilter">확인할 Tool Server</label>
|
||||
<select id="mcpToolServerFilter" style="width:100%;padding:10px;border:1px solid var(--line);border-radius:6px">
|
||||
<option value="external.">External Tool Server</option>
|
||||
<option value="business.">Business Tool Server</option>
|
||||
<option value="cus">DAP WAS CUS Tool Server</option>
|
||||
</select>
|
||||
<div class="actions">
|
||||
<button onclick="callApi('POST', mcpTestAction('initialize'))">Initialize</button>
|
||||
<button class="secondary" onclick="callApi('POST', mcpTestAction('initialized'))">Initialized</button>
|
||||
<button onclick="loadMcpTools()">Tools/List</button>
|
||||
</div>
|
||||
<div id="mcpToolList" class="stack" style="margin-top:12px"></div>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<section>
|
||||
<details>
|
||||
<summary><strong>Agent 테스트</strong></summary>
|
||||
<label for="message">사용자 요청</label>
|
||||
<textarea id="message" class="chat-box">서울 날씨 알려줘</textarea>
|
||||
<div class="actions">
|
||||
<button onclick="sendChat()">Agent 실행</button>
|
||||
<button class="soft" onclick="setMessage('서울 날씨 알려줘')">서울 날씨</button>
|
||||
<button class="soft" onclick="setMessage('부산 날씨 알려줘')">부산 날씨</button>
|
||||
<button class="soft" onclick="setMessage('달러 환율 알려줘')">달러 환율</button>
|
||||
<button class="soft" onclick="setMessage('대한민국 공휴일 조회해줘')">공휴일 조회</button>
|
||||
<button class="soft" onclick="setMessage('서울 좌표 조회해줘')">서울 좌표</button>
|
||||
<button class="soft" onclick="setMessage('대한민국 국가 정보 조회해줘')">국가 정보</button>
|
||||
<button class="soft" onclick="setMessage('고객 조회해줘')">고객 조회</button>
|
||||
<button class="soft" onclick="setMessage('주문 상태 조회해줘')">주문 상태</button>
|
||||
<button class="soft" onclick="setMessage('고객 문의 티켓 생성해줘')">티켓 생성</button>
|
||||
<button class="soft" onclick="setMessage('메타 공통코드 조회해줘')">메타 공통코드</button>
|
||||
<button class="soft" onclick="setMessage('메타 테이블 조회해줘')">메타 테이블</button>
|
||||
<button class="soft" onclick="setMessage('템플릿 다운로드 URL 알려줘')">템플릿 URL</button>
|
||||
<button class="soft" onclick="setMessage('SOL 의뢰서 목록 조회해줘')">SOL 목록</button>
|
||||
<button class="soft" onclick="setMessage('SOL 의뢰서 상세 조회해줘')">SOL 상세</button>
|
||||
<button class="soft" onclick="setMessage('보험금 청구 처리해줘')">보험금 청구</button>
|
||||
<button class="soft" onclick="setMessage('가입설계 한도 조회해줘')">가입설계 한도</button>
|
||||
<button class="soft" onclick="setMessage('CUS 달러 환율 조회해줘')">CUS 환율</button>
|
||||
<button class="soft" onclick="setMessage('CUS 서울 날씨 조회해줘')">CUS 날씨</button>
|
||||
<button class="soft" onclick="setMessage('오늘의 명언 알려줘')">오늘의 명언</button>
|
||||
<button class="soft" onclick="setMessage('TOOL 파트 구성원 조회해줘')">TOOL 구성원</button>
|
||||
</div>
|
||||
<div class="answer" id="answer">Agent 응답 대기 중</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<details>
|
||||
<summary><strong>Tool 직접 호출</strong></summary>
|
||||
<label for="toolPreset">호출할 Tool</label>
|
||||
<select id="toolPreset" onchange="setToolPreset(this.value)" style="width:100%;padding:10px;border:1px solid var(--line);border-radius:6px">
|
||||
<option value="external.weather_lookup">날씨 조회</option>
|
||||
<option value="external.exchange_rate">환율 조회</option>
|
||||
<option value="external.public_holiday_lookup">공휴일 조회</option>
|
||||
<option value="external.geocoding_lookup">도시 좌표 조회</option>
|
||||
<option value="external.country_info_lookup">국가 정보 조회</option>
|
||||
<option value="business.customer_search">고객 검색</option>
|
||||
<option value="business.order_status">주문 상태 조회</option>
|
||||
<option value="business.ticket_create">지원 티켓 생성(승인 필요)</option>
|
||||
<option value="cmm_comcode_lookup">메타 공통코드 조회</option>
|
||||
<option value="cmm_customer_tool">고객 통합 안내이력 조회</option>
|
||||
<option value="cmm_meta_table">메타 테이블 조회</option>
|
||||
<option value="cmm_template_url">템플릿 URL 조회</option>
|
||||
<option value="sol_request_list">SOL 의뢰서 목록</option>
|
||||
<option value="sol_request_detail">SOL 의뢰서 상세</option>
|
||||
<option value="ins_insurance_processor">보험금 청구 처리</option>
|
||||
<option value="oth_onnba3011_call">가입설계 한도 조회</option>
|
||||
<option value="smp_exchange_inquiry">CUS 환율 조회</option>
|
||||
<option value="smp_weather_inquiry">CUS 날씨 조회</option>
|
||||
<option value="smp_quote_daily">오늘의 명언</option>
|
||||
<option value="smp_team_list">TOOL 파트 구성원</option>
|
||||
</select>
|
||||
<div class="manual-grid">
|
||||
<div>
|
||||
<label for="toolName">Tool Name</label>
|
||||
<input id="toolName" value="external.weather_lookup">
|
||||
</div>
|
||||
<div>
|
||||
<label for="arguments">Arguments JSON</label>
|
||||
<textarea id="arguments">{
|
||||
"city": "Seoul",
|
||||
"timezone": "Asia/Seoul"
|
||||
}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button onclick="callTool()">Tools/Call</button>
|
||||
<button class="soft" onclick="setWeather()">Weather Args</button>
|
||||
<button class="soft" onclick="setExchange()">Exchange Args</button>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<details>
|
||||
<summary><strong>상세 응답 보기</strong></summary>
|
||||
<pre id="output">Waiting...</pre>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
const output = document.getElementById("output");
|
||||
const config = document.getElementById("config");
|
||||
const answer = document.getElementById("answer");
|
||||
|
||||
async function callApi(method, url, body) {
|
||||
output.textContent = "Requesting...";
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method,
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: body ? JSON.stringify(body) : undefined
|
||||
});
|
||||
const text = await response.text();
|
||||
const data = text ? JSON.parse(text) : {};
|
||||
output.textContent = JSON.stringify(data, null, 2);
|
||||
if (!response.ok) {
|
||||
throw new Error(data.detail || data.message || data.error || `HTTP ${response.status}`);
|
||||
}
|
||||
return data;
|
||||
} catch (error) {
|
||||
output.textContent = JSON.stringify({error: error.message}, null, 2);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadConfig() {
|
||||
const data = await callApi("GET", "/api/config");
|
||||
document.getElementById("routeKey").value = data.defaultRouteKey || "cus";
|
||||
config.innerHTML = `MCP ${data.mcpEndpointUrl}<br>Route MCP ${data.defaultRoutedMcpEndpointUrl}<br>Manifest ${data.toolManifestUrl}`;
|
||||
}
|
||||
|
||||
async function loadRegistry() {
|
||||
const bundles = await callApi("GET", "/api/portal/bundles");
|
||||
window.portalBundles = bundles;
|
||||
}
|
||||
|
||||
function openNewBundle() {
|
||||
clearBundleForm();
|
||||
document.getElementById("bundleEditor").open = true;
|
||||
}
|
||||
|
||||
function closeBundleEditor() {
|
||||
document.getElementById("bundleEditor").open = false;
|
||||
}
|
||||
|
||||
function clearBundleForm() {
|
||||
["bundleId", "toolServerName", "manifestUrl", "baseUrl", "namePrefix", "apiKey"]
|
||||
.forEach(id => document.getElementById(id).value = "");
|
||||
document.getElementById("bundleId").readOnly = false;
|
||||
document.getElementById("pollInterval").value = 60;
|
||||
document.getElementById("toolEndpoints").value = "{}";
|
||||
document.getElementById("bundleEnabled").checked = true;
|
||||
}
|
||||
|
||||
async function saveBundle() {
|
||||
const idInput = document.getElementById("bundleId");
|
||||
const payload = {
|
||||
bundleId: idInput.value.trim(),
|
||||
toolServerName: document.getElementById("toolServerName").value.trim(),
|
||||
manifestUrl: document.getElementById("manifestUrl").value.trim(),
|
||||
baseUrl: document.getElementById("baseUrl").value.trim(),
|
||||
namePrefix: document.getElementById("namePrefix").value.trim(),
|
||||
enabled: document.getElementById("bundleEnabled").checked,
|
||||
manifestPollIntervalSeconds: Number(document.getElementById("pollInterval").value),
|
||||
apiKey: document.getElementById("apiKey").value,
|
||||
toolEndpoints: JSON.parse(document.getElementById("toolEndpoints").value || "{}")
|
||||
};
|
||||
const url = idInput.readOnly
|
||||
? `/api/portal/bundles/${encodeURIComponent(payload.bundleId)}`
|
||||
: "/api/portal/bundles";
|
||||
await callApi(idInput.readOnly ? "PUT" : "POST", url, payload);
|
||||
await loadRegistry();
|
||||
closeBundleEditor();
|
||||
}
|
||||
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value).replace(/[&<>"']/g, char => ({"&":"&","<":"<",">":">","\"":""","'":"'"})[char]);
|
||||
}
|
||||
|
||||
function escapeJs(value) {
|
||||
return String(value).replace(/[\\']/g, "\\$&");
|
||||
}
|
||||
|
||||
async function bumpRevision() {
|
||||
const data = await callApi("POST", `/api/portal/registry/${selectedRoute()}/revision`);
|
||||
await loadRegistry();
|
||||
output.textContent = JSON.stringify({
|
||||
message: "Portal registry revision bumped",
|
||||
routeKey: data.routeKey,
|
||||
registryRevision: data.registryRevision
|
||||
}, null, 2);
|
||||
}
|
||||
|
||||
function renderGroup(title, rows) {
|
||||
return `<div><h3>${title}</h3>${rows.map(renderItem).join("")}</div>`;
|
||||
}
|
||||
|
||||
function renderItem(row) {
|
||||
const title = row.displayName || row.serviceKey || row.routeKey;
|
||||
const entries = Object.entries(row)
|
||||
.map(([key, value]) => `<div>${key}</div><div>${value}</div>`)
|
||||
.join("");
|
||||
return `<div class="item"><div class="item-title">${title} <span class="badge">${row.status || row.source}</span></div><div class="kv">${entries}</div></div>`;
|
||||
}
|
||||
|
||||
async function sendChat() {
|
||||
const message = document.getElementById("message").value.trim();
|
||||
answer.textContent = "Agent is selecting a tool...";
|
||||
const data = await callApi("POST", "/api/agent/chat", {message});
|
||||
answer.innerHTML = `<strong>${data.answer}</strong><br><br>Agent route 결정: ${data.routeKey}<br>판단: ${data.routeDecision}<br>MCP: ${data.mcpEndpointUrl}<br>Tool: ${data.selectedTool}<br>인자: ${JSON.stringify(data.arguments)}`;
|
||||
}
|
||||
|
||||
async function callTool() {
|
||||
const name = document.getElementById("toolName").value.trim();
|
||||
const args = JSON.parse(document.getElementById("arguments").value);
|
||||
const routeKey = name.startsWith("business.") ? "business"
|
||||
: /^(cmm|ins|oth|smp|sol)_/.test(name) ? "cus" : "external";
|
||||
await callApi("POST", `/api/mcp/${routeKey}/tools/call`, {name, arguments: args});
|
||||
}
|
||||
|
||||
function setToolPreset(name) {
|
||||
const presets = {
|
||||
"external.weather_lookup": {city: "Seoul", timezone: "Asia/Seoul"},
|
||||
"external.exchange_rate": {from: "USD", to: "KRW"},
|
||||
"external.public_holiday_lookup": {countryCode: "KR", year: new Date().getFullYear()},
|
||||
"external.geocoding_lookup": {city: "Seoul", language: "ko"},
|
||||
"external.country_info_lookup": {countryCode: "KR"},
|
||||
"business.customer_search": {keyword: "C-1001"},
|
||||
"business.order_status": {orderId: "O-9001"},
|
||||
"business.ticket_create": {
|
||||
title: "Portal test ticket",
|
||||
priority: "normal",
|
||||
description: "Created from the Portal Tool test screen"
|
||||
},
|
||||
"cmm_comcode_lookup": {groupCode: "GRP_COMM_CD", useYn: "Y"},
|
||||
"cmm_customer_tool": {csNo: "000000000001"},
|
||||
"cmm_meta_table": {tableName: "TB_CUST_BAS", owner: "DAPADM"},
|
||||
"cmm_template_url": {templateId: "TPL_001"},
|
||||
"sol_request_list": {status: "진행중", period: "1개월", target: "나의 업무"},
|
||||
"sol_request_detail": {srId: "SR-001"},
|
||||
"ins_insurance_processor": {claimNumber: "CLM20230001", claimAmount: 1500000, claimDate: "2026-08-12"},
|
||||
"oth_onnba3011_call": {dalScCd: "1", cstSucoRltyCd: "01", csNo: "000000000001"},
|
||||
"smp_exchange_inquiry": {currencyCode: "USD"},
|
||||
"smp_weather_inquiry": {city: "서울"},
|
||||
"smp_quote_daily": {category: "속담"},
|
||||
"smp_team_list": {teamName: "TOOL"}
|
||||
};
|
||||
document.getElementById("toolName").value = name;
|
||||
document.getElementById("arguments").value = JSON.stringify(presets[name] || {}, null, 2);
|
||||
}
|
||||
|
||||
function selectedRoute() {
|
||||
const route = document.getElementById("routeKey").value.trim();
|
||||
return route || "cus";
|
||||
}
|
||||
|
||||
function mcpAction(action) {
|
||||
return `/api/mcp/${selectedRoute()}/${action}`;
|
||||
}
|
||||
|
||||
async function loadMcpTools() {
|
||||
const data = await callApi("POST", mcpTestAction("tools/list"));
|
||||
const prefix = document.getElementById("mcpToolServerFilter").value;
|
||||
const tools = (((data || {}).body || {}).result || {}).tools || [];
|
||||
const filtered = prefix === "all" || prefix === "cus"
|
||||
? tools : tools.filter(tool => tool.name.startsWith(prefix));
|
||||
const target = document.getElementById("mcpToolList");
|
||||
target.innerHTML = filtered.length ? filtered.map(tool => `
|
||||
<div class="item">
|
||||
<div class="item-title">${escapeHtml(tool.title || tool.name)}</div>
|
||||
<div class="kv">
|
||||
<div>name</div><div>${escapeHtml(tool.name)}</div>
|
||||
<div>설명</div><div>${escapeHtml(tool.description || "-")}</div>
|
||||
<div>유형</div><div>${tool.annotations && tool.annotations.readOnlyHint ? "READ" : "WRITE"}</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="soft" onclick="prepareToolCall('${escapeJs(tool.name)}')">이 Tool 호출</button>
|
||||
</div>
|
||||
</div>`).join("") : `<div class="header-meta" style="text-align:left;color:var(--muted)">선택한 서버의 Tool이 없습니다.</div>`;
|
||||
}
|
||||
|
||||
function mcpTestAction(action) {
|
||||
const filter = document.getElementById("mcpToolServerFilter").value;
|
||||
const routeKey = filter === "business." ? "business"
|
||||
: filter === "cus" ? "cus" : "external";
|
||||
return `/api/mcp/${routeKey}/${action}`;
|
||||
}
|
||||
|
||||
function prepareToolCall(name) {
|
||||
setToolPreset(name);
|
||||
document.getElementById("toolPreset").value = name;
|
||||
document.getElementById("toolPreset").closest("details").open = true;
|
||||
document.getElementById("toolPreset").scrollIntoView({behavior: "smooth", block: "center"});
|
||||
}
|
||||
|
||||
function setMessage(value) {
|
||||
document.getElementById("message").value = value;
|
||||
}
|
||||
|
||||
function setWeather() {
|
||||
document.getElementById("toolName").value = "external.weather_lookup";
|
||||
document.getElementById("arguments").value = JSON.stringify({
|
||||
city: "Seoul",
|
||||
timezone: "Asia/Seoul"
|
||||
}, null, 2);
|
||||
}
|
||||
|
||||
function setExchange() {
|
||||
document.getElementById("toolName").value = "external.exchange_rate";
|
||||
document.getElementById("arguments").value = JSON.stringify({
|
||||
from: "USD",
|
||||
to: "KRW"
|
||||
}, null, 2);
|
||||
}
|
||||
|
||||
loadConfig().then(loadRegistry).catch(error => {
|
||||
config.textContent = "Configuration load failed";
|
||||
output.textContent = JSON.stringify({error: error.message}, null, 2);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user