Skip to main content
ClaudeWave

AI read-it-later & personal knowledge base

MCP ServersOfficial Registry2 stars0 forks● TypeScriptUpdated today
ClaudeWave Trust Score
62/100
· OK
Passed
  • ✓Actively maintained (<30d)
  • ✓Clear description
  • ✓Documented (README)
Flags
  • !No standard license detected
Last scanned: 10/2/2026
Install in Claude Code / Claude Desktop
Method: Manual
Claude Code CLI
git clone https://github.com/nublson/purl
claude_desktop_config.json (Claude Desktop)
{
  "mcpServers": {
    "purl": {
      "command": "node",
      "args": ["/path/to/purl/dist/index.js"],
      "env": {
        "DATABASE_URL": "<database_url>",
        "NEXT_PUBLIC_SUPABASE_URL": "<next_public_supabase_url>",
        "NEXT_PUBLIC_SUPABASE_ANON_KEY": "<next_public_supabase_anon_key>",
        "SUPABASE_SERVICE_ROLE_KEY": "<supabase_service_role_key>",
        "GOOGLE_CLIENT_SECRET": "<google_client_secret>",
        "GITHUB_CLIENT_SECRET": "<github_client_secret>"
      }
    }
  }
}
1. Run the command above in your terminal (Claude Code), or paste the JSON config into claude_desktop_config.json (Claude Desktop).
2. Replace any <placeholder> values with your API keys or paths.
3. Restart Claude. The MCP server and its tools appear automatically.
💡 Clone https://github.com/nublson/purl and follow its README for install instructions.
Detected environment variables
DATABASE_URLNEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEYGOOGLE_CLIENT_SECRETGITHUB_CLIENT_SECRET
Use cases

MCP Servers overview

<p align="center">
  <img src="thumbnail.jpeg" alt="Purl — Save Anything. Keep what matters. A home for your links, PDFs, video, and audio." width="920" />
</p>

# Purl

**Save anything. Keep it in one place.**

