Skip to main content
Glama
I-not-die-yet

Resource Review Bridge

Resource Review Bridge

A small, read-only MCP server that lets ChatGPT inspect public Instagram, Threads, and web resources through a local browser, then return a schema-validated Evidence Packet.

Status: experimental local-first software. It is intended for a single user, not as a hardened multi-tenant crawling service. This project is not affiliated with Instagram, Threads, Meta, or Notion.

It is designed for a simple research workflow:

  1. acquire the public source;

  2. separate direct evidence from promotional claims;

  3. discuss whether the resource is useful;

  4. save it elsewhere only after explicit user approval.

Why this exists

Public social posts are often awkward for an AI chat to inspect reliably: login reminders obscure otherwise public content, carousels require navigation, and repeated attempts can fill the conversation with duplicated or irrelevant page text. Resource Review Bridge performs bounded acquisition first and sends a compact, structured Evidence Packet into the reasoning step.

The goal is to reduce wasted context and repeated failed attempts, which can improve token efficiency. It does not guarantee lower token use: screenshots, long captions, large carousels, and detailed evidence still consume model context.

flowchart LR
    A[ChatGPT plugin] --> B[Secure MCP Tunnel]
    B --> C[Resource Review Bridge]
    C --> D[Isolated Playwright browser]
    D --> E[Codex app-server]
    E --> F[Validated Evidence Packet]
    F --> A

Related MCP server: Browser Intelligence MCP

What it does

  • Exposes one MCP tool: review_resource.

  • Accepts public http and https URLs only.

  • Rejects credentials, localhost, private IPs, and non-web schemes.

  • Uses an unsigned-in, temporary Playwright browser context.

  • Dismisses non-blocking Instagram Close/X login reminders.

  • Confirms the reminder actually disappears and checks again before each Reel frame capture. At most three close/backdrop attempts are allowed per acquisition; frames still blocked by a visible dialog are omitted and reported as missing targets.

  • Attempts one safe backdrop dismissal for a Threads login overlay.

  • Reads up to 12 carousel items, a bounded visible comment set, and 3 direct outbound pages. Filters known Instagram/Threads navigation links while preserving direct provider links; some platform links may still pass the filter.

  • Plays a public Instagram Reel when a visible video element is available, then seeks and captures confirmed video frames every 2 seconds through the first 60 seconds (at most 31 frames, with a 60-second visual acquisition budget). Cropped frames are grouped into chronological six-frame sheets with observed timestamps for the reviewer. Unknown-duration media uses bounded forward playback instead. Failed seeks and time caps are reported. Reel packets remain partial because frames between samples are not reviewed. An explicit local capture option can use 0.5-second sampling, capped at 121 frames.

  • Prioritizes readable on-screen names, subtitles, demonstrations, and results when identifying the video's subject. Audio is skipped by default and reported as not_attempted. Explicit local acquisition options or the benchmark's --include-audio flag can attempt up to 60 seconds with macOS on-device transcription. Missing or misrecognized audio does not invalidate clear visual evidence. An unidentified subject means insufficient acquisition and a deferred verdict, not low resource value.

  • Preserves the visible original caption in a dedicated caption field without reconstructing missing text.

  • Keeps caption and timestamped audio transcription separate in the Evidence Packet. A missing or inaccessible audio track is reported as blocked rather than inferred from the caption.

  • Recognizes Instagram video URLs with an author prefix and follows the final post URL after redirects. Audio completeness checks playback progress against the original recording duration, before WAV trimming. An unexpected seek or pause stops recording; only its continuous prefix is retained, and interrupted or capped recordings remain partial.

  • Continues navigation for mixed image/video carousels. Only an initially visible video is sampled, with optional transcription; other carousel video content is explicitly marked incomplete.

  • Returns a strict Evidence Packet with claims, evidence IDs, read states, and limitations.

  • Requests concise model output: up to two summary sentences, six claims, and six evidence items, while retaining the visible caption and all essential evidence and limitations.

  • Allows one active job, two bounded attempts, idempotent request IDs, and 24-hour local retention.

  • Returns within about 5 seconds on submission while a slow review continues in the same local server. Retrieve it with the same URL and returned request ID; each follow-up waits up to 25 seconds, with at most 6 automatic follow-ups per server session.

  • Reuses completed or partial results across request IDs and original/canonical URL aliases, so a retry can return immediately instead of repeating the browser review.

  • Validates cached packets against the current contract and requested URL; incompatible older packets are acquired again. Video results acquired before the installed visual reader's modification time are also refreshed, including canonical aliases. This conservative check may cause another acquisition after a checkout or file touch. Cache replay does not renew the original evidence's 24-hour retention period.

  • Runs Codex in a read-only sandbox without interactive approvals.

