Skip to content

Migrate to native @app.mcp_tool() decorators (azure-functions 1.25.0b2)#25

Draft
Copilot wants to merge 5 commits intomainfrom
copilot/migrate-function-app-to-mcp-decorators
Draft

Migrate to native @app.mcp_tool() decorators (azure-functions 1.25.0b2)#25
Copilot wants to merge 5 commits intomainfrom
copilot/migrate-function-app-to-mcp-decorators

Conversation

Copy link

Copilot AI commented Feb 12, 2026

Replaces generic_trigger/generic_*_binding pattern with first-class MCP decorators introduced in azure-functions 1.25.0b2. Removes ~100 lines of boilerplate JSON serialization and manual argument parsing.

Changes

Configuration:

  • Pin azure-functions==1.25.0b2 in requirements.txt
  • Add PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1 to local.settings.json and bicep (required for new decorators)

Code simplification:

  • Remove ToolProperty class and JSON serialization layer
  • Replace @app.generic_trigger(type="mcpToolTrigger", ...)@app.mcp_tool()
  • Replace @app.generic_input_binding(type="blob", ...)@app.blob_input(...)
  • Replace @app.generic_output_binding(type="blob", ...)@app.blob_output(...)
  • Remove manual json.loads(context) and argument extraction
  • Tool properties now inferred from function signatures; descriptions from @app.mcp_tool_property() or docstrings

Before:

@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="save_snippet",
    toolProperties=json.dumps([{"propertyName": "snippet", ...}]),
)
@app.generic_output_binding(arg_name="file", type="blob", ...)
def save_snippet(file: func.Out[str], context) -> str:
    content = json.loads(context)
    snippet = content["arguments"]["snippet"]
    file.set(snippet)

After:

@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippet", description="The content of the snippet.")
@app.blob_output(arg_name="file", ...)
def save_snippet(file: func.Out[str], snippet: str) -> str:
    """Save a snippet to Azure Blob Storage."""
    file.set(snippet)

Net: -94 lines across function_app.py and README.

Original prompt

This section details on the original issue you should resolve

<issue_title>Migrate function_app.py from generic_trigger/generic_*_binding to new @app.mcp_tool() decorators (azure-functions 1.25.0b2)</issue_title>
<issue_description>## 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]
    ...

</details>



<!-- START COPILOT CODING AGENT SUFFIX -->

- Fixes Azure-Samples/remote-mcp-functions-python#24

<!-- START COPILOT CODING AGENT TIPS -->
---Let Copilot coding agent [set things up for you](https://github.com/Azure-Samples/remote-mcp-functions-python/issues/new?title=+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits February 12, 2026 00:38
Co-authored-by: vrdmr <1047040+vrdmr@users.noreply.github.com>
Co-authored-by: vrdmr <1047040+vrdmr@users.noreply.github.com>
Co-authored-by: vrdmr <1047040+vrdmr@users.noreply.github.com>
Co-authored-by: vrdmr <1047040+vrdmr@users.noreply.github.com>
Copilot AI changed the title [WIP] Migrate function_app.py to new MCP decorators Migrate to native @app.mcp_tool() decorators (azure-functions 1.25.0b2) Feb 12, 2026
Copilot AI requested a review from vrdmr February 12, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants