Module: Html2rss::MCP::Server

Defined in:
lib/html2rss/mcp/server.rb,
lib/html2rss/mcp/server/tools.rb

Overview

Thin MCP wire adapter over public html2rss APIs.

Ownership: scraping/capture/validate/feed stay on gem entrypoints (Html2rss.auto_feed_result, Capture.build, Config.validate, Html2rss.feed_result). This module maps MCP kwargs to those APIs, then Outcome + Contract shape the envelope.

Strategy note: MCP auto passes through to FeedPipeline AutoFallback (default → botasaurus). Concrete strategies are used as-is. Botasaurus requires BOTASAURUS_SCRAPER_URL.

Defined Under Namespace

Modules: Tools

Constant Summary collapse

SERVER_NAME =

MCP server display name.

'html2rss'
SERVER_VERSION =

MCP server version (mirrors the gem version).

Html2rss::VERSION
HTTP_BIND_HOST =

Loopback bind for HTTP transport (local use only).

'127.0.0.1'
RESOURCES =

Declarative MCP resource registrations consumed by register_resources.

[
  {
    uri: 'html2rss://schema',
    name: 'Configuration JSON Schema',
    description: 'Full JSON Schema for html2rss feed configurations',
    mime_type: 'application/json',
    body: lambda {
      [{ uri: 'html2rss://schema', mimeType: 'application/json',
         text: Html2rss::Config.json_schema_json(pretty: true) }]
    }
  },
  {
    uri: 'html2rss://extractors',
    name: 'Available Extractors',
    description: 'Registered extractor names for selector configs ' \
                 '(full option docs live in html2rss://schema $defs)',
    mime_type: 'application/json',
    body: lambda {
      extractors = Html2rss::Selectors::Extractors::NAME_TO_CLASS.keys.map(&:to_s).sort
      [{ uri: 'html2rss://extractors', mimeType: 'application/json',
         text: JSON.pretty_generate(extractors) }]
    }
  },
  {
    uri: 'html2rss://strategies',
    name: 'Available Strategies',
    description: 'Published MCP request strategy names',
    mime_type: 'application/json',
    body: lambda {
      [{ uri: 'html2rss://strategies', mimeType: 'application/json',
         text: JSON.generate(Contract::STRATEGIES) }]
    }
  },
  {
    uri: 'html2rss://runtime',
    name: 'Runtime capabilities',
    description: 'Gem version, MCP contract version, catalog fingerprint, tool names, and Botasaurus config',
    mime_type: 'application/json',
    body: lambda {
      [{ uri: 'html2rss://runtime', mimeType: 'application/json',
         text: JSON.pretty_generate(Runtime.snapshot.to_h) }]
    }
  }
].freeze
PROMPTS =

Declarative MCP prompt registrations; SDK argument objects built at register_prompts time.

[
  {
    name: 'scrape-webpage',
    description: 'Guided one-shot scrape: one scrape call (auto already falls back)',
    arguments: [{ name: 'url', description: 'URL to scrape', required: true }],
    body: ->(args) { Outcome::Playbook.scrape_webpage_prompt(args.fetch(:url)) }
  },
  {
    name: 'capture-feed-config',
    description: 'Guided capture → test → apply; YAML draft plus catalog rewrite',
    arguments: [{ name: 'url', description: 'URL to analyze', required: true }],
    body: ->(args) { Outcome::Playbook.capture_feed_config_prompt(args.fetch(:url)) }
  }
].freeze

Class Method Summary collapse

Class Method Details

.build::MCP::Server

Builds the configured MCP protocol server (tools/resources/prompts).

Returns:

  • (::MCP::Server)


110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/html2rss/mcp/server.rb', line 110

def build # rubocop:disable Metrics/MethodLength -- protocol server construction
  ::MCP::Server.new(
    name: SERVER_NAME,
    title: SERVER_NAME,
    version: SERVER_VERSION,
    instructions: instructions_text,
    configuration: protocol_configuration
  ).tap do |server|
    register_tools(server)
    register_resources(server)
    register_prompts(server)
  end
end

.start(transport: :stdio, port: 8080) ⇒ Object

Starts the MCP server with the given transport.

Points Html2rss.logger at $stderr so stdio JSON-RPC on stdout stays intact, and raises the process log level to info unless LOG_LEVEL is set. A foreground watcher then sees the start banner, tool calls, and pipeline warns.

Parameters:

  • transport (Symbol) (defaults to: :stdio)

    :stdio or :http

  • port (Integer) (defaults to: 8080)

    port for HTTP transport

Raises:

  • (ArgumentError)


95
96
97
98
99
100
101
102
103
104
# File 'lib/html2rss/mcp/server.rb', line 95

def start(transport: :stdio, port: 8080)
  raise ArgumentError, "Unknown transport: #{transport.inspect}" unless %i[stdio http].include?(transport)

  configure_daemon_logging!
  app = build
  Log.info(start_banner(transport:, port:))
  return start_http(app, port:) if transport == :http

  ::MCP::Server::Transports::StdioTransport.new(app).open
end