Skip to main content
Glama
quillink-mcp

quillink-mcp

quillink-mcp

An MCP server exposing read-only access to your Quillink notes, tasks, canvases, folders, and tags to AI tools like Claude Desktop, Claude Code, and other MCP-compatible clients.

Requires a Pro Quillink plan (API Access is a Pro-only feature).

Not published to PyPI -- build and run it locally from source.

Build

git clone https://github.com/dagistankaradeniz/gcp-note-taking-mcp.git
cd gcp-note-taking-mcp
python3 -m venv .venv
.venv/bin/pip install -e .

This installs the quillink-mcp command into .venv/bin/. Note the full path to that binary (e.g. /Users/you/gcp-note-taking-mcp/.venv/bin/quillink-mcp) -- MCP clients launch it directly, not through an activated shell, so they need the absolute path.

macOS: if you clone the repo under ~/Documents, ~/Desktop, or ~/Downloads, Claude Desktop may fail to launch the server with "Server disconnected" -- macOS privacy protection (TCC) blocks its child processes from reading those folders. Either grant Claude Desktop access to the folder (System Settings -> Privacy & Security -> Files and Folders), or build the venv outside the protected folders:

python3 -m venv ~/.local/share/quillink-mcp-venv
~/.local/share/quillink-mcp-venv/bin/pip install /path/to/gcp-note-taking-mcp
# then point your MCP client at ~/.local/share/quillink-mcp-venv/bin/quillink-mcp

Related MCP server: notes-mcp

Authenticate

Two options:

Option A — OAuth device login (recommended for interactive use):

.venv/bin/quillink-mcp login

Opens the sign-in page in your browser with the code filled in; approve it there. Over SSH, or with --no-browser / QUILLINK_NO_BROWSER=1, it only prints the link and code. The token is stored in your OS keyring (or ~/.config/quillink-mcp/credential as a fallback).

Requires a registered OAuth client id for this tool -- if login fails with an "Unknown client_id" error, set QUILLINK_CLIENT_ID to a registered client id (see Configuration below), or use a PAT instead.

Option B — Personal Access Token:

Create one in Quillink under Settings → Developer → Tokens (scopes: notes:read, tasks:read, canvases:read, folders:read, tags:read, organizations:read), then set it as an environment variable:

export QUILLINK_TOKEN=qlk_pat_...

QUILLINK_TOKEN always takes priority over a stored OAuth login.

Use with Claude Desktop

Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "quillink": {
      "command": "/absolute/path/to/gcp-note-taking-mcp/.venv/bin/quillink-mcp"
    }
  }
}

If using a PAT instead of login, pass it as an env var in the config:

{
  "mcpServers": {
    "quillink": {
      "command": "/absolute/path/to/gcp-note-taking-mcp/.venv/bin/quillink-mcp",
      "env": { "QUILLINK_TOKEN": "qlk_pat_..." }
    }
  }
}

Restart Claude Desktop after editing the config.

Use with other MCP clients

Any client that can launch a local stdio MCP server works the same way: point it at the absolute path to .venv/bin/quillink-mcp (with QUILLINK_TOKEN set, or after running quillink-mcp login once so the credential is already stored). This includes Claude Code (claude mcp add quillink -- /absolute/path/to/.venv/bin/quillink-mcp) and any OpenAI-compatible agent runtime that supports the MCP stdio transport.

Tools

All read-only — this server cannot create, edit, or delete anything.

Tool

Description

list_notes

List notes, filterable by folder, status, tags (any, or all with tag_match), pinned

get_note

Get a single note by id

search_notes

Full-text search over title + body, optionally within tags

get_note_stats

Note count, storage used, pinned count

get_note_backlinks

Notes that link to a given note

get_related_notes

Tag/link-similarity "you might want to link these" suggestions for a note (Pro plan)

get_note_graph

Local link graph (1 or 2 hops) centered on a note

get_global_note_graph

Whole-account link graph, paginated (Pro plan)

get_graph_analysis

Community detection + centrality ranking over the whole-account graph (Pro plan)

export_note

A single note as a standalone Markdown file

export_notes

Bulk export of active notes as NDJSON or concatenated Markdown, for backups/scripting

list_attachments

List a note's file attachments

get_attachment_download_url

A short-lived signed URL to download one attachment's file bytes

list_note_recipients

Who a note has been shared with

list_shared_notes

Notes shared with you by others

list_tasks

List tasks, by status, due-date window, or shared with you

get_task

Get a single task by id

list_canvases

List canvases (without their elements), filterable like notes

get_canvas

Get a single canvas with its elements as JSON

list_folders

List folders

get_folder

Get a single folder by id

