Repository navigation
Conversation
`@tomic/mcp` (browser/mcp) is a stdio MCP server for Claude Code, Claude Desktop, Cursor and other clients. It runs locally and signs every edit with the user's own Agent key, so writes are ordinary signed commits. Tools: list_drives, get_resource (documents and meetings include their text), search, semantic_search, query, get_user_classes, get_schema, create_resource, edit_resource and delete_resource (never a whole drive). The data verbs behind those tools now live in @tomic/lib (assistant-tools.ts), and the in-app assistant calls the same functions, so there is one implementation behind both surfaces. The compact JSON-AD dialect, short subject refs and class helpers move from the data-browser to @tomic/lib for the same reason. Reads no longer include the genesis certificate, which is long and tells a model nothing. This is steps 1 and 2 of planning/mcp-endpoint.md; a hosted endpoint with OAuth (for claude.ai connectors) is not part of this change.
atomic-mcp now makes a key on the machine it runs on and prints a link. In the app (/app/connect-agent) the person picks which drives it may reach and whether it may edit, then clicks Allow. Account settings lists connected apps with a Revoke button. Only an agent may edit its own Agent resource, so the grant is not recorded there: the ACLs on the shared drives are the record. The app adds the key to read (and write), the MCP finds its access by searching for resources that list it, and revoking removes it everywhere, including from what it created. The app remembers which apps were connected on the private drive, so settings can list them. ATOMIC_AGENT_SECRET still works for scripts and CI.
Any app that makes its own key (a CLI, a script, the MCP server) can now ask for access the same way: connectAgentUrl builds the approval link, optionally asking for edit rights or specific resources, waitForGrant polls until the person clicks Allow, and publishAgentName sets the name shown under Connected apps. The approval page honours the write and target hints as preselections; the person still decides. The MCP server now uses these instead of its own copies.
atomic-cli connect makes a key on this machine, prints the approval link (/app/connect-agent), waits for Allow and writes the config. Without a config the CLI now points there instead of asking for the secret. ad-generate connect does the same for the codegen CLI, so private ontologies no longer need an agent secret in atomic.config.json, which often ends up in a repository. The key file helper moves from the MCP server into @tomic/lib/node (loadOrCreateLocalAgent), shared by both. Also adds the new screens' strings to the translation catalogs.
POST /mcp (Streamable HTTP) for clients that cannot run a local process. The node is the OAuth 2.1 authorization server (dynamic client registration, PKCE S256, RFC 9728 and 8414 metadata) with stateless signed tokens derived from the node key. The person approves in the app at /app/authorize-mcp, which gives a fresh issued agent read rights on the drives they pick, so revoking is removing it under Connected apps. The node never signs as the person. Tools: list_drives, get_resource (with document text) and search.
Signup, the Share dialog and the invite accept card each had their own name and picture form. They now share ProfileForm. The invite's 'Agent created!' dialog reuses the signup SecretStep, so the secret is shown and confirmed the same way everywhere.
…ate cargo build-dir scripts/run-branch.sh builds and serves any branch in its own worktree and target dir (optionally behind a cloudflared https tunnel for phones and tablets). build.rs now decides whether to rebuild the frontend from a content hash instead of mtimes, so a fresh checkout or branch switch with identical sources no longer pays minutes for a rebuild, and prints a line on the terminal before the silent step. The wasm-pack cargo gets a private CARGO_BUILD_BUILD_DIR: a global build-dir is shared by every cargo and one running cargo run made every other build block silently at 0% CPU.
Portal consent and the integration proxy need to know which account an agent belongs to. The only link so far was a sync enrollment, which needs a drive and a paid plan or an invite, so a Free account was never recognised. atomic-saas now links an agent to an account without a drive (#138); this is the app's side of it. Once the identity reconcile gate has settled on the identity the account uses, it links that identity in the background, next to the device directory: GET /api/agent-link, and when the account has no link or another agent's, a challenge from POST /api/agent-link/challenge, signed with createAuthentication like the enrollment proof, sent back to POST /api/agent-link. One attempt per account and identity per session, and it never throws. Keys are compared in any spelling (both base64 alphabets, padded or not), because the control plane answers the canonical unpadded base64url. An account service without agent links, an unconfirmed email and an identity linked to another account are left alone. A refused proof or a challenge for another agent is reported to Sentry, since only a bug produces either. Builds without an account service make no requests.
A checkbox sits left of each row number (on hover, always on touch). Once a row is ticked every row shows one and the toolbar next to the filter button shows the count with a delete button. Undo restores the whole batch.
Right-click a row's number for its resource menu. Right-clicking inside a shift+click or drag selection opens a menu that clears the values, sets one value in all of them, or deletes the rows they touch (one undo step). View tabs can be dragged to reorder them.
…r error toasts, fix stale rows after deleting a typed row
…ders in the account menu; undo the top bar change
Two people can message each other in a conversation only they can read. The server that hosts it stores ciphertext; it still checks every write, because the rights and signatures it needs stay readable. - atomic_lib::conversation: an agent's X25519 encryption key, derived from its vault proof so every device has the same one; a keyring with one key per epoch, wrapped to each member; sealing and opening messages, bound to the conversation; report-by-reveal of a single message key. WASM exports for the browser. - Ontology `conversations`: Conversation, SealedMessage, encryptionKey, conversationKeys, sealed, conversations. - GET /conversations lists the conversations on the server the requester is in, so the other person finds one they did not start. - A conversation is its own drive with read + append for its members and no writer, so nobody can change what someone else said. - App: Messages section in the sidebar, "Message" on every avatar, a New message dialog, and the conversation page, which opens each message before ChatView shows it. Store.subscribeLive (as in #1949) keeps the conversation live. The app publishes the agent's key at start.
The managed passkey calls relied on a ceremony cookie that a linked device never keeps, so its finish call failed. The start response now carries the ceremony id and the finish call returns it in X-Passkey-Ceremony. The portal still reads the cookie when the header is absent.
Undo drained and rewrote every stroke in the Loro list and then serialized up to 400 full stroke snapshots to localStorage, synchronously, per press. replaceListItems now deletes and inserts only the changed middle of the list, and the canvas writes its undo history once things settle (and on pagehide / close).
The hosted distribution (VITE_ATOMIC_HOSTED_DISTRIBUTION) is called atomic.place in the tab, description, splash caption, PWA manifest and user-facing copy; a build from source keeps the neutral name AtomicServer. Settings, account, sync and the other app pages now get their own title (for example Settings · atomic.place) instead of the bare product name.
A pasted atomic:, did:ad: or http(s) subject is now a pick of its own, and the agents with direct rights on the drive are offered next to the search results, since their data is not in the drive and search never finds them.
Shows what the vault's storage is made of and how much can be freed, keeping the part held only for undo separate and behind its own explicit button.
GET /drive-usage/breakdown returns every resource in a drive with its edit history and file bytes. The app draws them as a squarified treemap with drill-down and a sorted list, reachable from the sync page.
# Conflicts: # browser/CHANGELOG.md
# Conflicts: # CHANGELOG.md # browser/CHANGELOG.md # browser/data-browser/src/locales/de.po # browser/data-browser/src/locales/en.po # browser/data-browser/src/locales/es.po # browser/data-browser/src/locales/fr.po
A browser kept an old identity's encrypted backup next to the account's newer one, so the unlock screen listed the same address twice and only one of them opened. Caching a backup now drops older ones of the same account (the server holds one per account). Where an address still appears twice, each choice says when it was saved and which identity it is, and every choice can be forgotten on this device.
The consent page needs the identity the desktop app holds, which a browser on the same machine does not. connect --desktop opens atomic://authorize-mcp, the app routes it to the consent page, and after Allow it tells the loopback listener and stays in the app instead of navigating away.
A node moving from one domain to another has to answer under both while links to the old one are still around. The flag took a single suffix.
The identity the app has unlocked now signs the account in when it has no session (the portal's secret sign-in), so nobody is signed in on one side and out on the other. Sign-out goes through one helper that ends the account session and removes the key, and a new /app/sign-out route lets the account portal sign out through the app.
…ola/atomic-server into combined/2026-10-05
…ic-server into combined/2026-10-05
…ola/atomic-server into combined/2026-10-05
…ola/atomic-server into combined/2026-10-05
…ola/atomic-server into combined/2026-10-05
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Before: about fifteen open PRs, each on its own branch.
After: one branch with all of them, for testing on one machine. It is batch 2 plus #1870, #1969, #2056, #2057, #2058, #2065, #2066, #2068, #2070, #2071, #2073 and #2074, merged onto develop.
How: PR heads were merged one by one; conflicts were only in changelogs, catalogs (re-extracted with wuchale), imports and
tableAggregates.test.ts(kept both). Not included: the plugin stack (#1702 to #1788), #2045, #2072 and the Michiel PRs with stacked bases.Generated by Claude Code