Sitemap

Leverage MCP tools with Semantic Kernel Agents

Understanding Agentic Protocols — Part 2

7 min readJun 3, 2025

--

In my previous article of this series, we introduced the concept of Model Context Protocol (MCP) and demonstrated how to quickly deploy a tool as an MCP server and consume through Claude Desktop as MCP host.

In this chapter, we will do a step further and explore how to build your own Agent leveraging Semantic Kernel (SK) and equipping it with MCP servers as tool. Before starting, let’s quickly recap what SK and MCP are.

Semantic Kernel

Semantic kernel is a lightweight framework which make it easier to develop AI-powered applications. It falls into the category of AI orchestrators like Llama-Index, LangChain, TaskWeaver and so on.

It serves as a middleware layer, allowing for the orchestration of LLMs alongside traditional programming code in languages such as C#, Python, and Java . By providing abstractions for AI services, plugins, memory management, and planning, Semantic Kernel simplifies the development of AI-driven applications, making it easier to build intelligent agents that can perform complex tasks, interact with users, and adapt to changing requirements.

Model Context Protocol

The Model Context Protocol (MCP) is a standard designed to unify how large language models (LLMs) interact with external tools and data sources — much like how HTTP and REST APIs standardize web communication.

The MCP aims at addressing the following challenges:

  • Current fragmentation: Frameworks like Semantic Kernel and other popular offering - like LangChain or AutoGen - rely on custom, often siloed integrations.
  • Maintenance burden: Changes in provider-specific APIs require constant updates.
  • No universal standard: Each system connects to resources in its own way, limiting scalability and interoperability.

From an architecture perspective, the protocol is made of the following components:

  • MCP Host: The app managing LLM interactions (e.g., Claude Desktop, GitHub Copilot).
  • MCP Client: The middleware that connects hosts to external tools via the protocol.
  • MCP Server: The actual tools, prompts, or data sources the LLM can call.

What’s the difference between an AI framework and a Protocol?

Frameworks like Semantic Kernel (SK) and protocols like the Model Context Protocol (MCP) both aim to simplify how AI systems interact with tools and data — but they operate at different layers of abstraction and serve distinct purposes.

SK is a developer-facing framework that provides structured abstractions (e.g., planners, plugins, memory, and function calling) to help build and orchestrate AI-powered workflows inside applications. It offers rich tooling for working with language models, chaining functions, and managing state.

MCP, on the other hand, is a protocol — a standardized interface specification — that defines how LLMs can dynamically discover and invoke external tools at runtime, regardless of the implementation language or orchestration logic behind them. While SK is opinionated and extensible within its ecosystem, MCP is designed to be interoperable and model-agnostic, enabling cross-platform tool invocation directly from the LLM’s reasoning loop.

Building an AI Agent in SK with MCP Plugin

Let’s get practical. I this demonstration, I will re-use a SK plugin for TextToSQL interaction I developed here. Note that the plugin itself is leveraging LangChain as well — which confirms the design principle of AI Agents of modularity and abstractions. In other words, AI Agents’ components can be seen as stand-alone assets, which are agnostic with respect to the framework you use or the programming language you prefer.

Note: When it comes to modularity and abstraction, MCP is truly the cherry on top. It enables agent tools to be fully agnostic. For instance, in the repository mentioned earlier, I was able to integrate a LangChain component into an SK-based agent using SK-specific taxonomy. With MCP, that same LangChain component becomes compatible across any framework or host that supports the protocol — making it reusable and interoperable by design. The best part? Adoption is growing rapidly, especially among enterprise platforms.

Let’s have a look at the plugin:

import os
from dotenv import load_dotenv
from mcp.server.fastmcp import FastMCP
from semantic_kernel.functions import kernel_function

from langchain_community.utilities.sql_database import SQLDatabase
from langchain_community.agent_toolkits.sql.toolkit import SQLDatabaseToolkit
from langchain_openai import AzureChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

# -------------------------------
# Load environment variables
# -------------------------------
load_dotenv()


AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
AZURE_OPENAI_DEPLOYMENT = os.getenv("AZURE_OPENAI_DEPLOYMENT")
AZURE_OPENAI_API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION", "2025-01-01-preview")

# -------------------------------
# Initialize MCP Server
# -------------------------------
mcp = FastMCP("SQLAgentServer")

# -------------------------------
# Global components: LLM, DB, Tools, Prompt, Agent
# -------------------------------
llm = AzureChatOpenAI(
openai_api_version=AZURE_OPENAI_API_VERSION,
azure_deployment=AZURE_OPENAI_DEPLOYMENT,
)

db = SQLDatabase.from_uri("sqlite:///chinook.db")
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
tools = toolkit.get_tools()

