Skip to content

Request Normalizer

The Request Normalizer is a data-driven engine that sits between the MCP transport layer and tool handlers. It automatically corrects parameter mismatches that occur when AI clients (Claude Desktop, Claude Code) send parameters with wrong names, wrong types, or unexpected formats.

Introduced in v4.0.2 to address recurring patterns documented in Bug #22.

  1. MCP request arrives with tool name and arguments
  2. Normalizer looks up applicable rules (tool-specific + wildcard)
  3. Each matching rule is applied in order
  4. Corrected arguments are passed to the tool handler
  5. Applied normalizations are recorded in the audit log

All normalization happens transparently — the tool handler receives clean arguments without knowing corrections were made.


The normalizer supports 6 rule types:

TypeDescriptionExample
param_aliasRename a top-level parameterold_strold_text
param_defaultSet a default value if parameter is missingMissing id → auto-generated
type_coerceConvert a string value to the correct type"true"true (bool)
json_accept_bothAccept a parameter as raw JSON or string[{...}] or "[{...}]" both work
nested_aliasRename a field inside a JSON payloadStep typeaction
nested_defaultSet default for missing field inside JSON array itemsStep missing idstep-0, step-1, etc.

Rule IDToolFromTo
edit-old_stredit_fileold_strold_text
edit-new_stredit_filenew_strnew_text

Claude Desktop sometimes sends old_str/new_str instead of the expected old_text/new_text.

Rule IDToolsParameter
force-bool-coerceAll (*)force
dry_run-bool-coerceAll (*)dry_run
count_only-bool-coercesearch_filescount_only
permanent-bool-coercedelete_filepermanent
whole_word-bool-coerceedit_file, search_fileswhole_word
case_sensitive-bool-coercesearch_files, edit_filecase_sensitive
include_content-bool-coercesearch_filesinclude_content
include_context-bool-coercesearch_filesinclude_context
recursive-bool-coercelist_directory, search_filesrecursive

MCP transport delivers all values as strings. These rules convert "true"/"false"/"1"/"0" to native boolean values.

Rule IDToolDescription
multi_edit-edits-coercemulti_editAccept edits_json as raw JSON array or string
pipeline-type-aliasbatch_operationsRename typeaction inside pipeline steps
pipeline-auto-idbatch_operationsAuto-generate step-0, step-1, etc. for steps missing id

Add custom rules via --normalizer-rules rules.json:

[
{
"id": "my-custom-alias",
"tools": ["read_file"],
"type": "param_alias",
"from": "filename",
"to": "path"
},
{
"id": "my-default-encoding",
"tools": ["read_file"],
"type": "param_default",
"from": "encoding",
"value": "utf-8"
}
]

External rules are merged after built-in rules. Both sets apply to every request.

{
"id": "string (required, unique)",
"tools": ["tool_name", ...],
"type": "param_alias | param_default | type_coerce | json_accept_both | nested_alias | nested_default",
"from": "source parameter name",
"to": "target parameter name (alias types)",
"coerce_to": "bool | int | float (type_coerce only)",
"in_payload": "JSON payload param name (nested_* types)",
"array_path": "array path: '[]' or 'steps[]' (nested_* types)",
"value": "default value, supports '{{index}}' template (param_default, nested_default)"
}

Use "tools": ["*"] to apply a rule to all tools.


When --log-dir is set, the normalizer writes normalizer_stats.json with:

{
"total_processed": 5000,
"total_normalized": 1200,
"last_updated": "2026-03-16T10:30:00Z",
"by_tool": {
"edit_file": { "processed": 800, "normalized": 350 },
"search_files": { "processed": 1200, "normalized": 400 }
},
"by_rule": {
"force-bool-coerce": { "rule_id": "force-bool-coerce", "type": "type_coerce", "hits": 500, "tools": ["edit_file", "delete_file"] },
"edit-old_str": { "rule_id": "edit-old_str", "type": "param_alias", "hits": 200, "tools": ["edit_file"] }
},
"recent_normalizations": [
{
"ts": "2026-03-16T10:29:55Z",
"tool": "edit_file",
"applied": [
{ "rule_id": "edit-old_str", "type": "param_alias", "param": "old_str", "from": "old_str", "to": "old_text" }
]
}
]
}

These statistics are also visible in the Dashboard Normalizer page.


When a normalization is applied, it appears in the audit log entry:

{
"ts": "2026-03-16T10:29:55Z",
"tool": "edit_file",
"status": "ok",
"duration_ms": 12,
"norms": [
{ "rule_id": "edit-old_str", "type": "param_alias", "param": "old_str", "from": "old_str", "to": "old_text" },
{ "rule_id": "force-bool-coerce", "type": "type_coerce", "param": "force", "from": "\"true\"", "to": "true" }
]
}

This allows tracking which rules fire and how often, useful for identifying patterns in client behavior.


{
"mcpServers": {
"filesystem-ultra": {
"command": "C:\\path\\to\\filesystem-ultra-v4.exe",
"args": [
"--normalizer-rules", "C:\\config\\normalizer-rules.json",
"--log-dir", "C:\\Logs\\MCP",
"C:\\project"
]
}
}
}
FlagDescription
--normalizer-rulesPath to external rules JSON file (optional)
--log-dirEnables normalizer stats persistence (optional)

The normalizer is always active with its 14 built-in rules. No flag is needed to enable it.



Last updated: March 2026 Version: 4.1.0