Module: Html2rss::MCP::Server::Tools

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

Overview

MCP tool registration and outcome mapping over public html2rss APIs.

Constant Summary collapse

TOOLS =

Declarative MCP tool registrations consumed by register_all.

[
  {
    name: 'scrape',
    kind: :url,
    description: 'One-shot article extraction as JSON Feed items. ' \
                 'Use when you need articles now without a saved config. ' \
                 'strategy "auto" triggers fallback chain (default → botasaurus) for JS-rendered sites.',
    input_schema: Contract::SCRAPE_INPUT_SCHEMA,
    handler: :scrape_outcome
  },
  {
    name: 'inspect',
    kind: :url,
    description: 'Diagnostic page analysis (scrapers, SST, segments, final URL, status, ' \
                 'rel=alternate feeds). Use recon for BUILD/DEFER/DROP verdict and native_feed preference.',
    input_schema: Contract::INSPECT_INPUT_SCHEMA,
    call: lambda { |url:, strategy: 'auto', **|
      Outcome.inspect(
        report: PageRecon::Diagnostics.call(
          url:, strategy: Runtime.coerce_strategy(strategy), deep: false
        )
      )
    }
  },
  {
    name: 'recon',
    kind: :url,
    description: 'Curation verdict and native_feed preference for a URL. ' \
                 'Use after inspect when alternates warrant deeper recon, or when you need BUILD/DEFER/DROP.',
    input_schema: Contract::RECON_INPUT_SCHEMA,
    call: lambda { |url:, strategy: 'auto', **|
      Outcome.recon(result: Html2rss.recon(url, strategy: Runtime.coerce_strategy(strategy)))
    }
  },
  {
    name: 'batch_scrape',
    kind: :batch,
    batch_method: :batch_scrape,
    limit_default: 10,
    description: 'Scrape multiple URLs in parallel with per-URL error isolation. ' \
                 'Returns structured JSON Feed items and extraction counts.',
    input_schema: Contract::BATCH_SCRAPE_INPUT_SCHEMA
  },
  {
    name: 'batch_inspect',
    kind: :batch,
    batch_method: :batch_inspect,
    description: 'Inspect multiple URLs in parallel with per-URL error isolation. ' \
                 'Returns final redirected URLs, status codes, and rel="alternate" feeds.',
    input_schema: Contract::BATCH_INSPECT_INPUT_SCHEMA
  },
  {
    name: 'batch_recon',
    kind: :batch,
    batch_method: :batch_recon,
    description: 'Run recon across multiple URLs in parallel with per-URL error isolation. ' \
                 'Returns verdict, native_feed, and surface classification per URL.',
    input_schema: Contract::BATCH_RECON_INPUT_SCHEMA
  },
  {
    name: 'capture',
    kind: :capture,
    description: 'Derive a reusable html2rss feed config from a URL. ' \
                 'Use when the goal is a durable YAML (then test → apply). ' \
                 'Returns YAML inside payload.yaml (same serializer as CLI capture). ' \
                 'Draft only — catalog feeds still need directory.topics and title/url; ' \
                 'enhance defaults from admission evidence (false when chrome drops are high). ' \
                 'Full schema options live in resource html2rss://schema.',
    input_schema: Contract::CAPTURE_INPUT_SCHEMA,
    handler: :capture_outcome
  },
  {
    name: 'validate',
    kind: :config_xor,
    description: 'Validate a feed config hash XOR yaml string against the html2rss JSON schema. ' \
                 'Call before test. Failures return isError with payload.errors. ' \
                 'Full schema lives in resource html2rss://schema.',
    input_schema: Contract::CONFIG_XOR_SCHEMA,
    annotations: Contract::ANNOTATIONS_VALIDATE,
    call: lambda { |config: nil, yaml: nil, **|
      validation = Html2rss::Config.validate(ConfigArgument.parse(config:, yaml:).config)
      Outcome.validate(errors: validation.success? ? nil : validation.errors.to_h)
    }
  },
  {
    name: 'test',
    kind: :config_xor,
    description: 'Validate schema and execute live extraction (asserting >= min_items items). ' \
                 'Call after capture or validate; on success next_step is apply. ' \
                 'Returns test summary in payload with sample items, timing, failure_kind, ' \
                 'and quality_report (warnings for duplicate URLs, junk titles, native feed). ' \
                 'Set strict_quality to fail on duplicate URLs, >50% junk titles, or short titles.',
    input_schema: Contract::TEST_INPUT_SCHEMA,
    call: lambda { |config: nil, yaml: nil, min_items: 1, strict_quality: false,
                      compare_enhance: false, **kwargs|
      feed_config = ConfigArgument.parse(config:, yaml:).config
      test_args = { min_items:, strict_quality:, compare_enhance: }
      test_args[:strategy] = Runtime.coerce_strategy(kwargs[:strategy]) if kwargs.key?(:strategy)
      test_result = Html2rss.test(feed_config, **test_args)
      Outcome.test(test_result)
    }
  },
  {
    name: 'apply',
    kind: :config_xor,
    description: 'Apply a validated feed config (hash XOR yaml) and return RSS XML in payload.rss. ' \
                 'isError when the feed has zero items (ship gate). payload.item_count is RSS item count. ' \
                 'Use after test succeeds.',
    input_schema: Contract::APPLY_INPUT_SCHEMA,
    handler: :apply_outcome
  }
].freeze

Class Method Summary collapse

Class Method Details

.register_all(server, registrar:) ⇒ void

This method returns an undefined value.

Registers all MCP tools on server via registrar (Server.define_envelope_tool).

Parameters:



130
131
132
# File 'lib/html2rss/mcp/server/tools.rb', line 130

def register_all(server, registrar:)
  TOOLS.each { |entry| register_tool(server, registrar, entry) }
end