Connecting an AI assistant

Give Claude, Claude Code, Cursor or your own code the answers Louvain gives you — limited to exactly what you can see — and, if you allow it, a place to save what they write for you.

Louvain is an MCP server. Paste one URL into your assistant, approve once in the browser, and it can ask Louvain anything you could ask it yourself.

There is no key to copy and nothing to keep secret. The assistant registers itself, sends you to Louvain to approve, and gets a credential bound to you — in the workspace you approved it from.

The promise

An assistant connection is not a service account. It is you, narrowed.

  • It sees exactly what you see. The same permission fence, the same sources. Not your colleagues', not the workspace's. If a document is invisible to you, it is invisible to your assistant, and no wording in a prompt changes that.
  • It reads, and saves files only if you allow it. It cannot change a setting, connect a source, invite anyone or share anything — even if you are an owner. (If you let it save, then where you are an editor it can rename a file or a folder, delete an empty folder, and delete a file that only assistants wrote for you — never one you or anyone else saved a version of. It can rename a collection you are an admin of, and suggest a sharing change for you to confirm.) Your role is deliberately not carried across. If you turn on Save files for you when you connect it, it can also add files where you can add them yourself; see Saving work.
  • It can suggest an edit to a page in your own Notion or Confluence, and never make one. Where your workspace offers editing and you have connected that source as yourself, an assistant allowed to save files can propose a change. Nothing changes at the source until you review the suggestion in Louvain and apply it yourself.
  • You can end it at once. Disconnecting takes effect in that moment, not when a token expires.
  • It ends when your access does. If you leave the workspace, your assistants stop working in the same moment your sessions do.

All four are enforced in the API, not by the assistant's good behaviour.

If an assistant asks to know who you are, the approval screen says so:

  • Know it's you gives it your Louvain id in this workspace. That id is the workspace's short name and the part of your email address before the @, for example acme|dana.
  • See your email address gives it the address you sign in with.

ChatGPT, for example, uses these to let a company allow only accounts on its own domain. Neither reaches anything, and an assistant never gets them unless it asks.

Connect

Find the URL under Connections → Agents → Add agent, then pick your client — its setup shows the URL. It looks like:

https://louvain.example.com/api/mcp

Claude

Settings → Connectors → Add custom connector → paste the URL. Claude opens a Louvain window; approve it there.

Claude Code

claude mcp add --transport http louvain https://louvain.example.com/api/mcp

Then run /mcp inside Claude Code and choose Authenticate. Claude Code receives its approval on http://localhost on whatever port it opens that minute, which Louvain accepts.

Because that address is a port on your own computer, Louvain cannot tell which program is listening on it — any program can register under any name. So Claude Code, and every other command-line client, is shown without a maker's logo: a terminal mark on the approval screen, a neutral mark in the list of connected assistants, and its name marked Name not verified in both. That is expected: continue if you just started the connection yourself.

ChatGPT

Turn on developer mode (Settings → Connectors → Advanced), then Create and paste the URL. Custom connectors need a plan that offers them; a personal free account does not.

Cursor, VS Code, and other MCP clients

Add a remote MCP server over streamable HTTP at the same URL. Cursor: Settings → MCP → Add new MCP server. VS Code: MCP: Add Server from the command palette, then HTTP. Browser-based clients such as the MCP Inspector work too: the endpoint answers any origin without cookies and exposes the WWW-Authenticate header they read to find the sign-in.

Your own code

If your client cannot do OAuth, create a token under Connections → Agents → Your own code and send it as a bearer token to the same URL, or to /v1/answer. It is an assistant credential (louvain_agent_…), not a personal API token: it reads what you can read, can administer nothing whatever your role, reaches only the MCP endpoint and the answer and fact reads, and is listed and disconnected with your other assistants. It does not expire on its own; it ends when you disconnect it, when you leave the workspace, when you reset or change your password, or when an administrator ends it.

Whenever an assistant is connected to your account, by approval or with a token, you are emailed so; if it wasn't you, disconnect it under Connections → Agents. An administrator disconnecting one of yours emails you too. These are security emails, so they cannot be turned off (Emails).

What the approval screen shows

Louvain draws the maker's logo — Claude, ChatGPT, Cursor, VS Code — only when the address your browser returns to after approving is one that maker publishes — exactly one of these, with no subdomains:

  • Claude: claude.ai, claude.com
  • ChatGPT: chatgpt.com, chat.openai.com
  • Cursor: the cursor:// app
  • VS Code: vscode.dev, insiders.vscode.dev, and the vscode:// and vscode-insiders:// apps

