Skip to content

feat(init): detect user-scoped MCP servers and offer project-scope option #5

Description

@divideby0

Description

During ftk init, detect if MCP servers are already installed at user-scope (in ~/.claude.json) and offer users the option to also install them at project-scope (in .mcp.json) for team collaboration.

Use Case

Users may have MCP servers like exa, sequential, or notion already configured in their personal ~/.claude.json file. However, project-scoped configuration has several benefits:

  • Team Collaboration: Other developers know which servers are expected
  • Project-Specific Configuration: Use different API keys or settings per project
  • Onboarding: New team members get required servers automatically
  • Documentation: Server usage is documented in project CLAUDE.md

Detection Strategy

Use claude mcp get <server-name> to check installation scope:

$ claude mcp get exa
exa:
  Scope: User config (available in all your projects)
  Status: ✓ Connected
  Type: stdio
  Command: npx
  Args: -y exa-mcp-server@2.0.5
  Environment:
    EXA_API_KEY=fdfc9433-...

Key indicators:

  • Scope: User config → Installed at user-scope (~/.claude.json)
  • Scope: Project config → Installed at project-scope (.mcp.json)
  • Status: ✓ Connected → Server is working
  • Status: ✗ Error → Server has issues

Implementation Requirements

1. Detect User-Scoped Servers

For each server the user wants to install:

  • Run claude mcp get <server-name>
  • Parse output to determine scope
  • Check if server is already installed at user-scope
  • Detect if server is working (Status: ✓ Connected)

2. Prompt User for Project Installation

If server is detected at user-scope:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Server Already Installed (User Scope)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

The 'exa' server is already installed in your user config
(~/.claude.json) and available in all your projects.

Do you want to also install it at project scope?

Benefits of project-scope installation:
  ✓ Team members see it's required
  ✓ Can use project-specific API keys
  ✓ Version-controlled configuration
  ✓ Easier onboarding for new developers

Install at project scope? [y/N]: _

Default behavior:

  • Interactive mode: Ask user (default: No)
  • --no-prompt mode: Skip project installation, but still add to CLAUDE.md

3. CLAUDE.md Documentation Decision

If user chooses NOT to install at project-scope:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CLAUDE.md Documentation
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

The 'exa' server won't be in your project configuration,
but you can still document its usage for Claude Code.

Add 'exa' usage instructions to CLAUDE.md? [Y/n]: _

Default behavior:

  • Interactive mode: Ask user (default: Yes)
  • --no-prompt mode: Always add to CLAUDE.md

4. Handle Different Scenarios

Scenario A: Server not installed anywhere

// Normal flow - install at project scope
await installServer(serverName, projectScope: true);
await addToCLAUDEmd(serverName);

Scenario B: Server at user-scope, user wants project-scope too

// Install at both scopes (user already has it, add project config)
await installServer(serverName, projectScope: true);
await addToCLAUDEmd(serverName);

Scenario C: Server at user-scope, user declines project-scope

// Skip project installation
if (await promptAddToCLAUDEmd(serverName)) {
  await addToCLAUDEmd(serverName);
}

Scenario D: --no-prompt mode with user-scope server

// Skip project installation, always add docs
await addToCLAUDEmd(serverName);

Example Implementation

async function checkServerScope(serverName: string): Promise<ServerScope> {
  const result = await Deno.Command("claude", {
    args: ["mcp", "get", serverName],
  }).output();

  const output = new TextDecoder().decode(result.stdout);
  
  if (output.includes("Scope: User config")) {
    return {
      installed: true,
      scope: "user",
      connected: output.includes("Status: ✓ Connected"),
    };
  } else if (output.includes("Scope: Project config")) {
    return {
      installed: true,
      scope: "project",
      connected: output.includes("Status: ✓ Connected"),
    };
  } else {
    return {
      installed: false,
      scope: null,
      connected: false,
    };
  }
}

async function handleServerInstallation(
  serverName: string,
  noPrompt: boolean
) {
  const scope = await checkServerScope(serverName);

  if (scope.scope === "user") {
    console.log(`\n'${serverName}' is already installed at user scope.`);
    
    // Prompt for project installation
    const installProject = noPrompt 
      ? false 
      : await Confirm.prompt({
          message: `Install at project scope for team collaboration?`,
          default: false,
        });

    if (installProject) {
      await installServerProjectScope(serverName);
      await addToCLAUDEmd(serverName);
    } else {
      // Ask about CLAUDE.md
      const addDocs = noPrompt
        ? true  // Always add in --no-prompt mode
        : await Confirm.prompt({
            message: `Add usage instructions to CLAUDE.md?`,
            default: true,
          });
      
      if (addDocs) {
        await addToCLAUDEmd(serverName);
      }
    }
  } else if (!scope.installed) {
    // Normal installation flow
    await installServerProjectScope(serverName);
    await addToCLAUDEmd(serverName);
  }
}

User Experience Flow

Interactive Mode

$ ftk init

Select MCP servers to configure:
✓ Sequential Thinking
✓ Exa Search
✓ Basic Memory

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Checking installed servers...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✓ Sequential Thinking: Not installed
✓ Exa Search: Already installed (user scope)
✓ Basic Memory: Not installed

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Server Already Installed (User Scope)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

The 'exa' server is already installed in your user config.

Install at project scope for team collaboration? [y/N]: n

Add 'exa' usage instructions to CLAUDE.md? [Y/n]: y

✓ Added Exa documentation to CLAUDE.md

Configuring Sequential Thinking...
...

--no-prompt Mode

$ ftk init --no-prompt

✓ Sequential Thinking: Installing at project scope
✓ Exa Search: Already at user scope, adding docs only
✓ Basic Memory: Installing at project scope

✓ Configuration complete

Scope Detection Output Format

Parse claude mcp get output to extract:

  • Scope: "User config" | "Project config" | Not installed
  • Status: "✓ Connected" | "✗ Error"
  • Type: "stdio" | "sse" | "http"
  • Command: npm package or executable path
  • Environment: API keys and config (redacted)

Edge Cases

Server Installed But Not Connected

if (scope.installed && !scope.connected) {
  console.warn(`⚠️ ${serverName} is installed but not connected`);
  // Offer to reconfigure or skip
}

Server at Both Scopes Already

if (scope.scope === "both") {
  console.log(`✓ ${serverName} already configured at project scope`);
  // Skip installation, just verify CLAUDE.md
}

claude CLI Not Available

try {
  await checkServerScope(serverName);
} catch (error) {
  console.warn("Cannot detect scope without Claude CLI");
  // Fall back to normal installation
}

Acceptance Criteria

  • ftk init detects user-scoped MCP servers via claude mcp get
  • User is prompted to install at project-scope for collaboration
  • User can decline project installation and still add docs
  • --no-prompt mode skips project installation but adds docs
  • Servers already at project-scope are not duplicated
  • CLAUDE.md is updated appropriately in all scenarios
  • Edge cases (not connected, both scopes) are handled gracefully
  • Clear feedback about scope decisions is provided

Documentation Updates

  • Update docs/quickstart.md with scope detection explanation
  • Document --no-prompt behavior for user-scoped servers
  • Add examples of user-scope vs project-scope trade-offs

Related Issues

Future Enhancements

  • ftk config sync-user - Copy user-scope servers to project-scope
  • ftk config promote <server> - Move server from user to project scope
  • Detect scope conflicts (same server, different config at both scopes)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions