Référence d'outil MCP

Rechercher des contacts

search_persons

search_persons retrouve des contacts : plein texte déterministe sur tous les champs descriptifs de la fiche — nom, notes, email, téléphone, adresse — combiné à des filtres sur le graphe relationnel : restriction par kind, et par le rôle porté par un contact lié (with_role / without_role). Il ne crée jamais rien.

Sémantique

Comment le matching fonctionne

La requête s'exécute en recherche plein texte PostgreSQL sur les champs descriptifs indexés du contact. Le matching est insensible aux accents (« francois » trouve « François »), par mots entiers avec racinisation, pas par sous-chaîne. Les filtres de rôle s'exécutent dans la même fonction base de données, restreinte par visibilité, que le matching texte.

  • L'index plein texte couvre exactement les champs descriptifs : nom, notes, email, téléphone et adresse.
  • Les numéros d'immatriculation et de TVA ne sont pas dans l'index plein texte : lisez-les sur la fiche elle-même (get_person_context) plutôt que de les chercher via query.
  • Insensible aux accents dans les deux sens : une requête sans accents matche le texte stocké accentué, et une requête accentuée matche le texte sans accents.
  • Un rôle matche le label d'une relation (lu dans les deux sens) OU un tag porté par le contact lié : with_role=fournisseur trouve les contacts reliés à quelqu'un étiqueté fournisseur ou tagué fournisseur.
  • without_role est un anti-join exécuté côté serveur : il ne garde que les contacts sans AUCUN lien correspondant au rôle, évalué sur toute la base de contacts — pas un filtre côté client sur une première page de résultats.
  • La visibilité s'applique aux contacts renvoyés ET aux deux extrémités de chaque relation considérée : un lien vers un contact que vous ne pouvez pas voir ne matche aucun rôle et n'apparaît pas dans links.
  • Les réponses sont plafonnées à limit (au plus 50) contacts triés par nom d'affichage, sans curseur de pagination : si une recherche risque de dépasser le plafond, resserrez-la avec query, kind ou un filtre de rôle.
Entrée

Paramètres

Tout ce que l'outil accepte, directement depuis l'inputSchema en vigueur. Tous les paramètres sont facultatifs.

ParamètreTypeDescription
querystring (<= 200)Recherche plein texte sur le nom, les notes, l'email, le téléphone et l'adresse du contact. Insensible aux accents, par mots entiers avec racinisation, pas de sous-chaîne.
kind"individual" | "company"Restreint aux personnes physiques (individual) ou aux entreprises (company).
with_rolestring (<= 100)Uniquement les contacts ayant un contact lié dans ce rôle — matché sur le label de la relation (dans les deux sens) ou sur un tag du contact lié.
without_rolestring (<= 100)Uniquement les contacts sans AUCUN contact lié dans ce rôle. Exemple : kind=company avec without_role=commercial liste les entreprises sans commercial.
limitnumberNombre maximal de résultats. 20 par défaut, plafonné à 50.
Sortie

Réponse

Chaque contact revient sous forme compacte : identité, téléphone et email, un aperçu des notes, les tags, et ses liens relationnels avec leurs labels orientés. Récupérez la fiche complète avec get_person_context.

Exemple de structuredContent
{
  "contacts": [
    {
      "id": "b2ce0d4f-7e19-4c2b-8f3a-5d6e7f8a9b0c",
      "display_name": "François Martin",
      "kind": "individual",
      "phone": "+33 6 12 34 56 78",
      "email": "francois@boulangerie-martin.fr",
      "notes": "Prefers morning deliveries - ring the back door...",
      "tags": [
        "supplier"
      ],
      "links": [
        {
          "id": "0a1b2c3d-4e5f-4789-8bcd-ef0123456789",
          "name": "Boulangerie Martin",
          "kind": "company",
          "relationship": "manager",
          "note": null
        }
      ]
    }
  ]
}
Résultat vide
{
  "contacts": []
}
outputSchema, tel que servi par tools/list
{
  "type": "object",
  "properties": {
    "contacts": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "display_name": {
            "type": "string"
          },
          "kind": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "phone": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "email": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "notes": {
            "type": "string"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "links": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "relationship": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "note": {
                  "anyOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "additionalProperties": {}
            }
          }
        },
        "required": [
          "id",
          "display_name",
          "kind",
          "phone",
          "email",
          "notes",
          "tags",
          "links"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "contacts"
  ],
  "$schema": "http://json-schema.org/draft-07/schema#",
  "additionalProperties": false
}
Recettes

