mirror of
https://github.com/mudler/LocalAI.git
synced 2026-09-21 21:54:52 -04:00
mcp.tools.execute and mcp.discovery were the only NATS subjects that combined a queue group with a reply, and no carrier in this design provides both. They never needed one: a queue group is a way of choosing a subscriber, and choosing is a query. The frontend now lists the approved, non-draining agent nodes, asks the node_connections table in one joined statement which of those tunnels a live replica holds, prefers one this replica holds so the call skips the relay hop, and issues an ordinary control RPC on the path task 4 already mounted. A peer-held tunnel is reached through the relay. That is a choice a broker's hidden balancing could not make. The selection reads presence and nothing else. It is filtered only on node type and on the two statuses an operator controls, never on a health verdict written on another clock, because refusing a worker that is connected and answering is the same defect as picking one that is gone. An empty fleet answers ErrNoAgentWorker, which is deliberately neither ErrWorkerUnroutable nor anything cluster.IsWorkerAnswer accepts: nothing was asked of any worker, so no reap guard may act on it. A reply carrying an Error is the worker's own answer and is returned unchanged; it is never offered to a second worker, which would turn "this MCP server rejected your arguments" into "the fleet is broken" and could run a tool twice. A call that never reached a worker is retried against a different pick, at most three times, and whatever error is finally returned is returned unwrapped so its identity survives the loop. MCP prompts and resources now answer 501 in distributed mode instead of an empty 200. They are served only from sessions the frontend holds, and in distributed mode it holds none. That gap predates the removal of the bus and is not closed by it; this only stops it being silent. Agent workers keep every other subject, including nodes.<id>.backend.stop. Their minted JWT loses the two MCP subjects and keeps a non-empty allow list, because NATS reads an empty one as no restriction at all. Assisted-by: Claude Opus 5 [claude-code] Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
66 lines
2.8 KiB
Go
66 lines
2.8 KiB
Go
package mcp
|
|
|
|
import (
|
|
"github.com/mudler/LocalAI/core/config"
|
|
"github.com/mudler/LocalAI/pkg/functions"
|
|
)
|
|
|
|
// MCPToolRequest is the control-RPC payload for executing an MCP tool on an
|
|
// agent worker. The frontend serializes the model's MCP server config so the
|
|
// worker can create sessions and execute the tool.
|
|
//
|
|
// It lives in this package rather than beside either side of the call, because
|
|
// both sides need it and they are in packages that must not import each other:
|
|
// core/http/endpoints/mcp is the caller and core/cli is the worker.
|
|
type MCPToolRequest struct {
|
|
ModelName string `json:"model_name"`
|
|
ToolName string `json:"tool_name"`
|
|
Arguments map[string]any `json:"arguments,omitempty"`
|
|
RemoteServers config.MCPGenericConfig[config.MCPRemoteServers] `json:"remote_servers"`
|
|
StdioServers config.MCPGenericConfig[config.MCPSTDIOServers] `json:"stdio_servers"`
|
|
}
|
|
|
|
// MCPToolResponse is the reply to an MCP tool execution.
|
|
//
|
|
// A set Error is the WORKER'S OWN ANSWER, carried inside a successful reply
|
|
// rather than as a transport failure, which is what lets a caller tell "this
|
|
// tool rejected your arguments" from "this worker could not be reached".
|
|
type MCPToolResponse struct {
|
|
Result string `json:"result,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// MCPDiscoveryRequest is the control-RPC payload for discovering available MCP
|
|
// tools, prompts, and resources from a model's MCP servers.
|
|
type MCPDiscoveryRequest struct {
|
|
ModelName string `json:"model_name"`
|
|
RemoteServers config.MCPGenericConfig[config.MCPRemoteServers] `json:"remote_servers"`
|
|
StdioServers config.MCPGenericConfig[config.MCPSTDIOServers] `json:"stdio_servers"`
|
|
}
|
|
|
|
// MCPDiscoveryResponse is the reply to an MCP discovery request.
|
|
type MCPDiscoveryResponse struct {
|
|
Servers []MCPServerInfo `json:"servers,omitempty"`
|
|
Tools []MCPToolDef `json:"tools,omitempty"` // flattened tool list with functions
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// MCPServerInfo describes an MCP server and its available capabilities.
|
|
type MCPServerInfo struct {
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Tools []string `json:"tools"`
|
|
Prompts []string `json:"prompts,omitempty"`
|
|
Resources []string `json:"resources,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// MCPToolDef is a serializable tool definition (function schema) that can
|
|
// travel between processes. Unlike MCPToolInfo which holds a live session
|
|
// pointer, this is pure data.
|
|
type MCPToolDef struct {
|
|
ServerName string `json:"server_name"`
|
|
ToolName string `json:"tool_name"`
|
|
Function functions.Function `json:"function"`
|
|
}
|