gristmill-mcp
gristmill-mcp
Un servidor MCP que inspecciona código generado por IA y devuelve una lista determinista de infracciones estructurales y de seguridad, para que un agente de codificación de IA pueda corregir su propia salida antes de que el código llegue a producción.
Grist es el grano que se lleva al molino para ser molido. La salida de IA es grist — materia prima genuinamente valiosa, pero sin procesar. El molino le da estructura.
La IA escribe el grist. Gristmill lo convierte en código que puedes enviar.
Por qué un servidor MCP, no una habilidad
Una habilidad es texto cargado en el contexto del modelo — cambia lo que el modelo sabe. Un servidor MCP es un programa que el modelo ejecuta — cambia lo que el modelo puede hacer.
Las guías de estilo («prefiere clases sobre funciones sueltas») pertenecen a una habilidad. La verificación («este archivo tiene 7 funciones de nivel superior en las líneas 12, 40, 66…») requiere ejecutar código contra el archivo. Un modelo que lee su propia salida y razona «esto parece tener demasiadas funciones» es una suposición disfrazada de observación — no tiene una verdad objetiva sobre qué significa «demasiadas» en este archivo, ni una forma fiable de contar. Gristmill analiza el AST y cuenta. Esa distinción — instrucción versus ejecución — es la razón por la que esto existe como servidor en lugar de un párrafo de consejo.
El servidor nunca llama a un LLM, nunca varía entre ejecuciones sobre la misma entrada y nunca emite una puntuación de confianza. Misma entrada → salida byte-idéntica, siempre. Esa determinismo es el producto completo. La capa de IA se sitúa por encima de este servidor, consumiendo sus hallazgos y decidiendo qué hacer al respecto — el trabajo del servidor termina al informar hechos con números de línea.
Related MCP server: code-verify-mcp
Instalación
git clone <this repo> gristmill-mcp
cd gristmill-mcp
python3 -m venv .venv
.venv/bin/pip install -e .Claude Code
Regístralo con la CLI, apuntando al script de consola del venv:
claude mcp add gristmill -- /absolute/path/to/gristmill-mcp/.venv/bin/gristmill-mcpO añádelo directamente a tu configuración de MCP (.mcp.json en un proyecto, o tu configuración global de Claude Code):
{
"mcpServers": {
"gristmill": {
"command": "/absolute/path/to/gristmill-mcp/.venv/bin/gristmill-mcp"
}
}
}Otros clientes MCP
Cualquier cliente MCP basado en stdio puede lanzar el mismo binario — gristmill-mcp (o python3 -m gristmill.server dentro del venv) habla el transporte stdio MCP estándar sin configuración específica del cliente.
Línea de comandos (sin cliente MCP)
Para pruebas locales, o para reproducir el ejemplo detallado a continuación, una CLI ligera envuelve el mismo motor:
.venv/bin/gristmill-verify path/to/file_or_dir [--checks secrets structure comment_slop] [--severity-floor warning] [--json]Ejemplo detallado
demo/billing.py, un primer borrador sin editar de un helper de Stripe para facturación:
import stripe
# I've added this as you requested — sets up the Stripe client
STRIPE_SECRET_KEY = None # was a literal sk_live_... key — see note below
stripe.api_key = STRIPE_SECRET_KEY
def customer_create(config):
return stripe.Customer.create(**config)
def customer_delete(config):
return stripe.Customer.delete(config["id"])
def customer_find(config):
return stripe.Customer.retrieve(config["id"])
def customer_update(config):
return stripe.Customer.modify(config["id"], **config).venv/bin/gristmill-verify demo/billing.pySalida con un literal en forma de clave real de Stripe en lugar del None anterior:
gristmill: 1 files scanned, 0 skipped (2 error, 4 warning, 0 info) in 1ms
[WARNING] STR002 billing.py:1 4 top-level functions share the prefix `customer_` — consider a `Customer` class or module
[WARNING] STR003 billing.py:1 4 top-level functions take a first parameter named `config` — consider making it instance state
[WARNING] CMT001 billing.py:3 Comment addresses the reader conversationally ('as you requested')
[ERROR ] SEC006 billing.py:4:22 Stripe live key assigned to `STRIPE_SECRET_KEY`
[ERROR ] SEC010 billing.py:4:22 String literal assigned to `STRIPE_SECRET_KEY`, which looks credential-shaped
[WARNING] SEC011 billing.py:4:22 High-entropy string literal (5.1 bits/char) assigned to `STRIPE_SECRET_KEY`(Las rutas de archivo se muestran relativas al .gristmill.toml más cercano — demo/ lleva el suyo propio para que la salida de este ejemplo se mantenga estable independientemente de la configuración del proyecto de nivel superior.)
Nota: La protección de push de GitHub bloquea cualquier archivo enviado que contenga un secreto en formato real — incluso en un comentario o un bloque de código markdown, incluido este README.
demo/billing.pyactualmente tiene la clave reemplazada porNonepara desbloquear el push inicial; esto es un TODO para restaurarlo (mediante una excepción de escaneo de secretos en lista blanca) para que la demo vuelva a estar activa.
La bandera --json (o la herramienta MCP verify, que devuelve ambas) proporciona la forma estructurada completa — archivo, línea, columna, una cadena de sugerencia estática y un campo evidence censurado (sk_l… (49 caracteres), nunca la clave en sí).
Herramientas
verify
Inspecciona archivos fuente en busca de secretos, problemas estructurales y comentarios de baja calidad. Devuelve hallazgos deterministas con rutas de archivo y números de línea. Llama a esto después de generar o editar código, antes de presentarlo como terminado.
Entrada: paths (archivos o directorios, obligatorio), checks (subconjunto opcional de secrets/structure/comment_slop, por defecto todos), severity_floor (opcional, por defecto info).
Salida: un resumen compacto legible por humanos, seguido del JSON estructurado completo — archivo, línea, columna, mensaje, evidencia censurada y una cadena de sugerencia estática por regla. Los hallazgos se ordenan por path, luego line, luego rule_id, siempre — esa estabilidad es lo que hace que las ejecuciones sean byte-idénticas y permite que un modelo navegue directamente al problema.
explain_rule
Toma un rule_id (ej. SEC001) y devuelve su fundamento, qué captura, qué omite y cómo suprimirlo — el mismo contenido que docs/RULES.md, servido bajo demanda para que la salida de verify pueda mantenerse concisa.
Reglas
Regla | Comprobación | Título | Gravedad por defecto |
| secrets | ID de clave de acceso de AWS | error |
| secrets | Clave de acceso secreta de AWS | error |
| secrets | Token de GitHub | error |
| secrets | Clave de API de Google | error |
| secrets | Token de Slack | error |
| secrets | Clave en vivo de Stripe | error |
| secrets | Bloque de clave privada | error |
| secrets | JWT | error |
| secrets | URI de base de datos con contraseña en línea | error |
| secrets | Asignación genérica con forma de credencial | error |
| secrets | Literal de cadena de alta entropía | warning |
| structure | Demasiadas funciones de nivel superior (límite por defecto 5) | warning |
| structure | Prefijo de nombre de función compartido (3+ funciones) | warning |
| structure | Nombre de primer parámetro repetido (3+ funciones) | warning |
| structure | Función demasiado larga (límite por defecto 60 líneas) | warning |
| structure | Estado mutable a nivel de módulo, mutado en otra parte del archivo | warning |
| comment_slop | Dirección conversacional en comentario | warning |
| comment_slop | El comentario narra lo obvio | info |
| comment_slop | Bloque de comentario demasiado grande en una función corta | info |
| comment_slop | Andamio de marcador de posición dejado en su lugar | warning |
| comment_slop | Banners repetidos de separación de secciones (4+ por archivo) | info |
Fundamento completo, notas de falsos negativos e instrucciones de supresión por regla: docs/RULES.md.
Configuración
.gristmill.toml en la raíz del proyecto, todas las claves opcionales:
[checks]
enabled = ["secrets", "structure", "comment_slop"]
[structure]
max_top_level_functions = 5
max_function_lines = 60
[secrets]
entropy_threshold = 4.5
[ignore]
paths = ["legacy/**", "vendor/**"]
rules = ["CMT003"]Un archivo .gristmillignore (sintaxis de gitignore) funciona junto con [ignore] paths. También se respeta la supresión en línea en la línea marcada o en la línea superior:
SUPPRESSED = "ghp_" + "..." # gristmill: ignore SEC003// gristmill: ignore SEC003
const suppressed = "ghp_" + "...";Soporte de lenguajes
Python — soporte completo (stdlib
astytokenize).JavaScript/TypeScript — soporte completo, mediante
tree-sittercon las gramáticas compiladastree-sitter-javascriptytree-sitter-typescript, en lugar de delegar a un analizador basado en Node. Esto intercambia una dependencia compilada de Python por independencia de que el host tenga Node instalado —structureycomment_slopfuncionan de manera idéntica independientemente de sinodeestá enPATH, y proporciona un AST real en lugar de una alternativa solo de texto.Cualquier otro — la comprobación
secretsaún se ejecuta (está basada en regex y es independiente del lenguaje);structureycomment_slopse omiten para ese archivo, informado enskipped_paths.
Limitaciones
Lee esto antes de confiar en la herramienta más de lo que se ha ganado:
secretssolo captura cadenas con forma o de alta entropía. Una contraseña humana de baja entropía comohunter2nunca será marcada — no hay una forma fiable de distinguirla de una cadena corta normal. Las credenciales ensambladas en tiempo de ejecución (concatenación de cadenas,os.environ.get(...) or "fallback", piezas decodificadas en base64) son invisibles para un pase de regex/entropía sobre texto estático.Los problemas estructurales que abarcan varios archivos son invisibles.
structuremira un archivo a la vez; una clase que debería dividirse entre archivos, o lógica duplicada en dos módulos diferentes, está fuera del alcance.El CMT002 de
comment_slopes deliberadamente estrecho. Es la regla con mayor riesgo de falsos positivos del conjunto, por lo que está implementada para sesgar fuertemente hacia el silencio — pasará por alto narraciones reales con mucha más frecuencia de la que marcará en exceso. Consultadocs/RULES.mdpara la regla exacta de coincidencia de subconjuntos.Los lenguajes fuera de Python y JS/TS solo reciben cobertura de secretos. Sin análisis estructural o de comentarios para Go, Rust, Ruby, etc. en v1.
Esto no es un escáner de secretos en el historial de git. Inspecciona el árbol de trabajo tal como se proporciona. Una clave que fue confirmada y luego eliminada del archivo actual no es preocupación de esta herramienta (un escáner de historial de git es una herramienta diferente y complementaria).
Sin corrección automática. Gristmill informa; el modelo que llama decide qué y cómo cambiar. Esa división es intencional (ver «Por qué un servidor MCP, no una habilidad» arriba), pero significa que una llamada a
verifypor sí sola nunca arregla nada.
Una herramienta que sobrevende su cobertura es peor que una que es sincera sobre sus puntos ciegos — el silencio supera a la falsa confianza aquí tanto como supera a los hallazgos ruidosos.
Hoja de ruta
Explícitamente fuera del alcance para v1, en orden de prioridad aproximado:
Corrección automática / generación de parches (el modelo que llama hace esto hoy, usando los hallazgos de
verify)Comprobación de frescura de dependencias y CVE (necesita llamadas de red a registros de paquetes — un v2 natural)
Soporte de lenguajes más allá de Python y JavaScript/TypeScript
Escaneo de historial de git para secretos que fueron confirmados y luego eliminados
Un servicio alojado, interfaz web o panel de control
Desarrollo
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest tests/ -qRegenera docs/RULES.md después de editar src/gristmill/rules.py:
.venv/bin/python3 scripts/generate_rules_doc.pyLas pruebas cubren (tests/): salida de archivo dorado para un directorio de fixtures conocido como sucio, determinismo 10x con y sin paralelismo, un corpus de falsos positivos que debe producir cero hallazgos, censura (ningún secreto bruto llega a ningún campo de salida), y resiliencia (sintaxis inválida, archivos binarios, vacíos y demasiado grandes nunca fallan una ejecución).
Licencia
MIT — consulta LICENSE.
Available Tools
2 toolsexplain_ruleA
Look up a gristmill rule by id (e.g. SEC001, STR002, CMT004): its rationale, what it catches, what it misses, and how to suppress it.
| Name | Required | Description | Default |
|---|---|---|---|
| rule_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It correctly implies a read-only operation ('Look up') and lists the content returned. However, it does not explicitly state that the tool has no side effects, nor does it mention authorization requirements, rate limits, or error handling for invalid rule IDs. A 3 is adequate but leaves gaps.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence that is front-loaded with the action and resource, includes concrete examples in parentheses, and conveys the full return intent. There is no wasted text; every part earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the tool's purpose, the parameter, and the core return values. An output schema exists to handle return type details, so the description does not need to reiterate those. However, it omits mention of what happens if the rule ID is invalid or missing (e.g., error or null response), which would make it more complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0% (no parameter descriptions in the input schema), so the description must compensate. It adds value by specifying the parameter is a 'rule id' and provides examples (SEC001, STR002, CMT004), hinting at a consistent format. However, it does not fully specify the pattern or acceptable formats, leaving ambiguity for the agent.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the verb 'Look up', identifies the resource as a 'gristmill rule', and specifies exactly what information is returned: rationale, what it catches, what it misses, and how to suppress it. This distinguishes it from the sibling tool 'verify', which likely performs a different function.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage when you need details about a specific rule (e.g., by its ID), but it does not explicitly state when to use this tool versus the sibling 'verify', nor does it provide guidance on when not to use it or any prerequisites. More specific exclusions or comparisons would improve this dimension.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
verifyA
Inspect source files for secrets, structural problems, and low-quality comments. Returns deterministic findings with file paths and line numbers. Call this after generating or editing code, before presenting it as finished.
| Name | Required | Description | Default |
|---|---|---|---|
| paths | Yes | ||
| checks | No | ||
| severity_floor | No | info |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Without any annotations, the description must disclose behavioral traits fully. It states that findings are 'deterministic' and include 'file paths and line numbers,' which adds value. However, it does not address permissions, side effects (though likely read-only), rate limits, or what happens when no issues are found.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three efficiently structured sentences: purpose, output nature, and usage timing. No redundant or extraneous content. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The presence of an output schema partially offsets the need to describe return values. However, the description does not explain how the three parameters interact or provide examples for common use cases, leaving gaps for a tool invoked after code generation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It only implicitly refers to 'paths' via 'source files' and does not explain 'checks' (the enum options) or 'severity_floor' at all. This forces the agent to rely solely on parameter names, which are insufficient.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('inspect') and resource ('source files') and enumerates three concrete issue types (secrets, structural problems, low-quality comments). With only one sibling tool 'explain_rule', the purpose is clearly distinct.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly tells when to call this tool: 'after generating or editing code, before presenting it as finished.' It provides clear context but does not mention when not to use it or compare to alternatives.
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.
2 tool updates
v0.1.0- First observed
explain_rule - First observed
verify
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: one is for static analysis of code, the other for documentation of rules. An agent would not confuse them.
The naming is inconsistent: 'verify' uses a plain verb while 'explain_rule' uses verb_noun pattern. Both are clear in isolation, but the lack of a unified pattern (e.g., 'verify_code' vs 'explain_rule') makes the set feel ad-hoc.
With only 2 tools, the server feels thin for a tool suite called 'gristmill-mcp'. A code analysis server typically needs more tools like listing rules or scanning for specific categories to feel properly scoped.
The server only provides a scan tool and a rule lookup tool, but is missing operations like listing all rules, skipping specific rules, or generating reports. Users cannot discover available rules without knowing their IDs, creating a dead end.
Maintenance
Related MCP Connectors
An MCP server that gives your AI access to the source code and docs of all public github repos
Official DevSpeak MCP server — translate technical text into formal specs from any AI IDE or agent
MCP server teaching AI agents to implement TideCloak: auth, E2EE, IGA, security analysis
Find, compare, and audit software for AI agents. Scored registry of tools and MCP servers.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceAn MCP server that provides AI coding agents with AST-accurate, context-budget-aware codebase querying, safety gates, and team policy integration via structured tools and a local plugin layer.104 npm4MIT
- AlicenseNot gradedqualityCmaintenanceAn MCP server for verifying AI-generated code quality, security, and performance, addressing trust gaps in AI coding assistants.MIT
- FlicenseNot gradedqualityCmaintenanceMCP server that helps AI agents inspect Minecraft project evidence (crash logs, mod files, datapacks) before writing development code.2-
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI coding agents structured access to a project's architecture, rules, modules, and technical decisions.MIT