assign_tag
ChatGPTAssign a tag from the workspace registry to a journal. Entries in that journal can then carry the tag via write_entry / update_entry. Plus+ tier — assignment is what unlocks the tag for entry use.
create_entry
ChatGPTCreate a new journal entry in a journal. Pass workspace and journal slugs. The new entry gets an auto-assigned per-journal index (e.g. #7) returned in the response. Before calling, fetch the journal via get_journal and follow prompt.body as the writing instruction (structure, voice, sections to include or skip). If you want to tag the entry, first call list_tags(workspace_slug, journal_slug) and pass only tag names from that response — unknown names are rejected with a clear error listing the valid options (Pattern D — issue #239).
create_invitation
ChatGPTInvite a collaborator to a journal by email. Owner-only. Pass workspace and journal slugs plus the invitee email.
create_journal
ChatGPTCreate a new journal in a workspace. The slug is auto-derived from name; the response includes the new journal_slug to use in subsequent calls. Pass the workspace slug to scope creation.
create_journal_prompt
ChatGPTCreate a prompt bound to a journal: inserts the prompt, adds it to the journal's selectable pool, and selects it as the journal's active prompt — atomic in one call. Requires the prompt_templates tier feature (Pro). Body cap 10K chars; name cap 80 chars. If slug is omitted, it is derived from name. The reserved slug default is rejected.
create_tag
ChatGPTCreate a new tag in the workspace registry. The new tag is NOT auto-assigned to any journal — follow up with assign_tag to make it usable in a specific journal. Plus+ tier. Names are kebab-case (max 50); descriptions optional, max 200 chars.
delete_attachment
ChatGPTDelete an attachment from a journal by id. Removes the stored file and unlinks it from any entries that referenced it. Only the uploader or the journal owner can delete.
delete_contributor
ChatGPTRemove a contributor from a journal. Owner-only. Pass workspace and journal slugs plus the contributor user UUID returned by list_contributors. (User IDs stay UUIDs — they come from Supabase auth, no slug exists.)
delete_entry
ChatGPTDelete a journal entry by its per-journal index. Irreversible. Pass workspace and journal slugs plus the entry index.
delete_invitation
ChatGPTRevoke an invitation by its ID. Pass workspace and journal slugs plus the invitation UUID returned by list_invitations. (Invitation IDs stay UUIDs — they are internal handles, not user-visible slugs.)
delete_journal
ChatGPTDelete a journal and all its entries. Irreversible. Owner-only. Pass workspace and journal slugs.
delete_prompt
ChatGPTDelete a prompt. Cascades to remove it from any journal pool and resets selected_prompt_id on any journals selecting it back to the system default. Requires prompt_templates (Pro).
delete_tag
ChatGPTDelete a tag from the workspace registry. Cascades to remove the tag from every entry in every journal the tag was assigned to. Not tier-gated.
export_journal
ChatGPTExport a journal as JSON, Markdown, or CSV. Returns the raw export payload as text. Pass workspace and journal slugs plus the desired format.
fetch
ChatGPTFetch the full body of a single workjournal entry by the id returned from search. The id format is <workspace>/<journal>/<index> (e.g. acme/engineering-notes/42). Returns a JSON object {id, title, text, url, metadata} where text is the entry's full markdown content (title + summary + body).
get_entry
ChatGPTGet a single journal entry by its per-journal index. Pass workspace and journal slugs plus the entry index (e.g. 1, 2, 3 — shown as #N in list_entries output).
get_journal
ChatGPTFetch a single journal by its slug. Returns journal metadata including the active prompt: { source, name, slug, body, used_by_count } — prompt.body is always populated (system default when no custom prompt is assigned). Call this before create_entry and follow prompt.body as the writing instruction for the journal.
get_prompt
ChatGPTFetch a single prompt by slug. Returns the full body.
get_tag
ChatGPTFetch a single tag from the workspace registry by name. With journal_slug, also reports whether the tag is currently assigned to that journal.
get_workspace
ChatGPTFetch a single workspace by its slug. Slugs are short, hyphenated, globally unique strings (e.g. acme, juliusz-personal). Use list_workspaces first if you do not know the slug.
last_entries
ChatGPTReturn the N most recent journal entries with their full body content. Pass workspace and journal slugs.
list_attachments
ChatGPTList the file attachments in a journal — id, filename, type, size, content URL, and the indices of entries that reference each one. Read-only.
list_contributors
ChatGPTList contributors of a journal (users with access, including the owner). Pass workspace and journal slugs.
list_entries
ChatGPTList recent journal entries for a journal. Returns slim entries (no body). Use last_entries or get_entry to read entry content. Pass workspace and journal slugs.
list_invitations
ChatGPTList invitations for a journal (includes pending, accepted, and revoked). Pass workspace and journal slugs.
list_journals
ChatGPTList the journals in a workspace that the signed-in user has access to. Pass the workspace slug from list_workspaces. Each journal returned has a slug you then pass to get_journal, list_entries, etc. Pass shared-with-me as the workspace slug to list journals shared with the user from workspaces they don't own.
list_prompts
ChatGPTList the workspace's prompts. Returns name, slug, and updated_at for each. Workspace owner only — non-owners get an empty list.
list_tags
ChatGPTList tags in a workspace. Without journal_slug: returns the full workspace registry. With journal_slug: filters to tags assigned to that journal (the set entries in that journal can carry). Read-only — available to any workspace member (Free or paid). Each tag has name and description; with journal_slug, only assigned rows are listed. Tier gating applies only to create / assign / write operations.
list_workspaces
ChatGPTList the workspaces the signed-in user owns or belongs to. Returns each workspace with its slug — that slug is the first argument to every other tool (e.g. list_journals(workspace_slug)). Most users have a single personal workspace named after their account.
rename_journal
ChatGPTRename a journal (display name only). The slug stays the same, so existing links keep working. Use set_journal_slug if the user wants to change the URL slug instead.
search
ChatGPTSearch across the user's accessible workjournal entries (their own workspaces' journals plus journals shared with them). Returns a JSON object {results: [{id, title, text, url}]} where each id is a handle of the form <workspace>/<journal>/<index> you can pass to fetch for the full entry body. Stable within a session, but not across workspace or journal slug renames (set_journal_slug breaks old ids the same way it breaks old web URLs). Use as the primary discovery tool when the user asks about something they've previously logged. Uses Postgres websearch syntax: bare words match anywhere, double-quoted phrases match exactly.
search_entries
ChatGPTSearch journal entries in a journal. Returns slim entries — follow up with get_entry for the full body. Pass workspace and journal slugs.
select_journal_prompt
ChatGPTSet the journal's selected prompt. The target prompt must be in the journal's pool (or the global default — pass prompt_slug = 'default' to select it). Requires custom_prompt. Affects only the active selection — the journal's pool membership is untouched.
semantic_search
ChatGPTSemantic (RAG) search over a journal — ranks markdown-aware chunks of every entry by cosine similarity against the query embedding. Use this when the user asks about a concept rather than an exact keyword: 'find anything we decided about retry semantics', 'what did we say about pgvector'. Returns the matching CHUNKS (with their parent entry pointer + similarity score), not whole entries — follow up with get_entry(workspace_slug, journal_slug, entry_index) to read the full body. Plus+ tier; Free workspaces get a tier-rejection error verbatim.
set_journal_slug
ChatGPTChange a journal's URL slug. Changing the slug breaks any existing links to this journal — old <workspace>/<journal> URLs will return 404. Surface this consequence to the user before calling. The display name is unaffected; use rename_journal for that.
unassign_tag
ChatGPTUnassign a tag from a journal. Cascades to strip the tag from every existing entry in that journal. The tag itself stays in the workspace registry — other journals are unaffected. Not tier-gated.
unselect_journal_prompt
ChatGPTReset the journal's selected prompt to the system default. Equivalent to calling select_journal_prompt with prompt_slug = 'default'. Requires custom_prompt. The journal's pool is untouched.
update_entry
ChatGPTUpdate a journal entry by its per-journal index. All content fields are optional — only fields you pass are changed; the others (and created_at, index) are unchanged. At least one of title, summary, body, or tags must be provided. Use this to append verification notes, fix a typo, annotate an entry post-hoc, or replace the tag set — preferred over delete+recreate, which loses the original timestamp. To use tags, first call list_tags(workspace_slug, journal_slug); unknown names are rejected with the valid options in the error.
update_journal
ChatGPTUpdate a journal's display name and/or description in one call. Both fields are optional — only fields you pass are changed. Pass an empty string for new_description to clear it. At least one of new_name / new_description must be provided. Use set_journal_slug to change the URL slug (separate tool because it 404s old links).
update_prompt
ChatGPTUpdate a prompt's name and/or body. The slug is stable — renaming does not change the URL. Requires custom_prompt.
update_tag
ChatGPTRename a tag or replace its description. Renaming cascades to every entry in every journal the tag is assigned to. Description may be cleared. Not tier-gated — downgraded workspaces can still tidy up.
upload_attachment
ChatGPTUpload a file (image or PDF, max 5 MiB) as an attachment in a journal by passing its bytes as base64. Works on every surface including the hosted remote server. Returns the attachment id and a content URL you can embed in an entry as  or link to. Per-workspace storage quota applies by tier. On the local stdio server, prefer upload_attachment_from_path to avoid base64-encoding a file you already have on disk.