leek.io leek.io
contact
#ai #claude-code #mcp #tooling

A Linear workspace per project in Claude Code (no more reconnecting)

CJ
Chris Jones · @leek
Jun 6, 2026 · 8 min read · 1,427 words
A Linear workspace per project in Claude Code (no more reconnecting)

I work across two products. Each one lives in its own Linear workspace. And for a while, every time I jumped from one repo to the other, Claude Code’s Linear MCP would still be pointed at the other workspace — so I’d open /mcp, disconnect, reconnect, re-authorize, pick the right workspace, and only then get back to work. Every single switch.

It turns out you can make each project directory quietly use the correct Linear workspace on its own, with zero reconnecting. The trick generalizes to any OAuth-based remote MCP server, but Linear is the one that finally pushed me to fix it.

Why this happens in the first place

The instinct is to look for “a setting where I tell the Linear MCP which workspace to use per project.” There isn’t one. The Linear MCP doesn’t read any per-directory config file.

What actually decides which workspace you act on is the OAuth token of the connection — and a token is bound to a connection, not to a directory.

If you installed Linear the easy way — as a global plugin or a user-scoped MCP server — you have exactly one connection, loaded in every directory, holding one token, authorized to one workspace. So switching projects can’t switch workspaces. The only lever you have is to re-authorize that single connection, which is the disconnect/reconnect dance.

The fix is to stop sharing one connection across every project, and instead give each project its own connection with its own token store.

Two pieces make that work:

  1. A project-scoped MCP server (lives in the repo’s .mcp.json, so it only loads in that repo).
  2. A separate credential store per project, so authorizing one workspace can never clobber the other.

The tool that makes it possible: mcp-remote

Linear’s MCP server is remote (https://mcp.linear.app/mcp). You can point Claude Code straight at it with the native HTTP transport, but then token storage is managed for you and keyed by the server URL — two entries pointing at the same Linear URL end up sharing one token. That’s the exact collision we’re trying to avoid.

mcp-remote is a small stdio bridge that connects a local MCP client to a remote MCP server and handles the OAuth dance. Crucially, it lets you relocate where it stores credentials via the MCP_REMOTE_CONFIG_DIR environment variable. That one knob is what lets two projects hold two independent Linear tokens at the same time.

By default mcp-remote stores tokens in ~/.mcp-auth, keyed by the server URL. Point each project at its own config dir and they stop stepping on each other.

Step 1: kill the global connection

A connection that loads everywhere is the whole problem, so get rid of it. If you installed Linear as a plugin:

# from inside Claude Code
/plugin
# → disable the Linear plugin

If you added it as a user-scoped server instead:

claude mcp remove -s user linear

Either way, the goal is the same: after this, no Linear connection should load by default in every directory. Each project will bring its own.

Note: This means any other project that relied on the global connection for Linear will lose it until you give that project its own .mcp.json entry too. That’s the trade — explicit per-project wiring in exchange for never reconnecting.

Step 2: give each project its own server + token store

Drop a .mcp.json in each repo root. The two files are identical except for the credential directory.

projectA/.mcp.json

{
  "mcpServers": {
    "linear": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp"],
      "env": {
        "MCP_REMOTE_CONFIG_DIR": "${HOME}/.mcp-auth/projectA"
      }
    }
  }
}

projectB/.mcp.json

{
  "mcpServers": {
    "linear": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.linear.app/mcp", "3535"],
      "env": {
        "MCP_REMOTE_CONFIG_DIR": "${HOME}/.mcp-auth/projectB"
      }
    }
  }
}

Because .mcp.json is project-scoped, the linear server only loads when you’re in that directory. Because each one writes its token to a different MCP_REMOTE_CONFIG_DIR, the two workspaces never overwrite each other.

Tip: Claude Code expands environment variables in .mcp.json${VAR} and ${VAR:-default} work in command, args, url, headers, and env. Using ${HOME} keeps the file portable enough to commit. If you’d rather not commit a personal path at all, add the server at local scope instead (claude mcp add -s local …), which stores it in ~/.claude.json under the project path rather than in the repo.

What’s that 3535 doing in projectB?

That’s a fixed OAuth callback port, and it’s the one non-obvious gotcha here.

When mcp-remote authorizes, it briefly stands up a local HTTP server to catch the OAuth redirect — by default on port 3334. If you have both project sessions open at once, you can end up with two mcp-remote → Linear instances fighting over the same callback port, and the second one’s auth fails (you’ll see a cryptic -32000 error).

The callback port is a positional argument right after the URL. Giving each instance its own fixed port removes the collision entirely. projectA keeps the default; projectB gets 3535. Pick any free high port.

Step 3: authorize each project once

Open each project and authorize it a single time:

# from inside Claude Code, in projectA
/mcp
# → select "linear" → Authenticate → approve in the browser (Workspace A)

Then do the same in projectB, approving Workspace B.

The one thing to watch: which Linear identity you get is decided by whatever account your browser is logged into at approval time. If your two projects use two different Linear logins, make sure the browser is on the right account when you click Authorize — or use Linear’s workspace switcher on the consent screen.

After that, you’re done forever. cd into a repo and Claude Code is already on the right workspace. No reconnecting.

You can confirm the tokens landed in the right place:

ls ~/.mcp-auth/projectA/mcp-remote-*/*_tokens.json   # Workspace A
ls ~/.mcp-auth/projectB/mcp-remote-*/*_tokens.json   # Workspace B

Troubleshooting: the -32000 error

If a fresh authorization fails with Failed to reconnect to linear: -32000, it’s almost always one of two things:

  • A half-finished OAuth attempt. If the browser step gets interrupted, mcp-remote leaves a code_verifier and client_info behind but no tokens.json, and every retry reuses the dead state. Clear it and start over:

    rm -rf ~/.mcp-auth/projectB/*
    
  • The callback-port collision described above (both sessions open at once). Give the instance a dedicated port like 3535.

A couple more things worth knowing:

  • Changing .mcp.json args requires a full session restart. The “Reconnect” button reuses the old launch command, so it won’t pick up a new port or env var. Quit and reopen the session.
  • Turn on logs when stuck. Add "--debug" to the args and mcp-remote writes a verbose log next to the tokens (~/.mcp-auth/<project>/mcp-remote-*/*_debug.log). It’ll tell you exactly where the token exchange breaks. Pull it back out once things work.

The shortcut you might actually need

Everything above is for the case where the two projects use two separate Linear workspaces/accounts — that’s what forces separate tokens.

If your projects are in the same workspace and only differ by team, you don’t need any of this. One connection is fine; you just want Claude to target the right team. Put a line in each project’s CLAUDE.md:

## Linear
Always target the `ENG` team when using Linear MCP tools.

The Linear tools take a team parameter, Claude reads CLAUDE.md at session start, and you’re done. No second connection, no reconnecting.

It’s not really about Linear

The pattern is general: a project-scoped .mcp.json server, plus mcp-remote with a per-project MCP_REMOTE_CONFIG_DIR, gives any OAuth-based remote MCP its own isolated login per repo. Linear is just where it bit me first. The same approach works for GitHub, Sentry, Notion, or any other remote MCP where you keep more than one account and want the right one to load based on where you are.

The mental shift that fixed it for me: stop looking for a “which account” setting, and start thinking about which connection holds which token, and where that token lives. Once each project owns its own connection and its own credential store, the right account just follows you around.

CJ
Reply by email
[email protected] · I read everything.
$ reply