Metadata-Version: 2.4
Name: adapt-server
Version: 0.4.1
Summary: Adaptive file-backed FastAPI server that turns datasets into CRUD APIs and UIs.
Author-email: notesofcliff <notesofcliff@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://www.mcindi.com/software/adapt/
Project-URL: Repository, https://github.com/McIndi/adapt
Project-URL: Issues, https://github.com/McIndi/adapt/issues
Keywords: fastapi,api,csv,excel,parquet,markdown,media
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastapi<1.0,>=0.115
Requires-Dist: uvicorn<1.0,>=0.30
Requires-Dist: sqlmodel<0.1,>=0.0.14
Requires-Dist: pendulum<4,>=3.0
Requires-Dist: watchfiles<2,>=0.20
Requires-Dist: jinja2<4,>=3.1
Requires-Dist: openpyxl<4,>=3.1
Requires-Dist: xlrd<3,>=2.0.2
Requires-Dist: markdown<4,>=3.5
Requires-Dist: python-multipart<0.1,>=0.0.9
Requires-Dist: mutagen<2,>=1.47
Requires-Dist: imageio<3,>=2.31
Requires-Dist: imageio-ffmpeg<1,>=0.4
Requires-Dist: pillow<13,>=12.3
Requires-Dist: python-json-logger<5,>=2.0
Requires-Dist: numpy<2.5,>=1.26
Requires-Dist: pandas<4,>=2.0
Requires-Dist: fastparquet<2027,>=2024.0.0
Requires-Dist: mcp<2,>=1.28
Provides-Extra: dev
Requires-Dist: pytest<10,>=8; extra == "dev"
Requires-Dist: httpx<1,>=0.27; extra == "dev"
Requires-Dist: httpx2<3,>=2.0; extra == "dev"
Requires-Dist: build<2,>=1.0; extra == "dev"
Requires-Dist: twine<7,>=5.0; extra == "dev"
Dynamic: license-file

# Adapt

Adapt is a FastAPI server that turns files in a directory into APIs and UIs.

- Datasets (`.csv`, `.xlsx`, `.xls`, `.parquet`) become API endpoints and DataTables UIs
- Legacy `.xls` workbooks are read-only. Modern `.xlsx` workbooks support CRUD operations.
- Markdown/HTML become browsable pages
- Media files become streaming endpoints and player/gallery UIs
- Python files can register custom routers
- Everything is searchable in one place via full-text `/search`
- Everything is reachable by agentic tools via an MCP server at `/mcp`

## Quick Start

```bash
pip install adapt-server
adapt addsuperuser --username admin /path/to/docroot
adapt serve /path/to/docroot

# Generate permissions for all discovered resources
adapt admin create-permissions /path/to/docroot __all__

# Everything below here can be done in the admin UI at
# http://localhost:8000/admin/ after logging in with the superuser account.
#
# Create a regular user
adapt admin create-user --username editor --password secret /path/to/docroot

# Reset an existing password and revoke that user's browser sessions
adapt admin change-password --username editor /path/to/docroot

# By default, the editor user has no permissions.
# See available groups (created by `adapt admin create-permissions`) and assign user to desired group
adapt admin list-groups /path/to/docroot
adapt admin add-to-group --username editor --group <group_name> /path/to/docroot
```

Useful URLs:

