A cloud computing platform and infrastructure for building, deploying and managing applications and services through a worldwide network of Microsoft-managed datacenters.
Do the routing in your API, not in the model. In Semantic Kernel you can call MCP tools directly with explicit arguments and keep the LLM out of the decision loop. For transport-level context, front the MCP server with an SSE-capable proxy like Azure API Management and inject headers there. Details below.
A recommended, deterministic pattern
- Programmatic tool invocation - no LLM routing
Disable auto function calling and invoke the MCP tool yourself with the exact args you want to pass, like index_name, endpoint, top_k. In SK this is done with Function Choice Behavior or by directly invoking the function. This keeps routing deterministic and auditable.
Python - API side (FastAPI + SK)
from semantic_kernel import Kernel
from semantic_kernel.functions import KernelArguments
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion
from semantic_kernel.connectors.ai.function_choice_behavior import FunctionChoiceBehavior
kernel = Kernel()
kernel.add_service(OpenAIChatCompletion())
# Add MCP plugin (SSE)
from semantic_kernel.connectors.mcp import MCPSsePlugin
pinecone_plugin = MCPSsePlugin(name="pinecone_mcp", url="https://mcp.example.com/mcp") # SSE endpoint
await kernel.add_plugin(pinecone_plugin) # tools become kernel functions
# Tip: you can register multiple MCP plugins with different names for different indices
# Deterministic call - no LLM involved
fn = kernel.plugins.get_function("pinecone_mcp", "search_pinecone_tool")
args = KernelArguments(query=request.query, top_k=5, index_name=request.index) # pass index deterministically
result = await fn.invoke(kernel, arguments=args) # returns tool result directly
- If you still want an agent reply around the tool output, run a second LLM step that formats
resultinto text, but keep the tool call itself outside the LLM.
Make the index a first-class parameter
On the MCP server, update the tool signature to accept index_name (and optionally service_id or tenant_id). Route to the correct Pinecone or Azure AI Search connection in server code.
- Do not pass secrets from SK to MCP. Resolve keys in the server via Managed Identity + Key Vault.
Python - MCP server side (FastMCP)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("vector-search")
@mcp.tool()
async def search_pinecone_tool(query: str, index_name: str, top_k: int = 5) -> dict:
# look up credentials securely
from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
kv = SecretClient(f"https://{KV_NAME}.vault.azure.net", DefaultAzureCredential())
api_key = (await kv.get_secret(f"pinecone-{index_name}-key")).value
endpoint = (await kv.get_secret(f"pinecone-{index_name}-endpoint")).value
# instantiate client and query...
return {"hits": [...]}
Key Vault + Managed Identity is the Microsoft-recommended pattern for secrets at runtime.
Connection registry - many indexes
If you have many indexes, either:
Register one MCP plugin per logical route (for example pinecone_sales, pinecone_support) and pick the plugin by tenant in the API, or
Keep a single tool that accepts index_name and look up connection details on the server.