fix: tester.html 파라미터 스키마 렌더링 오류 수정 및 객체 재귀 탐색 기능 추가
Some checks failed
Deploy to OCIWP / deploy (push) Failing after 12s

This commit is contained in:
jade
2026-08-27 09:53:10 +09:00
parent 8810126185
commit a362fe69d9

View File

@@ -207,7 +207,7 @@
<svg class="w-3 h-3 text-zinc-500" fill="currentColor" viewBox="0 0 20 20"><path d="M10 18a8 8 0 100-16 8 8 0 000 16zM9 9a1 1 0 012 0v4a1 1 0 11-2 0V9zm1-5a1 1 0 110 2 1 1 0 010-2z"></path></svg> <svg class="w-3 h-3 text-zinc-500" fill="currentColor" viewBox="0 0 20 20"><path d="M10 18a8 8 0 100-16 8 8 0 000 16zM9 9a1 1 0 012 0v4a1 1 0 11-2 0V9zm1-5a1 1 0 110 2 1 1 0 010-2z"></path></svg>
Auto-saved to LocalStorage Auto-saved to LocalStorage
</div> </div>
<button onclick="resetPayload()" class="btn-sm py-2 px-4 text-red-400 hover:text-red-300 border border-red-500/30 hover:border-red-400 mr-2" style="font-size:12px;" title="저장된 페이로드를 삭제하고 스키마 기본값으로 초기화합니다">Reset to Schema</button> <button onclick="resetPayload()" class="btn-sm py-2 px-4 text-red-400 hover:text-red-300 border border-red-500/30 hover:border-red-400 mr-2" style="font-size:12px;" title="?€?¥ë<C2A5>œ ?˜ì<CB9C>´ë¡œë“œë¥??? œ?˜ê³  ?¤í‚¤ë§?기본값으ë¡?초기?”í•©?ˆë‹¤">Reset to Schema</button>
<button onclick="closePayloadModal()" class="btn-sm py-2 px-4">Cancel</button> <button onclick="closePayloadModal()" class="btn-sm py-2 px-4">Cancel</button>
<button onclick="savePayload()" class="btn-execute py-2 px-4" style="font-size:12px;">Save & Apply</button> <button onclick="savePayload()" class="btn-execute py-2 px-4" style="font-size:12px;">Save & Apply</button>
</div> </div>
@@ -369,24 +369,42 @@
localStorage.setItem('mcp_tester_payloads', JSON.stringify(dict)); localStorage.setItem('mcp_tester_payloads', JSON.stringify(dict));
} }
function generateDummyForSchema(schema) {
if (!schema) return null;
if (schema.example !== undefined) return schema.example;
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) return schema.examples[0];
if (schema.default !== undefined) return schema.default;
if (schema.type === 'object') {
const obj = {};
if (schema.properties) {
for (const [k, v] of Object.entries(schema.properties)) {
obj[k] = generateDummyForSchema(v);
}
}
return obj;
} else if (schema.type === 'array') {
return [generateDummyForSchema(schema.items || {})];
} else if (schema.type === 'string') {
if (schema.enum && schema.enum.length > 0) return schema.enum[0];
return "test_string";
} else if (schema.type === 'integer' || schema.type === 'number') {
return schema.minimum !== undefined ? schema.minimum : 1;
} else if (schema.type === 'boolean') {
return true;
}
return null;
}
function generateDummyPayload(tool) { function generateDummyPayload(tool) {
if (tool._customPayload) return tool._customPayload; if (tool._customPayload) return tool._customPayload;
const schema = tool.parametersSchema; const schema = tool.inputSchema || (tool.inputSchema || tool.parametersSchema);
if (!schema || !schema.properties) return {}; if (!schema || !schema.properties) return {};
const payload = {}; const payload = {};
for (const [key, value] of Object.entries(schema.properties)) { for (const [key, value] of Object.entries(schema.properties)) {
if (value.example !== undefined) payload[key] = value.example; payload[key] = generateDummyForSchema(value);
else if (value.examples && Array.isArray(value.examples) && value.examples.length > 0) payload[key] = value.examples[0];
else if (value.default !== undefined) payload[key] = value.default;
else {
if (value.type === 'string') {
if (value.enum && value.enum.length > 0) payload[key] = value.enum[0];
else payload[key] = "test_string";
} else if (value.type === 'integer' || value.type === 'number') payload[key] = value.minimum !== undefined ? value.minimum : 1;
else if (value.type === 'boolean') payload[key] = true;
else payload[key] = null;
}
} }
return payload; return payload;
} }
@@ -480,9 +498,9 @@
tool._responseData = res.data; tool._responseData = res.data;
if (tool._isFailed) { if (tool._isFailed) {
appendLog(`[ERROR] ${tool.name} failed (${res.status}) - ${res.latency}ms`, 'error'); appendLog(`??[ERROR] ${tool.name} failed (${res.status}) - ${res.latency}ms`, 'error');
} else { } else {
appendLog(`[SUCCESS] ${tool.name} completed - ${res.latency}ms`, 'success'); appendLog(`??[SUCCESS] ${tool.name} completed - ${res.latency}ms`, 'success');
} }
// Update UI if visible // Update UI if visible
@@ -746,7 +764,7 @@
} }
function renderSmartForm(tool, payloadToUse = null) { function renderSmartForm(tool, payloadToUse = null) {
const schema = tool.parametersSchema; const schema = (tool.inputSchema || tool.parametersSchema);
const form = document.getElementById('payloadFormContainer'); const form = document.getElementById('payloadFormContainer');
form.innerHTML = ''; form.innerHTML = '';
@@ -834,18 +852,18 @@
if (currentEditIndex === -1) return; if (currentEditIndex === -1) return;
const tool = allTools[currentEditIndex]; const tool = allTools[currentEditIndex];
// 로컬 스토리지에서 삭제 // 로컬 ?¤í† ë¦¬ì??<3F>서 ?? œ
const savedPayloads = JSON.parse(localStorage.getItem('mcp_tester_payloads') || '{}'); const savedPayloads = JSON.parse(localStorage.getItem('mcp_tester_payloads') || '{}');
delete savedPayloads[tool.name]; delete savedPayloads[tool.name];
localStorage.setItem('mcp_tester_payloads', JSON.stringify(savedPayloads)); localStorage.setItem('mcp_tester_payloads', JSON.stringify(savedPayloads));
// 런타임 상태 초기화 // ?°í????<3F>태 초기??
tool._customPayload = null; tool._customPayload = null;
// 새 스키마 기반 기본 페이로드 생성 // ???¤í‚¤ë§?기반 기본 ?˜ì<CB9C>´ë¡œë“œ ?<3F>성
const freshPayload = generateDummyPayload(tool); const freshPayload = generateDummyPayload(tool);
// UI 업데이트 // UI ?…ë<E280A6>°?´íЏ
document.getElementById('payloadEditor').value = JSON.stringify(freshPayload, null, 2); document.getElementById('payloadEditor').value = JSON.stringify(freshPayload, null, 2);
renderSmartForm(tool, freshPayload); renderSmartForm(tool, freshPayload);
} }
@@ -1048,8 +1066,8 @@
// Or we just map /mcp/api/v1/tools/call and use tool names as different request bodies, // Or we just map /mcp/api/v1/tools/call and use tool names as different request bodies,
// but for documentation purposes, mapping them as separate virtual paths is usually better to view in Swagger UI. // but for documentation purposes, mapping them as separate virtual paths is usually better to view in Swagger UI.
currentExportJson.forEach(tool => { currentExportJson.forEach(tool => {
const schemaProps = tool.parametersSchema?.properties || {}; const schemaProps = (tool.inputSchema || tool.parametersSchema)?.properties || {};
const required = tool.parametersSchema?.required || []; const required = (tool.inputSchema || tool.parametersSchema)?.required || [];
paths[`/mcp/api/v1/tools/call?name=${tool.name}`] = { paths[`/mcp/api/v1/tools/call?name=${tool.name}`] = {
post: { post: {
@@ -1133,8 +1151,8 @@
md += `### \`${tool.name}\`\n\n`; md += `### \`${tool.name}\`\n\n`;
md += `**Description:** ${tool.description || 'N/A'}\n\n`; md += `**Description:** ${tool.description || 'N/A'}\n\n`;
const props = tool.parametersSchema?.properties || {}; const props = (tool.inputSchema || tool.parametersSchema)?.properties || {};
const reqs = tool.parametersSchema?.required || []; const reqs = (tool.inputSchema || tool.parametersSchema)?.required || [];
if (Object.keys(props).length > 0) { if (Object.keys(props).length > 0) {
md += `#### Parameters\n\n`; md += `#### Parameters\n\n`;
@@ -1142,7 +1160,7 @@
md += `|------|------|----------|-------------|---------|\n`; md += `|------|------|----------|-------------|---------|\n`;
Object.keys(props).forEach(p => { Object.keys(props).forEach(p => {
const pData = props[p]; const pData = props[p];
const isReq = reqs.includes(p) ? 'Yes' : 'No'; const isReq = reqs.includes(p) ? '??Yes' : '??No';
let example = pData.example !== undefined ? pData.example : (pData.examples ? pData.examples[0] : ''); let example = pData.example !== undefined ? pData.example : (pData.examples ? pData.examples[0] : '');
if (typeof example === 'object') example = JSON.stringify(example); if (typeof example === 'object') example = JSON.stringify(example);
md += `| \`${p}\` | \`${pData.type || 'string'}\` | ${isReq} | ${pData.description || ''} | \`${example}\` |\n`; md += `| \`${p}\` | \`${pData.type || 'string'}\` | ${isReq} | ${pData.description || ''} | \`${example}\` |\n`;