Skip to main content
Glama

Agora

CI Licence MIT Index Hugging Face MCP

Semantic RAG over French National Assembly debates and film synopses, exposed as MCP servers for Claude. Runs locally with bge-m3 and LanceDB. No API key.

La place où l'on vient chercher. Agora met deux corpus à la disposition de Claude à travers des serveurs MCP : les comptes rendus des séances de l'Assemblée nationale et 35 000 synopsis de films. Chaque serveur fait de la recherche sémantique (embeddings bge-m3, index LanceDB) et renvoie des extraits sourcés : orateur, date et lien de la séance, ou fiche Wikipédia du film. Claude décide quand chercher, avec quels filtres, et rédige la réponse à partir de ces extraits.

Les serveurs tournent en local : on clone le dépôt, on télécharge l'index, et on les branche sur Claude. Pas de clé API à fournir, pas de modèle à installer : c'est l'assistant de l'utilisateur qui raisonne.

Contexte

Archives

Outils

Prompts

L'Hémicycle

601 séances de l'Assemblée nationale (17e législature, juillet 2024 → juillet 2026)

find_orateurs, search_debats, get_contexte

position-orateur, debat-sur-un-sujet, qui-a-repondu

La Salle obscure

~35 000 synopsis de films (Wikipédia), sortis de 1901 à 2017

search_films, get_film

trouver-un-film, recommander-des-films, fiche-film

Le dépôt contient aussi une application locale (interface React + modèle Ollama) pour ceux qui veulent tout faire tourner hors ligne, sans Claude.

Tester avec Claude

