chrome-extension.md
docs/integrations/chrome-extension.md
Web Clipper (Chrome extension)
<!-- translations:start -->
한국어 · 中文 · 日本語 · Русский · Español · Français · Deutsch
<!-- translations:end -->
Clip any web page — or just the text you selected — straight into your Tesserae knowledge base. The clipper POSTs the page to a local tesserae serve instance, which writes a provenance-stamped markdown file into the project corpus and runs an incremental compile so the clip shows up as typed nodes in your graph, vault, and site.
This is the "autonomous, proactive knowledge ingestion" pillar made one-click: see something worth keeping, clip it, and it becomes agent-ready context.
What it does
- You browse to a page and hit the clipper (toolbar button or keyboard shortcut).
- The extension grabs the page
url,title, page metadata, and either the full readable content or, if you have text highlighted, just your selection. You can add an optional note and tags, and toggle TL;DR generation. - It POSTs that payload to
http://localhost:<port>/api/clipon your runningtesserae serve. - The server resolves the project being served, writes
data/ingested/<slug>.md, optionally prepends a one-call LLM TL;DR, and calls the same ingestion path the CLI uses (ingest_sources), which incrementally compiles the new source into the graph. - You get back a JSON report (
status,path,tldr,node_count,edge_count).
The clipped markdown looks like:
---
clipped_at: 2026-06-13T00:00:00Z
note: read later
source: web-clip
tags: python, web
title: An Article
url: https://example.com/article
---
## TL;DR
A two-sentence summary (only present when TL;DR is enabled and succeeds).
## Note
read later
## Content
The clipped page text (or your selection).
The TL;DR is best-effort: it uses the CLI-backed Claude layer (no API key needed). If the claude CLI is unavailable or the call fails, the clip is still ingested — just without the ## TL;DR section.
Install (load unpacked)
The extension ships in the repo under
extension/(load-unpacked during development; a Chrome Web Store listing is in review).
- Open
chrome://extensions. - Toggle Developer mode (top-right) on.
- Click Load unpacked and select the
extension/directory. - Pin the Tesserae clipper to your toolbar.
The extension talks to http://localhost:8765 by default; set the port in the extension options to match the port you pass to tesserae serve.
Run the server
Compile your project, then serve it:
python3 -m tesserae serve --project /path/to/project --port 8765
tesserae serve exposes the static site plus two JSON routes on the same origin:
POST /api/ask— question answering (see mcp.md)POST /api/clip— web-clip ingestion (this feature)
Leave it running while you browse; each clip hits /api/clip.
The /api/clip contract
POST /api/clip with a JSON body:
| Field | Type | Required | Notes |
|---|---|---|---|
url | string | yes | Source page URL (provenance + filename slug). |
title | string | no | Page title; falls back to a derived title. |
content | string | yes\* | Full page text. |
selection | string | no | If present, overrides content — clips just the highlighted text. |
meta | object | no | Extra page metadata passed through. |
note | string | no | Your free-text annotation → ## Note. |
tags | string[] | no | Front-matter tags. |
tldr | boolean | no | Default true. Set false to skip TL;DR generation. |
project | string | no | Target project alias. Only consulted by a fleet server (see below); ignored by single-project serve. |
\* Either content or selection must be non-empty.
Which project a clip lands in
A clip comes from an external web page, so the server cannot infer the target project from the page itself. Resolution depends on how the server was started:
- Single-project (
tesserae serve --project /path): every clip goes to that one project.projectin the body is ignored. - Fleet (
tesserae servewith no--project, serving every registered project): the server picks the project in this order — - a
projectalias in the body that matches a registered alias; - otherwise, if exactly one project is registered, that one;
- otherwise
400 {"error": "specify 'project'", "available": [<aliases>]}.
With 2+ registered projects, an unknown/garbage project (or none) gives the 400 above — it never loads the wrong project. With exactly one registered project, any project value (including an unknown alias) routes to that sole project. Set the Target project field in the extension's options to the alias you want.
Response 200 OK:
{
"status": "ok",
"path": "/path/to/project/data/ingested/example-com-article.md",
"tldr": "A two-sentence summary…",
"node_count": 142,
"edge_count": 287
}
Errors return 400 (bad request / empty body) or 500 (ingestion failure) with {"error": "..."}.
CORS
Because the clipper is a browser extension hitting localhost, the endpoint speaks CORS — but only for trusted callers, so an arbitrary website you visit cannot POST into your graph:
OPTIONS /api/clipreturns the preflight headers.- The server validates the request
Originand reflects only browser-extension (chrome-extension://…) and loopback (http://localhost,http://127.0.0.1) origins. A foreign website origin is rejected with403and never reaches the ingest path. - Allowed responses send
Access-Control-Allow-Origin: <that origin>,Access-Control-Allow-Methods: POST, OPTIONS, andAccess-Control-Allow-Headers: Content-Type. - Chrome's Private Network Access preflight is honored: when the request carries
Access-Control-Request-Private-Network: true, the server repliesAccess-Control-Allow-Private-Network: trueso a Web-Store extension can reachlocalhost. - The request body is capped (5 MB) before it is read.
The MCP ingest tool
The same ingestion path is exposed to agents through the Tesserae MCP server as the ingest tool, so an agent can clip content it found without a browser:
| Input | Required | Notes |
|---|---|---|
content | yes | The text to ingest. |
url | no | Source URL (provenance + slug). |
title | no | Document title. |
note | no | Annotation → ## Note. |
tags | no | Front-matter tags. |
tldr | no | Default true. |
It ingests into the project the server resolves from its working directory (or pass project to target a registered alias) and returns the same {status, path, tldr, node_count, edge_count} report. See mcp.md for MCP setup.
TL;DR toggle
TL;DR is on by default. Turn it off per-clip in the extension popup (or send "tldr": false) when you want a fast, deterministic clip with no LLM call — e.g. clipping into an air-gapped project or when claude isn't on PATH. With it on, a failed/missing summarizer never blocks the clip; you simply get no ## TL;DR section.
Keyboard shortcut
The clipper registers a command you can bind under chrome://extensions/shortcuts. The default is:
- Clip current page / selection:
Ctrl+Shift+S(macOS:Cmd+Shift+S)
Rebind it there if it collides with another extension.