Skip to content

JavaScript client

The datanotes package exports a client of the HTTP API with no dependencies. It runs in Node, in browsers and in Obsidian. The MCP server is built on it.

Terminal window
npm install datanotes
import { DatanotesClient } from "datanotes/client";
const db = new DatanotesClient({
url: "http://127.0.0.1:27150",
token: process.env.DATANOTES_TOKEN,
actor: { harness: "my-script" }, // recorded with every write
});
const books = await db.call("select_rows", { table: "book", order_by: "-pages" });
if (books.ok) console.log(books.value.rows);
const r = await db.call("insert_row", { table: "book", values: { title: "Dune", pages: 412 } });
if (!r.ok) console.log(r.value); // why it was refused; nothing was written
  • call(name, args, actor?) runs an operation by name with the same arguments agents use, and answers { ok, value }. A refused write is ok: false with a readable reason.
  • invoke(name, args, actor?) runs an operation, or else the registered app tool of that name, and answers { ok, value, servedBy }.
  • post(route, body, actor?) sends POST /datanotes<route> (e.g. post("/apps/tools/")) and throws on an error status.
  • ping() tells whether the engine answers.
  • Errors that are not answers throw a DatanotesError with kind: unreachable (the engine did not answer), auth (the token was refused) or http (an error status from post, with status).

The catalogue itself is exported too: OPERATIONS (name, description, input schema) and operationRequest(name, args) (the HTTP request behind an operation).

datanotes/local (Node only) finds the engine running on a vault of this machine:

import { localEngine } from "datanotes/local";
const engine = localEngine("/path/to/vault");
// { url, token, lock } while an engine runs on the vault, else null;
// throws when the engine holding the vault runs on another machine

Pass Obsidian’s requestUrl as the transport, so requests are not subject to CORS:

const db = new DatanotesClient({
url, token,
transport: async (req) => {
const r = await requestUrl({ ...req, throw: false });
return { status: r.status, text: r.text };
},
});