Metadata-Version: 2.4
Name: envbert-mcp
Version: 4.0.0
Summary: MCP server for envbert EDD classification — stdio and streamable HTTP transports
License-Expression: MIT
Project-URL: Homepage, https://github.com/AfreenAman/envbert-mcp
Keywords: envbert,mcp,edd,environmental,due-diligence,claude,bert
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp>=1.28.0
Requires-Dist: envbert>=0.1.0
Requires-Dist: uvicorn[standard]>=0.29.0
Requires-Dist: starlette>=0.37.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
Requires-Dist: httpx>=0.27.0; extra == "dev"

# envbert-mcp

MCP server for **envbert** environmental due diligence (EDD) text classification —
supports both **stdio** (Claude Code, VS Code) and **streamable HTTP** (Claude.ai,
any remote MCP client) transports.

Classifies EDD text — site assessment reports, groundwater monitoring logs,
geological surveys — into categories like `Geology`, `Contaminants`, and
`Remediation Standards` using a local DistilBERT model
([d4data/environmental-due-diligence-model](https://huggingface.co/d4data/environmental-due-diligence-model)).
No LLM fallback, sub-second inference, and your data never leaves your machine.

---

## Features

- **Two transports, one codebase** — the same tool logic is exposed over stdio
  and streamable HTTP; the model loads once per process and is shared across
  all connections.
- **Three MCP tools:**
  - `check_envbert_status` — check whether the model is loaded and ready
  - `classify_environmental_text` — classify a single passage
  - `classify_environmental_document` — classify every paragraph of a document
    concurrently, with a category breakdown and low-confidence flags
- **Fast** — sub-second inference after warmup, no LLM round-trip
- **Local-only** — the model runs on your machine; HTTP mode only binds to
  `localhost` by default

---

## Installation

```bash
pip install envbert envbert-mcp
```

Verify:

```bash
python -c "from EnvBert.due_diligence import envbert_predict; print('OK')"
envbert-mcp --help
```

> **Note:** the importable module is `EnvBert` (capital E and B), not
> `envbert`.

---

## Usage

### stdio (Claude Code / VS Code)

Default mode — no flags needed. Claude Code and the Claude Code VS Code
extension spawn this as a subprocess automatically once configured
(see below).

```bash
envbert-mcp
```

### Streamable HTTP (Claude.ai / remote clients)

```bash
envbert-mcp --http
```

Runs on `http://127.0.0.1:8000` by default. First call after startup takes
10–40 seconds while the model warms up; subsequent calls are sub-second.

```bash
# custom host/port
envbert-mcp --http --host 127.0.0.1 --port 8765

# check it's alive
curl http://127.0.0.1:8000/health
# {"status": "ready", "model_ready": true, "load_error": null}
```

Full setup walkthrough, including auto-start scripts and login-time
launchers for macOS/Windows, is in [`LOCAL_SETUP.md`](./LOCAL_SETUP.md).

---

## Connecting a client

### Claude.ai (browser)

1. Start the server: `envbert-mcp --http`
2. In Claude.ai: **Settings → Integrations** (or **Settings → MCP Servers**)
3. Add server URL: `http://127.0.0.1:8000/mcp`
4. Name it `envbert` and save

> Claude.ai must be running on the same machine as the server — this is a
> localhost URL and isn't reachable from another computer.

### Claude Code

`~/.claude/mcp_config.json`:

```json
{
  "mcpServers": {
    "envbert": {
      "url": "http://127.0.0.1:8000/mcp"
    }
  }
}
```

### VS Code (Continue.dev)

`.continue/config.json`:

```json
{
  "mcpServers": [
    {
      "name": "envbert",
      "transport": {
        "type": "http",
        "url": "http://127.0.0.1:8000/mcp"
      }
    }
  ]
}
```

### GitHub Copilot (agent mode)

`settings.json`:

```json
{
  "github.copilot.chat.mcp.servers": {
    "envbert": {
      "url": "http://127.0.0.1:8000/mcp"
    }
  }
}
```

---

## Auto-start

Scripts to start the server without remembering the command each time, and
optional login-time launchers, are in [`scripts/`](./scripts):

| File | Platform | Purpose |
|---|---|---|
| `start-unix.sh` | macOS / Linux | Start server in background, wait for health check |
| `start-windows.bat` | Windows | Start server minimized, wait for health check |
| `com.envbert.mcp.plist` | macOS | `launchd` agent — auto-start at login |
| `envbert-mcp-task.xml` | Windows | Task Scheduler task — auto-start at login |

See [`LOCAL_SETUP.md`](./LOCAL_SETUP.md) for install/uninstall steps for each.

---

## Architecture

```
Claude Code / VS Code          Claude.ai / remote clients
      │ stdio                         │ HTTP POST /mcp
      ▼                               ▼
envbert_mcp/server.py  ←── FastMCP (single instance, both transports)
      │
      │ asyncio.to_thread()  (keeps event loop free during ~1s inference)
      ▼
EnvBert.due_diligence.envbert_predict()
      │
      ▼
DistilBERT (d4data/environmental-due-diligence-model)
```

---

## Tools reference

### `check_envbert_status`

No arguments. Returns:

```json
{
  "envbert_installed": true,
  "model_ready": true,
  "loading": false,
  "load_error": null,
  "load_time_s": 12.3,
  "last_inference_ms": 850.0
}
```

### `classify_environmental_text`

| Arg | Type | Description |
|---|---|---|
| `text` | `str` | A sentence or paragraph of EDD text |

Returns label, confidence (0–1), and inference time. Confidence is the raw
model estimate — below ~0.6, treat it as a best guess.

### `classify_environmental_document`

| Arg | Type | Description |
|---|---|---|
| `paragraphs` | `list[str]` | Paragraphs/sections, in document order |
| `low_confidence_threshold` | `float` | Default `0.6` — paragraphs below this are flagged |

Returns per-paragraph results, a category distribution summary, and a list
of low-confidence items worth a closer look.

> **Note on `confidence`:** this is a cosine similarity between the input
> text and a reference embedding for the predicted category — not a
> calibrated probability. It's a useful relative signal (higher = closer
> match) but shouldn't be read as "X% likely correct." Scores at or below
> 0.3 are already relabeled `Not Relevant` internally before they reach
> you. Every response also includes a `score_basis` field as a reminder.

### Why low-confidence flagging matters here

Without an LLM fallback, a low envbert confidence score (e.g. 0.42) is the
final answer — there's no second opinion baked in. `classify_environmental_document`
surfaces a `low_confidence_items` list explicitly so Claude can apply its
own judgement to exactly those paragraphs, rather than treating every
result as equally reliable.

---

## Troubleshooting

**Claude.ai says it can't connect:**
- Confirm the server is running: `curl http://127.0.0.1:8000/health`
- Confirm Claude.ai is running on the *same machine* (not a remote browser session)
- Check your firewall isn't blocking localhost connections

**Server starts but tools don't appear:**
- Wait for warmup to finish — `check_envbert_status` returns
  `"model_ready": false` until it completes
- Try disconnecting and reconnecting the MCP server in Claude.ai settings

**`envbert-mcp: command not found`:**
- Activate your venv first, or use the full path to the executable

More detail in [`LOCAL_SETUP.md`](./LOCAL_SETUP.md).

---

## envbert-mcp vs envbert-agent which to use

| | This package (raw envbert) | envbert-agent  |
|---|---|---|
| Used by | Claude / MCP clients | CLI, pipelines, non-LLM consumers |
| LLM fallback | None | Yes — Ollama (local) or Azure |
| Typical latency | <1s always | <1s confident, ~10s on fallback |
| External dependencies | None beyond envbert | Ollama or Azure OpenAI |
| Confidence on ambiguous text | Raw model score only | LLM-resolved final label |
| Why this shape | Claude can reason over raw scores itself — a second hidden LLM call is redundant | No reasoning layer downstream; the agent must resolve ambiguity itself |

Both are legitimate — they're solving for different consumers of the
classification, not competing implementations of the same thing.

---

## License

MIT
