agami-core — governed semantic model, the shared MCP TOOLS harness, and the unified local query executor.
Project description
agami-core (library)
The importable core behind agami: the governed semantic model, the shared MCP TOOLS
harness (stdio entrypoint), and the unified local query executor (execute_sql + the
read-only safety pass + unit formatting).
One package serves every consumer — the local Claude Code skill, the MCP server, and any downstream that imports the same flat module names.
Install
pip install agami-core # executor + stdio harness (pure-stdlib)
pip install 'agami-core[model]' # + the semantic model (pydantic / sqlglot / pyyaml)
pip install 'agami-core[server]' # + the networked HTTP MCP server (see below)
From a checkout, swap in the editable path — pip install -e 'packages/agami-core[model]'.
Flat module names (an invariant)
semantic_model, mcp_harness, execute_sql, agami_paths are top-level importable names —
no sys.path manipulation, no parent package — so a consumer's imports resolve unchanged:
from mcp_harness import TOOLS
import semantic_model
import execute_sql
Entry points
python -m mcp_harness # the stdio MCP server (Claude Desktop)
python -m execute_sql --sql … # the local query executor
python -m semantic_model.cli # the semantic-model CLI (driven by the `sm` launcher)
python -m mcp_http # the networked HTTP MCP server (see below)
HTTP server — networked, with auth (python -m mcp_http)
The [server] extra (pip install 'agami-core[server]') adds a networked MCP
transport: the same TOOLS surface as the stdio server, but over HTTP with OAuth + a small admin
console. It's the self-host shape of the hosted product.
PUBLIC_BASE_URL=https://your-host \
AGAMI_SIGNING_SECRET=$(openssl rand -hex 32) \
AGAMI_DB_URL=postgresql://… \
AGAMI_ADMIN_USERNAME=you@example.com \
AGAMI_ADMIN_FIRST_NAME=Alex AGAMI_ADMIN_LAST_NAME=Kim \
AGAMI_ADMIN_PASSWORD=… \
AGAMI_ADMIN_PROVIDER=google \
AGAMI_OIDC_GOOGLE_CLIENT_ID=… AGAMI_OIDC_GOOGLE_CLIENT_SECRET=… \
python -m mcp_http
The admin is identified by email (AGAMI_ADMIN_USERNAME). Their sign-in method is whatever you
configure — a password and/or a pinned social provider, at least one: set AGAMI_ADMIN_PASSWORD,
and/or AGAMI_ADMIN_PROVIDER (google | microsoft, which must also have its
AGAMI_OIDC_<PROVIDER>_CLIENT_ID/SECRET set). The admin login then offers the same Google/Microsoft
option as the MCP login. Register one OAuth redirect URI with the provider —
{base}/oauth/oidc/callback — it serves both the connector and the admin flows.
One host, two entry points
A deployment is one host (PUBLIC_BASE_URL). Everything lives under it:
| URL | Who | What |
|---|---|---|
{base}/mcp |
a teammate, in Claude | the only URL to add as a custom connector — Claude auto-discovers the OAuth endpoints from it |
{base}/admin |
the admin, in a browser | the console to add/enable/disable users |
Access model — two separate credentials
- Query surface (
/mcp) — gated by a Bearer JWT from the OAuth flow. Any onboarded user who signs in can query (that's the product). No token →401+WWW-Authenticate, which starts Claude's OAuth. Admin-ness does not gate/mcp. - Admin surface (
/admin) — gated by a session cookie and the admin-gate (AGAMI_ADMIN_USERNAME). The admin signs in with their pinned social provider or a password; a valid non-admin is refused (and a social identity for the admin email via a different provider is refused — the pin closes IdP-confusion). An/mcpbearer token is useless here (different credential). UnsetAGAMI_ADMIN_USERNAME⇒ the admin console is disabled entirely.
Onboarding a teammate
The admin adds a teammate by email + name (a pending user). How they finish setting up follows the deployment's single auth method — uniform for everyone, set by what you configured:
- OIDC deployment (a Google/Microsoft client is configured): the teammate just adds
{base}/mcpto Claude and signs in with that provider — the IdP verifies their email and binds the account on first login. No link to share. - Password deployment (no OIDC configured): the admin copies the teammate's setup link from the Users tab and shares it out-of-band; the teammate opens it and sets their own password. The link is a signed, time-boxed token and is single-use (it stops working once the account is set up).
The login surfaces show only the configured method (the admin keeps a password break-glass fallback
on /admin/login).
Trust note. OIDC onboarding binds the account to whoever first proves the teammate's email at the configured IdP — so add a teammate by an email the right person controls there. The setup link and the other pre-auth endpoints aren't rate-limited in-process; put them behind your proxy/LB if exposed.
Activity view
The admin console has one read-only Activity tab: every MCP tool call, folded into the conversation
it belongs to — thread (conversation) ▸ turn (one user question) ▸ call. Open a conversation and you
see its whole arc, not just the queries: the list_datasources / get_datasource_schema calls that
scoped the work sit alongside the execute_sqls that answered it ("User asked what datasources →
list_datasources; user asked revenue by region → get_datasource_schema, then execute_sql ×2"). A
query call shows its SQL, row count, latency, and status; a non-query call shows its tool name. Every
call carries its own datasource, because a conversation — or even a single turn — can span several:
the user switches datasource mid-session, or asks something that runs one query per datasource. The
conversation row lists the full set it touched.
The split is deliberate: it is audit-grade for what ran — the server observes every call directly,
so nothing is dropped — and best-effort for how it's grouped. The MCP protocol carries neither the
user's question, a conversation id, nor a turn boundary, so Claude self-reports them on every call: a
user_question (kept verbatim), a thread_id (per conversation), and a correlation_id (per turn). The
turn's question is taken from the first call in the turn (the model sometimes drifts it on later
refinements). When Claude doesn't supply the ids, a call simply shows as its own singleton conversation —
the view degrades, never drops a call. Treat the self-reported grouping as a hint, not a record.
Free-tier limit. A turn that produces no tool call — Claude answering "let's continue" from context — never reaches the server and so can't appear here; this is an audit of what ran against your data, not a chat transcript.
The tool_calls log grows one row per call and has no automatic retention — it's your local store,
so prune it on your own schedule if it gets large.
Model view
The Model tab ({base}/admin/model) is a read-only explorer of the semantic model you've
deployed — the same tree the MCP tools serve, so it can't drift from what Claude actually reads. It's a
catalog: a browse rail (datasource → subject area → table) and one page at a time —
- a datasource overview (description, glossary, storage-connection names/types, the subject areas),
- a subject-area landing (its tables, metrics, entities),
- a table page — the schema, with each column's type and description, and flags only where they
carry signal (PK / FK / sensitive / unit / enum / caveat). Trust is shown as a single table-level
confidence badge; caveats (the domain gotchas) are elevated to a callout; wide tables collapse
behind "show all N", and tables that author
column_groupsrender those as collapsible sections, - a Relationships page (when the model has cross-area joins) — the org-level relationships that span subject areas, grouped by area-pair, so the cross-area topology is readable in one place,
- a domain-context page (your
ORGANIZATION.md, rendered as safe markdown).
It is read-only by construction — a single GET endpoint, no write path. Editing the model stays conversational in Claude (the in-app editor is a Hosted feature); connection credentials are never rendered (names and types only). Deploy a change in Claude and it shows up here on the next deploy.
Local end-to-end test (HTTPS via a tunnel)
OAuth and the Secure admin cookie need HTTPS, so expose the local server through a tunnel:
# terminal 1 — the server
PUBLIC_BASE_URL=https://<your-subdomain>.trycloudflare.com \
AGAMI_SIGNING_SECRET=$(openssl rand -hex 32) \
AGAMI_DB_URL=sqlite:///$PWD/agami.db \
AGAMI_ADMIN_USERNAME=you@example.com AGAMI_ADMIN_PASSWORD=choose-a-strong-one \
python -m mcp_http
# terminal 2 — the HTTPS tunnel (prints the https URL to use as PUBLIC_BASE_URL)
cloudflared tunnel --url http://127.0.0.1:8000
Open {base}/admin, sign in, add a user — then add {base}/mcp as a connector in Claude.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agami_core-0.4.0.tar.gz.
File metadata
- Download URL: agami_core-0.4.0.tar.gz
- Upload date:
- Size: 248.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49f7da364ca010df4e0f0f65d2082066d2dc559b1748fa118178cd8d66ded3d0
|
|
| MD5 |
c0ae78c32b9fa71629a4d8cda55b18c9
|
|
| BLAKE2b-256 |
aa49571f1abcb3d28e527e8c0e7d08e755ec3849e977efdf905cdfa5391fb990
|
Provenance
The following attestation bundles were made for agami_core-0.4.0.tar.gz:
Publisher:
release-pypi.yml on AgamiAI/agami-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agami_core-0.4.0.tar.gz -
Subject digest:
49f7da364ca010df4e0f0f65d2082066d2dc559b1748fa118178cd8d66ded3d0 - Sigstore transparency entry: 2148262004
- Sigstore integration time:
-
Permalink:
AgamiAI/agami-core@4821dfbe7d3f663897e1fa759b5d0aa23e81bb7e -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/AgamiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@4821dfbe7d3f663897e1fa759b5d0aa23e81bb7e -
Trigger Event:
release
-
Statement type:
File details
Details for the file agami_core-0.4.0-py3-none-any.whl.
File metadata
- Download URL: agami_core-0.4.0-py3-none-any.whl
- Upload date:
- Size: 264.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a7935cf8b3f380baeec2b54bbcd5c0fb0ffd7bab0d5815354ccf94a078f2cc45
|
|
| MD5 |
86027ab3dfa362bd9c3c9abc325df190
|
|
| BLAKE2b-256 |
b9f614fa1fe4aec2e11aae97018659d3f8385cfc3b8add6f0236ca9d57793430
|
Provenance
The following attestation bundles were made for agami_core-0.4.0-py3-none-any.whl:
Publisher:
release-pypi.yml on AgamiAI/agami-core
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
agami_core-0.4.0-py3-none-any.whl -
Subject digest:
a7935cf8b3f380baeec2b54bbcd5c0fb0ffd7bab0d5815354ccf94a078f2cc45 - Sigstore transparency entry: 2148262009
- Sigstore integration time:
-
Permalink:
AgamiAI/agami-core@4821dfbe7d3f663897e1fa759b5d0aa23e81bb7e -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/AgamiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-pypi.yml@4821dfbe7d3f663897e1fa759b5d0aa23e81bb7e -
Trigger Event:
release
-
Statement type: