Skip to main content

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 /mcp bearer token is useless here (different credential). Unset AGAMI_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}/mcp to 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_groups render 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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agami_core-0.3.5.tar.gz (222.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agami_core-0.3.5-py3-none-any.whl (236.5 kB view details)

Uploaded Python 3

File details

Details for the file agami_core-0.3.5.tar.gz.

File metadata

  • Download URL: agami_core-0.3.5.tar.gz
  • Upload date:
  • Size: 222.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for agami_core-0.3.5.tar.gz
Algorithm Hash digest
SHA256 20f30278c9b381665f90151a34f7f3b172538b1e23f607f5c885f5b4259254c4
MD5 13fdea771c529df4c41b7d9062e8d87d
BLAKE2b-256 ce253959fddb57149797f1c01a8dc721ec3b276f609b0f0d0fcadf8268b84888

See more details on using hashes here.

Provenance

The following attestation bundles were made for agami_core-0.3.5.tar.gz:

Publisher: release-pypi.yml on AgamiAI/agami-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file agami_core-0.3.5-py3-none-any.whl.

File metadata

  • Download URL: agami_core-0.3.5-py3-none-any.whl
  • Upload date:
  • Size: 236.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for agami_core-0.3.5-py3-none-any.whl
Algorithm Hash digest
SHA256 202b85847eab130bc088e9e385f3d48305f94e2910728e583eb4bb4457e91e67
MD5 d2ec0d9c4fb262f937164f1e1a94ad15
BLAKE2b-256 72e4d96aab39c681de02ae63c9c61b163106e9e2fe85d6bbc730f7621db90f58

See more details on using hashes here.

Provenance

The following attestation bundles were made for agami_core-0.3.5-py3-none-any.whl:

Publisher: release-pypi.yml on AgamiAI/agami-core

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page