Skip to content

Commit c5f317a

Browse files
Add MCP prompts sample (#46)
* add prompt sample * add separate azure.yaml for each project * update prompt readme with test instructions * update requirements.txt to reference preview pkg with version --------- Co-authored-by: lilyjma <jm4303@columbia.edu>
1 parent 25f1df4 commit c5f317a

11 files changed

Lines changed: 328 additions & 1 deletion

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.venv

src/FunctionsMcpPrompts/.gitignore

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
bin
2+
obj
3+
csx
4+
.vs
5+
edge
6+
Publish
7+
8+
*.user
9+
*.suo
10+
*.cscfg
11+
*.Cache
12+
project.lock.json
13+
14+
/packages
15+
/TestResults
16+
17+
/tools/NuGet.exe
18+
/App_Data
19+
/secrets
20+
/data
21+
.secrets
22+
appsettings.json
23+
24+
node_modules
25+
dist
26+
27+
# Local python packages
28+
.python_packages/
29+
30+
# Python Environments
31+
.env
32+
.venv
33+
env/
34+
venv/
35+
ENV/
36+
env.bak/
37+
venv.bak/
38+
39+
# Byte-compiled / optimized / DLL files
40+
__pycache__/
41+
*.py[cod]
42+
*$py.class
43+
44+
# Azurite artifacts
45+
__blobstorage__
46+
__queuestorage__
47+
__azurite_db*__.json

src/FunctionsMcpPrompts/README.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# FunctionsMcpPrompts — MCP Prompts on Azure Functions (Python)
2+
3+
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.
4+
5+
> **Note:** MCP tools are in the [FunctionsMcpTool](../FunctionsMcpTool/) project.
6+
7+
## Prompts included
8+
9+
| Prompt | Arguments | Description |
10+
|--------|-----------|-------------|
11+
| `code_review_checklist` | _(none)_ | Returns a structured code review checklist for evaluating code changes. |
12+
| `summarize_content` | `topic` (required), `audience` (optional) | Generates a summarization prompt tailored to a given topic and audience. |
13+
| `generate_documentation` | `function_name` (optional), `style` (optional) | Generates API documentation for a function. |
14+
15+
## Key concepts
16+
17+
- **Simple prompts** (like `code_review_checklist`) take no arguments and return static prompt text.
18+
- **Parameterized prompts** use `prompt_arguments` to accept arguments from the client.
19+
- Prompts can define arguments as required or optional, and read them from `context.arguments`.
20+
21+
## Prerequisites
22+
23+
- [Python](https://www.python.org/downloads/) version 3.13 or higher
24+
- [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`
25+
- `azure-functions` version 2.2.0b2 or greater
26+
- .NET SDK (for building the MCP extension)
27+
28+
## Run locally
29+
30+
### 1. Build the MCP extension
31+
32+
From this directory (`src/FunctionsMcpPrompts`), build the MCP extension:
33+
34+
```shell
35+
dotnet restore extensions.csproj
36+
dotnet build extensions.csproj
37+
```
38+
39+
### 2. Install Dependencies
40+
41+
Create and activate a virtual environment, then install dependencies:
42+
43+
```shell
44+
python3 -m venv .venv
45+
46+
# macOS/Linux
47+
source .venv/bin/activate
48+
49+
# Windows
50+
.venv\Scripts\activate
51+
52+
pip install -r requirements.txt
53+
```
54+
55+
### 3. Start the Functions host
56+
57+
```shell
58+
func start
59+
```
60+
61+
The MCP endpoint will be available at `http://localhost:7071/runtime/webhooks/mcp`.
62+
63+
## Using the MCP Server
64+
65+
### Connect from VS Code - GitHub Copilot
66+
67+
1. Open [.vscode/mcp.json](../../.vscode/mcp.json)
68+
2. Find the server called `local-mcp-function` and click **Start**. The server uses the endpoint: `http://localhost:7071/runtime/webhooks/mcp`
69+
3. In Copilot chat agent mode, access prompts using the slash command format:
70+
- `/mcp.local-mcp-function.code_review_checklist`
71+
- `/mcp.local-mcp-function.summarize_content`
72+
- `/mcp.local-mcp-function.generate_documentation`
73+
74+
For remote servers, replace the server name accordingly (e.g., `/mcp.remote-mcp-function.code_review_checklist`).
75+
76+
### Connect from MCP Inspector
77+
78+
1. Install and run MCP Inspector:
79+
```shell
80+
npx @modelcontextprotocol/inspector
81+
```
82+
2. Open the URL displayed (e.g., http://0.0.0.0:5173/#resources)
83+
3. Set transport type to `Streamable HTTP`
84+
4. Set URL to `http://0.0.0.0:7071/runtime/webhooks/mcp` and **Connect**
85+
5. **List Prompts**, select a prompt, and **Get Prompt**
86+
87+
## Deploy to Azure
88+
89+
```shell
90+
azd env set DEPLOY_SERVICE prompts
91+
azd provision
92+
azd deploy --service prompts
93+
```
94+
95+
## Examining the code
96+
97+
Prompts are defined in `function_app.py`. Each prompt is a Python function with the `@app.mcp_prompt_trigger` decorator:
98+
99+
```python
100+
@app.mcp_prompt_trigger(
101+
arg_name="context",
102+
prompt_name="summarize_content",
103+
prompt_arguments=[
104+
func.PromptArgument("topic", "The topic or content to summarize.", required=True),
105+
func.PromptArgument("audience", "Target audience (e.g., 'executive', 'developer', 'beginner').", required=False)
106+
],
107+
description="Generates a summarization prompt tailored to a given topic and audience."
108+
)
109+
def summarize_content(context: func.PromptInvocationContext) -> str:
110+
topic = context.arguments.get("topic", "")
111+
audience = context.arguments.get("audience")
112+
# Returns a formatted prompt string
113+
```
114+
115+
The `prompt_arguments` parameter defines the arguments that MCP clients see when they list available prompts.

src/FunctionsMcpPrompts/azure.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
3+
name: remote-mcp-functions-python
4+
metadata:
5+
template: remote-mcp-functions-python@1.0.1
6+
infra:
7+
path: ../../infra
8+
services:
9+
api:
10+
project: .
11+
language: python
12+
host: function
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<TargetFramework>net8.0</TargetFramework>
4+
<WarningsAsErrors></WarningsAsErrors>
5+
<DefaultItemExcludes>**</DefaultItemExcludes>
6+
</PropertyGroup>
7+
<ItemGroup>
8+
<PackageReference Include="Microsoft.Azure.Functions.Extensions.Mcp" Version="1.4.0" />
9+
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="5.3.3" />
10+
<PackageReference Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.1.3" />
11+
</ItemGroup>
12+
<ItemGroup>
13+
<None Update="app/dist/*">
14+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
15+
</None>
16+
</ItemGroup>
17+
</Project>
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import azure.functions as func
2+
import logging
3+
4+
app = func.FunctionApp(http_auth_level=func.AuthLevel.FUNCTION)
5+
6+
# Simple prompt with no arguments. Returns a static code review checklist.
7+
# Demonstrates the basic mcp_prompt_trigger usage.
8+
@app.mcp_prompt_trigger(
9+
arg_name="context",
10+
prompt_name="code_review_checklist",
11+
description="Returns a structured code review checklist prompt for evaluating code changes."
12+
)
13+
def code_review_checklist(context: func.PromptInvocationContext) -> str:
14+
logging.info("Code review checklist prompt invoked.")
15+
16+
return """You are a senior software engineer performing a code review.
17+
Use the following checklist to evaluate the code:
18+
19+
1. **Correctness** — Does the code do what it's supposed to?
20+
2. **Error Handling** — Are edge cases and failures handled?
21+
3. **Security** — Are there any vulnerabilities (injection, auth, secrets)?
22+
4. **Performance** — Are there obvious inefficiencies?
23+
5. **Readability** — Is the code clear and well-named?
24+
6. **Tests** — Are there adequate tests for the changes?
25+
26+
Provide your feedback in a structured format with a severity level
27+
(critical, warning, suggestion) for each finding."""
28+
29+
30+
# Prompt with arguments.
31+
# Generates a context-aware summarization prompt for a given topic and audience.
32+
@app.mcp_prompt_trigger(
33+
arg_name="context",
34+
prompt_name="summarize_content",
35+
prompt_arguments=[
36+
func.PromptArgument("topic", "The topic or content to summarize.", required=True),
37+
func.PromptArgument("audience", "Target audience (e.g., 'executive', 'developer', 'beginner').", required=False)
38+
],
39+
description="Generates a summarization prompt tailored to a given topic and audience."
40+
)
41+
def summarize_content(context: func.PromptInvocationContext) -> str:
42+
topic = context.arguments.get("topic", "")
43+
audience = context.arguments.get("audience")
44+
45+
logging.info(f"Summarize prompt invoked for topic: {topic}")
46+
47+
audience_instruction = (
48+
f"Tailor the summary for a **{audience}** audience."
49+
if audience is not None
50+
else "Write the summary for a general technical audience."
51+
)
52+
53+
return f"""Summarize the following topic concisely and accurately:
54+
55+
**Topic:** {topic}
56+
57+
{audience_instruction}
58+
59+
Guidelines:
60+
- Start with a one-sentence overview.
61+
- Include 3–5 key points as bullet items.
62+
- End with a brief conclusion or recommendation.
63+
- Keep the total length under 300 words."""
64+
65+
66+
# Prompt with arguments for generating API documentation.
67+
@app.mcp_prompt_trigger(
68+
arg_name="context",
69+
prompt_name="generate_documentation",
70+
prompt_arguments=[
71+
func.PromptArgument("function_name", "The name of the function to document.", required=False),
72+
func.PromptArgument("style", "Documentation style: 'concise', 'detailed', or 'tutorial'.", required=False)
73+
],
74+
description="Generates API documentation for a function. Arguments are configured in Program.cs."
75+
)
76+
def generate_documentation(context: func.PromptInvocationContext) -> str:
77+
function_name = context.arguments.get("function_name", "(unknown)")
78+
style = context.arguments.get("style", "concise")
79+
80+
logging.info(f"Generate docs prompt invoked for function: {function_name}")
81+
82+
return f"""Generate API documentation for the function named **{function_name}**.
83+
84+
Documentation style: **{style}**
85+
86+
Include the following sections:
87+
- **Description** — What the function does.
88+
- **Parameters** — List each parameter with its type and purpose.
89+
- **Return Value** — What the function returns.
90+
- **Example Usage** — A short code example showing how to call it."""

src/FunctionsMcpPrompts/host.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "2.0",
3+
"extensions": {
4+
"mcp": {
5+
"system": {
6+
"webhookAuthorizationLevel": "Anonymous"
7+
}
8+
}
9+
},
10+
"logging": {
11+
"applicationInsights": {
12+
"samplingSettings": {
13+
"isEnabled": true,
14+
"excludedTypes": "Request"
15+
},
16+
"enableLiveMetricsFilters": true
17+
}
18+
}
19+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"IsEncrypted": false,
3+
"Values": {
4+
"FUNCTIONS_WORKER_RUNTIME": "python",
5+
"AzureWebJobsStorage": "UseDevelopmentStorage=true"
6+
}
7+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Do not include azure-functions-worker in this file
2+
# The Python Worker is managed by the Azure Functions platform
3+
# Manually managing azure-functions-worker may cause unexpected issues
4+
5+
azure-functions>=2.2.0b2

azure.yaml renamed to src/FunctionsMcpTool/azure.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
name: remote-mcp-functions-python
44
metadata:
55
template: remote-mcp-functions-python@1.0.1
6+
infra:
7+
path: ../../infra
68
services:
79
api:
8-
project: ./src/FunctionsMcpTool
10+
project: .
911
language: python
1012
host: function

0 commit comments

Comments
 (0)