Skip to main content
Glama
dacebt

Prompt Cleaner MCP Server

by dacebt

normalize-prompt

Normalize and restructure raw prompts for clarity and consistency. Adjust formatting, tighten language, and preflight prompts for optimal use in code or general contexts.

Instructions

Alias of cleaner. Keywords: normalize, restructure, clarify, tighten, format, preflight. Same input/output schema as 'cleaner'.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
modeNoRetouching mode; default 'general'. Use 'code' only for code-related prompts.
promptYesRaw user prompt
temperatureNoSampling temperature (0-2); default 0.2

Implementation Reference

  • Handler for the 'normalize-prompt' tool (alias of cleaner). Parses input using RetouchInput schema, calls retouchPrompt, validates output with RetouchOutput schema, logs, and returns JSON content.
    case "cleaner": case "sanitize-text": case "normalize-prompt": { const parsed = RetouchInput.parse(args); const result = await retouchPrompt(parsed); const safe = RetouchOutput.parse(result); logger.info("retouch.prompt", { elapsed_ms: Date.now() - start, input_len: parsed.prompt.length, preview: logger.preview(parsed.prompt), request_id: parsed.requestId, }); return jsonContent(safe); }
  • src/tools.ts:56-74 (registration)
    Registration of the 'normalize-prompt' tool in listTools(), including name, description, and input schema (identical to cleaner's).
    { name: "normalize-prompt", description: "Alias of cleaner. Keywords: normalize, restructure, clarify, tighten, format, preflight. Same input/output schema as 'cleaner'.", inputSchema: { type: "object", properties: { prompt: { type: "string", description: "Raw user prompt" }, mode: { type: "string", enum: ["code", "general"], description: "Retouching mode; default 'general'. Use 'code' only for code-related prompts.", }, temperature: { type: "number", description: "Sampling temperature (0-2); default 0.2" }, }, required: ["prompt"], }, },
  • Core implementation of prompt normalization/retouching logic (retouchPrompt). Loads system prompt, calls LLM with retries for JSON output, extracts/parses JSON, applies redactions, and returns structured output. Called by normalize-prompt handler.
    export async function retouchPrompt(input: RetouchInputT): Promise<RetouchOutputT> { const start = Date.now(); const system = await loadCleanerSystemPrompt(); const baseTemperature = input.temperature ?? 0; const userBody = `MODE: ${input.mode || "general"}\nRAW_PROMPT:\n${input.prompt}`; const sys = system; class CleanerNonJsonError extends Error { constructor(message = "Cleaner returned non-JSON") { super(message); this.name = "CleanerNonJsonError"; } } // Retry loop for content-level non-JSON responses const maxAttempts = Math.max(1, 1 + (config.contentMaxRetries ?? 0)); let lastErr: Error | undefined; for (let attempt = 1; attempt <= maxAttempts; attempt++) { // Escalate on retries: enforce temperature 0 and stricter system instructions const strictSuffix = attempt > 1 ? "\n\nSTRICT OUTPUT MODE: Respond with EXACTLY ONE JSON object and nothing else. No prose. No code fences. No prefix/suffix." : ""; const sysAttempt = sys + strictSuffix; const tempAttempt = attempt > 1 ? 0 : baseTemperature; const response = await chatCompletions( { model: config.model, temperature: tempAttempt, max_tokens: 600, messages: [ { role: "system", content: sysAttempt }, { role: "user", content: userBody }, ], }, { requestId: input.requestId }, ); const content = response.choices?.[0]?.message?.content ?? ""; const initial = redactSecrets(content); const redactedText = initial.text; try { const obj = extractFirstJsonObject(redactedText); const parsed = RetouchOutput.safeParse(obj); if (!parsed.success) { throw new Error("shape-error"); } const { value, redactions } = ensureNoSecretsInObject(parsed.data); const totalRedactions = initial.count + redactions; const result: RetouchOutputT = { ...value, redactions: totalRedactions > 0 ? Array(totalRedactions).fill("[REDACTED]") : value.redactions, }; logger.info("retouch.prompt", { elapsed_ms: Date.now() - start, input_len: input.prompt.length, preview: logger.preview(input.prompt), request_id: input.requestId, attempts: attempt, outcome: "ok", }); return result; } catch (e: any) { lastErr = new CleanerNonJsonError("Cleaner returned non-JSON"); if (attempt < maxAttempts) { const base = config.backoffMs ?? 250; const jitter = config.backoffJitter ?? 0.2; const exp = Math.pow(2, attempt - 1); const rand = 1 + (Math.random() * 2 - 1) * jitter; // 1 +/- jitter const delay = Math.max(0, Math.floor(base * exp * rand)); logger.warn("retouch.retry", { request_id: input.requestId, attempt, delay_ms: delay, reason: "non-json", }); await new Promise((r) => setTimeout(r, delay)); continue; } } } logger.info("retouch.prompt", { elapsed_ms: Date.now() - start, input_len: input.prompt.length, preview: logger.preview(input.prompt), request_id: input.requestId, attempts: maxAttempts, outcome: "error", reason: "non-json", }); throw lastErr ?? new CleanerNonJsonError("Cleaner returned non-JSON"); }
  • Zod schemas for input (RetouchInput) and output (RetouchOutput) validation used by normalize-prompt and cleaner tools.
    export const RetouchInput = z.object({ prompt: z.string().min(1), mode: z.enum(["code", "general"]).optional(), temperature: z.number().min(0).max(2).optional(), requestId: z.string().uuid().optional(), }); export const RetouchOutput = z.object({ retouched: z.string().min(1), notes: z.array(z.string()).optional(), openQuestions: z.array(z.string()).optional(), risks: z.array(z.string()).optional(), redactions: z.array(z.literal("[REDACTED]")).optional(), });

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dacebt/prompt-cleaner-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server