Agent-native notes, tasks, dev-docs, vaults, sync & handoffs. MCP + OpenAPI dual surface.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
McpKey <YOUR_KEY> -- same key as the AINOTE_API_KEY env var for the stdio transport.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Create or update an assumption under a project (L2 intent layer). Omit `id` to create, pass it to update. There is no delete tool. 🔴 `verdict` is REJECTED with an error if passed — verdict is confirmed only through a human judgment decision (see judgment_submit). This tool can only shape the assumption itself: what is being assumed, how critical it is, and what evidence would settle it.
Project UUID this assumption belongs to (required).
Assumption UUID to update. Omit to create a new assumption.
Optional Business Model Canvas block this assumption belongs to. Pass 'null' to clear.
What is being assumed true (required to create).
1=critical if wrong … 3=minor.
What would be observed to settle this assumption. Pass 'null' to clear.
The server marks this tool as destructive. Running it may delete or overwrite data on the server or in any account you connected with your headers.
Create or update an assumption under a project (L2 intent layer). Omit `id` to create, pass it to update. There is no delete tool. 🔴 `verdict` is REJECTED with an error if passed — verdict is confirmed only through a human judgment decision (see judgment_submit). This tool can only shape the assumption itself: what is being assumed, how critical it is, and what evidence would settle it.
| Argument | Type | Description |
|---|---|---|
| id | string | Assumption UUID to update. Omit to create a new assumption. |
| bmc_block | string | Optional Business Model Canvas block this assumption belongs to. Pass 'null' to clear. |
| statement | string | What is being assumed true (required to create). |
| project_id* | string | Project UUID this assumption belongs to (required). |
| criticality | integer | 1=critical if wrong … 3=minor. |
| evidence_condition | string | What would be observed to settle this assumption. Pass 'null' to clear. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"project_id"
],
"properties": {
"id": {
"type": "string",
"description": "Assumption UUID to update. Omit to create a new assumption."
},
"bmc_block": {
"enum": [
"key_partners",
"key_activities",
"key_resources",
"value_propositions",
"customer_relationships",
"channels",
"customer_segments",
"cost_structure",
"revenue_streams",
"null"
],
"type": "string",
"description": "Optional Business Model Canvas block this assumption belongs to. Pass 'null' to clear."
},
"statement": {
"type": "string",
"description": "What is being assumed true (required to create)."
},
"project_id": {
"type": "string",
"description": "Project UUID this assumption belongs to (required)."
},
"criticality": {
"enum": [
1,
2,
3
],
"type": "integer",
"description": "1=critical if wrong … 3=minor."
},
"evidence_condition": {
"type": "string",
"description": "What would be observed to settle this assumption. Pass 'null' to clear."
}
}
}Soft-delete a dev document by title or UUID. Reversible from trash. Pass `category` when multiple docs share the same title across subcategories (memory/claude/cursor/env/docs).
| Argument | Type | Description |
|---|---|---|
| id | string | Document UUID |
| title | string | Document title |
| category | string | Subcategory to disambiguate title |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"id": {
"type": "string",
"description": "Document UUID"
},
"title": {
"type": "string",
"description": "Document title"
},
"category": {
"type": "string",
"description": "Subcategory to disambiguate title"
}
}
}Soft-delete a task by ID. Destructive but reversible within 30 days (TaskCleanupJob purges trash daily at 2am KST). Returns 404 if the task does not exist or is not owned by the authenticated user.
| Argument | Type | Description |
|---|---|---|
| id* | string | Task ID to delete |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Task ID to delete"
}
}
}Create or update a dev doc. action: create | update. Deletion is a separate tool (delete_dev_doc). (consolidated surface — same handlers as the legacy tools) Required per action — create: title, content | update: content.
| Argument | Type | Description |
|---|---|---|
| id | string | [update] Document UUID |
| mode | string | [update] Update mode (default: replace) |
| title | string | [create] Filename as title (e.g., ainote-memory.md, project-claude.md) [update] Document title |
| action* | string | which operation to run |
| content | string | [create] Full file content (markdown, json, yaml, text) [update] New content |
| category | string | [create] Subcategory: memory | claude | cursor | env | docs | custom. Default: docs [update] Subcategory to disambiguate title |
| local_path | string | [create] Absolute path on this machine (e.g., ~/.claude/projects/-Users-seunghan/memory/MEMORY.md). Used by pull_dev_docs to restore file on other devices. Supports ~ expansion. [update] Update the local file path for multi-device sync |
| memory_type |
Read dev docs. action: get | list | categories | pull (consolidated surface — same handlers as the legacy tools)
| Argument | Type | Description |
|---|---|---|
| id | string | [get] Document UUID |
| title | string | [get] Document title (e.g., project-a-memory.md) |
| action* | string | which operation to run |
| search | string | [list] Search keyword in document title |
| category | string | [get] Subcategory (claude, cursor, etc.) to disambiguate title [list] Subcategory filter (claude, cursor, windsurf, copilot, docs, etc.). Omit to list all dev docs. [pull] Subcategory filter. Omit to pull all docs with local_path. |
| content_type | string | [list] Filter by content type |
| include_versions | boolean | [get] Include version history (default: false) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"id": {
"type": "string",
"description": "[get] Document UUID"
},
"title": {
"type": "string",
"description": "[get] Document title (e.g., project-a-memory.md)"
},
"action": {
"enum": [
"get",
"list",
"categories",
"pull"
],
"type": "string",
"description": "which operation to run"
},
"search": {
"type": "string",
"description": "[list] Search keyword in document title"
},
"category": {
"type": "string",
"description": "[get] Subcategory (claude, cursor, etc.) to disambiguate title [list] Subcategory filter (claude, cursor, windsurf, copilot, docs, etc.). Omit to list all dev docs. [pull] Subcategory filter. Omit to pull all docs with local_path."
},
"content_type": {
"enum": [
"markdown",
"json",
"yaml",
"text"
],
"type": "string",
"description": "[list] Filter by content type"
},
"include_versions": {
"type": "boolean",
"description": "[get] Include version history (default: false)"
}
},
"additionalProperties": true
}Pull an encrypted secret blob. Response is ciphertext_b64 only — server never sees and never returns plaintext.
| Argument | Type | Description |
|---|---|---|
| alias | string | |
| node_id | string | |
| device_id | string | Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). |
{
"type": "object",
"oneOf": [
{
"required": [
"node_id"
]
},
{
"required": [
"alias"
]
}
],
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"alias": {
"type": "string",
"maxLength": 128,
"minLength": 1
},
"node_id": {
"type": "string",
"format": "uuid"
},
"device_id": {
"type": "string",
"description": "Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH)."
}
},
"additionalProperties": false
}Push a new client-encrypted secret. Ciphertext only — server never sees plaintext. Recipients must include the pushing device's own pubkey.
| Argument | Type | Description |
|---|---|---|
| kind* | string | |
| alias* | string | |
| device_id* | string | |
| client_hlc | string | |
| recipients* | array | |
| ciphertext_b64* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"alias",
"kind",
"ciphertext_b64",
"recipients",
"device_id"
],
"properties": {
"kind": {
"enum": [
"api_key",
"oauth_token",
"private_key",
"passphrase"
],
"type": "string"
},
"alias": {
"type": "string",
"maxLength": 128,
"minLength": 1
},
"device_id": {
"type": "string",
"format": "uuid"
},
"client_hlc": {
"type": "string"
},
"recipients": {
"type": "array",
"items": {
"type": "string",
"pattern": "^age1[a-z0-9]{58}$"
},
"minItems": 1
},
"ciphertext_b64": {
"type": "string",
"maxLength": 1048576,
"minLength": 1
}
},
"additionalProperties": false
}Rotate a secret: soft-delete the old node, create a new live node with the same alias and the new ciphertext+recipients. 7-day grace before hard delete of old ciphertext bytes.
| Argument | Type | Description |
|---|---|---|
| node_id* | string | |
| device_id* | string | |
| client_hlc | string | |
| new_recipients* | array | |
| new_ciphertext_b64* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"node_id",
"new_ciphertext_b64",
"new_recipients",
"device_id"
],
"properties": {
"node_id": {
"type": "string",
"format": "uuid"
},
"device_id": {
"type": "string",
"format": "uuid"
},
"client_hlc": {
"type": "string"
},
"new_recipients": {
"type": "array",
"items": {
"type": "string",
"pattern": "^age1[a-z0-9]{58}$"
},
"minItems": 1
},
"new_ciphertext_b64": {
"type": "string",
"maxLength": 1048576,
"minLength": 1
}
},
"additionalProperties": false
}Read env-sync state. action: devices | drift | status | txn_pull. Secret reads are a separate tool (env_sync.secret_pull). (consolidated surface — same handlers as the legacy tools)
| Argument | Type | Description |
|---|---|---|
| op | string | [txn_pull] |
| limit | integer | [txn_pull] |
| action* | string | which operation to run |
| cursor | string | [txn_pull] |
| device_id | string | [devices] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [drift] [status] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [txn_pull] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). |
| since_hlc | string | [txn_pull] |
Ingest a batched envelope of client-recorded env_sync mutations. HLC-skew gated, partial-success on conflict.
| Argument | Type | Description |
|---|---|---|
| device_id | string | Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). |
| operations* | array |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"operations"
],
"properties": {
"device_id": {
"type": "string",
"description": "Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH)."
},
"operations": {
"type": "array",
"items": {
"type": "object",
"required": [
"tool",
"args",
"hlc"
],
"properties": {
"hlc": {
"type": "string"
},
"args": {
"type": "object"
},
"tool": {
"type": "string"
},
"client_txn_id": {
"type": "string"
}
},
"additionalProperties": false
},
"maxItems": 100,
"minItems": 1
}
},
"additionalProperties": false
}Inverse-apply a prior env_sync transaction. Safety gates: ownership, idempotency, descendant-conflict (force opt-out).
| Argument | Type | Description |
|---|---|---|
| force | boolean | |
| txn_id* | string | |
| device_id | string | Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). |
| include_descendants | boolean |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"txn_id"
],
"properties": {
"force": {
"type": "boolean",
"default": false
},
"txn_id": {
"type": "string",
"format": "uuid"
},
"device_id": {
"type": "string",
"description": "Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH)."
},
"include_descendants": {
"type": "boolean",
"default": false
}
},
"additionalProperties": false
}Non-destructive env-sync writes. action: enroll | request_share | heartbeat. Secret push/rotate are separate tools. (consolidated surface — same handlers as the legacy tools) Required per action — enroll: alias, age_pubkey, enrollment_token | request_share: target_device_id, secret_shares, device_id | heartbeat: device_id, installed_skills_hash, installed_hooks_hash.
| Argument | Type | Description |
|---|---|---|
| os | string | [heartbeat] |
| alias | string | [enroll] |
| action* | string | which operation to run |
| device_id | string | [enroll] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [request_share] The existing device performing the re-encryption (must own access to all secret_node_ids). [heartbeat] |
| age_pubkey | string | [enroll] |
| client_hlc | string | [request_share] |
| capabilities | object | [enroll] |
| secret_shares | array | [request_share] |
Get instructions for setting up AI Note MCP in Claude Desktop, Cursor, or other MCP clients. No authentication required.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
}Read knowledge-graph nodes. action: get | list (consolidated surface — same handlers as the legacy tools)
| Argument | Type | Description |
|---|---|---|
| alias | string | [get] |
| limit | integer | [list] |
| action* | string | which operation to run |
| cursor | string | [list] Opaque cursor (created_at ISO + id) from previous page's next_cursor |
| node_id | string | [get] |
| device_id | string | [get] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [list] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). |
| node_type | string | [get] [list] |
| updated_since_hlc |
Soft-delete an env_sync graph node (sets deleted_at; .live scope hides it from reads). Idempotent — re-deleting a deleted node is a no-op success.
| Argument | Type | Description |
|---|---|---|
| node_id* | string | |
| device_id* | string | |
| client_hlc | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"node_id",
"device_id"
],
"properties": {
"node_id": {
"type": "string",
"format": "uuid"
},
"device_id": {
"type": "string",
"format": "uuid"
},
"client_hlc": {
"type": "string"
}
},
"additionalProperties": false
}Add or update a graph entity. action: add | update. Deletion is a separate tool (graph_soft_delete). (consolidated surface — same handlers as the legacy tools) Required per action — add: node_type, alias, payload, device_id | update: node_id, device_id, field_updates.
| Argument | Type | Description |
|---|---|---|
| alias | string | [add] |
| action* | string | which operation to run |
| node_id | string | [update] |
| payload | object | [add] |
| device_id | string | [add] [update] |
| node_type | string | [add] |
| client_hlc | string | [add] [update] |
| field_updates | array | [update] |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"alias": {
"type": "string",
"maxLength": 128,
"minLength": 1,
"description": "[add]"
},
"action": {
"enum": [
"add",
"update"
],
"type": "string",
"description": "which operation to run"
},
"node_id": {
"type": "string",
"format": "uuid",
"description": "[update]"
},
"payload": {
"type": "object",
"description": "[add]"
},
"device_id": {
"type": "string",
"format": "uuid",
"description": "[add] [update]"
},
"node_type": {
"enum": [
"skill",
"hook",
"mcp_server",
"secret",
"setting_fragment"
],
"type": "string",
"description": "[add]"
},
"client_hlc": {
"type": "string",
"description": "[add] [update]"
},
"field_updates": {
"type": "array",
"items": {
"type": "object",
"required": [
"field",
"value",
"client_hlc"
],
"properties": {
"field": {
"type": "string",
"maxLength": 256,
"minLength": 1
},
"value": {},
"client_hlc": {
"type": "string"
}
},
"additionalProperties": false
},
"minItems": 1,
"description": "[update]"
}
},
"additionalProperties": true
}Read session handoffs. action: get | list (consolidated surface — same handlers as the legacy tools) Required per action — get: project, topic.
| Argument | Type | Description |
|---|---|---|
| date | string | [get] Optional YYYY-MM-DD. Omit for latest. |
| tags | array | [list] v2 only: AND match — entries must contain ALL given tags. Example: ['auth', 'mobile']. |
| time | string | [get] Optional HHMM (24h, KST) — same value passed at save time. Required to disambiguate when multiple handoffs share the same project/topic/date. |
| limit | integer | [list] Max entries to return (default 50). |
| since | string | [list] ISO8601 timestamp — only entries with updated_at >= since are returned. |
| topic | string | [get] Topic slug used at handoff_save time. |
| action* | string | which operation to run |
| status | string | [list] v2 only: filter by frontmatter status (e.g. 'in_progress', 'paused', 'completed', 'blocked'). v1 handoffs without frontmatter are excluded when this filter is set. |
Save a session handoff note for cross-device / cross-session continuation. Stored at handoffs/{project}-{topic}-{YYYY-MM-DD}.txt in the user's primary vault. Use the optional `time` param (HHMM, KST) to disambiguate multiple handoffs saved on the same day — it is appended to the topic slug (e.g. topic='phase-d', time='1555' → handoffs/{project}-phase-d-1555-{date}.txt).
| Argument | Type | Description |
|---|---|---|
| date | string | Optional override date (YYYY-MM-DD). Defaults to today in server timezone. |
| time | string | Optional time-of-day suffix in HHMM 24h format (KST), e.g. '1555'. Appended to the topic slug so multiple handoffs on the same day stay distinguishable. Omit for once-per-day saves. |
| topic* | string | Short topic slug for this handoff, e.g. 'phase-d-port', 'oauth-fix'. Lowercase-hyphen. |
| content | string | Full handoff text. Should follow the session-handoff skill template (현재 상태 / 작업 위치 / 수정 파일 / 결정 사항 / 다음 STEP / 알려진 이슈). WAF-bypass tip: prefix the string with '__B64__:' followed by base64-encoded payload to bypass Render/Cloudflare edge WAF for large bodies (~10KB+) that get false-positive blocked as SQLi/cmd-injection. |
| project* | string | Project slug, e.g. 'logi', 'ainote', 'krx_ai'. Lowercase, no spaces. |
| device_id | string |
List the authenticated user's own judgments (L3 layer) — use this to check your own blocking status (risk_tier 1 pending = execution blocked). Default order matches the /command queue: risk_tier ascending, then arrival order.
| Argument | Type | Description |
|---|---|---|
| kind | string | Filter by kind. |
| limit | integer | Max rows to return (default 50, max 200). |
| decision | string | Filter by decision. Omit for all. |
| risk_tier | integer | Filter by risk_tier. |
| project_id | string | Filter by project UUID. |
| subject_id | string | Filter by subject UUID (pair with subject_type). |
| subject_type | string | Filter by subject type. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"kind": {
"enum": [
"approval",
"evidence",
"verdict"
],
"type": "string",
"description": "Filter by kind."
},
"limit": {
"type": "integer",
"description": "Max rows to return (default 50, max 200)."
},
"decision": {
"enum": [
"pending",
"approved",
"rejected",
"deferred",
"superseded",
"expired"
],
"type": "string",
"description": "Filter by decision. Omit for all."
},
"risk_tier": {
"enum": [
1,
2,
3
],
"type": "integer",
"description": "Filter by risk_tier."
},
"project_id": {
"type": "string",
"description": "Filter by project UUID."
},
"subject_id": {
"type": "string",
"description": "Filter by subject UUID (pair with subject_type)."
},
"subject_type": {
"enum": [
"Assumption",
"Task"
],
"type": "string",
"description": "Filter by subject type."
}
}
}Request a human judgment (L3 layer) by attaching evidence — approval, evidence, or verdict. This is the ONLY door an agent has into the judgment queue. 🔴 `decision` is REJECTED with an error if passed — judgments are always created with decision='pending'. Only a human decides them (from the /command queue). Use judgment_list afterwards to check whether this judgment has since been decided. risk_tier controls urgency: 1=destructive (blocks execution until decided), 2=reversible (may auto-proceed after a wait window), 3=informational (auto-expires after 24h).
| Argument | Type | Description |
|---|---|---|
| kind* | string | approval | evidence | verdict. |
| summary* | string | One sentence: what is being judged (required). |
| evidence | object | Free-form evidence attached to the request — links, numbers, diff summaries. |
| risk_tier | integer | 1=destructive 2=reversible 3=informational. Default: 2. |
| project_id | string | Optional project this judgment relates to. |
| subject_id | string | Optional. UUID of the Assumption or Task this judgment is about. |
| subject_type | string | Optional. Pair with subject_id. Only Assumption or Task are allowed. |
List notes/papers from AI Note. Supports keyword search across title and content, category filtering, pagination, and sorting. Returns id/title/content_preview/category_id/created_at. Use this when the user asks 'find my note about X', 'what did I write on Y', or wants to search their knowledge base.
| Argument | Type | Description |
|---|---|---|
| limit | number | Maximum number of papers to return (default: 25, max: 200) |
| offset | number | Number to skip for pagination (default: 0) |
| search | string | Keyword to match in paper title or content (case-insensitive partial match) |
| sort_by | string | Sort field (default: updated_at) |
| sort_order | string | Sort order (default: desc) |
| category_id | string | Filter by category UUID |
| source_type | string | Filter by source type (e.g., 'manual', 'youtube', 'url', 'clipboard') |
| paper_category_id | string | Filter by paper-specific folder UUID |
Read agent memory. action: get | search (consolidated surface — same handlers as the legacy tools) Required per action — get: source | search: query.
| Argument | Type | Description |
|---|---|---|
| id | string | [get] Item ID returned by memory_search |
| hop | integer | [search] Expand results by following [[wikilinks]] N hops from keyword hits (vault only). Scores decay 0.5 per hop. Default 0 (off). |
| mode | string | [search] keyword (default) or latest_state |
| path | string | [get] vault_file only: file path inside the vault (e.g. 'global/MEMORY.md') |
| limit | integer | [search] Max results (default 10, max 20) |
| query | string | [search] Search text (e.g. 'render deploy key', 'ainote jwt'). May be blank only in latest_state mode. |
| action* | string | which operation to run |
| source | string | [get] Which store the item lives in (use the `source` field from memory_search results) |
| sources |
List the authenticated user's projects (L2 intent layer), or fetch one by id. Each entry includes riskiest_assumption — the single highest-priority unverified assumption (or, if all are verified, the highest-criticality one).
| Argument | Type | Description |
|---|---|---|
| id | string | Fetch a single project by UUID. Omit to list all. |
| status | string | Filter by status. Omit to list all statuses. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"id": {
"type": "string",
"description": "Fetch a single project by UUID. Omit to list all."
},
"status": {
"enum": [
"active",
"paused",
"archived"
],
"type": "string",
"description": "Filter by status. Omit to list all statuses."
}
}
}Create or update a project (L2 intent layer). Omit `id` to create, pass it to update. There is no delete tool — use status: 'archived' to retire a project.
| Argument | Type | Description |
|---|---|---|
| id | string | Project UUID to update. Omit to create a new project. |
| name | string | Project name (required to create). |
| intent | string | Why this project exists, in one paragraph. Pass 'null' to clear. |
| status | string | active | paused | archived. |
| success_criteria | string | What observable outcome counts as success. Pass 'null' to clear. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"id": {
"type": "string",
"description": "Project UUID to update. Omit to create a new project."
},
"name": {
"type": "string",
"description": "Project name (required to create)."
},
"intent": {
"type": "string",
"description": "Why this project exists, in one paragraph. Pass 'null' to clear."
},
"status": {
"enum": [
"active",
"paused",
"archived"
],
"type": "string",
"description": "active | paused | archived."
},
"success_criteria": {
"type": "string",
"description": "What observable outcome counts as success. Pass 'null' to clear."
}
}
}Record the result of a client-side Layer 5 codex review (sync.py merge gate) as a vault_events row. Body of the review is NOT stored — only an HMAC digest of the summary so operators can correlate without exposing review content. Opt-in: server skips writes (still returns success) unless ENV['AINOTE_LAYER5_AUDIT']='on' AND a versioned HMAC secret is configured. Used by the sync.py SessionStart hook + merge command to surface 'why was this blocked?' across multi-PC sessions.
| Argument | Type | Description |
|---|---|---|
| path* | string | Vault path the review covered. |
| summary | string | Free-form codex output summary. Server HMACs this; raw text is NEVER stored. |
| verdict* | string | One of: pass, flag, block, error. |
| chunk_count | integer | Optional: number of diff chunks the client fed to codex. |
| duration_ms | integer | Optional: wall-clock time of the codex review on the client. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"path",
"verdict"
],
"properties": {
"path": {
"type": "string",
"description": "Vault path the review covered."
},
"summary": {
"type": "string",
"description": "Free-form codex output summary. Server HMACs this; raw text is NEVER stored."
},
"verdict": {
"type": "string",
"description": "One of: pass, flag, block, error."
},
"chunk_count": {
"type": "integer",
"description": "Optional: number of diff chunks the client fed to codex."
},
"duration_ms": {
"type": "integer",
"description": "Optional: wall-clock time of the codex review on the client."
}
}
}Delete a file from the primary vault. Optional CAS via base_sha to detect concurrent multi-PC writes. Protected paths (global/memory/, global/skills/, global/planning/, global/intent/, global/claude-config/, handoffs/) require base_sha or explicit force:true (audited). Idempotent: deleting a non-existent path returns success with deleted:false.
| Argument | Type | Description |
|---|---|---|
| path* | string | Relative file path to delete (e.g., 'global/_waf_probe.md'). No leading slash, no '..'. |
| force | boolean | Optional: when true, skip CAS check even if base_sha is supplied. Use only for intentional overwrites; presence is auditable via warning logs. |
| base_sha | string | Optional: git_sha the client last observed for this path. When present, server compares with current git_sha and returns -32009 conflict on mismatch (with current content for 3-way diff). |
| device_id | string | Optional: client device identifier for audit log (e.g., from ~/.claude/ainote-sync/device.id). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"path"
],
"properties": {
"path": {
"type": "string",
"description": "Relative file path to delete (e.g., 'global/_waf_probe.md'). No leading slash, no '..'."
},
"force": {
"type": "boolean",
"description": "Optional: when true, skip CAS check even if base_sha is supplied. Use only for intentional overwrites; presence is auditable via warning logs."
},
"base_sha": {
"type": "string",
"description": "Optional: git_sha the client last observed for this path. When present, server compares with current git_sha and returns -32009 conflict on mismatch (with current content for 3-way diff)."
},
"device_id": {
"type": "string",
"description": "Optional: client device identifier for audit log (e.g., from ~/.claude/ainote-sync/device.id)."
}
}
}Push a markdown file into the primary vault. Optional CAS via base_sha to detect concurrent multi-PC writes (Layer 3 of multi-PC sync plan). WAF-bypass tip: large bodies (~10KB+) that get false-positive blocked at Cloudflare can be sent via `content_b64` (base64-encoded) OR `content` prefixed with `__B64__:` instead. When the push CREATES a new .md file, the response may carry `dedup_candidates` — existing memory files that look similar (keyword + title similarity, no LLM). It is an advisory, not a rejection: YOU decide to merge into one of them (re-push that path), drop this write as a duplicate (skip), or keep it as a new memory (store).
| Argument | Type | Description |
|---|---|---|
| path* | string | Relative file path (e.g., 'global/MEMORY.md', 'launchcrew/firebase.md'). No leading slash, no '..'. |
| force | boolean | Optional: when true, skip CAS check even if base_sha is supplied. Use only for intentional overwrites; presence is auditable via warning logs. |
| content | string | Full file content (markdown text). Alternatively prefix with '__B64__:' followed by base64 payload for WAF bypass. |
| base_sha | string | Optional: git_sha the client last observed for this path. When present, server compares with current git_sha and returns -32009 conflict on mismatch (with current content for 3-way diff). Omit for legacy LWW behavior (response will include a `warning` field nudging clients to upgrade). |
| device_id | string | Optional: client device identifier (e.g., from ~/.claude/ainote-sync/device.id). Stored on FileIndex so future conflict responses can report which device last wrote the path. |
Push MANY files into the primary vault in ONE call. Use this instead of looping sync_push whenever you have more than a couple of changed paths — the cost of sync_push is round trips, not bytes. Per-item semantics are identical to sync_push (Layer 3 base_sha CAS, superseded contract, content_b64 WAF fallback). Partial success is normal and returns HTTP 200: read `results[]` and match entries by `path`, NOT by position. A conflict result always carries `remote_sha` + `conflict_reason` so you can go straight to sync_merge. Request-level failures (duplicate paths in one batch, idempotency key reused with a different payload, >200 items, >4MB) return 4xx and apply NOTHING. Each item MUST carry `content_sha` (SHA1 hex of the decoded body, same algorithm as git_sha) — it powers both the integrity check and the retry-safe `skipped_identical` result. Newly created .md items may carry `dedup_candidates` (same merge/skip/store advisory as sync_push); a batch creating more than 10 new files skips the check entirely and reports `dedup_skipped: "bulk"`.
| Argument | Type | Description |
|---|---|---|
| items* | array | Up to 200 push items, 4MB total, one entry per unique path. Duplicate paths reject the whole batch. |
| device_id | string | Optional batch-level device identifier; per-item device_id overrides it. |
| idempotency_key* | string | REQUIRED. Opaque client-generated UUID, one per batch ATTEMPT-SET: reuse the same key when retrying the same items after a timeout (the server replays the stored results instead of re-applying), and generate a NEW key whenever the items change. Reusing a key with a different payload is a 400 collision, never a silent replay. Do not derive it from paths or content. |
Read-only vault sync operations. action: list | pull | diff | merge | conflicts. Writes (push/delete) are separate tools — they carry CAS semantics. (consolidated surface — same handlers as the legacy tools) Required per action — diff: path | merge: path, base_text, local_text.
| Argument | Type | Description |
|---|---|---|
| path | string | [list] A directory to list (everything under it, e.g. 'global') or an exact file path (e.g. 'global/MEMORY.md'). A trailing slash is ignored. Omit to list from the first page. [pull] Optional filter: a directory (everything under it, e.g. 'global') or an exact file path (e.g. 'global/MEMORY.md'). A trailing slash is ignored. [diff] Relative vault path. [merge] Relative vault path (e.g., 'global/MEMORY.md'). [conflicts] Optional: filter to a specific path. |
| limit | integer | [list] Max entries per page. Default 200, max 1000. [pull] Max files per page. Default 25, max 100. [conflicts] Optional: max rows to return (default 50, max 200). |
| paths | array | [pull] Up to 100 exact file paths fetched in ONE call. Absent paths are listed in `missing`. Cannot be combined with path/since/cursor. |
| since | string | [list] ISO 8601 timestamp. Only list files updated strictly after this time. Deletions are not reported — diff a full list against your local copy to find them. [pull] ISO 8601 timestamp. Only return files updated after this time. Omit to start from the first page. Deletions are not reported. |
| action* | string |
Create or update a task. action: create | update. Deletion is a separate tool (delete_task). (consolidated surface — same handlers as the legacy tools) Required per action — create: content | update: id.
| Argument | Type | Description |
|---|---|---|
| id | string | [update] Task ID (required) |
| notes | string | [create] Additional notes or details for the task [update] Update notes (or 'null' to clear) |
| action* | string | which operation to run |
| content | string | [create] Task content (required) [update] New task content |
| due_date | string | [create] Due date in ISO format (e.g., '2026-01-28' or '2026-01-28T15:00:00+09:00') [update] Update due date in ISO format (or 'null' to clear) |
| due_time | string | [create] Due time in HH:MM format (e.g., '15:00'). If not provided, task is treated as all-day. [update] Update due time in HH:MM format (or 'null' to make all-day) |
| location | string | [create] Location where the task takes place [update] Update location (or 'null' to clear) |
| is_all_day | boolean |
Read tasks and task categories. action: tasks | categories (consolidated surface — same handlers as the legacy tools)
| Argument | Type | Description |
|---|---|---|
| limit | number | [tasks] Maximum number of tasks to return (default: 25, max: 500) |
| action* | string | which operation to run |
| offset | number | [tasks] Number of tasks to skip for pagination (default: 0). Use with limit to fetch additional pages. |
| search | string | [tasks] Search keyword in task content and notes (case-insensitive partial match) |
| status | string | [tasks] Filter by task status |
| overdue | boolean | [tasks] Filter overdue incomplete tasks (due_date < today) |
| sort_by | string | [tasks] Sort field (default: created_at) |
| location | string | [tasks] Filter by location (partial match, e.g., '여의도', '서울') |
| due_today | boolean |
Create a new private vault as a GitHub repository under the user's account. Requires the user to have completed the GitHub App install flow first.
| Argument | Type | Description |
|---|---|---|
| name* | string | Vault display name. The GitHub repo name (slug) is derived from it unless `slug` is given. |
| slug | string | Optional explicit repo slug (ASCII lowercase-hyphen). Required when `name` has no ASCII letters or digits — e.g. a Korean-only name — because GitHub repo names are ASCII-only. |
| description | string | Optional GitHub repo description. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Vault display name. The GitHub repo name (slug) is derived from it unless `slug` is given."
},
"slug": {
"type": "string",
"description": "Optional explicit repo slug (ASCII lowercase-hyphen). Required when `name` has no ASCII letters or digits — e.g. a Korean-only name — because GitHub repo names are ASCII-only."
},
"description": {
"type": "string",
"description": "Optional GitHub repo description."
}
}
}Read vault metadata. action: list | clone | status (consolidated surface — same handlers as the legacy tools) Required per action — clone: name.
| Argument | Type | Description |
|---|---|---|
| name | string | [clone] Vault name or slug. |
| action* | string | which operation to run |
| target_path | string | [clone] Preferred local clone path for the returned instructions. |
| include_commit_sha | boolean | [list] Include each vault's latest_commit_sha (default false). Costs one GitHub API round-trip per vault, so only ask for it when you actually need the remote HEAD. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"name": {
"type": "string",
"description": "[clone] Vault name or slug."
},
"action": {
"enum": [
"list",
"clone",
"status"
],
"type": "string",
"description": "which operation to run"
},
"target_path": {
"type": "string",
"description": "[clone] Preferred local clone path for the returned instructions."
},
"include_commit_sha": {
"type": "boolean",
"description": "[list] Include each vault's latest_commit_sha (default false). Costs one GitHub API round-trip per vault, so only ask for it when you actually need the remote HEAD."
}
},
"additionalProperties": true
}Wrapper around vault file sync. action=list|pull|push to work against the primary vault. list/pull are paginated — narrow with `path`/`since` and follow `next_cursor` instead of pulling the whole vault. To fetch specific files, pull with `paths` (up to 100 exact paths, one call). For push: WAF-bypass via `content_b64` or `content: '__B64__:...'` prefix (mirrors sync_push).
| Argument | Type | Description |
|---|---|---|
| path | string | push: target file path. list/pull: a directory (everything under it, e.g. 'global') or an exact file path (e.g. 'global/MEMORY.md'); a trailing slash is ignored. |
| limit | integer | Max entries per page. list: default 200, max 1000. pull: default 25, max 100. |
| paths | array | pull only: up to 100 exact file paths fetched in ONE call. The response lists absent paths in `missing`. Cannot be combined with path/since/cursor. |
| since | string | ISO 8601 timestamp for incremental list/pull (files updated strictly after it). Deletions are not reported — compare a full list against your local copy to find them. |
| action | string | Sync action. Default list. |
| cursor | string | Pass `next_cursor` from the previous response to fetch the next page. |
| content | string |
| string |
| [create] Memory update semantics: 'state' = latest value replaces the past (used by memory_search latest_state mode), 'event' = immutable accumulating log, 'preference' = user preference. Omit for plain documents. [update] Set the memory update semantics (state | event | preference). Pass null/empty to clear. |
| content_type | string | [create] Content type. Auto-detected from title extension if omitted. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"id": {
"type": "string",
"description": "[update] Document UUID"
},
"mode": {
"enum": [
"replace",
"append",
"prepend"
],
"type": "string",
"description": "[update] Update mode (default: replace)"
},
"title": {
"type": "string",
"description": "[create] Filename as title (e.g., ainote-memory.md, project-claude.md) [update] Document title"
},
"action": {
"enum": [
"create",
"update"
],
"type": "string",
"description": "which operation to run"
},
"content": {
"type": "string",
"description": "[create] Full file content (markdown, json, yaml, text) [update] New content"
},
"category": {
"type": "string",
"description": "[create] Subcategory: memory | claude | cursor | env | docs | custom. Default: docs [update] Subcategory to disambiguate title"
},
"local_path": {
"type": "string",
"description": "[create] Absolute path on this machine (e.g., ~/.claude/projects/-Users-seunghan/memory/MEMORY.md). Used by pull_dev_docs to restore file on other devices. Supports ~ expansion. [update] Update the local file path for multi-device sync"
},
"memory_type": {
"enum": [
"state",
"event",
"preference"
],
"type": "string",
"description": "[create] Memory update semantics: 'state' = latest value replaces the past (used by memory_search latest_state mode), 'event' = immutable accumulating log, 'preference' = user preference. Omit for plain documents. [update] Set the memory update semantics (state | event | preference). Pass null/empty to clear."
},
"content_type": {
"enum": [
"markdown",
"json",
"yaml",
"text"
],
"type": "string",
"description": "[create] Content type. Auto-detected from title extension if omitted."
}
},
"additionalProperties": true
}| include_stale | boolean | [devices] |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"op": {
"enum": [
"push",
"install",
"uninstall",
"reconcile",
"migrate",
"rollback"
],
"type": "string",
"description": "[txn_pull]"
},
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "[txn_pull]"
},
"action": {
"enum": [
"devices",
"drift",
"status",
"txn_pull"
],
"type": "string",
"description": "which operation to run"
},
"cursor": {
"type": "string",
"description": "[txn_pull]"
},
"device_id": {
"type": "string",
"description": "[devices] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [drift] [status] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [txn_pull] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH)."
},
"since_hlc": {
"type": "string",
"description": "[txn_pull]"
},
"include_stale": {
"type": "boolean",
"default": true,
"description": "[devices]"
}
},
"additionalProperties": true
}| installed_hooks | array | [heartbeat] |
| enrollment_token | string | [enroll] |
| installed_skills | array | [heartbeat] |
| target_device_id | string | [request_share] |
| claude_code_version | string | [heartbeat] |
| installed_hooks_hash | string | [heartbeat] |
| installed_skills_hash | string | [heartbeat] |
| installed_mcp_servers_hash | string | [heartbeat] |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"os": {
"type": "string",
"maxLength": 32,
"description": "[heartbeat]"
},
"alias": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "[enroll]"
},
"action": {
"enum": [
"enroll",
"request_share",
"heartbeat"
],
"type": "string",
"description": "which operation to run"
},
"device_id": {
"type": "string",
"description": "[enroll] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [request_share] The existing device performing the re-encryption (must own access to all secret_node_ids). [heartbeat]"
},
"age_pubkey": {
"type": "string",
"pattern": "^age1[a-z0-9]{58}$",
"description": "[enroll]"
},
"client_hlc": {
"type": "string",
"description": "[request_share]"
},
"capabilities": {
"type": "object",
"properties": {
"os": {
"type": "string",
"maxLength": 32
},
"transports": {
"type": "array",
"items": {
"type": "string"
}
},
"claude_code_version": {
"type": "string",
"maxLength": 32
}
},
"description": "[enroll]",
"additionalProperties": true
},
"secret_shares": {
"type": "array",
"items": {
"type": "object",
"required": [
"secret_node_id",
"re_encrypted_ciphertext_b64"
],
"properties": {
"secret_node_id": {
"type": "string",
"format": "uuid"
},
"re_encrypted_ciphertext_b64": {
"type": "string",
"maxLength": 1048576,
"minLength": 1
}
},
"additionalProperties": false
},
"minItems": 1,
"description": "[request_share]"
},
"installed_hooks": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128,
"minLength": 1
},
"maxItems": 2000,
"description": "[heartbeat]"
},
"enrollment_token": {
"type": "string",
"minLength": 16,
"description": "[enroll]"
},
"installed_skills": {
"type": "array",
"items": {
"type": "string",
"maxLength": 128,
"minLength": 1
},
"maxItems": 2000,
"description": "[heartbeat]"
},
"target_device_id": {
"type": "string",
"format": "uuid",
"description": "[request_share]"
},
"claude_code_version": {
"type": "string",
"maxLength": 32,
"description": "[heartbeat]"
},
"installed_hooks_hash": {
"type": "string",
"maxLength": 64,
"minLength": 64,
"description": "[heartbeat]"
},
"installed_skills_hash": {
"type": "string",
"maxLength": 64,
"minLength": 64,
"description": "[heartbeat]"
},
"installed_mcp_servers_hash": {
"type": "string",
"maxLength": 64,
"minLength": 64,
"description": "[heartbeat]"
}
},
"additionalProperties": true
}| string |
| [list] |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"alias": {
"type": "string",
"maxLength": 128,
"description": "[get]"
},
"limit": {
"type": "integer",
"default": 100,
"maximum": 500,
"minimum": 1,
"description": "[list]"
},
"action": {
"enum": [
"get",
"list"
],
"type": "string",
"description": "which operation to run"
},
"cursor": {
"type": "string",
"description": "[list] Opaque cursor (created_at ISO + id) from previous page's next_cursor"
},
"node_id": {
"type": "string",
"format": "uuid",
"description": "[get]"
},
"device_id": {
"type": "string",
"description": "[get] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH). [list] Optional calling device id for audit attribution. The X-Ainote-Device-Id header is the canonical source; this argument is the backwards-compatible fallback for clients that predate the header. If both are sent and disagree, the request is rejected (DEVICE_MISMATCH)."
},
"node_type": {
"enum": [
"skill",
"hook",
"mcp_server",
"secret",
"setting_fragment"
],
"type": "string",
"description": "[get] [list]"
},
"updated_since_hlc": {
"type": "string",
"description": "[list]"
}
},
"additionalProperties": true
}| project | string | [get] Project slug used at handoff_save time. [list] Optional project filter, e.g. 'logi' to list only logi-* handoffs. |
| task_type | string | [list] v2 only: filter by frontmatter task_type (e.g. 'feature', 'bugfix', 'refactor', 'research', 'ops'). |
| has_blockers | boolean | [list] v2 only: filter by frontmatter has_blockers (true/false). |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"date": {
"type": "string",
"description": "[get] Optional YYYY-MM-DD. Omit for latest."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "[list] v2 only: AND match — entries must contain ALL given tags. Example: ['auth', 'mobile']."
},
"time": {
"type": "string",
"description": "[get] Optional HHMM (24h, KST) — same value passed at save time. Required to disambiguate when multiple handoffs share the same project/topic/date."
},
"limit": {
"type": "integer",
"description": "[list] Max entries to return (default 50)."
},
"since": {
"type": "string",
"description": "[list] ISO8601 timestamp — only entries with updated_at >= since are returned."
},
"topic": {
"type": "string",
"description": "[get] Topic slug used at handoff_save time."
},
"action": {
"enum": [
"get",
"list"
],
"type": "string",
"description": "which operation to run"
},
"status": {
"type": "string",
"description": "[list] v2 only: filter by frontmatter status (e.g. 'in_progress', 'paused', 'completed', 'blocked'). v1 handoffs without frontmatter are excluded when this filter is set."
},
"project": {
"type": "string",
"description": "[get] Project slug used at handoff_save time. [list] Optional project filter, e.g. 'logi' to list only logi-* handoffs."
},
"task_type": {
"type": "string",
"description": "[list] v2 only: filter by frontmatter task_type (e.g. 'feature', 'bugfix', 'refactor', 'research', 'ops')."
},
"has_blockers": {
"type": "boolean",
"description": "[list] v2 only: filter by frontmatter has_blockers (true/false)."
}
},
"additionalProperties": true
}| Optional: client device identifier (e.g., from ~/.claude/ainote-sync/device.id). Stored on FileIndex so future conflict responses can report which device last wrote the handoff. Same field sync_push uses. |
| content_b64 | string | Optional: base64-encoded handoff text (use INSTEAD of `content`). For WAF-bypass on large bodies. Server decodes before storing. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"project",
"topic"
],
"properties": {
"date": {
"type": "string",
"description": "Optional override date (YYYY-MM-DD). Defaults to today in server timezone."
},
"time": {
"type": "string",
"description": "Optional time-of-day suffix in HHMM 24h format (KST), e.g. '1555'. Appended to the topic slug so multiple handoffs on the same day stay distinguishable. Omit for once-per-day saves."
},
"topic": {
"type": "string",
"description": "Short topic slug for this handoff, e.g. 'phase-d-port', 'oauth-fix'. Lowercase-hyphen."
},
"content": {
"type": "string",
"description": "Full handoff text. Should follow the session-handoff skill template (현재 상태 / 작업 위치 / 수정 파일 / 결정 사항 / 다음 STEP / 알려진 이슈). WAF-bypass tip: prefix the string with '__B64__:' followed by base64-encoded payload to bypass Render/Cloudflare edge WAF for large bodies (~10KB+) that get false-positive blocked as SQLi/cmd-injection."
},
"project": {
"type": "string",
"description": "Project slug, e.g. 'logi', 'ainote', 'krx_ai'. Lowercase, no spaces."
},
"device_id": {
"type": "string",
"description": "Optional: client device identifier (e.g., from ~/.claude/ainote-sync/device.id). Stored on FileIndex so future conflict responses can report which device last wrote the handoff. Same field sync_push uses."
},
"content_b64": {
"type": "string",
"description": "Optional: base64-encoded handoff text (use INSTEAD of `content`). For WAF-bypass on large bodies. Server decodes before storing."
}
}
}{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"kind",
"summary"
],
"properties": {
"kind": {
"enum": [
"approval",
"evidence",
"verdict"
],
"type": "string",
"description": "approval | evidence | verdict."
},
"summary": {
"type": "string",
"description": "One sentence: what is being judged (required)."
},
"evidence": {
"type": "object",
"description": "Free-form evidence attached to the request — links, numbers, diff summaries."
},
"risk_tier": {
"enum": [
1,
2,
3
],
"type": "integer",
"description": "1=destructive 2=reversible 3=informational. Default: 2."
},
"project_id": {
"type": "string",
"description": "Optional project this judgment relates to."
},
"subject_id": {
"type": "string",
"description": "Optional. UUID of the Assumption or Task this judgment is about."
},
"subject_type": {
"enum": [
"Assumption",
"Task"
],
"type": "string",
"description": "Optional. Pair with subject_id. Only Assumption or Task are allowed."
}
}
}{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "number",
"description": "Maximum number of papers to return (default: 25, max: 200)"
},
"offset": {
"type": "number",
"description": "Number to skip for pagination (default: 0)"
},
"search": {
"type": "string",
"description": "Keyword to match in paper title or content (case-insensitive partial match)"
},
"sort_by": {
"enum": [
"created_at",
"updated_at",
"title"
],
"type": "string",
"description": "Sort field (default: updated_at)"
},
"sort_order": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "Sort order (default: desc)"
},
"category_id": {
"type": "string",
"description": "Filter by category UUID"
},
"source_type": {
"type": "string",
"description": "Filter by source type (e.g., 'manual', 'youtube', 'url', 'clipboard')"
},
"paper_category_id": {
"type": "string",
"description": "Filter by paper-specific folder UUID"
}
}
}| array |
| [search] Memory stores to search. Default: both. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"id": {
"type": "string",
"description": "[get] Item ID returned by memory_search"
},
"hop": {
"type": "integer",
"maximum": 2,
"minimum": 0,
"description": "[search] Expand results by following [[wikilinks]] N hops from keyword hits (vault only). Scores decay 0.5 per hop. Default 0 (off)."
},
"mode": {
"enum": [
"keyword",
"latest_state"
],
"type": "string",
"description": "[search] keyword (default) or latest_state"
},
"path": {
"type": "string",
"description": "[get] vault_file only: file path inside the vault (e.g. 'global/MEMORY.md')"
},
"limit": {
"type": "integer",
"description": "[search] Max results (default 10, max 20)"
},
"query": {
"type": "string",
"description": "[search] Search text (e.g. 'render deploy key', 'ainote jwt'). May be blank only in latest_state mode."
},
"action": {
"enum": [
"get",
"search"
],
"type": "string",
"description": "which operation to run"
},
"source": {
"enum": [
"paper",
"vault_file"
],
"type": "string",
"description": "[get] Which store the item lives in (use the `source` field from memory_search results)"
},
"sources": {
"type": "array",
"items": {
"enum": [
"papers",
"vault"
],
"type": "string"
},
"description": "[search] Memory stores to search. Default: both."
}
},
"additionalProperties": true
}| content_b64 | string | Optional: base64-encoded file content (use INSTEAD of `content`). For WAF-bypass on large bodies. Server decodes before storing. If both supplied, content_b64 wins. |
| supports_superseded | boolean | Optional capability opt-in. When true AND resolves_conflict_id points at an already-resolved conflict whose (path, remote_sha) matches this push, the server returns a NORMAL 200 with resource_data {status:'superseded', reason:'resolved_elsewhere', applied:false, git_sha:<current remote>} instead of a -32009 error, so the client pulls to converge. Omit (default) to keep the legacy -32009 contract. |
| resolves_conflict_id | string | Optional: UUID of a vault_conflicts row this push resolves. When supplied, server validates ownership/unresolved/path/sha and atomically marks the conflict as resolution=clean inside the same transaction as the file_indices write. Validation failure rolls back the whole push and returns -32009 stale_conflict_id. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"path"
],
"properties": {
"path": {
"type": "string",
"description": "Relative file path (e.g., 'global/MEMORY.md', 'launchcrew/firebase.md'). No leading slash, no '..'."
},
"force": {
"type": "boolean",
"description": "Optional: when true, skip CAS check even if base_sha is supplied. Use only for intentional overwrites; presence is auditable via warning logs."
},
"content": {
"type": "string",
"description": "Full file content (markdown text). Alternatively prefix with '__B64__:' followed by base64 payload for WAF bypass."
},
"base_sha": {
"type": "string",
"description": "Optional: git_sha the client last observed for this path. When present, server compares with current git_sha and returns -32009 conflict on mismatch (with current content for 3-way diff). Omit for legacy LWW behavior (response will include a `warning` field nudging clients to upgrade)."
},
"device_id": {
"type": "string",
"description": "Optional: client device identifier (e.g., from ~/.claude/ainote-sync/device.id). Stored on FileIndex so future conflict responses can report which device last wrote the path."
},
"content_b64": {
"type": "string",
"description": "Optional: base64-encoded file content (use INSTEAD of `content`). For WAF-bypass on large bodies. Server decodes before storing. If both supplied, content_b64 wins."
},
"supports_superseded": {
"type": "boolean",
"description": "Optional capability opt-in. When true AND resolves_conflict_id points at an already-resolved conflict whose (path, remote_sha) matches this push, the server returns a NORMAL 200 with resource_data {status:'superseded', reason:'resolved_elsewhere', applied:false, git_sha:<current remote>} instead of a -32009 error, so the client pulls to converge. Omit (default) to keep the legacy -32009 contract."
},
"resolves_conflict_id": {
"type": "string",
"description": "Optional: UUID of a vault_conflicts row this push resolves. When supplied, server validates ownership/unresolved/path/sha and atomically marks the conflict as resolution=clean inside the same transaction as the file_indices write. Validation failure rolls back the whole push and returns -32009 stale_conflict_id."
}
}
}| supports_superseded | boolean | Same capability opt-in as sync_push, applied to every item in the batch. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"items",
"idempotency_key"
],
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"content_sha"
],
"properties": {
"path": {
"type": "string",
"description": "Relative file path. No leading slash, no '..'."
},
"force": {
"type": "boolean",
"description": "Optional per-item CAS bypass (audited). Suppresses the skipped_identical short-circuit."
},
"content": {
"type": "string",
"description": "Full file content. May use the '__B64__:' prefix like sync_push."
},
"base_sha": {
"type": "string",
"description": "Same tri-state semantics as sync_push: omit for legacy LWW, null to assert the path does not exist yet, or the last observed git_sha for CAS."
},
"device_id": {
"type": "string",
"description": "Optional per-item device id; falls back to the batch-level device_id."
},
"content_b64": {
"type": "string",
"description": "Optional base64 body (use INSTEAD of content) for WAF bypass. Wins over content if both are present."
},
"content_sha": {
"type": "string",
"description": "REQUIRED. SHA1 hex of the decoded content. Mismatch fails this item only (status 'error'); equality with the current remote git_sha yields 'skipped_identical' instead of a conflict, which is what makes a crashed batch safe to retry."
},
"resolves_conflict_id": {
"type": "string",
"description": "Optional vault_conflicts UUID this item closes. Requires a non-null base_sha and force=false."
}
}
},
"description": "Up to 200 push items, 4MB total, one entry per unique path. Duplicate paths reject the whole batch."
},
"device_id": {
"type": "string",
"description": "Optional batch-level device identifier; per-item device_id overrides it."
},
"idempotency_key": {
"type": "string",
"description": "REQUIRED. Opaque client-generated UUID, one per batch ATTEMPT-SET: reuse the same key when retrying the same items after a timeout (the server replays the stored results instead of re-applying), and generate a NEW key whenever the items change. Reusing a key with a different payload is a 400 collision, never a silent replay. Do not derive it from paths or content."
},
"supports_superseded": {
"type": "boolean",
"description": "Same capability opt-in as sync_push, applied to every item in the batch."
}
}
}| which operation to run |
| cursor | string | [list] Pass `next_cursor` from the previous response to fetch the next page. [pull] Pass `next_cursor` from the previous response to fetch the next page. |
| base_sha | string | [merge] Optional: git_sha the client believes is the merge base. When supplied, server verifies (informational; merge still runs). |
| base_text | string | [diff] Optional: client's baseline (last-known remote). When supplied along with local_text, response includes both diffs (base → remote, base → local) so the client can render a 3-way view. [merge] Common ancestor text — what the client had cached when it started editing. Source: client state.json baseline. Required. |
| device_id | string | [merge] Optional: client device identifier. Recorded on any vault_conflict row created downstream. |
| local_text | string | [diff] Optional: client's current text. When supplied, response includes unified diff (remote → local). [merge] Current local text on the client (post-edit). Required. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"path": {
"type": "string",
"description": "[list] A directory to list (everything under it, e.g. 'global') or an exact file path (e.g. 'global/MEMORY.md'). A trailing slash is ignored. Omit to list from the first page. [pull] Optional filter: a directory (everything under it, e.g. 'global') or an exact file path (e.g. 'global/MEMORY.md'). A trailing slash is ignored. [diff] Relative vault path. [merge] Relative vault path (e.g., 'global/MEMORY.md'). [conflicts] Optional: filter to a specific path."
},
"limit": {
"type": "integer",
"description": "[list] Max entries per page. Default 200, max 1000. [pull] Max files per page. Default 25, max 100. [conflicts] Optional: max rows to return (default 50, max 200)."
},
"paths": {
"type": "array",
"items": {
"type": "string"
},
"description": "[pull] Up to 100 exact file paths fetched in ONE call. Absent paths are listed in `missing`. Cannot be combined with path/since/cursor."
},
"since": {
"type": "string",
"description": "[list] ISO 8601 timestamp. Only list files updated strictly after this time. Deletions are not reported — diff a full list against your local copy to find them. [pull] ISO 8601 timestamp. Only return files updated after this time. Omit to start from the first page. Deletions are not reported."
},
"action": {
"enum": [
"list",
"pull",
"diff",
"merge",
"conflicts"
],
"type": "string",
"description": "which operation to run"
},
"cursor": {
"type": "string",
"description": "[list] Pass `next_cursor` from the previous response to fetch the next page. [pull] Pass `next_cursor` from the previous response to fetch the next page."
},
"base_sha": {
"type": "string",
"description": "[merge] Optional: git_sha the client believes is the merge base. When supplied, server verifies (informational; merge still runs)."
},
"base_text": {
"type": "string",
"description": "[diff] Optional: client's baseline (last-known remote). When supplied along with local_text, response includes both diffs (base → remote, base → local) so the client can render a 3-way view. [merge] Common ancestor text — what the client had cached when it started editing. Source: client state.json baseline. Required."
},
"device_id": {
"type": "string",
"description": "[merge] Optional: client device identifier. Recorded on any vault_conflict row created downstream."
},
"local_text": {
"type": "string",
"description": "[diff] Optional: client's current text. When supplied, response includes unified diff (remote → local). [merge] Current local text on the client (post-edit). Required."
}
},
"additionalProperties": true
}| [create] Mark as all-day event — suppresses time-of-day rendering. [update] Toggle all-day mode. |
| start_date | string | [create] Start date for date range tasks in ISO format (e.g., for multi-day events) [update] Update start date in ISO format (or 'null' to clear) |
| category_id | string | [create] Category ID (UUID) to organize the task [update] Update category ID (or 'null' to remove category) |
| repeat_rule | string | [create] Recurrence rule (e.g., 'daily', 'weekly', 'monthly', 'yearly', or RRULE format) [update] Update recurrence rule (or 'null' to make non-recurring) |
| travel_time | number | [create] Minutes of travel time to allow before due_date. [update] Update travel-time minutes. |
| completed_at | string | [update] Mark as completed (ISO format) or 'null' to uncomplete |
| is_important | boolean | [create] Mark task as important (default: false) [update] Update important status |
| location_lat | number | [create] GPS latitude. Pair with location_lng. [update] Update GPS latitude. |
| location_lng | number | [create] GPS longitude. Pair with location_lat. [update] Update GPS longitude. |
| reminder_timing | integer | [create] Minutes before due time to send reminder (e.g., 10 for 10 minutes before). Default: 30. Only used when has_notification is true. [update] Minutes before due time to send reminder (e.g., 10 for 10 minutes before). Only used when has_notification is true. |
| has_notification | boolean | [create] Enable reminder notifications (default: false) [update] Enable/disable reminder notifications |
| reminder_timings | array | [create] Multiple reminder offsets in minutes before due time (e.g., [10, 60] fires 10 min and 1 hour before). Overrides reminder_timing if provided. [update] Multiple reminder offsets in minutes before due time (e.g., [10, 60]). Pass empty array [] to clear. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"id": {
"type": "string",
"description": "[update] Task ID (required)"
},
"notes": {
"type": "string",
"description": "[create] Additional notes or details for the task [update] Update notes (or 'null' to clear)"
},
"action": {
"enum": [
"create",
"update"
],
"type": "string",
"description": "which operation to run"
},
"content": {
"type": "string",
"description": "[create] Task content (required) [update] New task content"
},
"due_date": {
"type": "string",
"description": "[create] Due date in ISO format (e.g., '2026-01-28' or '2026-01-28T15:00:00+09:00') [update] Update due date in ISO format (or 'null' to clear)"
},
"due_time": {
"type": "string",
"description": "[create] Due time in HH:MM format (e.g., '15:00'). If not provided, task is treated as all-day. [update] Update due time in HH:MM format (or 'null' to make all-day)"
},
"location": {
"type": "string",
"description": "[create] Location where the task takes place [update] Update location (or 'null' to clear)"
},
"is_all_day": {
"type": "boolean",
"description": "[create] Mark as all-day event — suppresses time-of-day rendering. [update] Toggle all-day mode."
},
"start_date": {
"type": "string",
"description": "[create] Start date for date range tasks in ISO format (e.g., for multi-day events) [update] Update start date in ISO format (or 'null' to clear)"
},
"category_id": {
"type": "string",
"description": "[create] Category ID (UUID) to organize the task [update] Update category ID (or 'null' to remove category)"
},
"repeat_rule": {
"type": "string",
"description": "[create] Recurrence rule (e.g., 'daily', 'weekly', 'monthly', 'yearly', or RRULE format) [update] Update recurrence rule (or 'null' to make non-recurring)"
},
"travel_time": {
"type": "number",
"description": "[create] Minutes of travel time to allow before due_date. [update] Update travel-time minutes."
},
"completed_at": {
"type": "string",
"description": "[update] Mark as completed (ISO format) or 'null' to uncomplete"
},
"is_important": {
"type": "boolean",
"description": "[create] Mark task as important (default: false) [update] Update important status"
},
"location_lat": {
"type": "number",
"description": "[create] GPS latitude. Pair with location_lng. [update] Update GPS latitude."
},
"location_lng": {
"type": "number",
"description": "[create] GPS longitude. Pair with location_lat. [update] Update GPS longitude."
},
"reminder_timing": {
"type": "integer",
"description": "[create] Minutes before due time to send reminder (e.g., 10 for 10 minutes before). Default: 30. Only used when has_notification is true. [update] Minutes before due time to send reminder (e.g., 10 for 10 minutes before). Only used when has_notification is true."
},
"has_notification": {
"type": "boolean",
"description": "[create] Enable reminder notifications (default: false) [update] Enable/disable reminder notifications"
},
"reminder_timings": {
"type": "array",
"items": {
"type": "integer"
},
"description": "[create] Multiple reminder offsets in minutes before due time (e.g., [10, 60] fires 10 min and 1 hour before). Overrides reminder_timing if provided. [update] Multiple reminder offsets in minutes before due time (e.g., [10, 60]). Pass empty array [] to clear."
}
},
"additionalProperties": true
}| [tasks] Filter tasks due today |
| sort_order | string | [tasks] Sort order (default: desc) |
| category_id | string | [tasks] Filter by category UUID |
| due_date_end | string | [tasks] Filter tasks with due_date <= this date (ISO 8601 format) |
| is_important | boolean | [tasks] Filter by important tasks only |
| due_date_start | string | [tasks] Filter tasks with due_date >= this date (ISO 8601 format) |
| created_date_end | string | [tasks] Filter tasks created <= this date (ISO 8601 format) |
| has_notification | boolean | [tasks] Filter by notification enabled status |
| completed_date_end | string | [tasks] Filter tasks completed <= this date (ISO 8601 format) |
| created_date_start | string | [tasks] Filter tasks created >= this date (ISO 8601 format) |
| completed_date_start | string | [tasks] Filter tasks completed >= this date (ISO 8601 format) |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"action"
],
"properties": {
"limit": {
"type": "number",
"description": "[tasks] Maximum number of tasks to return (default: 25, max: 500)"
},
"action": {
"enum": [
"tasks",
"categories"
],
"type": "string",
"description": "which operation to run"
},
"offset": {
"type": "number",
"description": "[tasks] Number of tasks to skip for pagination (default: 0). Use with limit to fetch additional pages."
},
"search": {
"type": "string",
"description": "[tasks] Search keyword in task content and notes (case-insensitive partial match)"
},
"status": {
"enum": [
"pending",
"completed"
],
"type": "string",
"description": "[tasks] Filter by task status"
},
"overdue": {
"type": "boolean",
"description": "[tasks] Filter overdue incomplete tasks (due_date < today)"
},
"sort_by": {
"enum": [
"due_date",
"created_at",
"completed_at",
"updated_at",
"is_important"
],
"type": "string",
"description": "[tasks] Sort field (default: created_at)"
},
"location": {
"type": "string",
"description": "[tasks] Filter by location (partial match, e.g., '여의도', '서울')"
},
"due_today": {
"type": "boolean",
"description": "[tasks] Filter tasks due today"
},
"sort_order": {
"enum": [
"asc",
"desc"
],
"type": "string",
"description": "[tasks] Sort order (default: desc)"
},
"category_id": {
"type": "string",
"description": "[tasks] Filter by category UUID"
},
"due_date_end": {
"type": "string",
"description": "[tasks] Filter tasks with due_date <= this date (ISO 8601 format)"
},
"is_important": {
"type": "boolean",
"description": "[tasks] Filter by important tasks only"
},
"due_date_start": {
"type": "string",
"description": "[tasks] Filter tasks with due_date >= this date (ISO 8601 format)"
},
"created_date_end": {
"type": "string",
"description": "[tasks] Filter tasks created <= this date (ISO 8601 format)"
},
"has_notification": {
"type": "boolean",
"description": "[tasks] Filter by notification enabled status"
},
"completed_date_end": {
"type": "string",
"description": "[tasks] Filter tasks completed <= this date (ISO 8601 format)"
},
"created_date_start": {
"type": "string",
"description": "[tasks] Filter tasks created >= this date (ISO 8601 format)"
},
"completed_date_start": {
"type": "string",
"description": "[tasks] Filter tasks completed >= this date (ISO 8601 format)"
}
},
"additionalProperties": true
}| Required for push (or `content_b64`). Prefix with '__B64__:' for WAF bypass. |
| content_b64 | string | Optional: base64-encoded content (use INSTEAD of `content`) for WAF-bypass on large bodies. Server decodes before storing. |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"path": {
"type": "string",
"description": "push: target file path. list/pull: a directory (everything under it, e.g. 'global') or an exact file path (e.g. 'global/MEMORY.md'); a trailing slash is ignored."
},
"limit": {
"type": "integer",
"description": "Max entries per page. list: default 200, max 1000. pull: default 25, max 100."
},
"paths": {
"type": "array",
"items": {
"type": "string"
},
"description": "pull only: up to 100 exact file paths fetched in ONE call. The response lists absent paths in `missing`. Cannot be combined with path/since/cursor."
},
"since": {
"type": "string",
"description": "ISO 8601 timestamp for incremental list/pull (files updated strictly after it). Deletions are not reported — compare a full list against your local copy to find them."
},
"action": {
"enum": [
"list",
"pull",
"push"
],
"type": "string",
"description": "Sync action. Default list."
},
"cursor": {
"type": "string",
"description": "Pass `next_cursor` from the previous response to fetch the next page."
},
"content": {
"type": "string",
"description": "Required for push (or `content_b64`). Prefix with '__B64__:' for WAF bypass."
},
"content_b64": {
"type": "string",
"description": "Optional: base64-encoded content (use INSTEAD of `content`) for WAF-bypass on large bodies. Server decodes before storing."
}
}
}