Introduction #
The growth of large‑language models has been exponential since Google introduced the transformer in June 2017. Just a couple of years ago, I was deploying an OpenAI‑powered RAG pipeline while still wrestling with zero‑, one‑, and few‑shot prompting techniques. Then, in June 2023, OpenAI added function calling to the Chat Completions API—shortly followed by multi‑step Agents with tool‑chaining and self‑calling. Finally, on November 25, 2024, Anthropic released the Model Context Protocol (MCP), a new standard for securely connecting AI assistants to the systems and content repositories where enterprise data lives.
Core Components #
- Model Context Protocol Specification & SDKs
A formal schema (usually in TypeScript/JSON‑RPC) that defines the wire‑level messages, data types, and session semantics, plus official SDKs in Python, TypeScript, C#, Java, etc. so every implementation speaks the same “language”
- Hosts
LLM applications (e.g. Claude Desktop, IDE plugins, custom agents or builds) that initiate MCP sessions
- MCP Client
Lightweight connectors running inside the host. Each MCP client maintains a 1:1 JSON‑RPC session with a single server, handling protocol framing, capability negotiation, and bidirectional message routing
- MCP Server
External services (local or remote) that expose “tools,” “resources,” and “prompts” via MCP primitives. A server implements the MCP spec over stdio (for local), SSE and HTTP (for remote), and responds to client calls with structured results. It also offers features like Tools, Resources and Prompts
- Local or Remote Services
Local or remote DataSources/APIs that the Servers can connect to
High Level Flow #
[ USER ]
│
▼
[ Host App ] ← Claude Desktop, IDE Plugin, Custom Client/Agent
│
└──▶ [ MCP Client ]
(Implements JSON-RPC 2.0)
- Maintains session state
- Handles tool calls, context updates
- Talks to…
▼
[ MCP Server(s) ] ← Exposes tools/resources
- Responds to tool invocation
- Exposes APIs, context documents, databases, etc.
Proof of Concept #
I’ve developed many MCP tools using Claude (personal use), but I thought, why not build our own MCP as a proof of concept using VS Code as it just landed. The goal is to create an MCP Server to fetch Azure Inventory and Azure Recommendations. POC requirements
- VS Code /w copilot
- Python SDK and npm
- Azure Account
MCP support in agent mode in VS Code is available starting from VS Code 1.99 and is currently in preview. Documentation
There are a few prerequisites before proceeding,
- Install the Github Copilot VS Code Extension
- Enable MCP support on settings.json manually or by going to Files > Preferences > Settings , and set these parameters
"chat.mcp.discovery.enabled": true
and"chat.mcp.enabled": true
- Setup MCP.json for your MCP Server discovery , add
.vscode/mcp.json
in your VS Code workspace or global settings.json
I will not cover setting up an Azure account and service principal here. In short, you need to create a service principal or app registration, obtain the client ID and secret, and add them to the .env
file.
I have set the environment using uv
, here the dependencies
dependencies = [
"azure-identity>=1.23.0",
"azure-mgmt-advisor>=9.0.0",
"azure-mgmt-resource>=24.0.0",
"mcp[cli]>=1.10.1",
"python-dotenv>=1.1.1",
]
That said, creating an MCP server is fairly simple. All you need to do is import the libraries , setup @mcp.tool()
decorator to define your tools and use type hints along with doc strings to give the tool a context for the LLM.
from mcp.server.fastmcp import FastMCP # MCP server framework
# Instantiate the FastMCP server with the name of your server "azure-mcp"
mcp = FastMCP("azure-mcp")
# Asynchronous function to fetch all Azure resources in the subscription
@mcp.tool()
async def list_azure_resources() -> list[dict[str, Any]]:
"""List all Azure resources. Uses subscription ID from environment."""
return await fetch_azure_resources()
# Asynchronous function to fetch Azure Advisor recommendations
@mcp.tool()
async def get_azure_advisor_recommendations() -> list[dict[str, Any]]:
"""Get Azure Advisor recommendations. Uses subscription ID from environment."""
return await fetch_advisor_recommendations()
if __name__ == "__main__":
mcp.run(transport="streamable-http")
Now, all that’s left is to create the actual tool functionality and add your tool to the MCP.json
. As per the code snippet, my mcp.tool list_azure_resources()
calls fetch_azure_resources()
. We will write a function that uses the Azure ResourceManagementClient to retrieve the resources.
async def fetch_azure_resources() -> list[dict[str, Any]]:
subscription_id = os.environ.get("AZURE_SUBSCRIPTION_ID")
if not subscription_id:
raise ValueError("AZURE_SUBSCRIPTION_ID not set in env")
credential = DefaultAzureCredential()
client = ResourceManagementClient(credential, subscription_id)
results = []
async for rg in client.resource_groups.list():
async for res in client.resources.list_by_resource_group(rg.name):
results.append({
"resource_group": rg.name,
"name": res.name,
"type": res.type,
"location": res.location,
"id": res.id
})
await client.close()
await credential.close()
return results
Finally, you need to add the MCP Server in .vscode/mcp.json
The Server name needs to match what you instantiated with FastMCP earlier
{
"servers": {
"azure-mcp": {
"url": "http://localhost:8000/mcp/",
}
},
}
Now you can run the MCP server with uv/python or just use VS Code command palette (ctrl/cmd + shift + p) and look for MCP: List Servers
. Change Copilot mode to Agent
from Ask.
(mcppoc) @Panda ➜ mcppoc git(master) uv run main.py
INFO: Started server process [15728]
INFO: Waiting for application startup.
[07/13/25 17:04:59] INFO StreamableHTTP session streamable_http_manager.py:112
manager started
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
Here’s the MCP server in action,
While I could have used stdio
transport since everything is running locally, I opted for http streamable
transport to capture the traffic over the network for analysis. With stdio, I’d need to explicitly enable and parse server-side logging to observe tool interactions , though VS Code does provide logging if you use the MCP Server extension to manage the server.

