Multi-Workspace MCP Server
README.md
# Multi-Workspace MCP Server
Enables coding agents to access configured repository workspaces from a single session, especially companion repos outside the current project workspace.
## Problem Solved
AI coding agents are normally confined to a single workspace. When developing cross-platform features (e.g., FlowCoordinator on both Android/Kotlin and iOS/Swift), the agent loses all context when switching platforms. This MCP server gives the agent simultaneous access to both repositories, enabling:
- Port features from Kotlin to Swift (or vice versa) without context loss
- Read iOS code to learn conventions while working in Android Studio
- Generate Swift files directly in the iOS repo
- Search and inspect another repo without leaving the current agent session
## Tools Provided
### `read_crossproject`
Read a file from a **configured workspace root** — usually a companion repo outside the current project workspace. Prefer the IDE's built-in read/navigation tools for files in the current workspace. Returns up to 500 lines by default with line numbers and metadata (`totalLines`, `startLine`, `endLine`, `truncated`). Use `offset`/`limit` to paginate.
```typescript
read_crossproject(workspace: "ios", path: "Modules/Messaging/Sources/MessagingCoordinator/StreamState.swift")
read_crossproject(workspace: "ios", path: "Modules/.../LargeFile.swift", offset: 100, limit: 50)
```
### `write_crossproject`
Create or overwrite a file in a **configured workspace root** — typically another repo exposed through this MCP, not the current project. Prefer the IDE's built-in editing/refactoring tools for current-workspace files. If a `writeAllowlist` is configured, only matching paths are writable.
```typescript
write_crossproject(
workspace: "ios",
path: "Modules/Messaging/Sources/MessagingCoordinator/v2/KeyReducer.swift",
content: "// Swift code here"
)
```
### `list_crossproject`
List files and directories in a **configured workspace root**, usually an external or companion repo rather than the current project workspace. Returns metadata (type, size, modified date) and supports recursive tree listing.
```typescript
list_crossproject(workspace: "ios", path: "Modules/Messaging/Sources")
list_crossproject(workspace: "ios", path: "Modules", recursive: true, maxDepth: 2)
```
### `search_crossproject`
Search text/regex in files inside a **configured workspace root**, usually a companion repo outside the current project workspace. Prefer the IDE's built-in code navigation/LSP tools in the current workspace when they apply. Supports output modes (`content`, `files`, `count`), context lines, path scoping (directory or file), and pagination.
```typescript
search_crossproject(workspace: "ios", pattern: "FlowCoordinator", glob: "**/*.swift")
search_crossproject(workspace: "ios", pattern: "class.*Coordinator", path: "Modules/Messaging", outputMode: "files")
```
### `edit_crossproject`
Apply search-and-replace edits to one or more files in a **configured workspace root**, typically for companion repos outside the current project workspace. Prefer the IDE's built-in refactoring/editing tools for typed changes in the current workspace. Supports regex with backreferences.
```typescript
// Single file
edit_crossproject(
workspace: "ios",
paths: "Modules/.../MyFile.swift",
oldString: "func oldName()",
newString: "func newName()",
)
// Multiple files (same replacement applied to all)
edit_crossproject(
workspace: "ios",
paths: ["FileA.swift", "FileB.swift", "FileC.swift"],
oldString: "oldValue",
newString: "newValue",
replaceAll: true,
)
// Regex with backreferences
edit_crossproject(
workspace: "ios",
paths: "Modules/.../MyFile.swift",
oldString: "func (\\w+)\\(param: String\\)",
newString: "func $1(param: Int)",
useRegex: true,
)
```
### `run_crossproject`
Run a shell command in a **configured workspace root**. Supports pipes, redirects, and chained commands. Returns stdout, stderr, and exit code. Use for build tools, codegen, git operations, or any command that needs to run in the workspace directory.
```typescript
run_crossproject(workspace: "ios", command: "git status")
run_crossproject(workspace: "ios", command: "fastlane ios codegen_messaging")
run_crossproject(workspace: "android", command: "./gradlew :messaging:impl:test")
run_crossproject(workspace: "ios", command: "git log --oneline | head -5", timeout: 10000)
```
## Configuration
Create a `workspace-config.json` in the repo root (see `workspace-config.example.json`):
```json
{
"workspaces": {
"ios": {
"root": "$HOME/git/zillow/ZillowMap",
"name": "iOS (ZillowMap)"
}
}
}
```
By default, the agent can write to any file and run any command within the workspace root. To restrict either, add optional allowlists:
```json
{
"workspaces": {
"ios": {
"root": "$HOME/git/zillow/ZillowMap",
"name": "iOS (ZillowMap)",
"writeAllowlist": ["Modules/**/*.swift", "Tests/**/*.swift"],
"runAllowlist": ["git", "fastlane", "xcodebuild", "swift"]
}
}
}
```
- **`writeAllowlist`** (optional): Glob patterns. If omitted, all writes allowed. Controls `write_crossproject` and `edit_crossproject`.
- **`runAllowlist`** (optional): Command prefixes. If omitted, all commands allowed. Controls `run_crossproject`. When configured, the first word of the command must match one of the prefixes (e.g., `"git status"` matches `"git"`).
Supports `$HOME` and `~` expansion in `root` paths. There are no hardcoded defaults — all workspaces must be defined in this file. If missing, the server starts with zero workspaces and logs instructions.
## Installation
```bash
npm install -g @exaudeus/workspace-mcp
```
### Firebender Registration
Add to `~/.firebender/firebender.json`:
```json
{
"mcpServers": {
"workspace": {
"command": "npx",
"args": ["-y", "@exaudeus/workspace-mcp"]
}
}
}
```
Alternatively, if installed globally:
```json
{
"mcpServers": {
"workspace": {
"command": "workspace-mcp"
}
}
}
```
## Usage Pattern
1. Agent reads Android Kotlin file: `read_file("libraries/illuminate/flow-coordinator/v2/KeyReducer.kt")`
2. Agent reads iOS conventions: `read_crossproject("ios", "Modules/Messaging/Sources/MessagingCoordinator/SubjectCoordinator.swift")`
3. Agent references Rosetta mapping: (read `.firebender/rosetta-kotlin-swift.md` in Android workspace)
4. Agent generates Swift equivalent
5. Agent writes: `write_crossproject("ios", "Modules/.../KeyReducer.swift", content)`
All in one session, no context loss.
## Companion Projects
- **Memory MCP**: [`memory-mcp`](../memory-mcp) — persistent codebase knowledge for AI agents (separate repo)
- **Rosetta rules**: `.firebender/rosetta-kotlin-swift.md` in Android workspace — idiom mapping reference
## Development
```bash
# Install dependencies
npm install
# Build
npm run build
# Run in dev mode
npm run dev
# Run tests
npm test
```
## Safety Features
### Stateless Safety Model
The MCP uses **stateless validation** instead of session tracking - no brittle state to manage:
**Edit Safety**:
- Verifies old string exists before editing (reads file fresh every time)
- Enforces uniqueness (unless replaceAll=true)
- Fails fast with clear errors if string not found or not unique
- No need to "read first" - the verification IS the safety check
**Write Safety**:
- Warns when overwriting existing files (logged to stderr)
- Agent can still overwrite if intentional (not blocked)
- Optional `writeAllowlist` restricts writes to matching paths (omit to allow all)
**Why Stateless?**
- No session state = no brittleness across restarts/reconnects
- Always reads fresh from disk (catches external changes)
- Works across multiple agents/sessions
- Self-documenting (failures explain what's wrong)
### Other Safety Features
- **Write allowlist (optional)**: When configured, only allows writes to paths matching the allowlist patterns
- **Path validation**: Resolved paths are verified to stay within workspace root (prevents directory traversal)
- **Shell injection prevention**: All external commands use `execFile` with argument arrays (no shell interpolation)
- **Audit logging**: All write/edit operations logged to stderr
## Future Enhancements
- Git operations (`git_crossproject`)
- Additional workspaces (backend repos, etc.)
- Auto-context loading (inject `.firebender/platform-context.md` on first access)
TDQS
A4.4/5.0
Scored across 6 tools
Disambiguation5/5
Each tool maps to a distinct filesystem operation: read, write, edit, list, search, and run. Even the two mutation tools are clearly separated by whole-file overwrite versus targeted search-and-replace.
Naming Consistency5/5
All six tool names follow the same `verb_crossproject` pattern with snake_case throughout. The naming makes the domain and operation immediately predictable.
Tool Count5/5
Six tools is well-scoped for a companion-repo file and command operations server. Each tool covers a distinct need without bloat or redundancy.
Completeness4/5
The set covers list, read, write, edit, search, and arbitrary command execution, which is strong for the stated purpose. A direct delete or rename tool is missing, though `run_crossproject` can compensate via shell commands.
Maintenance
ActivityInactive
ResponsivenessNo issues