diff --git a/src/FunctionsMcpPrompts/.funcignore b/src/FunctionsMcpPrompts/.funcignore new file mode 100644 index 0000000..b694934 --- /dev/null +++ b/src/FunctionsMcpPrompts/.funcignore @@ -0,0 +1 @@ +.venv \ No newline at end of file diff --git a/src/FunctionsMcpPrompts/.gitignore b/src/FunctionsMcpPrompts/.gitignore new file mode 100644 index 0000000..c6be22e --- /dev/null +++ b/src/FunctionsMcpPrompts/.gitignore @@ -0,0 +1,47 @@ +bin +obj +csx +.vs +edge +Publish + +*.user +*.suo +*.cscfg +*.Cache +project.lock.json + +/packages +/TestResults + +/tools/NuGet.exe +/App_Data +/secrets +/data +.secrets +appsettings.json + +node_modules +dist + +# Local python packages +.python_packages/ + +# Python Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Azurite artifacts +__blobstorage__ +__queuestorage__ +__azurite_db*__.json \ No newline at end of file diff --git a/src/FunctionsMcpPrompts/README.md b/src/FunctionsMcpPrompts/README.md new file mode 100644 index 0000000..8da614d --- /dev/null +++ b/src/FunctionsMcpPrompts/README.md @@ -0,0 +1,91 @@ +# FunctionsMcpPrompts — MCP Prompts on Azure Functions (Python) + +This project is a Python Azure Function app that exposes MCP (Model Context Protocol) prompts as a remote MCP server. Prompts are reusable prompt templates that MCP clients can discover and invoke, optionally with arguments. + +> **Note:** MCP tools are in the [FunctionsMcpTool](../FunctionsMcpTool/) project. + +## Prompts included + +| Prompt | Arguments | Description | +|--------|-----------|-------------| +| `code_review_checklist` | _(none)_ | Returns a structured code review checklist for evaluating code changes. | +| `summarize_content` | `topic` (required), `audience` (optional) | Generates a summarization prompt tailored to a given topic and audience. | +| `generate_documentation` | `function_name` (optional), `style` (optional) | Generates API documentation for a function. | + +## Key concepts + +- **Simple prompts** (like `code_review_checklist`) take no arguments and return static prompt text. +- **Parameterized prompts** use `prompt_arguments` to accept arguments from the client. +- Prompts can define arguments as required or optional, and read them from `context.arguments`. + +## Prerequisites + +- [Python](https://www.python.org/downloads/) version 3.13 or higher +- [Azure Functions Core Tools](https://learn.microsoft.com/azure/azure-functions/functions-run-local?pivots=programming-language-python#install-the-azure-functions-core-tools) >= `4.8.0` +- `azure-functions` version 2.2.0b2 or greater +- .NET SDK (for building the MCP extension) + +## Run locally + +### 1. Build the MCP extension + +From this directory (`src/FunctionsMcpPrompts`), build the MCP extension: + +```shell +dotnet restore extensions.csproj +dotnet build extensions.csproj +``` + +### 2. Install Dependencies + +Create and activate a virtual environment, then install dependencies: + +```shell +python3 -m venv .venv + +# macOS/Linux +source .venv/bin/activate + +# Windows +.venv\Scripts\activate + +pip install -r requirements.txt +``` + +### 3. Start the Functions host + +```shell +func start +``` + +The MCP endpoint will be available at `http://localhost:7071/runtime/webhooks/mcp`. + +## Deploy to Azure + +```shell +azd env set DEPLOY_SERVICE prompts +azd provision +azd deploy --service prompts +``` + +## Examining the code + +Prompts are defined in `function_app.py`. Each prompt is a Python function with the `@app.mcp_prompt_trigger` decorator: + +```python +@app.mcp_prompt_trigger( + arg_name="context", + prompt_name="summarize_content", + prompt_arguments=[ + func.PromptArgument("topic", "The topic or content to summarize.", required=True), + func.PromptArgument("audience", "Target audience (e.g., 'executive', 'developer', 'beginner').", required=False) + ], + description="Generates a summarization prompt tailored to a given topic and audience." +) +def summarize_content(context: func.PromptInvocationContext) -> str: + topic = context.arguments.get("topic", "") + audience = context.arguments.get("audience") + # Returns a formatted prompt string +``` + +The `prompt_arguments` parameter defines the arguments that MCP clients see when they list available prompts. diff --git a/src/FunctionsMcpPrompts/extensions.csproj b/src/FunctionsMcpPrompts/extensions.csproj new file mode 100644 index 0000000..1fb29b5 --- /dev/null +++ b/src/FunctionsMcpPrompts/extensions.csproj @@ -0,0 +1,17 @@ + + + net8.0 + + ** + + + + + + + + + PreserveNewest + + + \ No newline at end of file diff --git a/src/FunctionsMcpPrompts/function_app.py b/src/FunctionsMcpPrompts/function_app.py new file mode 100644 index 0000000..f30c51b --- /dev/null +++ b/src/FunctionsMcpPrompts/function_app.py @@ -0,0 +1,90 @@ +import azure.functions as func +import logging + +app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION) + +# Simple prompt with no arguments. Returns a static code review checklist. +# Demonstrates the basic mcp_prompt_trigger usage. +@app.mcp_prompt_trigger( + arg_name="context", + prompt_name="code_review_checklist", + description="Returns a structured code review checklist prompt for evaluating code changes." +) +def code_review_checklist(context: func.PromptInvocationContext) -> str: + logging.info("Code review checklist prompt invoked.") + + return """You are a senior software engineer performing a code review. +Use the following checklist to evaluate the code: + +1. **Correctness** — Does the code do what it's supposed to? +2. **Error Handling** — Are edge cases and failures handled? +3. **Security** — Are there any vulnerabilities (injection, auth, secrets)? +4. **Performance** — Are there obvious inefficiencies? +5. **Readability** — Is the code clear and well-named? +6. **Tests** — Are there adequate tests for the changes? + +Provide your feedback in a structured format with a severity level +(critical, warning, suggestion) for each finding.""" + + +# Prompt with arguments. +# Generates a context-aware summarization prompt for a given topic and audience. +@app.mcp_prompt_trigger( + arg_name="context", + prompt_name="summarize_content", + prompt_arguments=[ + func.PromptArgument("topic", "The topic or content to summarize.", required=True), + func.PromptArgument("audience", "Target audience (e.g., 'executive', 'developer', 'beginner').", required=False) + ], + description="Generates a summarization prompt tailored to a given topic and audience." +) +def summarize_content(context: func.PromptInvocationContext) -> str: + topic = context.arguments.get("topic", "") + audience = context.arguments.get("audience") + + logging.info(f"Summarize prompt invoked for topic: {topic}") + + audience_instruction = ( + f"Tailor the summary for a **{audience}** audience." + if audience is not None + else "Write the summary for a general technical audience." + ) + + return f"""Summarize the following topic concisely and accurately: + +**Topic:** {topic} + +{audience_instruction} + +Guidelines: +- Start with a one-sentence overview. +- Include 3–5 key points as bullet items. +- End with a brief conclusion or recommendation. +- Keep the total length under 300 words.""" + + +# Prompt with arguments for generating API documentation. +@app.mcp_prompt_trigger( + arg_name="context", + prompt_name="generate_documentation", + prompt_arguments=[ + func.PromptArgument("function_name", "The name of the function to document.", required=False), + func.PromptArgument("style", "Documentation style: 'concise', 'detailed', or 'tutorial'.", required=False) + ], + description="Generates API documentation for a function. Arguments are configured in Program.cs." +) +def generate_documentation(context: func.PromptInvocationContext) -> str: + function_name = context.arguments.get("function_name", "(unknown)") + style = context.arguments.get("style", "concise") + + logging.info(f"Generate docs prompt invoked for function: {function_name}") + + return f"""Generate API documentation for the function named **{function_name}**. + +Documentation style: **{style}** + +Include the following sections: +- **Description** — What the function does. +- **Parameters** — List each parameter with its type and purpose. +- **Return Value** — What the function returns. +- **Example Usage** — A short code example showing how to call it.""" \ No newline at end of file diff --git a/src/FunctionsMcpPrompts/host.json b/src/FunctionsMcpPrompts/host.json new file mode 100644 index 0000000..66e29a8 --- /dev/null +++ b/src/FunctionsMcpPrompts/host.json @@ -0,0 +1,19 @@ +{ + "version": "2.0", + "extensions": { + "mcp": { + "system": { + "webhookAuthorizationLevel": "Anonymous" + } + } + }, + "logging": { + "applicationInsights": { + "samplingSettings": { + "isEnabled": true, + "excludedTypes": "Request" + }, + "enableLiveMetricsFilters": true + } + } +} \ No newline at end of file diff --git a/src/FunctionsMcpPrompts/local.settings.json b/src/FunctionsMcpPrompts/local.settings.json new file mode 100644 index 0000000..504bb06 --- /dev/null +++ b/src/FunctionsMcpPrompts/local.settings.json @@ -0,0 +1,7 @@ +{ + "IsEncrypted": false, + "Values": { + "FUNCTIONS_WORKER_RUNTIME": "python", + "AzureWebJobsStorage": "UseDevelopmentStorage=true" + } + } \ No newline at end of file diff --git a/src/FunctionsMcpPrompts/requirements.txt b/src/FunctionsMcpPrompts/requirements.txt new file mode 100644 index 0000000..9298ea1 --- /dev/null +++ b/src/FunctionsMcpPrompts/requirements.txt @@ -0,0 +1,5 @@ +# Do not include azure-functions-worker in this file +# The Python Worker is managed by the Azure Functions platform +# Manually managing azure-functions-worker may cause unexpected issues + +azure-functions --pre