**Live preview:** [https://purl.nublson.com](https://purl.nublson.com)

Purl is a read-it-later app — a home for your "pearls". You paste URLs: web pages, PDFs, YouTube videos, and audio. Purl resolves each item's metadata (title, favicon, description, thumbnail) and keeps everything in one place, available from the app, a REST API, and an MCP server.

Purl is free, with one limit: each account can save up to **1,000 links** (`MAX_SAVED_LINKS` in [`src/lib/limits.ts`](src/lib/limits.ts)).

The product goal: one place to stash material you care about.

## Implemented today

- **Marketing site** — Landing page (hero with a live preview), docs for the REST API and MCP server, privacy and terms.
- **Authentication** — Google and GitHub sign-in on the landing page via [Better Auth](https://www.better-auth.com/) (Apple too when its env vars are set). Every account gets a unique username, editable in Settings, where you can also connect or disconnect sign-in methods; avatars come from the OAuth provider's profile photo.
- **Save & organize**
  - Add items by URL with automatic content-type detection (web, PDF, YouTube, audio).
  - Links grouped by relative time (e.g. Today, This week, Last month).
  - Preview metadata (title, description, favicon, thumbnail where available).
- **Hardened outbound fetch** — Server-side `safeFetch` with optional proxy/DNS controls (see `AGENTS.md`). An egress proxy can be configured via [`SAFE_OUTBOUND_HTTP_PROXY`](docs/production-outbound-proxy.md).
- **Realtime list sync** — Supabase Realtime so saves and updates propagate across tabs/devices quickly.
- **Link actions** — Open original, copy URL, edit metadata, delete.
- **REST API & MCP** — `/api/v1` and an MCP server (`save_link`, `list_saved_items`, `get_link`) with API-key or OAuth auth.
- **Operational extras** — Optional Upstash-backed API rate limiting, Vitest coverage for critical paths.
- **PWA (installable app)** — [Web App Manifest](public/manifest.json) plus a [Serwist](https://serwist.pages.dev/) service worker ([`src/app/sw.ts`](src/app/sw.ts)) that builds to **`public/sw.js`** (generated on `pnpm build`, gitignored). Enables **Install** in Chrome/Edge and similar where the platform supports it, with runtime caching via Serwist's Next.js defaults and a static offline shell at [`/~offline`](src/app/~offline/page.tsx). **Serwist is disabled in `pnpm dev`** to avoid service-worker cache surprises during development — use **`pnpm build && pnpm start`** (or your production URL) to exercise installability and the SW.

## Save flow

Saving a link is fully **synchronous** — there is no background processing.

1. **Input** — `POST /api/links` with a URL (also `/api/v1/links` and the MCP `save_link` tool).
2. **Classify & decorate** — Server-side [`detectContentType`](src/lib/server-detect-content-type.ts) (SSRF-safe `HEAD` / sniff) plus [`scrapeLinkMetadata`](src/lib/links.ts) (Open Graph HTML, PDF `Content-Disposition` / size, YouTube oEmbed). Saving an existing URL again refreshes its metadata and moves it to the top.
3. **Persist** — A `Link` row with title, favicon, thumbnail, domain, and `contentType` (`WEB`, `PDF`, `YOUTUBE`, or `AUDIO`).
4. **Sync** — [`broadcastLinksChanged`](src/lib/realtime-broadcast.ts) notifies other tabs/devices via Supabase Realtime.

## Not implemented yet

These are called out explicitly because the repo is going public:

- **Settings breadth** — Settings cover username, sign-in methods, and account deletion; broader account preferences (notification settings, etc.) are not implemented yet.

**Marketing vs. product:** The landing page copy mentions ideas such as **collections** and a **weekly digest**. Those are **not** built in the current schema or app — treat them as roadmap, not shipped features.

## Tech stack

- **Web:** Next.js (App Router), React, TypeScript
- **UI:** Tailwind CSS, shadcn/ui
- **Auth:** Better Auth
- **Database:** PostgreSQL + Prisma
- **Email (optional in dev):** Resend for feedback emails
- **Realtime:** Supabase client (anon + service role on server)
- **PWA:** [Serwist](https://serwist.pages.dev/) (`@serwist/next`), web manifest + precache / offline fallback

## CI / GitHub Actions

Automation lives under [`.github/workflows/`](.github/workflows/). Every PR and manual release is gated by these pipelines.

### PR checks — [`pr-checks.yml`](.github/workflows/pr-checks.yml)

Runs on **`pull_request`** to **`develop`** and **`main`**: **Setup & validation** → **Prisma** (generate client + type fixes) → **Lint** and **type check** (in parallel) → **Tests** and **production build** (in parallel, after lint and type check pass). Concurrency is per-PR so new pushes cancel stale runs.

<p align="center">
  <img src="prCheckPipeline.png" alt="GitHub Actions graph for pr-checks.yml: Setup, Prisma, Lint & Type Check, Test & Build" width="920" />
</p>

### Release — [`release.yml`](.github/workflows/release.yml)

Runs on **`workflow_dispatch`** (manual): **Merge `develop` into `main`**, then **build validation** so production is only promoted after a green build.

<p align="center">
  <img src="releasePipeline.png" alt="GitHub Actions graph for release.yml: merge develop into main, then build validation" width="920" />
</p>

## Security

Purl is built around **untrusted input** (arbitrary URLs). A few layers matter in production:

- **SSRF-aware outbound fetches** — User-supplied URLs are not passed to raw `fetch`. OG/thumbnail probes, PDF fetch, content-type sniffing, and similar paths go through [`safeFetch`](src/lib/safe-outbound-fetch.ts): HTTP(S) only, blocked private/link-local/reserved targets, redirect handling with per-hop host checks, DNS resolution pinned before connect (mitigates classic DNS rebinding against the pre-check), optional response size caps (e.g. PDF proxy). Optional **egress proxy** and custom DNS servers are documented in [`AGENTS.md`](AGENTS.md).
- **Authentication & route gating** — [Better Auth](https://www.better-auth.com/) sessions; Next.js [`proxy`](src/proxy.ts) redirects unauthenticated users away from private routes. Sign-in is OAuth-only (Google/GitHub, plus Apple when configured); there are no passwords.
- **API authorization** — Sensitive routes (`/api/links`, `/api/v1/*`, MCP, etc.) resolve the session server-side and scope work to the signed-in user.
- **Rate limiting** — When `UPSTASH_REDIS_REST_URL` and `UPSTASH_REDIS_REST_TOKEN` are set, the proxy applies per-IP limits to **`/api/auth/*`**, **`POST /api/links`**, and **`POST /api/feedback`** (see [`proxy-rate-limit.ts`](src/lib/proxy-rate-limit.ts)). Without Upstash, limits are disabled — fine locally, not ideal for production.
- **Secrets & client exposure** — `SUPABASE_SERVICE_ROLE_KEY` and similar values are server-only. The browser uses the Supabase **anon** key for Realtime only; `.env` stays gitignored.
- **Response bounds** — PDF proxy streaming is size-capped (see `safe-outbound-fetch`).

**Reporting a vulnerability:** use [GitHub Security Advisories](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) for this repository so details stay private until patched.

## Setup (local development)

### Prerequisites

- **Node.js:** recent LTS
- **Package manager:** `pnpm` (this repo includes `pnpm-lock.yaml`)
- **Postgres:** local or hosted (Supabase works well)

### 1) Install dependencies

```bash
pnpm install
```

### 2) Configure environment variables

Create a `.env` file in the repo root. See `.env.example` for the full list; minimum for core behavior:

```bash
DATABASE_URL="postgresql://USER:PASSWORD@HOST:5432/DBNAME"

# Supabase Realtime — cross-device instant link list sync (same project as Postgres)
NEXT_PUBLIC_SUPABASE_URL="https://YOUR_PROJECT.supabase.co"
NEXT_PUBLIC_SUPABASE_ANON_KEY="eyJ..."
SUPABASE_SERVICE_ROLE_KEY="eyJ..."

# Sign-in (OAuth apps with callback {BETTER_AUTH_URL}/api/auth/callback/{google,github})
GOOGLE_CLIENT_ID="..."
GOOGLE_CLIENT_SECRET="..."
GITHUB_CLIENT_ID="..."
GITHUB_CLIENT_SECRET="..."

# Optional (used to send in-app feedback emails)
RESEND_API_KEY="re_..."
RESEND_FROM="Purl <onboarding@resend.dev>"
```

Notes:

- **`DATABASE_URL`** is required (Prisma + Better Auth).
- **Supabase** env vars are required for realtime link list sync. Use **Project Settings → API** in the Supabase dashboard. The service role key must stay server-only.
- **Google/GitHub** OAuth credentials are required in production; locally, a provider whose vars are missing is simply hidden. For local dev, use callback URLs `http://localhost:3000/api/auth/callback/{google,github}`. Apple is optional and enabled only when all four `APPLE_*` vars are set (see `.env.example`).
- **Resend** is optional for local dev: without `RESEND_API_KEY`, in-app feedback can't be sent.
- **Better Auth** secrets and URLs are in `.env.example` — copy those keys for a working auth setup.

### 3) Run database migrations

```bash
pnpm prisma migrate dev
```

### 4) Generate Prisma client (if needed)

```bash
pnpm prisma generate
```

### 5) Start the dev server

```bash
pnpm dev
```

Open `http://localhost:3000`.

**Optional: a named HTTPS URL with [portless](https://portless.sh/).** With portless installed globally (`npm install -g portless`, Node.js 24+), run `portless run` instead of `pnpm dev`. It runs the `dev` script behind a local proxy at `https://purl.localhost`. Point auth at that URL in `.env.local`, or sign-in rejects the origin:

```bash
BETTER_AUTH_URL="https://purl.localhost"
BASE_URL="https://purl.localhost"
```

In a git worktree, portless prefixes the branch name (for example `https://my-branch.purl.localhost`); set both variables to that URL there.

**PW

What people ask about purl

What is nublson/purl?

+

nublson/purl is mcp servers for the Claude AI ecosystem. AI read-it-later & personal knowledge base It has 2 GitHub stars and its last recorded update is dated 2026-10-01.

How do I install purl?

+

You can install purl by cloning the repository (https://github.com/nublson/purl) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.

Is nublson/purl safe to use?

+

Our security agent has analyzed nublson/purl and assigned a Trust Score of 62/100 (tier: OK). See the full breakdown of passed checks and flags on this page.

Who maintains nublson/purl?

+

nublson/purl is maintained by nublson. The last recorded GitHub activity is dated 2026-10-01, with 1 open issues.

Are there alternatives to purl?

+

Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.

Deploy purl to your cloud

Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.

Maintain this repo? Add a badge to your README

Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.

Featured on ClaudeWave: nublson/purl
[![Featured on ClaudeWave](https://claudewave.com/api/badge/nublson-purl)](https://claudewave.com/repo/nublson-purl)
<a href="https://claudewave.com/repo/nublson-purl"><img src="https://claudewave.com/api/badge/nublson-purl" alt="Featured on ClaudeWave: nublson/purl" width="320" height="64" /></a>

More MCP Servers

purl alternatives