seamless docs

Notes, projects & capture

Work artifacts, project scope, and SSRF-safe URL capture - the eight tools around the edges of memory.

A note is not a memory

This is the most common confusion in the whole system, so it is worth being blunt about it.

Memory Note
Answers "What is true about this project?" "What did we produce?"
Length One idea, one line of description However long the artifact is
Lifecycle Superseded and archived Written, occasionally updated
Reaches a briefing Yes - this is what agents get injected No; found via recall
Good examples A constraint, a gotcha, a decision Research findings, a meeting summary, a design record

The test: would a future agent need this injected into its context before it starts working? If yes, it is a memory, and it needs to fit in a description. If it is something you'd want to find and read in full when the topic comes up, it is a note.

Writing a journal entry into memory is the classic failure: it is too long to inject, too specific to generalize, and it pushes real constraints out of the briefing's budget.

Agent-created notes are automatically tagged created-by:agent.

Notes are how plans get their narrative

A plan is not a primitive - it is a composition keyed by plan:<slug>. Tag a note plan:<slug> and it joins that plan's supporting context, so the next agent inherits the design and the reasoning behind it, not just the step list. See Tasks for the step half of the composition.

Projects and scope

project_list and project_create manage the scopes everything else inherits. Most agents never call either: a git repo maps itself on the first session and resolves its project from the agent's working directory, and session_start binds it.

The global project slug is the deliberate cross-project scope. It is a token you pass on purpose, never a default you fall into - see the fail-closed rule in the MCP API overview.

capture_url is SSRF-safe on purpose

capture_url fetches a URL and returns its content as markdown. It is the one tool that makes an outbound request on an agent's behalf, so it is guarded: destination ports are restricted to capture.allowed_ports (80 and 443 by default, never "any port"), and the fetcher refuses to be talked into reaching things it should not. See Configuration.

Scope resolves before the network fetch, so an ambiguous durable destination fails without making a request. Success returns the note's id, slug, title, resolved project, and source_url - the URL as requested; redirects are followed but do not rewrite it. The fetcher validates the initial URL and every redirect, rejecting non-HTTP schemes, private/loopback destinations, and disallowed ports. Size is bounded by truncation rather than rejection: at most 2 MB of the response is read, and the stored readable body is capped again at 50,000 runes with a visible [content truncated] marker.

notes_create

Create a work note (research finding, decision record, summary). Auto-tagged created-by:agent.

Parameter Type Required Description
title string yes note title
body string yes markdown body (aliases: content, text)
description string no optional one-line summary
plan string no optional plan slug (plan:<slug> convention): tags this note into that plan's composition so it surfaces on the Plans screen alongside its tasks_add plan=<slug> steps. Use it whenever this note is a plan's narrative or supporting context.
project string no project slug; defaults to the bound/ambient session's project. An unknown slug CREATES that project -- naming a new one is normal and never an error. Pass project=global ONLY for knowledge that belongs in EVERY project's briefing; it is not a neutral default. With no session and no explicit project the call is rejected as ambiguous.
source_url string no optional source URL
tags array no tags (a comma-separated string is also accepted)

notes_read

Read a note by id.

Parameter Type Required Description
id string yes note id (ULID)

notes_update

Update a note's fields by id (title, description, body, project, tags). Omitted fields are untouched; tags replace all. The slug and id stay stable.

Parameter Type Required Description
id string yes note id (ULID)
body string no new body (aliases: content, text)
description string no new description
project string no new project slug ("" or "global" = global scope)
tags array no tags, replacing all (a comma-separated string is also accepted); an empty list is read as absent and leaves the tags untouched
title string no new title

notes_append

Append a timestamped line to a note's body.

Parameter Type Required Description
id string yes note id (ULID)
body string yes text to append (aliases: content, text)

notes_delete

Delete a note by id (removes the file and its index).

Parameter Type Required Description
id string yes note id (ULID)

project_list

List all projects (slug, name, description).

Takes no parameters.

project_create

Create a project. The slug defaults to a slugified name.

Parameter Type Required Description
name string yes human-readable project name
description string no optional one-line description
slug string no optional explicit slug

capture_url

Fetch a web page (SSRF-guarded: private/loopback addresses are rejected) and save its readable content as a note. Returns the new note's id.

Parameter Type Required Description
url string yes http(s) URL to capture
project string no project slug; defaults to the bound/ambient session's project. An unknown slug CREATES that project -- naming a new one is normal and never an error. Pass project=global ONLY for knowledge that belongs in EVERY project's briefing; it is not a neutral default. With no session and no explicit project the call is rejected as ambiguous.