Skip to main content
Glama
dgholamian

Bookmarks MCP Server

by dgholamian
README.md
# Bookmarks MCP Server

A Postgres-backed bookmark manager, plus a generic REST API tool, exposed as
an [MCP](https://modelcontextprotocol.io) server so an LLM client (e.g.
Claude Desktop) can save, search, and manage bookmarks on your behalf.

## What it does

- Stores bookmarks (url, title, description, tags) in Postgres via `asyncpg`
- Exposes CRUD tools: `add_bookmark`, `list_bookmarks`, `get_bookmark`,
  `search_bookmarks`, `update_bookmark`, `delete_bookmark`
- Exposes DB inspection tools: `list_tables`, `describe_table`, `run_query`
  (SELECT-only, for safety)
- Exposes one generic REST tool, `api_request`, that calls whatever API you
  configure via `API_BASE_URL` in `.env` (any method, path, query params,
  JSON body) — by default this is set up to use
  [Microlink](https://microlink.io) (free, no API key) for fetching link
  previews (title/description/image) of a URL before saving it

## Setup

### 1. Database

Create a dedicated role and database (don't use the Postgres superuser for
the app itself):

```sql
CREATE ROLE bookmarks_app WITH LOGIN PASSWORD 'choose-a-password';
CREATE DATABASE bookmarks OWNER bookmarks_app;
```

Then create the table:

```
psql -U bookmarks_app -h localhost -d bookmarks -f schema.sql
```

### 2. Python environment

```
python -m venv venv
venv\Scripts\activate        # Windows
# source venv/bin/activate   # macOS/Linux
pip install -r requirements.txt
```

### 3. Configure `.env`

```
copy .env.example .env        # Windows
# cp .env.example .env        # macOS/Linux
```

Fill in:
- `DATABASE_URL` — the `bookmarks_app` connection string
- `API_BASE_URL` / `API_KEY` — the REST API you want `api_request` to call
  (defaults to Microlink, which needs no key; leave `API_KEY` blank for any
  API that doesn't require auth)

### 4. Run it

```
python src/server.py
```

## Connect to Claude Desktop

Add an entry to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "bookmarks": {
      "command": "/absolute/path/to/bookmarks-server/venv/Scripts/python.exe",
      "args": ["/absolute/path/to/bookmarks-server/src/server.py"]
    }
  }
}
```

Env vars are read from `.env` (via `python-dotenv`, resolved relative to the
project root regardless of the process's working directory), so nothing
sensitive needs to go in the Claude config itself.

**Windows Store (MSIX) installs of Claude Desktop:** the config file isn't
at the usual `%APPDATA%\Claude\` path — it's sandboxed under
`%LOCALAPPDATA%\Packages\<Claude package id>\LocalCache\Roaming\Claude\claude_desktop_config.json`.
Fully quit Claude Desktop from the system tray (not just closing the window)
before editing it, then relaunch.

## Security notes

- All queries use parameterized SQL (`$1`, `$2`, ...) — no string-built SQL,
  so no injection risk from tool arguments.
- `run_query` only allows `SELECT` statements. Writes go through the
  dedicated CRUD tools instead, which is a deliberate narrower surface for
  an LLM-driven client.
- `bookmarks_app` is a non-superuser role scoped to the `bookmarks`
  database — the server never connects as the Postgres superuser.
- `.env` is gitignored. Never commit real credentials — use `.env.example`
  as the template.