<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>#AzureFunctions on Nirav Raval</title>
    <link>https://niravraval.com/tags/%23azurefunctions/</link>
    <description>Recent content in #AzureFunctions on Nirav Raval</description>
    <image>
      <title>Nirav Raval</title>
      <url>https://niravraval.com/Images/Nirav_about.jpg</url>
      <link>https://niravraval.com/Images/Nirav_about.jpg</link>
    </image>
    <generator>Hugo -- 0.147.3</generator>
    <language>en-us</language>
    <lastBuildDate>Sun, 03 May 2026 23:34:56 -0400</lastBuildDate>
    <atom:link href="https://niravraval.com/tags/%23azurefunctions/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How to build a High-Performance MCP Server on Azure Functions</title>
      <link>https://niravraval.com/blog/2026/may/how-to-build-a-high-performance-mcp-server-on-azure-functions/</link>
      <pubDate>Mon, 04 May 2026 00:00:00 +0000</pubDate>
      <guid>https://niravraval.com/blog/2026/may/how-to-build-a-high-performance-mcp-server-on-azure-functions/</guid>
      <description>&lt;p&gt;In my recent project, I integrated the &lt;strong&gt;Model Context Protocol (MCP)&lt;/strong&gt; into an existing &lt;strong&gt;Azure Functions&lt;/strong&gt; API and I wanted to share how it works and why it matters. MCP (Model Context Protocol) provides a standardized interface for connecting Large Language Models (LLMs), such as Microsoft 365 Copilot, to private data and specialized tools. While many implementations focus on local setups, deploying an MCP server in a cloud-native, serverless environment like Azure Functions offers the scalability, security, and flexibility needed for enterprise-grade applications.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>In my recent project, I integrated the <strong>Model Context Protocol (MCP)</strong> into an existing <strong>Azure Functions</strong> API and I wanted to share how it works and why it matters. MCP (Model Context Protocol) provides a standardized interface for connecting Large Language Models (LLMs), such as Microsoft 365 Copilot, to private data and specialized tools. While many implementations focus on local setups, deploying an MCP server in a cloud-native, serverless environment like Azure Functions offers the scalability, security, and flexibility needed for enterprise-grade applications.</p>
<h2 id="the-gateway-pattern">The Gateway Pattern</h2>
<p>The Gateway Pattern is the optimal architecture for implementing MCP in Azure Functions. Rather than mapping every tool to a distinct function endpoint, a single <strong>McpGateway</strong> function serves as a protocol adapter. This function exposes all your API endpoints through a unified entry point.</p>
<h3 id="request-flow">Request Flow</h3>
<p>The interaction follows a structured flow where the client sends a <a href="https://www.jsonrpc.org/specification"><strong>JSON-RPC</strong></a> request to the <code>/api/mcp</code> endpoint. The gateway then validates this request, identifies the user context, and parses the specific method requested. Next, the execution engine uses reflection and dependency injection to find and run the necessary tool. Finally, the system packages the result into a JSON-RPC response and sends it back to the client.</p>
<figure class="align-center ">
    <img loading="lazy" src="/Images/blog_images/2026/May/MCPAZ1.png#center"
         alt="Alt text"/> <figcaption>
            <p>(Generated using <a href="https://www.eraser.io/">eraser.io</a>)</p>
        </figcaption>
</figure>

<h2 id="the-json-rpc-router">The JSON-RPC Router</h2>
<p>MCP operations utilize <a href="https://www.jsonrpc.org/specification"><strong>JSON-RPC 2.0</strong></a> (a stateless protocol). The gateway must process three core method types: <code>initialize</code>, <code>tools/list</code>, and <code>tools/call</code>.</p>
<h3 id="1-the-initialize-handshake">1. The Initialize Handshake</h3>
<p>Before calling any tools, the client &ldquo;handshakes&rdquo; with your server to understand its capabilities. This code below defines an Azure Function named McpGateway that acts as an HTTP endpoint for handling JSON-RPC requests. It begins by asynchronously reading the incoming request body and parsing it into a JSON object to extract the method and id fields.</p>
<p>The logic specifically checks if the client is calling the initialize method, which is the standard handshake for the Model Context Protocol (MCP). If it matches, the function returns a successful response containing the protocol version, the server’s capabilities (specifically tool listing), and metadata about the server. This confirms the connection between the client and the server.
 </p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#a6e22e">[Function(&#34;McpGateway&#34;)]</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">async</span> Task&lt;HttpResponseData&gt; Run([HttpTrigger(AuthorizationLevel.Anonymous, <span style="color:#e6db74">&#34;post&#34;</span>)] HttpRequestData req)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> requestBody = <span style="color:#66d9ef">await</span> req.ReadAsStringAsync();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> jsonRequest = JsonConvert.DeserializeObject&lt;JObject&gt;(requestBody);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> method = jsonRequest[<span style="color:#e6db74">&#34;method&#34;</span>]?.ToString();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> id = jsonRequest[<span style="color:#e6db74">&#34;id&#34;</span>];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> (method == <span style="color:#e6db74">&#34;initialize&#34;</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">await</span> CreateJsonRpcResponse(req, id, <span style="color:#66d9ef">new</span> {