Prérequis : uv, Claude Code ou Claude Desktop, et environ 4 Go de disque (index ~890 Mo, modèle d'embeddings ~2,3 Go, environnement Python).

git clone https://github.com/rayaneferr/agora-rag.git && cd agora-rag
uv sync
uv run agora-index import        # index + bge-m3 depuis Hugging Face, empreintes vérifiées (une seule fois)

claude mcp add -s user agora-assemblee -- uv --directory "$PWD" run mcp-assemblee
claude mcp add -s user agora-cinema -- uv --directory "$PWD" run mcp-cinema

-s user rend les serveurs disponibles dans tous les projets ; sans lui, ils ne le sont que dans ce dossier. Claude Code lance chaque serveur lui-même (transport stdio) quand il en a besoin : rien à démarrer à la main. Au premier appel, le chargement de bge-m3 prend quelques secondes ; ensuite, une recherche prend 0,1 à 0,3 s (M4 Pro).

Ensuite, on pose simplement la question :

  • « Qu'a dit Éric Coquerel sur la dette publique ? »

  • « Comment les députés ont-ils débattu de l'intelligence artificielle ? »

  • « Un film où un voleur s'introduit dans les rêves des gens »

Les prompts sont des parcours prêts à l'emploi, qui fixent la marche à suivre (quels outils, dans quel ordre) et les règles de sourçage. Dans Claude Code, ils apparaissent comme des commandes : /mcp__agora-assemblee__position-orateur, /mcp__agora-cinema__trouver-un-film… Les resources assemblee://archives et cinema://archives décrivent l'étendue réelle de chaque base.

Dans claude_desktop_config.json (Réglages → Développeur → Modifier la configuration), avec des chemins absolus : une application lancée depuis le Dock n'hérite pas du PATH du terminal (which uv donne le bon).

{
  "mcpServers": {
    "agora-assemblee": {
      "command": "/opt/homebrew/bin/uv",
      "args": ["--directory", "/chemin/vers/agora-rag", "run", "mcp-assemblee"]
    },
    "agora-cinema": {
      "command": "/opt/homebrew/bin/uv",
      "args": ["--directory", "/chemin/vers/agora-rag", "run", "mcp-cinema"]
    }
  }
}

Tout autre client MCP (Cursor, l'inspecteur MCP…) se branche de la même façon en stdio, ou en HTTP (voir Serveurs MCP).

Related MCP server: Wikipedia MCP Server

Évolution du projet

Agora a changé trois fois de forme. Chaque étape répond à une limite de la précédente.

v0.1 → v0.2 : des serveurs MCP et un client multi-fournisseurs. Le point de départ : deux serveurs MCP de RAG (Qdrant + bge-m3) et un agent en ligne de commande qui passait par LiteLLM, avec la clé API de l'utilisateur. Le choix de MCP date de là : les outils de recherche sont séparés de l'agent qui les appelle, et testés à travers le protocole plutôt qu'en appel Python direct.

v0.3 : une application 100 % locale, sans clé. Une interface React et un backend FastAPI sont venus s'ajouter, d'abord avec un écran de saisie de clé OpenAI. Deux problèmes : demander une clé API est un frein (peu de gens en ont une, et encore moins la confient à une application inconnue), et chaque question envoyait les extraits à un tiers. La gestion de clé a été retirée au profit d'Ollama : gratuit, hors ligne, rien ne sort de la machine. Le code a été restructuré au passage en architecture hexagonale par contextes, pour que le cœur (la boucle agent) ignore quel modèle et quels outils il utilise.

v0.4 : plus de Docker. Qdrant est un serveur, donc une VM Linux sur Mac et Windows : c'était le prérequis le plus lourd. L'index est passé sur LanceDB, embarqué comme SQLite (voir plus bas), et publié sur Hugging Face. LiteLLM a ensuite été remplacé par un appel direct à l'API d'Ollama : une centaine de paquets en moins.

v0.5 : audit. Index et modèle épinglés à une révision et vérifiés empreinte par empreinte, en-tête Host validé, XML durci, actions de CI épinglées par SHA, Biome et Vitest sur le front.

v0.6 : les serveurs MCP au premier plan. L'application locale a un coût d'entrée élevé : Node, Ollama, et un modèle de 9 Go qui doit savoir appeler des outils. Or la partie qui a de la valeur, c'est le retrieval : l'index, les outils, les filtres, le sourçage. En branchant les serveurs MCP directement sur Claude, l'utilisateur garde son assistant et son abonnement, et n'a plus besoin que de uv et de l'index. L'idée vient de Civis, un serveur MCP sur les programmes présidentiels de 2022 qui ne rédige rien lui-même et laisse la synthèse au client, à partir de citations vérifiables.

Pour que l'expérience guidée des agents survive au changement de client, chaque serveur expose désormais des prompts (la marche à suivre et les règles de sourçage, qu'on ne peut plus imposer par un prompt système) et une resource qui décrit l'étendue réelle de ses archives.

L'hébergement public : prêt, mais pas en ligne. L'étape suivante naturelle était de faire comme Civis : un serveur public, branché en une commande sans rien installer. Tout est prêt : une app qui monte les serveurs de tous les contextes sur un seul port, avec les garde-fous d'une exposition sans authentification, une image Docker et un script de déploiement vers un Space Hugging Face, vérifiés de bout en bout en local avec l'index réel. Mais Civis tient sur l'offre gratuite de Cloudflare Workers parce qu'il fait de la recherche lexicale (FTS5) ; la recherche sémantique d'Agora demande de garder bge-m3 en mémoire, soit 3 à 4 Go de RAM : bien au-delà des offres gratuites courantes (512 Mo), et les Spaces Docker de Hugging Face demandent désormais un abonnement. Les alternatives gratuites imposent une mise en veille (30 à 60 s d'attente au réveil) ou l'administration d'une VM. Le projet reste donc local pour l'instant, et le déploiement tient en une commande le jour où il sera utile (voir Déploiement).

Architecture hexagonale

                 adaptateurs entrants                    adaptateurs sortants
            ┌──────────────────────────┐          ┌──────────────────────────────────┐
navigateur ─┤ api.py (FastAPI, SSE)    │          │ llm.py      Ollama (API native)  │─> Ollama
terminal ───┤ cli.py                   │          │ mcp.py      passerelle MCP       │─> serveur MCP du contexte
client MCP ─┤ mcp_public.py (HTTP)     │          │ mcp_serve   stdio/HTTP, prompts  │
            └────────────┬─────────────┘          │ vectorstore LanceDB + bge-m3     │─> data/lancedb/
                         ▼                        │ index_hub   index ↔ Hugging Face │─> dataset épinglé
                ┌─────────────────────────────────┴───────────────────────────────────┐
                │ core/   agent.py (boucle agent, événements)                         │
                │         ports.py (LLMPort, ToolGateway)                             │
                │         context.py (ContextSpec, étendue des archives) · text.py    │
                └─────────────────────────────────────────────────────────────────────┘