- `/` landing page
- `/admin/` admin UI
- `/api/<resource>` resource API
- `/ui/<resource>` resource UI
- `/schema/<resource>` resource schema
- `/search` full-text search across every resource you can read
- `/mcp` MCP server for agentic tools (see [MCP Interface](#mcp-interface) below)

## What Adapt Generates

From files in your docroot, Adapt auto-discovers resources and mounts routes with extensionless URLs where possible.

Example:

```text
data/
  employees.csv
  sales.xlsx
  video.mp4
  readme.md
  stats.py
```

Rough output:

- `/api/employees`, `/ui/employees`, `/schema/employees`
- `/api/sales/<sheet>`, `/ui/sales/<sheet>`
- `/media/video.mp4`, `/ui/video.mp4`, `/ui/media`
- `/readme`
- `/api/stats/*`

## Current Security Posture

This reflects the current implementation in the codebase.

### In Place

- **Authentication:** session cookies, API keys (`X-API-Key`), and inactive-user enforcement
- **Authorization:** RBAC (users, groups, permissions), plus superuser bypass
- **Password security:** PBKDF2 hashing with per-user salts
- **Password changes:** self-service and administrator resets revoke all browser sessions for the user
- **Session security:** expiration enforcement, sliding renewal, cleanup task
- **CSRF protection:** enforced for cookie-authenticated unsafe methods (`POST/PUT/PATCH/DELETE`), including mixed session + API-key requests
- **Redirect hardening:** login `next` paths are validated as local relative paths
- **Response hardening:** CSP, `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Permissions-Policy`, HSTS (when TLS is enabled)
- **Host header hardening:** Trusted Host middleware
- **Data integrity:** lock-based, atomic writes for mutable dataset plugins
- **Auditability:** audit records for authentication, administration, and successful dataset mutations
- **Sensitive response cleanup:** admin user APIs no longer expose `password_hash`

### Important Deployment Notes

- Use TLS in non-local environments (`--tls-cert` + `--tls-key`) so secure cookies and HSTS protections are effective.
- API-key-only clients are exempt from CSRF checks by design; cookie-auth browser flows require CSRF tokens.

## Core Features

- Adaptive discovery and route generation
- Dataset CRUD with schema exposure
- Caching with invalidation on mutations
- Built-in admin UI for users/groups/permissions/locks/cache/api keys/audit logs
- Root-level file upload endpoint (`POST /api/uploads`) with permission checks and audit logging
- Optional landing-page upload card for authenticated users with root write permission
- Plugin architecture with companion overrides in `.adapt/`
- Permission-filtered full-text search across every resource type
- MCP server for agentic tool access, mounted alongside the REST API

Upload permission note:

- Non-superusers need `write` permission on the document-root boundary.
- In admin permission creation, use an empty resource (or `__root__`) with
  action `write`, then assign that permission through a group.

## Full-Text Search

`GET /search?q=<query>` searches datasets, Markdown, HTML, and media metadata
in one ranked list, filtered to what the caller may read — a query term that
matches a resource you can't see never shows up, and never leaks via the
result count either.

```bash
curl -H "X-API-Key: <key>" "http://localhost:8000/search?q=parental+leave"
```

The index refreshes incrementally on startup (`search_on_startup`, default
`true`) and can be rebuilt on demand with `adapt reindex <root>`. See the
[API Reference](docs/manual/api_reference.md#search-endpoint) for query
parameters and result shape.

## MCP Interface

Adapt mounts a [Model Context Protocol](https://modelcontextprotocol.io)
server at `/mcp`, on the same host/port as everything else, exposing five
tools that wrap the same permission checks and plugin methods as the REST
API — `list_resources`, `get_schema`, `read_resource`, `write_resource`, and
`search`. There's no separate process, no separate API surface, and no
extra permission model to maintain.

Minimal walkthrough — create an account for the agent, grant it read access,
mint an API key, and connect a client:

```bash
adapt addsuperuser /path/to/docroot --username admin
adapt serve /path/to/docroot &

adapt admin create-permissions /path/to/docroot __all__
adapt admin create-user /path/to/docroot --username agent --password <strong-password>
adapt admin add-to-group /path/to/docroot --username agent --group <resource>_readonly
```

Log in as `agent` and self-issue an API key from `/profile` (any
authenticated user can create their own key — no superuser needed), then
point a client at `/mcp` with that key:

```bash
# Claude Code CLI
claude mcp add --transport http adapt http://localhost:8000/mcp \
  --header "X-API-Key: <key>"
```

```json
// Generic MCP client config (Claude Desktop and similar)
{
  "mcpServers": {
    "adapt": {
      "url": "http://localhost:8000/mcp",
      "headers": { "X-API-Key": "<key>" }
    }
  }
}
```

MCP checks authentication when a tool runs. Tool calls use the shared
authentication resolver, which accepts a session cookie or an API key. API
keys are the supported and recommended mechanism for MCP clients. Set
`mcp_enabled: false` in `.adapt/conf.json` (or `ADAPT_MCP_ENABLED=false`) to
remove `/mcp` entirely. For setup and troubleshooting, read the
[MCP guide](docs/manual/mcp_guide.md).
For dataset reads, `sort` is the column name and `order` must be `asc` or
`desc`.

## Dataset Mutation Envelope

For dataset endpoints, write operations use this payload structure:

```json
{
  "action": "create|update|delete",
  "data": []
}
```

Use object data for `update`/`delete` as needed (for example, with `_row_id`).

## CLI (Common Commands)

```bash
adapt serve <root> [--host ... --port ... --tls-cert ... --tls-key ... --reload --readonly --debug]
adapt check <root>
adapt addsuperuser <root> --username <name>
adapt list-endpoints <root>
adapt reindex <root> [--force]
adapt admin list-resources <root>
adapt admin create-permissions <root> __all__
```

Use `--reload` during development. Uvicorn watches Python files in the document
root and restarts Adapt after a change.

## Helm (Kubernetes)

A Helm chart is included at `charts/adapt/`.

Uploads are disabled by default. Enable them by passing the upload environment
variables through Helm values so the container receives the same config as a
local install.

**Ephemeral (default — data lost on pod restart):**

```bash
helm install adapt ./charts/adapt
```

**Dynamic persistent volume (cluster provisions storage automatically):**

```bash
helm install adapt ./charts/adapt \
  --set persistence.enabled=true \
  --set persistence.size=20Gi \
  --set persistence.storageClass=standard
```

**Existing PVC (cluster admin creates the PVC beforehand):**

```bash
# Cluster admin creates the PVC first, e.g.:
kubectl apply -f my-adapt-pvc.yaml

helm install adapt ./charts/adapt \
  --set persistence.enabled=true \
  --set persistence.existingClaim=my-adapt-pvc
```

Key persistence values:

| Value | Default | Description |
|---|---|---|
| `persistence.enabled` | `false` | Enable durable storage at `/data` |
| `persistence.existingClaim` | `""` | Name of a pre-created PVC to mount |
| `persistence.storageClass` | `""` | StorageClass name; cluster default if empty |
| `persistence.accessModes` | `[ReadWriteOnce]` | PVC access modes |
| `persistence.size` | `10Gi` | Storage request size |
| `persistence.mountPath` | `""` (uses `adapt.rootPath`) | Mount path inside the container |
| `persistence.annotations` | `{}` | Annotations added to the PVC |

Example upload settings in `values.yaml`:

```yaml
env:
  - name: ADAPT_UPLOAD_ENABLED
    value: "true"
  - name: ADAPT_UPLOAD_MAX_SIZE_BYTES
    value: "10485760"
  - name: ADAPT_UPLOAD_ALLOWED_EXTENSIONS
    value: ".csv,.md,.txt"
  - name: ADAPT_UPLOAD_STRICT_MIME_SNIFFING
    value: "true"
```

When uploads are enabled, authenticated users with `write` permission on the
document-root boundary see the upload card on `/` and can upload directly from
the landing page.

> **Admin responsibility:** the cluster admin must supply a matching StorageClass
> and sufficient quota before enabling dynamic provisioning. For `ReadWriteOnce`
> volumes, keep `replicaCount=1` (the default).

**Bootstrap a superuser automatically** (requires `persistence.enabled=true`
— see [docs/manual/installation.md](docs/manual/installation.md#bootstrapping-a-superuser)
for why):

```bash
helm install adapt ./charts/adapt \
  --set persistence.enabled=true \
  --set bootstrapAdmin.enabled=true

kubectl get secret adapt-bootstrap-admin -o jsonpath='{.data.password}' | base64 -d && echo
```

**Expose it without an Ingress controller** (e.g. bare-metal/k3s):

```bash
helm install adapt ./charts/adapt --set service.type=NodePort --set service.nodePort=30080
```

`charts/adapt/values-dev.yaml` bundles persistence + bootstrap + a pinned
NodePort together for local VM/k3s development — see
[docs/manual/installation.md](docs/manual/installation.md#local-development-overlay).

## Documentation

Read the full documentation at **https://www.mcindi.com/adapt/**.

Detailed docs live under `docs/manual/`.

- Manual index: [docs/manual/index.md](docs/manual/index.md)
- Security: [docs/manual/security.md](docs/manual/security.md)
- Quick start: [docs/manual/quick_start.md](docs/manual/quick_start.md)
- Configuration: [docs/manual/configuration.md](docs/manual/configuration.md)
- API reference: [docs/manual/api_reference.md](docs/manual/api_reference.md)
- MCP guide: [docs/manual/mcp_guide.md](docs/manual/mcp_guide.md)
- Plugin development: [docs/manual/plugin_development.md](docs/manual/plugin_development.md)
- Known limitations: [docs/manual/known_limitations.md](docs/manual/known_limitations.md)

Generated reference docs live under `docs/reference/` and are published via
MkDocs and GitHub Pages.

- REST API reference: generated from app routes with an empty docroot
- OpenAPI schema artifact: generated from that same common-surface schema
- Python API reference: generated from docstrings and signatures

Build docs locally:

```bash
python -m pip install -e ".[dev]"
python -m pip install -r requirements-docs.txt
mkdocs build --strict
```

## License

MIT. See [LICENSE](LICENSE).
