> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tiro.ooo/llms.txt
> Use this file to discover all available pages before exploring further.

# Folder Search

> Search private and team folders by name with pagination

## Overview

Folder search tools (`search_private_folders` and `search_team_folders`) provide a unified way to find folders by name within a workspace's private or team-shared folder hierarchy. Both tools use **case-insensitive substring matching** with NFC normalization, ensuring reliable matching for Korean and other CJK text.

* **`search_private_folders`** — Search your private (non-shared) folders.
* **`search_team_folders`** — Search team (workspace-shared) folders.

Both tools share the same signature and behavior; the only difference is whether they search private or team-shared folders. Both require the `mcp:folders:read` scope and operate on your API key's workspace by default — pass an explicit `workspaceGuid` (from `list_workspaces`) to target another workspace.

**Primary Use Cases:**

* Find a specific folder by name when you only remember part of it (e.g., "general" matches "0.General", "General", "general-things").
* Enumerate all folders in a workspace (empty keyword returns all, paginated).
* Korean character search: `keyword="네이버"` matches `"네이버 모니터링"`, `"0.네이버"`, etc.

**Key Features:**

* Case-insensitive substring matching (NFC-normalized for CJK reliability).
* Folders sorted by match position (start-of-name ranks higher) then by name length (shorter ranks higher).
* Each result includes the folder's breadcrumb path (e.g., `"Engineering > Q1 Planning > Sprint 1"`).
* Empty keyword returns all folders, paginated.
* Backward-compatible dual response fields: `content` (recommended) and `folders` (legacy alias).

<Note>
  **v1 implementation note:** These tools fetch the upstream folder tree once per call and filter server-side. A native backend folder-search endpoint is on the v2 roadmap. When added, the MCP signature stays identical — this is an implementation detail.
</Note>

## Parameters

Both `search_private_folders` and `search_team_folders` accept identical parameters:

| Parameter       | Type   | Required | Description                                                                                               |
| --------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------- |
| `keyword`       | string | Optional | Folder-name substring to search (case-insensitive, NFC-normalized). Omit to enumerate all folders.        |
| `cursor`        | string | Optional | Pagination cursor from a previous `nextCursor`. Pass this to fetch the next page.                         |
| `size`          | number | Optional | Results per page (default `30`, max `100`).                                                               |
| `workspaceGuid` | string | Optional | Workspace GUID (from `list_workspaces`). Omit to use the API key's workspace; pass one to target another. |

### keyword (optional)

Case-insensitive substring match. The search is normalized using NFC (Canonical Decomposition, followed by Canonical Composition) to handle Korean, Japanese, Chinese, and other CJK text reliably.

Empty or omitted keyword returns all folders, paginated.

**Examples:**

* `"general"` matches `"0.General"`, `"General"`, `"general-things"` (sorted by match position, then name length).
* `"네이버"` matches `"네이버 모니터링"`, `"0.네이버"`, `"팀별 > 네이버"`.

### cursor (optional)

Pass the `nextCursor` from a previous response to fetch the next page. Omit for the first page.

### size (optional)

Results per page. Default `30`, max `100`. Smaller pages return faster; larger pages reduce round-trips.

## Response Format

### Success Response (search\_private\_folders)

```json theme={"system"}
{
  "content": [
    {
      "folderId": 12345,
      "name": "General",
      "path": "General"
    },
    {
      "folderId": 12346,
      "name": "Q1 Planning",
      "path": "Engineering > Q1 Planning"
    }
  ],
  "folders": [
    {
      "folderId": 12345,
      "name": "General",
      "path": "General"
    },
    {
      "folderId": 12346,
      "name": "Q1 Planning",
      "path": "Engineering > Q1 Planning"
    }
  ],
  "total": 47,
  "totalSize": 2,
  "nextCursor": "eyJvZmZzZXQiOjMwfQ=="
}
```

**Field Descriptions:**

| Field                | Type           | Description                                                                                                                       |
| -------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `content[]`          | array          | Search results (recommended field).                                                                                               |
| `content[].folderId` | number         | Stable folder identifier.                                                                                                         |
| `content[].name`     | string         | Folder name (e.g., `"General"`, `"Q1 Planning"`).                                                                                 |
| `content[].path`     | string         | Breadcrumb path (e.g., `"Engineering > Q1 Planning > Sprint 1"`), joined with `>`. Top-level folders have `path` equal to `name`. |
| `folders[]`          | array          | Legacy alias for `content`. Both arrays contain identical data. Pick whichever your client prefers.                               |
| `total`              | number         | Total number of folders matching the keyword (entire result set, across all pages).                                               |
| `totalSize`          | number         | Number of results on this page.                                                                                                   |
| `nextCursor`         | string \| null | Cursor for the next page. `null` when no more results.                                                                            |

<Note>
  The response includes both `content` (recommended, modern naming) and `folders` (legacy alias) for backward compatibility. They reference the same array object — pick whichever your code prefers and ignore the other.
</Note>

## Usage Examples

### Example 1: search\_private\_folders — Simple keyword

**Request:**

```json theme={"system"}
{
  "keyword": "general"
}
```

Returns all private folders matching "general" (case-insensitive), paginated.

**Response:**