contexts/cinema/     __init__ (identité, prompt, sources) · server.py (outils, prompts, resource MCP) · ingestion.py
contexts/assemblee/  idem

Claude Code / Desktop ──stdio──> contexts/<nom>/server.py        (usage principal : pas d'agent Agora, pas d'Ollama)
  • Le cœur ne dépend de rien : il reçoit un modèle (LLMPort) et des outils (ToolGateway) par ses ports. Un test (tests/test_architecture.py) échoue si le cœur importe un adaptateur ou un contexte.

  • Un contexte = un dossier : identité (lieu, guide, thème), prompt système, serveur MCP (outils, prompts, resource), ingestion, et la façon de transformer les résultats d'outils en sources citables. Ajouter un domaine (sport, droit…) revient à créer contexts/<nom>/ et à l'inscrire dans contexts/__init__.py : il est alors exposé par l'application, par agora-mcp-public et par sa propre commande stdio.

  • Les serveurs MCP sont autonomes : Claude s'y connecte directement, l'application locale passe par une passerelle MCP par contexte. Chaque agent ne voit que ses outils.

  • Ollama est appelé directement (/api/chat, streaming, appels d'outils natifs) : pas de SDK intermédiaire, une centaine de paquets en moins.

Application locale (optionnelle)

Une interface pour interroger les deux corpus sans Claude, avec un modèle local : tout reste sur la machine. Prérequis supplémentaires : Node, et Ollama avec un modèle qui sait appeler des outils (ollama pull qwen2.5:14b recommandé, environ 10 Go de RAM ; qwen2.5:7b si la machine est plus modeste).

L'Hémicycle : question sur la dette publique, appel MCP déplié, réponse sourcée

uv sync
(cd web && npm install && npm run build)
uv run agora                                           # ouvre http://127.0.0.1:8765

Au premier lancement, l'application télécharge l'index et bge-m3 si agora-index import n'a pas été lancé, en affichant la progression dans l'interface ; la saisie s'ouvre quand tout est prêt. Ensuite, tout fonctionne hors ligne : seul Ollama (localhost:11434) est appelé.

Parcours : l'Agora (choix du modèle local) → le Forum (choix du guide : Lumière pour les films, L'Huissier pour les débats) → la salle du contexte, qui prend l'identité visuelle de son lieu. L'interface montre en direct les appels d'outils, le journal technique et la latence.

  • Mode démo : AGORA_DEMO=1 uv run agora ajoute un faux modèle instantané qui appelle vraiment les outils.

  • Front en développement : uv run agora --dev + cd web && npm run dev (Vite, rechargement à chaud).

  • Terminal : uv run agora-chat cinema ou uv run agora-chat assemblee --model qwen2.5:7b.

La Salle obscure en thème sombre : recherche dans les synopsis, sources Wikipédia

L'index

uv run agora-index import                              # index + bge-m3, avec vérification des empreintes
uv run agora-index verify                              # compare data/lancedb/ à la révision épinglée
uv run python -m agora.contexts.cinema.ingestion       # reconstruction (~2 h sur un M4 Pro) ; --limit 500 pour tester
uv run python -m agora.contexts.assemblee.ingestion    # --limit-seances 20 pour tester vite

Ni Docker, ni VM, ni base à lancer : l'index est un dossier (data/lancedb/, ou $AGORA_HOME/lancedb) ouvert directement par les serveurs.

Pourquoi LanceDB plutôt qu'un serveur vectoriel

La première version stockait l'index dans Qdrant. C'est une très bonne base, mais c'est un serveur : sur Mac et Windows, il tourne dans Docker, donc dans une machine virtuelle Linux. Pour une application personnelle qui doit s'installer par git clone + uv sync, c'était le prérequis le plus lourd.

LanceDB est embarqué, comme SQLite : une bibliothèque Python qui lit un dossier de fichiers (format colonnaire Lance). Conséquences mesurées sur ce corpus (187 000 chunks, 1024 dimensions, M4 Pro) :

