MCP-server-function
This project is an MCP server implementation using Azure Functions and .NET 8. It allows interaction with models supporting the MCP standard, offering custom operations and is optimized for scalability and performance.
MCP Server Azure Function
A Model Context Protocol (MCP) server implementation using Azure Functions with the .NET 8.0 isolated worker model.
Overview
This project implements a Model Context Protocol (MCP) server as an Azure Function, enabling interaction with language models that support the MCP standard. The implementation uses the Azure Functions v4 isolated worker model which offers better performance, scalability, and compatibility with .NET 8.
Features
-
Implements MCP protocol operations:
list_operations
: Standard MCP operation to list all available operationsget_openapi_spec
: Standard MCP operation to provide API documentationgetBusinessData
: Custom operation to retrieve business metrics and datatriggerLogicApp
: Custom operation to trigger Azure Logic AppscallInternalApi
: Custom operation to proxy requests to internal APIs
-
Built on Azure Functions v4 with isolated worker process
-
Uses .NET 8.0 for improved performance and features
-
Structured error handling and logging
-
JSON serialization with System.Text.Json
-
Infrastructure as Code (IaC) using Bicep templates
Prerequisites
- .NET 8 SDK
- Azure Functions Core Tools v4
- Visual Studio 2022 or Visual Studio Code
- Azure CLI (for deployment)
- Azure Bicep CLI (for infrastructure deployment)
Setup and Configuration
Local Development Setup
-
Clone the repository
-
Configure local.settings.json
The local.settings.json file contains the necessary configuration for running the function locally. Make sure it includes:
{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated", "DOTNET_ISOLATE_TIMEOUT_SECONDS": "60", "LOGIC_APP_ENDPOINT": "https://prod-12.eastus.logic.azure.com:443/workflows/your-logic-app-id", "INTERNAL_API_BASE_URL": "https://api.internal.company.com", "INTERNAL_API_KEY": "your-api-key-here", "MCP_AUTH_ENABLED": "false", "MCP_ALLOWED_OPERATIONS": "getBusinessData,triggerLogicApp,callInternalApi,list_operations,get_openapi_spec" } }
-
Install dependencies
dotnet restore
Running Locally
-
Start the function
func start
The function will be available at
http://localhost:7071/api/MCPFunction
-
Stop the function
Press
Ctrl+C
in the terminal where the function is running, or find the PID usingnetstat -ano | findstr :7071 | findstr LISTENING
and then kill it withtaskkill /F /PID <PID>
Testing
Local Testing with JSON Files
The project includes test JSON files to help validate the MCP server functionality:
-
Test list_operations
Use the
test-list-operations.json
file to test the standard MCP operation that lists all available operations:curl -X POST http://localhost:7071/api/MCPFunction -H "Content-Type: application/json" -d @test-list-operations.json
-
Test getBusinessData
Use the
test-business-data.json
file to test retrieving business data:curl -X POST http://localhost:7071/api/MCPFunction -H "Content-Type: application/json" -d @test-business-data.json
-
Test other operations
You can create additional test files for other operations following the MCP protocol format:
{ "requestId": "unique-request-id", "messageType": "invoke", "invokeRequest": { "operation": "operationName", "parameters": { "param1": "value1", "param2": "value2" } } }
Testing Deployed Functions with Function Keys
When testing functions deployed to Azure, you need to include the function key in your requests:
-
Using function key in curl request
curl -X POST https://your-function-app.azurewebsites.net/api/MCPFunction \ -H "Content-Type: application/json" \ -H "x-functions-key: YOUR_FUNCTION_KEY" \ -d @test-list-operations.json
-
Alternative: Using function key as query parameter
curl -X POST "https://your-function-app.azurewebsites.net/api/MCPFunction?code=YOUR_FUNCTION_KEY" \ -H "Content-Type: application/json" \ -d @test-list-operations.json
-
Testing with Postman
- Create a new POST request to your function URL
- Add an
x-functions-key
header with your function key - Set the
Content-Type
header toapplication/json
- Add the MCP request body in JSON format
- Send the request and examine the response
Deployment to Azure
Deploying with Bicep (Recommended)
This project includes Bicep templates for Infrastructure as Code deployment, following Azure best practices.
-
Navigate to the infrastructure directory
cd infra
-
Deploy to a specific Azure region (e.g., Sweden Central)
# Create resource group (if it doesn't exist) az group create --name rg-mcp-function --location swedencentral # Validate the deployment with what-if operation (recommended practice) az deployment group what-if --name mcp-server-deployment --resource-group rg-mcp-function --template-file main.bicep --parameters main.parameters.json # Deploy the infrastructure az deployment group create --name mcp-server-deployment --resource-group rg-mcp-function --template-file main.bicep --parameters main.parameters.json
-
Deploy function code to the created Function App
# Build the project for release dotnet publish -c Release # Deploy to Azure Function App (replace FUNCTION_APP_NAME with your deployed function app name) func azure functionapp publish FUNCTION_APP_NAME --dotnet-isolated
-
Retrieve function key for testing
# List function keys az functionapp function keys list --name FUNCTION_APP_NAME --resource-group rg-mcp-function --function-name MCPFunction
Bicep Template Details
The Bicep template (main.bicep
) includes:
- Function App with .NET 8.0 isolated worker runtime
- App Service Plan (Consumption or dedicated)
- Storage Account for function execution
- Application Insights for monitoring
- Log Analytics Workspace for logs
- User-Assigned Managed Identity for secure authentication
Key parameters that can be customized:
location
: Azure region for deployment (default: swedencentral)environmentName
: Name prefix for resourcesappServicePlanSku
: SKU for the App Service Plan (Y1, B1, S1)mcpAuthEnabled
: Whether to enable MCP authenticationmcpAllowedOperations
: Comma-separated list of allowed operations
Deploying with Azure CLI (Alternative)
-
Create a Function App in Azure
az login az group create --name mcp-server-rg --location eastus az storage account create --name mcpserverfunc --location eastus --resource-group mcp-server-rg --sku Standard_LRS az functionapp create --resource-group mcp-server-rg --consumption-plan-location eastus --runtime dotnet-isolated --functions-version 4 --name mcp-server-function --storage-account mcpserverfunc --os-type Windows
-
Set Function App settings
az functionapp config appsettings set --name mcp-server-function --resource-group mcp-server-rg --settings "LOGIC_APP_ENDPOINT=https://prod-12.eastus.logic.azure.com:443/workflows/your-logic-app-id" "INTERNAL_API_BASE_URL=https://api.internal.company.com" "MCP_AUTH_ENABLED=true" "MCP_ALLOWED_OPERATIONS=getBusinessData,triggerLogicApp,callInternalApi,list_operations,get_openapi_spec"
For sensitive settings like API keys, use Azure Key Vault:
az keyvault create --name mcpserver-kv --resource-group mcp-server-rg --location eastus az keyvault secret set --vault-name mcpserver-kv --name "InternalApiKey" --value "your-api-key-here" az functionapp identity assign --name mcp-server-function --resource-group mcp-server-rg # Grant the function access to Key Vault $principalId=$(az functionapp identity show --name mcp-server-function --resource-group mcp-server-rg --query principalId --output tsv) az keyvault set-policy --name mcpserver-kv --object-id $principalId --secret-permissions get list # Add Key Vault reference to Function App settings az functionapp config appsettings set --name mcp-server-function --resource-group mcp-server-rg --settings "INTERNAL_API_KEY=@Microsoft.KeyVault(SecretUri=https://mcpserver-kv.vault.azure.net/secrets/InternalApiKey/)"
-
Publish the Function
func azure functionapp publish mcp-server-function
Deploying with Azure DevOps or GitHub Actions
Create a CI/CD pipeline using:
- Azure DevOps Azure Functions pipeline template
- GitHub Actions Azure Functions workflow
Production Deployment Considerations
Regional Selection
- Sweden Central Region Benefits:
- Strong compliance offerings for European data residency requirements
- Sustainability focus with high renewable energy usage
- Low-latency access for Nordic and Northern European users
- Modern Azure datacenter with latest infrastructure
Security Best Practices
- Enable MCP authentication by setting
MCP_AUTH_ENABLED=true
in production - Store sensitive information like API keys in Azure Key Vault
- Use managed identities for authenticating with other Azure services
- Monitor function execution with Application Insights
- Regularly update dependencies to address security vulnerabilities
- Enable HTTPS-only access to your function app
- Use the minimum TLS version 1.2
- Disable FTP access to your function app
Monitoring and Operational Excellence
- Enable Application Insights for comprehensive monitoring
- Set up alerts for abnormal function behavior
- Configure diagnostic settings for log retention
- Use resource tags for better resource management
- Implement proper RBAC for administrative access
- Regular backup of function app configurations
Troubleshooting
Common Issues
-
System.Net.Primitives assembly not found
This can occur when running .NET 8 functions with the in-process model. The solution is to use the isolated worker process model, which is configured in this project.
-
Function doesn't start locally
Ensure you have the right .NET SDK version specified in global.json:
{ "sdk": { "version": "8.0.115" } }
-
Authentication or authorization failures
Check your
MCP_AUTH_ENABLED
setting and ensure proper authentication is configured in Azure. -
Bicep deployment errors
- Check for linting errors with
az bicep build --file main.bicep
- Verify resource name conventions and uniqueness
- Ensure proper parameter types are used
- Use what-if operation to validate changes before deployment
- Common Bicep errors include:
- Using functions in incorrect locations (e.g., utcNow() can only be used in parameter defaults)
- Invalid variable references
- Missing parameters or expressions
- Check for linting errors with
-
Function Key Issues
If you receive 401 Unauthorized errors when testing your deployed function, ensure you're including the function key with your requests, either as an
x-functions-key
header or as a?code=
query parameter.
Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests
- Submit a pull request
License
[Specify the license under which this project is released]