Environment
- v0.40.7.2, postgres engine
- Single brain with one non-default source (e.g.
source_id='brain', no sources.default set or set to that source)
- Legacy
access_tokens row (pre-OAuth, bearer token used by an MCP client)
Symptom
All MCP read ops silently return empty:
list_pages → []
get_page → not found
query / search → no hits
…even though the brain has hundreds of pages on disk and in DB. Writes via MCP also land in the wrong place (under a default source dir) until the routing is fixed.
Root cause
src/core/oauth-provider.ts (around line 596 on 0a03ca82) hardcodes sourceId: 'default' for the legacy bearer-token fallback, regardless of what's in access_tokens.permissions.source_id:
return {
token,
clientId: name,
clientName: name,
scopes: ['read', 'write', 'admin'],
expiresAt: …,
sourceId: 'default', // ← ignores permissions.source_id
} as AuthInfo;
The CLI resolver path already honors a three-tier fallback (token perms → brain default → 'default'); the MCP/HTTP path doesn't.
Workaround (operator-side, what we did)
gbrain sources default <id> to persist a brain-wide default.
- Hand-patch
oauth-provider.ts to pull permissions from the access_tokens row and resolve sourceId = permissions?.source_id ?? brain_default ?? 'default'.
UPDATE gbrain.access_tokens SET permissions = permissions || '{"source_id":"<id>"}' WHERE … for the legacy token.
After (1)+(2)+(3) all MCP reads/writes scope to the correct source.
Proposed fix
Make the legacy-token branch of oauth-provider.ts mirror the CLI resolver's three-tier semantics:
const perms = row.permissions ?? {};
const brainDefault = await this.getBrainDefaultSourceId(); // existing helper
const sourceId = perms.source_id ?? brainDefault ?? 'default';
Bonus drive-by: gbrain reindex unreachable
'reindex' is missing from CLI_ONLY in src/cli.ts (~line 30) even though case 'reindex' exists in the dispatch at ~line 1222. The command falls through and errors out. One-line add to CLI_ONLY fixes it.
Happy to send a PR if useful — let me know if you'd prefer to keep the legacy-token path explicitly 'default' and force operators onto OAuth for non-default sources instead.
Environment
source_id='brain', nosources.defaultset or set to that source)access_tokensrow (pre-OAuth, bearer token used by an MCP client)Symptom
All MCP read ops silently return empty:
list_pages→[]get_page→ not foundquery/search→ no hits…even though the brain has hundreds of pages on disk and in DB. Writes via MCP also land in the wrong place (under a
defaultsource dir) until the routing is fixed.Root cause
src/core/oauth-provider.ts(around line 596 on0a03ca82) hardcodessourceId: 'default'for the legacy bearer-token fallback, regardless of what's inaccess_tokens.permissions.source_id:The CLI resolver path already honors a three-tier fallback (token perms → brain default →
'default'); the MCP/HTTP path doesn't.Workaround (operator-side, what we did)
gbrain sources default <id>to persist a brain-wide default.oauth-provider.tsto pullpermissionsfrom the access_tokens row and resolvesourceId = permissions?.source_id ?? brain_default ?? 'default'.UPDATE gbrain.access_tokens SET permissions = permissions || '{"source_id":"<id>"}' WHERE …for the legacy token.After (1)+(2)+(3) all MCP reads/writes scope to the correct source.
Proposed fix
Make the legacy-token branch of
oauth-provider.tsmirror the CLI resolver's three-tier semantics:Bonus drive-by:
gbrain reindexunreachable'reindex'is missing fromCLI_ONLYinsrc/cli.ts(~line 30) even thoughcase 'reindex'exists in the dispatch at ~line 1222. The command falls through and errors out. One-line add toCLI_ONLYfixes it.Happy to send a PR if useful — let me know if you'd prefer to keep the legacy-token path explicitly
'default'and force operators onto OAuth for non-default sources instead.