Azure-Samples / Azure-Samples/remote-mcp-functions-python

Migrate function_app.py from generic_trigger/generic_*_binding to new @app.mcp_tool() decorators (azure-functions 1.25.0b2)

Đang mở
#24 1 bình luận 2 reaction 2 người được giao Được @vrdmr nhận Xem trên GitHub
Ngôn ngữ chính
Python
Star
125
Fork
83
Chỉ số merge pull request
Không có pull request nào được merge trong 30 ngày

Mô tả

## 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:
```json
{
"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:**
```python
@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:**
```python
@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:**
```python
@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:**
```python
@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:**
```python
@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:**
```python
@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

- [ ] Update `src/requirements.txt` to pin `azure-functions==1.25.0b2`
- [ ] Add `PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1` to `src/local.settings.json`
- [ ] Add `PYTHON_ISOLATE_WORKER_DEPENDENCIES: 1` to deployed app settings (infra/bicep)
- [ ] Remove `ToolProperty` class and all `tool_properties_*` boilerplate from `function_app.py`
- [ ] Migrate `hello_mcp` to `@app.mcp_tool()`
- [ ] Migrate `get_snippet` to `@app.mcp_tool()` + `@app.blob_input()` + `@app.mcp_tool_property()`
- [ ] Migrate `save_snippet` to `@app.mcp_tool()` + `@app.blob_output()` + `@app.mcp_tool_property()`
- [ ] Remove `json` import (no longer needed)
- [ ] Update `README.md` Source Code section with new decorator examples
- [ ] Test locally with Azurite + MCP Inspector
- [ ] Test deployed with `azd up`

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.