langextract-mcp
# langextract-mcp
`langextract-mcp` is an independent, standalone stdio MCP server that exposes Google LangExtract through three bounded tools:
- `langextract_extract`: model-assisted extraction from literal text, with source spans.
- `langextract_visualize`: return escaped interactive HTML for an annotated payload.
- `langextract_status`: safe configuration and limit report.
This is an independent wrapper. It is not affiliated with or supported by Google. It is not published to PyPI.
The server does not read caller files, fetch caller URLs, accept model URLs, accept credentials, or write output files. `langextract_extract` wraps text in a `langextract.core.data.Document` iterable and passes `fetch_urls=False`, including on LangExtract versions where URL-shaped strings could otherwise be significant.
## Install and run from a clone
Requirements: Python 3.11+ and [uv](https://docs.astral.sh/uv/).
git clone https://github.com/CuriosityQuantified/langextract-mcp.git
cd /absolute/path/to/langextract-mcp
uv sync --frozen --all-groups
uv run --frozen langextract-mcp
No MCP client registration occurs automatically. For an MCP client that uses `mcpServers`, add a server entry manually:
```json
{
"mcpServers": {
"langextract": {
"command": "uv",
"args": [
"--directory",
"/absolute/path/to/langextract-mcp",
"run",
"--frozen",
"langextract-mcp"
]
}
}
}
```
## Optional Git-installed launch
After the `v0.1.0` tag exists, an MCP client can launch the tagged source directly with uvx:
uvx --from git+https://github.com/CuriosityQuantified/langextract-mcp@v0.1.0 langextract-mcp
This command installs from Git, not PyPI. It requires network access and does not replace a clone when you need the frozen repository lockfile for reproducible development or checks.
## Hermes configuration
Add this to the appropriate Hermes profile, replacing the example path with your clone's absolute path:
```yaml
mcp_servers:
langextract:
command: uv
args: ["--directory", "/absolute/path/to/langextract-mcp", "run", "--frozen", "langextract-mcp"]
timeout: 180
sampling:
enabled: true
max_tokens_cap: 1024
timeout: 120
max_tool_rounds: 0
```
Use an absolute path to `uv` if your MCP host does not inherit your shell's PATH. Reload the MCP connection or start a fresh host session after configuration.
## Inference providers
The default (`LANGEXTRACT_MCP_PROVIDER=sampling`) uses the MCP host's sampling capability. The host must implement MCP sampling (`sampling/createMessage`); this server asks for a single text response, no tools, no included context, and at most 1,024 generated tokens. This is the preferred mode because the server does not need a new API key.
An operator may instead set `LANGEXTRACT_MCP_PROVIDER` to `gemini`, `openai`, or `ollama`, and optionally `LANGEXTRACT_MCP_MODEL`. OpenAI mode requires the optional extra and a locked install:
uv sync --frozen --all-groups --extra openai
Credentials remain server environment only: `LANGEXTRACT_API_KEY` for Gemini and `OPENAI_API_KEY` for OpenAI. Ollama uses its default local endpoint; the tool never receives a URL or provider kwargs. Network providers can disclose submitted text to the configured provider and can charge for tokens. Extraction passes multiply inference cost; smaller character buffers can cause more calls. Direct provider calls use one worker, disable retries where supported, and set a 120-second request timeout. Direct inference checks cancellation and a 100-request/120-second budget before each new request. An already-blocking SDK request may still run until its request timeout; cancellation prevents later requests rather than forcibly terminating the SDK.
This project deliberately does not load `.env` files; set environment variables in the host process.
## Grounding and visualization
`grounded_only=true` is the default. It removes model outputs LangExtract could not align to source text. A retained span may have `match_exact`, `match_greater`, `match_lesser`, or `match_fuzzy`; `exact_match` is computed by direct character comparison of `extraction_text` and `source_substring`. Every returned span includes `source_substring`, the literal substring at the reported character interval. Missing interval/status/substrings means the model output was not grounded and appears only when `grounded_only=false`.
Visualization accepts payload JSON, not paths. The wrapper bounds the payload before rendering and re-encodes the released renderer's embedded JSON with script-safe escaping; visible text, classes, and attributes are HTML-escaped. Treat returned HTML as display content, not trusted executable application code.
Extraction quality depends on the host model and examples. An empty extraction list is not proof that no relevant entities exist. Models may repeat few-shot examples; `grounded_only` removes outputs without a source span, not every possible semantic mistake. Review the actual source substring and `exact_match` flag for important uses.
## Calling the extraction tool
```json
{
"text": "Grace Hopper visited London.",
"prompt_description": "Extract people and cities using exact source text, in order of appearance.",
"examples": [{
"text": "Ada Lovelace visited Paris.",
"extractions": [
{"extraction_class": "person", "extraction_text": "Ada Lovelace"},
{"extraction_class": "city", "extraction_text": "Paris"}
]
}],
"visualization": true
}
```
Pass the returned annotated document to `langextract_visualize` as `annotated_document` to render it separately. The HTML is returned as text; saving it is the client's choice, not a server-side file operation.
## Checks
uv sync --frozen --all-groups --all-extras
uv run --frozen ruff check .
uv run --frozen ruff format --check .
uv run --frozen pytest
uv run --frozen python -m build --no-isolation
uv venv --clear .wheel-venv
uv pip install --python .wheel-venv/bin/python dist/*.whl
.wheel-venv/bin/python -c "import langextract_mcp.server"
For a real local-host smoke test, set `LX_SAMPLING_COMMAND` to a command that reads the LangExtract prompt on stdin and emits a real JSON extraction envelope, then run:
uv run --frozen python example/real_sampling_smoke.py
The smoke script is not a product provider and does not claim fixture output is live inference.
## License
Apache-2.0. The repository release must include the `LICENSE` file.TDQS
Scored across 3 tools
The three tools serve clearly distinct purposes: extraction, server status, and visualization. There is no overlap in functionality, and an agent can easily select the right tool based on the task.
All tools share the consistent 'langextract_' prefix followed by a single word. However, 'extract' and 'visualize' are verbs while 'status' is a noun, a minor deviation from a pure verb_noun pattern.
Three tools is well within the typical 3-15 range and each tool has a clear, non-redundant role. The server is focused and does not appear to have unnecessary or missing tools for its scope.
The core extraction, status, and visualization operations are covered, providing a complete basic workflow. Minor gaps exist, such as batch extraction, schema listing, or configuration updates, but agents can work around them.