Skip to main content
Glama
dacebt

Prompt Cleaner MCP Server

by dacebt

normalize-prompt

Standardizes and refines prompts by removing sensitive data, restructuring content, and clarifying formatting for consistent LLM processing.

Instructions

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

Input Schema

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

Implementation Reference

  • src/tools.ts:56-74 (registration)
    Registration of the 'normalize-prompt' tool in the listTools() function, including its description and input schema (identical to 'cleaner').
    {
      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"],
      },
    },
  • Dispatch handler in callTool() for 'normalize-prompt' (shared with 'cleaner' and 'sanitize-text'), which validates input using RetouchInput schema, calls retouchPrompt, validates output, logs, and returns JSON.
    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);
    }
  • Core implementation of prompt normalization in retouchPrompt(): loads system prompt from file, calls LLM (chatCompletions) with retry logic for JSON output, extracts/parses JSON, applies redaction, validates with RetouchOutput schema, and logs extensively.
    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 RetouchInput and RetouchOutput used for validation in the normalize-prompt (cleaner) tool handler.
    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(),
    });

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