Summary
On a hosted MCP-server deployment that serves its semantic model from the database (no on-disk model artifacts) and whose model is stored under a non-default organization id, the safety guard cannot resolve the model, so every execute_sql is refused with model_unavailable — even though the serve/discovery tools (list_datasources, get_datasource_schema) resolve the same model fine.
Root cause
The guard-model and serve-model resolvers scope their DB reads differently:
- Serve (
tools._load_org): model_store.load_datasource(store, profile, org_id=_current_org_id())
- Guard (
execute_sql._resolve_guard_model): model_store.load_datasource(store, profile) — no org_id
model_store.load_datasource(...) defaults org_id to DEFAULT_ORG ("local"). When the model is seeded under a real (minted) org id, the guard's DB read returns None and the hosted guard fails closed with model_unavailable. Introduced in #108.
It is masked on the usual deployment because the resolver then falls through to the on-disk AGAMI_ARTIFACTS_DIR model; a deployment that serves the model only from the DB has no fallback and refuses every query.
Impact
Any hosted deployment that (a) serves the model from Postgres, (b) has a non-local org id, and (c) does not also ship the model on disk → 100% of queries refused. Invisible to deployments that bake the model on disk.
Reproduction
- Set
AGAMI_DB_URL (Postgres or SQLite).
- Seed a model under a non-
local org: model_store.write_datasource(store, ds, model, org_id="<minted>").
- Resolve the runtime org to that id (via
organization.yaml or AGAMI_ORG_ID).
- Point
AGAMI_ARTIFACTS_DIR at an empty dir (no on-disk model).
- Call
execute_sql on a declared table → refused model_unavailable (expected: the query runs, guarded).
Suggested fix
Scope the guard's DB load by the resolved org, mirroring the serve path — pass the resolved org id (with a lean fallback for the stdlib-only executor mirror) into model_store.load_datasource(...) inside _resolve_guard_model.
Test gap
The existing DB-sourced guard tests seed and read under the default local org, so this org-scoping divergence isn't exercised. A regression test should seed under a non-local org and assert the guard resolves it.
Summary
On a hosted MCP-server deployment that serves its semantic model from the database (no on-disk model artifacts) and whose model is stored under a non-default organization id, the safety guard cannot resolve the model, so every
execute_sqlis refused withmodel_unavailable— even though the serve/discovery tools (list_datasources,get_datasource_schema) resolve the same model fine.Root cause
The guard-model and serve-model resolvers scope their DB reads differently:
tools._load_org):model_store.load_datasource(store, profile, org_id=_current_org_id())execute_sql._resolve_guard_model):model_store.load_datasource(store, profile)— noorg_idmodel_store.load_datasource(...)defaultsorg_idtoDEFAULT_ORG("local"). When the model is seeded under a real (minted) org id, the guard's DB read returnsNoneand the hosted guard fails closed withmodel_unavailable. Introduced in #108.It is masked on the usual deployment because the resolver then falls through to the on-disk
AGAMI_ARTIFACTS_DIRmodel; a deployment that serves the model only from the DB has no fallback and refuses every query.Impact
Any hosted deployment that (a) serves the model from Postgres, (b) has a non-
localorg id, and (c) does not also ship the model on disk → 100% of queries refused. Invisible to deployments that bake the model on disk.Reproduction
AGAMI_DB_URL(Postgres or SQLite).localorg:model_store.write_datasource(store, ds, model, org_id="<minted>").organization.yamlorAGAMI_ORG_ID).AGAMI_ARTIFACTS_DIRat an empty dir (no on-disk model).execute_sqlon a declared table → refusedmodel_unavailable(expected: the query runs, guarded).Suggested fix
Scope the guard's DB load by the resolved org, mirroring the serve path — pass the resolved org id (with a lean fallback for the stdlib-only executor mirror) into
model_store.load_datasource(...)inside_resolve_guard_model.Test gap
The existing DB-sourced guard tests seed and read under the default
localorg, so this org-scoping divergence isn't exercised. A regression test should seed under a non-localorg and assert the guard resolves it.