Skip to main content

glin

CLI tool and Python library that equips AI agents with an instant, statistical "gut feeling" (System 1 thinking).

glin trains an Explainable Boosting Machine on a CSV, then exposes it to LLM agents over MCP — locally over stdio, or remotely over MCP's standard streamable-http transport. Every prediction comes with an exact, zero-approximation breakdown of which features drove it, straight from the model's own additive structure (no SHAP/LIME approximation).

Install

The PyPI package is named glin-ml (the name glin was already taken by an unrelated project) — but the CLI command and Python import are both just glin.

uv tool install glin-ml     # recommended: installs the `glin` command in an isolated env
pipx install glin-ml        # equivalent, if you use pipx instead of uv
pip install glin-ml         # plain pip also works, into whatever environment is active

Either way, you get the glin command:

glin --help

For development (editable install from a clone of this repo):

git clone https://github.com/AkashChatterjee/glin.git
cd glin
pip install -e ".[dev]"

Train a model

glin train path/to/data.csv --target churn --name churn_v1

Models are saved under ~/.glin/models/<name>/.

glin list
glin delete churn_v1        # prompts for confirmation
glin delete churn_v1 --yes  # skips the prompt

Use it locally (Claude Desktop, Cursor, ...)

Add to your MCP client's config (e.g. claude_desktop_config.json):

{
  "mcpServers": {
    "glin": {
      "command": "glin",
      "args": ["serve", "--mode", "stdio"]
    }
  }
}

Deploy it remotely (e.g. one EC2 box, any MCP-aware agent)

docker build -t glin .
docker run -p 8000:8000 -v ~/.glin:/root/.glin glin

Then point any MCP client at the standard streamable-http endpoint:

claude mcp add --transport http glin http://<host>:8000/mcp

Tools exposed over MCP

  • list_models() — all trained models available.
  • inspect_model(model_name) — feature schema and target classes.
  • predict(model_name, features, top_n=10) — predicted class and probabilities, plus the full glassbox audit: base rate, every term's contribution (sorted by magnitude), and an explicit additivity check against the model's own predicted probability.

Data requirements

glin train validates your CSV before doing any work — hard problems (e.g. a target column with only one class) stop training with a clear error; soft issues (e.g. a date-like column) print a warning and training proceeds anyway. These rules live in glin/validation.py as a flat, appendable list, so support for a currently-unsupported shape below can be added by adding one rule and one preprocessing case, without touching the rest of the pipeline.

Feature columns — supported today:

Data shape What happens
Numeric (int/float), including NaN Passed through as-is; EBM natively bins missing values into their own split.
Dirty numeric strings (blanks, e.g. " " for a new customer's total charges) Coerced to float64 if ≥80% of non-null values parse as numbers; unparseable cells become NaN.
Categorical strings, any cardinality up to 250 unique values Standardized (lowercased, stripped) and one-hot-style binned by EBM; missing/unseen values map to a __missing__ sentinel.
Boolean columns Treated as a 0/1 continuous numeric feature.

Feature columns — not yet supported (each is flagged by glin train's validator when detected):

Data shape What actually happens Why
Dates / timestamps No date features are extracted. The column is either dropped (if high-cardinality) or kept as a meaningless categorical label — no time-based signal survives either way. Cut from V1 scope; a real dataset need should drive adding cyclical date-feature extraction.
Free text / natural language Dropped once it exceeds 250 unique values; below that threshold it becomes a set of (almost certainly useless) categorical labels. glin doesn't do NLP; a text column isn't a set of classes.
Currency symbols / thousands separators ($, , £, ,) Not stripped. "$1,234.56" fails the ≥80% numeric-parse threshold and falls back to categorical (i.e. garbage). Clean these before training. Cut from V1 scope under an "assume a healthy dataset" simplification.
Lists / dicts / nested JSON in a cell Not parsed structurally; treated as an opaque string. No structured extraction implemented.
Row identifiers (sequential IDs, UUIDs, hashes) Dropped intentionally — not a gap, this is by design. IDs carry no predictive signal.

Target column requirements:

  • At least 2 distinct non-null values (binary or multiclass) — a single-class target is a hard error.
  • Rows with a missing target value are dropped automatically (with a warning); the rest are unaffected.
  • A numeric target with many distinct values (>20) triggers a warning that it looks like a regression target — glin trains classifiers, not regressors.

Releasing (maintainers)

PyPI rejects re-uploading a version number, so bump version in pyproject.toml first and double check the metadata (description, classifiers, URLs) before publishing — there's no fixing a released version after the fact, only shipping a new one.

rm -rf dist
uv build

Dry run against TestPyPI first:

twine upload --repository testpypi dist/*
# username: __token__, password: a TestPyPI API token

Verify it actually installs from there before touching the real index:

pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ glin-ml

Then publish for real:

twine upload dist/*
# username: __token__, password: a PyPI API token (separate account/token from TestPyPI)

Confirm the public release works with a clean install:

uv tool install glin-ml
glin train some.csv --target y --name smoke_check

Release files for glin-ml 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for glin-ml 0.1.1
File Size Uploaded
glin_ml-0.1.1.tar.gz 18.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for glin-ml 0.1.1
File Interpreter ABI Platform
glin_ml-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 34.0 kB

Release files / glin_ml-0.1.1.tar.gz

Download URL glin_ml-0.1.1.tar.gz
Size 18.3 kB
Tags Source
SHA-256 checksum
How to use checksums
c04a142cd84d0cace5c96209559bba6de327999ba57d3d76c12f0d7a2987c378
BLAKE2b-256 checksum
How to use checksums
90ed9db408879ae42759b1e6b5120e23d104c80d4c8edc5baf7db5e001417cb7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / glin_ml-0.1.1-py3-none-any.whl

Download URL glin_ml-0.1.1-py3-none-any.whl
Size 15.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e3aea161f590b139578c4921e10869946873c0750f2d1d1b9180d7395ba25165
BLAKE2b-256 checksum
How to use checksums
2cc5c42b30df5dd4504ba29d4d097b9e2cbed5ac46aae533a2257d210dfd7d5b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page