Requirements

  • Python 3.9 or newer.

  • Node.js and npm.

  • A local Codex executable, either on PATH, supplied with RESOURCE_REVIEW_CODEX, or bundled with the ChatGPT macOS app.

  • Access to OpenAI Secure MCP Tunnels for ChatGPT integration.

The repository does not include Tunnel, Cloudflared, browser, or Codex binaries.

Installation

These instructions set up a private, local developer-mode connection through OpenAI Secure MCP Tunnel. Secure MCP Tunnel keeps the MCP server off the public internet. It is intended here for private use and testing, not public plugin distribution.

1. Check the prerequisites

You need:

  • macOS or another local environment that can run the scripts in this repository;

  • Python 3.9 or newer;

  • Node.js and npm;

  • an authenticated local Codex executable, or the ChatGPT macOS app with its bundled Codex executable;

  • ChatGPT developer mode access;

  • an OpenAI Platform tunnel with Tunnels Read + Use permission;

  • a Tunnel runtime API key.

Creating or editing a tunnel requires Tunnels Read + Manage permission. ChatGPT developer mode and Platform tunnel permissions are separate.

2. Download the project

git clone https://github.com/I-not-die-yet/resource-review-bridge.git
cd resource-review-bridge

3. Install the browser dependency

npm ci
npx playwright install chromium

The repository has no third-party Python package dependency. Do not place API keys, Tunnel profiles, or other credentials inside the repository.

4. Run the local tests

python3 -m unittest discover -s tests -v

All tests should pass before connecting the bridge to ChatGPT. The tests use fakes and temporary SQLite files; they do not access private accounts or the network.

5. Confirm the MCP server starts

python3 -m resource_review_bridge.server --stdio \
  --state ./work/resource-review-state.sqlite

The process waits for MCP input on standard input. Press Control+C after this smoke test.

If Codex is not found automatically, set RESOURCE_REVIEW_CODEX to the executable path before starting the server. The supplied wrapper automatically checks the standard ChatGPT macOS app locations.

6. Configure Secure MCP Tunnel

Download the latest tunnel-client from OpenAI Platform tunnel settings or the latest official openai/tunnel-client release. Keep the binary and its profile outside this repository.

Start with the client's current built-in instructions:

/path/to/tunnel-client help quickstart

Create a local stdio profile using your own runtime API key and Tunnel ID. Use the absolute path to this repository's wrapper as the MCP command:

export CONTROL_PLANE_API_KEY="your-runtime-api-key"

/path/to/tunnel-client init \
  --sample sample_mcp_stdio_local \
  --profile resource-review \
  --tunnel-id your-tunnel-id \
  --mcp-command "/absolute/path/to/resource-review-bridge/scripts/run-resource-review-mcp"

Validate the profile, then start it:

/path/to/tunnel-client doctor --profile resource-review --explain
/path/to/tunnel-client run --profile resource-review

Keep that terminal window open. Run only one copy of the profile at a time. App discovery and calls to review_resource depend on the client remaining connected.

7. Add the private plugin in ChatGPT

  1. Enable Developer mode in ChatGPT under Settings → Security and login.

  2. Open Plugins, select the plus button, and create a developer-mode plugin.

  3. Set the connection type to Tunnel.

  4. Select the tunnel you created, or enter its Tunnel ID if it is not listed.

  5. Name the plugin Resource Review Bridge and complete the custom MCP server acknowledgement.

If the tunnel is missing, confirm that it is associated with the target ChatGPT workspace and that your account has Tunnels Read + Use permission.

Test the installation by asking ChatGPT to use review_resource on a public URL. A successful response contains a schema-validated Evidence Packet. The first live request may consume the Codex allowance associated with the local Codex session.

In Chat mode, select Resource Review Bridge from Add files and more before sending the URL. Typing or pasting an @Resource Review Bridge mention does not attach the tool. A conversation created before developer MCP support may continue to reject the tool; start a new Chat mode conversation and select the plugin there.

Updating

After pulling a new version, reinstall the locked browser dependencies and restart the single running Tunnel process:

git pull
npm ci