Exemples d'utilisation

01

Retrouver un contact par un détail descriptif

Demandez à votre assistant
« Qui était le contact qui préfère les livraisons le matin ? »
Arguments MCP
{
  "query": "morning deliveries"
}
Réponse (abrégée)
{
  "contacts": [
    {
      "id": "b2ce0d4f-...",
      "display_name": "François Martin",
      "kind": "individual",
      "notes": "Prefers morning deliveries - ring the back door...",
      "tags": [
        "supplier"
      ]
    }
  ]
}
02

Chercher malgré un accent manquant

Demandez à votre assistant
« Trouve le contact francois. »
Arguments MCP
{
  "query": "francois"
}
Réponse (abrégée)
{
  "contacts": [
    {
      "id": "b2ce0d4f-...",
      "display_name": "François Martin",
      "kind": "individual",
      "email": "francois@boulangerie-martin.fr",
      "tags": [
        "supplier"
      ]
    }
  ]
}
03

Les entreprises ayant un contact dans un rôle

Demandez à votre assistant
« Quelles entreprises ont un contact commercial ? »
Arguments MCP
{
  "kind": "company",
  "with_role": "sales"
}
Réponse (abrégée)
{
  "contacts": [
    {
      "id": "7d8e9f0a-...",
      "display_name": "Atelier Lemaire",
      "kind": "company",
      "tags": [
        "client"
      ],
      "links": [
        {
          "id": "3c4d5e6f-...",
          "name": "Claire Lemaire",
          "kind": "individual",
          "relationship": "sales",
          "note": null
        }
      ]
    }
  ]
}
04

Les entreprises sans un rôle

Demandez à votre assistant
« Quelles entreprises n'ont pas encore de commercial ? »
Arguments MCP
{
  "kind": "company",
  "without_role": "sales"
}
Réponse (abrégée)
{
  "contacts": [
    {
      "id": "9e0f1a2b-...",
      "display_name": "Garage Petit",
      "kind": "company",
      "tags": [
        "prospect"
      ],
      "links": []
    }
  ]
}
05

Un rôle peut venir d'un label ou d'un tag

Demandez à votre assistant
« Qui sont nos fournisseurs ? »
Arguments MCP
{
  "with_role": "supplier"
}
Réponse (abrégée)
{
  "contacts": [
    {
      "id": "b2ce0d4f-...",
      "display_name": "François Martin",
      "kind": "individual",
      "tags": [],
      "links": [
        {
          "id": "0a1b2c3d-...",
          "name": "Boulangerie Martin",
          "kind": "company",
          "relationship": "supplier",
          "note": null
        }
      ]
    },
    {
      "id": "5f6a7b8c-...",
      "display_name": "Vergers Rousseau",
      "kind": "company",
      "tags": [
        "supplier"
      ],
      "links": []
    }
  ]
}
Contrat

Garanties et limites

  • Les deux recherches sont des recherches plein texte PostgreSQL déterministes : même requête, mêmes données, mêmes résultats. Rien de sémantique, pas de vecteurs — la requête matche des mots, pas des sens.
  • La visibilité est résolue avant le matching : le serveur restreint d'abord la requête aux enregistrements que l'appelant a le droit de voir, puis seulement cherche des correspondances.
  • Un enregistrement invisible est indistinguable d'un enregistrement inexistant : la recherche ne confirme jamais l'existence de données masquées.
  • Les index sont recalculés dans la même transaction que chaque écriture : une entrée est cherchable à l'instant où elle est créée, sans indexeur en arrière-plan ni fenêtre d'incohérence.
  • Historis n'exécute aucun modèle d'IA pour retrouver les résultats. Votre agent décide quoi chercher puis classe et interprète ce qui revient ; le matching lui-même est une simple recherche en base.
Aller plus loin

Deux recherches, deux métiers

search_events
Cherche dans la timeline : ce qui s'est passé, ce qui est dû, ce qui a changé. Compose le plein texte avec des filtres métier et temporels, paginé par curseur keyset.
search_persons
Cherche dans les fiches contact et le graphe relationnel : qui est quelqu'un, comment il est relié, quel rôle porte le lien.