Los Modelos de Lenguaje (LLM) son increíblemente potentes, pero por sí solos viven completamente aislados del mundo real. ¿Cómo hacemos para que un chatbot pueda consultar el estado de un pedido, ver el tiempo en tu ciudad o buscar en tu base de datos? La respuesta está en el Model Context Protocol (MCP).
Tabla de contenidos
1 - Qué es Model context protocol (MCP)?
El Model Context Protocol (MCP) es un protocolo estándar diseñado para facilitar la comunicación entre clientes (por ejemplo, apps, agentes, bots) y servidores que exponen herramientas, funciones o datos para que los modelos de IA y los consumidores los utilizan.
En otras palabras, los siguientes puntos:
- Permite que un servidor declare un conjunto de funciones que se pueden invocar de forma remota.
- A estas funciones o herramientas se les llama Tools.
- Clientes, servidores y modelos pueden entenderse sin depender de cada API personalizada.
- En lugar de que un modelo esté “cerrado” a una sola lógica, puede invocar múltiples herramientas a través de MCP.
Con esto lo que terminamos consiguiendo es que los LLM puedan utilizar recursos externos.
1.1 - Arquitectura de un MCP
MCP se basa en una arquitectura cliente-servidor muy sencilla:
- Host MCP: Es la aplicación donde vive el LLM (por ejemplo, un chatbot, tu IDE de programación, etc.).
- Cliente MCP: Reside en el Host y se encarga de traducir las peticiones del LLM para que el servidor las entienda.
- Servidor MCP: Es una pequeña aplicación que expondrá recursos, los cuales los queremos ofrecer al LLM. Estas capacidades pueden ser de tres tipos:
- Tools (Herramientas): Funciones que el LLM puede ejecutar. Por ejemplo, obtener_clima(ciudad) o consultar_stock_producto(id).
- Resources (Recursos): Información o datos que proveemos al modelo para darle más contexto, como el contenido de un archivo o el resultado de una consulta a una base de datos.
- Prompts: Plantillas de texto predefinidas para ayudar al LLM a realizar tareas específicas.
El flujo es simple; cuando haces una pregunta al LLM, el cliente MCP se comunica con uno o varios servidores MCP, descubre qué "capacidades" tienen disponibles y se las presenta al modelo. El LLM, de forma inteligente, decide si necesita usar alguna de esas herramientas o recursos para construir una respuesta mucho más completa y precisa.

La gran ventaja es que desacopla por completo el LLM de las herramientas. Puedes crear un único servidor MCP para tu base de datos y cualquier LLM compatible podrá usarlo sin necesidad de programar una integración a medida para cada uno.
1.2 - Qué beneficios trae utilizar MCP?
Algunos de los beneficios que trae utilizar MCP son estos aunque no son los únicos
- Facilita la integración con clientes como tu IDE, o editores que soportan MCP ya que detectan automáticamente que Tools tenemos disponibles y las podemos ejecutar con mucha facilidad, esto es lo que vamos a ver en la parte práctica.
- Si estás construyendo una app con IA que no solo llama a un modelo, sino que también necesita consultar tu base de datos, llamar a tu API, procesar datos complejos, el MCP te da un patrón para exponer esas Tools.
- Si tienes múltiples modelos o servicios, MCP ayuda a unificar cómo los clientes los consumen.
2 - Crear un servidor MCP con C#
Pasemos a la parte práctica, que nos va a servir como un hola mundo dentro de lo que es un MCP.
Para ello creamos una app de consola y necesitamos dos paquetes. Primer el ModelContextProtocol, el cual está en versión preview y luego para hacernos la vida más facil, Microsoft.Extenions.Hosting
Con ello podemos construir el siguiente código:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
Este código a primera vista es muy sencillo, porque lo es. Al margen de crear el application builder, tenemos nuestro contenedor de dependencias que incluye:
AddMcpServer()para registrar el servidor MCPWithStdioServerTransport()para la capa de transporte de entrada y salida, el servidor MCP en vez de abrir un puerto de red simplemente escucha por lo que llega en stdin y responde por stdout.- Esto es en local, fuera de local lo suyo es una petición http y un endpoint.
WithToolsFromAssembly(): este método escanea el proyecto actual e incluye todas las herramientas (recuerda se llaman tools) que tenemos creadas.- Alternativamente podemos utilizar
WithTools<TTool>()
- Alternativamente podemos utilizar
Ahora debemos crear la tool o herramienta MCP que vamos a utilizar. Para ello simplemente creamos una clase, la cual decoramos con el atributo [McpServerToolType], y cada una de las herramientas que podemos utilizar con McpServerTool
[McpServerToolType]
public class WeatherTool
{
[McpServerTool, Description("Returns the temperature in degrees.")]
public static string GetWeather(string city)
{
//in a real case this will call an API
var temperature = Random.Shared.Next(1, 40);
return $"the temperature in {city} is {temperature}°C";
}
}
Para seguir con los ejemplos que solemos ver con C# simularemos una app del tiempo, donde le pasas la ciudad y te devuelve el tiempo de esa ciudad.
En producción esto debería estar en una URL, pero aquí como test, nos sirve como una app de consola. Explicación producción.
Para acceder a este MCP server tenemos dos opciones, la primera es a través de la consola y el navegador.
Para ello necesitamos node instalado y el mcp-inspector (npm install -g @modelcontextprotocol/inspector)
Una vez lo tenemos, debemos ejecutar el siguiente comando:
mcp-inspector dotnet run --project ‘project/path’
Esto nos abre una web, la cual puede acceder al servidor MCP, podemos listar las tools, etc:

2.1 - Conectar a un MCP local de C# con visual studio code
La otra opción es con visual studio code, donde necesitamos en la ruta de nuestro directorio una carpeta llamada .vscode con el fichero mcp.json, el cual contendrá la ruta a nuestro proyecto:
{
"inputs": [],
"servers": {
"ExampleIA03MCP": {
"type": "stdio",
"command": "dotnet",
"args": [
"run",
"--project",
"..\\ExampleIA03MCP.csproj"
]
}
}
Con esto, cualquier chat integrado en VS Code que soporte MCP podrá descubrir y usar tus Tools automáticamente.
Si esto fuera un proyecto en producción y desplegado pues sería una URL.
Para probar que nuestro MCP funciona simplemente debemos hablar con el chat.
Podemos forzar el uso de nuestra tool invocandola con # en nuestro caso #ExampleIAMCP.

Y ya de aquí podemos ver el resultado con total claridad:

Nota, alternativamente hay otra librería que se esta haciendo muy popular llamada MCPSharp , que también es recomendable.
Si quisierais un post sobre esa librería exclusivamente me lo podéis dejar abajo en los comentarios o en los de youtube y podemos hacer contenido sobre ella.
3 - Mejorar nuestro MCP con C#
Dentro de los MCPs no solo tenemos los tools que acabamos de mencionar, sino que tenemos prompts y recursos.
Antes de crear los recursos y los prompts no te olvides que debes especificarlos en el builder:
var builder = Host.CreateApplicationBuilder(args);
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly()
.WithResourcesFromAssembly() // 👈
.WithPromptsFromAssembly(); // 👈
await builder.Build().RunAsync();
3.1 - Los prompts en los MCP con C#
Cuando tenemos un prompt dentro de un MCP no es el texto que envía el usuario, sino que son capacidades extra que el servidor expone de igual manera que hace con las tools.
Su principal ventaja es encapsular la lógica de un prompt muy bien diseñado. En lugar de que el desarrollador del cliente tenga que ingeniárselas para crear un prompt perfecto, el servidor MCP ya se lo da hecho.
Al final es lo mismo que si lo escribe un usuario, pero, escrito por quienes trabajan con dicho prompt:
[McpServerPromptType]
public class WeatherPrompt
{
[McpServerPrompt(Name = "FormatReport")]
public static string MakeWeatherFriendly()
{
return """
from the next information on the weather provided by the server.
{{input_clima}}
Convert it into
Example:
Input: "the temperature in Madrid is 25°C"
Output: "Madrid,25,°C"
""";
}
}
En visual studio code, puedes acceder a él utilizando ‘/’ y automáticamente te rellena el prompt:

3.2 - Los recursos en los MCP con C#
Los recursos son partes que dan información sobre nuestro MCP, la idea es explicar cómo funciona y que estos se “activen” antes de que el usuario mande el prompt al servidor.
Por ejemplo en este caso le decimos que solo ciertas ciudades están permitidas:
[McpServerResourceType]
public class WeatherResource
{
[McpServerResource(Name = "SupportedCities")]
public static string SupportedCities()
{
return """
**Critical context:**
Only the following cities are allowed:
- Madrid
- London
- Lima
- Buenos Aires
If the user ask for any other city you should return an error and
do not call the GetWeather tool.
""";
}
}
El problema de los recursos es que estos están controlados desde la parte del cliente, por lo que no podemos garantizar que van a ser utilizados, así que debemos controlar en el servidor, donde tenemos la tool que lo que indicamos en el resource está bien configurado.
De hecho, Copilot Chat en VS code, los ignora completamente. Podemos utilizar extensiones como OpenAI, Continue/Cline y estos si lo soportan, o directamente cursor o claude code en la CLI.
4 - Crear un Cliente MCP con C#
No quiero terminar no sin antes mostrar cómo podemos crear un cliente MCP directamente en C#.
Y podemos utilizar ModelContextProtocol con la clase MCPClient, o como he mencionado antes la librería MCPSharp, para lo que vamos a ver hoy no importa mucho cual porque ambas funcionan bien, pero si queréis más contenido dejadlo abajo en los comentarios.
Para configurarlo, simplemente creamos un McpClient y le indicamos donde está nuestro servidor, en nuestro caso, lo estamos haciendo local, pero recuerda que puede ser completamente online.
var serverExePath = @"C:\Path to exe";
var transport = new StdioClientTransport(new StdioClientTransportOptions
{
Name = "WeatherClient",
Command = serverExePath,
Arguments = Array.Empty<string>()
});
await using var client = await McpClient.CreateAsync(transport);
var tools = await client.ListToolsAsync();
var prompts = await client.ListPromptsAsync();
var resources = await client.ListResourcesAsync();
Console.WriteLine("Tools: " + string.Join(", ", tools.Select(t => t.Name)));
Console.WriteLine("Prompts: " + string.Join(", ", prompts.Select(p => p.Name)));
Console.WriteLine("Resources: " + string.Join(", ", resources.Select(r => r.Uri)));
Y con este cliente podemos leer los prompts, recursos y tools que tenemos.
Para el escenario de hoy simplemente vamos a ejecutar nuestra tool, para ello la debemos de llamar (debemos de sabernos el nombre de dicha tool) y enviar los parámetros necesarios, que en nuestro caso es únicamente la ciudad.
var toolResult = await client.CallToolAsync(
"get_weather",
new Dictionary<string, object?> { ["city"] = "Madrid" },
cancellationToken: CancellationToken.None
);
var weatherLine = toolResult.Content
.OfType<TextContentBlock>()
.FirstOrDefault()?.Text ?? "";
Console.WriteLine(weatherLine);
Mencionar que como ves, estamos ignorando el prompt, y estamos ignorando los recursos, porque esas funcionalidades son controladas desde el lado cliente que es lo que veremos en uno de los siguientes vídeos!