The wrapper derives the repository location at runtime, creates local state under work/, and locates the Codex executable. An explicit RESOURCE_REVIEW_CODEX value takes precedence; otherwise it prefers codex on PATH, then the ChatGPT app's CLI executable, then bundled fallbacks.

Configuration

Variable

Purpose

RESOURCE_REVIEW_CODEX

Explicit Codex executable path or command

RESOURCE_REVIEW_PYTHON

Explicit Python executable path or command

RESOURCE_REVIEW_NODE

Explicit Node.js executable path

RESOURCE_REVIEW_PLAYWRIGHT_PATH

Explicit Playwright package directory

RESOURCE_REVIEW_STATE_PATH

SQLite state file path

When no browser variables are set, the bridge checks the repository's node_modules first and then common local Codex runtime locations.

Reel audio on macOS

Audio is disabled by default in normal MCP reviews; the tool has no per-request audio switch. The benchmark's --include-audio option enables it for a local test. Reel audio requires macOS and Apple's command-line developer tools (swiftc, xcrun, and codesign). The first audio capture attempt builds a signed local speech helper in the ignored work/Transcriber.app directory. Normal review calls do not request new system permissions.

In macOS System Settings, enable Keyboard → Dictation. After the helper has been built, run this command from the repository directory to request Speech Recognition access:

open -W -n -a "$PWD/work/Transcriber.app" --args --authorize

Allow the macOS prompt, then run a fresh benchmark with --include-audio and a new output directory. The bridge requests on-device recognition for English speech and records timestamped transcript segments in the Evidence Packet. If permission is denied, audio is marked blocked; visual and caption evidence can still be returned. This optional test does not change normal MCP defaults. An already cached partial MCP packet is reused until its 24-hour retention period expires, unless the reader changes.

Suggested ChatGPT project workflow

The reusable AI Tools Log instructions are in prompts/ai-tools-log-project-instructions.md. They tell ChatGPT to review evidence first, give a concise judgment, discuss it, and write to Notion only after explicit approval.

Long reviews and result retrieval

Keep the tunnel running while a review is processing. review_resource returns either a terminal result or status=running, request_id, remaining_poll_attempts, and next_action. A running response is not a failure and contains no Evidence Packet yet. Call the same tool with the exact same URL and returned request ID to retrieve the admitted review. The bridge does not reacquire the source or rerun the model when polling; the existing maximum of two processing attempts still applies to transient failures.

The first call waits up to 5 seconds for processing, and each follow-up up to 25 seconds. Stop automatic retrieval after 6 follow-ups or when next_action=wait_for_user. The job can continue beyond this waiting budget. A later user-requested call can retrieve its terminal result within the retention period. The poll budget resets when the local server restarts; clients must also respect the 6-follow-up limit. These waits exclude database admission and transport overhead.

A new request ID for the exact same running URL joins the original job and returns its original request ID. A different URL receives busy. Closing standard input gracefully lets the admitted review finish before the server exits. A forced shutdown can interrupt it: restart the tunnel, then retrieve with the same URL and request ID. An interrupted running row becomes stale_interrupted after 15 minutes; after that terminal failure, a fresh request ID permits one new review. No completion notification is sent automatically.

After updating the server, restart the local tunnel. Refresh the plugin's tool metadata in ChatGPT if available, and replace the project instructions with the updated prompt above. The tool name and input fields have not changed; running responses also contain recovery instructions for clients with older metadata.

Test

python3 -m unittest discover -s tests -v

Tests use fakes and temporary SQLite files. They do not access the network, private accounts, or model usage.

The optional local browser fixture for Reel playback requires an unrestricted terminal with Playwright Chromium installed. Run it with RESOURCE_REVIEW_BROWSER_TEST=1 npm run check; it creates synthetic video and audio locally and makes no network or model calls. To also verify spoken-word transcription, set RESOURCE_REVIEW_SPEECH_FIXTURE to an absolute path for a non-sensitive local WAV file with audible English speech. That test requires macOS Speech Recognition permission and enabled Dictation or Siri; it makes no model or paid API call.

Timing a fresh review

The one-shot benchmark uses isolated temporary state, so an existing cached packet cannot make a new acquisition look faster. It runs one attempt, uses the existing Codex allowance, and saves numeric phase timings and the resulting packet under an ignored output directory:

python3 scripts/benchmark-resource-review.py https://www.instagram.com/reel/PUBLIC_REEL_ID/ --output work/timing-run