list_tags

List all tags in use

list_tag_counts

Every tag with how many notes and canvases carry it

get_organization

Your Team-plan organization's name, plan tier, seats, alerts

list_organization_members

Your organization's members (plain members see only their own entry)

Vault notes are never accessible here — they're end-to-end encrypted client-side, so no server (including this one) can read them. Zero-Knowledge accounts can't use this server: their content is encrypted on their own devices, so every content tool answers "not available for Zero-Knowledge accounts" (use the app or the quillink CLI). Organization tools 404 for a caller who isn't on a Team-plan organization.

Security notes

  • Read-only. The server only requests read scopes and has no tools that create, change or delete anything.

  • Note content is untrusted input. A note -- especially one shared with you or imported -- can contain text written to steer an AI assistant ("prompt injection"), e.g. asking it to send your notes somewhere through another tool. The server tells clients to treat note content as data, not instructions, but that's a request to the model, not a guarantee. Keep tool approval on in your MCP client when other tools (email, web, file write) are enabled alongside this one.

  • HTTPS only. QUILLINK_API_BASE must be https:// (plain http:// is allowed for localhost only).

  • Token storage. The OS keychain, or a 0600 file under ~/.config/quillink-mcp/ when no keychain is available.

Configuration (environment variables)

Variable

Purpose

QUILLINK_TOKEN

A Personal Access Token — bypasses OAuth login entirely

QUILLINK_API_BASE

Override the API base URL (default: production)

QUILLINK_CLIENT_ID

Override the OAuth client id used by login

QUILLINK_WORKSPACE

Use a named workspace's api-base/client-id/credential instead (see below)

Workspaces (multiple environments/accounts)

This server is a single long-running process, launched once per MCP client config entry — there's no per-call "switch workspace" the way the CLI has --workspace. To use several environments or accounts, register one server entry per workspace, each pinned to a different QUILLINK_WORKSPACE:

# One-time: define the workspaces with the CLI (gcp-note-taking-cli)
quillink workspace add staging --api-base https://staging.example.com
quillink workspace login staging

# Point this server at it
QUILLINK_WORKSPACE=staging .venv/bin/quillink-mcp login

Then in your MCP client config, add a second server entry (e.g. quillink-staging) alongside your existing one, with "env": {"QUILLINK_WORKSPACE": "staging"} — both can be active at once.

Workspace metadata (api_base/client_id, no secrets) lives in ~/.config/quillink/workspaces.json — the same file gcp-note-taking-cli's quillink workspace add/use manages, so a workspace defined once via the CLI is immediately visible here. This server keeps its own separate keyring entry per workspace for the actual token, though, so you still need to login (or set QUILLINK_TOKEN) once per workspace here too. QUILLINK_TOKEN/QUILLINK_API_BASE/QUILLINK_CLIENT_ID set directly always take precedence over QUILLINK_WORKSPACE.

Running it directly (for testing)

.venv/bin/quillink-mcp login
.venv/bin/quillink-mcp   # runs the server on stdio -- an MCP client normally launches this for you

Available Tools

21 tools
export_noteB

A single note rendered as a standalone Markdown file (title as an H1, followed by the body). A locked note's body can't be exported without its password -- returns just the title with a note explaining why. Fails with 403/404 same as get_note.

Inputs: note_id (required). Returns: result

export_notesA

Bulk export of the caller's active notes -- for backups/scripting, not paged browsing (use list_notes for that). format="ndjson" (default) returns one full note object per line; format="markdown" returns one "# Title" block per note, concatenated into a single document. Locked notes are included with their body withheld, same as list_notes.

Inputs: tags, limit, format, folder_id, +1 more. Returns: result

get_attachment_download_urlA

A short-lived signed URL to download one attachment's file bytes directly (not through this tool) -- expires_in seconds from now. A ZK/ Vault/lock-encrypted note's attachment is still opaque ciphertext at that URL; there's no decrypt path here, same as a locked note's body.

Inputs: note_id (required), attachment_id (required). Returns: object

get_folderA

Get a single folder by id.

Inputs: folder_id (required). Returns: object

get_global_note_graphA

Whole-account link graph, paginated: nodes and edges across every note that has at least one link (in or out) or is locked. Pass the response's next_cursor back in to fetch the next page; None means the graph is exhausted. Requires a Pro plan, same gate as the web app's Global Graph feature.

Inputs: limit, cursor. Returns: object

get_graph_analysisA

Community detection + importance ranking over the caller's whole account graph. community_algo: "louvain" (default), "label_propagation", "greedy_modularity", or "connected_components". centrality_algo: "pagerank" (default), "betweenness", "degree", or "closeness". Requires a Pro plan, same gate as get_global_note_graph. On a very large graph the response may note that a cheaper approximation was substituted for the exact algorithm requested.

