# An MCP server for team notes has to know who is asking

> Most note MCP servers authenticate with one key over one folder. What changes when the notes belong to a team, and what a server needs to handle it.

Source: https://baalda.com/blog/mcp-server-team-notes
Site: Baalda, https://baalda.com
Published: 2026-10-10

**Short answer.** One that resolves permissions per person, not per installation. Most note MCP servers use a single API key and a folder allowlist, so every caller acts as the vault. For a team, the server has to identify the caller and re-check what that user may read on each tool call, against the same ACL the app uses.

Every round-up of MCP servers for notes ranks the same things: where the data sits, how hard the setup is, whether search is semantic. Those are fine questions for one person with one vault. The moment the notes belong to a team, a different question decides everything, and none of the round-ups ask it. When the assistant calls `read_note`, who is it acting as?

Most note MCP servers have no answer. They authenticate with one key and reach one folder, so there is no "who" in the request at all. That is workable on your own laptop and it is the wrong shape for a shared vault, because a team's notes are not uniformly readable by everyone in the team.

## What does an MCP server for team notes actually need?

Three things, in this order.

1. **An identity per person, not per installation.** The server has to know that this call came from Priya and not from "the vault".
2. **Permissions resolved on the call, not declared in a config.** What Priya can reach changes when someone shares a folder with her or revokes it. A config file written in March cannot know that.
3. **The same answer as the app.** If Priya cannot open a note in the UI, her assistant should not be able to read it either. Two doors onto the same data with different locks is not a permission system.

The directories do not sort servers this way, so it is worth going through what the popular ones actually do.

## Why a path allowlist is not a permission

The most-recommended self-hosted option is the Obsidian MCP server, and it is a good piece of software. It talks to Obsidian's Local REST API with a single bearer token, `OBSIDIAN_API_KEY`, and it does offer scoping: `OBSIDIAN_READ_PATHS=projects/,scratch/` limits reads by folder prefix, `OBSIDIAN_WRITE_PATHS=public/inbox/` limits writes, and `OBSIDIAN_READ_ONLY=true` turns writes off entirely. Matching is by prefix, recursive and case-insensitive.

That is a scope. It is not an identity. The distinction matters because the allowlist is a property of the server process, so everyone pointed at that process gets the same one. The project's own documentation is direct about it when you expose the server over HTTP: unless you set `MCP_AUTH_MODE` to `jwt` or `oauth`, with the default `none` "every caller acts on your vault".

So a team sharing one of these shares one permission set. You can run a separate server per person with a different allowlist each, which works and which nobody does for long, because now the permissions live in however many `.env` files there are people, and nothing keeps them in step with who actually has access to what.

Notion's MCP is the honest counter-example and deserves the credit. It authorises over OAuth, and Notion's documentation says the client can "read and update content that you can access". Permissions come from the person who authorised, which is exactly right. The cost is the one Notion always charges: the content is in Notion's database, not in files you hold, and the [Baalda and Notion comparison](/compare/notion) goes through what that means.

## How Baalda answers it

Baalda is a team second brain built on plain markdown files on your own disk, with real-time multi-user editing, per-file permissions, open source under Apache-2.0 and self-hostable. It serves MCP at `POST /api/mcp` over Streamable HTTP, as one route on the server the team already runs rather than a process on anyone's laptop.

The connection carries a person. A token is minted in **Vault Settings → MCP** and is scoped to one user within one vault. It is stored as `sha256(token)` only, so the plaintext is shown once at creation and a database leak cannot reveal a live token. Every request re-checks that the user is still a member of the vault, which means membership revoked after the token was minted kills that token on its next call rather than whenever someone remembers to go and delete it.

```bash
Authorization: Bearer mcp_your_token_here
```

Then the part that does the work. All 22 tools are gated by the same ACL the rest of the app uses, `src/permissions/resolver.ts`, resolved per call. Read operations need `view`, writes need `edit`, and creating or deleting inside a folder needs `edit` on the parent. The useful consequence shows up in `list_notes`: each note is resolved through `effectivePermission` for the calling user, and anything coming back `none` is skipped rather than returned with an error. A note nobody shared with Priya does not appear to Priya's assistant as a locked door. It does not appear.

That is the difference between a scope and an identity. Ask two teammates' assistants to list the same folder and they get different lists, with no second configuration anywhere describing what those lists should be.

## Where this does not help

Four limits worth knowing before you decide.

- **It does not sandbox the other servers in the client.** Every MCP server configured in one client shares the model's context, so a poisoned third-party server can steer the agent into misusing Baalda's write tools. [The team's vault is the one MCP server that should not run on a laptop](/blog/local-mcp-server-your-team-vault) covers that in full.
- **An admin's token reaches what an admin reaches**, which is everything. Owners and admins get `edit` everywhere, and the access-management tools (`manage_access`, `set_access_default`) are available to them over MCP too. If you want an agent that cannot read the whole vault, mint its token as a member with specific shares, not as yourself.
- **Permissions you never set do not protect anything.** The resolver is only as good as the shares behind it. A vault where everything was left at the default is one permission set wearing a fancier mechanism.
- **There is no importer.** Moving an existing wiki in is a conversion job, the same as it would be anywhere else.

## Does the AI get its own account, or borrow a person's?

It borrows a person's, and that is a deliberate choice rather than a missing feature. A token authenticates as one user within one vault, so "what can this agent reach" has exactly one answer: whatever that person can reach. There is no second permission model to keep in sync, and the audit question stays answerable, because Vault Settings → MCP lists each connection with the client name read off its User-Agent, a call count bumped per `tools/call`, last-active, and Revoke.

The trade is that an agent cannot be given access a human does not have, and cannot be given a narrower slice than some human has. If you want the narrow slice, you make a member account with that slice and mint the token there. More setup than a path allowlist, and it has the property the allowlist never had, which is that it stays true when the team changes.

If you are connecting an assistant to your own notes rather than a team's, [a second brain over MCP](/blog/mcp-notes-ai) is the simpler version of this, and the single-key servers above are genuinely fine. The identity question only starts to bite when the second person arrives. It is the same boundary [per-file permissions](/blog/obsidian-per-file-permissions) run into on the human side, and for the same reason: something in the path has to be able to say no, and a file sitting in a folder cannot.

## FAQ

### What is the best MCP server for team notes?

The one whose permissions survive a second person. Judge a candidate on whether a tool call carries an identity rather than a shared key: Notion's MCP authorises over OAuth and reads "content that you can access", and Baalda mints a token scoped to one user within one vault and resolves every call against the same ACL the app uses. The popular self-hosted Obsidian servers use one API key plus folder prefix allowlists, which is a scope shared by everyone pointed at that process.

### Does a folder allowlist stop the AI reading the rest of the vault?

It constrains that server process, not a particular person. The allowlist is set once in the server's environment, so everyone connected to it gets the same one. The Obsidian MCP server's documentation is explicit that when it is exposed over HTTP with the default MCP_AUTH_MODE of none, "every caller acts on your vault". Running one server per teammate works, but it moves the permission model into a pile of .env files that nothing keeps in step with who actually has access.

### Does the assistant get its own account or use a person's?

In Baalda it uses a person's. An mcp_ token authenticates as one user within one vault, so what the agent can reach is exactly what that user can reach, with no second permission model to keep in sync. The trade is that an agent cannot be granted access no human has, and to give it a narrower slice you mint the token on a member account that has that slice.

### What happens to a live token when someone leaves the team?

Baalda re-checks the user's membership on every request rather than only at mint time, so a token belonging to someone whose membership was revoked stops working on its next call. Tokens are stored as sha256 only and the plaintext is shown once at creation, so a database leak cannot reveal a working token.

### Can an MCP server over shared notes change the permissions themselves?

In Baalda the access-management tools, manage_access and set_access_default, are gated to owners and admins. That is worth knowing in both directions: a member's token cannot reshuffle who sees what, and an admin's token can. If you want an agent that cannot alter access, do not mint its token as an owner or admin.