Its defaults match normal MCP reviews: two-second visual sampling, intentionally skipped audio, filtered platform navigation links, and concise model output. Use --interval-seconds 0.5 --include-audio --keep-platform-links --full-output for a comparison with dense sampling, audio, unfiltered navigation, and unrestricted output size. The interval is bounded to 0.5–2 seconds. Supply RESOURCE_REVIEW_CODEX when the executable is not discoverable. Keep other reviews idle during the benchmark.

report.json separates browser acquisition from model startup and analysis; nested browser/video timings cover page load, auth dismissal, playback validation, visual sampling, image-sheet generation, audio, carousel capture, and outbound pages. Skipped audio is recorded as not_attempted, with zero audio time. Temporary browser screenshots and state are removed after the run; a representative visual proof, report, and packet remain in the specified ignored output directory. Timings are diagnostics and never source evidence. A single measurement does not establish a reliable latency guarantee.

On 2026-10-12, one fresh local benchmark of the same public Reel completed in 82.310 seconds: acquisition 25.484 seconds, model startup/analysis 56.473 seconds, and other work 0.353 seconds. It captured 27 confirmed frames in five sheets, identified Nex-N2.5 at 12 seconds, and returned a schema-valid partial packet. Visual sampling itself took 3.540 seconds; outbound browsing consumed 13.569 seconds and still encountered irrelevant platform links. This experiment used two-second sampling, no audio, navigation filtering, and concise output together, so the improvement cannot be attributed to sampling alone. It excludes ChatGPT plugin transport and polling time and does not establish direct computer-use performance. These settings were subsequently promoted to normal MCP defaults with the user's approval; 90 seconds is a target, not a latency guarantee.

Inspect the latest terminal job without printing retained evidence:

python3 -m resource_review_bridge.server --status \
  --state ./work/resource-review-state.sqlite

Recovery and error states

A job left running for more than 15 minutes is changed to stale_interrupted before a new job is admitted. Failures after admission always become terminal states:

  • browser failure: acquisition_failed;

  • Codex generation failure: evidence_generation_failed;

  • schema failure: invalid_evidence_packet;

  • unexpected failure: internal_error.

This prevents a failed review from permanently blocking later requests as busy. New Codex generation failures also return a fixed error_reason category and retain only the latest category in the private work/last-failure.json file. The --status command shows it for the matching job. Raw process output and source content are not retained there. Earlier failures cannot be diagnosed retroactively from this field.

The app-server client drains stdout and stderr as byte streams so batched events and verbose process output cannot stall the review. An error_reason of codex_other still means the upstream Codex turn failed without a more specific safe category. Intermediate commentary messages are ignored when selecting the final packet. Legacy messages without a phase start the completion fallback only when they contain a JSON candidate with the required packet fields.

Security and privacy

  • The tool is read-only and exposes no general shell or filesystem action.

  • Acquired page text is untrusted evidence, never an instruction.

  • Downloads, forms, login, likes, comments, follows, and posting are not allowed.

  • Local screenshots, temporary audio, and SQLite state may contain source content. They are stored outside version control; temporary audio is removed after each attempt.

  • Snapshot text, audio transcripts, and screenshots are sent to the user's Codex service for Evidence Packet generation. Review only material you are permitted to process this way.

  • The browser applies public-address checks before navigation and during browser subrequests. Treat these checks as defense in depth, not as a hardened network isolation boundary for hostile URLs.

See SECURITY.md for disclosure guidance and operational limits.

