linkwarden-mcp
by o-gent
README.md
# linkwarden-mcp
An [MCP](https://modelcontextprotocol.io) server that wraps the
[Linkwarden](https://linkwarden.app) bookmark API in a small set of token-frugal
tools. Tool responses are compact plain text (`#id name :: url`) so listing
collections or links costs as few tokens as possible.
## Tools
| Tool | What it does |
| --- | --- |
| `list_collections` | List collections as `#id name (count)`. |
| `list_links` | List links as `#id name :: url`; optional `collection_id`, `limit`. |
| `find_link` | Check if a URL already exists (tracking params stripped first). |
| `add_link` | Add a link: strips tracking params, skips duplicates, then moves it into a collection. |
| `move_link` | Move an existing link to another collection. |
| `delete_link` | Delete a link by id. |
| `create_collection` | Create a new collection. |
### Behaviour baked in from experience
- **Tracking params are stripped** on add (`utm_*`, `fbclid`, `gclid`, `gbraid`,
`gad_source`, `gad_campaignid`, `rcm`, …); meaningful params like `variant` and
`model` are kept.
- **Two-step create.** Linkwarden ignores `collectionId` on link creation, so
`add_link` POSTs the link then PUTs it into the target collection.
- **Correct PUT shape.** Updates always send `id`, `url`, `collection.{id,ownerId}`
and `tags`, which the API requires.
- **Full-list dedup.** Linkwarden caps page size at ~50, so dedup walks every page
via cursor pagination rather than trusting a single `limit`.
## Configuration
Configuration comes from environment variables — no secret is ever hardcoded:
| Variable | Required | Default | Notes |
| --- | --- | --- | --- |
| `LINKWARDEN_URL` | no | `http://links.lan` | Base URL, no `/api/v1`. |
| `LINKWARDEN_TOKEN` | **yes** | — | API token (Settings → Access Tokens). Secret. |
| `LINKWARDEN_OWNER_ID` | no | `1` | Owner id used when moving links. |
**How the token is stored:** in production it is supplied by the MCP host via the
server config's `env` block (see below) — it is not read from disk. For local
development you may instead copy `.env.example` to `.env` and fill in the token;
`.env` is git-ignored and loaded automatically. Host-provided env vars take
precedence over `.env`.
## Run
```bash
uv sync
uv run linkwarden-mcp # serves over stdio
```
## Register with an MCP host
Claude Code:
```bash
claude mcp add linkwarden -e LINKWARDEN_URL=http://links.lan -e LINKWARDEN_TOKEN=your-token -- uv --directory C:/Users/olive/Github/linkwarden-mcp run linkwarden-mcp
```
Or Claude Desktop (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"linkwarden": {
"command": "uv",
"args": ["--directory", "C:/Users/olive/Github/linkwarden-mcp", "run", "linkwarden-mcp"],
"env": {
"LINKWARDEN_URL": "http://links.lan",
"LINKWARDEN_TOKEN": "your-token"
}
}
}
}
```
## Development
```bash
uv run ruff check . # lint
uv run ruff format . # format
uv run ty check # type check
```
TDQS
A3.7/5.0
Scored across 7 tools
Disambiguation5/5
Each tool has a clear, distinct purpose: add, delete, find, move links; create and list collections; list links. There is no overlap or ambiguity.
Naming Consistency5/5
All tool names follow a consistent verb_noun pattern in snake_case (e.g., add_link, list_collections), making it easy for an agent to infer functionality.
Tool Count5/5
With 7 tools, the set is well-scoped. It covers core operations for a link management service without being too sparse or bloated.
Completeness4/5
The set covers primary CRUD operations for links (add, delete, move, find) and collection management (create, list). Missing update operations for links/collections and deletion of collections are minor gaps.
Maintenance
ActivityStale
ResponsivenessNo issues