Connect a client

Use the HTTPS address you exposed the server on (see Tailscale Serve, nginx or Caddy) with /mcp on the end.

Claude Code

Add the server to your Claude Code config (~/.claude.json, or with claude mcp add):

{
  "mcpServers": {
    "obsidian": {
      "type": "http",
      "url": "https://your-hostname:4001/mcp"
    }
  }
}

Claude Code prompts you to authenticate the first time. On an unauthenticated server click through the OAuth flow: it uses a public, no-credentials token, so no real account is needed. When the server has auth set, the same flow sends you to your OAuth service’s login page, and the token you get is checked by the server.

Claude Desktop

Servers added through claude_desktop_config.json are launched by Claude Desktop as local stdio subprocesses, so reaching a remote server that way needs a small relay that turns its stdio traffic into HTTP calls against the server’s /mcp endpoint. There are two options.

mcp-remote through npx is the quickest: nothing to install and no local files. It suits a plain, no-credentials setup like this server’s.

Edit claude_desktop_config.json (in Claude Desktop, Settings → Developer → Edit Config) and add an entry under mcpServers:

{
  "mcpServers": {
    "obsidian": {
      "command": "npx",
      "args": [
        "mcp-remote@latest",
        "https://your-hostname:4001/mcp"
      ]
    }
  }
}

Restart Claude Desktop after saving.

mcp-shim is a zero-dependency relay script in this project’s repository. Use it if you need a bearer token, a custom request timeout, or you don’t want an npx download every time Claude Desktop starts. It isn’t published to npm, so clone the repository and point Claude Desktop at the script:

{
  "mcpServers": {
    "obsidian": {
      "command": "node",
      "args": ["/absolute/path/to/obsidian-mcp-brain/mcp-shim/mcp-shim.mjs"],
      "env": {
        "MCP_URL": "https://your-hostname:4001/mcp"
      }
    }
  }
}

It needs Node.js 18 or later and is configured with environment variables:

Variable Required Default Description
MCP_URL Yes none Full https:// URL of the remote MCP endpoint. It can also be passed as the first argument to the script
MCP_TOKEN No none Bearer token sent with every request as Authorization: Bearer TOKEN
MCP_TIMEOUT No 60000 Timeout in milliseconds for POST and DELETE requests. The SSE stream has no timeout

The shim uses HTTPS only, so an http:// URL such as http://localhost:3002/mcp will not work through it. It forwards each JSON-RPC message from Claude Desktop as an HTTPS POST to MCP_URL, opens a persistent SSE stream for server-initiated messages once the server has issued a session ID, reconnects that stream with exponential backoff if it drops, and sends an HTTP DELETE to end the session cleanly when Claude Desktop exits.

Tool names and titles

Each tool has a function name with dashes, such as read-note, and a display title with spaces, such as “Read note”. The server sends both in its tool list, and Claude shows the title where the client supports it; the tool table lists them side by side. Permission rules, allowedTools entries and the audit log always use the function name. The server speaks MCP protocol versions 2025-06-18, 2025-03-26 and 2024-11-05. It answers initialize with the version the client asks for if it speaks it, and with the newest otherwise. A request that names another version in an MCP-Protocol-Version header is refused with 400 (a header that lists several versions is fine if the server speaks them all); a client that sends no such header is fine. A request carries one JSON-RPC message: a batch (an array) is refused with 400.

Reading PDFs and other binary files

read-file returns a file as an MCP embedded resource, and what a client does with that is up to the client.

The repository includes an agent skill, skills/read-file, that works out which Claude surface it is running on, and on a chat surface tells the user that a non-image file can’t be shown and suggests a code mode chat, instead of the agent reporting an empty result. It is only effective where it is installed; see Agent skills and agents.

The server never extracts text itself, so it behaves the same whichever client connects.

Uploading files

upload-file gives the agent a URL to send a file to with curl, so the file doesn’t pass through the model. It therefore needs an agent that can run commands on a machine that can reach the server’s address (mcpBaseUrl).