Qdrant 1.19 (Docker)

LanceDB 0.39 (embarqué)

Prérequis

Docker + VM

aucun (uv sync)

Index distribué

snapshots, 1,19 Go, à restaurer

dossiers, 0,87 Go, ouverts tels quels

Recherche films (sans filtre)

HNSW approché

exacte, ~50 ms

Recherche filtrée (orateur, genre, dates)

filtre pendant le parcours

pré-filtre SQL, 60 à 250 ms

La recherche est exhaustive (pas d'index ANN) : à cette taille elle reste sous le quart de seconde et ne manque aucun voisin. Comparé à Qdrant sur quatre requêtes, le top 5 est identique partout ; sur le top 20 des films, l'index HNSW de Qdrant ne retrouvait que 16 à 17 des 20 vrais plus proches voisins, contre 20/20 ici. Les filtres que le modèle passe aux outils sont échappés avant d'entrer dans la clause SQL (tests/test_servers.py).

Sécurité et intégrité

Une application locale qui télécharge des fichiers et ouvre un port mérite quelques garde-fous :

  • Index et modèle épinglés. Le code fixe un commit du dataset Hugging Face et un commit de bge-m3. Chaque fichier téléchargé est comparé aux empreintes publiées par le Hub pour ce commit (sha256 des fichiers LFS, sha1 git des autres) ; un fichier remplacé ou tronqué est refusé. Les poids PyTorch étant du pickle, seule la révision validée est chargée.

  • Boucle locale seulement. L'API écoute sur 127.0.0.1 et refuse toute requête dont l'en-tête Host ne la désigne pas (protection contre le DNS rebinding : un site tiers ne peut pas piloter l'agent). Les serveurs MCP en HTTP refusent une adresse d'écoute non locale sans --allow-remote, car ils n'ont pas d'authentification.

  • Exposition publique encadrée. agora-mcp-public ajoute ses propres garde-fous : hôtes et origines autorisés, taille des requêtes, débit par IP, aucune question ni IP journalisée (voir Déploiement).

  • Données externes traitées comme telles. Le XML de l'Assemblée est parsé sans résolution d'entités ni accès réseau ; les filtres SQL sont échappés ; le Markdown du modèle est rendu sans HTML brut.

  • Chaîne d'intégration. Actions GitHub épinglées par SHA, jeton en lecture seule, Dependabot sur les trois écosystèmes (uv, npm, actions), audits pip-audit et npm audit propres à la date de publication.

Serveurs MCP : stdio ou HTTP

Chaque contexte a sa commande, en stdio par défaut (lancée par le client, comme ci-dessus) ou en service HTTP autonome (Streamable HTTP, sans état) :

uv run mcp-cinema --transport http       # http://127.0.0.1:8101/mcp
uv run mcp-assemblee --transport http    # http://127.0.0.1:8102/mcp
uv run agora-chat cinema --mcp-url http://127.0.0.1:8101/mcp

agora-mcp-public sert tous les contextes sur un seul port, dans un seul processus (bge-m3 chargé une fois) :

uv run agora-mcp-public                  # http://127.0.0.1:8100/cinema/mcp, /assemblee/mcp, /health
claude mcp add --transport http agora-cinema http://127.0.0.1:8100/cinema/mcp

/health décrit chaque contexte (étendue, volume indexé) et répond 503 tant que l'index manque ou que bge-m3 se charge : la sonde dit la vérité sur la capacité à répondre vite.

Déploiement

Prêt, mais pas en ligne (voir Évolution du projet). Ce qui existe :

  • Dockerfile : images de base épinglées par empreinte, dépendances verrouillées (torch CPU), index et bge-m3 téléchargés et vérifiés à la construction, plus aucun accès au Hub à l'exécution. Lance agora-mcp-public.

  • scripts/deploy_space.py : pousse vers un Space Hugging Face (sdk: docker) uniquement ce dont l'image a besoin, en un commit qui référence celui du dépôt ; refuse un arbre non commité.

  • scripts/smoke_test.py <url> : vérifie un déploiement à travers le protocole MCP en HTTP (outils, recherche réelle et sa latence, prompt, resource), après avoir attendu le chargement du modèle.

Exposé sans authentification, le service s'appuie sur des garde-fous, configurables dans .env.example : hôtes et origines autorisés, corps de requête limité à 64 Ko, limite de débit par IP (lue derrière N proxys de confiance), écoute non locale seulement avec --allow-remote, journal d'accès coupé.

Données

Contexte

Source

Unité indexée

Cinéma

Wikipedia Movie Plots

chunk de synopsis (~1500 car.), préfixé titre/année/genre/réalisateur

Assemblée

data.assemblee-nationale.fr (XML Syceron)

une intervention (paragraphes consécutifs d'un même orateur sous un même point), préfixée orateur/date/sujet

Embeddings locaux BAAI/bge-m3 (multilingue, 1024 dim). L'index prêt à l'emploi (tables LanceDB + manifeste avec empreintes) est publié sur Hugging Face : rferrat/agora-rag-index. Synopsis issus de Wikipédia (CC BY-SA) ; comptes rendus de l'Assemblée nationale sous Licence Ouverte.

Développement

uv run pytest                                     # architecture, parseur, outils/prompts/resources MCP, app publique, API
uv run ruff check . && uv run ruff format --check .
(cd web && npm run lint && npm test)              # Biome (lint + format) et Vitest (état de conversation, parseur SSE)
uv run python scripts/smoke_test.py               # contre agora-mcp-public lancé en local, avec l'index réel

Les tests n'ont besoin ni de bge-m3, ni d'Ollama, ni de l'index : les outils, prompts et resources sont appelés à travers un client MCP (en mémoire, ou en HTTP pour l'app publique), sur une base LanceDB temporaire, avec un embedder factice et le modèle de démo.

Feuille de route

  • v0.1.0 — CLI + 2 serveurs MCP (RAG cinéma et Assemblée nationale)

  • v0.2 — tests, lint, CI, serveurs MCP en HTTP, ingestion complète + export d'index

  • v0.3 — application locale : architecture hexagonale par contextes, Ollama, sans clé API

  • v0.4 — installation sans Docker : index embarqué (LanceDB) publié sur Hugging Face, 100 % hors ligne

  • v0.5.0 — interface épurée : recherches MCP repliables, sources compactes, thème clair/sombre

  • v0.5.1 — audit : index épinglé et vérifié, Host validé, premier lancement visible, Ollama natif, Biome + Vitest

  • v0.6.0 — serveurs MCP pour Claude : prompts guidés et resources d'archives, app multi-contextes sur un seul port avec garde-fous d'exposition, image Docker et smoke test (déploiement prêt, non actif)

  • v0.7.0 — benchmark du retrieval : dense vs hybride (BM25 + dense), reranking, exactitude des citations, latence. Exemple à corriger : « a man relives the same day over and over » ne remonte pas Groundhog Day dans le top 5 (des films de boucle temporelle, mais pas le plus attendu)

  • v0.8.0 — installation en une commande (uvx, données dans ~/.cache), historique des conversations

  • v0.9.0 — groupes politiques, mise à jour incrémentale des séances, nouveaux contextes

  • Hébergement public des serveurs MCP, quand un hébergeur adapté (≥ 4 Go de RAM, sans mise en veille) sera retenu

Licence

Code sous licence MIT. Merci à Civis pour l'idée d'un serveur MCP qui cite plutôt qu'il ne résume.

Available Tools

2 tools
get_filmA

Retourne la fiche complète d'un film (synopsis intégral), à partir du film_id de search_films.

ParametersJSON Schema
NameRequiredDescriptionDefault
film_idYes

TDQS

A3.6/5.0
Behavior3/5

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 usefully discloses the return payload (complete record with full synopsis, versus the presumably thinner search results), but says nothing about invalid-id behavior, permissions, or error handling for a lookup that can fail.

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?

A single, front-loaded sentence with no filler — the resource, the payload scope, and the id source all in one clause. Nothing needs trimming.

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 one-parameter read tool with no output schema, the description covers what is fetched, from which id, and where that id originates. Only failure/error behavior is absent, which is a minor gap for a getter.

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

Parameters3/5

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

Schema coverage is 0% and the schema only types film_id as an integer, so the description must compensate. It does add provenance ('le film_id de search_films'), which tells the agent where to obtain the value, but gives no format, range, or validity details beyond that.

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?

States a specific verb+resource ('Retourne la fiche complète d'un film') and scopes the payload ('synopsis intégral'), which clearly separates it from the search tool. It does not explicitly name what it is not, but the sibling reference to search_films supplies the contrast.

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

Usage Guidelines3/5

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

Saying the film_id comes from search_films implies the intended workflow (search first, then retrieve full record), but there is no explicit when-to-use/when-not or statement of prerequisites. The guidance is inferred rather than stated.

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

search_filmsA

Recherche sémantique de films par intrigue, thème ou ambiance.

Args:
    query: description libre de ce qu'on cherche (ex. « un homme revit la même journée en boucle »).
        Les synopsis sont en anglais ; une requête en anglais donne souvent de meilleurs résultats.
    genre: filtre texte sur le genre (ex. « comedy », « horror »).
    director: filtre texte sur le réalisateur (ex. « Nolan »).
    year_min: année de sortie minimale (seulement si l'utilisateur précise une période).
    year_max: année de sortie maximale (seulement si l'utilisateur précise une période).
    limit: nombre de films à retourner (1-20).
ParametersJSON Schema
NameRequiredDescriptionDefault
genreNo
limitNo
queryYes
directorNo
year_maxNo
year_minNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior4/5

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

With no annotations, the description carries the burden and discloses meaningful behavioral traits: this is semantic (not keyword) retrieval, the underlying corpus is English, and English queries improve results, plus the 1-20 limit bound. It omits ranking behavior, empty-result handling, and pagination, so not quite a 5.

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?

The purpose is front-loaded in one sentence, followed by a compact per-argument list where every line adds information absent from the schema. Slightly verbose formatting for what is essentially a 6-param list, but nothing is redundant.

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?

An output schema exists, so return values need not be described, and all six parameters plus the semantic-search behavior are covered. The main gaps are the absence of any exclusion relative to get_film and no guidance on result handling, which for a simple search tool is a minor shortfall.

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

Parameters5/5

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, and it documents all six parameters with examples ('un homme revit la même journée en boucle', 'comedy', 'Nolan'), clarifies that year bounds apply only when a period is given, and states the limit range 1-20. This fully covers the schema gap.

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?

States a specific verb ('Recherche sémantique') plus resource ('films') and scoping modes ('par intrigue, thème ou ambiance'), which clearly marks it as a semantic search rather than a fetch-by-id. It does not name the sibling get_film, so an agent must infer the distinction, keeping it below 5.

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

Usage Guidelines3/5

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

Offers useful invocation hints (queries in English work better since synopses are English; year filters only when the user specifies a period), but never states when to use this tool versus get_film or what to do when no results are found. Usage is implied rather than contrasted with 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.

  1. 2 tool updatesv0.6.0
    • First observedget_film
    • First observedsearch_films

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

get_film and search_films have clearly distinct purposes: one retrieves full details by film_id, the other performs semantic search by query and filters. The descriptions explicitly link them (get_film uses film_id from search_films), leaving no ambiguity.

Naming Consistency5/5

Both names follow a consistent snake_case verb_noun pattern: get_film and search_films. The convention is predictable and readable throughout.

Tool Count3/5

Two tools is thin for a cinema domain, even if the server is intentionally minimal. It covers search and retrieval but lacks other common operations (e.g., listing genres, getting people, or browsing without a query), making the set feel under-scoped.

Completeness4/5

The core read-only workflow is covered: semantic search with filters and full film details by ID. Minor gaps exist (e.g., no way to list films without a query or explore related entities), but agents can work around them for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that provides offline access to ZIM file archives, including Wikipedia, medical knowledge, and maps. It dynamically exposes tools like search, article retrieval, and driving route planning based on available ZIM files.
    4
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables offline CRUD and semantic search on Wikipedia ZIM archives via MCP tools for reading, writing, editing, deleting, and searching articles.
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that exposes a single tool to retrieve Wikipedia articles from local multistream dump files, offering fast offline access with redirect following and fuzzy matching.
    -