prompt = ChatPromptTemplate.from_messages(
[
("system", """You are an agent designed to interact with a SQL database.
Given an input question, create a syntactically correct SQLite query to run, then look at the results of the query and return the answer.
Unless the user specifies a specific number of examples they wish to obtain, always limit your query to at most 5 results.
You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for the relevant columns given the question.
You have access to tools for interacting with the database.
Only use the below tools. Only use the information returned by the below tools to construct your final answer.
You MUST double check your query before executing it. If you get an error while executing a query, rewrite the query and try again.

DO NOT make any DML statements (INSERT, UPDATE, DELETE, DROP etc.) to the database.

To start you should ALWAYS look at the tables in the database to see what you can query.
Do NOT skip this step.
Then you should query the schema of the most relevant tables.
"""),
MessagesPlaceholder("chat_history", optional=True),
("human", "{input}"),
MessagesPlaceholder("agent_scratchpad"),
]
)

chat_history = ChatMessageHistory()
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

agent_with_memory = RunnableWithMessageHistory(
executor,
lambda session_id: chat_history,
input_messages_key="input",
history_messages_key="chat_history",
)

# -------------------------------
# MCP Tool: SQL Agent Query Runner
# -------------------------------
@mcp.tool()
def run_sql_query(user_input: str) -> str:
"""
Tool specialized in retrieving information from the chinook database using SQL queries. The chinook database is a sample database that contains information about a music store, including artists, albums, tracks, and customers.

"""
result = agent_with_memory.invoke(
{"input": user_input},
config={"configurable": {"session_id": "<foo>"}},
)
return result["output"]

# -------------------------------
# MCP Entry Point
# -------------------------------
if __name__ == "__main__":
mcp.run(transport="stdio")

As you can see, the core structure doesn’t change. There are three main additional elements that we need to keep in mind:

  • Initialization of the MCP
mcp = FastMCP("SQLAgentServer")
  • mcp.tool() decorator: this is needed to initialize the core function of the mcp, in our case the SQL tool.
@mcp.tool()
def func():
return

Note that MCP servers can host multiple tools and resources. You will have as many decorators as the number of assets you host on your server, each one provided with a name and description in natural language.

  • MCP entry point: When running Model Context Protocol (MCP) servers, you can choose between two primary transport mechanisms: STDIO (Standard Input/Output) and SSE (Server-Sent Events). STDIO transport facilitates communication between the client and server through standard input and output streams. SSE transport enables servers to push real-time updates to clients over HTTP. Clients initiate a connection via an HTTP GET request, and the server sends events as they occur. In our case, we will go for the STDIO mechanism:
if __name__ == "__main__":
mcp.run(transport="stdio")

Great! Now that we have our plugins, we need to attach them to our agent in SK. To do so, we will use the The MCPStdioPlugin,a component of SK Python SDK that facilitates integration with MCP servers via STDIO. This plugin allows your Semantic Kernel applications to communicate with local MCP servers by spawning them as subprocesses and interacting through their stdin and stdout streams.

Let’s see how to do that:

  mcp_plugin = MCPStdioPlugin(
name="SQLServer",
command="python",
args=[str(Path("your_path_to_mcp_server")],
)

The moment you initialize this plugin, you can manage it as a standard SK plugin, meaning that you can either add it to your kernel, or directly at your agent level. In my case, I added the plugin directly at kernel level:

try:
await mcp_plugin.connect()
print("✅ MCP plugin connected.")
kernel.add_plugin(mcp_plugin, plugin_name="sqltool")
print("🔧 MCP plugin registered with kernel.")
except Exception as e:
print(f"❌ Error: Could not register the MCP plugin: {str(e)}")
await mcp_plugin.close()
sys.exit(1)

agent = ChatCompletionAgent(
kernel=kernel,
name="Assistant",
instructions="You are a helpful assistant."
)

Note: mcp_plugin.connect() is asynchronous because it launches and manages a subprocess with potentially streaming input/output. By doing so, the app waits for the tool to be ready and it can support long-lived communication (e.g., streaming SQL results).

We are now ready to test our app (you can find the whole code here):

python test_mcp_sql.py

As you can see, the Agent first answered without the tool to greet me, then it invoked the MCP tool to run the SQL query as needed.

Conclusion

By combining Semantic Kernel’s orchestration capabilities with the interoperability of the Model Context Protocol, developers can build AI agents that are both modular and framework-agnostic. This integration allows components — which can be developed with the framework and language of your choice — to be reused seamlessly across different environments, making your agent architecture more scalable and future-proof.

References

--

--

Valentina Alto
Valentina Alto

Written by Valentina Alto

Data&AI Specialist at @Microsoft | MSc in Data Science | AI, Machine Learning and Running enthusiast

No responses yet