Verification status

  • Unit tests cover URL policy, packet validation, job recovery, idempotency, error finalization, and the single-tool MCP surface.

  • Python tests also cover fast submission, joining the same running URL, the 6-follow-up limit, DNS rejection before acquisition, background failures, graceful stdio closure while processing, visual evidence requirements, dense image-sheet input, preservation of readable mixed-carousel images, and refresh of old sparse video caches.

  • A public Instagram carousel was read after dismissing its login reminder; the caption, carousel, visible comments, and direct links produced a valid Evidence Packet.

  • A live Secure MCP Tunnel and ChatGPT plugin request returned the packet to the originating conversation.

  • A public 50.28-second Instagram Reel was acquired locally after the audio change: 5 visual samples and 7 timestamped transcript segments were returned. This verified acquisition and local transcription, not a new end-to-end Evidence Packet generation through the tunnel.

  • Local synthetic video tests exercise dense finite-duration sampling, chronological image sheets, interruption after a playback seek, and temporary WAV cleanup. The optional spoken-word fixture was skipped in the latest run because no input WAV was supplied; that run does not reverify spoken-word recognition.

  • On 2026-10-11, a live Reel review generated a validated partial packet with 6 transcript segments. The first plugin call exceeded the host timeout; replaying the same request ID returned the cached packet through the live tunnel without another acquisition or model call. The audio coverage check marked the real recording partial, rather than claiming full coverage.

  • After the background-submission change on 2026-10-11, another live review of that Reel returned running in about 8 seconds including transport overhead. Four follow-ups retrieved a schema-valid partial packet with 7 transcript segments; processing took 145 seconds. No plugin call timed out. Audio remained partial. This verifies the existing plugin and tunnel result-retrieval path; separate ChatGPT project instructions still need to be updated by the user.

  • On 2026-10-12, the updated live tunnel acquired 102 confirmed frames from the same public Reel, arranged in 17 chronological sheets. The returned packet identified Nex-N2.5 from the 10.5–12-second title frames and separately identified the Nex-N2.5-mini label in later comparison frames. This passes the visual-subject acceptance check that the earlier sparse packet failed. The packet remained partial and distinguished promotional claims from independently verified facts. Processing took 236 seconds; no individual plugin call timed out. Both same-ID and new-ID cache replays returned the unchanged packet and review guidance in about 2.2 seconds without a new acquisition. A client that reaches its six-follow-up limit must retrieve the saved result later.

  • The earlier dense live public Reel returned 6 audio segments, with capture coverage marked completed; recognition errors remained visible rather than being replaced with guessed text. Completed audio coverage does not mean flawless transcription. No new paid API was used; the live review used the existing Codex allowance.

  • After promoting the fast defaults on 2026-10-12, the local run passed 46 Python tests, 9 browser-rule tests, and 2 synthetic browser tests. The optional spoken-word test was skipped. Synthetic tests cover a reminder appearing after playback starts and a Close button that leaves the dialog visible; a click alone no longer counts as a successful dismissal.

  • A fresh live plugin review with the fast defaults and confirmed reminder dismissal returned a schema-valid partial packet in 87.470 seconds from initial request to retrieval; local job processing took 84 seconds. It captured 27 confirmed frames in five sheets, reported zero failed targets, retained the caption, identified Nex-N2.5 at 12 seconds and Nex-N2.5-mini at 18 seconds, and correctly marked intentionally skipped audio as not_attempted. Same-ID replay took 2.602 seconds and returned an unchanged packet. This is one successful live measurement, not a guarantee that future reviews finish within 90 seconds.

  • That live packet reported a failed Meta AI footer link while retaining outbound_links=completed. The acquirer was subsequently corrected to mark failed outbound navigation as partial; this metadata correction was syntax-checked but not followed by another model run. The retained packet predates that correction and states its failed link in limitations.

  • Threads public pages were acquired, but the backdrop-dismiss branch remains code-reviewed rather than live-triggered.

Known limitations

  • Instagram and Threads markup can change without notice.

  • The browser URL checks reduce accidental private-network access but are not a complete defense against hostile DNS infrastructure; use additional network isolation for untrusted arbitrary domains.

  • Publicly visible comments are bounded and may be incomplete.

  • Two-second Reel sampling may miss brief titles, visual events, or actions between frames; it covers at most the first 60 seconds. Audio is intentionally skipped by default. When enabled locally, browser media restrictions can prevent audio capture. Transcription requires macOS Speech Recognition permission, enabled Dictation or Siri, and English on-device recognition support. The Bridge sets requiresOnDeviceRecognition=true and does not call a paid API. Enabling the macOS Dictation setting is a separate system choice: macOS warns that its normal dictation feature may send voice input and contact names to Apple. Other spoken languages remain unsupported. Temporary captured audio is deleted after each attempt. The signed local helper bundle is kept under ignored work/ so its system permission can persist.

  • Evidence generation consumes the user's existing Codex allowance.

  • A slow review may outlast the 6-follow-up waiting budget. Its result must be retrieved later while the tunnel is available; there is no durable MCP Apps completion card or automatic notification.

  • This project does not bypass authentication, paywalls, CAPTCHAs, or access controls.

  • Users are responsible for following applicable laws and each source site's terms.

License

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI assistants to inspect web pages, monitor network requests, extract HTML, analyze console output, and examine DOM elements in real-time through a Playwright-powered browser.
    1 npm
    7
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables coding agents to retrieve verified web evidence via headless Chromium rendering, sitemap navigation, and search candidate discovery, returning clean Markdown, structured JSON, and verbatim quoted proof.
    1 npm
    MIT