Skip to content

Multi-org developers get both MCPs/skills loaded in every project (no per-repo isolation) #20

@byapparov

Description

@byapparov

Summary

A developer with access to multiple aictrl orgs (e.g. celliq and talentrix) can install the plugin for each org and both installs technically coexist — but every Claude Code session in every project loads both MCP servers and both skill sets, regardless of which repo the developer is actually in. The current design has no per-repo routing for MCP or skill activation, even though every other layer (credentials, project config, telemetry) is already org-aware.

What works today

Multi-org is a first-class concept in the data layer:

  • ~/.aictrl/credentials.json stores per-org API keys: { orgs: { celliq: { apiKey }, talentrix: { apiKey } } }.
  • Each repo's .aictrl.json records its home org ({ orgSlug: "celliq" }).
  • The telemetry hook (src/hooks/resolve-credentials.sh.ts) walks CWD upward to find .aictrl.json, reads orgSlug, then picks the matching key from credentials. Telemetry attribution is correct per repo.
  • Running npx @aictrl/plugin twice (one per org) coexists cleanly: marketplace manifest carries both plugin entries, two plugin dirs sit side by side under ~/.claude/plugins/marketplaces/aictrl/plugins/, and both rows land in installed_plugins.json.

What breaks the "based on the repo folder" expectation

The installer enables plugins at user scope (~/.claude/settings.json enabledPlugins), not project scope. Consequences:

  1. Both MCP servers connect in every session. Each plugin's .mcp.json hardcodes its org's URL and bearer token, so opening Claude Code in celliq/ also opens an HTTP connection to https://aictrl.dev/talentrix/mcp (and vice versa). This is wasted connection capacity, an unnecessary auth surface, and confusing in tool listings.
  2. Both skill catalogues load in every project. Slash commands need the aictrl-<org>:<skill> qualifier to disambiguate, and the auto-loaded-skill ordering for same-named skills across orgs is undefined.
  3. No project-local enablement. Disabling an org for a specific repo requires hand-editing ~/.claude/settings.json — which then disables it everywhere, defeating the point.

Proposed fix

Write enabledPlugins["aictrl-<org>@aictrl"] = true to project-scope settings instead of user-scope. Two ways to do it:

Option A (preferred): <projectDir>/.claude/settings.local.json

  • Per-developer, per-project enablement.
  • Should be gitignored (similar shape to existing .claude/settings.local.json conventions across Anthropic projects).
  • The installer already runs from the project dir, so it has the right CWD.

Option B: <projectDir>/.claude/settings.json

  • Per-project but team-shared (committed).
  • Probably wrong because enablement is a developer preference, not a project requirement.

Keep marketplace registration + plugin files + installed_plugins.json global (those are tied to the install itself, not enablement). Only the enablement toggle moves.

After this change:

  • cd ~/code/celliq && claude → only aictrl-celliq MCP + skills load.
  • cd ~/code/talentrix && claude → only aictrl-talentrix MCP + skills load.
  • Telemetry, credentials, project config all continue to work unchanged (they already key off .aictrl.json).

Implementation notes

  • src/writers/claude.ts mergeSettings currently takes settingsFile from CLAUDE_SETTINGS_FILE (user). It would need to take a project-scope path instead, derived from the project dir already passed to the CLI.
  • The installer should ensure .claude/settings.local.json is gitignored (add to ensureGitignore step alongside existing .opencode/ / .cursor/ entries).
  • Re-running the installer in a new repo would now be a one-off per repo (already the documented mental model — fits npx @aictrl/plugin naturally).
  • Backward compatibility: existing user-scope entries in ~/.claude/settings.json from prior installs should probably be cleaned up on next run so they don't double-enable. Equivalent to the legacy-cleanup pattern added in fix: register aictrl marketplace so Claude Code can resolve plugin enablement (#18) #19.

Out of scope

  • Routing a single MCP server to multiple orgs based on CWD — much heavier and not needed if per-project enablement does the job.
  • Cross-org skill sharing (e.g. one developer wanting code-review available everywhere regardless of org).

Repro

  1. Run npx @aictrl/plugin --org celliq --api-key ... in ~/code/celliq.
  2. Run npx @aictrl/plugin --org talentrix --api-key ... in ~/code/talentrix.
  3. cd ~/code/celliq && claude — observe both aictrl-celliq and aictrl-talentrix MCP servers connecting, both skill sets listed.

Acceptance

  • Each Claude Code session loads only the MCP + skills belonging to the org that owns the current project (per .aictrl.json).
  • Telemetry attribution remains correct (already is).
  • Existing single-org users see no behaviour change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions