Optimising the API and MCP server for agentic clients #1185
Replies: 2 comments
|
Hi, Thanks a lot for this write-up, and for the kind words about Poznote and the MCP server. It's great to hear it's being driven daily by automated workflows at this scale. This is a really well-argued proposal, and the general observation makes sense to me. The API was designed around the UI's one-note-at-a-time model, and agentic clients clearly have a different cost profile. I want to take the time to look at each of the three points properly against the current architecture before answering in detail or changing anything. So I'll come back to this discussion once I've done that. At first glance the optimistic concurrency one does look like the best effort-to-value ratio, as you suggested. I might implement this one first and take some more time to study both other points. Thanks again! 😊 |
|
https://github.com/timothepoznanski/poznote/releases/tag/6.43.0 |
Uh oh!
There was an error while loading. Please reload this page.
My setup
First, thanks for Poznote. I run a self-hosted instance as my daily second brain: several hundred notes across workspaces, a structured folder hierarchy and tags. It is driven from Claude Desktop through the built-in MCP server, and an unattended job runs every evening to triage newly captured notes, update running documents and file them into the right folders. Around fifteen documented workflows depend on it.
The MCP server is one of the reason I chose Poznote over the alternatives, and it is genuinely good. This issue is not a complaint list. I would like to open a conversation about one specific question: what would it take for the API to be as good for automated clients as it already is for the UI?
I am happy to split this into separate enhancement, to start with just one of the three, or to drop any of them if they conflict with where you want the project to go. I would rather understand what is realistic given the architecture than push a wish list.
The general observation
The current API is a clean per-item CRUD surface, which is the right design for a UI where a human clicks one note at a time. Agentic clients have a different cost model. Their scarce resources are not clicks but round trips and context tokens, and the current surface makes both expensive in ways that are invisible from the UI side.
Three primitives would close most of that gap. They are independent, they can land in any order, and each is useful on its own.
1. Partial writes
What.
append,prependandreplaceoperations on an existing note, in addition to the current full-contentupdate_note.Why. Today, appending one line to a 3,000-token note costs a full read plus a full write, so roughly 6,000 tokens to add 15. That is the cost argument, and it is the smaller one.
The real problem is correctness. Because the model has to regenerate the entire note body to send it back, any truncation or reformatting during regeneration silently overwrites content the user never intended to touch. Partial writes remove that failure mode completely, because untouched content is never transmitted.
Suggested contract.
replaceshould require exactly one match ofold_stringand reject zero or multiple matches with a clear error rather than guessing. Callers then widen the string with surrounding context until it is unique. An emptynew_stringgives deletion for free.Rough weight. Moderate. The awkward part is format handling: for HTML notes,
appendneeds to insert inside the content container rather than concatenate after the closing tag.2. Batch operations
What. A multi-get and a multi-write.
Why. Processing 40 inbox notes currently requires 40 separate round trips. That costs latency, but more importantly each call and response pair is appended to the model's context, so protocol overhead crowds out the content the model actually needs to reason about. A long sequence is also far more likely to be interrupted partway with no clean way to know where it stopped.
Suggested contract. Non-transactional, continue on error, one result entry per submitted operation with index and status. Agents recover much better from partial success with a report than from an all-or-nothing rollback. Cap the batch at something like 100 operations.
Rough weight. Moderate for the generic form. If that feels too heavy, a narrower alternative would work almost as well: one or two composite operations covering the common bulk cases, for example a single call that moves and retags a list of notes. Fewer, higher-level tools reduce round trips just as effectively as a generic batch envelope, and are considerably less work to specify.
3. Optimistic concurrency (version tokens)
What. Return a version identifier with a note, and let writes declare which version they expect.
Why. This is the one that worries me most in practice and the one that is least visible. I have a scheduled job writing to notes in the evening while I may have an interactive session open against the same instance. Today a write always wins, so the later write silently discards the earlier one. There is no error, no conflict, no trace. I only find out when content is missing.
Suggested contract. The 409 response should include the current content so the caller can merge and retry in a single turn rather than issuing another read. Standard HTTP
ETagandIf-Matchsemantics would be perfectly idiomatic here.Rough weight. Low, most likely. If notes already carry an
updated_attimestamp, that value can serve as the token with almost no schema change. This is probably the best effort-to-value ratio of the three.I run this daily against a real instance with real volume, so I am glad to test a branch before release and report back precisely.
All reactions