Kevin McMahon

Enthusiast

Introducing smartpin - AI-Powered Bookmarking for Pinboard

I’m excited to announce the release of smartpin, a new command-line tool that leverages AI to improve the metadata you capture for your Pinboard bookmarking. This tool scratches an itch that I’ve had for a while to apply some meaningful curation and organization to the links and articles I save off.

The Problem with Manual Bookmarking

We’ve all been there - you find an interesting article, quickly save it to your bookmarking service, and promise yourself you’ll come back later to properly tag and describe it. But “later” rarely comes, and you end up with a graveyard of poorly organized bookmarks that are nearly impossible to find when you need them.

Even when you do take the time to add metadata, it’s often inconsistent. Sometimes you use “machine-learning,” other times “ml” or “AI.” Your descriptions might be too verbose one day and non-existent the next. This inconsistency makes your bookmark collection less useful over time.

Enter SmartPin

smartpin solves this problem by using AI to automatically analyze web pages and extract relevant metadata. When you install the package from PyPI, you get the pinit command-line tool that:

  • Extracts meaningful titles - Not just the HTML title tag, but the actual content headline
  • Generates concise descriptions - A 1-2 sentence summary of what the page is actually about
  • Suggests relevant tags - 3-8 contextually appropriate tags for better organization
  • Supports multiple AI models - Use Claude, GPT-4, Gemini, or other LLMs based on your preference

How It Works

The usage is dead simple. Once configured with your Pinboard API token and AI provider credentials, just run:

pinit add https://example.com/interesting-article

The tool fetches the page content, sends it to your chosen AI model for analysis, and automatically creates a well-structured bookmark with proper metadata. No manual input required!

Here’s what the output looks like:

┌─ Extracted Bookmark ─────────────────────────────────┐
│ Title: How to Build Better Software with AI          │
│ URL: https://example.com/ai-software-development     │
│ Description: A comprehensive guide exploring how     │
│ artificial intelligence can enhance software         │
│ development workflows and code quality.              │
│ Tags: ai, software-development, programming, guide   │
└──────────────────────────────────────────────────────┘

✓ Bookmark saved successfully!

Key Features

Flexible AI Integration: The tool uses Simon Willison’s excellent LLM library, which means you can easily switch between different AI providers. Prefer Claude? Use it. Have OpenAI credits? Switch to GPT-4. The choice is yours.

Privacy Controls: Mark bookmarks as private or “to read” with simple flags:

pinit add https://example.com --private --toread

Dry Run Mode: Preview what the AI extracts before saving:

pinit add https://example.com --dry-run

JSON Output: Need to integrate with other tools? Get machine-readable output:

pinit add https://example.com --json | jq '.tags'

Multiple Configuration Options: Configure via environment variables, local .env files, or user config at ~/.pinit/config. The tool supports whatever workflow fits your style.

Installation and Setup

Getting started is straightforward:

# Install from PyPI
pip install smartpin

# Or if you use uv (recommended)
uv pip install smartpin

# Set up your API tokens
export PINBOARD_API_TOKEN=your_username:your_token
export ANTHROPIC_API_KEY=your_key  # Or OPENAI_API_KEY, etc.

# Start bookmarking!
pinit add https://example.com

Technical Details

For the curious, smartpin is built with:

  • Click for the CLI framework
  • Rich for beautiful terminal output
  • BeautifulSoup for robust HTML parsing
  • httpx for reliable web page fetching
  • Type hints throughout for better development experience

The architecture is modular and clean, making it easy to extend or modify. The AI prompting is done through Jinja2 templates, so you can even customize how the AI analyzes pages if needed.

What’s Next?

This is just the beginning. Some ideas for future development:

  • Bulk import of existing bookmarks for re-tagging
  • Integration with browser extensions
  • iOS share extension for quick capture of links on your iOS devices

Try It Out

smartpin is open source and available now on PyPI and GitHub. I’d love to hear your feedback, feature requests, or contributions.

Making Sure OpenSea Can Read Your Contract-level Metadata

Leaving this here for my future self and for others as this question pops up on the OpenSea discord quite a bit.