Below are logs from the perspective of VScode to MCP Server showing discovery of tools using JSON-RPC
[debug] [editor -> server]
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{"roots":{"listChanged":true},"sampling":{},"elicitation":{}},"clientInfo":{"name":"Visual Studio Code","version":"1.102.0"}}}
[debug] [server -> editor]
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"azure-mcp","version":"1.10.1"}}}
[debug] [editor -> server]
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
[debug] [server -> editor]
{"jsonrpc":"2.0","id":2,"result":
{"tools":[
{"name":"list_azure_resources","description":"List all Azure resources in tabular format. Uses subscription ID from environment.","inputSchema":{"properties":{},"title":"list_azure_resourcesArguments","type":"object"},"outputSchema":{"properties":{"result":{"items":{"additionalProperties":true,"type":"object"},"title":"Result","type":"array"}},"required":["result"],"title":"list_azure_resourcesOutput","type":"object"}},
{"name":"get_azure_advisor_recommendations","description":"Get Azure Advisor recommendations in tabular format. Uses subscription ID from environment.","inputSchema":{"properties":{},"title":"get_azure_advisor_recommendationsArguments","type":"object"},"outputSchema":{"properties":{"result":{"items":{"additionalProperties":true,"type":"object"},"title":"Result","type":"array"}},"required":["result"],"title":"get_azure_advisor_recommendationsOutput","type":"object"}}]}}
[info] Discovered 2 tools
Final Words #
In conclusion, is MCP solving anything? Well, yes and no. Core developers are largely content with existing methods - MCP hasn’t fundamentally changed the paradigm. However, what it does offer is valuable: a layer of standardization that turns the spaghetti mess of tool invocation into a clean, structured protocol. It’s not revolutionary, but it’s a pragmatic step toward maintainable, scalable tooling … for now.
If you would like to read more , do check this white paper https://arxiv.org/pdf/2504.16736
What’s Next #
Next MCP posts would be focused on,
- S in MCP stands for Security - Security Mechanism in MCP framework (oAuth)
- From Theory to Exploit: Practical Attacks on MCP Deployment - We will take a look at Attack Vectors related to some ongoing CVE and implement a Proof of concept
- Cisco’s Foundation-sec‑8B Isn’t Just Another LLM — But Is It Any Better?
- Can MCP Replace the CLI? - We will attempt to interface with the Cli world.