Claude, You Magnificent Bastard, I Didn't Read Your Book

[Amazon Link]
(paid link)

Introduction/Rationale

Some readers may have noticed me griping about my blogging "infrastructure" a few weeks ago. To repeat and summarize: One of the bits of code I relied on was a Google Chrome extension called chromix-too. It did something I found incredibly useful: allowed access to Chrome's "tabs" API from the Linux command line. It was also quite powerful, but I only used it for four relatively simple things:

  1. Tell me how many tabs I have open;
  2. Tell me the URL of the active tab, and the title of its rendered page;
  3. Open a new inactive tab to a specified URL;
  4. Open a new active tab to a specified URL.

Granted, the last one is easy without an extension. The others don't seem to be, "as far as I can tell."

Alas, chromix-too used some features that Google deprecated years ago. It had a V2 manifest, and maybe violated other guidelines. And Google promised/warned that it would just stop working eventually.

And, as noted, "eventually" turned out to be "a few weeks ago". An upgraded Google Chrome refused to load chromix-too.

The author of chromix-too seemed uninterested in updating his code. I didn't press him about it.

I toyed with bringing it into compliance myself. Unfortunately, it was in Javascript, and even though it was but a few thousand bytes, I found it totally impenetrable. My efforts were feeble and futile.

But I had heard that AI tools, specifically Claude, could write code for you. A little Googling showed the "right way" to do what I wanted was via Chrome's Native Messaging facility. Which led to my Claude prompt:

I want a Chrome extension using Native Messaging to access the chrome.tabs API from the Linux shell.

And it worked. Kind of. Source is at GitHub.

Caveat

When I say "kind of", I'm not kidding. If you play with this yourself, be aware there's a bug that I haven't fixed. Described, with my workaround, at the end. I'll edit this if I ever fix it. Geeky readers, please let me know if you spot the problem.

The Overall Idea

Command Line HTTP Server (7444) Native Host Chrome Extension Chrome APIs

Claude provided the code to go in the middle three boxes.

Code Details

  • Command Line: you use an HTTP client (I use curl) to talk to the HTTP server which is listening on localhost port 7444. (That's the port chromix-too used to listen on.)

  • HTTP Server: A small (8779 bytes as I type) Python script that interprets the HTTP commands received from the command line and translates them into appropriate API code.

    Its filename is chrome_api_bridge.py, and I installed it in /usr/local/bin

  • Native Host: A JSON file that serves as a bit of glue between the HTTP server and the actual extension. It's very small (243 bytes), named com.chrome.api.bridge.json, and in LinuxLand it goes in the directory $HOME/.config/google-chrome/NativeMessagingHosts/.

    Note for those trying this at home: you have to fill in the ID of the chrome extension in this file. Which you won't know until you install the extension, which is…

  • Chrome Extension: A group of four files in their own directory:

    • manifest.json The extension's JSON manifest (duh);
    • background.js … and the extension's JavaScript code
    • popup.html and popup,js … I don't use these, but Claude provided them.

Installation Details

I think you should do things in this order. Sometime next month I will be installing Fedore 43 from scratch, and if I get anything wrong here, I'll amend.

Assuming you have Linux running and (specifically) Chrome installed normally…

  1. Install the extension. Point your browser at chrome://extensions; turn the "Developer mode" toggle on; click the "Load unpacked" button; in the resulting dialog, highlight the directory containing those four extension files, and click "Select".

    Copy the 32-character ID you should now see in your new extension's box.

  2. "Register" the Native Host. Edit the file com.chrome.api.bridge.json, pasting that 32-character ID string into the obvious place under the allowed_origins key. Put this file into the directory $HOME/.config/google-chrome/NativeMessagingHosts/.

  3. Install the HTTP server script. As stated above, I used /usr/local/bin/chrome_api_bridge.py. You can probably install it anywhere you want, but you'll have to change the path in the glue file installed in the previous step.

  4. Try it. Return to the chrome://extensions tab and turn the extension on. And (assuming nothing obviously bad happened) proceed to…

Examples

How do I count Chrome's open tabs? I give the command:

curl -s http://localhost:7444/chrome/tabs/query

This gives JSON output, which I parse, The "result" key has an array value, and the number of items in the array is the number of open tabs. A complete Perl script:

#!/usr/bin/perl

use strict;
use warnings;
use English qw( -no_match_vars );
use JSON;
use version; our $VERSION = qv('v2025.07.31');

my $curl_cmd = q{curl -s http://localhost:7444/chrome/tabs/query};
open my $CTQ, q{-|}, $curl_cmd or die "Failed to run curl command: $ERRNO\n";
my $ctq_json = <$CTQ>;
my $status   = close $CTQ;
my $decoded  = decode_json($ctq_json);
printf "Chrome open tabs: %d\n", scalar @{ $decoded->{'result'} };

Similarly, the command

curl -s http://localhost:7444/chrome/tabs/getCurrent

… just gives a one-element "result" and the current tab's URL and title are easily parsed out.

Opening a tab uses a POST:

curl -s -X POST http://localhost:7444/chrome/tabs/create -H "Content-Type: application/json" -d '{"url": "https://reason.com/latest", "active": true}'

That brings up the specified URL as an active window. Bringing it up as an inactive window… is left as an exercise for the reader.

That Darn Bug

Things should "just work" after starting Chrome. They do not. My extension throws an error:

Unchecked runtime.lastError: Native host has exited.

The workaround, which I arrived at after a few hours of trying everything else, is (I am not kidding):

From the chrome://extensions page:

  1. Turn the extension off.
  2. Turn it back on.

And then things seem to work fine.

This is puzzling.

When I Google that error message, all the "fixes" seem to assume a persistent error, that things aren't working at all. Nothing about problems fixed by "turn it off, then back on." So I'm stumped for now. Again, let me know if you happen to spot the problem.

I haven't tried asking Claude.

(Headline adapted from a classic movie.)


Last Modified 2025-08-02 10:48 AM EDT