基于.Net与Ollama的MCP服务端工具调用实践

基于.Net与Ollama的MCP服务端工具调用实践

一、技术背景与架构设计

MCP(Model Context Protocol)作为新兴的AI服务协议,通过标准化接口实现大模型与外部工具的解耦交互。其核心价值在于将工具调用能力抽象为可复用的服务组件,使模型能够动态调用数据库查询、API接口、本地脚本等外部资源。

在架构设计上,采用分层模型:

  1. 协议层:实现MCP标准接口,处理HTTP请求/响应
  2. 工具管理层:维护工具注册表,管理工具元数据
  3. 执行层:调用本地或远程工具,处理参数转换
  4. 模型层:集成Ollama等本地大模型服务
  1. graph TD
  2. A[客户端] --> B[MCP服务端]
  3. B --> C[协议解析]
  4. C --> D[工具管理]
  5. D --> E[工具执行]
  6. E --> F[Ollama模型]
  7. E --> G[数据库/API]

二、.Net实现MCP服务端核心步骤

1. 创建ASP.NET Core Web API项目

  1. dotnet new webapi -n McpServer
  2. cd McpServer

2. 实现MCP协议接口

定义符合MCP规范的DTO模型:

  1. public class McpRequest
  2. {
  3. public string Id { get; set; }
  4. public List<McpTool> Tools { get; set; }
  5. public McpCommand Command { get; set; }
  6. }
  7. public class McpResponse
  8. {
  9. public string Id { get; set; }
  10. public List<McpToolCall> ToolCalls { get; set; }
  11. public string Text { get; set; }
  12. }

创建MCP控制器:

  1. [ApiController]
  2. [Route("mcp")]
  3. public class McpController : ControllerBase
  4. {
  5. private readonly IToolManager _toolManager;
  6. public McpController(IToolManager toolManager)
  7. {
  8. _toolManager = toolManager;
  9. }
  10. [HttpPost]
  11. public async Task<McpResponse> HandleRequest([FromBody] McpRequest request)
  12. {
  13. // 1. 解析模型请求
  14. var context = new McpContext(request);
  15. // 2. 执行工具调用
  16. var toolCalls = await _toolManager.ExecuteTools(context);
  17. // 3. 构建响应
  18. return new McpResponse
  19. {
  20. Id = request.Id,
  21. ToolCalls = toolCalls
  22. };
  23. }
  24. }

3. 工具管理系统实现

定义工具接口:

  1. public interface ITool
  2. {
  3. string Name { get; }
  4. string Description { get; }
  5. Task<ToolResult> Execute(ToolParameters parameters);
  6. }

实现具体工具(以数据库查询为例):

  1. public class DatabaseTool : ITool
  2. {
  3. public string Name => "database_query";
  4. public string Description => "执行SQL查询";
  5. public async Task<ToolResult> Execute(ToolParameters parameters)
  6. {
  7. var query = parameters.GetString("query");
  8. // 使用Dapper等ORM执行查询
  9. using var connection = new SqlConnection("连接字符串");
  10. var results = await connection.QueryAsync<dynamic>(query);
  11. return new ToolResult
  12. {
  13. Data = results
  14. };
  15. }
  16. }

三、Ollama模型集成方案

1. 模型服务部署

通过行业常见技术方案部署Ollama服务:

  1. # 示例:使用Docker部署
  2. docker run -d -p 11434:11434 ollama/ollama

2. 创建模型客户端

  1. public class OllamaClient
  2. {
  3. private readonly HttpClient _httpClient;
  4. public OllamaClient(string baseUrl)
  5. {
  6. _httpClient = new HttpClient
  7. {
  8. BaseAddress = new Uri(baseUrl)
  9. };
  10. }
  11. public async Task<string> GenerateText(string prompt)
  12. {
  13. var request = new
  14. {
  15. model = "llama2",
  16. prompt = prompt,
  17. stream = false
  18. };
  19. var response = await _httpClient.PostAsJsonAsync("api/generate", request);
  20. response.EnsureSuccessStatusCode();
  21. var result = await response.Content.ReadAsStringAsync();
  22. // 解析JSON获取生成文本
  23. return ParseResponse(result);
  24. }
  25. }

