Skip to main content
Glama
gauravsdama

Binoculars Local MCP

by gauravsdama

Binoculars Local MCP

This repository is a local, security-focused derivative of Binoculars, the zero-shot AI-text detection method introduced in Spotting LLMs With Binoculars. The upstream authors created the scoring method and original implementation. This project adds a read-only MCP interface, offline model loading, process-level network blocking, a compact local model profile, and profile-specific calibration.

The detector returns a statistical signal, not proof that a person or model wrote a passage. It should not be the sole basis for grading, discipline, employment decisions, accusations, or other consequential action.

Local security boundary

  • MCP transport is stdio; the server does not listen on a port.

  • Before MCP, model, or HTTP-capable dependencies import, the MCP process forces Hugging Face and Transformers offline modes and installs a guard for common Python TCP/UDP socket egress. Local Unix-domain sockets remain available to libraries.

  • Model arguments must resolve to existing local directories; remote model IDs are rejected.

  • Loads require local_files_only=True, trust_remote_code=False, and SafeTensors weights.

  • The compact profile verifies SHA-256 digests for every required config, tokenizer, vocabulary, merge, generation-config, and SafeTensors file before reporting readiness or loading the detector. Cached digests are invalidated when file identity or metadata changes.

  • Both tools are read-only and declare openWorldHint=false.

  • Input is limited to 100,000 characters, and the compact profile analyzes at most 256 tokens.

Installing Python packages may contact a package index. Model acquisition is a separate explicit step. Once configured, model inference uses only verified local files. The in-process guard covers common Python socket APIs; it is defense in depth, not an operating-system sandbox or a guarantee against native code, subprocesses, or a compromised dependency. Use an OS-level network sandbox when that stronger boundary is required.

Related MCP server: avoid-ai-writing-mcp

Install

Python 3.11 through 3.13 is supported. This checkout uses Python 3.12 with uv:

On a Mac, begin with the optional setup helper. Its default mode only reports macOS, processor, memory, disk, Python, and uv readiness:

./scripts/setup_macos.sh

Dependency installation and model acquisition remain separate choices:

./scripts/setup_macos.sh --install
./scripts/setup_macos.sh --download-models
source .models/env.zsh

The second command explains the download before it starts, fetches the two pinned model revisions, verifies every file in each pinned model manifest, and writes an ignored local environment file. Use --yes only after reviewing the requested operation.

For manual setup:

uv sync --python python3.12 --extra dev --locked

Model weights stay outside Git under .models/. Point the server at compatible local directories:

export BINOCULARS_PROFILE=qwen2.5-0.5b
export BINOCULARS_OBSERVER_MODEL=/path/to/qwen2.5-0.5b-base
export BINOCULARS_PERFORMER_MODEL=/path/to/qwen2.5-0.5b-instruct
export BINOCULARS_DEVICE=cpu  # or mps, cuda, cuda:N, or auto

Without both model directories, the MCP server can still start, but binoculars_status reports that analysis is not ready. Configuration is captured once when the MCP process starts; restart it after changing these environment variables.

Compact model profile

The qwen2.5-0.5b profile pairs Qwen2.5-0.5B with Qwen2.5-0.5B-Instruct. Their SafeTensors weights use about 1.9 GB together, substantially less than the two 7B models in the original configuration. Repository revisions, file hashes, thresholds, token limit, and the calibration summary are pinned in binoculars/profiles.py.

This compact profile was calibrated on 360 local texts and evaluated on a separate 240-text holdout derived from CC-News, CNN, and PubMed human/Falcon samples. The calibration corpus is not distributed with this repository. On that holdout, accuracy mode reached 88.3%. Conservative mode measured a 2.5% human false-positive rate and a 60.0% machine true-positive rate. This is a limited local benchmark, not a general performance guarantee and not the original paper's evaluation.

MCP tools

binoculars_status

Reports model readiness, selected profile, weight-hash checks, stdio transport, the active network-guard probe, and calibration context. Call it before analysis.

binoculars_analyze_text

Accepts text and an optional threshold mode:

  • low-fpr is the default and is more conservative about AI-generated labels.

  • accuracy provides the profile's broader screening threshold.

The result includes the score, threshold, mode, label, model profile, device, character count, original tokenizer count, analyzed tokenizer count, and whether truncation occurred. Lower scores indicate a stronger AI-generated signal under the configured threshold. Inputs with fewer than eight analyzed tokens are rejected instead of labeled, as are non-finite numerical results. Samples of roughly 200-300 words are generally more useful than very short passages.

Run and verify

Start the stdio server:

.venv/bin/binoculars-mcp

Available project checks are:

.venv/bin/pytest -q
.venv/bin/ruff check binoculars tests
.venv/bin/bandit -q -c pyproject.toml -r binoculars
.venv/bin/pip-audit

Requests wait at most 120 seconds for another analysis to release the detector lock. Set BINOCULARS_LOCK_TIMEOUT_SECONDS between 0.1 and 3600 to change that queueing bound. Once native model inference starts, MCP cancellation cannot safely interrupt it in-process; process isolation is required for a hard execution deadline.

The default test suite uses tiny local models and runs without downloads. With the pinned models already present, run the CPU/MPS golden and performance checks explicitly:

BINOCULARS_RUN_DEVICE_TESTS=1 \
BINOCULARS_OBSERVER_MODEL=/path/to/qwen2.5-0.5b-base \
BINOCULARS_PERFORMER_MODEL=/path/to/qwen2.5-0.5b-instruct \
uv run pytest -q tests/test_device_regression.py

Inspect a Codex registration with:

codex mcp get binoculars-local
codex mcp list

Inference for a local stdio server uses the machine's MPS or CPU resources. It does not use hosted Codex inference compute.

Local performance

Measurements below were taken on a 24 GB M4 Pro MacBook Pro with macOS 26.6.2. Each run loaded both pinned Qwen2.5-0.5B models and analyzed the same 1,196-character passage six times.

Device

Model load

First call

Repeated median

Repeated p95

Memory

MPS

1.54 s

0.31 s

0.159 s

0.159 s

538 MiB peak RSS; 3,123 MiB MPS driver

CPU

1.73 s

1.25 s

0.346 s

0.425 s

5,234 MiB peak RSS

The first uncached MPS process took 2.85 seconds to load the models and 2.21 seconds for its first call. Later runs benefited from operating-system file caches. Reproduce the measurement with:

uv run python scripts/benchmark_local.py --device mps --iterations 6
uv run python scripts/benchmark_local.py --device cpu --iterations 6

Limitations

Binoculars signals are weaker on short, non-English, heavily quoted, or memorized text. Results also depend on the selected observer/performer models and calibration data. Report the score, threshold, mode, and profile together; do not translate one label into certainty about authorship.

Attribution

The Binoculars score and original implementation are by Abhimanyu Hans, Avi Schwarzschild, Valeriia Cherepanova, Hamid Kazemi, Aniruddha Saha, Micah Goldblum, Jonas Geiping, and Tom Goldstein. See the paper, upstream repository, and preserved license.

The local MCP runtime, offline and network guards, compact profile configuration, weight verification, security tests, and local calibration are Gaurav Dama's derivative work. The underlying detection method remains the upstream authors' work.

Available Tools

2 tools
binoculars_analyze_textB
Read-onlyIdempotent

Assess text using pre-existing local models; never proof of authorship.

NameRequiredDescriptionDefault
paramsYes

Output Schema

NameRequiredDescription

No output parameters

TDQS

B3.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, and destructiveHint false. The description adds meaningful behavioral context beyond those: the analysis is not proof of authorship and uses local models, which is a useful limitation for an agent to know.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single front-loaded sentence with no filler. Every element earns its place: the action, the local-model context, and the critical limitation about authorship.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple read-only tool with annotations, schema-provided parameter descriptions, and an output schema, the description covers the main non-obvious context. The absence of explicit usage guidance versus alternatives is the main gap preventing a higher score.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool description gives no parameter-level guidance for `text` or `mode`. With schema description coverage at 0%, the description was expected to compensate; it does not. The input schema itself documents the parameters well, which prevents a score of 1.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action and resource: 'Assess text using pre-existing local models.' It is semantically distinct from the sibling `binoculars_status`, though it does not explicitly name that sibling or draw a contrast.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no explicit when-to-use guidance or exclusions beyond the caution 'never proof of authorship.' It does not mention alternatives or describe situations where this tool should be preferred, so the agent must infer usage from the name and schema.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

binoculars_statusC
Read-onlyIdempotent

Check local model configuration and the scoped Python socket guard.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription

No output parameters

TDQS

C2.9/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, openWorldHint=false, and destructiveHint=false, so the safety profile is fully covered. The description adds no behavior beyond what annotations state — it doesn't say what a failed check yields, whether it hits the network, or what side effects (none) to expect.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short sentence with no filler, appropriately front-loaded. It is efficient, though the second clause is vague rather than informative.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With zero parameters and an output schema present, the description needn't explain returns, and it is minimally adequate. However, the phrase "scoped Python socket guard" is unexplained jargon that leaves an agent unsure what is actually being verified.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. There is no parameter semantics to explain and the description correctly introduces none.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a verb ("Check") and a resource ("local model configuration and the scoped Python socket guard"), but the resource is jargon-heavy and the purpose remains abstract. The distinction from the sibling binoculars_analyze_text is only implied by the word "status".

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to call this versus binoculars_analyze_text, no prerequisites, and no statement about typical call context. The agent must infer that this is a readiness/preflight check.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedbinoculars_analyze_text
    • First observedbinoculars_status

TDQS

B3.4/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct purposes: one performs text analysis, while the other reports local model configuration and socket guard status. There is no plausible overlap or risk of misselection.

Naming Consistency5/5

Both tools consistently use the `binoculars_` prefix followed by a clear snake_case action/object name. The convention is predictable and uniform across the entire surface.

Tool Count3/5

Two tools is borderline thin for a server that could reasonably expose model listing, configuration, or batch analysis operations. The count is defensible for a very narrow utility, but it feels under-scoped.

Completeness3/5

The surface covers the core analyze operation and a status check, but lacks obvious companion operations such as listing available models, inspecting a specific model, or configuring local model settings. Agents may hit dead ends when they need to discover or modify model configuration.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Provides local, deterministic scoring and detailed auditing of text for AI-writing patterns via two read-only tools, with no network calls or language models.
    2
    2 npm
    3
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Provides MCP-compatible AI clients with offline text analysis and rewriting tools, including statistics, extractive summaries, keywords, readability scores, case conversion, entity extraction, and diffing, all running locally without API keys or network calls.
    7
    MIT