tl;dr – Don’t return an IPFS URI (ipfs://{cid}) or IPFS HTTP gateway URL (https://ipfs.io/ipfs/{cid} or https://gateway.pinata.cloud/ipfs/{cid}) via the contractURI() function. Use a traditional HTTP web API or base64 encode the metadata JSON and return it from the function if you want OpenSea to be able to read the contract-level metadata and display it on your collection’s page.

OpenSea allows smart contract developers to include storefront-level metadata by implementing a function named contractURI() and returning a URI pointing to the metadata. OpenSea expects metadata formatted as JSON that consists of a few specific attributes they recognize. This information is pre-populated into the NFT’s collections page associated with the smart contract on OpenSea.

For individual non-fungible tokens (NFT) like ERC721’s and ERC1155’s, calling the tokenURI() function with a tokenId value as a parameter returns a URI that points at the token’s metadata. The tokenURI() function typically returns URI that point to traditional endpoints like web servers or cloud storage using conventional URL protocols (https://) or, more commonly in web3, to files stored on IPFS via the IPFS URL protocol (ipfs://). URIs returned from this function can point directly to IPFS or via IPFS HTTP gateways without problems.

It is unclear from the docs, but the responses from the token URI function can vary more than the contract URI function. I have experimented with valid URI variations, all pointing to the same JSON, and had no success with having the contract URI function return either IPFS or IPFS public gateway URIs. Providing the same types of URI from the token URI functions has worked as expected. What I have found to work are the following:

  1. Return the metadata JSON as a base64 encoded string. Do not forget to prepend the string with data:application/json;base64,{yourBase64EncodedStringHere}.

  2. Return a URL pointing to a conventional REST API endpoint that returns the metadata JSON.

I cannot explain why the API requests to a REST API work as expected or why the requests to IPFS or an IPFS public gateway do not. I plan to file this as a ticket with OpenSea, and hopefully, if they’re unable to address the issue outright, they can at least update the docs to be more explicit about what can and cannot be returned from the contract URI function.

Authenticating to Heroku via CLI with MFA Turned On

Leaving this here for my future self.

Heroku has turned off interactive login and pushes folks to authenticate via a browser when authenticating to use the CLI app. This works fine if you don’t have MFA turned on (you should though!) and the flow completes without issue. If you do have MFA turned on, you’ll get a blank screen and a cryptic message (“IP address mismatch”) when attempting to complete the login.

To authenticate from the CLI you can follow this note from the Heroku Developer Docs and put your newly generated API key in a .netrc file or set and environment variable (HEROKU_API_KEY) or just enter the API key to the password prompt when interacting with the CLI.

➜  ~ heroku auth:login -i
heroku: Enter your login credentials
Email : [YOUR_EMAIL_HERE]
Password : [THE_API_KEY_YOU_GENERATED]

Logged in as [YOUR_EMAIL_HERE]

Carvey Replacement Hinges

The internet is almost as impressive as my ability to break things. This is a story about how I broke a CNC machine and was able to not only find the exact replacement parts but order them for next day delivery within about 20 minutes.

tl;dr

The Inventables Carvey model I am using uses Southco E6-10-301-20 Series Adjustable Torque Position Control Hinge with Holes.

Southco E6-10-301-20 Series Adjustable Torque Position Control Hinge with Holes (PDF)

Amazon Link

What Happened

Recently a friend let me borrow a Carvey that was taking up space at his place and, not 10 mins after getting it powered up, I broke it. To make a long story short, I racked the lid when attempting to pick it up, which caused both hinges to fail catastrophically. The hydraulic lift, which props the lid open and provides a soft close, put a tremendous amount of pressure on the device’s plastic hinges. Once the first hinge failed, the second failed immediately. It felt like my heart was going to be next.

After calling the very understanding owner and apologizing for destroying the hinges, we discussed the steps necessary for repair. Inventables support is great. I’m sure I could’ve got replacement hinges from them. But that would take phone calls, support tickets, and waiting for them to ship one-off parts. The parts are just hinges. If we could get replacements, it is a simple process to affix them to the case. Maybe we can find replacements ourselves.

So the search began. After coming up empty via the search engine route, McMaster-Carr was the next stop. It turns out there are tons of different hinges. McMaster-Carr apparently has all of them. We were however able to find something that looked promising. With the specs of the McMaster-Carr hinge now in hand, I got my calipers out. The sizes matched (give or take a ten-thousandth of an inch). We felt good about having a viable replacement option, but the question lingered if we could do better.

After a closer inspection of the broken hinges, I noticed that they had Southco branding. A quick search of their site led us to the spec sheet of the Southco E6-10-301-20. That was it, and of course, Amazon has it stocked and ready to ship.

The fact that I was able to identify these hinges, source them from a supplier, order a small number of them, and have them shipped to my door in two days is amazing. That I was able to do it within 20 minutes after I broke them blows my mind—what a world.

Favorites from September 2020

A collection of some of my favorite links, images, tweets and quotes from September 2020. This is not an exhaustive list but does captures some things I found interesting, thought-provoking, or cool over the past month.

via wendy macnaughton on Instagram: “Art.”

Quotes