```json theme={"system"}
{
  "content": [
    {
      "folderId": 100,
      "name": "General",
      "path": "General"
    },
    {
      "folderId": 101,
      "name": "0.General Announcements",
      "path": "General Folder > 0.General Announcements"
    }
  ],
  "total": 2,
  "totalSize": 2,
  "nextCursor": null
}
```

### Example 2: search\_team\_folders — Korean keyword

**Request:**

```json theme={"system"}
{
  "keyword": "네이버"
}
```

Returns all team folders with "네이버" in the name (NFC-normalized for CJK matching).

**Response:**

```json theme={"system"}
{
  "content": [
    {
      "folderId": 200,
      "name": "네이버",
      "path": "파트너십 > 네이버"
    },
    {
      "folderId": 201,
      "name": "네이버 API 모니터링",
      "path": "기술 > 파트너 > 네이버 API 모니터링"
    }
  ],
  "total": 2,
  "totalSize": 2,
  "nextCursor": null
}
```

### Example 3: Enumerate all folders (empty keyword)

**Request:**

```json theme={"system"}
{
  "keyword": "",
  "size": 50
}
```

Returns the first 50 folders (all top-level and nested) in no particular order.

**Response:**

```json theme={"system"}
{
  "content": [
    { "folderId": 1, "name": "Engineering", "path": "Engineering" },
    { "folderId": 2, "name": "Q1 Planning", "path": "Engineering > Q1 Planning" },
    { "folderId": 3, "name": "General", "path": "General" }
  ],
  "total": 137,
  "totalSize": 3,
  "nextCursor": "eyJvZmZzZXQ6NTB9"
}
```

### Example 4: Pagination

**First page (omit cursor):**

```json theme={"system"}
{
  "keyword": "sprint",
  "size": 20
}
```

**Second page (using nextCursor from first response):**

```json theme={"system"}
{
  "keyword": "sprint",
  "size": 20,
  "cursor": "eyJvZmZzZXQ6MjB9"
}
```

## Sort Order

Results are sorted by match position, then by folder name length (shorter names rank higher at the same match position):

1. **Match position ascending** — Folders whose `name` **starts with** the keyword rank above folders that contain it **later** in the name.
   * `keyword="general"` → `"General"` (position 0) ranks above `"0.General"` (position 2).

2. **Name length ascending** — When match position is tied, shorter names rank higher.
   * Both start at position 0 and match "gen": `"General"` (7 chars) ranks above `"General Announcements"` (21 chars).

This ensures the most specific matches surface first.

## Best Practices

<AccordionGroup>
  <Accordion title="Pick the tool by folder type, not API key type">
    `search_private_folders` searches your private (non-shared) folders; `search_team_folders` searches team (workspace-shared) folders. Both accept any API key with the `mcp:folders:read` scope; choose the tool by folder type. When the workspace has no matching folders, the response is an empty list (not an error).
  </Accordion>

  <Accordion title="Target a specific workspace with workspaceGuid">
    Both tools operate on your API key's workspace by default. To search folders in another workspace you can access, pass an explicit `workspaceGuid` obtained from `list_workspaces`.
  </Accordion>

  <Accordion title="For CJK text, rely on NFC normalization">
    Korean, Japanese, and Chinese text is automatically NFC-normalized server-side. You don't need to preprocess your keywords — just pass the raw text and the matching engine handles it.

    Example: `keyword="네이버"` will match folders with composed or decomposed forms of the same text.
  </Accordion>

  <Accordion title="Paginate with nextCursor, not offsets">
    Always use the `nextCursor` returned in the previous response. Don't try to compute offsets yourself — pagination behavior may change in future releases.
  </Accordion>

  <Accordion title="Use empty keyword to enumerate all folders">
    To get a complete list of folders without filtering, omit `keyword` or pass an empty string. Use `size` and `nextCursor` to paginate through the full result set.
  </Accordion>
</AccordionGroup>

## Common Errors

### Insufficient scope

<Error>
  ```json theme={"system"}
  {
    "error": "FORBIDDEN",
    "message": "Insufficient scope: mcp:folders:read required",
    "statusCode": 403
  }
  ```
</Error>

**Solution:** Your API key lacks the `mcp:folders:read` scope. Generate a new key with the correct scopes, or contact your workspace administrator to grant access.

## Token Usage

| Operation                             | Tokens    |
| ------------------------------------- | --------- |
| Empty keyword (enumerate all folders) | \~200–500 |
| Keyword search                        | \~200–500 |

Folder search is lightweight — results include only names and paths, not content.

## Scope Requirements

Both `search_private_folders` and `search_team_folders` require the `mcp:folders:read` scope. Configure your API key at [Tiro Platform API Keys](https://platform.tiro.ooo/dashboard/api-keys).

<Warning>
  **Note:** The `mcp:folders:write` scope no longer exists (as of 2026-05-06). All folder write operations were removed. If you have an old API key with `mcp:folders:write`, it will be ignored — the scope is not used.
</Warning>

## Related Tools

* **[`list_notes`](./list-notes)** — Find notes within a specific folder using `filter.folderId` (no separate "list folders" tool needed).
* **[`search_notes`](./search-notes)** — Search notes in a folder with `filter.folderId` (keyword-required deep search).
