Summary
The sample currently uses @app.generic_trigger(type="mcpToolTrigger", ...) and @app.generic_input_binding / @app.generic_output_binding decorators with a manual ToolProperty helper class and JSON-serialized toolProperties. As of azure-functions==1.25.0b2, first-class MCP decorators are available that dramatically simplify this code. The sample should be updated to use them.
Motivation
- The current code requires a boilerplate
ToolProperty class, manual JSON serialization of tool properties, and parsing context JSON to extract arguments — all of which the new decorators eliminate.
- The new decorators infer tool properties (name, type, description) directly from the Python function signature and docstrings, aligning with standard Python patterns.
- This sample is a quickstart template; it should showcase the latest recommended API surface.
Prerequisites / Environment Changes
src/requirements.txt
Pin the new beta version:
azure-functions==1.25.0b2
src/local.settings.json
Add the required worker isolation setting:
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "python",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"PYTHON_ISOLATE_WORKER_DEPENDENCIES": "1"
}
}
Deployed App Settings (infra / bicep / azd)
Ensure PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1 is also set in the deployed Function App configuration (bicep / app settings).
Required Code Changes in src/function_app.py
1. Remove the ToolProperty helper class and all related boilerplate
Delete the entire ToolProperty class definition, the tool_properties_save_snippets_object / tool_properties_get_snippets_object lists, and the tool_properties_save_snippets_json / tool_properties_get_snippets_json JSON serialization lines. These are no longer needed — the new decorator infers tool properties from the function signature.
2. Migrate hello_mcp
Current:
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="hello_mcp",
description="Hello world.",
toolProperties="[]",
)
def hello_mcp(context) -> None:
return "Hello I am MCPTool!"
New:
@app.mcp_tool()
def hello_mcp() -> str:
"""Hello world."""
return "Hello I am MCPTool!"
Key changes:
@app.generic_trigger(...) → @app.mcp_tool()
- The tool
description is now inferred from the function docstring.
- The
toolName is inferred from the function name.
- No
context arg is needed when the tool has no properties.
- Return type should be
str, not None.
3. Migrate get_snippet
Current:
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="get_snippet",
description="Retrieve a snippet by name.",
toolProperties=tool_properties_get_snippets_json,
)
@app.generic_input_binding(arg_name="file", type="blob", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, context) -> str:
snippet_content = file.read().decode("utf-8")
logging.info(f"Retrieved snippet: {snippet_content}")
return snippet_content
New:
@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.blob_input(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, snippetname: str) -> str:
"""Retrieve a snippet by name from Azure Blob Storage."""
snippet_content = file.read().decode("utf-8")
logging.info(f"Retrieved snippet: {snippet_content}")
return snippet_content
Key changes:
@app.generic_trigger(...) → @app.mcp_tool()
@app.generic_input_binding(...) → @app.blob_input(...); the type="blob" parameter is no longer needed (it is implicit).
toolProperties JSON is replaced by @app.mcp_tool_property(...) input binding and/or function parameter type hints. Args in the function signature that don't map to another binding are automatically treated as MCP tool properties.
context parameter is removed; MCP arguments are received as named function parameters (snippetname).
4. Migrate save_snippet
Current:
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="save_snippet",
description="Save a snippet with a name.",
toolProperties=tool_properties_save_snippets_json,
)
@app.generic_output_binding(arg_name="file", type="blob", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], context) -> str:
content = json.loads(context)
snippet_name_from_args = content["arguments"][_SNIPPET_NAME_PROPERTY_NAME]
snippet_content_from_args = content["arguments"][_SNIPPET_PROPERTY_NAME]
if not snippet_name_from_args:
return "No snippet name provided"
if not snippet_content_from_args:
return "No snippet content provided"
file.set(snippet_content_from_args)
logging.info(f"Saved snippet: {snippet_content_from_args}")
return f"Snippet '{ snippet_content_from_args}' saved successfully"
New:
@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.mcp_tool_property(arg_name="snippet", description="The content of the snippet.")
@app.blob_output(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], snippetname: str, snippet: str) -> str:
"""Save a snippet to Azure Blob Storage."""
if not snippetname:
return "No snippet name provided"
if not snippet:
return "No snippet content provided"
file.set(snippet)
logging.info(f"Saved snippet: {snippetname}")
return f"Snippet '{ snippetname}' saved successfully with content: {snippet}"
Key changes:
@app.generic_trigger(...) → @app.mcp_tool()
@app.generic_output_binding(...) → @app.blob_output(...); type="blob" is implicit.
- Manual
json.loads(context) and content["arguments"][...] extraction is eliminated — arguments arrive as named function parameters.
@app.mcp_tool_property(...) decorators explicitly set descriptions for each tool property. These are optional if the defaults are acceptable; properties are inferred from the signature.
5. Clean up imports
The json import can likely be removed since context is no longer manually parsed. Keep logging and azure.functions as func.
How new decorator property inference works (for reference)
| Scenario |
Behavior |
Arg has a type hint but no mcp_tool_property |
Type and name inferred from signature; description left empty |
Arg has mcp_tool_property |
Values from the decorator take precedence over inferred values |
Both type hint and property_type in decorator |
Decorator value wins |
Arg matches another binding's arg_name (e.g. file in blob_output) |
Excluded from MCP tool properties automatically |
Arg has Optional[T] type hint |
Treated as an optional tool property |
Documentation Updates
The README.md Source Code section includes inline code snippets of the old API. These must be updated to reflect the new decorators. The prerequisites section should also note the minimum azure-functions version.
Checklist
Summary
The sample currently uses
@app.generic_trigger(type="mcpToolTrigger", ...)and@app.generic_input_binding/@app.generic_output_bindingdecorators with a manualToolPropertyhelper class and JSON-serializedtoolProperties. As ofazure-functions==1.25.0b2, first-class MCP decorators are available that dramatically simplify this code. The sample should be updated to use them.Motivation
ToolPropertyclass, manual JSON serialization of tool properties, and parsingcontextJSON to extract arguments — all of which the new decorators eliminate.Prerequisites / Environment Changes
src/requirements.txtPin the new beta version:
src/local.settings.jsonAdd the required worker isolation setting:
{ "IsEncrypted": false, "Values": { "FUNCTIONS_WORKER_RUNTIME": "python", "AzureWebJobsStorage": "UseDevelopmentStorage=true", "PYTHON_ISOLATE_WORKER_DEPENDENCIES": "1" } }Deployed App Settings (infra / bicep /
azd)Ensure
PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1is also set in the deployed Function App configuration (bicep / app settings).Required Code Changes in
src/function_app.py1. Remove the
ToolPropertyhelper class and all related boilerplateDelete the entire
ToolPropertyclass definition, thetool_properties_save_snippets_object/tool_properties_get_snippets_objectlists, and thetool_properties_save_snippets_json/tool_properties_get_snippets_jsonJSON serialization lines. These are no longer needed — the new decorator infers tool properties from the function signature.2. Migrate
hello_mcpCurrent:
New:
Key changes:
@app.generic_trigger(...)→@app.mcp_tool()descriptionis now inferred from the function docstring.toolNameis inferred from the function name.contextarg is needed when the tool has no properties.str, notNone.3. Migrate
get_snippetCurrent:
New:
Key changes:
@app.generic_trigger(...)→@app.mcp_tool()@app.generic_input_binding(...)→@app.blob_input(...); thetype="blob"parameter is no longer needed (it is implicit).toolPropertiesJSON is replaced by@app.mcp_tool_property(...)input binding and/or function parameter type hints. Args in the function signature that don't map to another binding are automatically treated as MCP tool properties.contextparameter is removed; MCP arguments are received as named function parameters (snippetname).4. Migrate
save_snippetCurrent:
New:
Key changes:
@app.generic_trigger(...)→@app.mcp_tool()@app.generic_output_binding(...)→@app.blob_output(...);type="blob"is implicit.json.loads(context)andcontent["arguments"][...]extraction is eliminated — arguments arrive as named function parameters.@app.mcp_tool_property(...)decorators explicitly set descriptions for each tool property. These are optional if the defaults are acceptable; properties are inferred from the signature.5. Clean up imports
The
jsonimport can likely be removed sincecontextis no longer manually parsed. Keeploggingandazure.functions as func.How new decorator property inference works (for reference)
mcp_tool_propertymcp_tool_propertyproperty_typein decoratorarg_name(e.g.fileinblob_output)Optional[T]type hintDocumentation Updates
The
README.mdSource Code section includes inline code snippets of the old API. These must be updated to reflect the new decorators. The prerequisites section should also note the minimumazure-functionsversion.Checklist
src/requirements.txtto pinazure-functions==1.25.0b2PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1tosrc/local.settings.jsonPYTHON_ISOLATE_WORKER_DEPENDENCIES: 1to deployed app settings (infra/bicep)ToolPropertyclass and alltool_properties_*boilerplate fromfunction_app.pyhello_mcpto@app.mcp_tool()get_snippetto@app.mcp_tool()+@app.blob_input()+@app.mcp_tool_property()save_snippetto@app.mcp_tool()+@app.blob_output()+@app.mcp_tool_property()jsonimport (no longer needed)README.mdSource Code section with new decorator examplesazd up