</span></span><span style="display:flex;"><span>            protocolVersion = <span style="color:#e6db74">&#34;2025-11-25&#34;</span>,
</span></span><span style="display:flex;"><span>            capabilities = <span style="color:#66d9ef">new</span> { tools = <span style="color:#66d9ef">new</span> { listChanged = <span style="color:#66d9ef">true</span> } },
</span></span><span style="display:flex;"><span>            serverInfo = <span style="color:#66d9ef">new</span> { name = <span style="color:#e6db74">&#34;EnterpriseMcpServer&#34;</span>, version = <span style="color:#e6db74">&#34;1.0.0&#34;</span> }
</span></span><span style="display:flex;"><span>        });
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Example Input:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;jsonrpc&#34;</span>: <span style="color:#e6db74">&#34;2.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;method&#34;</span>: <span style="color:#e6db74">&#34;initialize&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;params&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;protocolVersion&#34;</span>: <span style="color:#e6db74">&#34;2025-11-25&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;capabilities&#34;</span>: {}
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Example Output:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;jsonrpc&#34;</span>: <span style="color:#e6db74">&#34;2.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#ae81ff">1</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;result&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;protocolVersion&#34;</span>: <span style="color:#e6db74">&#34;2025-11-25&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;capabilities&#34;</span>: { <span style="color:#f92672">&#34;tools&#34;</span>: { <span style="color:#f92672">&#34;listChanged&#34;</span>: <span style="color:#66d9ef">true</span> } },
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;serverInfo&#34;</span>: { <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;McpServer&#34;</span>, <span style="color:#f92672">&#34;version&#34;</span>: <span style="color:#e6db74">&#34;1.0.0&#34;</span> }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="2-dynamic-tool-discovery-toolslist">2. Dynamic Tool Discovery (<code>tools/list</code>)</h2>
<p>Dynamic tool discovery lets your server automatically find and expose available tools without hard-coding them. Instead of maintaining a manual list, you simply mark the methods you want to expose, and the system discovers them at runtime.</p>
<p>You do this using attributes (decorators) to clearly define which classes and methods act as tools:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#a6e22e">[AttributeUsage(AttributeTargets.Class)]</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">McpToolProviderAttribute</span> : Attribute { }
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">
</span></span></span><span style="display:flex;"><span><span style="color:#a6e22e">[AttributeUsage(AttributeTargets.Method)]</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">McpToolAttribute</span> : Attribute { 
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">string</span> Name { <span style="color:#66d9ef">get</span>; <span style="color:#66d9ef">set</span>; }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">string</span> Description { <span style="color:#66d9ef">get</span>; <span style="color:#66d9ef">set</span>; }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The server then uses reflection to scan for these attributes and build a tool list dynamically:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#66d9ef">var</span> toolList = Assembly.GetExecutingAssembly()
</span></span><span style="display:flex;"><span>    .GetTypes()
</span></span><span style="display:flex;"><span>    .Where(t =&gt; t.GetCustomAttribute&lt;McpToolProviderAttribute&gt;() != <span style="color:#66d9ef">null</span>)
</span></span><span style="display:flex;"><span>    .SelectMany(t =&gt; t.GetMethods())
</span></span><span style="display:flex;"><span>    .Where(m =&gt; m.GetCustomAttribute&lt;McpToolAttribute&gt;() != <span style="color:#66d9ef">null</span>)
</span></span><span style="display:flex;"><span>    .Select(m =&gt; <span style="color:#66d9ef">new</span> {
</span></span><span style="display:flex;"><span>        name = m.GetCustomAttribute&lt;McpToolAttribute&gt;().Name,
</span></span><span style="display:flex;"><span>        description = m.GetCustomAttribute&lt;McpToolAttribute&gt;().Description,
</span></span><span style="display:flex;"><span>        inputSchema = <span style="color:#66d9ef">new</span> {
</span></span><span style="display:flex;"><span>            type = <span style="color:#e6db74">&#34;object&#34;</span>,
</span></span><span style="display:flex;"><span>            properties = m.GetParameters().ToDictionary(
</span></span><span style="display:flex;"><span>                p =&gt; p.Name, 
</span></span><span style="display:flex;"><span>                p =&gt; <span style="color:#66d9ef">new</span> { type = GetJsonType(p.ParameterType), description = GetDescription(p) }
</span></span><span style="display:flex;"><span>            ),
</span></span><span style="display:flex;"><span>            required = m.GetParameters().Where(p =&gt; !p.IsOptional).Select(p =&gt; p.Name)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    });
</span></span></code></pre></div><p><strong>Tool Definition Example:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#a6e22e">[McpToolProvider]</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">SharePointTools</span> {
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">    [McpTool(Name = &#34;search_documents&#34;, Description = &#34;Search for files in a document library&#34;)]</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">string</span> SearchDocs(<span style="color:#66d9ef">string</span> query, <span style="color:#66d9ef">int</span> limit = <span style="color:#ae81ff">5</span>) =&gt; <span style="color:#e6db74">$&#34;Results for {query}&#34;</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If you define multiple tools (for example, 5 methods with <code>[McpTool]</code>), the server will automatically return all 5 in the response:</p>
<p><strong>Output to LLM:</strong></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;tools&#34;</span>: [
</span></span><span style="display:flex;"><span>    { <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;search_documents&#34;</span> },
</span></span><span style="display:flex;"><span>    { <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;get_document&#34;</span> },
</span></span><span style="display:flex;"><span>    { <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;create_document&#34;</span> },
</span></span><span style="display:flex;"><span>    { <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;update_document&#34;</span> },
</span></span><span style="display:flex;"><span>    { <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;delete_document&#34;</span> }
</span></span><span style="display:flex;"><span>  ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="3-the-execution-toolscall">3. The Execution (<code>tools/call</code>)</h2>
<p>The final component is the execution engine. Once the AI knows what tools exist, it needs a way to trigger them. The tools/call method maps incoming JSON arguments to C# types and invokes the corresponding method using Dependency Injection (DI). This allows your tools can access databases, logging, or other enterprise services safely.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-csharp" data-lang="csharp"><span style="display:flex;"><span><span style="color:#66d9ef">private</span> <span style="color:#66d9ef">async</span> Task&lt;<span style="color:#66d9ef">string</span>&gt; InvokeToolAsync(<span style="color:#66d9ef">string</span> toolName, JObject arguments)
</span></span><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Locate the tool metadata in the registry</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> toolInfo = _toolRegistry[toolName];
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Resolve the class instance via DI (Dependency Injection)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> toolInstance = _serviceProvider.GetRequiredService(toolInfo.Type);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> method = toolInfo.Method;
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Prepare the arguments for the C# method</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> parameters = method.GetParameters();
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">object</span>[] methodArgs = <span style="color:#66d9ef">new</span> <span style="color:#66d9ef">object</span>[parameters.Length];
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> (<span style="color:#66d9ef">int</span> i = <span style="color:#ae81ff">0</span>; i &lt; parameters.Length; i++) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> p = parameters[i];
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> (arguments.TryGetValue(p.Name, <span style="color:#66d9ef">out</span> <span style="color:#66d9ef">var</span> token)) {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Convert JSON types to C# types</span>
</span></span><span style="display:flex;"><span>            methodArgs[i] = token.ToObject(p.ParameterType);
</span></span><span style="display:flex;"><span>        } <span style="color:#66d9ef">else</span> <span style="color:#66d9ef">if</span> (p.IsOptional) {
</span></span><span style="display:flex;"><span>            methodArgs[i] = p.DefaultValue;
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Execute the logic and handle Async/Sync results</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> result = method.Invoke(toolInstance, methodArgs);
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> result <span style="color:#66d9ef">is</span> Task&lt;<span style="color:#66d9ef">string</span>&gt; t ? <span style="color:#66d9ef">await</span> t : result?.ToString();
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This execution engine is the bridge between the AI and your application logic. It finds the correct tool, prepares the inputs, runs the method, and returns the result. Everything in a structured way.</p>
<p><strong>Example Input (<code>tools/call</code>):</strong> Sends the function name along with the necessary arguments.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;method&#34;</span>: <span style="color:#e6db74">&#34;tools/call&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;params&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Get_record&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;arguments&#34;</span>: {
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;recordId&#34;</span>: <span style="color:#e6db74">&#34;REC-99&#34;</span>,
</span></span><span style="display:flex;"><span>      <span style="color:#f92672">&#34;status&#34;</span>: <span style="color:#e6db74">&#34;InProgress&#34;</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><strong>Example Output:</strong> The server executes the C# method and wraps the return value into a standardized MCP response format for the AI to process.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;jsonrpc&#34;</span>: <span style="color:#e6db74">&#34;2.0&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;id&#34;</span>: <span style="color:#e6db74">&#34;call-456&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">&#34;result&#34;</span>: {
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;content&#34;</span>: [
</span></span><span style="display:flex;"><span>      {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;text&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&#34;text&#34;</span>: <span style="color:#e6db74">&#34;{ recordId: 12, description: \&#34;...\&#34; }&#34;</span>
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="best-practices">Best Practices</h2>
<ul>
<li>Extract JWT tokens from the <code>Authorization</code> header to expose and connect to the server.</li>
<li>Implement all tools as <code>Task&lt;string&gt;</code>. This aligns with the asynchronous nature of Azure Functions and external Graph API calls.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>It can feel time consuming and a bit tedious to set everything up, but once it’s in place, MCP delivers real value. In my opinion, building an MCP server with the gateway pattern isn’t about adding complexity, it’s about creating a clean, secure, and scalable bridge between LLMs and your enterprise systems. With a platform like Azure Functions, this approach improves reliability and security while making it much easier to grow and adapt your capabilities as LLM-powered applications evolve.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
