- name
- linkly-ai
- description
- Search, browse, read, and take notes across the user's documents indexed by Linkly AI — local files and linked cloud libraries. Use when the user asks to 'search my documents', 'find files about a topic', 'read a local document', 'what's in this folder', 'list the files in that library', 'browse document outlines', 'list knowledge libraries', 'save this as a note', 'list my notes', or any task involving searching, listing, reading, or noting stored content (PDF, Markdown, Excel, CSV, DOCX, TXT, PPTX, DOC, RTF, EPUB, HTML, images, audio, video). Also triggered by: 'linkly not working', 'cloud library', '搜索我的文档', '查找文件', '这个文件夹里有什么', '列出文件', '知识库搜索', '云端知识库', '记笔记', '我的笔记', '连接不上', '故障排查'. Provides full-text search, container enumeration, structural outlines, paginated reading, and local note capture via CLI or MCP tools.
- license
- Apache-2.0
- version
- 0.8.2
# Linkly AI — Document Search (Local + Cloud)
Linkly AI indexes documents on the user's local machine (PDF, Markdown, Excel, CSV, DOCX, TXT, PPTX, DOC, RTF, EPUB, HTML, images, audio, video) and can also reach cloud libraries the user has linked via Linkly Web. It exposes them through a progressive disclosure workflow: **search → grep or outline → read**. It can also capture and list the user's local Markdown notes.
## Environment Detection
Before executing any document operation, detect what's available and pick a mode. CLI and MCP are **two independent access paths** — check both, don't treat MCP as a CLI fallback.
### 1. Check what's available
Run both checks independently (skip a check if its prerequisite isn't there):
- **CLI**: if Bash is available, run `linkly --version`. Success means only that the CLI is installed. Then run `linkly status` to confirm the desktop app is reachable. A successful status means CLI mode is usable; `desktop_unreachable` in JSON or `App: Unreachable` in human output is **inconclusive** — the Desktop may be stopped, or an AI-agent network sandbox may be blocking localhost. Run `linkly doctor`, but do not report that the Desktop is stopped from this transport failure alone (see `references/troubleshooting.md`).
- **MCP**: check whether MCP tools named `search`, `find_paths`, `list`, `outline`, `grep`, `read`, `list_libraries`, `explore`, and `note_save` are accessible in the current environment. The `linkly-ai` server (local Desktop MCP) exposes exactly those nine. The `linkly-ai-cloud` server (the `mcp.linkly.ai` cloud gateway) exposes the same nine **plus two cloud-only tools** — `library_search` and `library_link` — that discover, link and unlink cloud libraries (`library_link` with `action: "unlink"` releases one); they are never advertised on a local or LAN connection. Beyond those two, the difference is reach, not the tool list — see "Know what your connection reaches" below. `note_save` is the one tool whose reach never varies: it always resolves to the user's Desktop, whichever server it arrived from.
### 2. Pick a mode
| Available | Action |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **CLI reachable + MCP** | Prefer **CLI mode** — clearer error messages and exit codes are easier to surface back to the user. |
| **CLI reachable only** | Use **CLI mode**. |
| **CLI unreachable + MCP** | Use **MCP mode**. The CLI binary exists, but its localhost path may be blocked by the shell sandbox. Do not claim the Desktop is stopped and do not keep retrying the sandboxed CLI. |
| **CLI installed but unreachable, no MCP tools** | If the command is running in an AI-agent network sandbox, ask the user to approve retrying the **exact Linkly CLI command** outside that sandbox. Do not ask them to disable sandboxing globally or request broad approval for every `linkly` subcommand. Only if the approved retry also fails should you ask the user to launch Linkly AI Desktop, then retry. |
| **MCP only** | Use **MCP mode**. This is normal in restricted agent environments where the CLI binary is not installed inside the shell. Don't tell the user to install the CLI; MCP is sufficient. |
| **Neither installed** | If Bash works and `linkly --version` failed, recommend installing the CLI: [Install Linkly AI CLI](https://linkly.ai/docs/en/use-cli). Otherwise inform the user that Linkly AI requires either the CLI or the MCP integration and stop. |
### 3. Know what your connection reaches
**The connection mode decides which content is visible, and no tool call can cross that boundary.** This is the single most common source of "the document is there but Linkly can't find it".
| Connection | Reaches |
| ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Local** (`linkly` default) / **LAN** (`--endpoint`), or a `linkly-ai` MCP server on localhost | The user's **local** indexed content only. Cloud libraries are **not reachable** — `cloud://` references are rejected on this path. |
| **Cloud gateway** (`linkly --remote`, `linkly mcp --remote`, or the `linkly-ai-cloud` MCP server) | Both local content (through the desktop tunnel) and linked **cloud** libraries. |
| **Cloud gateway only** — the `library_search` / `library_link` tools | The cloud library **catalog** (including libraries not linked yet) and the link / unlink actions (`library_link`, `action: "unlink"` to release). Not advertised on Local / LAN — if the user needs them there, tell them to switch connection. |
If the user asks for cloud-library content while you are on a local or LAN connection, **tell them to switch connection** (`--remote`, or configure the cloud gateway connector). Do not retry, and do not attempt a `cloud://` reference from a local connection — it will fail every time.
**Notes sit outside this table.** They are local files with no cloud counterpart, so `note_save`, `list` with `scope="notes"` and `search` with `scope="notes"` always reach the Desktop no matter which connection you are on — over the tunnel when you are on the cloud gateway. Consequences: they need the Desktop online (over the tunnel, that also means Pro), and when it is unreachable there is **no cloud fallback to retry against** — do not "retry with a cloud library", there is nothing there to retry against. Never pass a `library` alongside `scope="notes"`, or to `note_save` at all — notes are a single local container and the call is rejected.
**`list` straddles the table**, by `scope`: `folder`, `notes`, and a `local://` library are Desktop-only and ride the same paths as everything else local; `scope="library"` with a `cloud://owner/slug` is served by the gateway itself — reachable on the Free plan, and still answerable while the Desktop is offline.
The CLI's three connection modes:
- **Local** (default): auto-discovers the desktop app via `~/.linkly/port`. Requires the app running locally.
- **LAN**: `--endpoint <url> --token <token>` reaches a Linkly AI instance on the local network.
- **Remote**: `--remote` connects through the `https://mcp.linkly.ai` gateway. Linked cloud libraries are served by the gateway and stay reachable even when the desktop is offline; local content additionally needs the desktop online and its tunnel connected. Requires `linkly auth set-key <api-key>` first. (Reaching **local** content over the tunnel is a Pro feature; linked **cloud** libraries are served on all plans.)
If you have no path to Linkly at all (neither CLI nor an MCP connection), tell the user instead of retrying.
### 4. Name yourself on every CLI call
Linkly AI Desktop keeps a local access log so the user can see which AI applications read their documents. **Pass `--client <your-app-name>` on every `linkly` command** — `--client claude-code`, `--client cursor`, `--client codex-cli`, whatever your host application is called. Use lowercase with hyphens.
```bash
linkly search "budget report" --client claude-code
```
Without it your calls appear in that log as an anonymous local entry, and the user cannot tell you apart from anything else running on their machine.
It is a label, not a credential: it grants nothing, is never checked against anything, and never changes what you can reach. The examples below leave it out to keep them about the flag they demonstrate — add it anyway.
On MCP there is nothing to pass: the connection itself carries the name.
See `references/mcp-tools-reference.md` for MCP parameter schemas and response formats.
## Document Search Workflow
**Two entry points, picked by what the user handed you:**
- **You know the content** ("anything about Q3 pricing?") → `search` (Step 1). Ranked and capped by `limit`, so it answers "what is relevant", not "what is there".
- **You know the container** ("what's in this folder / this library / my notes") → `list` (Step 0b). Complete, paginated enumeration — no query invented, nothing hidden behind relevance.
`find_paths` (Step 0) turns a fuzzy container name into a real address for either one. Both return real `doc_id`s, which is what `outline` / `grep` / `read` need.
### Step 0: Find Paths (when the user names a container by a fuzzy word)
When the user names a container by a fuzzy or cross-language word — folder, app, project, repo, or cloud drive (e.g. "in my WeChat", "in my Notion notes", "in the linkly-ai repo", "in my iCloud Drive") — and you don't yet know the on-disk path, run `find_paths` first. Pass several variants in a single call, then pipe a distinctive segment of any returned folder path into `linkly search` as `--path-glob` (or, to scope to a whole folder, copy that candidate's `path_glob` field verbatim — it is already glob-quoted, so a folder name with `* ? [` still matches literally). This also works inside a Linkly cloud library; candidates there carry a `cloud://owner/slug` reference to pass to the follow-up search's `library` (see `references/mcp-tools-reference.md`).
```bash
linkly find-paths --patterns WeChat,微信,wxid --limit 5
linkly search "购物订单" --path-glob "*xinWeChat*"
```
**Skip this step** for pure content queries ("find resumes"), file-type filters (use `search --type pdf` directly), or queries with no container intent.
**Zero-directory fallback:** if `find_paths` returns 0 directories, the patterns may have only matched filenames, not directory segments — fall back to `linkly search` directly (without `--path-glob`); the `filename` BM25 field will still pick those up.
For aggregation behaviour and the full when-to-use matrix, see `references/search-strategies.md` ("Locate the container first") and `references/mcp-tools-reference.md` (`find_paths`).
### Step 0b: List (enumerate a known container)
Once the container is known, `list` enumerates it. There is no query to invent and no ranking to hide items behind.
```bash
linkly list --scope folder --path /Users/me/Documents/reports
linkly list --scope folder --path /Users/me/notes --type md --modified-after 2026-01-01
linkly list --scope library --library my-research --limit 200 --no-snippet
linkly list --scope notes --tags project
```
- **`scope="folder"`** — an absolute disk path; omit `path` to sweep every watched root. The path is an **address, not a glob**: no `*`, no fuzzy names. If you only have a fuzzy name, run Step 0 first and pass the path it returns.
- **`scope="library"`** — one library: `local://<id>`, a plain local name, or `cloud://owner/slug` (discover it with `list_libraries`). A cloud library takes a **relative** `path` prefix, never an absolute one.
- **`scope="notes"`** — the user's own notes; see ["Notes"](#notes-local-markdown-cards).
**`sort` is the truncation policy, not decoration.** With `has_more: true` you are holding the newest slice (`recent`, the default), the oldest (`oldest`), or the A→Z head (`name`) — never a random one. Say which slice you looked at, and page with `offset` when completeness matters.
**Read the `readme` pointer only when the folder's purpose matters** — "what is this folder for?", or before acting on files you don't recognize. For plain enumeration or locating one file, skip it; it costs an extra `read`.
**`total: 0` is not automatically "there's nothing there".** A local `list` distinguishes a path that doesn't exist, a path outside the watched roots, and a directory that is genuinely empty of indexed files — the response says which, so relay that instead of reporting an empty folder. Cloud libraries cannot tell "prefix doesn't exist" from "prefix is empty" and say so in the response.
Full parameter matrix and response shapes: `references/mcp-tools-reference.md` (`list`).
### Step 1: Search
Auf GitHub ansehen