big prompt improvement to mcp

This commit is contained in:
southseact-3d
2026-02-20 18:17:36 +00:00
parent d3580b091a
commit dfc4a0d2a9
9 changed files with 1120 additions and 59 deletions

View File

@@ -71,6 +71,22 @@
</div>
<div id="system-tests-output" class="admin-list" style="margin-top: 12px;"></div>
</div>
<div class="admin-card">
<header>
<h3>WordPress Validator MCP Test</h3>
<div class="pill">MCP Server</div>
</header>
<p class="muted" style="margin-top:0;">
Tests the WordPress Validator MCP server end-to-end: creates a minimal test plugin,
runs the validator MCP tool, and verifies the response format.
</p>
<div class="admin-actions">
<button id="validator-mcp-test-run" class="primary">Test Validator MCP</button>
<div class="status-line" id="validator-mcp-status"></div>
</div>
<div id="validator-mcp-output" class="admin-list" style="margin-top: 12px;"></div>
</div>
</div>
</main>
</div>

View File

@@ -128,6 +128,9 @@
systemTestsRun: document.getElementById('system-tests-run'),
systemTestsStatus: document.getElementById('system-tests-status'),
systemTestsOutput: document.getElementById('system-tests-output'),
validatorMcpTestRun: document.getElementById('validator-mcp-test-run'),
validatorMcpStatus: document.getElementById('validator-mcp-status'),
validatorMcpOutput: document.getElementById('validator-mcp-output'),
};
function ensureAvailableDatalist() {
@@ -230,6 +233,122 @@
el.systemTestsStatus.style.color = isError ? 'var(--danger)' : 'inherit';
}
function setValidatorMcpStatus(msg, isError = false) {
if (!el.validatorMcpStatus) return;
el.validatorMcpStatus.textContent = msg || '';
el.validatorMcpStatus.style.color = isError ? 'var(--danger)' : 'inherit';
}
function renderValidatorMcpOutput(data) {
if (!el.validatorMcpOutput) return;
el.validatorMcpOutput.innerHTML = '';
if (!data) return;
const summaryRow = document.createElement('div');
summaryRow.className = 'admin-row';
const summaryLabel = document.createElement('div');
summaryLabel.style.minWidth = '180px';
summaryLabel.style.color = 'var(--muted)';
summaryLabel.textContent = 'Test Summary';
const summaryValue = document.createElement('div');
const statusSpan = document.createElement('span');
statusSpan.textContent = data.ok ? 'PASSED' : 'FAILED';
statusSpan.style.color = data.ok ? 'var(--shopify-green)' : 'var(--danger)';
statusSpan.style.fontWeight = '600';
summaryValue.appendChild(statusSpan);
if (data.durationMs) {
const timing = document.createElement('span');
timing.textContent = ` (${data.durationMs}ms)`;
timing.style.color = 'var(--muted)';
timing.style.marginLeft = '8px';
summaryValue.appendChild(timing);
}
summaryRow.appendChild(summaryLabel);
summaryRow.appendChild(summaryValue);
el.validatorMcpOutput.appendChild(summaryRow);
if (data.error) {
const errorRow = document.createElement('div');
errorRow.className = 'admin-row';
errorRow.style.background = 'rgba(220, 38, 38, 0.1)';
errorRow.style.padding = '12px';
errorRow.style.borderRadius = '6px';
errorRow.style.marginTop = '8px';
const errorLabel = document.createElement('div');
errorLabel.style.minWidth = '180px';
const errorStrong = document.createElement('strong');
errorStrong.textContent = 'Error';
errorStrong.style.color = 'var(--danger)';
errorLabel.appendChild(errorStrong);
const errorValue = document.createElement('div');
errorValue.textContent = data.error;
errorValue.style.color = 'var(--danger)';
errorValue.style.fontFamily = 'monospace';
errorValue.style.fontSize = '12px';
errorValue.style.wordBreak = 'break-word';
errorRow.appendChild(errorLabel);
errorRow.appendChild(errorValue);
el.validatorMcpOutput.appendChild(errorRow);
}
const details = [
['MCP Server Path', data.mcpServerPath || '—'],
['Validation Script', data.validationScriptPath || '—'],
['Test Plugin Path', data.testPluginPath || '—'],
['Tool Invoked', data.toolInvoked ? 'Yes' : 'No'],
['Response Valid', data.responseValid ? 'Yes' : 'No'],
];
details.forEach(([label, value]) => {
const row = document.createElement('div');
row.className = 'admin-row';
const labelWrap = document.createElement('div');
labelWrap.style.minWidth = '180px';
const strong = document.createElement('strong');
strong.textContent = label;
labelWrap.appendChild(strong);
const valueWrap = document.createElement('div');
valueWrap.textContent = value;
row.appendChild(labelWrap);
row.appendChild(valueWrap);
el.validatorMcpOutput.appendChild(row);
});
if (data.validationResult) {
const resultSection = document.createElement('div');
resultSection.style.marginTop = '12px';
resultSection.style.padding = '12px';
resultSection.style.background = 'var(--bg-subtle)';
resultSection.style.borderRadius = '6px';
const resultHeader = document.createElement('div');
resultHeader.style.fontWeight = '600';
resultHeader.style.marginBottom = '8px';
resultHeader.textContent = 'Validation Result';
resultSection.appendChild(resultHeader);
const resultPre = document.createElement('pre');
resultPre.style.margin = '0';
resultPre.style.padding = '8px';
resultPre.style.background = 'var(--bg)';
resultPre.style.borderRadius = '4px';
resultPre.style.fontSize = '11px';
resultPre.style.overflow = 'auto';
resultPre.style.maxHeight = '200px';
resultPre.textContent = typeof data.validationResult === 'string'
? data.validationResult
: JSON.stringify(data.validationResult, null, 2);
resultSection.appendChild(resultPre);
el.validatorMcpOutput.appendChild(resultSection);
}
}
function renderExternalTestingConfig(config) {
if (!el.externalTestingConfig) return;
el.externalTestingConfig.innerHTML = '';
@@ -2935,6 +3054,33 @@
});
}
// WordPress Validator MCP Test button handler
if (el.validatorMcpTestRun) {
el.validatorMcpTestRun.addEventListener('click', async () => {
el.validatorMcpTestRun.disabled = true;
setValidatorMcpStatus('Testing WordPress Validator MCP...');
if (el.validatorMcpOutput) el.validatorMcpOutput.innerHTML = '';
try {
const data = await api('/api/admin/validator-mcp-test', { method: 'POST' });
renderValidatorMcpOutput(data);
if (data.ok) {
setValidatorMcpStatus(`Test passed! (${data.durationMs}ms)`);
} else {
setValidatorMcpStatus(`Test failed: ${data.error || 'Unknown error'}`, true);
}
} catch (err) {
setValidatorMcpStatus(err.message || 'Test failed', true);
if (el.validatorMcpOutput) {
renderValidatorMcpOutput({ ok: false, error: err.message || 'Request failed' });
}
} finally {
el.validatorMcpTestRun.disabled = false;
}
});
}
if (el.logout) {
el.logout.addEventListener('click', async () => {
await api('/api/admin/logout', { method: 'POST' }).catch(() => { });

View File

@@ -1429,66 +1429,93 @@ function classifyStatusMessage(msg) {
if (!text) return { userText: '', adminText: '' };
// Mask all OpenCode, provider, and internal error messages with branded message
const internalErrorPatterns = [
/opencode/i,
/openrouter/i,
/mistral/i,
/anthropic/i,
/error:.*\d{3}/i,
/exit code/i,
/stderr/i,
/stdout/i,
/provider.*error/i,
/api.*error/i,
/rate limit/i,
/quota/i,
/insufficient/i,
/unauthorized/i,
/forbidden/i,
/internal server error/i,
/service unavailable/i,
/gateway/i,
/timeout/i,
/connection.*refused/i,
/connection.*lost/i,
/process.*exited/i,
/tool.*call.*format/i,
/malformed/i,
/invalid.*edit/i,
/proper prefixing/i,
/session terminated/i,
/early termination/i,
/model.*not found/i,
/unknown model/i,
/context length/i,
/token limit/i,
];
const isInternalError = internalErrorPatterns.some(pattern => pattern.test(text));
if (isInternalError) {
return {
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
if (lower.startsWith('no models configured')) {
return {
userText: 'No models are configured. Please contact support.',
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
if (lower.startsWith('model load failed:')) {
return {
userText: 'Models are currently unavailable. Please contact support.',
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
if (lower.startsWith('planning failed:')) {
return {
userText: 'Planning is currently unavailable. Please contact support.',
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
// Surface missing provider API keys to the user with actionable text
if (lower.includes('missing provider api keys') || lower.includes('no configured planning providers')) {
// Try to extract provider list from the message if present
const m = text.match(/Missing provider API keys:\s*([^\n\r]+)/i);
const providers = m ? m[1].trim() : null;
return {
userText: providers
? `Planning unavailable: missing API keys for ${providers}. Please set the environment variables (e.g. GROQ_API_KEY) or configure providers in Admin.`
: 'Planning unavailable: missing provider API keys. Please check server configuration.',
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
if (lower.includes('openrouter api key') || lower.includes('openrouter request failed')) {
return {
userText: 'Planning is currently unavailable. Please contact support.',
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
if (lower.startsWith('warning: opencode cli not available')) {
return {
userText: 'Builder service is currently unavailable. Please contact support.',
userText: 'Plugin Compass failed. Please try again.',
adminText: text,
};
}
if (lower.includes('proper prefixing') ||
lower.includes('tool call format') ||
lower.includes('tool call prefix') ||
lower.includes('session terminated') ||
lower.includes('early termination')) {
return {
userText: 'Connection interrupted. Resuming...',
adminText: `Early termination detected: ${text}`,
type: 'warning'
};
}
return { userText: text, adminText: '' };
}
@@ -2152,14 +2179,16 @@ function renderMessages(session) {
const err = document.createElement('div');
err.className = 'body';
err.style.color = 'var(--danger)';
err.textContent = msg.error;
const { userText: maskedError } = classifyStatusMessage(msg.error);
err.textContent = maskedError;
assistantCard.appendChild(err);
}
if ((!msg.reply || !msg.reply.length) && (!msg.partialOutput || !msg.partialOutput.length) && msg.opencodeSummary) {
const summary = document.createElement('div');
summary.className = 'body';
summary.style.color = 'var(--muted)';
summary.textContent = `Opencode output: ${msg.opencodeSummary}`;
const { userText: maskedSummary } = classifyStatusMessage(msg.opencodeSummary);
summary.textContent = maskedSummary;
assistantCard.appendChild(summary);
}
@@ -3161,7 +3190,9 @@ function streamMessage(sessionId, messageId) {
// Update session list (no-op in builder)
renderSessions();
} else if (data.type === 'error') {
message.error = data.error || 'Unknown error';
const rawError = data.error || 'Unknown error';
const { userText: maskedError } = classifyStatusMessage(rawError);
message.error = maskedError;
message.reply = data.content || message.partialOutput || '';
message.status = 'error';
message.finishedAt = data.timestamp;
@@ -3181,7 +3212,7 @@ function streamMessage(sessionId, messageId) {
scrollChatToBottom();
if (!message.isBackgroundContinuation) {
setStatus('Error: ' + (data.error || 'Unknown error'));
setStatus(rawError);
}
stopUsagePolling();