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 <noreply@anthropic.com>
This commit is contained in:
parent
65fd24165a
commit
76f6ce5340
2 changed files with 66 additions and 4 deletions
|
|
@ -156,6 +156,42 @@ func (m *Manager) CallTool(ctx context.Context, serverName, toolName string, arg
|
||||||
return text, nil
|
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).
|
// BuildSummary generates XML for the system prompt using config only (no process start).
|
||||||
func (m *Manager) BuildSummary() string {
|
func (m *Manager) BuildSummary() string {
|
||||||
m.mu.RLock()
|
m.mu.RLock()
|
||||||
|
|
@ -282,6 +318,7 @@ func (m *Manager) ensureRunning(ctx context.Context, serverName string) (*Server
|
||||||
transport = &sdkmcp.StreamableClientTransport{
|
transport = &sdkmcp.StreamableClientTransport{
|
||||||
Endpoint: cfg.URL,
|
Endpoint: cfg.URL,
|
||||||
HTTPClient: httpClient,
|
HTTPClient: httpClient,
|
||||||
|
DisableStandaloneSSE: true,
|
||||||
}
|
}
|
||||||
inst.isHTTP = true
|
inst.isHTTP = true
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func (t *MCPBridgeTool) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MCPBridgeTool) Description() 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{} {
|
func (t *MCPBridgeTool) Parameters() map[string]interface{} {
|
||||||
|
|
@ -34,7 +34,7 @@ func (t *MCPBridgeTool) Parameters() map[string]interface{} {
|
||||||
"action": map[string]interface{}{
|
"action": map[string]interface{}{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The MCP action to perform",
|
"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{}{
|
"server": map[string]interface{}{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|
@ -48,6 +48,10 @@ func (t *MCPBridgeTool) Parameters() map[string]interface{} {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Arguments to pass to the tool (for mcp_call)",
|
"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"},
|
"required": []string{"action"},
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +70,8 @@ func (t *MCPBridgeTool) Execute(ctx context.Context, args map[string]interface{}
|
||||||
return t.getTools(ctx, args)
|
return t.getTools(ctx, args)
|
||||||
case "mcp_call":
|
case "mcp_call":
|
||||||
return t.callTool(ctx, args)
|
return t.callTool(ctx, args)
|
||||||
|
case "mcp_read_resource":
|
||||||
|
return t.readResource(ctx, args)
|
||||||
default:
|
default:
|
||||||
return ErrorResult(fmt.Sprintf("unknown action: %s", action))
|
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)
|
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)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue