Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
325 changes: 226 additions & 99 deletions bun.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"format": "biome check --write --formatter-enabled=true --assist-enabled=true ",
"version": "changeset version",
"release": "bun run build:packages && changeset publish",
"build:packages": "bun run build:sdk && bun run build:gateway && bun run build:pm2 && bun run build:cron && bun run build:linear && bun run build:jira && bun run build:telegram && bun run build:cli",
"build:packages": "bun run build:sdk && bun run build:gateway && bun run build:pm2 && bun run build:cron && bun run build:linear && bun run build:mcp && bun run build:jira && bun run build:telegram && bun run build:cli",
"build:mcp": "cd packages/mcp && bun run build",
"build:linear": "cd packages/linear && bun run build",
"build:jira": "cd packages/jira && bun run build",
"build:cron": "cd packages/cron && bun run build",
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"license": "MIT",
"dependencies": {},
"devDependencies": {
"@locusai/locus-mcp": "^0.26.1",
"@locusai/sdk": "^0.26.1",
"@types/bun": "latest",
"typescript": "^5.8.3"
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ ${bold("Commands:")}
${cyan("packages")} Manage installed packages (list, outdated)
${cyan("pkg")} ${dim("<name> [cmd]")} Run a command from an installed package
${cyan("skills")} Discover and manage agent skills
${cyan("mcp")} Manage MCP servers (add, remove, list)
${cyan("memory")} Inspect, search, and manage memory
${cyan("sandbox")} Manage Docker sandbox lifecycle
${cyan("upgrade")} Check for and install updates
Expand Down Expand Up @@ -700,6 +701,13 @@ async function main(): Promise<void> {
break;
}

case "mcp": {
const { mcpCommand } = await import("./commands/mcp.js");
const mcpArgs = parsed.flags.help ? ["help"] : parsed.args;
await mcpCommand(projectRoot, mcpArgs);
break;
}

// ── Phase 7 commands ────────────────────────────────────────────────

case "upgrade": {
Expand Down
113 changes: 113 additions & 0 deletions packages/cli/src/commands/mcp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* `locus mcp` — MCP server management command.
*
* Routes subcommands to the @locusai/locus-mcp package:
* add <template> Add a server from a built-in template
* add-custom Add a custom MCP server
* remove <name> Remove an MCP server
* list List configured servers
* sync Sync config to provider-specific formats
* test <name> Test an MCP server connection
* status Show config and provider sync status
* enable <name> Enable a server
* disable <name> Disable a server
*/

import {
addCommand,
addCustomCommand,
disableCommand,
enableCommand,
handleCommandError,
listCommand,
removeCommand,
statusCommand,
syncCommand,
testCommand,
} from "@locusai/locus-mcp";

export async function mcpCommand(
projectRoot: string,
args: string[]
): Promise<void> {
const subcommand = args[0] ?? "help";
const subArgs = args.slice(1);

try {
switch (subcommand) {
case "add":
await addCommand(projectRoot, subArgs);
break;
case "add-custom":
await addCustomCommand(projectRoot, subArgs);
break;
case "remove":
await removeCommand(projectRoot, subArgs);
break;
case "list":
await listCommand(projectRoot, subArgs);
break;
case "sync":
await syncCommand(projectRoot, subArgs);
break;
case "test":
await testCommand(projectRoot, subArgs);
break;
case "status":
await statusCommand(projectRoot, subArgs);
break;
case "enable":
await enableCommand(projectRoot, subArgs);
break;
case "disable":
await disableCommand(projectRoot, subArgs);
break;
case "help":
case "--help":
case "-h":
printHelp();
break;
default:
process.stderr.write(` Unknown mcp subcommand: ${subcommand}\n`);
printHelp();
process.exit(1);
}
} catch (err) {
handleCommandError(err);
}
}

function printHelp(): void {
process.stderr.write(`
locus mcp — Multi-provider MCP server management

Usage:
locus mcp <command> [options]

Commands:
add <template> Add a server from a built-in template
add-custom Add a custom MCP server
remove <name> Remove an MCP server
list List configured servers
sync Sync config to provider-specific formats
test <name> Test an MCP server connection
status Show config and provider sync status
enable <name> Enable a server
disable <name> Disable a server

Examples:
locus mcp add github
locus mcp add postgres --name mydb --env POSTGRES_CONNECTION=postgresql://...
locus mcp add-custom --name api --transport stdio --command node --args server.js
locus mcp remove mydb
locus mcp list
locus mcp list --json
locus mcp sync --dry-run
locus mcp sync --provider claude
locus mcp test mydb
locus mcp status
locus mcp disable mydb
locus mcp enable mydb

`);
}
49 changes: 49 additions & 0 deletions packages/mcp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"name": "@locusai/locus-mcp",
"version": "0.26.1",
"description": "Multi-provider MCP server management for Locus — configure, sync, and manage MCP servers across AI coding agents",
"type": "module",
"exports": {
".": "./src/index.ts"
},
"bin": {
"locus-mcp": "./bin/locus-mcp.js"
},
"files": [
"bin",
"package.json",
"README.md"
],
"locus": {
"displayName": "MCP Servers",
"description": "Multi-provider MCP server management with config sync and health checks",
"commands": [
"mcp"
],
"version": "0.1.0"
},
"scripts": {
"build": "bun build src/cli.ts --outfile bin/locus-mcp.js --target node",
"typecheck": "tsc --noEmit",
"lint": "biome lint .",
"format": "biome format --write ."
},
"dependencies": {
"@locusai/sdk": "^0.26.1",
"@modelcontextprotocol/sdk": "^1.12.1",
"smol-toml": "^1.3.1",
"zod": "^3.24.4"
},
"devDependencies": {
"typescript": "^5.8.3"
},
"keywords": [
"locusai-package",
"locus",
"mcp"
],
"engines": {
"node": ">=18"
},
"license": "MIT"
}
31 changes: 31 additions & 0 deletions packages/mcp/src/bridges/bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Bridge interface and shared types for provider-specific config sync.
*
* Re-exports the canonical ProviderBridge and SyncResult types from types.ts.
* Bridges translate `.locus/mcp.json` into provider-specific formats
* (e.g. Claude's `.claude/mcp.json`, Codex's `.codex/config.toml`).
*
* Convention: all locus-managed servers are prefixed with `locus-` in provider
* configs to avoid conflicts with user-managed servers.
*/

export type { ProviderBridge, SyncResult } from "../types.js";

/** Prefix applied to server names in provider configs. */
export const LOCUS_SERVER_PREFIX = "locus-";

/** Check if a server name is locus-managed. */
export function isLocusManaged(name: string): boolean {
return name.startsWith(LOCUS_SERVER_PREFIX);
}

/** Add the locus prefix to a server name. */
export function toLocusName(name: string): string {
return `${LOCUS_SERVER_PREFIX}${name}`;
}

/** Strip the locus prefix from a server name. Returns null if not locus-managed. */
export function fromLocusName(name: string): string | null {
if (!isLocusManaged(name)) return null;
return name.slice(LOCUS_SERVER_PREFIX.length);
}
Loading