npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

knownfy-mcp

v1.0.5

Published

MCP server for the Knownfy compliance intelligence API (KYC/KYB). Investigate entities, query relationship graphs, monitor watchlists and run bulk batches from Claude Desktop, Cursor or Claude Code.

Readme

knownfy-mcp

MCP (Model Context Protocol) server para la API de Knownfy. Expone la capa de inteligencia de compliance (KYC/KYB) como tools para que un agente —Claude Desktop, Cursor, Claude Code, etc.— pueda investigar entidades, consultar grafos de relaciones, monitorear watchlists y correr lotes masivos.

Framing: Knownfy expone información pública para apoyar el criterio del analista. No es una plataforma de decisión; los reportes son inteligencia, no veredictos.

Instalación

No hay que instalar nada: se corre con npx.

{
  "mcpServers": {
    "knownfy": {
      "command": "npx",
      "args": ["knownfy-mcp"],
      "env": {
        "KNOWNFY_API_TOKEN": "knfx_live_...",
        "KNOWNFY_API_URL": "https://api.knownfy.app"
      }
    }
  }
}
  • Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Cursor: .cursor/mcp.json en el workspace
  • Claude Code: claude mcp add knownfy -- env KNOWNFY_API_TOKEN=knfx_live_... npx knownfy-mcp

Autenticación (dual)

Knownfy acepta dos tipos de credencial — configurá una:

| Var | Cuándo | Header | Notas | |---|---|---|---| | KNOWNFY_BEARER_TOKEN | usuario con cuenta / subscription (JWT de Auth0) | Authorization: Bearer | Precede a la API key si están ambas. | | KNOWNFY_API_KEY | partner / máquina | X-API-Key | Generala en Settings → API keys. | | KNOWNFY_API_TOKEN | una sola var (lo que muestra /developers) | autodetecta | Si empieza con eyJ → Bearer; si no → X-API-Key. | | KNOWNFY_API_URL | opcional | — | Default https://api.knownfy.app. Sandbox: https://api.dev.knownfy.app. |

Cómo obtener la credencial

  • Usuario con cuenta (JWT): logueá en el front, DevTools → Network → cualquier request a la API → copiá el header Authorization: Bearer eyJ... (sin el prefijo Bearer ).
  • Partner (API key): https://knownfy.app/settings/api-keys → Generar clave. Se muestra una sola vez; guardala. Se revoca desde la misma pantalla.

Mandá una sola cabecera. El middleware evalúa el camino Bearer de Auth0 antes que el de X-API-Key, así que una key válida enviada también como Bearer muere como JWT inválido (401). El cliente ya resuelve esto solo — no lo pises a mano.

Tools (13)

| Tool | Qué hace | |---|---| | investigate_entity | Resuelve persona/empresa a entidad canónica y devuelve inteligencia + cobertura. Dice si investigó o si devolvió una corrida anterior; force: true encola una nueva. | | get_entity_intelligence | Composite de inteligencia de una entidad (tier, PEP/sanciones, señales, riesgo relacional). | | search_entities | Busca entidades ya investigadas en el workspace. | | get_entity_graph | Grafo de relaciones de una entidad, resumido: vecinos, histogramas y cautelas. Todo recorte se declara en warnings. | | get_tenant_graph | Grafo del workspace, resumido y ordenado por señalamiento. Todo recorte se declara en warnings. | | get_report | Reporte CDR completo. Acepta report_id (entidad ya investigada) o job_id (corrida recién encolada). | | watch_entity | Suscribe webhooks ante cambios del perfil de una entidad. | | list_watchlist | Lista las entidades en monitoreo. | | list_alerts | Lista alertas de watchlist. Cada alerta trae metadata.alert_type (direct / relational_risk): filtrá sobre ese campo, la API no tiene filtro server-side. | | link_entities | Crea una relación verificada por analista entre dos entidades. | | submit_bulk_batch | Encola hasta 500 entidades para investigación en paralelo. | | get_bulk_status | Progreso de un lote. | | get_bulk_intelligence | Inteligencia completa de todas las entradas terminadas de un lote. |

Flujo típico: investigate_entity → get_entity_intelligence → get_entity_graph / watch_entity. Para volumen: submit_bulk_batch → get_bulk_status → get_bulk_intelligence.

Desarrollo

npm install
npm run build      # compila TS → dist/
npm test           # tsc + guardas (node:test, sin deps nuevas)
npm start          # corre dist/index.js (stdio)

npm test corre también como prepublishOnly: un publish con las guardas en rojo no sale.

Probar con MCP Inspector:

npm run build && npx @modelcontextprotocol/inspector node dist/index.js

Changelog

1.0.5

El aviso de identidad que 1.0.4 estrenó podía mentir. Medido contra dev el mismo día que salió, sobre una entidad real, estas dos frases volvieron en el mismo objeto:

note:              "NO se investigó nada en esta llamada."
identity_warning:  "Se investigó sin CUIT/CUIL."

y tres campos más abajo, identifiers: [{type: "cuit", value: "20-…"}], con la fuente ARCA en status: "found". La entidad tenía el identificador guardado y la investigación lo había usado.

La causa: el aviso se calculaba con los argumentos de la llamada, antes de mirar la respuesta. Como el tax_id no viajaba en esa llamada, disparaba — sin enterarse de que el backend ya lo tenía. Es la misma familia que este cliente persigue, apuntando al otro lado: no vuelve limpio lo sucio, pero un aviso que aparece siempre deja de leerse, y el día que salga el verdadero va a estar tapado por el ruido que generó el falso.

  • Se calcula después del fetch y mirándolo. Si la ficha ya trae un ancla fiscal, no se advierte. Los tipos que cuentan como ancla se enumeran (cuit, cpf, rut, ruc, rfc…): la ficha también trae identificadores que no desambiguan a nadie, y contarlos apagaría la guarda justo cuando hace falta.
  • Se leen los dos niveles del payload. identifiers cuelga de la raíz, pero el mismo objeto trae un sub-objeto intelligence anidado. Leer el nivel equivocado devuelve false y el aviso vuelve a mentir — el mismo error una capa más adentro.
  • El tiempo verbal sigue a los hechos. Sobre una llamada que no investigó nada, ahora dice "la investigación previa se corrió sin …, y en la ficha tampoco hay uno guardado", en vez de afirmar una corrida que no ocurrió.

La guarda no se desactiva sola: sin ficha, con identifiers: [], o con un identificador que no ancla, la advertencia sigue saliendo. Hay un test para cada uno de esos tres casos, porque la diferencia entre un arreglo y un silenciador es exactamente esa.

1.0.4

El resumidor de grafo que salió en 1.0.3 leía el endpoint equivocado. Knownfy tiene dos grafos y hablan dos idiomas: /api/v2/tenant/graph manda source / target / relation_type / confidence, y /api/v1/entities/{id}/graph —el que usa get_entity_graph— manda source_node_id / target_node_id / relation / weight. El cliente pedía el segundo y leía los campos del primero.

Medido contra una entidad real: los vecinos salían sin id, sin nombre y sin tipo de vínculo (sólo source_type), y el histograma decía relation_types: {undeclared: 5}. Un agente recibía cinco relaciones anónimas y las narraba como "no se declara el tipo" — cuando el tipo estaba en la respuesta, intacto, bajo otra clave. Los 17 tests pasaban en verde, porque los fixtures los escribí desde la forma que supuse en vez de capturar una respuesta. Un test contra un esquema inventado no es una guarda: es una segunda copia de la misma suposición.

  • Se leen los dos vocabularios. Los nodos se indexan por node_id y por id (las aristas referencian el primero, la identidad viaja en el segundo), y la raíz se resuelve por target_node_id, entity_id o is_target, así que la arista que apunta hacia la entidad también resuelve su otro extremo.
  • weight no se publica como confianza. En el v1 está cableado en 3.0 para toda arista. Presentarlo como confidence fabricaba un orden con apariencia de medición. Sin confianza real, los vecinos se ordenan por respaldo (grounding_status) y se dice con qué criterio se ordenó.
  • topological_inference ahora cuenta como inferencia. Es "los conecto porque aparecen afiliados al mismo hub", la evidencia más débil del set — y era el 60% de las aristas del caso medido. 1.0.3 no emitía una sola cautela.
  • grounding_status: unverified sale nombrado, con su grounding_reason. De dónde salió una arista y si alguien la corroboró son dos ejes distintos.
  • Se declara cuándo las aristas exceden a los vecinos. El mismo par puede entrar dos veces con respaldo distinto en cada observación: contar filas dice "5 vínculos" sobre 4 contrapartes.
  • is_canonical no existe en el v1; se deriva de entity_id / raw_entity_id. Antes daba undefined para todos y ningún nodo contaba como crudo: nombres que nadie investigó se leían como entidades investigadas y limpias.
  • El tope de 200 del endpoint se nombra. El backend pide las relaciones con limit=200 cableado y no declara si topeó. No poder saber si cortó no es lo mismo que saber que no cortó.

La raíz: los fixtures de los tests pasan a ser respuestas capturadas de la API (src/fixtures.ts), copiadas verbatim en claves y estructura. Las 10 guardas nuevas fallan las 8 que corresponden contra el código de 1.0.3 — verificado corriéndolas contra el commit publicado, no contra la intención.

El contexto se aceptaba y se tiraba

investigate_entity declaraba extra_context en el schema y lo mandaba sólo en la rama force, que es la que va por /api/v1/investigations/. El body de resolve no lo llevaba — y resolve es el único camino por el que se encola la investigación de una entidad nueva. O sea: justo en el caso donde la investigación de verdad corre, el "para qué" se descartaba en silencio. Del otro lado había dos pérdidas más, arregladas en el mismo release (apply_extra_context en el backend): resolve no leía extra_context del body, y /api/v1/investigations/ lo escribía siempre bajo la clave del perfil, mientras la rama company del flow lee company_extra_context y nunca mira la otra.

