Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 25 additions & 17 deletions internal/tool/agentic_fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,36 @@ func (AgenticFetchTool) Description() string {
return "Fetch and intelligently summarize web content using a sub-agent. Better than raw WebFetch for research — the sub-agent extracts only the relevant information."
}

func (AgenticFetchTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"url": map[string]interface{}{"type": "string", "description": "URL to fetch and analyze. Provide this OR urls (not both)."},
"urls": map[string]interface{}{
"type": "array",
"items": map[string]interface{}{"type": "string"},
"description": "Multiple URLs to fetch and summarize concurrently against the same query, in a single call.",
},
"query": map[string]interface{}{"type": "string", "description": "What to look for or extract from the page(s)"},
// Schema returns the typed input schema. Parameters() delegates to it so the
// two cannot diverge.
func (AgenticFetchTool) Schema() ToolSchema {
return ToolSchema{
Type: "object",
Properties: map[string]SchemaProperty{
"url": {Type: "string", Description: "URL to fetch and analyze. Provide this OR urls (not both)."},
"urls": {Type: "array", Items: &SchemaProperty{Type: "string"}, Description: "Multiple URLs to fetch and summarize concurrently against the same query, in a single call."},
"query": {Type: "string", Description: "What to look for or extract from the page(s)"},
},
}
}

func (AgenticFetchTool) Parameters() map[string]interface{} {
return agenticFetchSchema.ToJSONSchema()
}

// agenticFetchSchema is the single source of truth for AgenticFetch's input schema.
var agenticFetchSchema = AgenticFetchTool{}.Schema()

// AgenticFetchInput is the typed input for AgenticFetchTool.
type AgenticFetchInput struct {
URL string `json:"url"`
URLs []string `json:"urls"`
Query string `json:"query"`
}

func (t AgenticFetchTool) Execute(ctx context.Context, input json.RawMessage) (string, error) {
var p struct {
URL string `json:"url"`
URLs []string `json:"urls"`
Query string `json:"query"`
}
if err := json.Unmarshal(input, &p); err != nil {
p, err := DecodeInput[AgenticFetchInput]("AgenticFetch", input)
if err != nil {
return "", err
}

Expand Down
50 changes: 26 additions & 24 deletions internal/tool/mcp_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,42 +53,44 @@ const mcpAuthCallbackTimeout = 5 * time.Minute
// same server_name later to check progress.
type McpAuthTool struct{}

// McpAuthInput is the typed input for McpAuthTool.
type McpAuthInput struct {
ServerName string `json:"server_name"`
ServerURL string `json:"server_url"`
ClientID string `json:"client_id"`
}

func (McpAuthTool) Name() string { return "McpAuth" }
func (McpAuthTool) Aliases() []string { return []string{"mcp_auth"} }
func (McpAuthTool) Description() string {
return "Start OAuth authentication for an MCP server that requires authorization. " +
"Call again with the same server_name to check progress once the user has visited the URL."
}

func (McpAuthTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"server_name": map[string]interface{}{
"type": "string",
"description": "Name of the MCP server to authenticate",
},
"server_url": map[string]interface{}{
"type": "string",
"description": "URL of the MCP server",
},
"client_id": map[string]interface{}{
"type": "string",
"description": "Optional pre-registered OAuth client_id. If omitted, rho attempts " +
"dynamic client registration (RFC 7591) against the server's advertised registration endpoint.",
},
// Schema returns the typed input schema. Parameters() delegates to it so the
// two cannot diverge.
func (McpAuthTool) Schema() ToolSchema {
return ToolSchema{
Type: "object",
Properties: map[string]SchemaProperty{
"server_name": {Type: "string", Description: "Name of the MCP server to authenticate"},
"server_url": {Type: "string", Description: "URL of the MCP server"},
"client_id": {Type: "string", Description: "Optional pre-registered OAuth client_id. If omitted, rho attempts dynamic client registration (RFC 7591) against the server's advertised registration endpoint."},
},
"required": []string{"server_name", "server_url"},
Required: []string{"server_name", "server_url"},
}
}

func (McpAuthTool) Parameters() map[string]interface{} {
return mcpAuthSchema.ToJSONSchema()
}

// mcpAuthSchema is the single source of truth for McpAuth's input schema.
var mcpAuthSchema = McpAuthTool{}.Schema()

func (McpAuthTool) Execute(ctx context.Context, input json.RawMessage) (string, error) {
var p struct {
ServerName string `json:"server_name"`
ServerURL string `json:"server_url"`
ClientID string `json:"client_id"`
}
if err := json.Unmarshal(input, &p); err != nil {
p, err := DecodeInput[McpAuthInput]("McpAuth", input)
if err != nil {
return "", err
}
if p.ServerName == "" {
Expand Down
78 changes: 34 additions & 44 deletions internal/tool/mcp_lsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,61 +13,51 @@ import (
// across multiple languages without running a language server directly.
type MCPLanguageServerTool struct{}

// MCPLanguageServerInput is the typed input for MCPLanguageServerTool.
type MCPLanguageServerInput struct {
Action string `json:"action"`
File string `json:"file"`
Line int `json:"line"`
Column int `json:"column"`
Symbol string `json:"symbol"`
NewName string `json:"newName"`
Language string `json:"language"`
}

func (MCPLanguageServerTool) Name() string { return "MCPLSP" }
func (MCPLanguageServerTool) Aliases() []string { return []string{"mcplsp", "lsp-mcp"} }
func (MCPLanguageServerTool) Description() string {
return "Deep code understanding via MCP language server. Provides go-to-definition, find-references, rename, and diagnostics across multiple languages."
}

func (MCPLanguageServerTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"action": map[string]interface{}{
"type": "string",
"enum": []string{"definition", "references", "rename", "diagnostics", "hover", "symbols"},
"description": "LSP action to perform",
},
"file": map[string]interface{}{
"type": "string",
"description": "File path",
},
"line": map[string]interface{}{
"type": "integer",
"description": "Line number (1-based)",
},
"column": map[string]interface{}{
"type": "integer",
"description": "Column number (1-based)",
},
"symbol": map[string]interface{}{
"type": "string",
"description": "Symbol name (for rename: old name)",
},
"newName": map[string]interface{}{
"type": "string",
"description": "New name for rename action",
},
"language": map[string]interface{}{
"type": "string",
"description": "Language server to use (go, python, typescript, rust)",
},
// Schema returns the typed input schema. Parameters() delegates to it so the
// two cannot diverge.
func (MCPLanguageServerTool) Schema() ToolSchema {
return ToolSchema{
Type: "object",
Properties: map[string]SchemaProperty{
"action": {Type: "string", Enum: []interface{}{"definition", "references", "rename", "diagnostics", "hover", "symbols"}, Description: "LSP action to perform"},
"file": {Type: "string", Description: "File path"},
"line": {Type: "integer", Description: "Line number (1-based)"},
"column": {Type: "integer", Description: "Column number (1-based)"},
"symbol": {Type: "string", Description: "Symbol name (for rename: old name)"},
"newName": {Type: "string", Description: "New name for rename action"},
"language": {Type: "string", Description: "Language server to use (go, python, typescript, rust)"},
},
"required": []string{"action"},
Required: []string{"action"},
}
}

func (MCPLanguageServerTool) Parameters() map[string]interface{} {
return mcpLSPLanguageSchema.ToJSONSchema()
}

// mcpLSPLanguageSchema is the single source of truth for MCPLanguageServer's input schema.
var mcpLSPLanguageSchema = MCPLanguageServerTool{}.Schema()

func (MCPLanguageServerTool) Execute(ctx context.Context, input json.RawMessage) (string, error) {
var p struct {
Action string `json:"action"`
File string `json:"file"`
Line int `json:"line"`
Column int `json:"column"`
Symbol string `json:"symbol"`
NewName string `json:"newName"`
Language string `json:"language"`
}
if err := json.Unmarshal(input, &p); err != nil {
p, err := DecodeInput[MCPLanguageServerInput]("MCPLanguageServer", input)
if err != nil {
return "", err
}

Expand Down
70 changes: 48 additions & 22 deletions internal/tool/mcp_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import (

type ListMcpResourcesTool struct{}

// ListMcpResourcesInput is the typed input for ListMcpResourcesTool.
type ListMcpResourcesInput struct {
Server string `json:"server"`
}

func (ListMcpResourcesTool) Name() string { return "ListMcpResourcesTool" }
func (ListMcpResourcesTool) Aliases() []string {
return []string{"list_mcp_resources", "listMcpResources"}
Expand All @@ -19,23 +24,32 @@ func (ListMcpResourcesTool) Description() string {
return "List resources exposed by connected MCP servers. Optionally filter by server name."
}

func (ListMcpResourcesTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"server": map[string]interface{}{"type": "string", "description": "Optional MCP server name to filter resources by"},
// Schema returns the typed input schema. Parameters() delegates to it so the
// two cannot diverge.
func (ListMcpResourcesTool) Schema() ToolSchema {
return ToolSchema{
Type: "object",
Properties: map[string]SchemaProperty{
"server": {Type: "string", Description: "Optional MCP server name to filter resources by"},
},
}
}

func (ListMcpResourcesTool) Parameters() map[string]interface{} {
return listMcpResourcesSchema.ToJSONSchema()
}

// listMcpResourcesSchema is the single source of truth for ListMcpResources's input schema.
var listMcpResourcesSchema = ListMcpResourcesTool{}.Schema()

func (ListMcpResourcesTool) Execute(_ context.Context, input json.RawMessage) (string, error) {
var p struct {
Server string `json:"server"`
}
if len(input) > 0 {
if err := json.Unmarshal(input, &p); err != nil {
var p ListMcpResourcesInput
if len(input) > 0 && string(input) != "null" {
decoded, err := DecodeInput[ListMcpResourcesInput]("ListMcpResources", input)
if err != nil {
return "", err
}
p = decoded
}

type resourceOut struct {
Expand Down Expand Up @@ -79,6 +93,12 @@ func (ListMcpResourcesTool) Execute(_ context.Context, input json.RawMessage) (s

type ReadMcpResourceTool struct{}

// ReadMcpResourceInput is the typed input for ReadMcpResourceTool.
type ReadMcpResourceInput struct {
Server string `json:"server"`
URI string `json:"uri"`
}

func (ReadMcpResourceTool) Name() string { return "ReadMcpResourceTool" }
func (ReadMcpResourceTool) Aliases() []string {
return []string{"read_mcp_resource", "readMcpResource"}
Expand All @@ -88,23 +108,29 @@ func (ReadMcpResourceTool) Description() string {
return "Read a resource exposed by a connected MCP server."
}

func (ReadMcpResourceTool) Parameters() map[string]interface{} {
return map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"server": map[string]interface{}{"type": "string", "description": "MCP server name"},
"uri": map[string]interface{}{"type": "string", "description": "Resource URI"},
// Schema returns the typed input schema. Parameters() delegates to it so the
// two cannot diverge.
func (ReadMcpResourceTool) Schema() ToolSchema {
return ToolSchema{
Type: "object",
Properties: map[string]SchemaProperty{
"server": {Type: "string", Description: "MCP server name"},
"uri": {Type: "string", Description: "Resource URI"},
},
"required": []string{"server", "uri"},
Required: []string{"server", "uri"},
}
}

func (ReadMcpResourceTool) Parameters() map[string]interface{} {
return readMcpResourceSchema.ToJSONSchema()
}

// readMcpResourceSchema is the single source of truth for ReadMcpResource's input schema.
var readMcpResourceSchema = ReadMcpResourceTool{}.Schema()

func (ReadMcpResourceTool) Execute(_ context.Context, input json.RawMessage) (string, error) {
var p struct {
Server string `json:"server"`
URI string `json:"uri"`
}
if err := json.Unmarshal(input, &p); err != nil {
p, err := DecodeInput[ReadMcpResourceInput]("ReadMcpResource", input)
if err != nil {
return "", err
}
if p.Server == "" || p.URI == "" {
Expand Down
Loading
Loading