DEV Community

Cover image for In APX, `scope` Should Not Mean One Universal Store

In APX, `scope` Should Not Mean One Universal Store

In APX, scope Should Not Mean One Universal Store

One overloaded word can quietly break an otherwise clean architecture.

In APX, that word is scope.

APC is the portable context layer. It keeps project-owned context in the repository through AGENTS.md, .apc/, agent files, skills, and safe shared metadata. APX is the daily-use runtime and tooling layer. It has to turn that portable contract into real local behavior: MCP registration, variables, integrations, sessions, and runtime state under ~/.apx/.

That is exactly why APX should not pretend that every subsystem uses the same meaning of scope.

Same shape, different vocabularies

At first glance, scope controls look similar everywhere. Something can be local to one project or available more broadly. That similarity creates a tempting mistake: one generic helper, one generic UI mental model, one generic storage rule.

The APX code explicitly rejects that shortcut.

In the MCP API, normalizeMcpScope() accepts shared, runtime, and global, with apc as a friendly alias for shared. That maps to three distinct files with three distinct trust rules:

  • shared<repo>/.apc/mcps.json
  • runtime~/.apx/projects/<id>/mcps.json
  • global~/.apx/mcps.json

That model exists because MCP entries can contain secrets, machine-specific endpoints, or safe team-wide defaults. APC owns the committed project contract, while APX keeps secret and machine-local runtime state outside the repo.

Variables are different.

normalizeVarScope() only accepts project and global, and its default depends on context: base project means global, any normal project means project. That is a different storage problem. A variable does not need the same three-way split as an MCP server, because there is no APC shared variable file that should travel with the repo in the same way.

Integrations are different again.

normalizeIntegrationScope() maps global and default to the global store, but treats project, shared, and runtime as the project-level store. That is deliberate. Integrations are credential-aware plugins in the web admin, not raw MCP server registrations. Their fallback rule is project-or-global, not runtime-shared-global.

The comments around these helpers are unusually direct: "do not merge these three helpers", "helpers share a shape but not a vocabulary", and "one helper would silently reroute writes to the wrong store."

That is not style. That is architecture.

Why APC makes this more important

Without APC, a tool could get away with a blurrier storage model because everything would already be machine-local.

With APC, one storage target is now a committed project artifact.

That changes the stakes.

If APX treated every scope selector as interchangeable, it could easily write a secret into .apc/mcps.json, or fail to commit a team-wide MCP definition because it misread shared through a variable-oriented lens. Once a portable layer exists, the runtime has to respect which subsystem is allowed to touch the repo and which one must stay local.

That is the APC/APX split in miniature:

  • APC defines portable project context.
  • APX decides which runtime data belongs in the repo, which belongs in project-local runtime storage, and which belongs machine-wide.

Using one word does not remove those distinctions.

Practical example

Imagine a project with three needs:

  • a filesystem MCP server every teammate should share
  • a GitHub token that should stay on one machine
  • an Asana plugin connection that one project should override locally

Those three things all feel "scoped," but they should not land in one model.

The filesystem server belongs in MCP shared, so it can live in .apc/mcps.json and travel with the repo.

The GitHub token belongs in MCP runtime, so it stays in ~/.apx/projects/<id>/mcps.json and never gets committed.

The Asana plugin belongs in integrations project, unless you intentionally want the default global fallback.

If one generic scope helper handled all three, one of those writes would land in the wrong place sooner or later.

Small rule, big payoff

A clean runtime does not only separate files. It separates meanings.

APX reuses the word scope, but it does not force every subsystem into one fake universal storage model. That restraint keeps APC portable, keeps secrets out of repo context, and makes runtime behavior easier to reason about.

So the practical rule is simple:

  • use MCP scopes for MCP semantics
  • use variable scopes for variable semantics
  • use integration scopes for integration semantics
  • do not normalize different storage contracts just because the dropdown label looks similar

Portable context works only when the runtime keeps these boundaries sharp.

APC gives APX a repo-owned layer.

APX stays trustworthy by not writing every kind of scope to the same place.

Top comments (0)