# Mloda

> Give an AI agent declarative, deterministic data access via mloda (https://github.com/mloda-ai/mloda) - a Python plugin framework where the caller describes WHAT features or data it needs and mloda resolves HOW to compute or fetch them across a plugin graph, with built-in lineage back to source. Use when an agent needs to check whether an existing mloda plugin already covers a requested feature (see the mloda-plugins skill for writing a new one), before requesting structured data or feature aggregations as a tool call instead of writing ad hoc fetch/transform code (the "LLM Tool Function" pattern), assembling a multi-source context window declaratively, or chaining retrieval/validation/redaction steps for a RAG pipeline by feature name.

- Skill: `tomkaltofen/mloda` (Agent Skill)
- Install (CLI): `npx skillmds add tomkaltofen/mloda`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomkaltofen/mloda/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: any agent that reads SKILL.md (Claude Code, Claude.ai, Cursor, Codex, Windsurf, 60+ more)
- Category: DevOps & Infra
- License: MIT
- Author: tomkaltofen (https://skillmd.com/u/tomkaltofen)
- Updated: 2026-09-02
- Page: https://skillmd.com/skills/tomkaltofen/mloda

---


# mloda: Declarative Data Access for AI Agents

## Check the registry index first

Always do this before hand-writing a chain step. mloda-registry's community set covers common `{col}__{op}`
transforms: aggregation, window/scalar/frame aggregate, scalar/point arithmetic, rank, offset, percentile,
binning, datetime, string ops, time bucketization, ffill, ema, sessionization, resample. None of it ships with
plain `pip install mloda`; install what you need, e.g. `pip install mloda-community` (all) or
`pip install "mloda-community-rank[pandas]"` (one). See its
[plugins table](https://github.com/mloda-ai/mloda-registry#plugins) for the full list and feature-name
patterns.

The table drifts, so once installed, confirm what's actually loaded rather than trusting a static list:

```python
from mloda.user import PluginLoader
from mloda.steward import get_feature_group_docs

PluginLoader.all()
for fg in get_feature_group_docs():
    print(fg.name, fg.description)
```

This only reflects installed packages: an empty or unrelated result means the plugin isn't installed yet, not
that it doesn't exist.

Nothing covering it? See the `mloda-plugins` skill for how to write a FeatureGroup, ComputeFramework, or
Extender.

## Mental model

mloda separates **WHAT** a caller needs from **HOW** it gets computed. A caller (human or LLM) requests
features by name; mloda resolves dependencies across a plugin graph and executes them on a compute framework
(`PandasDataFrame`, `PyArrowTable`, `PythonDictFramework`, ...), returning the result with lineage back to
source.

Three plugin types, most work happens in the first:

| Type | Role |
|------|------|
| **FeatureGroup** | Defines a data transformation - the unit you'll write most often |
| **ComputeFramework** | Execution backend the transformation runs on |
| **Extender** | Hooks for logging, validation, monitoring |

Roles map to modules: `mloda.provider` (define plugins), `mloda.user` (request data), `mloda.steward`
(govern execution).

## When to reach for mloda

- The agent should declare a data/feature request as a tool call instead of writing ad hoc fetch/transform
  code (see "LLM Tool Function" below).
- Multiple context sources (system prompt, user profile, retrieved docs, history) need declarative assembly
  before being handed to a model.
- A RAG pipeline needs chained steps (retrieve -> validate -> redact) expressed as a feature name or config
  instead of hand-wired code.
- You need to check whether a requested feature is already covered by a plugin before writing new code (see
  "Check the registry index first" above); to write one instead, see the `mloda-plugins` skill.

## LLM Tool Function pattern

The agent emits a feature request as JSON, no Python required from the agent itself:

```python
from mloda.user import PluginLoader, load_features_from_config, mloda

PluginLoader.all()  # every mloda example calls this once before the first run_all()

llm_output = '''
[
    "customer_id",
    {"name": "income__sum_aggr"},
    {"name": "total_spend", "options": {"aggregation_type": "sum", "in_features": "income"}}
]
'''

features = load_features_from_config(llm_output, format="json")
result = mloda.run_all(
    features=features,
    compute_frameworks=["PandasDataFrame"],
    api_data={"SampleData": {"customer_id": ["C001", "C002"], "income": [50000, 75000]}},
)
```

`api_data` inlines data under a label (e.g. `"SampleData"`); features are matched to columns by name. For
data on disk, pass a `DataAccessCollection` instead (see mloda's
[API Request docs](https://mloda-ai.github.io/mloda/chapter1/api-request/)).

## Feature-name chaining

A chain like `documents__retrieved__pii_redacted` expresses a pipeline without wiring it by hand - if you ask
for `pii_redacted`, mloda traces that it depends on `retrieved`, which depends on `documents`, and resolves
the whole chain from the single requested name:

```python
Feature(name="user_query__injection_checked__retrieved__pii_redacted")
```

This only works when a FeatureGroup plugin exists for every step in the chain - the chaining syntax alone
does not fetch data mloda has no plugin for.

## Latency-sensitive / repeated requests

`mloda.run_all()` rebuilds the full execution plan every call, fine for batch jobs but wasteful when the same
features are recomputed per request (model serving, streaming, dashboards). Use the two-phase API to plan
once and execute cheaply per request:

```python
session = mloda.prepare(features, compute_frameworks=["PyArrowTable"], data_access_collection=data_access_collection)
result = session.run(api_data={"MyKey": {"col": [1, 2]}})
```

## Reference

- Docs: <https://mloda-ai.github.io/mloda/>
- Install: `pip install mloda`
- Source: <https://github.com/mloda-ai/mloda>

