mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 19:41:17 +00:00
Major tools refactoring for better organization and capabilities: New consolidated tools: - pulse_query: Unified resource search, get, config, topology operations - pulse_read: Safe read-only command execution with NonInteractiveOnly - pulse_control: Guest lifecycle control (start/stop/restart) - pulse_docker: Docker container operations - pulse_file: Safe file read/write operations - pulse_kubernetes: K8s resource management - pulse_metrics: Performance metrics retrieval - pulse_alerts: Alert management - pulse_storage: Storage pool operations - pulse_knowledge: Note-taking and recall - pulse_pmg: Proxmox Mail Gateway integration Executor improvements: - Cleaner tool registration pattern - Better error handling and recovery - Protocol layer for result formatting - Enhanced adapter interfaces Includes comprehensive tests for: - File and Docker operations - Kubernetes control operations - Command execution safety
335 lines
9.8 KiB
Go
335 lines
9.8 KiB
Go
package tools
|
|
|
|
import (
|
|
"encoding/json"
|
|
)
|
|
|
|
// JSON-RPC 2.0 types for MCP protocol
|
|
|
|
// Request represents a JSON-RPC request
|
|
type Request struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID interface{} `json:"id,omitempty"`
|
|
Method string `json:"method"`
|
|
Params json.RawMessage `json:"params,omitempty"`
|
|
}
|
|
|
|
// Response represents a JSON-RPC response
|
|
type Response struct {
|
|
JSONRPC string `json:"jsonrpc"`
|
|
ID interface{} `json:"id,omitempty"`
|
|
Result json.RawMessage `json:"result,omitempty"`
|
|
Error *Error `json:"error,omitempty"`
|
|
}
|
|
|
|
// Error represents a JSON-RPC error
|
|
type Error struct {
|
|
Code int `json:"code"`
|
|
Message string `json:"message"`
|
|
Data interface{} `json:"data,omitempty"`
|
|
}
|
|
|
|
// Standard JSON-RPC error codes
|
|
const (
|
|
ErrParse = -32700
|
|
ErrInvalidRequest = -32600
|
|
ErrMethodNotFound = -32601
|
|
ErrInvalidParams = -32602
|
|
ErrInternal = -32603
|
|
)
|
|
|
|
// MCP-specific types
|
|
|
|
// ServerInfo describes the MCP server
|
|
type ServerInfo struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
Capabilities Capabilities `json:"capabilities,omitempty"`
|
|
}
|
|
|
|
// Capabilities describes what the server supports
|
|
type Capabilities struct {
|
|
Tools *ToolsCapability `json:"tools,omitempty"`
|
|
Resources *ResourcesCapability `json:"resources,omitempty"`
|
|
Prompts *PromptsCapability `json:"prompts,omitempty"`
|
|
}
|
|
|
|
// ToolsCapability describes tool support
|
|
type ToolsCapability struct {
|
|
ListChanged bool `json:"listChanged,omitempty"`
|
|
}
|
|
|
|
// ResourcesCapability describes resource support
|
|
type ResourcesCapability struct {
|
|
Subscribe bool `json:"subscribe,omitempty"`
|
|
ListChanged bool `json:"listChanged,omitempty"`
|
|
}
|
|
|
|
// PromptsCapability describes prompt support
|
|
type PromptsCapability struct {
|
|
ListChanged bool `json:"listChanged,omitempty"`
|
|
}
|
|
|
|
// InitializeParams are the params for the initialize method
|
|
type InitializeParams struct {
|
|
ProtocolVersion string `json:"protocolVersion"`
|
|
Capabilities Capabilities `json:"capabilities"`
|
|
ClientInfo ClientInfo `json:"clientInfo"`
|
|
}
|
|
|
|
// ClientInfo describes the client
|
|
type ClientInfo struct {
|
|
Name string `json:"name"`
|
|
Version string `json:"version"`
|
|
}
|
|
|
|
// InitializeResult is the result of the initialize method
|
|
type InitializeResult struct {
|
|
ProtocolVersion string `json:"protocolVersion"`
|
|
Capabilities Capabilities `json:"capabilities"`
|
|
ServerInfo ServerInfo `json:"serverInfo"`
|
|
}
|
|
|
|
// Tool describes an available tool
|
|
type Tool struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
InputSchema InputSchema `json:"inputSchema"`
|
|
}
|
|
|
|
// InputSchema describes the expected input for a tool
|
|
type InputSchema struct {
|
|
Type string `json:"type"` // Always "object"
|
|
Properties map[string]PropertySchema `json:"properties"`
|
|
Required []string `json:"required,omitempty"`
|
|
}
|
|
|
|
// PropertySchema describes a property in the input schema
|
|
type PropertySchema struct {
|
|
Type string `json:"type"`
|
|
Description string `json:"description,omitempty"`
|
|
Enum []string `json:"enum,omitempty"`
|
|
Default interface{} `json:"default,omitempty"`
|
|
}
|
|
|
|
// ListToolsResult is the result of tools/list
|
|
type ListToolsResult struct {
|
|
Tools []Tool `json:"tools"`
|
|
}
|
|
|
|
// CallToolParams are the params for tools/call
|
|
type CallToolParams struct {
|
|
Name string `json:"name"`
|
|
Arguments map[string]interface{} `json:"arguments,omitempty"`
|
|
}
|
|
|
|
// CallToolResult is the result of tools/call
|
|
type CallToolResult struct {
|
|
Content []Content `json:"content"`
|
|
IsError bool `json:"isError,omitempty"`
|
|
}
|
|
|
|
// Content represents content in a tool result
|
|
type Content struct {
|
|
Type string `json:"type"` // "text", "image", "resource"
|
|
Text string `json:"text,omitempty"`
|
|
MimeType string `json:"mimeType,omitempty"`
|
|
Data string `json:"data,omitempty"` // base64 for images
|
|
URI string `json:"uri,omitempty"` // for resources
|
|
}
|
|
|
|
// Resource describes an available resource
|
|
type Resource struct {
|
|
URI string `json:"uri"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
MimeType string `json:"mimeType,omitempty"`
|
|
}
|
|
|
|
// ListResourcesResult is the result of resources/list
|
|
type ListResourcesResult struct {
|
|
Resources []Resource `json:"resources"`
|
|
}
|
|
|
|
// ReadResourceParams are the params for resources/read
|
|
type ReadResourceParams struct {
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
// ReadResourceResult is the result of resources/read
|
|
type ReadResourceResult struct {
|
|
Contents []ResourceContent `json:"contents"`
|
|
}
|
|
|
|
// ResourceContent is the content of a resource
|
|
type ResourceContent struct {
|
|
URI string `json:"uri"`
|
|
MimeType string `json:"mimeType,omitempty"`
|
|
Text string `json:"text,omitempty"`
|
|
Blob string `json:"blob,omitempty"` // base64
|
|
}
|
|
|
|
// Prompt describes an available prompt template
|
|
type Prompt struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Arguments []PromptArgument `json:"arguments,omitempty"`
|
|
}
|
|
|
|
// PromptArgument describes an argument for a prompt
|
|
type PromptArgument struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description,omitempty"`
|
|
Required bool `json:"required,omitempty"`
|
|
}
|
|
|
|
// ListPromptsResult is the result of prompts/list
|
|
type ListPromptsResult struct {
|
|
Prompts []Prompt `json:"prompts"`
|
|
}
|
|
|
|
// GetPromptParams are the params for prompts/get
|
|
type GetPromptParams struct {
|
|
Name string `json:"name"`
|
|
Arguments map[string]string `json:"arguments,omitempty"`
|
|
}
|
|
|
|
// GetPromptResult is the result of prompts/get
|
|
type GetPromptResult struct {
|
|
Description string `json:"description,omitempty"`
|
|
Messages []PromptMessage `json:"messages"`
|
|
}
|
|
|
|
// PromptMessage is a message in a prompt
|
|
type PromptMessage struct {
|
|
Role string `json:"role"` // "user" or "assistant"
|
|
Content Content `json:"content"`
|
|
}
|
|
|
|
// ToolResponse is a consistent envelope for tool results.
|
|
// All tool results should use this structure for predictable parsing.
|
|
type ToolResponse struct {
|
|
OK bool `json:"ok"` // true if tool succeeded
|
|
Data interface{} `json:"data,omitempty"` // result data if ok=true
|
|
Error *ToolError `json:"error,omitempty"` // error details if ok=false
|
|
Meta map[string]interface{} `json:"meta,omitempty"` // optional metadata
|
|
}
|
|
|
|
// ToolError provides consistent error structure for tool failures.
|
|
// Use Blocked=true for policy/validation blocks, Failed=true for runtime errors.
|
|
type ToolError struct {
|
|
Code string `json:"code"` // Error code (e.g., "STRICT_RESOLUTION", "NOT_FOUND")
|
|
Message string `json:"message"` // Human-readable message
|
|
Blocked bool `json:"blocked,omitempty"` // True if blocked by policy/validation
|
|
Failed bool `json:"failed,omitempty"` // True if runtime failure
|
|
Retryable bool `json:"retryable,omitempty"` // True if auto-retry might succeed
|
|
Details map[string]interface{} `json:"details,omitempty"` // Additional context
|
|
}
|
|
|
|
// Common error codes
|
|
const (
|
|
ErrCodeStrictResolution = "STRICT_RESOLUTION"
|
|
ErrCodeNotFound = "NOT_FOUND"
|
|
ErrCodeActionNotAllowed = "ACTION_NOT_ALLOWED"
|
|
ErrCodePolicyBlocked = "POLICY_BLOCKED"
|
|
ErrCodeApprovalRequired = "APPROVAL_REQUIRED"
|
|
ErrCodeInvalidInput = "INVALID_INPUT"
|
|
ErrCodeExecutionFailed = "EXECUTION_FAILED"
|
|
ErrCodeNoAgent = "NO_AGENT"
|
|
)
|
|
|
|
// NewToolSuccess creates a successful tool response
|
|
func NewToolSuccess(data interface{}) ToolResponse {
|
|
return ToolResponse{
|
|
OK: true,
|
|
Data: data,
|
|
}
|
|
}
|
|
|
|
// NewToolSuccessWithMeta creates a successful tool response with metadata
|
|
func NewToolSuccessWithMeta(data interface{}, meta map[string]interface{}) ToolResponse {
|
|
return ToolResponse{
|
|
OK: true,
|
|
Data: data,
|
|
Meta: meta,
|
|
}
|
|
}
|
|
|
|
// NewToolBlockedError creates a policy/validation blocked error
|
|
func NewToolBlockedError(code, message string, details map[string]interface{}) ToolResponse {
|
|
return ToolResponse{
|
|
OK: false,
|
|
Error: &ToolError{
|
|
Code: code,
|
|
Message: message,
|
|
Blocked: true,
|
|
Details: details,
|
|
},
|
|
}
|
|
}
|
|
|
|
// NewToolFailedError creates a runtime failure error
|
|
func NewToolFailedError(code, message string, retryable bool, details map[string]interface{}) ToolResponse {
|
|
return ToolResponse{
|
|
OK: false,
|
|
Error: &ToolError{
|
|
Code: code,
|
|
Message: message,
|
|
Failed: true,
|
|
Retryable: retryable,
|
|
Details: details,
|
|
},
|
|
}
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
// NewTextContent creates a text content object
|
|
func NewTextContent(text string) Content {
|
|
return Content{
|
|
Type: "text",
|
|
Text: text,
|
|
}
|
|
}
|
|
|
|
// NewErrorResult creates an error tool result
|
|
func NewErrorResult(err error) CallToolResult {
|
|
return CallToolResult{
|
|
Content: []Content{NewTextContent(err.Error())},
|
|
IsError: true,
|
|
}
|
|
}
|
|
|
|
// NewTextResult creates a successful text tool result
|
|
func NewTextResult(text string) CallToolResult {
|
|
return CallToolResult{
|
|
Content: []Content{NewTextContent(text)},
|
|
IsError: false,
|
|
}
|
|
}
|
|
|
|
// NewJSONResult creates a successful JSON tool result
|
|
// The data is marshaled to JSON and returned as text content
|
|
func NewJSONResult(data interface{}) CallToolResult {
|
|
b, err := json.Marshal(data)
|
|
if err != nil {
|
|
return NewErrorResult(err)
|
|
}
|
|
return CallToolResult{
|
|
Content: []Content{NewTextContent(string(b))},
|
|
IsError: false,
|
|
}
|
|
}
|
|
|
|
// NewToolResponseResult creates a CallToolResult from a ToolResponse
|
|
// This provides the consistent envelope while maintaining MCP protocol compatibility
|
|
func NewToolResponseResult(resp ToolResponse) CallToolResult {
|
|
b, err := json.Marshal(resp)
|
|
if err != nil {
|
|
return NewErrorResult(err)
|
|
}
|
|
return CallToolResult{
|
|
Content: []Content{NewTextContent(string(b))},
|
|
IsError: !resp.OK,
|
|
}
|
|
}
|