Connecting an AI client

By the end of this a client will be connected over MCP, reaching exactly one workspace, able to do exactly what you allowed, and acting as you.

Which of the two you need

There are two ways in, and which one applies depends on the client rather than on a preference.

  • OAuth, if the client can open a browser. This is most desktop assistants, and it is the one to use: you approve the connection on a screen that says what is being asked for, and you can revoke it later without touching a config file.
  • A static bearer token, if the client has no browser — scripts, CI, and the stdio transport.

Connecting over OAuth

Point the client at the MCP endpoint:

http://127.0.0.1:3001/mcp

Substitute your installation's public MCP address. The client discovers the rest by itself: it fetches the resource metadata, finds the authorization server, registers itself, and sends you to a consent screen.

That screen states three things, and they are worth reading rather than clicking through:

  1. Which client is asking. The name is self-declared and unverified, and the screen says so. Any client can register; registering is not a trust decision.
  2. Which workspace it will reach. You choose it here, from the workspaces you belong to. The client does not get to ask for one — the workspace is never taken from the request.
  3. What it may do. Read, write, delete. read is always included, because a connection that cannot read can neither answer a question nor find the document it was asked to change.

Approve, and the client is connected.

Connecting with a static token

Issue one for yourself, in a workspace you belong to:

pnpm --filter @md-platform/core issue-token you@example.com <workspaceId> "claude desktop"

A fourth argument narrows what it may do:

pnpm --filter @md-platform/core issue-token you@example.com <workspaceId> "research" read

Valid scopes are read, write, and delete, comma or space separated. Omit the argument and the token gets all three. There is no way to widen a token afterwards — issue a new one and revoke the old.

The token is printed once and stored only as a hash. For the stdio transport it goes in MCP_TOKEN; stdio has no headers to carry a credential, so it comes from the environment the client launches the process with.

The ten tools

A connected client gets these, and only these:

Tool Scope What it does
list_documents read Every document, paths and titles, without bodies
search_documents read Matching excerpts, optionally within one folder
get_document read One document in full, with its version
create_document write A new document, optionally in a folder
update_document write Replaces a body, refused if the version is stale
move_document write Changes folder or name, keeping identity
delete_document delete Removes a document; the content stays in git history
list_assets read Uploaded files, with size and type
get_asset read One uploaded file
delete_asset delete Removes an upload, and the bytes with it

A client is only given the tools its grant covers. A read-only connection does not see create_document and get refused — it never sees it at all, which is a far better instruction to a model than an error after the fact.

What a connection can and cannot reach

  • One workspace. Chosen when you connected it, and not changeable by the client. There is no tool argument that widens the scope, and no tool that returns documents from two workspaces.
  • As you. Every credential belongs to a person and acts as that person. When you lose access to a workspace, every client you connected to it stops working on the next request — not when its token expires.
  • Nothing outside it. A document in another workspace is reported as not found, never as forbidden. A "forbidden" would confirm the identifier exists somewhere.

Reviewing and revoking

Settings → Connected applications lists what is connected, which workspace each one reaches, what it was allowed to do, and when it was last used. Revoking takes effect on the client's next request.

The same from a terminal, for when the browser is not the tool you have:

pnpm --filter @md-platform/core connections you@example.com
pnpm --filter @md-platform/core connections you@example.com <grantId>

Scopes cannot be widened here. Giving a client more requires going through consent again, and there is no path that does it quietly.

What "done" looks like

Ask the client to search the workspace for a word you know is in a document. It comes back with the path, the title, and an excerpt with the match marked. If it comes back with nothing, check that it is pointed at the workspace you think it is — the connection reaches one, and the consent screen said which.

Next: writing so both readers get good answers.

Connecting an AI client — md-platform