Module: Html2rss::MCP::Server::Inspect
- Defined in:
- lib/html2rss/mcp/server.rb
Overview
Diagnostic inspect path (not Capture ownership). Fetches once for SST/scraper stats.
Class Method Summary collapse
- .call(url:, strategy: :auto) ⇒ Hash
-
.concrete_strategy(strategy) ⇒ Symbol
Resolves feed-level strategy plans to concrete strategies for diagnostic fetch.
- .discover_segments(sst, url) ⇒ Array
- .fetch_response(url, strategy) ⇒ Html2rss::RequestService::Response
-
.redacted_endpoint(entry) ⇒ String?
Scheme+host+path only.
- .scraper_info(parsed) ⇒ Array<String>, Hash
- .segment_stats(sst, url) ⇒ Hash
- .sst_document(response) ⇒ Html2rss::SST::Document?
- .sst_stats_from(response) ⇒ Hash?
- .xhr_candidate_articles?(entry) ⇒ Boolean
-
.xhr_capture_info(response) ⇒ Hash
Redacted XHR capture diagnostics (no query strings).
Class Method Details
.call(url:, strategy: :auto) ⇒ Hash
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 |
# File 'lib/html2rss/mcp/server.rb', line 406 def call(url:, strategy: :auto) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize resolved = concrete_strategy(strategy) response = fetch_response(url, resolved) parsed = response.parsed_body result = { url:, strategy: resolved, content_type: response.content_type, html_response: response.html_response?, scraper_eligibility: scraper_info(parsed), sst_stats: sst_stats_from(response) } if response.html_response? sst = sst_document(response) if sst result[:sst] = { node_count: sst.node_count, degraded: sst.degraded, segment_stats: segment_stats(sst, url) } end end blocked = Html2rss::RequestService::BlockedSurface.interstitial_signature_for(response.body) result[:blocked_surface] = blocked[:key].to_s if blocked result[:xhr_capture] = xhr_capture_info(response) if resolved == :botasaurus result end |
.concrete_strategy(strategy) ⇒ Symbol
Resolves feed-level strategy plans to concrete strategies for diagnostic fetch.
:auto collapses to :faraday (inspect is a single-request diagnostic, not a fallback run).
397 398 399 400 |
# File 'lib/html2rss/mcp/server.rb', line 397 def concrete_strategy(strategy) plan = FeedPipeline::StrategyPlan.resolve(Server.resolve_mcp_strategy(strategy)) plan.is_a?(FeedPipeline::StrategyPlan::Auto) ? :faraday : plan.strategy end |
.discover_segments(sst, url) ⇒ Array
562 563 564 565 566 567 568 569 570 571 572 |
# File 'lib/html2rss/mcp/server.rb', line 562 def discover_segments(sst, url) link_resolver = Scoring::LinkResolver.new(url) AutoSource::Segmenter.call( sst, base_url: url, strategy: :list, link_resolver: ) rescue StandardError [] end |
.fetch_response(url, strategy) ⇒ Html2rss::RequestService::Response
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 |
# File 'lib/html2rss/mcp/server.rb', line 485 def fetch_response(url, strategy) # rubocop:disable Metrics/MethodLength -- session construction raw_config = Config.auto_source_config( url:, request_controls: Config::RequestControls.from_shortcut(strategy:) ) raw_config[:strategy] = strategy config = Config.from_hash(raw_config) resources = FeedPipeline::RuntimePolicy.resources_for(config) session = RequestSession.build( config:, strategy: config.strategy, budget: resources.budget, policy: resources.policy ) session.fetch_initial_response end |
.redacted_endpoint(entry) ⇒ String?
Returns scheme+host+path only.
454 455 456 457 458 459 460 461 462 463 464 |
# File 'lib/html2rss/mcp/server.rb', line 454 def redacted_endpoint(entry) raw = entry['url'] || entry[:url] return unless raw uri = URI.parse(raw.to_s) return unless uri.scheme && uri.host "#{uri.scheme}://#{uri.host}#{uri.path}" rescue URI::InvalidURIError nil end |
.scraper_info(parsed) ⇒ Array<String>, Hash
506 507 508 509 510 511 512 513 514 |
# File 'lib/html2rss/mcp/server.rb', line 506 def scraper_info(parsed) return { error: 'Response is not HTML' } unless parsed.is_a?(Nokogiri::HTML::Document) begin Html2rss::AutoSource::Scraper.from(parsed).map(&:name) rescue Html2rss::AutoSource::Scraper::NoScraperFound => error { none_found: error.category.to_s } end end |
.segment_stats(sst, url) ⇒ Hash
546 547 548 549 550 551 552 553 554 555 |
# File 'lib/html2rss/mcp/server.rb', line 546 def segment_stats(sst, url) segments = discover_segments(sst, url) return { found: 0 } if segments.empty? { found: segments.size, strategies: segments.map(&:strategy).uniq, sample_paths: segments.first(5).map { |s| s.root_node.tag_path } } end |
.sst_document(response) ⇒ Html2rss::SST::Document?
535 536 537 538 539 |
# File 'lib/html2rss/mcp/server.rb', line 535 def sst_document(response) Html2rss::SST::Normalizer.call(response.body) rescue ArgumentError nil end |
.sst_stats_from(response) ⇒ Hash?
520 521 522 523 524 525 526 527 528 529 |
# File 'lib/html2rss/mcp/server.rb', line 520 def sst_stats_from(response) return nil unless response.html_response? doc = sst_document(response) return nil unless doc { node_count: doc.node_count, degraded: doc.degraded } rescue StandardError nil end |
.xhr_candidate_articles?(entry) ⇒ Boolean
470 471 472 473 474 475 476 477 478 |
# File 'lib/html2rss/mcp/server.rb', line 470 def xhr_candidate_articles?(entry) body = entry['body'] || entry[:body] return false unless body.is_a?(String) document = JSON.parse(body, symbolize_names: true) AutoSource::Scraper::JsonState::CandidateDetector.candidate_array?(document) rescue JSON::ParserError false end |
.xhr_capture_info(response) ⇒ Hash
Returns redacted XHR capture diagnostics (no query strings).
441 442 443 444 445 446 447 448 |
# File 'lib/html2rss/mcp/server.rb', line 441 def xhr_capture_info(response) captured = response.captured_responses { count: captured.size, sample_endpoints: captured.first(5).filter_map { |entry| redacted_endpoint(entry) }, candidate_articles: captured.any? { |entry| xhr_candidate_articles?(entry) } } end |