What is MCP?
The Model Context Protocol (MCP) is a standardized way for AI systems to access and interact with various external resources file systems, databases, APIs, and other services. Instead of writing unique connectors for each integration, MCP provides a unified interface that any compliant system can use.
By using MCP, developers can empower AI agents to discover, retrieve, and act upon information regardless of the underlying technology. This abstraction allows developers to focus more on what the system does rather than how it connects to the data.
Core MCP Primitives
MCP is built around three key concepts:
1. Resources
These represent any external data the AI agent might need to access: documents, images, files, databases, and so on.
- Consistent Access: Agents use a unified interface to request any resource.
- Always Up to Date: The system ensures access to the latest information.
- Abstracted Complexity: Developers donβt need to write custom code for every data source.
2. Tools
Tools represent the functions or operations that the AI agent can invoke to perform specific tasks like fetching data, editing files, processing content, and more.
- Action Oriented: Agents donβt just read data, they can also modify or trigger workflows.
- Dynamic Selection: Agents can dynamically discover and choose the right tool based on context.
3. Prompts
Prompts are structured templates or instructions that define how AI agents interact with tools and resources.
- Guided Interactions: Prompts ensure all requests are formatted correctly.
- Context Management: Agents maintain coherent interaction across multiple operations.
- Clarity: Standardized prompts reduce ambiguity and misinterpretation.
Together, these primitives allow agents to operate intelligently and autonomously in a modular system.
MCP Architecture: Client-Server Model
MCP is designed using a client-server model:
- The Client (typically the AI agent) sends structured requests, which include prompts and resource identifiers.
- The MCP Server processes these requests, maintains context, and returns results.
This separation ensures scalability, security, and flexibility across various applications.
C# SDK for MCP
To support .NET developers, there is a C# SDK available on GitHub:
π MCP SDK for C#
This SDK simplifies the process of integrating your AI agents with an MCP server, handling serialization, request/response formats, and context management.
Sample Solution: Simple MCP Server and Client
To demonstrate how MCP works in practice, Iβve created a minimalistic MCP solution in C#:
π GitHub Repository: petersaktor/mcp
The solution includes both a server and a client, showing how to:
- Define and expose tools on the server side
- Send prompts and interact with resources
- Log and handle results returned from the MCP server
MCP Server
Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
Console.WriteLine("Hello, MCP Simple server!");
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithPromptsFromAssembly()
.WithToolsFromAssembly()
.WithResourcesFromAssembly();
await builder.Build()
.RunAsync();
SimplePrompt.cs
using Microsoft.Extensions.AI;
using ModelContextProtocol.Server;
using System.ComponentModel;
namespace SimpleServer.Prompts;
[McpServerPromptType]
public static class SimplePrompt
{
[McpServerPrompt, Description("Creates a prompt to summarize the provided message.")]
public static ChatMessage Summarize(string content) =>
new(ChatRole.User, $"Please summarize this content into a single sentence: {content}");
}
SimpleTool.cs
using ModelContextProtocol.Server;
using System.ComponentModel;
namespace SimpleServer.Prompts;
[McpServerToolType]
public static class SimpleTool
{
[McpServerTool, Description("Returns the current time for the specified time zone.")]
public static string GetCurrentTime(string timeZone)
{
try
{
var tz = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
var now = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz);
return now.ToString("o"); // ISO 8601 format
}
catch (TimeZoneNotFoundException)
{
return $"Time zone '{timeZone}' not found.";
}
catch (InvalidTimeZoneException)
{
return $"Time zone '{timeZone}' is invalid.";
}
}
}
SimpleResource.cs
using ModelContextProtocol.Server;
using System.ComponentModel;
namespace SimpleServer.Prompts;
[McpServerResourceType]
public static class SimpleResource
{
[McpServerResource, Description("Returns the Bing endpoint URL.")]
public static string GetBingEndpoint() => "https://bing.com";
}
MCP Client
Program.cs
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
Console.WriteLine($"Hello, MCP Simple client!");
// Create the stdio transport (using current process's stdin/stdout)
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
Name = "MCP Simple Client",
Command = "..\\..\\..\\..\\SimpleServer\\bin\\Debug\\net8.0\\SimpleServer.exe",
// The command to start the MCP server process
// If the server is already running, you can use the same command as the server
// If you want to use a different command, specify it here
});
try
{
// Create the MCP client
var client = await McpClientFactory.CreateAsync(cancellationToken: default, clientTransport: transport);
// List available prompts from the server
foreach (var pr in await client.ListPromptsAsync())
{
Console.WriteLine($"Available prompt: {pr.Name} - {pr.Description}");
}
// Specify the prompt you want to use
var promptName = "Summarize";
// Create a dictionary of arguments for the prompt
IReadOnlyDictionary<string, object> promptArguments = new Dictionary<string, object>()
{
{ "content", "ModelContextProtocol enables structured communication." }
};
// Get the prompt from the server using the specified name and arguments
var prompt = await client.GetPromptAsync(promptName, promptArguments!);
if (prompt == null)
{
Console.WriteLine($"Prompt '{promptName}' not found.");
return;
}
// Print the prompt details
foreach (var message in prompt.Messages)
{
Console.WriteLine($"Message Role: {message.Role}, Content: {message.Content?.Text}");
}
// List available tools from the server
foreach (var to in await client.ListToolsAsync())
{
Console.WriteLine($"Available tool: {to.Name} - {to.Description}");
}
// Specify the tool you want to use
var toolName = "GetCurrentTime";
// Create a dictionary of arguments for the tool
IReadOnlyDictionary<string, object> toolArguments = new Dictionary<string, object>()
{
{ "timeZone", "Pacific Standard Time" } // Example time zone
};
// Call tool from the server using the specified name and arguments
var result = await client.CallToolAsync(toolName, toolArguments!);
if (result == null)
{
Console.WriteLine($"Tool '{toolName}' not found.");
return;
}
// Print the tool result
foreach (var res in result.Content)
{
Console.WriteLine($"Tool Result: {res.Text}");
}
// List available resources from the server
foreach (var res in await client.ListResourcesAsync())
{
Console.WriteLine($"Available resource: {res.Name} - {res.Description}");
}
// Specify the resource you want to use
var resourceName = "resource://GetBingEndpoint";
// Get the resource from the server using the specified name
var resource = await client.ReadResourceAsync(resourceName);
if (resource == null)
{
Console.WriteLine($"Resource '{resourceName}' not found.");
return;
}
// Print the resource details
foreach (var res in resource.Contents)
{
Console.WriteLine($"Resource Content: {((TextResourceContents)res)?.Text}");
}
}
catch (Exception ex)
{
Console.WriteLine("Error creating MCP client: " + ex.Message);
return;
}
Summary
MCP brings a new level of consistency and modularity to AI integrations. With a simple but powerful abstraction using Resources, Prompts, and Tools, and a clean client-server model, you can build intelligent agents that interact seamlessly with the outside world.
Explore more:
Top comments (0)