using MCPDemo.Models;
namespace MCPDemo.Services
{
public interface IMcpServer
{
Task<McpMessage> HandleMessageAsync(McpMessage message);
Task<List<McpTool>> GetToolsAsync();
Task<List<McpResource>> GetResourcesAsync();
}
public class McpServer : IMcpServer
{
private readonly ILogger<McpServer> _logger;
private bool _initialized = false;
public McpServer(ILogger<McpServer> logger)
{
_logger = logger;
}
public async Task<McpMessage> HandleMessageAsync(McpMessage message)
{
_logger.LogInformation($"Handling MCP message: {message.Method}");
try
{
return message.Method switch
{
"initialize" => await HandleInitializeAsync(message),
"initialized" => HandleInitialized(message),
"tools/list" => await HandleToolsListAsync(message),
"tools/call" => await HandleToolCallAsync(message),
"resources/list" => await HandleResourcesListAsync(message),
"resources/read" => await HandleResourceReadAsync(message),
_ => CreateErrorResponse(message.Id, -32601, "Method not found")
};
}
catch (Exception ex)
{
_logger.LogError(ex, "Error handling MCP message");
return CreateErrorResponse(message.Id, -32603, "Internal error");
}
}
private Task<McpMessage> HandleInitializeAsync(McpMessage message)
{
var serverInfo = new McpServerInfo
{
Name = "MCP Demo Server",
Version = "1.0.0",
ProtocolVersion = "2024-11-05",
Capabilities = new McpCapabilities
{
Tools = new McpToolCapabilities { ListChanged = false },
Resources = new McpResourceCapabilities { Subscribe = false, ListChanged = false }
}
};
return Task.FromResult(new McpMessage
{
Id = message.Id,
Result = serverInfo
});
}
private McpMessage HandleInitialized(McpMessage message)
{
_initialized = true;
_logger.LogInformation("MCP Server initialized");
// No response needed for initialized notification
return new McpMessage
{
Id = message.Id,
Result = new { }
};
}
private async Task<McpMessage> HandleToolsListAsync(McpMessage message)
{
var tools = await GetToolsAsync();
return new McpMessage
{
Id = message.Id,
Result = new { tools }
};
}
private async Task<McpMessage> HandleToolCallAsync(McpMessage message)
{
// Extract tool call from params
var toolCall = System.Text.Json.JsonSerializer.Deserialize<McpToolCall>(
System.Text.Json.JsonSerializer.Serialize(message.Params));
if (toolCall == null)
{
return CreateErrorResponse(message.Id, -32602, "Invalid tool call parameters");
}
var result = await ExecuteToolAsync(toolCall);
return new McpMessage
{
Id = message.Id,
Result = result
};
}
private async Task<McpMessage> HandleResourcesListAsync(McpMessage message)
{
var resources = await GetResourcesAsync();
return new McpMessage
{
Id = message.Id,
Result = new { resources }
};
}
private Task<McpMessage> HandleResourceReadAsync(McpMessage message)
{
// This is a simplified implementation
return Task.FromResult(new McpMessage
{
Id = message.Id,
Result = new
{
contents = new[]
{
new McpContent
{
Type = "text",
Text = "Sample resource content"
}
}
}
});
}
public Task<List<McpTool>> GetToolsAsync()
{
return Task.FromResult(new List<McpTool>
{
new McpTool
{
Name = "get_time",
Description = "Get the current time",
InputSchema = new
{
type = "object",
properties = new { },
required = new string[] { }
}
},
new McpTool
{
Name = "echo",
Description = "Echo back the provided message",
InputSchema = new
{
type = "object",
properties = new
{
message = new
{
type = "string",
description = "The message to echo back"
}
},
required = new[] { "message" }
}
},
new McpTool
{
Name = "calculate",
Description = "Perform basic arithmetic calculations",
InputSchema = new
{
type = "object",
properties = new
{
operation = new
{
type = "string",
description = "The operation to perform (add, subtract, multiply, divide)",
@enum = new[] { "add", "subtract", "multiply", "divide" }
},
a = new
{
type = "number",
description = "First number"
},
b = new
{
type = "number",
description = "Second number"
}
},
required = new[] { "operation", "a", "b" }
}
}
});
}
public Task<List<McpResource>> GetResourcesAsync()
{
return Task.FromResult(new List<McpResource>
{
new McpResource
{
Uri = "demo://sample-data",
Name = "Sample Data",
Description = "Sample data resource for demonstration",
MimeType = "text/plain"
}
});
}
private async Task<McpToolResult> ExecuteToolAsync(McpToolCall toolCall)
{
try
{
return toolCall.Name switch
{
"get_time" => new McpToolResult
{
Content = new List<McpContent>
{
new McpContent
{
Type = "text",
Text = $"Current time: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"
}
}
},
"echo" => new McpToolResult
{
Content = new List<McpContent>
{
new McpContent
{
Type = "text",
Text = toolCall.Arguments.ContainsKey("message")
? toolCall.Arguments["message"].ToString() ?? "No message provided"
: "No message provided"
}
}
},
"calculate" => await ExecuteCalculateToolAsync(toolCall.Arguments),
_ => new McpToolResult
{
Content = new List<McpContent>
{
new McpContent
{
Type = "text",
Text = $"Unknown tool: {toolCall.Name}"
}
},
IsError = true
}
};
}
catch (Exception ex)
{
return new McpToolResult
{
Content = new List<McpContent>
{
new McpContent
{
Type = "text",
Text = $"Error executing tool: {ex.Message}"
}
},
IsError = true
};
}
}
private Task<McpToolResult> ExecuteCalculateToolAsync(Dictionary<string, object> arguments)
{
try
{
var operation = arguments["operation"].ToString();
var a = Convert.ToDouble(arguments["a"]);
var b = Convert.ToDouble(arguments["b"]);
double result = operation switch
{
"add" => a + b,
"subtract" => a - b,
"multiply" => a * b,
"divide" => b != 0 ? a / b : throw new DivideByZeroException("Cannot divide by zero"),
_ => throw new ArgumentException($"Unknown operation: {operation}")
};
return Task.FromResult(new McpToolResult
{
Content = new List<McpContent>
{
new McpContent
{
Type = "text",
Text = $"{a} {operation} {b} = {result}"
}
}
});
}
catch (Exception ex)
{
return Task.FromResult(new McpToolResult
{
Content = new List<McpContent>
{
new McpContent
{
Type = "text",
Text = $"Calculation error: {ex.Message}"
}
},
IsError = true
});
}
}
private McpMessage CreateErrorResponse(string? id, int code, string message)
{
return new McpMessage
{
Id = id,
Error = new McpError
{
Code = code,
Message = message
}
};
}
}
}