MCP Server with Azure Functions: Part 2

Mustahid AhmedMustahid Ahmed
4 min read

In Part 1 of this series, we created a simple hello_mcp Azure Function and walked through how to test it locally and deploy it to the Azure cloud. That was a great starting point for understanding how Model Context Protocol (MCP) tools can be implemented using Azure Functions.

In this second part, weโ€™ll take the next step by implementing an MCP tool that accepts arguments โ€” specifically, two integers that are added together. This will demonstrate how to structure your Azure Function to handle structured input from the MCP system and return meaningful output or error messages when things go wrong. You can refer to the full code from function_app.py.


๐Ÿง  What is an MCP Tool ?

An MCP (Model Context Protocol) tool allows you to expose custom logic to LLM-powered applications like GitHub Copilot or Visual Studio Code. By creating these tools, developers can integrate their own backend logic into AI-assisted workflows.

In this case, our tool will be called add_integers, and it will accept two integer inputs and return their sum.


๐Ÿ› ๏ธ The Structure of Our Azure Function

We're going to create an Azure Function that uses the mcpToolTrigger binding, which allows the function to be invoked as an MCP tool. The function will:

  • Accept two integers (num_a and num_b)

  • Add them together

  • Return the result in JSON format

๐Ÿ”‘ Constants and Property Definitions

To maintain consistency and clarity, we define constants for the property names:

_PROPERTY_A_NAME = "num_a"
_PROPERTY_B_NAME = "num_b"

Then, we use a helper class ToolProperty to describe each parameter expected by the tool:

class ToolProperty:
    def __init__(self, property_name: str, property_type: str, description: str):
        self.propertyName = property_name
        self.propertyType = property_type
        self.description = description

    def to_dict(self):
        return {
            "propertyName": self.propertyName,
            "propertyType": self.propertyType,
            "description": self.description,
        }

This helps us generate a valid schema for the tool properties in the required JSON format.

๐Ÿ“ฆ Defining the Tool Properties

We then define the list of expected properties:

tool_properties_add_integers_object = [
    ToolProperty(_PROPERTY_A_NAME, "integer", "The first integer to add."),
    ToolProperty(_PROPERTY_B_NAME, "integer", "The second integer to add."),
]

tool_properties_add_integers_json = json.dumps(
    [prop.to_dict() for prop in tool_properties_add_integers_object]
)

This JSON will be passed to the mcpToolTrigger so that the MCP system knows what inputs to expect.


๐Ÿš€ Azure Function Implementation

Now comes the main function decorated with the @app.generic_trigger decorator:

@app.generic_trigger(
    arg_name="context",
    type="mcpToolTrigger",
    toolName="add_integers",
    description="Adds two integers and returns the sum.",
    toolProperties=tool_properties_add_integers_json,
)
def add_integers_tool(context: str) -> str:
    ...

The function expects a JSON string in the context argument containing the actual input values under the "arguments" key.

โœ… Input Validation

Inside the function, we perform several checks:

  1. Ensure the context is valid JSON.

  2. Check if the "arguments" key exists.

  3. Make sure both num_a and num_b are present.

  4. Convert the values to integers, handling any conversion errors.

If any of these steps fail, we return an appropriate error message in JSON format.

โž• Performing the Addition

Once the inputs are validated and parsed:

result = num_a + num_b
return json.dumps({"sum": result})

We log the operation and return the result wrapped in a JSON object.

โš ๏ธ Error Handling

We wrap everything in a try-except block to catch unexpected issues and return descriptive error messages:

except json.JSONDecodeError:
    logging.error(f"Failed to decode JSON from context: {context}")
    return json.dumps({"error": "Invalid JSON format in input context."})
except Exception as e:
    logging.error(f"An unexpected error occurred: {str(e)}")
    return json.dumps({"error": f"An unexpected error occurred: {str(e)}"})

๐Ÿงช Testing Locally

Follow Part 1 if you want to learn about local deployment of azure function.

To test this function locally from the project directory:

  1. Start the Azure Functions server:

     source .venv/bin/activate
     func start
    
  2. Use MCP Inspector to verify that your MCP tool function has been deployed. For a guide on MCP Inspector Usage follow Install MCP Inspector and Check with MCP Inspector from Part 1 .

You should see logs indicating the function was triggered and receive a response like:


โ˜๏ธ Deploying to Azure

Once tested locally, deploy the function using the instruction from part 1 .

After deployment, the function will be accessible via its public URL. You can now register this tool with the MCP system.


๐ŸŽฏ Summary

In this blog post, weโ€™ve built on the foundation from Part 1 and created an MCP tool using Azure Functions that:

  • Accepts two integer inputs

  • Validates the inputs and handles errors gracefully

  • Returns a structured JSON response

  • Can be tested locally and deployed to Azure

This pattern can be extended to implement more complex logic, such as calling external APIs, processing files, or integrating with databases โ€” all while being exposed as a tool for Copilot experiences.


๐Ÿ“Œ Next Steps

In the next part of this series, we'll explore how to implement MCP tools for Postgre SQL server, allowing your LLM to access necessary features of your sql server.

Stay tuned!

0
Subscribe to my newsletter

Read articles from Mustahid Ahmed directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Mustahid Ahmed
Mustahid Ahmed