The approval screen also names Windsurf (the windsurf:// app), without a logo. The name an assistant gives itself is never enough, because anyone can register one called "Claude". Otherwise it shows what kind of place the approval returns to:

  • A program on this computer (a command-line client such as Claude Code): a terminal mark, with the name marked as not verified.
  • A website or app Louvain does not recognise: a warning. Only continue if you started the connection from there.

The list of connected assistants follows the same rule. It remembers which maker was confirmed when you approved, not where the assistant returns to, so every assistant without a confirmed maker there — a command-line client, an unrecognised website, or an editor Louvain names but draws no logo for, such as Windsurf — has the same neutral mark and Name not verified.

What an assistant can do

Thirteen tools for reading, which are the product's own reads — the same functions the Ask page calls, under the same fence. A collection in the Library is called a space in the tools and their fields (louvain_spaces, spaceId). An assistant you allowed to save files also gets thirty-one more — louvain_spaces, louvain_save, louvain_save_to_space, louvain_upload_url, louvain_save_link, louvain_update, louvain_rename_file, louvain_restore_version, louvain_suggest_knowledge, louvain_saved, louvain_space_files, louvain_create_space, louvain_create_folder, louvain_rename_folder, louvain_delete_folder, louvain_delete_file, louvain_bin, louvain_restore_file, louvain_rename_space, louvain_move_file, louvain_move_folder, louvain_set_reading, louvain_plate_set, louvain_plate_add, louvain_category_add, louvain_discuss_post, louvain_propose_sharing, louvain_propose_reading, louvain_propose_space_delete, louvain_propose_delegation and louvain_propose_tidy — described in Saving work, and, only where editing a source is offered to you, louvain_propose_edit (Editing a source). An assistant that can show apps also gets louvain_open_document, which opens a document for you inside the conversation (Opening a document in your assistant).

Every document a tool names comes with its link. A search result, a source that was read, an answer's evidence, a statement, a file in a collection and the reply to every save carry louvainUrl: the document's link in Louvain, https://<your Louvain address>/app/doc/<id>, which opens it in Louvain's document viewer, over the collection folder or Search view it belongs to. Each tool tells your assistant to give you that link. A save — louvain_save, louvain_save_to_space, louvain_save_link and louvain_update — also returns the file's link as an MCP resource_link (its name, and its type where the file name says it), so an assistant that lists the files a conversation produced can list it beside its own; a client on an MCP version older than 2025-06-18 gets the reply as before. The link is built only from your deployment's configured address, never from anything in the request. It is attached only to what the tool already returned to you, and opening it checks your access again: a document you can't open shows the same "This source isn't available" as one that doesn't exist. A saved file's link names the file and works as soon as the save returns: until Louvain has read the file, the page says it is still being read, then opens the document. If reading takes more than about five minutes, the page stops checking and asks you to open the link again later. Where a result also has url, that is the source's own address in its app (a Google Drive file, a Gmail message), not its page in Louvain.

ToolWhat it is for
louvain_askAsk a question. The answer is verified: every figure, date and name is checked against the source that states it. It reads everything you can see unless client (an organisation's or person's entity id, from louvain_entities) names one: then it answers only from that client's sources — the ones that state the client's verified addresses or domains, the newest 500 — and says so in scope. A client you can't see, or one with nothing you can read, answers as nothing found.
louvain_search_sourcesFind documents, emails, messages and files by meaning or by keyword. It ranks with the same retrieval louvain_ask reads with, and returns documents, each with its title, app, date (modifiedAt) and the excerpt that matched best as excerpt; a source with no readable excerpts (a file read for its metadata) is still found, after those. Results are ranked by relevance, not date. A chat message among the first five results of the first page comes with conversation: the few messages said just before and after it in the same channel or DM (each with offset — negative before, positive after — author, at, text and its sourceId, which opens it; writtenByAgent and instructionLike as on a result), read with the same access as the search, so nothing you could not already read appears. It is there to show what the message answers or refers to; it does not change the order. hasMore: true means more documents ranked below the ones returned, and the result then carries nextCursor: pass it back as cursor, with the same query and filters, for the next page (up to 24 a page). Walking the cursors returns every match you can read exactly once, the best-ranked first and then the rest of the matches; a cursor from a different search is refused. A cursor is sealed: its text reveals nothing about what it points at, and one that was altered is refused. view: "list" lists EVERY match instead, newest first, by title, date, app and author only — no excerpts — up to 100 a page (limit, 50 by default), with total, which is "at least" when totalIsLowerBound is true, and the same nextCursor paging: use it for "every email about X" rather than paging the ranked view. A search that matches by meaning keeps a bounded set of nearest candidates, so its total is "at least" whenever that set or the keyword set was cut. With after or before, those nearest candidates are found inside the date range, so a narrow range still returns what in it matches by meaning; a range of more than 10,000 documents or 50,000 passages is searched by an index scan that may stop short, so its total is then always "at least". With after or before, queryMatched: false (and a note saying so) means nothing in that date range matched the query's words or meaning, and the list is then every source in the range you can read, newest first; its nextCursor keeps listing the range. A call that takes longer than 90 seconds (an answer: its mode's deadline plus 30 seconds) ends with an error asking for a narrower call, never with silence; a call that changes something (a save, an update, a move, a mark on your plate) is told instead that the change may still have been made, so the assistant checks before making it again. after and before (dates), app and space only narrow; asOf searches what had been said by then. A result whose matched passage came from an older version of the document carries matchedVersion; its id opens the current one. space (an id from louvain_spaces) searches only that collection's items. client (an organisation's or person's entity id, from louvain_entities) searches only the sources placed under that client by verified addresses and domains (declared, or the domain of an organisation the workspace corresponds with), never by a name — the same narrowing as the client picker; a client you can't see returns nothing. The ranked view narrows what it found, so a page may come back short; view: "list" lists every source with the client. A collection you can't read returns nothing, the same as one that doesn't exist. Each result carries connectionId, and connectionName where you may see it: "Your Google Drive" for your own connection, the workspace's name for one it shares; another person's own connection is never named.
louvain_driveBrowse your files the way your drive shows them: a place (recent, shared, agents, louvain, starred, suggested, or stores for the connected stores), or one level of a connected store (storeId, and unit for a folder in it). It lists exactly what your own drive lists you — your own stars, the files your own assistants wrote, what you can read — and never another person's. It doesn't list discussions: Needs attention, Discussed and Following stay in Louvain, and its Suggested leaves out what your drive suggests because of a discussion or a follow. A page ends with more and a nextCursor to continue.
louvain_read_sourceRead one document in full — the text Louvain itself read, whole: nothing is cut short on the way to you, and textTruncated: true means only that the source itself was cut when it was read. One source is one record: an email is ONE message, a meeting is the whole recording — summary and every transcript turn — and a document is the whole document. related says what else belongs with it, fenced exactly as the read is, so it never names a source you can't read: related.thread lists the other messages of an email's or a chat's thread (id, title, date, oldest first), and related.versions the other versions of the same document and its copies elsewhere, newest first, each with its date, sameDocument, and newest marking the newest you can read (selfNewest when that is this one). Each list holds at most 20, and more says when there are others. Every id opens with this tool. When sources disagree, the tool tells an assistant to answer with the value the evidence supports and give the others as dated history (see When sources disagree). It takes any id the other tools return: a search result's id, an evidence row's or a statement's sourceId, or the id of a file in a collection (louvain_saved lists the ones your assistants saved). version opens an earlier version of that same source. It names the connection it came through as louvain_search_sources does. An id you can't read gets the same answer as one that doesn't exist, and so does an earlier version once the source itself is somewhere you can't read.
louvain_factsSearch statements, including their source, modality and certainty. A statement said again by the same source is one row with its witnesses and witnessCount, as in louvain_timeline.
louvain_entityRead an entity profile: derived intervals, statements and relationships. Related things are named as your sources name them, never shown as an internal id, and a relationship reads as a sentence ("Dana reports to Ihaia") or as its value ("Ihaia — Position: Louvain, CEO"). Among the passages that name it, what people wrote comes first; files assistants saved follow, labelled writtenByAgent.
louvain_timelineHow one thing changed over time, and which source changed it. A run of the same statement said again by the same source is one row carrying witnesses (up to 20, each with its source id, span and date) and witnessCount; a value that changed and changed back stays three rows.
louvain_entitiesWhat Louvain knows about, to orient before asking.
louvain_locate_fileWhich file in which collection a Louvain document is (id: a Louvain link …/app/doc/<id>, or its id, a search result's or a file's): its fileId and spaceId, what moving, renaming and its versions take. A source that lives in its own app (a Drive file, a mail) has no collection file, and a file shared with you on its own does not name its collection: both say so (inSpace: false).
louvain_file_versionsA file's versions in a collection you can read (id, spaceId), newest first: when each was saved and by whom (an assistant's names the assistant and you), whether it is the current one, and whether its contents are still kept (kept; only the newest twenty versions keep theirs). canRestore says whether the assistant may bring one back.
louvain_plateYour plate as it stands: what you were asked to do, asked others for or were promised, each with its key, which list it is in (side: you for what you have to do, them for what you wait on; something agreed is you when you or both of you do it, them when only they do), its
one-line task (what to do, or the bare thing you wait on), who it is with (party: their name, and whether that is
a bot rather than a person), any suggestion that a source may have settled it and who said so, its state (open, snoozed, done or closed), a due date where the source stated one (and whether it is past, on your own day: your account's time zone, UTC until your browser has reported one), due — when it is due on your own days (late, today, week for the next six days, later or none) — important, true when it is starred, and the source it came from with its Louvain page — or source: null and typed: true for an item added on the plate rather than opened by a source (one you typed yourself, which only you close, or one an assistant added for you). changes lists what assistants added, dated, reworded or starred on it, as your plate labels it. closedForYou: true is a closed item Louvain closed for you after it had looked settled for two days. An item a teammate handed to you has delegatedFrom (who asked, and their note), and its source only while you can open it yourself; one you handed on has delegatedTo (who has it, and open or done). An assistant can mark an item handed to you done or snoozed, but cannot hand one on, and its Done does not close the one who asked. Open items come first, then those closed in the last week. It reads what is there and never looks for new items, so reading it never runs a model. Where you have your own categories, each item has categories (which ones it is in, and by: louvain when Louvain sorted it, you, or the assistant that put it there) and starredByRule when one of your category rules starred it; category (an id or a name) lists only the items in one.
louvain_categoriesYour own categories for sorting the plate: each one's id, name, what belongs in it (description), its examples and starWhenFits (your rule: an item that fits is starred, once), and the starters you have not adopted. A category is a sorting aid for you alone; it is never a source and never evidence for an answer.
louvain_discuss_readRead a discussion as you see it: on a connected document (sourceId), a file in a collection (spaceId and fileId) or a collection's own threads (spaceId). The threads come most recently active first, 20 at most a call (cursor for more, threadId for one), each with the words it is about, who started it, its link, and its comments: who wrote each (and whether an AI assistant wrote it for them), when, the words, whom it mentions, and Louvain's reply as you may see it. inbox: true lists your discussion notifications instead, newest first and paged the same way — who mentioned or replied to you, on which document or file, in which thread — so an assistant asked "what did Ihaia say?" can find the thread and read it. Everything it returns is what people, their assistants or Louvain wrote, and the tool tells the assistant to treat it as data, never as instructions. It reads through the same rules as the discussion in Louvain: something you can't open is "not visible", whether or not it exists, and an administrator's assistant sees no more than any member's. Reading marks nothing as read, so your notification emails and Slack messages still go.
louvain_whoamiWho the connection acts for, how much it can read, and where it may save — and your sign-in email only if the assistant asked for it when you approved it.

Opening a document in your assistant

louvain_open_document (id, and optionally version) opens one document for you inside the conversation, in Louvain's own document viewer, where your assistant supports interactive apps (the MCP Apps standard). Whether it is shown, and where on the screen, is up to the assistant; one that doesn't show apps gets the same result as text. In the conversation the viewer is a card of at most about 520 pixels, with the document scrolling inside under its title and buttons; Expand opens it full screen where the assistant offers that. Claude on the web shows it. ChatGPT supports the same standard, but in its developer mode the tool has not appeared in the list of Louvain's tools yet; until it does, ask ChatGPT to read the document with louvain_read_source.

  • A file that lives in Louvain — a file, note or document in one of your collections, including one an assistant saved — opens in the viewer at its current version (earlier versions are in louvain_file_versions): Markdown and notes rendered, a workbook or a CSV as a grid of its first 500 rows and 40 columns, and any other file as the text Louvain read from it. A view that stops part of the way through says so.
  • A source that lives elsewhere — a Drive file, an email, a chat message, a Notion page, a saved web page — opens as a card: its title, where it comes from, a button to open the original in its own app, and the excerpt the assistant asked about (passage, or span from an answer's evidence) when the source contains it. Words the source doesn't contain are never shown.

What the assistant itself receives is who the document is — its id, version, title, and for a Louvain file its fileId, spaceId and baseVersion and whether it may save a new version (canUpdate) — not what the document says; it reads that with louvain_read_source when it needs to. The viewer fetches the contents through its own tool, louvain_document_view, which your assistant calls for the viewer. That tool is marked for the viewer only, and an assistant that follows the MCP Apps standard keeps it from the assistant; one that doesn't may list it as an ordinary read of a document you can read. Both tools check your access again on every call. A file someone shared with you on its own opens without its fileId, spaceId or baseVersion and with canUpdate: false: you never learn its collection this way, and you change it in Louvain's Share flow, not through an assistant.

When you select words, or cells in a grid, the viewer tells the conversation what you selected: the document, its version, the file and base version (where the assistant was given them), and the words or the cell range with their values, marked when the source contains text that reads like instructions to an AI model. Links in a document open through your assistant. It is a pointer, never permission: every tool that takes those ids checks your access again, and the selected words are passed as the document's words, not as instructions. A new version clears the selection.

The viewer checks again when you press Refresh and when you come back to the conversation. If a newer version was saved it shows that one. If you can no longer read the document — you were removed from the collection, or it moved somewhere you can't read — it clears what it showed, says the document is no longer available, and tells the conversation that the selection it passed on is gone. If the connection itself ends (you disconnected it, an administrator revoked it, or you left the workspace), the viewer's next check fails and it stops showing the document's contents. Anything already shown in the conversation stays in your assistant's history; Louvain can't take it back. An earlier version of a collection's file opens through the file's history (louvain_file_versions), not in the viewer.

To change a document, the assistant saves a new version with louvain_update and the baseVersion it saw: if anyone saved since, the save is refused and the assistant reads the current version first. Opening a document never lets an assistant save anything it couldn't save before.

Answer modes

louvain_ask takes a mode (the Ask page offers two of them: its default is normal, and its Research switch is thorough):

  • fast — one page of results read once, no follow-up searches and fewer model calls. The quickest answer, in normal's default answer style (not shorter, and not held to a single sentence), and it can miss what a follow-up search would find.
  • normal — the default.
  • thorough — reads whole documents and states every fact it finds. Slower, and the right choice for "everything about X".

Reading the answer

louvain_ask returns more than prose, and the extra fields are the point:

  • evidence — the rows the answer cites, each with the character spans of the sentences it actually rests on, and the sourceId of the document it came from. That id is what louvain_read_source takes. speaker says who wrote it: where a chat source stamped only a user id, it is the person that id belongs to if you can see them in Louvain, otherwise the name the source gave the author, otherwise "a Slack user", never the raw id.
  • refused — a refusal is a result. true means nothing you can see supports an answer. It is not an error and retrying with a paraphrase will not change it.
  • findings — when it refused, what the sources do establish, each marked confirmed, agreed, discussed or proposed.
  • inferred — what the sources imply but never state. This one is not verified. Every token in it is supported by the evidence, so it cannot invent a price or a date, but the connection it draws is new and may be wrong. A good assistant offers it as a suggestion and never as a fact.
  • retrievalDegraded — retrieval that was cut short under load. Present means the search was bounded, so "that is everything" is not a claim to make.

When sources disagree

What was said is not always what holds: a draft is revised, a figure is updated, a general policy has a specific exception. The server tells every assistant that connects how to answer then:

  • answer with the value the evidence supports, and say why: a later statement over an earlier one, an approved or final version over a draft or proposal, a specific source over a general one;
  • where a source says a value was replaced or revised, name the earlier value as history, with its source and date;
  • leave out a document that only resembles the question (another team's plan, a neighbouring incident, a template): it is not a rival answer, and a value is called outdated only when a source says it was replaced;
  • say the sources don't settle it only when nothing separates them;
  • cite the sources that state the answer by title and date, not every source read, and never state a value no source states.

Where louvain_ask returns state, a current value is that choice already made from what you can see.

Saving work

An assistant can keep what it wrote for you (a summary, a draft, meeting notes, a page, a spreadsheet, a slide deck) in Louvain, so you and your other assistants can find it again and cite it. This is off unless you turn it on.

Any file you could upload. An assistant saves a file by its whole name, extension and all (bugs.csv, Q3 plan.xlsx, contract.pdf), sending text as content or any file's bytes as base64 in content_base64. It is exactly an upload of that name and those bytes, under the same rule the Library applies to an upload. That rule refuses by the file's extension first: a name ending in a type Louvain has no reader for (a video, audio, archive, font or executable extension such as .mp4, .zip or .exe, a picture other than PNG, JPEG, GIF or WebP such as .heic or .tiff, or a Word, Excel or PowerPoint 97-2003 .doc, .xls or .ppt) is refused with the reason, and nothing is saved. A .png, .jpg, .jpeg, .gif or .webp must be a picture by its bytes or it is refused the same way; one that is, is kept as the picture its bytes are, opens as itself and is found by its file name and by the words read off it (Uploads); nothing in it is learned from as fact. Any other name, including an unknown extension or none, is accepted, and what the bytes are decides how it is read; bytes Louvain cannot read are kept, and the file's row says No readable text and why. Louvain never takes the assistant's word for what a file is: it reads the name and the bytes, as for an upload, so a CSV opens as a spreadsheet, a workbook with its sheets, a Word file as a document, a PDF with its pages, a .md file rendered and a .txt as its text, each exactly as your own upload of it would, and a PDF named .docx is read as the PDF it is. Opened by its file link, a PDF or a picture whose bytes are one shows in the browser as yours does, and anything else downloads. One file may be as large as an upload (50 MB by default), but one tool call carries at most about 6 MB sent as base64, or about 8 MB of text: an assistant's request is limited to 8 MiB, and base64 takes four characters for every three bytes. A larger request is refused before anything is saved.

A file on disk goes by link. For a file the assistant already has on disk — a screenshot, a PDF, a spreadsheet — louvain_upload_url saves it without the assistant printing its bytes into a tool call. The assistant names the file (filename), its exact length (bytes) and its sha256 (required), plus spaceId and folderId as a save takes them. Louvain checks it as a save would be checked — where it may go, the name, the type, the size, a name already taken — and returns a link, with a ready curl command; the assistant then sends the file's raw bytes to it (curl --upload-file report.pdf '<url>'), with no sign-in or Authorization header: the link is the credential. The reply is the saved file, as louvain_save's, louvainUrl included. Up to the upload limit (50 MB by default) goes this way. Given a file's id, spaceId and the baseVersion it replaces, the link saves a new version of that file instead, by louvain_update's rules, so a large PDF or Word file is never pasted into a call as base64. A link:

  • lasts ten minutes and is spent by its first upload that reaches the bytes: a second use, even at the same moment, is refused (link_used), and so is an expired link (link_expired). An upload refused before that — the connection already sending two files, or Louvain busy (busy, with Retry-After) — leaves the link unused, so send it again;
  • counts as a save when it is made: it spends one of the connection's saves (30 a minute, 500 a day), and a workspace out of allowance or storage is refused then, and again before the bytes are read;
  • is for exactly those bytes: a different length is refused (wrong_size) and so is a different sha256 (content_mismatch), with nothing saved;
  • is bound to this save — your workspace, you, this connection, the collection, the folder and the name are sealed inside it, and a link Louvain didn't make, or one changed in any way, is refused (link_invalid). Nothing sent with the bytes can move them anywhere else;
  • is checked again when the bytes arrive, by the same rules as louvain_save: a disconnected assistant, saving turned off, a collection that has since blocked assistants or a role you have since lost wins, and nothing is saved.

A refused upload asks for a new link; each link is spent by its first attempt. The file's name is sealed, never readable in the link.

A save says what it meant to send. louvain_save, louvain_save_to_space and louvain_update take an optional sha256 (the hex digest of the whole file) and bytes (its length), which the tools ask assistants to send. If what arrives doesn't match — a tool call cut off partway still arrives as valid, shorter text — the save is refused as content_mismatch and nothing is stored, so the assistant can send it again rather than leaving a partial file that says it was saved.

Names are unique within a folder. Two files in the same folder can't share a name (whatever the capitals), but a README.md in two different folders is two files. A save whose name is already in that folder is refused; moving a file into a folder that already holds its name is refused too, and never renames it.

Turning it on. When you connect an assistant, the approval screen shows Save files for you as its own switch, off. Turn it on to allow saving. Louvain grants saving only when the switch was turned on: an assistant can't get it just by asking for it. Doing so creates your Saved by agents collection: a personal collection only you can read, where an assistant saves by default. An assistant you connected without it can't save. To add it, disconnect the assistant and connect it again. An administrator can turn saving off for the whole workspace. That stops every assistant saving at once and keeps what was already saved.

Where it can save. Only in a collection where you are an editor or admin: an assistant acting for you has your access, no more. An admin of a collection can turn on Block agents from adding files to keep assistants out of it (see Library), and your workspace's admins can stop assistants saving anywhere. Your own Saved by agents collection can't be blocked. The assistant never chooses who can read what it saves: a file has exactly the readers of the collection it is in. It can't share a file, change a collection's sharing or give anyone a role. Sharing is only ever a suggestion you confirm: louvain_propose_sharing prepares the change, and nothing changes until you apply it yourself in Louvain. The same goes for anything that can't be undone: an assistant only proposes moving a collection to Reference, or suggests reading one as Knowledge (louvain_propose_reading), or deleting it (louvain_propose_space_delete), and you apply or discard the proposal in Louvain. Your workspace role doesn't count here: an administrator's assistant saves only where the administrator has a role in the collection. A file someone shared with you on its own, rather than through a collection, is one your assistant can read but not save to, even if you may edit it yourself. If a shared collection you expect is missing from louvain_spaces, check that you hold editor or admin in it (a workspace administrator who created a collection without making themselves its admin holds no role there).

Working on a document together. In a collection where you are an editor, your assistant can save a new version of any file, including one a person wrote, as a colleague editing a shared document would. The new version replaces the whole file (nothing is merged), and the file keeps its name unless the assistant gives it a new one with filename, as when you replace a file with a renamed one: a tracker saved as bugs.txt can become bugs.csv. louvain_space_files lists a collection's files with each one's current version; louvain_read_source opens its text; louvain_update saves the new version from the version it read, and is refused if anyone saved since. Every version is kept in the file's history with who saved it, whether a person or their assistant ("Written by an AI agent (Claude) on behalf of Sam"). The newest 20 keep their contents and can be downloaded; older ones are listed as no longer kept. While an assistant's version is the current one, the document is labelled "not used as knowledge": nothing in it becomes a fact Louvain relies on, whatever the collection's reading. A person with edit rights can choose Use as knowledge on the file (or edit it themselves), which saves the text again as their own version, read like anything they add. A note stays text: an assistant updates it with text, under its own name. A saved web page is the site's words, so an assistant never edits one. An update over a version that is still being read is refused until it has been read.

Organising: collections, folders and moves. An assistant allowed to save can also set a collection up for you, exactly as you could in the Library. louvain_create_space creates a private collection (one only you read, which you own) for anyone. If you are a workspace admin, it can also create a shared collection: for everyone in the workspace, for people it names by email, or for a group you can see, by its name. Anyone who couldn't create that collection in Louvain is refused through their assistant too ("Ask a workspace admin to create it, or create a private collection"). A shared collection your assistant creates makes you its admin, so the assistant can add to it. Every collection an assistant creates is labelled as created by an assistant, and every new collection starts read as Reference. louvain_create_folder makes a folder in a collection where the assistant could save, louvain_move_file moves a file into a folder of the same collection or back to its top, louvain_move_folder moves a folder, with everything in it, the same way, and louvain_save and louvain_save_to_space take a folderId to save straight into one. A folder only organises: everyone who reads the collection reads what is in its folders, and nobody else does. louvain_rename_folder renames a folder and changes nothing in it, and louvain_rename_file renames a file as a new version of the same contents, keeping its kind of file (a different extension is refused), so the earlier name stays in its history. A file a person wrote stays theirs: it is still read as their words (and still knowledge where its collection is), and its history says it was renamed by an AI assistant. A note isn't renamed: its name is its title. louvain_delete_folder deletes an empty folder (one holding a file or a folder is refused, never emptied, so nothing anyone wrote goes with it), and the reply says where it sat so it can be made again. louvain_rename_space renames a collection you are an admin of (an editor, or a workspace administrator with no role in it, is refused), and says the name it had so it can be renamed back. louvain_delete_file deletes a file only assistants wrote for you — every version saved by an assistant you connected or by Louvain, none by a person — straight away, as a delete in Louvain does: it leaves search and what Louvain knows. A file you or anyone else saved a version of is refused, with a reply telling you to delete it yourself in Louvain. An assistant can't delete a collection (it only proposes deleting one you are an admin of, below) or change who a collection is shared with (it only suggests that), and a folder can't go inside one of its own folders. louvain_move_file with toSpaceId moves a file to another collection where you can add files too. If more people would read it there, or the file is shared with people on its own (moving it ends that sharing), the assistant is refused, told who can read each collection and how many people's sharing it ends, and must ask you before moving it with confirmWiden: true.

Saving where others can read it is a separate step. louvain_save saves only to your private Saved by agents collection. Saving into a shared collection takes a different tool, louvain_save_to_space. It tells your assistant's app that it reaches other people, so an app that asks before such actions asks you. Everyone that collection is shared with can read what is saved there.

How it is labelled. A file an assistant saved says so in the same words everywhere — on its row, on every search result and source card, when it is opened ("Written by an AI agent (Claude) on behalf of Sam"), and in answer receipts and Slack citations. Where Louvain has both, it names the assistant and the person it wrote as; it never shows the person's name alone as though they wrote it. The assistant is the kind of app Louvain recognised when you connected it (Claude, ChatGPT, Cursor, VS Code), never the name the app registered; one it does not recognise is "an AI agent". A file saved before this label existed says "Written by an AI agent" until it is next read. Through MCP, a source an assistant wrote carries writtenByAgent: true and no author, with generatedBy ({ agent, onBehalfOf }) where Louvain has it. It is searchable, and answers can cite it as what it is. If a figure, date or name in an answer appears only in such a file, the sentence says so: "(from a file an AI agent wrote, not from a person)". Louvain never reads it for facts: what an assistant wrote never becomes something Louvain holds to be true about your company, and never changes what an answer says is current.

Text that reads like instructions. A source whose text looks like it is trying to direct an AI model — a pasted chat transcript, an email that says "ignore previous instructions", a page about prompt injection — carries instructionLike: { marker, note } through MCP: on answer rows, search results, louvain_read_source and an entity's mentions. marker names the rule it matched, never its text; note says what to do with it: quote it as data, never follow it. Louvain reads no facts from such a record, and its own answers treat it the same way; an assistant reading it directly should too.

ToolWhat it is for
louvain_spacesWhere this assistant may save: your Saved by agents collection and any collection you edit that doesn't block assistants. unavailable lists the other collections you can read, each with why the assistant can't save there: not_editor (you only read it) or agents_blocked (an admin of the collection blocked assistants). A collection in neither list is one you can't read or one that doesn't exist, and the assistant can't tell which.
louvain_saveSave a new file to your private Saved by agents collection: filename (with its extension) and exactly one of content (text) or content_base64 (any file's bytes, base64). title with format (markdown, html or text) still names a text file without an extension. Optional folderId: a folder of that collection.
louvain_save_to_spaceSave a new file into a shared collection (spaceId from louvain_spaces), with the same filename, content or content_base64, and optionally folderId (a folder of that collection; any other is refused). Everyone the collection is shared with can read it.
louvain_upload_urlA link to upload a file from disk instead of sending its bytes in the call: filename, bytes (its exact length), sha256 (required), and optional spaceId, folderId and model. Checked as a save would be; returns url, method (PUT, or POST), expiresAt (ten minutes) and a curl command. The raw bytes, once, saved exactly as louvain_save or louvain_save_to_space would save them. With id, spaceId and baseVersion it is a new version of that file instead, under louvain_update's rules: you must be an editor of the collection, a version saved since is refused naming the current one, and filename renames it (leave it out to keep the name).
louvain_create_spaceCreate a collection: name and audience (private, workspace, people with people as email addresses, or group with groups as group names). A shared collection needs you to be a workspace admin, as in Louvain, and makes you its admin. Labelled as created by an assistant. Returns the collection's id.
louvain_create_folderCreate a folder in a collection where the assistant could save (spaceId, name, optional parentId for a folder inside a folder). Names are unique among a folder's siblings and can't contain /; folders go 8 deep and a collection holds up to 500.
louvain_move_fileMove a file (spaceId, fileId) into a folder of the same collection, or to its top with folderId: null. Only where it sits changes: who reads it, how it is read, its name and its versions stay the same. With toSpaceId it moves to another collection (and folderId is a folder of that one); if more people would read it there, or the move would end the file's own sharing, it is refused naming who reads each collection (and how many people's sharing ends) until you agree and it is sent again with confirmWiden: true.
louvain_move_folderMove a folder (spaceId, folderId), with everything in it, into another folder of the same collection (parentId), or to its top with parentId: null. Refused into itself or one of its own folders, deeper than 8, onto a folder of the same name, and where the collection blocks assistants. Nothing is read again and nobody's access changes. With toSpaceId it moves the folder and everything in it (up to 50 files) to another collection where you can add files too: each file is read again there and the folder's new id is returned; refused like a file move (confirmWiden) when more people would read it there or files in it are shared on their own, refused outright (narrows) when someone who reads it now could not read it there — only you can do that, in Louvain, and Louvain itself asks you on a card first — and before anything moves when a file in it is still being read. A stop part way says how many files moved; moving it again finishes it into the same folder.
louvain_rename_folderRename a folder (spaceId, folderId, name) in a collection where you are an editor. Only its name changes: what is in it, where it sits and who reads it stay. Refused onto a name another folder beside it has, an empty name or one with a slash, and where the collection blocks assistants.
louvain_delete_fileDelete a file (spaceId, fileId) in a collection where you are an editor, when every version of it was written for you by an assistant — this one, another assistant you connected, or Louvain. It goes to Recently deleted for 30 days, as when you delete it: out of the collection, search and what Louvain knows until it is restored, then deleted for good. A file any person saved a version of — yours included — is refused (not_louvains) and kept: delete it yourself in Louvain. Refused where the collection blocks assistants. In Ask, Louvain shows you a card and you delete it yourself.
louvain_delete_folderDelete an empty folder (spaceId, folderId) in a collection where you are an editor. A folder holding a file or another folder is refused (folder_not_empty) and never emptied, so nothing anyone wrote is deleted. The folder goes to Recently deleted for 30 days; louvain_restore_file brings it back. Refused where the collection blocks assistants.
louvain_binList Recently deleted for the collections where you are an editor (spaceId for one): what was deleted in the last 30 days, by you, a teammate or an assistant, with when, by whom and when it is deleted for good; 200 at a time, nextCursor for more.
louvain_restore_fileRestore a file or a folder (spaceId, kind, id, from louvain_bin) into the same collection and folder, read again so it answers again. A folder comes back with everything deleted with it; a name now taken comes back as "name (restored)". Refused while its folder is itself deleted, where you can't edit the collection, or where assistants are blocked. If more people can read the collection now than when it was deleted, it is refused saying so and restored only with confirmWiden: true once you agreed. No assistant deletes anything for ever.
louvain_rename_spaceRename a collection (spaceId, name, 1 to 120 characters) you are an admin of (not only an editor; a workspace administrator's role alone doesn't count). Only its name changes: its files, who reads it and how it is read stay. The reply carries previousName, so it can be renamed back. Refused where the collection blocks assistants.
louvain_rename_fileRename a file (id, spaceId, name, and baseVersion, the current version it saw) in a collection where you are an editor, as a NEW version with the same contents under the new name; the earlier version keeps the old name in the history. A version a person wrote stays theirs (read as their words, so it still teaches where its collection is Knowledge) and the history says "Renamed by an AI agent (Claude) for" and the person's name. The new name is how the file is listed; what Louvain reads as the file's title stays the name the person gave it, so an assistant's name never becomes part of what the file says; a version an assistant wrote stays labelled as the assistant's. It keeps its kind of file: a name without an extension takes the file's own (cut to 255 characters with the extension kept), and a different extension is refused. Also refused: a note (its name is its title), a name another file in the same folder has, the name it already has, a saved web page, a file still being read, a stale baseVersion, and where the collection blocks assistants. Returns louvainUrl. A collection is renamed only with louvain_rename_space.
louvain_discuss_postPost a comment in the discussion on a file in a collection where you are an editor and assistants are allowed (spaceId, fileId, body): a new thread on the whole file, a thread on words quoted from it (quote, exactly as the file says them), or a reply (threadId), with mentions of people who read the collection. Everyone in the collection sees it as written by an AI assistant on your behalf, never as your own comment, and the Inbox and the email say so too ("Claude for Omar mentioned you"). It never asks Louvain in the thread. Refused in a personal collection, where you only read the collection, where the collection blocks assistants, for a mention of someone who can't read it or more than 20 mentions, a thread on another file or a quote the file doesn't hold. Your assistants can post 40 comments an hour between them (30 through any one connection) and mention 60 people an hour. The reply has the thread's id and louvainUrl, the file's page open at that thread. To read a discussion first, use louvain_discuss_read.
louvain_propose_sharingSuggest a sharing change for you to confirm: who a file is shared with on its own (kind: "file_shares", spaceId, fileId), where you are an editor of its collection, or a collection's general access and grants (kind: "space_access", spaceId, general), where you are the collection's admin. grants is the whole list as it should be afterwards. It shares nothing. The draft waits in that file's or that collection's Share dialog, saying who would get or lose access, and only you apply it or discard it there. If the sharing changes before you do, the draft can only be discarded. It names only members of the workspace and never adds a group (that is a workspace admin's to do). Refused where you couldn't make the change yourself, where the collection blocks assistants, and for a draft that changes nothing. At most 20 drafts an hour per connection, 30 for you across all your assistants, and 20 waiting at once, counted together with louvain_propose_reading and louvain_propose_space_delete. The reply says who would gain or lose access, by name and never by address, and has louvainUrl, where you review it. A grant it can't make is refused the same way whether or not the person is a member.
louvain_save_linkSave a public web page or online document (url, optional spaceId and title). Louvain fetches it itself, without your cookies or sign-ins, and keeps it as it was; a page that needs a sign-in or isn't public is refused with the reason. It is labelled "Saved by Claude from example.com": the words are the site's, not the assistant's.
louvain_updateSave a new version of a file in a collection where you are an editor — one an assistant saved or one a person wrote — as content or content_base64, replacing the whole file; filename renames it. It takes baseVersion, and refuses if anyone saved since, naming the current version. Earlier versions stay in the file's history. For a large file on disk (a PDF, a Word file), louvain_upload_url with the file's id saves the new version by link instead, with no base64 in the call.
louvain_suggest_knowledgeSuggest that a file you saved (its current version written by this person's own assistant, and read) should become knowledge. It changes nothing by itself: the person sees "Suggested as knowledge by" your assistant beside Use as knowledge when they open the file, and only their click makes it knowledge. A file a person wrote, a file another person's assistant wrote, or a saved web page, is refused; suggesting again changes nothing. Asks for write.
louvain_restore_versionBring back an earlier version of a file in a collection where you are an editor (id, spaceId, version, and baseVersion, the current version it saw), as a NEW version: the next version carries that version's contents and name, labelled as written by the assistant for you, and every version stays in the history — nothing is deleted or rewritten. It renames the file only back to that version's own name. It refuses if anyone saved since; for the version that is already current, one that doesn't exist, or one whose contents are no longer kept; for a saved web page; while the current version is still being read; when another file in the collection now has that version's name; and wherever saving is refused (a collection that blocks assistants, a collection you only read, a disconnected assistant, a workspace that has turned assistant saving off). The reply carries the file's louvainUrl.
louvain_set_readingMove a collection you are an admin of (not only an editor; a workspace administrator's role alone doesn't count) from Reference to Knowledge (spaceId, to: "knowledge"), so Louvain reads its files and learns from them. The assistant previews first (apply false, the default: it changes nothing and says how many files would be read), tells you, and applies (apply: true) only if you agree, because reading uses your workspace's reading allowance and is refused while it is used up. It never moves a collection back to Reference: that removes what the collection taught and the discussions on its files, so it only proposes that (louvain_propose_reading). It is refused in a collection that blocks assistants, and limited to ten changes an hour.
louvain_propose_readingPropose changing how a collection you are an admin of is read (spaceId, to), for you to confirm. It moves nothing. to: "reference" (the default) moves it from Knowledge to Reference, which removes what the collection's files taught Louvain and deletes the discussions on its documents and can't be undone; to: "knowledge" reads every file in it in full, which uses your workspace's reading allowance. The proposal waits in the collection's How it's read dialog, saying how many files it holds, and only you apply it (Move to Reference, or Read as knowledge) or discard it there. If the collection is read that way before you do, it can only be discarded. Refused in a collection already read the way it proposes, where you aren't its admin, and where it blocks assistants. The reply says how many files it holds and has louvainUrl, where you review it. It shares the limits of louvain_propose_sharing.
louvain_propose_space_deletePropose deleting a collection you are an admin of (spaceId), for you to confirm. It deletes nothing. Deleting a collection withdraws its files from search and answers for everyone who could read them and deletes the stored copies, which can't be undone, so the proposal waits in the collection's delete dialog, saying how many files it holds now (and how many when it was proposed, if that changed), and only you apply it (one tap on Delete collection in the proposal) or discard it. If files are added to the collection before you do, it can only be discarded: it is no longer what you were asked about. Refused where you aren't the collection's admin and where it blocks assistants. The reply says how many files it holds and has louvainUrl. It shares the limits of louvain_propose_sharing.
louvain_propose_tidySuggest tidying one of your own personal collections (spaceId), when you ask. It changes nothing. Louvain plans new folders, moves within the collection, drafts merged into one file's versions, and items sent to Recently deleted, each with its reason; a step it is unsure of, and any delete but an exact copy or an empty file or folder, comes unticked. At most three of an assistant's suggestions wait at once, and they share your hourly Tidy up limit. The reply has louvainUrl, where you review each step beside the collection before and after, untick what you don't want and apply it yourself, with one Undo. It never moves anything to another collection and never deletes anything for ever.
louvain_propose_delegationPropose asking a teammate to take one of your open plate items (key, from louvain_plate; to, the teammate's principal; task, the line they will see, at most 200 characters; an optional note, at most 500), for you to send. It hands nothing on and tells nobody. Your row says who it suggests asking; Review opens Ask someone to take this with its teammate, line and note for you to change, and only your Send hands it on, through the same checks as your own. The teammate sees only that line and note, and the source only if they can already open it. Refused for what your own send would be refused for: yourself, someone who isn't a member, an item that isn't open on your plate or is already with someone, one asked of you by someone else, and a workspace that doesn't hand items on. At most 20 an hour per connection, 30 for you across all your assistants, and 20 waiting at once.
louvain_plate_setChange an item on your plate (key, from louvain_plate): done; snooze it for a day or to until (a day, in your own time zone); star or unstar it; set or clear its date (due, with dueDate); reword it (edit, with task); put it in or take it out of one of your categories (categorize or uncategorize, with category, an id or a name — the opposite action reverses it; your own choice on the plate replaces the assistant's, and an assistant never changes yours); or undo the newest change this assistant made to it. Your plate then says so on the row — "Marked done by an AI agent (Claude) for you", "Dated and starred by …" — until you change it yourself (handing it to a teammate counts), which clears the label. Undo beside the label on your Plate page puts back what was there before an assistant changed the date, the wording or the star. It never dismisses an item as not yours and never settles one on a source's say-so: those are yours to decide in Louvain. undo reverses only what this assistant did that you have not changed since, and never another assistant's later change to the same thing; your own changes stay yours. An item you snoozed is left alone until it comes back, an item you added yourself is yours to close (an assistant may still date, reword or star it), and so is one another assistant added, and a change you make while the assistant is acting wins. It is limited to sixty changes an hour.
louvain_plate_addAdd an item to your plate when you ask (text, in your words; optional dueDate, or a day the words name; who it is with as a member id, an entity id for an organisation or person you can see (a client: the item then shows when your plate is focused on them), or a name that is only a label). It says "Added by an AI agent (Claude) for you"; the assistant can date, reword, star, mark done or snooze it (no other assistant can close it), and undo takes it off again while it is untouched: not once you change it, hand it to a teammate, or it is marked done or snoozed. Thirty an hour, within your plate's limit on added items.
louvain_category_addAdd one of your own categories when you ask (name, a one-sentence description of what belongs in it, up to five examples, and starWhenFits to star what fits, once). It is yours, shows as added by the assistant, and Louvain sorts your plate into it within a few minutes. Changing or deleting a category stays yours, in Louvain. It counts toward your plate's sixty changes an hour.
louvain_savedWhat your assistants saved, newest first, a page at a time (limit, 20 by default, at most 50). more: true means there are older ones: pass its nextCursor back as cursor for the next page. A file's id is what louvain_update takes, and louvain_read_source opens it.
louvain_space_filesThe files in a collection you can read (spaceId), most recently changed first, with the collection's folders (each with its parentId) and each file's id, folderId (null at the top), current version, whether an assistant wrote that version and whether an assistant can update it (agentCanUpdate: any file but a saved web page); who changed it last is changedBy for a person's version, and generatedBy ({ agent, onBehalfOf }) for an assistant's. Files come a page at a time (limit, 20 by default, at most 50), with more and a nextCursor to pass back as cursor; the folders are listed whole on every page.
louvain_propose_editSuggest a change to a page in one of your own connected Notion or Confluence sources: replace exact words (quote, with prefix and suffix for context) with text. It only drafts: you review it in Louvain and apply it yourself, as your own edit. The reply gives the document's page (louvainUrl) and where the suggestion waits for you (applyAt: under Ask about this, with Apply and Dismiss). Listed only where your workspace offers editing that source and you have connected it as yourself; limited to 60 suggestions an hour.

Saving, making, renaming or deleting a folder, renaming a collection and moving a file share one limit: 30 a minute and 500 a day per connection; creating collections takes your own hourly allowance for creating collections, and the private-collection cap you have in the Library. A file may be as large as an upload, within what one call carries (above); as for an upload, Louvain reads the first 400,000 characters of its text. Saving counts as ingest against the workspace's monthly allowance and takes storage like any upload. Reading what was saved never counts. Every refusal (a collection that blocks assistants, a role you don't have, a limit) comes back to the assistant as a message it can act on.

The model an assistant says it is appears beside the file as the assistant stated it: a short plain name of up to 64 characters, or nothing.

When you disconnect it, what it saved stays where it is. The disconnect dialog has Also remove what it saved. A file is removed when all of these hold:

  • that assistant created it for you;
  • every version of it is that assistant's: a document a person (or another assistant) also saved a version of is shared work, and stays;
  • you can still edit the collection it is in. Shared collections are included where you still can.

A removed file is withdrawn from search and answers. Disconnecting never waits for the removal: the dialog closes at once, and an assistant that saved a great many files goes on being cleaned up in the background, shown in Background activity at the top of every page — to you only — until it is done.

Asking as of a past date

louvain_ask, louvain_facts, louvain_entity, louvain_timeline and louvain_search_sources all take asOf and knownAt. A plain asOf asks what was true then, using the evidence available now: a later correction or retraction still applies.

{ "question": "What was the renewal fee?", "asOf": "2026-03-01T00:00:00.000Z" }

knownAt replays what Louvain had recorded by a moment instead: nothing the source was first seen to say later, no document first seen later and no retraction made later is used. A bare date means the end of that day, and today means now. Alone, it also sets asOf to the same moment ("what did we know on the 3rd" is about the 3rd). louvain_ask returns it as knownAt beside asOf. It never widens what you can read: a source you lost access to stays hidden. A knownAt that is not a date, or is days in the future, is refused rather than ignored. louvain_search_sources with knownAt returns only its ranked results (the documents whose excerpts match), not the listing that fills past them, and refuses view: "list"; louvain_read_source reads the record as it is now. A question about the person's Plate is refused with knownAt: the Plate is what is open now. Names are matched against what Louvain knows today, and an identity link recorded later still applies.

{ "question": "What stage was Globex at?", "knownAt": "2026-08-10T23:59:59.999Z" }

Managing connections

Connections → Agents lists every assistant you have approved: what it can do (reads as you, and saves files if you allowed it), when you connected it, and when it last asked something. Disconnect is in each row's ⋯ menu. An assistant that is connected but has never asked anything is worth a look — that is what a setup that silently failed looks like.

Disconnect ends it immediately — its tokens die with the connection, not when they would have expired. You can connect it again any time. Files it saved stay unless you choose Also remove what it saved.

When a connection ends without you

  • You leave the workspace (an administrator removes you, or your identity provider deprovisions you). Every assistant you connected stops at once, with your sessions and API tokens. If you had asked Louvain to also remove what it saved and that was still going on in the background, it stops too: what was not removed yet stays in the workspace.
  • You reset or change your password. Every assistant connection ends, along with your API tokens and your other sessions — the credentials somebody holding your account could have created.
  • An administrator ends it, or turns assistant connections off for the workspace, which ends every connection and refuses new ones until it is turned back on. Turning it back on restores nothing; each person connects again.
  • Its refresh chain runs out. An assistant that signed in through OAuth refreshes its access hourly for 30 days after you approved it, then asks you to approve again. If a refresh token that was already used is presented a second time — something only a copy of it can do — the connection is ended, and you reconnect.

Limits

  • A question is at most 4,000 characters; longer is refused as a tool error the assistant can see and shorten.
  • Each person has a per-minute answer rate that the answer API and every assistant they have connected share, so connecting three assistants does not triple it. louvain_ask draws on it (and on a lower one in thorough mode); the lookups draw on a higher one. Over it, the tool returns an error the assistant can read, with how long to wait.
  • Only louvain_ask counts as an answer against the workspace's monthly answer allowance. Searching, reading a source, facts, entities and louvain_whoami read rows and are not metered as answers.
  • louvain_entities returns at most 300 entities and says truncated: true when there are more.

See Limits for the numbers and the settings that change them.

For administrators

People → Agent connections lists every member's connected AI assistants — who connected which assistant, what it may do, and when it last asked. It never shows what an assistant asked or was told: the words of a question, whoever or whatever asked it, are shown only to the person it was asked for. The answers log on Louvain health counts assistants' questions with everyone else's — refusals, response times and feedback — without their words. Each assistant carries the same mark as on its owner's Agents tab: the maker's logo only when approval confirmed it, otherwise a neutral mark and Name not verified — worth a look before deciding whether a connection nobody recognises should stay. An administrator can Revoke any of them, and can turn Allow assistant connections off for the workspace, which ends every connection at once. Agents can save files is a separate switch, and it covers Louvain acting for a person in Ask too: saving, creating collections and folders, moving, renaming and marking the plate. Turning it off stops every assistant writing, Louvain in Ask included, and keeps what was saved; reading is unaffected. An administrator can't see what is in a person's Saved by agents collection, the same as any personal collection.

For operators

The MCP endpoint and its OAuth server are published on the web app's origin, so a customer pastes one hostname:

  • POST {public URL}/api/mcp — the MCP endpoint. Besides tools it serves one resource, the document viewer (ui://louvain/document-viewer-<hash>.html, text/html;profile=mcp-app, read with resources/read): a static page with no data of anyone's in it, named by its content hash. LOUVAIN_MCP_APPS=off stops offering it and louvain_open_document/louvain_document_view, and changes nothing else.
  • {public URL}/.well-known/oauth-protected-resource/api/mcp — resource metadata (RFC 9728). The endpoint's 401 names it, which is how every client discovers the rest.
  • {public URL}/.well-known/oauth-authorization-server — server metadata (RFC 8414).
  • {public URL}/api/v1/oauth/userinfo — OpenID Connect UserInfo, advertised as userinfo_endpoint.

LOUVAIN_AGENT_BASE_URL overrides the base for a deployment that publishes the API directly. It is the OAuth issuer as well as a base path, so it must be exactly what discovery advertises; a trailing slash is stripped, so discovery never advertises one.

The OAuth server follows what real clients send:

  • The token and revocation endpoints take application/x-www-form-urlencoded (RFC 6749 §4.1.3, RFC 7009), and JSON too. A repeated parameter is refused.

  • Client authentication is none (PKCE S256 only), client_secret_post or client_secret_basic. A failed Basic authentication is 401 with a Basic challenge.

  • Redirect URIs may be https, http on loopback (127.0.0.1, [::1] or localhost, matched on any port, RFC 8252 §7.3), a reverse-DNS private scheme (com.example.app:/cb), or a known editor scheme (cursor, vscode, vscode-insiders, windsurf).

  • An authorization request that does not parse never redirects to the client. It lands on the product's consent page with the reason, because registration is open and a client's redirect is anybody's.

  • A grant holds only the scopes the client registered for, and those scopes are enforced on the REST routes too: answer for /v1/answer and /v1/answer/stream, graph for the entity and statement reads. write is never a default:

    • a grant with no scope named gets the read scopes only;
    • a client that registers without naming any registers for all of them;
    • the approval screen offers write unticked;
    • a request for write alone is refused, because saving is granted beside reading and never instead of it.
  • openid and email are identity scopes. They unlock no tool, and are granted only when the client names them and only beside a read scope. UserInfo (GET or POST, the access token in the Authorization header) returns up to three claims, never more than the louvain_whoami tool says:

    • sub: the grant's principal id, which is whoami's actingAs. It is <workspace slug>|<email local part> (for example acme|dana), so it is not opaque. It reveals the workspace's slug and most of the address, and one person's ids in two workspaces share the local part.
    • Under email, email: the account's sign-in address.
    • Under email, email_verified: whether that address was proved. It is reported as it stands, never assumed.

    A grant without openid gets 403 insufficient_scope. A revoked, removed or disabled grant gets 401 invalid_token. A POST may carry a form body, but the token is read only from the header.

  • profile and offline_access are accepted and grant nothing. profile adds no claim. offline_access changes nothing, because every code exchange already returns a refresh token and its chain ends 30 days after approval either way. Neither is stored on a registration or a grant.

  • A client registered before the identity scopes existed can't hold them. A grant holds only scopes the client registered for, so that client's request for openid or email is dropped, and it is still granted its read scopes. To use UserInfo, the client registers again.

    Writes exist only as MCP tools. Every REST route an assistant can reach admits only the methods listed for it, so a route an assistant reads can't be written through.

  • The MCP endpoint's 401 carries error="invalid_token" only when a token was sent; a request with no token is told only where the metadata is (RFC 6750 §3.1).

  • CORS: /mcp, the token, registration, revocation and UserInfo endpoints and discovery answer any origin without credentials and expose WWW-Authenticate; every other route keeps the credentialed allowlist.

Client registration is open by design: a registration grants nothing until a signed-in person approves it, it is rate-limited per address, and a client nobody approves within seven days is deleted. LOUVAIN_AGENT_REFRESH_TTL_DAYS (30) sets how long a refresh chain lives. Every approval and revocation is audited: agent.connected, agent.revoked, agent.revoked_by_admin, agent.token_minted, agent.policy_changed, agent.writes_changed and agent.refresh_reused. Each save is audited as agent.file_saved or agent.file_updated, with ids, sizes and the kind of client, never a file's name or content.