Inputs: community_algo, centrality_algo. Returns: object

get_noteA

Get a single note by id. Fails with 403 if it isn't owned by the caller, 404 if it doesn't exist.

Inputs: note_id (required). Returns: object

get_note_graphA

Local link graph centered on note_id: nodes (id/title/locked) and edges (source -> target) for its direct links and backlinks. hops=1 is direct neighbors only; hops=2 also expands each neighbor's own neighbors one level further.

Inputs: note_id (required), hops. Returns: object

get_note_statsA

Aggregate stats: total active notes, total storage bytes used, pinned-note count, and a created-at histogram by day.

Inputs: no named parameters. Returns: object

get_organizationA

Get the caller's Team-plan organization: name, plan tier, seat allocations, member count, and any active alerts. 404s if the caller doesn't belong to a Team-plan organization.

Inputs: no named parameters. Returns: object

list_attachmentsA

List a note's file attachments. Fails with 403 if note_id isn't owned by the caller, 404 if it doesn't exist.

Inputs: note_id (required). Returns: object

list_foldersC

List the caller's notebooks/folders.

Inputs: include_trashed. Returns: object

list_note_recipientsA

List who a note (owned by the caller) has been shared with.

Inputs: note_id (required). Returns: object

list_notesA

List the caller's notes. folder_id="" lists notes with no folder; omit it to list across all folders. status is "active" or "trashed". tags filters to notes with any of them, or every one of them with tag_match="all". Locked notes are included but their body is withheld (null).

Inputs: tags, limit, pinned, status, +3 more. Returns: object

list_organization_membersA

List the caller's organization's members. A plain member sees only their own entry -- other members' emails/names/roles are admin/owner- only information, scoped the same way the web app scopes it.

Inputs: no named parameters. Returns: result

list_shared_notesA

List notes that have been shared with the caller by someone else (received copies), most recently shared first.

Inputs: no named parameters. Returns: object

list_tag_countsA

Every tag with how many active notes and canvases carry it, most-used first.

Inputs: no named parameters. Returns: result

list_tagsA

List every tag used across the caller's active notes.

Inputs: no named parameters. Returns: result

search_notesA

Full-text search over the caller's active notes' titles and body text, optionally only within notes carrying any of tags (every one with tag_match="all"). Locked notes only match on title (their body is never substring-matched, so a search hit can't leak hidden content).

Inputs: q (required), tags, limit, tag_match. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.5/5.0

    Scored across 21 tools

    Disambiguation4/5

    Most tools are clearly scoped by resource and operation, but the graph family (get_note_backlinks, get_related_notes, get_note_graph, get_global_note_graph, get_graph_analysis) and the tag pair (list_tags vs list_tag_counts) have overlapping boundaries that could cause misselection without careful reading. The descriptions mostly disambiguate them, so only minor confusion remains.

    Naming Consistency5/5

    All names use snake_case with a consistent verb_noun shape (get_*, list_*, search_*, export_*). No mixed conventions or vague verbs; prefixes consistently signal the operation type.

    Tool Count3/5

    21 tools is on the heavy side for a note-app integration, especially with separate local/global graph tools and overlapping tag-listing variants. The count is not extreme, but consolidation would improve scoping.

    Completeness3/5

    Read coverage is broad across notes, folders, tags, sharing, graphs, export, attachments, and organization data. However, there are no create, update, delete, move, or share operations, leaving common note lifecycle workflows as dead ends.

    Maintenance

    ActivityActive
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      B
      maintenance
      MCP server that provides standardized tools for querying and retrieving notes from Joplin personal knowledge manager through its API, enabling AI assistants to access and reference personal notes contextually.
      9
      -
    • A
      license
      A
      quality
      D
      maintenance
      MCP server for a personal notes/knowledge base that enables AI assistants to create, search, and retrieve notes using natural language. It exposes tools like create_note and search_notes, resources for each note, and a summarize_tag prompt.
      6
      MIT
    • F
      license
      Not graded
      quality
      D
      maintenance
      MCP server for reading and searching Markdown notes in any directory, such as an Obsidian vault. It integrates with MCP clients like Claude Desktop to access note contents.
      914 npm
      -
    • A
      license
      Not graded
      quality
      C
      maintenance
      Remote MCP server for Obsidian vaults, enabling secure access to markdown notes from Claude clients without the Obsidian desktop app. Provides tools for searching, reading, listing recent notes, resolving daily notes, and creating/appending notes via HTTPS.
      7 npm
      MIT