使用Spring AI+Ollama Llama3.2的MCP客户端调用添加员工工具时出现JSON解析错误的排查请求
使用Spring AI+Ollama Llama3.2的MCP客户端调用添加员工工具时出现JSON解析错误的排查请求
大家好,我最近在基于Spring AI和Ollama Llama3.2(部署在Docker容器中,暴露端口http://localhost:11434)探索MCP(Model Context Protocol),采用STDIO传输方式,大部分工具调用操作都能正常执行,但唯独调用添加员工的工具时遇到了问题,推测是JSON解析相关的错误,想请社区的朋友们帮忙排查下。
环境与整体流程说明
- MCP服务端维护了一个内存中的员工列表,提供了增删改查的工具方法,通过
@Tool注解暴露给模型 - MCP客户端基于
spring-boot-web-starter实现,通过POST接口接收用户输入,再传递给模型触发工具调用 - 我用curl命令发送请求体来测试客户端接口
关键代码片段
服务端EmployeeService
import com.demo.mcp.server.data.Employee; import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; @Service public class EmployeeService { private static final Logger logger = LoggerFactory.getLogger(EmployeeService.class); private List<Employee> employees = new ArrayList<>(); @Tool(name="t_list_all_employees", description = "This tool will use the list of employees that is managed in the in memory list within the application") public List<Employee> getEmployees() { logger.info("getEmployees() invoked"); return employees; } @Tool(name="t_add_one_employee", description = "Add an employee to the employee list, before invoking this tool create the employee string with json template in following format" + "{ \"name\": \"SET-WITH-USER-INPUT\", \"email\": \"SET-WITH-USER-INPUT\", \"phone\": \"SET-WITH-USER-INPUT\"}" + " with this this method can add the employee to in memory list. For example, if the context include " + "add an employee to the list where the employee name is joe I, email is joe@email.com and phone is 11231" + "The employee name should be provided") public Employee addEmployee(Employee employee) { logger.info("Adding employee: {}", employee); if(employee != null && employee.name() != null && !employee.name().isEmpty()) { logger.info("Employee name is valid: {}", employee.name()); } else { logger.warn("Invalid employee name provided: {}", employee); throw new IllegalArgumentException("Employee name cannot be null or empty"); } employees.add(employee); return employee; } @Tool(name="t_fetch_employee_with_name", description = "This tool is used to get one employee based on user provided employee name from the in memory list in this app") public Employee getEmployee(String name) { logger.info("Getting employee by name: {}", name); if (name == null || name.isEmpty()) { logger.warn("Invalid employee name provided: {}", name); throw new IllegalArgumentException("Employee name cannot be null or empty"); } return employees.stream() .filter(employee -> employee.name().equalsIgnoreCase(name)) .findFirst() .orElse(null); } @Tool(name="t_delete_employee", description = "This tool will delete the employee matching the user provided name from the in memory list in this app") public void deleteEmployee(String name) { if (name == null || name.isEmpty()) { logger.warn("Invalid employee name provided for deletion: {}", name); throw new IllegalArgumentException("Employee name cannot be null or empty"); } logger.info("Deleting employee by name: {}", name); employees.removeIf(employee -> employee.name().equalsIgnoreCase(name)); } @PostConstruct public void init() { employees.addAll(List.of( new Employee("Joy one","joy_one@email.com","1234567890"), new Employee("Joy two","joe_two@email.com","0987654321"), new Employee("Joy three","joy_three@email.com","1122334455"), new Employee("Joy four","joy_four@email.com","5566778899"), new Employee("Joy five","joy_five@email.com","9988776655") )); } }
Employee 实体类
public record Employee(String name, String email, String phone){}
客户端InputController(部分代码)
import io.modelcontextprotocol.client.McpSyncClient; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.mcp.SyncMcpToolCallbackProvider; import org.springframework.ai.tool.ToolCallback; import org.springframework.ai.tool.ToolCallbackProvider; import org.springframework.ai.tool.definition.ToolDefinition; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RequestMapping("/input") @RestController public class InputController { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(InputController.class); // 省略ChatClient、McpSyncClient等注入及初始化代码 // ... @PostMapping public String handleInput(@RequestBody String userInput) { // 省略构建Prompt、调用模型及处理工具回调的逻辑 // ... } }
问题现象
当我通过curl发送类似"add an employee named Alice with email alice@test.com and phone 123456"的请求时,模型确实会触发t_add_one_employee工具调用,但服务端会抛出JSON解析相关的异常(或者参数绑定失败),其他工具比如查询所有员工、按名称查询都能正常工作。
我已经排查的点
- 确认了Employee record的字段名称和JSON模板中的字段完全一致
- 检查了服务端日志,能看到工具被调用,但参数解析失败
- 尝试手动构造符合模板的JSON请求,直接调用服务端接口是正常的
想请教大家的问题
- 有没有可能是
@Tool注解中的描述不够明确,导致模型生成的JSON格式不符合要求?比如有没有优化描述的建议? - Spring AI在处理MCP工具调用的参数解析时,有没有需要注意的配置点?
- 有没有类似场景的调试经验,比如如何查看模型实际生成的工具调用参数?
感谢大家的帮助!
内容来源于stack exchange
相关产品推荐
相关产品推荐