四、工具调用流程实现

1. 工具注册机制

  1. public class ToolRegistry
  2. {
  3. private readonly Dictionary<string, ITool> _tools = new();
  4. public void RegisterTool(ITool tool)
  5. {
  6. _tools[tool.Name] = tool;
  7. }
  8. public ITool GetTool(string name)
  9. {
  10. if (_tools.TryGetValue(name, out var tool))
  11. {
  12. return tool;
  13. }
  14. throw new KeyNotFoundException($"工具 {name} 未注册");
  15. }
  16. }

2. 调用流程示例

  1. public async Task<List<McpToolCall>> ExecuteTools(McpContext context)
  2. {
  3. var calls = new List<McpToolCall>();
  4. foreach (var toolReq in context.Request.Tools)
  5. {
  6. var tool = _toolRegistry.GetTool(toolReq.Name);
  7. var parameters = ParseParameters(toolReq.Parameters);
  8. var result = await tool.Execute(parameters);
  9. calls.Add(new McpToolCall
  10. {
  11. Id = toolReq.Id,
  12. Function = tool.Name,
  13. Arguments = result.Data
  14. });
  15. }
  16. return calls;
  17. }

五、性能优化与异常处理

1. 并发控制策略

  1. public class ConcurrentToolExecutor
  2. {
  3. private readonly SemaphoreSlim _semaphore = new(10); // 限制并发数
  4. public async Task ExecuteWithConcurrency(ITool tool, ToolParameters parameters)
  5. {
  6. await _semaphore.WaitAsync();
  7. try
  8. {
  9. return await tool.Execute(parameters);
  10. }
  11. finally
  12. {
  13. _semaphore.Release();
  14. }
  15. }
  16. }

2. 异常处理机制

  1. public class ToolExecutionHandler
  2. {
  3. public async Task<ToolResult> SafeExecute(ITool tool, ToolParameters parameters)
  4. {
  5. try
  6. {
  7. return await tool.Execute(parameters);
  8. }
  9. catch (Exception ex)
  10. {
  11. return new ToolResult
  12. {
  13. Error = new ToolError
  14. {
  15. Message = ex.Message,
  16. Type = ex.GetType().Name
  17. }
  18. };
  19. }
  20. }
  21. }

六、部署与监控建议

  1. 容器化部署:使用Docker Compose编排MCP服务与Ollama模型

    1. version: '3.8'
    2. services:
    3. mcp-server:
    4. build: ./McpServer
    5. ports:
    6. - "5000:80"
    7. environment:
    8. - OLLAMA_BASE_URL=http://ollama:11434
    9. ollama:
    10. image: ollama/ollama
    11. ports:
    12. - "11434:11434"
  2. 监控指标

    • 工具调用成功率
    • 平均响应时间
    • 模型生成延迟
    • 并发请求数
  3. 日志设计

    1. {
    2. "timestamp": "2023-07-20T12:34:56Z",
    3. "requestId": "abc123",
    4. "toolName": "database_query",
    5. "status": "success",
    6. "durationMs": 45,
    7. "parameters": {
    8. "query": "SELECT * FROM users"
    9. }
    10. }

七、最佳实践总结

  1. 工具设计原则

    • 保持工具功能单一
    • 提供清晰的输入/输出规范
    • 实现幂等性操作
  2. 安全考虑

    • 实施工具调用权限控制
    • 对输入参数进行验证
    • 限制敏感工具的访问
  3. 性能优化

    • 对耗时工具实现异步调用
    • 使用缓存机制存储工具结果
    • 实施请求限流策略

通过上述实现方案,开发者可以快速构建具备工具调用能力的MCP服务端,并与本地大模型服务形成完整解决方案。这种架构既保持了模型的灵活性,又通过标准化接口实现了工具资源的复用,特别适合需要动态扩展AI能力的企业级应用场景。