When the server has authentication on

With auth set, every client needs a token for a user the server lists. How each one gets it:

Client How it signs in
Claude Code Follows the server’s discovery documents to your OAuth service, opens the login in a browser and keeps the token; /mcp is where you sign in again
Claude Desktop with mcp-remote Runs the OAuth flow itself the first time it connects: opens your browser to the login page, receives the code on a local address, and caches the token on that machine. No change to the config entry is needed
Claude Desktop with mcp-shim The shim sends a fixed token from MCP_TOKEN and cannot refresh it, so it suits only an issuer that can hand out a long-lived token. Prefer mcp-remote
claude.ai and the Claude mobile app Add the server as a custom connector with its public /mcp address. The connector runs the sign-in flow from the vendor’s cloud, so the server must be reachable from the internet over HTTPS

Your OAuth service has to support what the clients use: see what the OAuth service has to do.

An agent’s curl upload from a cloud sandbox is not signed in: it uses the one-time URL from upload-file, which needs no token.

If a client reports that it cannot sign in, check the server’s log: each refusal is logged with its reason.

Agent skills and agents

The repository ships three agent skills, read-file, upload-file and replace-file, packaged as a plugin and as individual .skill files. A plugin can also carry agents (an agents/ folder); each one is listed the same way as a skill. The tools work without them; the skills tell an agent how to use the tools well and, in read-file’s case, what to say when the client can’t show a file.

The server tells a connected agent about them. The initialize response carries short instructions, and the get-setup-status tool returns the list of skills and agents, the server’s version, how to tell which Claude surface the agent is on, and the install steps for each. The server does not know what a client has installed: the agent checks its own skill list for the names and compares a skill’s metadata.version with the server version, and tells you when a plugin update may be available. upload-file, replace-file, read-file and create-file point to get-setup-status in their descriptions.

How you get them depends on where you run Claude:

Where How
Claude Code, including the code mode of the Claude Desktop app /plugin marketplace add a1ecbr0wn/obsidian-mcp-brain, then /plugin install obsidian-mcp-brain@obsidian-mcp-brain
Cowork, Claude Desktop chat and claude.ai Download obsidian-mcp-brain.plugin from the latest release and upload it at Customize → Plugins → Add → Upload plugin
One skill on Claude Desktop chat or claude.ai Upload its .skill file from the latest release in Settings → Capabilities → Skills
Anywhere else Copy a folder from skills/ into your agent’s skills directory (for Claude Code, ~/.claude/skills/), or an agent’s <name>.md file from the release into ~/.claude/agents/

A plugin marketplace is a Claude Code feature, so the other surfaces upload the plugin file. Agents in a plugin run in Claude Code and Cowork; Claude Desktop chat and claude.ai ignore them. upload-file and replace-file need a shell on a machine that can reach the server, so they are of use only where the agent has one. The .skill, .plugin and agent files are built from the repository by scripts/package-skills.sh, which needs only zip, and are attached to each release.

If Claude Code refuses the curl command that upload-file (or replace-file) runs, allow the skill’s script with a permission rule in permissions.allow of ~/.claude/settings.json: Bash(sh <path of the skill's upload-file.sh> *) (for the other skill, its replace-file.sh). A Bash rule matches the command as the agent wrote it, from its first word (the skill runs its script with sh), so write the path the way it appears in the refusal; /permissions lists the rules. Bash(curl *) would allow every curl, which is broader than needed. For a small file, create-file needs no curl.

Upgrading from a version that called the tools *-binary-file. The seven tools were renamed (create-file, fetch-file, upload-file, replace-file, read-file, move-file, delete-file) and the old names no longer exist, so a client that calls one gets Unknown tool. The three skills were renamed with them, so install the plugin (or the .skill files) again from the new release and remove the old copies. A permission rule written for the old script, Bash(sh <path>/upload-binary-file.sh *), no longer matches: write it again for upload-file.sh, and for replace-file.sh.