From 76f6ce5340ccafe60182ff072da65e4078c25f8f Mon Sep 17 00:00:00 2001 From: KoheiYamashita Date: Mon, 23 Feb 2026 01:57:05 +0900 Subject: [PATCH] feat: add MCP resource reading and fix HTTP transport hang - Add ReadResource method to MCP Manager for reading server resources - Add mcp_read_resource action to MCPBridgeTool so LLM can read MCP resources like data type catalogs - Set DisableStandaloneSSE=true on StreamableClientTransport to prevent connection hang when server/proxy doesn't support standalone SSE GET Co-Authored-By: Claude Opus 4.6 --- pkg/mcp/manager.go | 41 +++++++++++++++++++++++++++++++++++++++-- pkg/tools/mcp.go | 29 +++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/pkg/mcp/manager.go b/pkg/mcp/manager.go index 9e9915c20..937398506 100644 --- a/pkg/mcp/manager.go +++ b/pkg/mcp/manager.go @@ -156,6 +156,42 @@ func (m *Manager) CallTool(ctx context.Context, serverName, toolName string, arg return text, nil } +// ReadResource reads a resource from an MCP server by URI. +func (m *Manager) ReadResource(ctx context.Context, serverName, uri string) (string, error) { + inst, err := m.ensureRunning(ctx, serverName) + if err != nil { + return "", err + } + + inst.mu.Lock() + defer inst.mu.Unlock() + + inst.lastUsed = time.Now() + + result, err := inst.session.ReadResource(ctx, &sdkmcp.ReadResourceParams{ + URI: uri, + }) + if err != nil { + m.handleSessionError(serverName, inst, err) + return "", fmt.Errorf("resources/read %s: %w", uri, err) + } + + var parts []string + for _, content := range result.Contents { + if content.Text != "" { + parts = append(parts, content.Text) + } else if len(content.Blob) > 0 { + parts = append(parts, fmt.Sprintf("[blob: %s, %d bytes]", content.MIMEType, len(content.Blob))) + } + } + + if len(parts) == 0 { + return "(no content)", nil + } + + return strings.Join(parts, "\n"), nil +} + // BuildSummary generates XML for the system prompt using config only (no process start). func (m *Manager) BuildSummary() string { m.mu.RLock() @@ -280,8 +316,9 @@ func (m *Manager) ensureRunning(ctx context.Context, serverName string) (*Server } } transport = &sdkmcp.StreamableClientTransport{ - Endpoint: cfg.URL, - HTTPClient: httpClient, + Endpoint: cfg.URL, + HTTPClient: httpClient, + DisableStandaloneSSE: true, } inst.isHTTP = true diff --git a/pkg/tools/mcp.go b/pkg/tools/mcp.go index f3c5550f8..079eb73d2 100644 --- a/pkg/tools/mcp.go +++ b/pkg/tools/mcp.go @@ -24,7 +24,7 @@ func (t *MCPBridgeTool) Name() string { } func (t *MCPBridgeTool) Description() string { - return "Interact with MCP (Model Context Protocol) servers. Actions: mcp_list (list available servers), mcp_tools (get server's tool list), mcp_call (call a server tool)" + return "Interact with MCP (Model Context Protocol) servers. Actions: mcp_list (list available servers), mcp_tools (get server's tool list), mcp_call (call a server tool), mcp_read_resource (read a resource by URI)" } func (t *MCPBridgeTool) Parameters() map[string]interface{} { @@ -34,7 +34,7 @@ func (t *MCPBridgeTool) Parameters() map[string]interface{} { "action": map[string]interface{}{ "type": "string", "description": "The MCP action to perform", - "enum": []string{"mcp_list", "mcp_tools", "mcp_call"}, + "enum": []string{"mcp_list", "mcp_tools", "mcp_call", "mcp_read_resource"}, }, "server": map[string]interface{}{ "type": "string", @@ -48,6 +48,10 @@ func (t *MCPBridgeTool) Parameters() map[string]interface{} { "type": "object", "description": "Arguments to pass to the tool (for mcp_call)", }, + "uri": map[string]interface{}{ + "type": "string", + "description": "Resource URI to read (required for mcp_read_resource)", + }, }, "required": []string{"action"}, } @@ -66,6 +70,8 @@ func (t *MCPBridgeTool) Execute(ctx context.Context, args map[string]interface{} return t.getTools(ctx, args) case "mcp_call": return t.callTool(ctx, args) + case "mcp_read_resource": + return t.readResource(ctx, args) default: return ErrorResult(fmt.Sprintf("unknown action: %s", action)) } @@ -142,3 +148,22 @@ func (t *MCPBridgeTool) callTool(ctx context.Context, args map[string]interface{ return SilentResult(result) } + +func (t *MCPBridgeTool) readResource(ctx context.Context, args map[string]interface{}) *ToolResult { + server, ok := args["server"].(string) + if !ok || server == "" { + return ErrorResult("server is required for mcp_read_resource") + } + + uri, ok := args["uri"].(string) + if !ok || uri == "" { + return ErrorResult("uri is required for mcp_read_resource") + } + + result, err := t.manager.ReadResource(ctx, server, uri) + if err != nil { + return ErrorResult(fmt.Sprintf("mcp_read_resource %s/%s failed: %v", server, uri, err)) + } + + return SilentResult(result) +}