La guarda de identidad que la UI tiene y el agente no

En la UI, si el país es LATAM y no cargaste identificador fiscal, un modal te frena y te hace elegir "Continuar sin identificador". Esa protección no llega al canal del agente por dos razones: un agente no puede clickear un modal, y volver tax_id obligatorio es peor que no tenerlo — un LLM frente a un campo requerido no pregunta, rellena, y un CUIT inventado no es una omisión sino una afirmación falsa que ancla la investigación a otra persona.

Así que la protección cambia de forma, no de umbral (src/identity.ts, espejo de LATAM_COUNTRIES y de la copia taxid_warning.* del front):

  • investigate_entity devuelve identity_warning cuando el país es LATAM y no hay tax_id, nombrando el documento que correspondía (CUIT/CUIL, CPF/CNPJ, RUT…) y diciendo qué hacer: pedirle el identificador al usuario y volver a llamar.
  • submit_bulk_batch lo resume por lote: cuántas filas de cuántas van sin ancla, nombrando hasta cinco. 500 avisos iguales no son 500 avisos, son ruido que tapa el único que importaba.
  • Las descripciones de tool piden preguntar ANTES, que es la única manera de que la guarda sea previa al gasto del crédito, como el modal.

1.0.3

Tres defectos medidos contra dev con una cuenta real. Los tres comparten forma: un resultado recortado o filtrado que no decía que lo estaba.

  • status: "all" devolvía vacío. El centinela lo inventó este cliente; la API nunca lo tuvo, y del otro lado entraba como WHERE status = 'all'. Medido: list_watchlist(status:"all") devolvía entries: [] en el mismo objeto que total_monitored: 9 y un histograma que sumaba 9. Nada fallaba: un 200 con lista vacía es indistinguible de un workspace sin nada monitoreado. Ahora "all" omite el parámetro.
  • Los grafos no entraban en una ventana de razonamiento. 139 KB para una entidad y 763 KB / 419 nodos para el workspace — pasando limit: 5, porque el backend aplica ese tope a la semilla de canónicas y cada punta de arista vuelve a entrar como nodo. Ahora se devuelve un resumen, y todo recorte —del backend o de este cliente— sale nombrado en warnings.
  • alert_type no existía del lado del servidor. Se declaraba en el schema y viajaba en la query string; Flask lo ignoraba. Un agente que pedía sólo las relacionales recibía todas y las narraba como relacionales. Se saca del schema; el campo viaja en metadata.alert_type de cada alerta. En su lugar se exponen severity y entity_id, que el endpoint sí filtra.

Primeras guardas del paquete (17 casos) y primer job de CI: hasta esta versión el cliente MCP no tenía tests, ni typecheck en CI, ni gate de publicación.

1.0.2

Un veredicto limpio y un veredicto sin datos se veían igual. Esta versión los separa.

  • get_report acepta report_id, no sólo job_id. Para una entidad ya investigada investigation_job_id es null, así que el informe CDR completo (~170 KB: qué fuentes se consultaron, cuáles fallaron, qué datasets se screenearon) era inalcanzable desde el MCP y la única lectura posible eran los ~1,5 KB de síntesis.
  • Bloque coverage nuevo en investigate_entity y get_entity_intelligence: fuentes consultadas con su estado, datasets screeneados, consultas ejecutadas, frescura — más warnings en prosa. La prosa importa: un null estructural no sobrevive a un LLM resumiendo su propia salida.
  • Las advertencias marcan calidad o confianza < 60, fuentes en error/timeout, crawls inalcanzables, cobertura parcial declarada, y el caso peor: screening: "clear" sin registro de ninguna consulta ejecutada.
  • investigate_entity ya no miente sobre lo que hizo. Antes decía "investigación completada" cuando sólo había resuelto una entidad preexistente. Ahora devuelve investigation_performed y un note que dice si los datos son de esta corrida o de una anterior (con fecha). El nuevo flag force encola una corrida real.
  • Con una clave de sandbox (environment: test) la API devuelve un job simulado (mock_…). El MCP lo detecta y lo dice, en vez de reportar una investigación que nunca corrió.

1.0.1

  • list_watchlist pegaba a /api/v2/watchlist sin barra final. La ruta canónica es /api/v2/watchlist/, así que Flask contestaba 308 y la credencial se perdía en el salto: el síntoma era un 401 que parecía "key mala" cuando en realidad era la URL.
  • El cliente ahora sigue los 307/308 a mano re-enviando las cabeceras, para que un redirect nunca vuelva a leerse como un problema de credencial.
  • El source del paquete pasa a estar versionado (clients/knownfy-mcp/). Hasta 1.0.0 sólo existía el dist/ publicado en npm y no se podía reconstruir.

Notas

  • Disponible en planes Pro y Teams.
  • La superficie cubre el camino curado v1 de developers. Para todos los endpoints, usá la colección de Postman Knownfy API (Partners).