MCP server
Every tool the Treehouse MCP server exposes, with parameters, outputs, limits and the instructions it gives connected agents.
Connection
| Setting | Value |
|---|---|
| Endpoint | https://api.trytree.house/api/mcp |
| Transport | Streamable HTTP, stateless (no session id), JSON responses |
| Auth | x-api-key: <key> header. Authorization: Bearer is not accepted. |
| OAuth | Not supported. The key header is the only credential. |
| Server name | treehouse |
{
"mcpServers": {
"treehouse": {
"type": "http",
"url": "https://api.trytree.house/api/mcp",
"headers": { "x-api-key": "th_example_key" }
}
}
}
The exact configuration format depends on your client; connect other agents has examples. Every tool call is attributed to the key's actor type (agent or human) and name, exactly as a REST request would be. See authentication.
Two tool sets
Which tools a client sees depends on the key it connects with.
| Key | Tools |
|---|---|
| Account-wide (created in the web app) | All the tools below. Every tool except list_workspaces and create_workspace takes a required workspaceId string, checked against your membership on each call. |
| Workspace-scoped (device flow) | The same tools without list_workspaces and create_workspace, and without a workspaceId parameter: every call acts on the key's workspace. |
The parameter tables below omit workspaceId. Add it when you use an account-wide key.
Server instructions
Connected clients receive these instructions with the tool list. They are quoted exactly:
Treehouse is a shared file workspace for humans and agents. Files are plain bytes; a markdown file may begin with a YAML front-matter block (--- ... ---) carrying metadata.
Read AGENTS.md at the workspace root before doing anything else, if it exists. It carries that workspace's own conventions — how it is organised and what belongs where. In a workspace that has not been set up yet, it instead carries a setup guide: follow it when the user asks you to set up, bootstrap, or organise the workspace.
Front-matter conventions the web UI understands — set them by writing the file, no special API needed:
- "date: YYYY-MM-DD" on a markdown file places it on its folder's Calendar view for that day. Change the date to reschedule it; remove it to take it off the calendar. Only files with a valid date appear there.
- A folder's README.md (or index.md) front matter can declare "view: calendar" to make the folder open as a calendar, and "title:" to give the folder a human-readable heading (shown instead of the raw folder name).
Anything the web UI does with front matter is a plain-file operation, so reading and writing files is enough to participate in those. Comments are the exception: they are review threads held by the server, not files, and only the comment tools can see them.
Reviewers comment on markdown files by quoting the text they mean — there are no line numbers. When asked to address comments on a file: list_comments for that path, read_file it (note the returned version), write_file your revision with that version as baseVersion, then reply_comment on each thread saying what you changed, and resolve_comment only the threads you actually fixed. Don't resolve what you didn't fix — reply instead. A thread whose anchorStatus is not "anchored" refers to text that has since changed: read_file_version at its pinnedVersion to see the document it was written against. Use add_comment to raise a question of your own, quoting the passage it is about.
The front-matter conventions are described in front matter and folder pages.
Result conventions
- Most tools return a short text summary plus a
structuredContentobject. Read values such asversionfromstructuredContent, not by parsing the text: file content can contain anything. - Hard errors set
isError: truewith a text message: not found, invalid path, file too large, read-only workspace, a refused share, a stale delete, and malformed input. Paths you can't see (another member's private files) are reported as not found. - Recoverable outcomes are returned as data with
ok: false, so the agent keeps its work and can retry: awrite_fileversion conflict (your content is saved as a conflicted copy), a storage or file-count limit, and anadd_commentquote that can't be placed. - Paths are workspace-relative with
/separators, such asProjects/plan.md. A leading/and.segments are ignored;.., backslashes, null bytes and an empty path are rejected withInvalid path: ….
Workspaces
list_workspaces
Lists the workspaces the key can reach. Account-wide keys only. No parameters.
Output: { workspaces: [{ id, name }] }.
create_workspace
Creates a workspace owned by you, with no other members. Account-wide keys only. Subject to your plan's workspace allowance.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Workspace name. |
icon | string | No | An emoji or an Iconify name. For an image, create first, then call set_workspace_image_icon. |
color | string | No | blue, violet, emerald, amber, rose, cyan, slate or neutral. |
Output: { id, name }. New workspaces start with a README.md and an AGENTS.md setup guide unless the server disables seeding.
set_workspace
Updates the workspace's display settings. Pass an empty string to clear icon, color or theme. An empty name is ignored.
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | No | New name. |
icon | string | No | An emoji or an Iconify name. |
color | string | No | blue, violet, emerald, amber, rose, cyan, slate or neutral. |
theme | string | No | light, dark, dracula, nord, solarized-light, rose-pine or treehouse. |
Output: { id, name, icon, color, theme }.
set_workspace_image_icon
Sets an image as the workspace icon.
| Parameter | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Base64-encoded PNG, JPEG, GIF or WebP, up to 1 MiB. Square images around 256×256 look best; other shapes are letterboxed, never cropped. |
Output: { id, name, icon }.
get_usage
Storage and file-count usage against the workspace's plan. No parameters.
Output: { usedBytes, limitBytes, remainingBytes, warning, usedFiles, limitFiles, remainingFiles }. Limits and remaining values are null when unlimited. warning is true once the file count reaches 90% of its limit. See plans and limits.
Files
list_files
Lists files under an optional folder, sorted by path. Hidden .keep placeholders are omitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
prefix | string | No | Folder to list, such as Projects. Omit for the whole workspace. |
limit | integer | No | Maximum entries. Default and maximum 1000. |
offset | integer | No | Entry to start from, for paging. |
Output: text only, one line per file in the form path<TAB>(v3, 1204 bytes). When there are more entries, the last line says how many and which offset to pass next. An empty result is (no files).
search_files
Fuzzy search over file and folder names and paths, not contents. The characters of the query must appear in order but need not be adjacent: clinot matches Clients/acme/notes.md. Results are ranked best first and there is no paging, so narrow the query instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
query | string | Yes | Text to match. |
limit | integer | No | Maximum results. Default 50, maximum 200. |
Output: { results: [{ path, name, isDir }], totalMatches }. totalMatches counts every match before the limit. The text summary frames names as untrusted data, because anyone in the workspace can name a file.
read_file
Reads a file's current bytes and version.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path. |
encoding | string | No | utf8 (default) or base64. Use base64 for images, PDFs and other binary files. |
maxBytes | integer | No | Read at most this many bytes, starting at offset. |
offset | integer | No | Byte offset to start from. |
Output: { version, contentType, content }. Keep version: it is the baseVersion for your next write.
A single response is capped at 1 MiB. A larger file read without maxBytes or offset is refused with its size, and maxBytes above the cap is clamped to it. Read a large file in windows by advancing offset.
write_file
Creates or updates a file.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path. Missing parent folders are created implicitly. |
content | string | Yes | The complete new content. |
baseVersion | integer | No | The version you last read. Omit only when creating a new file. |
encoding | string | No | utf8 (default) or base64. |
| Outcome | structuredContent |
|---|---|
| Written | { ok: true, version } |
| Version conflict | { ok: false, currentVersion, conflictCopyPath }. Your content was saved at conflictCopyPath. |
| Storage limit reached | { ok: false, quotaExceeded: true, usedBytes, limitBytes }. Nothing written. |
| File limit reached | { ok: false, fileCountExceeded: true, usedFiles, limitFiles }. Nothing written. |
How baseVersion is handled:
- Omitted, path doesn't exist: the file is created at version 1.
- Omitted, path exists: if your content is byte-for-byte identical, the call succeeds and returns the existing version. If it differs, nothing is overwritten: your content is saved as a conflicted copy and the result is
ok: false. - Matches the current version: the file is updated and the version goes up by one.
- Stale: identical content succeeds without a new version; different content becomes a conflicted copy. If the file was deleted since you read it, your write recreates it at version 1.
A file larger than the workspace's per-file limit is a hard error. A read-only workspace (lapsed subscription) is a hard error. Versions and conflicts explains the rules in full.
move_file
Moves or renames one file. Folders are moved by moving their files.
| Parameter | Type | Required | Description |
|---|---|---|---|
from | string | Yes | Current path. |
to | string | Yes | New path. |
Output: text only. Fails if to already exists. There is no baseVersion: the move applies to whatever the current version is. The file restarts at version 1 at its new path, and its comment threads move with it. Re-read before writing to the new path.
delete_file
Deletes a file.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path. |
baseVersion | integer | Yes | The version you last read. |
Output: text only. If the file has changed since baseVersion, nothing is deleted and the call returns an error naming the current version. Re-read before deciding again.
set_file_icon
Sets the icon shown for a file or folder.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File or folder path, up to 1024 characters. |
icon | string | No | An emoji or an Iconify name such as solar:document-bold-duotone, up to 64 characters. Empty or omitted clears it. |
Output: text only.
set_private
Makes a file or folder private to the key's owner, or shared again. A private folder hides its whole subtree from other members.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File or folder path, up to 1024 characters. |
kind | string | No | file or folder. Defaults to folder, so pass file for a file. |
private | boolean | No | true (default) to make private, false to share again. |
Output: text only. Only the owner can make a private item shared again.
History
read_file_version
Reads the bytes of an earlier version.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path. |
version | integer | Yes | Version number to read. |
encoding | string | No | utf8 (default) or base64. |
Output: { version, contentType, content }. If that version's content isn't available, the call returns an error.
restore_file
Restores an earlier version as a new version on top of the history. Nothing is overwritten or removed.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File path. |
targetVersion | integer | Yes | The version to bring back. |
Output: { status: "ok", version, restoredFrom }, or { status: "conflict", conflictCopyPath } if other writes kept winning and the restored content was saved as a conflicted copy instead.
Sharing
create_share
Creates a member link to a file or folder: a link that only workspace members can open.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File or folder path. |
kind | string | Yes | file or folder. |
access | string | No | Only member is accepted. public is refused. |
Output: { id, path, access, memberUrl }. Public and password-protected links can only be made by a person signed in to the web app, so a leaked key can't publish your files. Private items can't be shared.
list_shares
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | File or folder path. |
Output: { shares: [...] }, the active shares on exactly that path.
revoke_share
| Parameter | Type | Required | Description |
|---|---|---|---|
shareId | string | Yes | The share's id. |
Output: text only.
Comments
Comment threads live on markdown files and are anchored by quoting text, never by line or character position. See comments for how they look to people.
list_comments
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | No | Only threads on this file. |
threadId | string | No | Read one thread. |
status | string | No | open or resolved. |
anchorStatus | string | No | anchored, ambiguous or outdated. |
author | string | No | Only threads started by this user id. |
limit | integer | No | Threads per page. Default 50, maximum 200. |
offset | integer | No | Thread to start from. |
entriesLimit | integer | No | Replies per thread. Default 20, maximum 100. |
entriesOffset | integer | No | Reply to start from, to read past the first 100. |
Output: { threads, total, more, nextOffset, truncated }. Each thread has id, path, status, anchorKind, quote, contextBefore, contextAfter, currentText (the matching text now, or null once the anchor stops resolving), pinnedVersion (the version it was written against), anchorStatus, createdBy, entries, entryCount and resolution details. nextOffset is null on the last page. truncated: true means an anchorStatus filter stopped scanning early; narrow the other filters rather than paging.
add_comment
Starts a thread on a markdown file.
| Parameter | Type | Required | Description |
|---|---|---|---|
path | string | Yes | Markdown file path. |
body | string | Yes | The comment, up to 64 KiB. |
quote | string | For range | The exact text being commented on, up to 4 KiB. |
contextBefore | string | No | Text just before the quote (or the point), up to 512 bytes. |
contextAfter | string | No | Text just after, up to 512 bytes. |
occurrence | integer | No | Which match to use (from 1) when the quote appears more than once. |
kind | string | No | range (default) or point. A point needs both contexts and no quote. |
baseVersion | integer | No | The version you copied the quote from. |
| Outcome | structuredContent |
|---|---|
| Created | { ok: true, thread } |
| Quote appears several times | { ok: false, error: "anchor_ambiguous", occurrences } |
| Quote not in the file | { ok: false, error: "anchor_not_found" } |
| File changed too much | { ok: false, error: "base_version_conflict", baseVersion, currentVersion, reason } |
Nothing is created on an ok: false result. A non-markdown file is a hard error.
reply_comment
| Parameter | Type | Required | Description |
|---|---|---|---|
threadId | string | Yes | The thread. |
body | string | Yes | The reply, up to 64 KiB. |
dedupeKey | string | No | Any string unique to this reply. Send one every time: a retried call with the same key records one reply, not two. |
Output: { ok: true, entry, deduped }. deduped: true means the reply already existed.
resolve_comment
| Parameter | Type | Required | Description |
|---|---|---|---|
threadId | string | Yes | The thread. |
resolved | boolean | Yes | true to resolve, false to reopen. |
Output: { ok: true, resolved, changed, resolvedAt, resolvedAtVersion, resolvedBy }. Idempotent: changed: false means it was already in that state.
Limits
| Limit | Value |
|---|---|
read_file response | 1 MiB per call; page larger files with maxBytes and offset |
list_files entries | 1000 per call; page with offset |
search_files results | Default 50, maximum 200, no paging |
| File size on write | Your plan's per-file limit |
| Comment body / reply | 64 KiB |
| Comment quote | 4 KiB |
| Comment context (each side) | 512 bytes |
Threads per list_comments page | Default 50, maximum 200 |
| Replies per thread per page | Default 20, maximum 100 |
| Workspace image icon | 1 MiB |
Related
- Connect Claude Code
Give Claude Code access to a workspace through your synced folder, the Treehouse MCP server, or both.
- Connect other agents
Connection settings for Cursor, VS Code, Codex, Gemini CLI, Claude Desktop and any other MCP client.
- Versions and conflicts
The concurrency contract every Treehouse client shares - version numbers, compare-and-swap writes, conflicted copies, deletes, moves and restores.
Last updated