{"record":{"id":"9f291ab0f37073b9","repo":"PrefectHQ/fastmcp","slug":"function-missing-parameters-required-after-transfo","errorCode":null,"errorMessage":"Function missing parameters required after transformation: {', '.join(sorted(missing_params))}. Function declares: {', '.join(sorted(fn_params))}","messagePattern":"Function missing parameters required after transformation: (.+?)\\. Function declares: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/tools/tool_transform.py","lineNumber":546,"sourceCode":"            final_schema = schema\n        else:\n            # parsed fn is not none here\n            parsed_fn = cast(ParsedFunction, parsed_fn)\n            # User provided custom function - merge schemas\n            final_fn = transform_fn\n\n            has_kwargs = cls._function_has_kwargs(transform_fn)\n\n            # Validate function parameters against transformed schema\n            fn_params = set(parsed_fn.input_schema.get(\"properties\", {}).keys())\n            transformed_params = set(schema.get(\"properties\", {}).keys())\n\n            if not has_kwargs:\n                # Without **kwargs, function must declare all transformed params\n                # Check if function is missing any parameters required after transformation\n                missing_params = transformed_params - fn_params\n                if missing_params:\n                    raise ValueError(\n                        f\"Function missing parameters required after transformation: \"\n                        f\"{', '.join(sorted(missing_params))}. \"\n                        f\"Function declares: {', '.join(sorted(fn_params))}\"\n                    )\n\n                # ArgTransform takes precedence over function signature\n                # Start with function schema as base, then override with transformed schema\n                final_schema = cls._merge_schema_with_precedence(\n                    parsed_fn.input_schema, schema\n                )\n            else:\n                # With **kwargs, function can access all transformed params\n                # ArgTransform takes precedence over function signature\n                # No validation needed - kwargs makes everything accessible\n\n                # Start with function schema as base, then override with transformed schema\n                final_schema = cls._merge_schema_with_precedence(\n                    parsed_fn.input_schema, schema","sourceCodeStart":528,"sourceCodeEnd":564,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/tools/tool_transform.py#L528-L564","documentation":"When a custom replacement function doesn't accept **kwargs, from_tool() requires the function's signature to cover every parameter that exists after transformation. If transformed_params contains names the function doesn't declare, ValueError is raised listing the missing parameters and what the function declares.","triggerScenarios":"Providing fn= to from_tool with a fixed-signature function while transform_args renames/adds parameters the function lacks; the transform makes a hidden param's new name absent from the function signature; forgetting that ArgTransform renames change required parameter names.","commonSituations":"Writing a slim wrapper function and forgetting a renamed argument; transformation later updated to add new params while the custom fn wasn't updated.","solutions":["Add the missing parameter names to the replacement function's signature.","Or accept **kwargs in the replacement function so all transformed params are forwarded.","Or adjust transform_args so the resulting parameter names match the function's declared parameters."],"exampleFix":"// before\nasync def run(query): ...\nfrom_tool(tool, fn=run, transform_args={\"query\": ArgTransform(name=\"q\")})\n\n// after\nasync def run(q): ...\n# or: async def run(**kwargs): ...","handlingStrategy":"validation","validationCode":"import inspect\n\ndef check_fn_covers(tool, transform_args: dict, fn) -> None:\n    transformed = set(transform_args) | set(tool.parameters[\"properties\"])\n    sig = inspect.signature(fn)\n    if sig.parameters.get(\"kwargs\").kind is not inspect.Parameter.VAR_KEYWORD:\n        missing = {p for p in transformed if p not in sig.parameters}\n        if missing:\n            raise ValueError(f\"fn missing params: {sorted(missing)}\")","typeGuard":null,"tryCatchPattern":"try:\n    t = ParentTool.from_tool(tool, fn=fn, transform_args=ta)\nexcept ValueError as e:\n    if \"missing parameters\" in str(e):\n        log.error(\"Update fn signature or add **kwargs: %s\", e)\n    raise","preventionTips":["Accept **kwargs in replacement functions to stay forward-compatible with transform changes.","Keep the fn signature and transform_args in the same commit so they can't drift.","Mirror the final transformed parameter names exactly in the fn signature."],"tags":["python","tool-transform","signature-mismatch"],"backgroundTag":"function-signature-mismatch","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}