structural-thinking-mcp
Version:
MCP server for converting free-text prompts into structured thinking frameworks with validation and gap detection.
682 lines (655 loc) • 33.9 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import Ajv from "ajv";
import fs from "node:fs";
import path from "node:path";
import url from "node:url";
// --- Schema ---
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
const schemaPath = path.join(__dirname, "schema", "structural-thinking.v1.json");
const schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
const ajv = new Ajv({ allErrors: true, allowUnionTypes: true, validateSchema: false });
const validateStructuralThinking = ajv.compile(schema);
// --- Configuration ---
const SCORING_CONFIG = {
clarity: {
baseScore: 0.5,
vagueWordPenalty: 0.08,
maxVagueWordPenalty: 0.4,
clarityIndicatorBonus: 0.12,
maxClarityIndicatorBonus: 0.35,
measurementBonus: 0.1,
maxMeasurementBonus: 0.25,
verbBonus: 0.06,
maxVerbBonus: 0.18,
longSentencePenalty1: 0.08,
longSentencePenalty2: 0.12,
structureBonus: 0.12,
sentenceLengthThreshold1: 150,
sentenceLengthThreshold2: 200,
minSentenceLength: 10,
proportionalScoring: true
},
completeness: {
baseScore: 0.35,
titleBonus: 0.08,
titleMinLength: 5,
instructionsBonus: 0.18,
constraintsBonus: 0.12,
domainBonus: 0.08,
formatBonus: 0.08,
sectionsBonus: 0.04,
maxSectionsBonus: 0.16,
completenessIndicatorBonus: 0.025,
maxCompletenessIndicatorBonus: 0.1,
exampleBonus: 0.08,
depthBonus1: 0.04, // >50 words
depthBonus2: 0.04, // >100 words
depthBonus3: 0.04, // >200 words
depthThreshold1: 50,
depthThreshold2: 100,
depthThreshold3: 200,
shortInstructionPenalty: 0.1,
shortInstructionThreshold: 50
},
validation: {
minPromptLength: 3,
maxPromptLength: 10000,
roundingPrecision: 2
}
};
// --- Word Lists ---
const vagueWords = [
/\boptimi[sz]e[ds]?\b/i, /\bbetter\b/i, /\bquickly\b/i, /\bimprove[ds]?\b/i,
/\bnice\b/i, /\bgood\b/i, /\bclean\b/i, /\befficient\b/i, /\bsimple\b/i,
/\beasy\b/i, /\bfast\b/i, /\bsmooth\b/i, /\bpretty\b/i, /\bquite\b/i,
/\bvery\b/i, /\breally\b/i, /\bsomewhat\b/i, /\brather\b/i, /\bbasically\b/i,
/\bobviously\b/i, /\bclearly\b/i
];
const clarityIndicators = [
/\bspecifically\b/i, /\bexactly\b/i, /\bmust\b/i, /\bshall\b/i,
/\brequired\b/i, /\bmandatory\b/i, /\bwill\b/i, /\bshould\b/i,
/\bprecisely\b/i, /\bexplicitly\b/i, /\bdefined as\b/i, /\bmeasured by\b/i,
/\bstrictly\b/i, /\bcompulsory\b/i
];
const completenessIndicators = [
/\binput\b/i, /\boutput\b/i, /\bformat\b/i, /\bconstraints?\b/i,
/\blimits?\b/i, /\bsteps?\b/i, /\bprocess\b/i, /\bmethods?\b/i,
/\bworkflows?\b/i, /\bpipelines?\b/i, /\brequirements?\b/i,
/\bspecifications?\b/i, /\bparameters?\b/i, /\bcriteria\b/i
];
const concreteVerbs = [
/\bcreate[ds]?\b/i, /\bgenerate[ds]?\b/i, /\bwrite[ns]?\b/i, /\banalyze[ds]?\b/i,
/\bcompare[ds]?\b/i, /\blist[s]?\b/i, /\bsummari[sz]e[ds]?\b/i, /\bextract[s]?\b/i,
/\bidentify\b/i, /\bcalculate[ds]?\b/i, /\bimplement[s]?\b/i, /\bdesign[s]?\b/i
];
function validateInputDetailed(value, type, minLength, maxLength) {
if (type === 'string') {
if (typeof value !== 'string') {
return { valid: false, error: `Expected string but received ${typeof value}`, code: 'INVALID_TYPE' };
}
if (minLength !== undefined && value.length < minLength) {
return { valid: false, error: `String too short: ${value.length} chars (min: ${minLength})`, code: 'TOO_SHORT' };
}
if (maxLength !== undefined && value.length > maxLength) {
return { valid: false, error: `String too long: ${value.length} chars (max: ${maxLength})`, code: 'TOO_LONG' };
}
}
if (type === 'object' && (typeof value !== 'object' || value === null)) {
return { valid: false, error: `Expected object but received ${value === null ? 'null' : typeof value}`, code: 'INVALID_TYPE' };
}
return { valid: true };
}
// Legacy wrapper for backward compatibility
function validateInput(value, type, minLength, maxLength) {
return validateInputDetailed(value, type, minLength, maxLength).valid;
}
function safeRound(value, precision = SCORING_CONFIG.validation.roundingPrecision) {
const factor = Math.pow(10, precision);
return Math.round(value * factor) / factor;
}
function hasVagueLanguage(text) {
if (!validateInput(text, 'string'))
return false;
return vagueWords.some(rx => rx.test(text));
}
// Enhanced vague language analysis with detailed feedback
function analyzeVagueLanguage(text) {
if (!validateInput(text, 'string'))
return { hasVague: false, vagueTerms: [], suggestions: [] };
const vagueTerms = [];
const suggestions = [];
vagueWords.forEach(rx => {
const matches = text.match(rx);
if (matches) {
vagueTerms.push(...matches);
// Add specific suggestions for common vague terms
if (rx.source.includes('better'))
suggestions.push('Specify measurable improvements');
if (rx.source.includes('optimi'))
suggestions.push('Define optimization criteria and metrics');
if (rx.source.includes('improve'))
suggestions.push('Quantify the improvement goals');
if (rx.source.includes('good|nice'))
suggestions.push('Define quality criteria specifically');
if (rx.source.includes('fast|quick'))
suggestions.push('Specify performance requirements');
}
});
return { hasVague: vagueTerms.length > 0, vagueTerms: [...new Set(vagueTerms)], suggestions: [...new Set(suggestions)] };
}
function getWordCount(text) {
if (!text || typeof text !== 'string')
return 0;
return text.trim().split(/\s+/).filter(word => word.length > 0).length;
}
function calculateClarity(text, hasConstraints) {
try {
if (!validateInput(text, 'string', SCORING_CONFIG.validation.minPromptLength)) {
return 0;
}
const config = SCORING_CONFIG.clarity;
let clarityScore = config.baseScore;
const totalWords = getWordCount(text);
// Proportional vague language penalty
const vagueCount = vagueWords.filter(rx => rx.test(text)).length;
if (config.proportionalScoring && totalWords > 0) {
const vagueRatio = vagueCount / totalWords;
clarityScore -= Math.min(vagueRatio * totalWords * config.vagueWordPenalty, config.maxVagueWordPenalty);
}
else {
clarityScore -= Math.min(vagueCount * config.vagueWordPenalty, config.maxVagueWordPenalty);
}
// Reward clarity indicators
const clarityCount = clarityIndicators.filter(rx => rx.test(text)).length;
clarityScore += Math.min(clarityCount * config.clarityIndicatorBonus, config.maxClarityIndicatorBonus);
// Reward specific numbers/measurements
const numberMatches = text.match(/\b\d+\s*(words?|characters?|minutes?|hours?|days?|%|percent|items?|steps?)\b/gi) || [];
clarityScore += Math.min(numberMatches.length * config.measurementBonus, config.maxMeasurementBonus);
// Reward concrete verbs
const verbCount = concreteVerbs.filter(rx => rx.test(text)).length;
clarityScore += Math.min(verbCount * config.verbBonus, config.maxVerbBonus);
// Penalize overly long sentences
const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > config.minSentenceLength);
if (sentences.length > 0) {
const avgSentenceLength = sentences.reduce((sum, s) => sum + s.length, 0) / sentences.length;
if (avgSentenceLength > config.sentenceLengthThreshold1) {
clarityScore -= config.longSentencePenalty1;
}
if (avgSentenceLength > config.sentenceLengthThreshold2) {
clarityScore -= config.longSentencePenalty2;
}
}
// Reward structured language (bullets, numbered lists)
if (/[-•*]\s+/g.test(text) || /\d+\.\s+/g.test(text)) {
clarityScore += config.structureBonus;
}
return Math.max(0, Math.min(1, clarityScore));
}
catch (error) {
console.error('Error calculating clarity:', error);
return SCORING_CONFIG.clarity.baseScore;
}
}
function calculateCompleteness(text, spec) {
try {
if (!validateInput(text, 'string') || !validateInput(spec, 'object')) {
return 0;
}
const config = SCORING_CONFIG.completeness;
let completenessScore = config.baseScore;
// Check for key components
if (spec.title && spec.title.length > config.titleMinLength) {
completenessScore += config.titleBonus;
}
if (spec.instructions && spec.instructions.length > 0) {
completenessScore += config.instructionsBonus;
}
if (spec.constraints && spec.constraints.length > 0) {
completenessScore += config.constraintsBonus;
}
if (spec.context?.domain) {
completenessScore += config.domainBonus;
}
if (spec.io?.format) {
completenessScore += config.formatBonus;
}
// Reward specific format requirements
if (spec.io?.contract?.sections && spec.io.contract.sections.length > 0) {
completenessScore += Math.min(spec.io.contract.sections.length * config.sectionsBonus, config.maxSectionsBonus);
}
// Check for completeness indicators in text
const completenessCount = completenessIndicators.filter(rx => rx.test(text)).length;
completenessScore += Math.min(completenessCount * config.completenessIndicatorBonus, config.maxCompletenessIndicatorBonus);
// Reward examples or concrete references
if (/\bexamples?\b/i.test(text) || /\bfor instance\b/i.test(text) || /\bsuch as\b/i.test(text)) {
completenessScore += config.exampleBonus;
}
// Check instruction depth using word count instead of character count
const instructionText = spec.instructions ? spec.instructions.join(' ') : '';
const instructionWordCount = getWordCount(instructionText);
if (instructionWordCount > config.depthThreshold1) {
completenessScore += config.depthBonus1;
}
if (instructionWordCount > config.depthThreshold2) {
completenessScore += config.depthBonus2;
}
if (instructionWordCount > config.depthThreshold3) {
completenessScore += config.depthBonus3;
}
// Penalize overly brief instructions
if (spec.instructions && spec.instructions.length === 1 &&
getWordCount(spec.instructions[0]) < config.shortInstructionThreshold) {
completenessScore -= config.shortInstructionPenalty;
}
return Math.max(0, Math.min(1, completenessScore));
}
catch (error) {
console.error('Error calculating completeness:', error);
return SCORING_CONFIG.completeness.baseScore;
}
}
function toLines(text) {
return text.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
}
// Very lightweight parser (rule-based) to keep MVP vendor-neutral
function draftFromText(prompt, domain) {
const lines = toLines(prompt);
const joined = lines.join(" ");
const spec = {
version: "1.0",
intent: (/translate/i.test(joined) ? "translate" :
/refactor|rewrite/i.test(joined) ? "refactor" :
/classify/i.test(joined) ? "classify" :
/explain/i.test(joined) ? "explain" :
/plan/i.test(joined) ? "plan" :
/compare/i.test(joined) ? "compare" :
/summari[sz]e/i.test(joined) ? "summarize" :
/generate|create|write/i.test(joined) ? "generate_code" :
"general"),
title: lines[0]?.slice(0, 80),
context: { domain },
instructions: lines,
constraints: [],
io: { format: "markdown", contract: {} },
ambiguities: [],
metrics: { clarity: 0.0, completeness: 0.0 },
notes: ""
};
// Extract word limit like "in 200 words"
const m = joined.match(/(\d{2,4})\s*words?/i);
if (m)
spec.constraints?.push(`max ${m[1]} words`);
// Common section hints
const sections = [];
if (/risk/i.test(joined))
sections.push("Risks");
if (/next\s*steps?/i.test(joined))
sections.push("NextSteps");
if (/highlights?|summary/i.test(joined))
sections.push("Highlights");
if (sections.length)
spec.io.contract = { ...(spec.io.contract || {}), sections };
// Dynamic clarity and completeness calculation
const clarity = calculateClarity(joined, !!spec.constraints?.length);
const completeness = calculateCompleteness(joined, spec);
spec.metrics = {
clarity: safeRound(clarity),
completeness: safeRound(completeness)
};
return spec;
}
function detectGaps(spec) {
const issues = [];
if (!spec.io?.format) {
issues.push({ path: "/io/format", message: "Missing output format", severity: "error" });
}
if (spec.io?.format === "markdown" && !spec.io?.contract?.sections?.length) {
issues.push({ path: "/io/contract/sections", message: "Missing markdown sections for output contract", severity: "warn" });
}
if (!spec.instructions?.length) {
issues.push({ path: "/instructions", message: "No instructions provided", severity: "error" });
}
const joined = (spec.instructions || []).join(" ");
if (hasVagueLanguage(joined)) {
issues.push({ path: "/instructions", message: "Vague language detected (optimize/better/quickly/etc.)", severity: "warn" });
}
if (!spec.constraints || spec.constraints.length === 0) {
issues.push({ path: "/constraints", message: "Consider adding constraints (word limit, style, tone)", severity: "info" });
}
if (spec.context && !spec.context.inputs?.length) {
issues.push({ path: "/context/inputs", message: "No inputs linked (file/url/text). Add at least one if applicable.", severity: "info" });
}
// Recalculate metrics if not present or seem outdated
const recalculatedClarity = calculateClarity(joined, !!spec.constraints?.length);
const recalculatedCompleteness = calculateCompleteness(joined, spec);
const clarity = typeof spec.metrics?.clarity === "number" ? spec.metrics.clarity : recalculatedClarity;
const completeness = typeof spec.metrics?.completeness === "number" ? spec.metrics.completeness : recalculatedCompleteness;
return { issues, score: { clarity: safeRound(clarity), completeness: safeRound(completeness) } };
}
// JSON Conversion Helper
function convertPromptToJson(refinedPrompt, domain, originalPrompt) {
// Split the refined prompt to extract base task and requirements
const parts = refinedPrompt.split(/\.\s*Requirements?:/i);
const baseTask = parts[0]?.trim() || refinedPrompt;
const requirementsText = parts[1]?.trim() || '';
const words = baseTask.split(/\s+/);
// Extract main task/action verbs
const actionVerbs = ['create', 'build', 'generate', 'design', 'implement', 'develop', 'write', 'make', 'add', 'fix', 'update', 'analyze', 'review', 'test', 'deploy', 'login', 'authenticate'];
const mainAction = words.find(word => actionVerbs.some(verb => word.toLowerCase().includes(verb))) || words[0];
// Extract subject/entity (usually noun phrases after the action)
const actionIndex = words.findIndex(word => actionVerbs.some(verb => word.toLowerCase().includes(verb)));
const subjectWords = actionIndex >= 0 ? words.slice(actionIndex + 1, actionIndex + 4) : words.slice(1, 4);
const subject = subjectWords.join(' ') || 'system';
// Extract constraints from base task
const constraintKeywords = ['with', 'using', 'for', 'that', 'which', 'including', 'containing', 'having'];
const constraints = [];
// Look for constraint patterns in base task
constraintKeywords.forEach(keyword => {
const keywordIndex = words.findIndex(word => word.toLowerCase() === keyword);
if (keywordIndex >= 0 && keywordIndex < words.length - 1) {
const constraintPhrase = words.slice(keywordIndex + 1, keywordIndex + 5).join(' ');
if (constraintPhrase)
constraints.push(constraintPhrase);
}
});
// Parse requirements from the refined prompt
const requirements = [];
if (requirementsText) {
// Extract specific requirements
if (requirementsText.includes('comprehensive response')) {
requirements.push('Provide comprehensive response');
}
if (requirementsText.includes('specific examples')) {
requirements.push('Include specific examples and actionable details');
}
if (requirementsText.includes('clear sections')) {
requirements.push('Structure response with clear sections');
}
if (requirementsText.includes('measurable outcomes')) {
requirements.push('Include measurable outcomes');
}
}
// Extract output structure
const outputStructure = [];
if (requirementsText.includes('Overview'))
outputStructure.push('Overview');
if (requirementsText.includes('Details'))
outputStructure.push('Details');
if (requirementsText.includes('Next Steps'))
outputStructure.push('Next Steps');
// Default structure if none found
if (outputStructure.length === 0) {
outputStructure.push('Overview', 'Details', 'Next Steps');
}
return {
task: mainAction || 'process request',
subject: subject || 'system',
constraints: constraints.length > 0 ? constraints : [],
requirements: requirements.length > 0 ? requirements : ['Provide comprehensive response'],
outputFormat: {
structure: outputStructure,
type: 'structured response',
includeExamples: requirementsText.includes('examples'),
includeMeasurableOutcomes: requirementsText.includes('measurable')
},
domain: domain || 'general',
originalPrompt: originalPrompt || baseTask,
refinedPrompt: refinedPrompt
};
}
// --- Server ---
const server = new McpServer({ name: "StructuralThinking", version: "0.2.0" });
// Tool: st_refine (Structural Thinking Analysis)
server.registerTool("st_refine", {
title: "Prompt Refinement with Structural Analysis (Review Only - No Auto-Execution)",
description: "Analyzes and refines prompts for review. Shows improved prompt and optional JSON structure for manual copy/paste. Does NOT automatically execute the refined prompt - user must review and manually use the improved version if desired.",
inputSchema: {
prompt: z.string(),
domain: z.enum(["code", "docs", "data", "product", "research"]).optional(),
includeValidation: z.boolean().optional().default(true),
includeImprovements: z.boolean().optional().default(true),
json: z.boolean().optional().default(false)
}
}, async ({ prompt, domain, includeValidation = true, includeImprovements = true, json = false }) => {
try {
// Debug logging for JSON parameter
console.error(`[DEBUG] st_refine called with json parameter: ${json} (type: ${typeof json})`);
// Enhanced input validation with better error reporting
const promptValidation = validateInputDetailed(prompt, 'string', SCORING_CONFIG.validation.minPromptLength, SCORING_CONFIG.validation.maxPromptLength);
if (!promptValidation.valid) {
return {
content: [{ type: "text", text: `❌ **Input Validation Error**\n\n**Issue:** ${promptValidation.error}\n**Code:** ${promptValidation.code}\n\n**Expected:** String between ${SCORING_CONFIG.validation.minPromptLength}-${SCORING_CONFIG.validation.maxPromptLength} characters\n**Received:** ${typeof prompt} (${typeof prompt === 'string' ? prompt.length : 'N/A'} characters)\n\n**Fix:** Provide a valid prompt string within the specified length range.` }]
};
}
const validDomains = ["code", "docs", "data", "product", "research"];
if (domain && !validDomains.includes(domain)) {
return {
content: [{ type: "text", text: `❌ **Invalid Domain**\n\n**Issue:** Domain '${domain}' is not supported\n**Valid Options:** ${validDomains.join(', ')}\n\n**Fix:** Choose one of the supported domains above.` }]
};
}
// Step 1: Transform prompt to structural thinking spec
const spec = draftFromText(prompt, domain);
const specValid = validateStructuralThinking(spec);
const specErrors = specValid ? [] : (validateStructuralThinking.errors || []);
// Step 2: Detect gaps and issues
const gapAnalysis = detectGaps(spec);
// Step 3: Validate (if requested)
let validationResults = null;
if (includeValidation) {
const valid = validateStructuralThinking(spec);
const errors = valid ? [] : (validateStructuralThinking.errors || []);
const warnings = [];
if (!spec?.constraints || spec.constraints.length === 0) {
warnings.push({ path: "/constraints", message: "No constraints specified" });
}
if (!spec?.title || spec.title.length < SCORING_CONFIG.completeness.titleMinLength) {
warnings.push({ path: "/title", message: "Title is missing or too short" });
}
if (!spec?.context?.domain) {
warnings.push({ path: "/context/domain", message: "Domain not specified" });
}
if (spec?.instructions && getWordCount(spec.instructions.join(' ')) < SCORING_CONFIG.completeness.depthThreshold1) {
warnings.push({ path: "/instructions", message: "Instructions may be too brief" });
}
validationResults = { valid, errors, warnings };
}
// Step 4: Generate improvements (if requested)
let improvementSuggestions = null;
if (includeImprovements) {
const patches = [];
// Suggest adding sections for markdown format
if (spec?.io?.format === "markdown" && !(spec?.io?.contract?.sections?.length)) {
patches.push({
op: "add",
path: "/io/contract",
value: { sections: ["Highlights", "Details", "NextSteps"] }
});
}
// Suggest adding title if missing or too short
if (!spec?.title || spec.title.length < SCORING_CONFIG.completeness.titleMinLength) {
patches.push({
op: spec?.title ? "replace" : "add",
path: "/title",
value: "Enhanced Structural Thinking Specification"
});
}
// Suggest adding domain if missing
if (!spec?.context?.domain) {
patches.push({
op: "add",
path: "/context/domain",
value: domain || "general"
});
}
// Suggest adding more detailed instructions if too brief
const instructionText = spec?.instructions ? spec.instructions.join(' ') : '';
if (getWordCount(instructionText) < SCORING_CONFIG.completeness.depthThreshold1) {
patches.push({
op: "replace",
path: "/instructions",
value: spec?.instructions ? [
...spec.instructions,
"Provide specific examples where applicable",
"Include measurable criteria for success"
] : ["Define clear, actionable instructions", "Include specific requirements and constraints"]
});
}
improvementSuggestions = { patches, improvementCount: patches.length };
}
// Generate improved prompt based on analysis
let improvedPrompt = prompt;
let improvementNotes = [];
// Apply improvements to create better prompt
if (improvementSuggestions && improvementSuggestions.patches.length > 0) {
// Add constraints if missing
const hasConstraints = spec?.constraints && spec.constraints.length > 0;
if (!hasConstraints) {
improvedPrompt = `${improvedPrompt}. Requirements: Provide a comprehensive response with specific examples and actionable details.`;
improvementNotes.push("Added clarity requirements");
}
// Add output format if missing
const hasSections = spec?.io?.contract?.sections && spec.io.contract.sections.length > 0;
if (!hasSections) {
improvedPrompt = `${improvedPrompt} Structure the response with clear sections: Overview, Details, and Next Steps.`;
improvementNotes.push("Added output structure");
}
// Add success criteria if instructions are brief
const instructionText = spec?.instructions ? spec.instructions.join(' ') : '';
if (getWordCount(instructionText) < SCORING_CONFIG.completeness.depthThreshold1) {
improvedPrompt = `${improvedPrompt} Include specific examples and measurable outcomes where applicable.`;
improvementNotes.push("Added success criteria");
}
}
// Create user-friendly response with analysis first, then improved prompt
const qualityScore = safeRound((gapAnalysis.score.clarity + gapAnalysis.score.completeness) / 2);
const isReady = gapAnalysis.issues.filter(issue => issue.severity === "error").length === 0 &&
gapAnalysis.score.clarity > 0.6 &&
gapAnalysis.score.completeness > 0.6;
// Enhanced output formatting with better readability and icons
const qualityLevel = qualityScore >= 0.8 ? '🟢 Excellent' : qualityScore >= 0.6 ? '🟡 Good' : '🔴 Needs Work';
const readinessIcon = isReady ? '✅' : '⚠️';
// Enhanced vague language analysis for user feedback
const vagueAnalysis = analyzeVagueLanguage(prompt);
const vagueSection = vagueAnalysis.hasVague ?
`\n\n### 🎯 **Vague Language Detected**\n${vagueAnalysis.vagueTerms.map(term => `- "${term}"`).join('\n')}\n\n**Suggestions:**\n${vagueAnalysis.suggestions.map(s => `- ${s}`).join('\n')}` : '';
let userResponse = `# 🔍 ST_REFINE - PROMPT IMPROVEMENT SUGGESTIONS
> **🎯 READY TO USE:** Your refined prompt is ready below! Choose your next action:
>
> - **🔄 Copy & Run** - Copy the improved prompt and paste it in a new message
> - **✏️ Edit & Run** - Copy the prompt, make your changes, then run your version
> - **📊 Use JSON** - Copy the structured JSON format for advanced use cases
## 📊 Analysis Summary
**Quality Score:** ${qualityLevel} (${qualityScore}/1.0)
**Ready for Implementation:** ${readinessIcon} ${isReady ? 'Yes' : 'Needs refinement'}
**Domain Context:** ${domain || 'General'}
**Word Count:** ${getWordCount(prompt)} words
**Improvements Applied:** ${improvementNotes.join(', ') || 'None needed'}
### 🔍 **Detailed Metrics**
- **Clarity Score:** ${safeRound(gapAnalysis.score.clarity)}/1.0
- **Completeness Score:** ${safeRound(gapAnalysis.score.completeness)}/1.0
- **Issues Found:** ${gapAnalysis.issues.length}
- **Suggestions Available:** ${improvementSuggestions?.improvementCount || 0}${vagueSection}
### ⚠️ **Issues Detected**
${gapAnalysis.issues.length > 0 ? gapAnalysis.issues.map(issue => {
const icon = issue.severity === 'error' ? '🔴' : issue.severity === 'warn' ? '🟡' : '🔵';
return `${icon} **${issue.severity.toUpperCase()}:** ${issue.message}\n *Location:* \`${issue.path}\``;
}).join('\n\n') : '🎉 No issues found'}
### 💡 **Improvement Suggestions**
${improvementSuggestions?.patches && improvementSuggestions.patches.length > 0 ?
improvementSuggestions.patches.map((patch, i) => {
const action = patch.op === 'add' ? '➕' : patch.op === 'replace' ? '🔄' : '➖';
let valueStr;
if (typeof patch.value === 'object') {
// Format JSON with proper indentation and readable structure
if (Array.isArray(patch.value)) {
valueStr = '[\n' + patch.value.map(item => ` "${item}"`).join(',\n') + '\n]';
}
else {
valueStr = JSON.stringify(patch.value, null, 2);
}
}
else {
valueStr = patch.value;
}
return `${action} **${patch.op.toUpperCase()}** at \`${patch.path}\`:\n\`\`\`json\n${valueStr}\n\`\`\``;
}).join('\n\n') : '✨ No suggestions needed'}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
## 🎯 IMPROVED PROMPT - READY TO USE
> **⚡ SUGGESTION:** Your refined prompt is ready! Click inside the box below to select all text, then copy and paste it into a new message.
\`\`\`markdown
${improvedPrompt}
\`\`\`
### 🚀 **Choose Your Next Action:**
| Action | Instructions |
|--------|-------------|
| 🔄 **Copy & Run** | Select all text above → Copy (Cmd/Ctrl+C) → Paste in new message → Send |
| ✏️ **Edit & Run** | Copy text above → Modify as needed → Paste in new message → Send |
| 📊 **Use JSON** | Scroll down to JSON section → Copy structured format instead |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`;
// Add JSON conversion if requested
console.error(`[DEBUG] Checking JSON parameter: ${json}, type: ${typeof json}`);
if (json === true) {
console.error(`[DEBUG] Adding JSON section to response`);
userResponse += `
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
## 📋 STRUCTURED JSON FORMAT - ADVANCED OPTION
> **🔧 ALTERNATIVE:** Use this JSON structure for prompt engineering, API integration, or template-based generation.
\`\`\`json
${JSON.stringify(convertPromptToJson(improvedPrompt, domain, prompt), null, 2)}
\`\`\`
### 🛠️ **JSON Action Options:**
| Use Case | Instructions |
|----------|-------------|
| 📋 **Template Creation** | Copy JSON → Use for consistent prompt generation |
| 🔌 **API Integration** | Copy JSON → Feed into prompt engineering APIs |
| 📝 **Field Extraction** | Copy specific fields (task, subject, requirements) |
| 🔄 **Prompt Iteration** | Modify JSON structure → Generate new prompts |
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`;
}
else {
console.error(`[DEBUG] JSON parameter is false, not adding JSON section`);
// Add debug information to help troubleshoot
userResponse += `
---
**Debug Info:** JSON parameter received as: ${json} (type: ${typeof json})
`;
}
// IMPORTANT: This tool should ONLY show analysis and refined prompt for review
// It should NOT auto-execute the refined prompt - user must review first
return {
content: [{
type: "text",
text: userResponse
}]
};
}
catch (error) {
console.error('Error in st_refine:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
const errorStack = error instanceof Error ? error.stack : 'No stack trace available';
return {
content: [{ type: "text", text: `🚨 **Internal Server Error**
**What happened:** An unexpected error occurred during structural thinking analysis
**Error Details:**
- **Message:** ${errorMessage}
- **Type:** ${error instanceof Error ? error.constructor.name : typeof error}
**Troubleshooting:**
1. Check if your prompt contains any special characters that might cause parsing issues
2. Ensure the prompt length is within acceptable limits (3-10,000 characters)
3. Try simplifying the prompt and running the analysis again
4. If the error persists, this may be a server-side issue
**For developers:**
\`\`\`
${errorStack}
\`\`\`
**Support:** Contact the system administrator if this error continues to occur.` }]
};
}
});
// Start server over stdio
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("[StructuralThinking MCP] Server connected over stdio");