# Publishing a Document (`gakaso_publish_doc`)
Gakaso can publish an agent- or human-authored document to a public, unauthenticated URL at `/d/{slug}` — useful for handing a human (or anyone) a shareable write-up, spec, or changelog without them needing a Gakaso account. This is a distinct capability from code/design export (see `/export.md`) and from the faithful UI round-trip (see `/mcp/faithful-roundtrip.md`) — it publishes **document content** (markdown or HTML text you provide), not canvas state.
## Tool: `gakaso_publish_doc`
| Parameter | Required | Notes |
|---|---|---|
| `projectId` | yes | Project the doc is associated with. |
| `title` | yes | Document title. |
| `content` | yes | Document body — markdown by default. |
| `slug` | no | URL slug. If omitted, derived from `title` (lowercased, non-alphanumeric runs collapsed to a single `-`, leading/trailing `-` trimmed, truncated to 80 chars). If your derived/explicit slug is empty after sanitization, the call fails with `INVALID_PARAMS` (`-32602`) — pick something with at least one alphanumeric character. |
| `format` | no | `"markdown"` (default) or `"html"`. |
| `classification` | no | `"public"` (default) / `"internal"` / `"confidential"` / `"restricted"` — see `/security/model.md`. |
| `isPublic` | no | Defaults to `true`. |
Authorization: the calling identity needs `write` permission on the project (same RBAC layer as other mutating tools — see `/mcp/overview.md`); there is currently no dedicated "publish" scope separate from that.
## The classification gate
**A doc can only be made public (`isPublic: true`, the default) if `classification` is `"public"` (also the default).** If you pass `isPublic: true` together with any other classification, the call is rejected with `INVALID_PARAMS` (`-32602`) and an explicit message rather than silently publishing something sensitive. If you actually want a non-public classification, set `isPublic: false` in the same call — Gakaso will not infer that for you from the classification value alone.
Practical rule of thumb: don't set a non-default `classification` on a document you intend to publish. If a human asks you to "publish this internal doc," that request is contradictory as far as this tool is concerned — flag it back to them rather than picking one side silently.
## Slugs, updates, and collisions
- Calling `gakaso_publish_doc` again with the **same slug** for the **same project** updates the existing document in place (title/content/format/classification/`isPublic` are all overwritten with whatever you pass this time — there's no partial-update mode, so if you're only changing the content, still pass the current `title` etc. or they'll be blanked/changed).
- If the slug is already taken by a **different project**, the call fails with `INVALID_PARAMS` (`-32602`) ("Slug is taken by another project") rather than silently overwriting someone else's doc. Pick a more specific slug (or let it derive from a more specific title) if this happens.
- There is no `gakaso_unpublish_doc` / `gakaso_delete_doc` tool in the current surface — to take a document down, the practical option is to call `gakaso_publish_doc` again for the same slug with `isPublic: false` (which still requires the classification rule above — set both together) or with a non-public `classification` and `isPublic: false`.
## Return value and result
On success: `{ slug, url: "/d/{slug}", isPublic, classification }`. The `url` is a path — prefix it with the project's actual Gakaso host to get a shareable absolute link (this doc set intentionally doesn't hard-code a hostname; use whatever host the human's Gakaso instance is actually running at). A published, public doc is viewable by anyone with the link, with no sign-in required — treat it exactly like publishing something to the open internet.
## Typical call sequence
1. Draft the content (markdown is simplest unless the human specifically wants HTML).
2. Decide on a slug — omit it and let the title drive it unless you need a stable/predictable URL (e.g. matching an existing external link).
3. Call `gakaso_publish_doc` with `{ projectId, title, content }` (classification/isPublic left at their public defaults, if that's actually what's wanted).
4. Report the returned `url` back to the human.
5. If asked to revise later, call the same tool again with the same `slug` (or the same `title`, if you let it derive originally and want it to derive to the same value again) and the updated `content`.