Class: Utcp::Providers::McpProvider
- Inherits:
-
BaseProvider
- Object
- BaseProvider
- Utcp::Providers::McpProvider
- Defined in:
- lib/utcp/providers/mcp_provider.rb
Overview
Minimal HTTP-based MCP provider. Works in two modes:
- Manual discovery: GET {url}{discovery_path} returns a UTCP manual (tools array).
- Execution: POST {url}{call_path} with {"tool": name, "arguments": {...}}.
Streaming:
- If the server replies with 'text/event-stream', we'll parse SSE 'data:' lines and yield them.
- Otherwise, if a block is given, chunks from the HTTP body are yielded as they arrive.
Instance Attribute Summary
Attributes inherited from BaseProvider
Instance Method Summary collapse
-
#call_tool(tool, arguments = {}, &block) ⇒ Object
Expects tool.provider to include MCP endpoint info: { “provider_type”: “mcp”, “url”: “host:port/mcp”, “call_path”: “/call”, “headers”: { … } }.
- #discover_tools! ⇒ Object
-
#initialize(name:, url:, headers: {}, auth: nil, manual: false, discovery_path: "/manual", call_path: "/call") ⇒ McpProvider
constructor
A new instance of McpProvider.
Constructor Details
#initialize(name:, url:, headers: {}, auth: nil, manual: false, discovery_path: "/manual", call_path: "/call") ⇒ McpProvider
Returns a new instance of McpProvider.
22 23 24 25 26 27 28 29 |
# File 'lib/utcp/providers/mcp_provider.rb', line 22 def initialize(name:, url:, headers: {}, auth: nil, manual: false, discovery_path: "/manual", call_path: "/call") super(name: name, provider_type: manual ? "mcp_manual" : "mcp", auth: auth) @url = Utils::Subst.apply(url) @headers = Utils::Subst.apply(headers || {}) @manual = manual @discovery_path = discovery_path @call_path = call_path end |
Instance Method Details
#call_tool(tool, arguments = {}, &block) ⇒ Object
Expects tool.provider to include MCP endpoint info: { “provider_type”: “mcp”, “url”: “host:port/mcp”,
"call_path": "/call", "headers": { ... } }
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/utcp/providers/mcp_provider.rb', line 53 def call_tool(tool, arguments = {}, &block) p = tool.provider || {} base = Utils::Subst.apply(p["url"] || @url) call_path = p["call_path"] || @call_path uri = URI(join_path(base, call_path)) body = { "tool" => tool.name, "arguments" => Utils::Subst.apply(arguments || {}) } req = Net::HTTP::Post.new(uri) headers = default_headers.merge({ "Content-Type" => "application/json" }).merge(Utils::Subst.apply(p["headers"] || {})) apply_auth!(uri, headers) headers.each { |k, v| req[k] = v } req.body = JSON.dump(body) http = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") begin if block_given? http.request(req) do |res| ctype = (res["Content-Type"] || "").downcase if ctype.include?("text/event-stream") buffer = +"" res.read_body do |chunk| buffer << chunk while (line = buffer.slice!(/.*\n/)) line = line.strip next if line.empty? || line.start_with?(":") if line.start_with?("data:") data = line.sub(/^data:\s?/, "") yield data end end end else res.read_body do |chunk| yield chunk end end end nil else res = http.request(req) begin JSON.parse(res.body) rescue res.body end end ensure http.finish if http.active? end end |
#discover_tools! ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/utcp/providers/mcp_provider.rb', line 31 def discover_tools! raise ProviderError, "Not a manual provider" unless @manual uri = URI(join_path(@url, @discovery_path)) req = Net::HTTP::Get.new(uri) headers = default_headers apply_auth!(uri, headers) headers.each { |k, v| req[k] = v } http = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") begin res = http.request(req) raise ProviderError, "MCP discovery failed: #{res.code}" unless res.is_a?(Net::HTTPSuccess) manual = JSON.parse(res.body) to_tools(manual) ensure http.finish if http.active? end end |