@ainote/sdk
v0.1.1
Published
TypeScript SDK for the ainote API — typed REST client for tasks, categories & papers, plus an MCP tool-mirror escape hatch for vault/sync.
Maintainers
Readme
@ainote/sdk
TypeScript SDK for ainote — use ainote as a backend for your own app. Typed REST client for tasks, categories, and papers, plus an MCP tool-mirror escape hatch for vault & file-sync.
- Zero runtime dependencies (uses the built-in
fetch, Node ≥18) - ESM + CJS +
.d.ts - Types generated from the live API spec, so they track the server contract
npm install @ainote/sdkQuickstart
import { AiNote } from '@ainote/sdk';
const ai = new AiNote({ apiKey: process.env.AINOTE_KEY! });
// list (typed Task[])
const { data: tasks } = await ai.tasks.list({ is_important: true });
// create → update → delete
const task = await ai.tasks.create({ content: 'Ship the SDK', due_date: '2026-06-10' });
await ai.tasks.update(task.id, { is_important: true });
await ai.tasks.delete(task.id);
// iterate every task across pages
for await (const t of ai.tasks.listAll()) {
console.log(t.content);
}Two audiences
1. App developers — wrap ainote's typed REST surface (ai.tasks, ai.categories).
Structured JSON in, typed objects out.
2. Sync-tool builders — reach vault & file-sync via the MCP mirror. These tools
return human-readable text plus structured JSON, surfaced honestly as
{ content, text, resources }:
const res = await ai.mcp.call('list_tasks', { limit: 1 });
console.log(res.text); // "Found 1 tasks: ⏳ ..." (human)
// convenience wrappers
await ai.vault.list();
const conflicts = await ai.sync.pendingConflicts();
const [data] = conflicts.resources; // parsed JSON (machine) — use this to build sync engines
await ai.sync.diff({ path: 'global/MEMORY.md' });Auth
One ainote key works for both surfaces — the SDK sends the right header per call
(Bearer for REST, McpKey for MCP):
new AiNote({ apiKey: '<your-key>' }); // both surfaces
new AiNote({ auth: { type: 'bearer', token } }); // REST + MCP (Bearer drives both)
new AiNote({ auth: { type: 'mcpKey', key } }); // MCP onlyGet a key from your ainote account Settings › MCP keys, or mint one with no prior
auth via the onboarding tools (signup_and_get_key / login_and_get_key) — see
docs.ainote.dev/build/auth. Keep it server-side —
it's account-wide access; never ship it in a browser bundle.
Error handling
Non-2xx responses throw typed errors you can branch on:
import { AuthError, NotFoundError, ValidationError, RateLimitError } from '@ainote/sdk';
// sleep() and reauth() below are your app's helpers, not SDK exports.
try {
await ai.tasks.create({ content: '' });
} catch (e) {
if (e instanceof ValidationError) console.log(e.fieldErrors);
else if (e instanceof RateLimitError) await sleep(e.retryAfterMs ?? 1000);
else if (e instanceof AuthError) reauth();
}Retries: only idempotent requests (GET/HEAD/DELETE) are auto-retried on
429/5xx with backoff (configurable via maxRetries). Writes (create/update/publish)
and MCP calls are never retried — retrying a committed POST/PATCH could duplicate data.
MCP errors: MCP-mirror tool failures (ai.mcp / ai.vault / ai.sync) arrive as
JSON-RPC errors inside an HTTP 200 body. The SDK still throws AiNoteApiError, but
with status === 200 and code set to the JSON-RPC error code — so branch on the error
class, not on e.status >= 400.
Options
new AiNote({
apiKey,
baseUrl, // default https://api.ainote.dev
userAgent, // default @ainote/sdk/<version>
maxRetries, // default 2
fetchImpl, // inject a custom fetch (tests/proxies)
});Status
v0.1 covers tasks, categories, and papers (typed REST) plus the vault/sync MCP
mirror (text + structured resources). Not yet wrapped: a signup/login onboarding
helper and "Sign in with logi" SSO (planned). See
docs.ainote.dev/build.
License
MIT
