mcp-tools.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. package agent
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/opencode-ai/opencode/internal/config"
  7. "github.com/opencode-ai/opencode/internal/llm/tools"
  8. "github.com/opencode-ai/opencode/internal/logging"
  9. "github.com/opencode-ai/opencode/internal/permission"
  10. "github.com/opencode-ai/opencode/internal/version"
  11. "github.com/mark3labs/mcp-go/client"
  12. "github.com/mark3labs/mcp-go/mcp"
  13. )
  14. type mcpTool struct {
  15. mcpName string
  16. tool mcp.Tool
  17. mcpConfig config.MCPServer
  18. permissions permission.Service
  19. }
  20. type MCPClient interface {
  21. Initialize(
  22. ctx context.Context,
  23. request mcp.InitializeRequest,
  24. ) (*mcp.InitializeResult, error)
  25. ListTools(ctx context.Context, request mcp.ListToolsRequest) (*mcp.ListToolsResult, error)
  26. CallTool(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
  27. Close() error
  28. }
  29. func (b *mcpTool) Info() tools.ToolInfo {
  30. return tools.ToolInfo{
  31. Name: fmt.Sprintf("%s_%s", b.mcpName, b.tool.Name),
  32. Description: b.tool.Description,
  33. Parameters: b.tool.InputSchema.Properties,
  34. Required: b.tool.InputSchema.Required,
  35. }
  36. }
  37. func runTool(ctx context.Context, c MCPClient, toolName string, input string) (tools.ToolResponse, error) {
  38. defer c.Close()
  39. initRequest := mcp.InitializeRequest{}
  40. initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
  41. initRequest.Params.ClientInfo = mcp.Implementation{
  42. Name: "OpenCode",
  43. Version: version.Version,
  44. }
  45. _, err := c.Initialize(ctx, initRequest)
  46. if err != nil {
  47. return tools.NewTextErrorResponse(err.Error()), nil
  48. }
  49. toolRequest := mcp.CallToolRequest{}
  50. toolRequest.Params.Name = toolName
  51. var args map[string]any
  52. if err = json.Unmarshal([]byte(input), &args); err != nil {
  53. return tools.NewTextErrorResponse(fmt.Sprintf("error parsing parameters: %s", err)), nil
  54. }
  55. toolRequest.Params.Arguments = args
  56. result, err := c.CallTool(ctx, toolRequest)
  57. if err != nil {
  58. return tools.NewTextErrorResponse(err.Error()), nil
  59. }
  60. output := ""
  61. for _, v := range result.Content {
  62. if v, ok := v.(mcp.TextContent); ok {
  63. output = v.Text
  64. } else {
  65. output = fmt.Sprintf("%v", v)
  66. }
  67. }
  68. return tools.NewTextResponse(output), nil
  69. }
  70. func (b *mcpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
  71. sessionID, messageID := tools.GetContextValues(ctx)
  72. if sessionID == "" || messageID == "" {
  73. return tools.ToolResponse{}, fmt.Errorf("session ID and message ID are required for creating a new file")
  74. }
  75. permissionDescription := fmt.Sprintf("execute %s with the following parameters: %s", b.Info().Name, params.Input)
  76. p := b.permissions.Request(
  77. permission.CreatePermissionRequest{
  78. SessionID: sessionID,
  79. Path: config.WorkingDirectory(),
  80. ToolName: b.Info().Name,
  81. Action: "execute",
  82. Description: permissionDescription,
  83. Params: params.Input,
  84. },
  85. )
  86. if !p {
  87. return tools.NewTextErrorResponse("permission denied"), nil
  88. }
  89. switch b.mcpConfig.Type {
  90. case config.MCPStdio:
  91. c, err := client.NewStdioMCPClient(
  92. b.mcpConfig.Command,
  93. b.mcpConfig.Env,
  94. b.mcpConfig.Args...,
  95. )
  96. if err != nil {
  97. return tools.NewTextErrorResponse(err.Error()), nil
  98. }
  99. return runTool(ctx, c, b.tool.Name, params.Input)
  100. case config.MCPSse:
  101. c, err := client.NewSSEMCPClient(
  102. b.mcpConfig.URL,
  103. client.WithHeaders(b.mcpConfig.Headers),
  104. )
  105. if err != nil {
  106. return tools.NewTextErrorResponse(err.Error()), nil
  107. }
  108. return runTool(ctx, c, b.tool.Name, params.Input)
  109. }
  110. return tools.NewTextErrorResponse("invalid mcp type"), nil
  111. }
  112. func NewMcpTool(name string, tool mcp.Tool, permissions permission.Service, mcpConfig config.MCPServer) tools.BaseTool {
  113. return &mcpTool{
  114. mcpName: name,
  115. tool: tool,
  116. mcpConfig: mcpConfig,
  117. permissions: permissions,
  118. }
  119. }
  120. var mcpTools []tools.BaseTool
  121. func getTools(ctx context.Context, name string, m config.MCPServer, permissions permission.Service, c MCPClient) []tools.BaseTool {
  122. var stdioTools []tools.BaseTool
  123. initRequest := mcp.InitializeRequest{}
  124. initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
  125. initRequest.Params.ClientInfo = mcp.Implementation{
  126. Name: "OpenCode",
  127. Version: version.Version,
  128. }
  129. _, err := c.Initialize(ctx, initRequest)
  130. if err != nil {
  131. logging.Error("error initializing mcp client", "error", err)
  132. return stdioTools
  133. }
  134. toolsRequest := mcp.ListToolsRequest{}
  135. tools, err := c.ListTools(ctx, toolsRequest)
  136. if err != nil {
  137. logging.Error("error listing tools", "error", err)
  138. return stdioTools
  139. }
  140. for _, t := range tools.Tools {
  141. stdioTools = append(stdioTools, NewMcpTool(name, t, permissions, m))
  142. }
  143. defer c.Close()
  144. return stdioTools
  145. }
  146. func GetMcpTools(ctx context.Context, permissions permission.Service) []tools.BaseTool {
  147. if len(mcpTools) > 0 {
  148. return mcpTools
  149. }
  150. for name, m := range config.Get().MCPServers {
  151. switch m.Type {
  152. case config.MCPStdio:
  153. c, err := client.NewStdioMCPClient(
  154. m.Command,
  155. m.Env,
  156. m.Args...,
  157. )
  158. if err != nil {
  159. logging.Error("error creating mcp client", "error", err)
  160. continue
  161. }
  162. mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c)...)
  163. case config.MCPSse:
  164. c, err := client.NewSSEMCPClient(
  165. m.URL,
  166. client.WithHeaders(m.Headers),
  167. )
  168. if err != nil {
  169. logging.Error("error creating mcp client", "error", err)
  170. continue
  171. }
  172. mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c)...)
  173. }
  174. }
  175. return mcpTools
  176. }