Skip to main content
Glama
dacebt

Prompt Cleaner MCP Server

by dacebt

sanitize-text

Remove sensitive information and normalize formatting from prompts using configurable sanitization modes for code or general content.

Instructions

Alias of cleaner. Keywords: sanitize, scrub, redact, filter, pii, normalize, preprocess. 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

  • Core handler logic for sanitizing text: loads system prompt, calls LLM via chatCompletions with user input, extracts JSON, validates with RetouchOutput schema, applies redaction checks, with retry logic for non-JSON responses.
    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");
    }
  • Tool dispatch handler in callTool() that handles 'sanitize-text' by parsing input with RetouchInput schema, calling retouchPrompt, parsing output, logging, and returning 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);
    }
  • src/tools.ts:37-55 (registration)
    Registration of the 'sanitize-text' tool in the listTools() array, defining its name, description, and input schema (alias to cleaner).
    {
      name: "sanitize-text",
      description:
        "Alias of cleaner. Keywords: sanitize, scrub, redact, filter, pii, normalize, preprocess. 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"],
      },
    },
  • Zod schema definition for tool input validation (RetouchInput), used by parse() in 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(),
    });
  • Zod schema definition for tool output validation (RetouchOutput), used by parse() in handler.
    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(),
    });
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It hints at functionality through keywords (e.g., 'pii', 'redact') but doesn't explain what the tool actually does behaviorally—such as whether it modifies input, returns cleaned output, or handles errors. This leaves critical operational traits unspecified.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is brief and front-loaded with key information (alias and keywords), but the second sentence about the schema is somewhat redundant given the structured input. It avoids unnecessary elaboration, though it could be more streamlined by integrating the alias and keyword info more cohesively.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given no annotations and no output schema, the description is incomplete for a tool with 3 parameters. It fails to explain what the tool returns or how it behaves, relying too heavily on the schema and leaving gaps in understanding the tool's overall functionality and results, which is inadequate for effective agent use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents parameters like 'prompt', 'mode', and 'temperature'. The description adds no additional semantic context beyond stating 'Same input/output schema as cleaner', which doesn't enhance understanding of parameter purposes or interactions, meeting the baseline for high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states this is an 'alias of cleaner' and lists keywords like 'sanitize, scrub, redact, filter, pii, normalize, preprocess', which gives a general sense of purpose. However, it doesn't specify a clear verb+resource combination (e.g., 'sanitize text by removing PII') and doesn't distinguish it from its sibling 'cleaner' beyond stating it's an alias, leaving the relationship ambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no explicit guidance on when to use this tool versus alternatives like 'cleaner' or 'normalize-prompt'. It mentions it's an alias of 'cleaner' but doesn't clarify if they are interchangeable or if there are specific contexts favoring one over the other, offering minimal usage direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other 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