cmd: add --tools flag for MCP server integration

Integrate MCP servers with the ollama CLI and add route handlers for
tool-enabled chat completions.

CLI (cmd/cmd.go):
- Add --tools flag to 'ollama run' command
- Supports path argument: --tools /path/to/directory
- Multi-round tool execution loop with result display
- Displays available tools on startup

Routes (server/routes.go, server/routes_tools.go):
- MCP server handling in chat completions
- /api/tools endpoint for tool listing
- Session-based MCP manager lifecycle
- Detailed documentation of MCP integration flow

Parser (harmony/harmonyparser.go):
- Handle embedded <think> segments in tool call responses
- Extract tool calls from thinking model outputs

Relates to #7865
This commit is contained in:
code4me2
2025-12-14 21:10:05 -08:00
parent fc05536d52
commit abcb81bb07
5 changed files with 1117 additions and 323 deletions

91
server/routes_tools.go Normal file
View File

@@ -0,0 +1,91 @@
package server
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/ollama/ollama/api"
)
// ToolsHandler handles requests to list available MCP tools.
// GET: Returns available MCP server definitions from configuration.
// POST with mcp_servers: Returns tools from the specified MCP servers.
func (s *Server) ToolsHandler(c *gin.Context) {
var req struct {
MCPServers []api.MCPServerConfig `json:"mcp_servers,omitempty"`
}
if c.Request.Method == "POST" {
if err := c.BindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
}
// If MCP servers provided, list their tools
if len(req.MCPServers) > 0 {
manager := NewMCPManager(10)
defer manager.Close()
var allTools []ToolInfo
for _, config := range req.MCPServers {
if err := manager.AddServer(config); err != nil {
// Include error in response but continue
allTools = append(allTools, ToolInfo{
Name: config.Name,
Description: "Failed to initialize: " + err.Error(),
Error: err.Error(),
})
continue
}
// Get tools from this server
tools := manager.GetAllTools()
for _, tool := range tools {
allTools = append(allTools, ToolInfo{
Name: tool.Function.Name,
Description: tool.Function.Description,
Parameters: &tool.Function.Parameters,
ServerName: config.Name,
})
}
}
c.JSON(http.StatusOK, ToolsResponse{
Tools: allTools,
})
return
}
// Otherwise, list available MCP server definitions
defs, err := LoadMCPDefinitions()
if err != nil {
// Config parsing errors are client errors (bad config), not server errors
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid MCP configuration: " + err.Error()})
return
}
servers := defs.ListServers()
c.JSON(http.StatusOK, MCPServersResponse{
Servers: servers,
})
}
// ToolInfo provides information about a single tool
type ToolInfo struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters *api.ToolFunctionParameters `json:"parameters,omitempty"`
ServerName string `json:"server,omitempty"`
Error string `json:"error,omitempty"`
}
// ToolsResponse contains the list of available tools
type ToolsResponse struct {
Tools []ToolInfo `json:"tools"`
}
// MCPServersResponse contains the list of available MCP server types
type MCPServersResponse struct {
Servers []MCPServerInfo `json:"servers"`
}