mcp-stashbox
The mcp-stashbox server provides read-only tools to search and retrieve metadata about scenes and performers from multiple public stash-box catalogues (StashDB, ThePornDB, FansDB, PMV Stash, JAVStash), and to identify media files by fingerprints.
Tools:
search_scenes– search scenes across all catalogues using full-text query or faceted filters (title, date, studio, performer, tag, etc.).search_performers– search performers by name/aliases or filters like country, studio, or co-performer.get_scene– retrieve detailed scene info by ID, with optional sections for fingerprints and images.get_performer– retrieve detailed performer info by ID, with optional sections for appearance, images, scenes, and studios.find_by_fingerprint– submit MD5, OSHASH, and/or PHASH hashes to identify which scene a local file corresponds to; distinguishes exact file matches from perceptual similarity matches.
Key characteristics:
Queries all configured catalogues simultaneously, with per-source results and status (answered, failed, absent).
Dates preserve their original precision (year, month, or day) and are never fabricated.
Merged or deleted record identifiers resolve to marker records pointing to successors or former names/titles.
Heavy data sections (fingerprints, performer scenes) are opt-in to control response size.
Only metadata is returned; no media content is accessed.
Configurable via per-catalogue API keys and optional settings for User-Agent, request intervals, timeouts, retries, and caching.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-stashboxsearch for scenes featuring Riley Reid"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-stashbox
A stash-box is a shared metadata catalogue: it records scenes, the performers credited on them, the studios that released them, and the tags they are filed under, each curated by submission and review. A catalogue holds no media — a record names where something was published and carries nothing of it — and it identifies a file by the fingerprints computed from it. Five such catalogues run independently, each issuing its own key to a registered account.
This server connects a chat client to all of them at once. You can search the scenes, performers, studios and tags of every catalogue you hold a key for, read one record as a single card assembled from every catalogue that holds it, identify a file from its fingerprints, and ask what each catalogue was measured answering. It needs a key per catalogue, and reads only the catalogues it has one for.
Install
One-click install
Claude Code
claude mcp add stashbox --env STASHBOX_STASHDB_KEY=your-key -- npx -y mcp-stashboxClaude Desktop, Cursor, and any client using the standard config format
{
"mcpServers": {
"stashbox": {
"command": "npx",
"args": ["-y", "mcp-stashbox"],
"env": {
"STASHBOX_STASHDB_KEY": "your-key"
}
}
}
}Node 24 or later is required. Set a key for each catalogue you want read; the others are named as absent from every answer.
With Docker
{
"mcpServers": {
"stashbox": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"STASHBOX_STASHDB_KEY",
"ghcr.io/smeet666/mcp-stashbox:2.0.1"
]
}
}
}-i keeps stdin open, which is where the protocol travels, and -t is left out
because a TTY rewrites the stream. The container needs outbound HTTPS to the
catalogues you hold keys for, and the keys from your environment: no volume, no
port.
Bundle, without npm
Download mcp-stashbox-2.0.1.mcpb from
the latest release
and open it. A client that supports MCP bundles installs it on its own, with no
npm to run. The keys are still set in the client's configuration.
Related MCP server: MusicBrainz MCP Server
What you can ask
"Which catalogues am I actually reading?"
"Find the performers credited under that name."
"Read me that studio's record."
"What is this file? Here is its MD5."
"Which scenes did those two perform in together?"
The ordinary path runs from a search to a card: a row carries an id written
instance:uuid, and the record tool reads it on every catalogue that holds it.
The catalogues
Catalogue | Address | Key |
StashDB |
|
|
TPDB |
|
|
FansDB |
|
|
PMV Stash |
|
|
JAVStash |
|
|
They answer different surfaces: StashDB answers every route this server knows,
and the others answer fewer. get_sources states what each was measured
answering and the day it was measured. A catalogue with no key is named as
absent from every answer, so an answer holding rows from some of them is never
read as the whole.
Tools
Tool | What it does |
| States what each catalogue answers, and which keys are held. |
| Searches the scenes of every configured catalogue. |
| Searches the performers. |
| Searches the studios. |
| Searches the tags. |
| Reads one scene as a single card. |
| Reads one performer as a single card. |
| Reads one studio as a single card. |
| Reads one tag as a single card. |
| Identifies a file from the hashes held for it. |
Every search takes two exclusive paths. query runs each catalogue's own
text index, which reads the words as a union. The typed arguments narrow as an
intersection. Writing both is refused.
get_sources
States what each configured catalogue was measured answering, and the day its surface was read. It reaches no catalogue and takes no argument.
In return: one entry per catalogue with its name, its identifier prefix, whether a key is held for it in this install, the variable to set when none is, and the routes it answers. Whether a key is held is a fact about this install and changes nothing about what the catalogue does.
search_scenes
Searches the scenes.
Argument | Type | Required | What it does |
| string | no | Words for each catalogue's own text index. |
| string | no | Words a title carries. |
| string | no | The studio's own reference for the release. |
| string | no | Another title the release is known by. |
| a calendar day | no | The release date to compare against. |
|
| no | How that date is read. |
| list of identifiers | no | Performers credited on it. |
| list of identifiers | no | Studios that released it. |
| an identifier | no | A studio the releasing studio sits under. |
| list of identifiers | no | Tags it is filed under. |
|
| no | How a list of identifiers is read. |
|
| no | The order the catalogue applies. |
|
| no | Which way that order runs. |
| integer, 1 to 1000 | no | Which page of each catalogue's own order. |
| integer, 1 to 100 | no | Rows one page of one catalogue carries. |
| list of catalogues | no | Read these catalogues alone. |
In return: rows carrying the id written instance:uuid, which get_scene
takes, and what names the record. A row leaves the synopsis, the link lists and
the editing stamps to the card, since none of those separates two releases.
The answer says per catalogue which of three it met: a failure, a catalogue
nobody asked, or an emptiness it established. Counts are never added across
catalogues. A search written with words alone reads the first rows each text
index answers with, since those routes take no page.
search_performers
Searches the performers.
Argument | Type | Required | What it does |
| string | no | Words for each catalogue's own text index. |
| string | no | Words a name carries. |
| string | no | Another name they are known by. |
| string | no | What the catalogue adds to tell two apart. |
| one of the values the catalogue records | no | The gender the catalogue records. |
| a two-letter country code | no | The country the catalogue records. |
| one of the values the catalogue records | no | The ethnicity the catalogue records. |
| integer, 1800 to 2200 | no | The year of birth. |
| integer, 1800 to 2200 | no | The year a career opened. |
| integer, 1800 to 2200 | no | The year a career closed. |
| an identifier | no | Someone they are credited alongside. |
| an identifier | no | A studio they are credited on. |
|
| no | The order the catalogue applies. |
|
| no | Which way that order runs. |
| integer, 1 to 1000 | no | Which page. |
| integer, 1 to 100 | no | Rows one page of one catalogue carries. |
| list of catalogues | no | Read these catalogues alone. |
alias is declared and never sent. No catalogue's faceted route applies it:
a request carrying it answers as wide as one carrying none, so it is left out and
the answer names it as a narrowing nobody received.
In return: the rows and the per-catalogue accounting search_scenes
returns.
search_studios
Searches the studios.
Argument | Type | Required | What it does |
| string | no | Words for each catalogue's own text index. |
| string | no | Words a name carries. |
| an identifier | no | A studio it sits under. |
| boolean | no | Whether it sits under another at all. |
|
| no | The order the catalogue applies. |
|
| no | Which way that order runs. |
| integer, 1 to 1000 | no | Which page. |
| integer, 1 to 100 | no | Rows one page of one catalogue carries. |
| list of catalogues | no | Read these catalogues alone. |
In return: the rows and the per-catalogue accounting search_scenes returns.
search_tags
Searches the tags.
Argument | Type | Required | What it does |
| string | no | Words for each catalogue's own text index. |
| string | no | Words a name carries. |
| an identifier | no | A category the tag belongs to. |
|
| no | The order the catalogue applies. |
|
| no | Which way that order runs. |
| integer, 1 to 1000 | no | Which page. |
| integer, 1 to 100 | no | Rows one page of one catalogue carries. |
| list of catalogues | no | Read these catalogues alone. |
In return: the rows and the per-catalogue accounting search_scenes returns.
get_scene
Reads one scene as a single card.
Argument | Type | Required | What it does |
| an identifier written | yes | The record to read. |
| any of | no | The blocks read beside the card. |
| list of catalogues | no | Read these catalogues alone. |
| list of catalogues | no | The order preferred where they disagree. |
In return: one card, read on every catalogue that holds the record and reached by the link each of them publishes to the same record elsewhere. Every value names the catalogues that said it, and where they disagree the reading nobody preferred is published beside the one that won. Left out, the registry's own order stands, and every card states the order applied.
get_performer
Reads one performer as a single card.
Argument | Type | Required | What it does |
| an identifier written | yes | The record to read. |
| any of | no | The blocks read beside the card. |
| list of catalogues | no | Read these catalogues alone. |
| list of catalogues | no | The order preferred where they disagree. |
studios is the whole table of studios they are credited on, which runs to
hundreds of rows.
In return: the card get_scene returns, for a performer.
get_studio
Reads one studio as a single card.
Argument | Type | Required | What it does |
| an identifier written | yes | The record to read. |
| list of catalogues | no | Read these catalogues alone. |
| list of catalogues | no | The order preferred where they disagree. |
In return: the card get_scene returns, for a studio.
get_tag
Reads one tag as a single card.
Argument | Type | Required | What it does |
| an identifier written | yes | The record to read. |
| list of catalogues | no | Read these catalogues alone. |
| list of catalogues | no | The order preferred where they disagree. |
In return: the card get_scene returns, for a tag.
find_by_fingerprint
Identifies a file from the hashes held for it.
Argument | Type | Required | What it does |
| a list of | yes | The hashes to look up. |
| any of | no | The blocks read beside each card. One call answers a card per record reached, so a block asked for here reaches a reader once per match. |
| list of catalogues | no | Read these catalogues alone. |
| list of catalogues | no | The order preferred where they disagree. |
MD5 and OSHASH name the bytes of a file. PHASH states a likeness, which a re-encode, a crop or another scene from the same shoot can satisfy: read a PHASH match as a resemblance rather than as an identity.
In return: each record reached, answered as one card read on every catalogue that holds it.
What an answer states about the catalogues
Every answer accounts for each catalogue separately, because merging them would lose what a caller needs. A catalogue that failed, one nobody asked, and one that answered with nothing are three different things, and they are reported as three. Counts stay beside the catalogue that produced them and are never added up. On a card, each value names the catalogues that said it, and a disagreement is published rather than resolved silently.
Configuration
A key per catalogue, and everything else optional. All of it goes in the env
block of your client config.
Variable | Default | What it does |
| none | The key StashDB issues to your account. |
| none | The key TPDB issues to your account. |
| none | The key FansDB issues to your account. |
| none | The key PMV Stash issues to your account. |
| none | The key JAVStash issues to your account. |
| the project identity | Names your application to the catalogues, with an address where a person can be reached. |
|
| Gap between two requests, from 1000 to 60000. |
|
| Deadline for one request, from 1 to 600000. |
|
| Attempts after a transient failure, from 0 to 10. |
|
| How long an answer stays in memory, from 0 to 86400000. |
|
| Answers held in memory at once, from 1 to 100000. |
|
|
|
Each catalogue issues its key to a registered account, in that account's settings. This server ships no key of its own, and each user brings their own. A value outside its range falls back to the default, and the reason is written to stderr.
Errors
Every failure carries one of six codes, a message, and where it helps a hint naming the next move.
Code | What happened | What to do |
| A catalogue answered, and holds no such record. | Check the identifier with a search. |
| The arguments were refused before any request went out. | Read the message, which names the argument. |
| A catalogue asked this client to slow down. | Wait, then call again with the same arguments. The record is still there. |
| An answer arrived in a shape this client cannot read. | Report it at the issue tracker. |
| The request did not complete. | Try again shortly. |
| The request passed its deadline. | Raise |
A catalogue that failed is reported per catalogue rather than failing the whole answer, so one silent catalogue never hides the others.
As a library
The layer reading the catalogues is published on its own, with its pacing, its cache and its errors, and with no protocol attached.
import { Catalogues } from "mcp-stashbox/client";
const client = new Catalogues();
const read = await client.searchPerformers({ name: "example", limit: 5 });
console.log(read.data.rows.length, read.cached);Each read answers { data, cached }, and throws an error carrying one of the six
codes. The one-second floor between two requests holds here as well.
Pacing and attribution
Requests go out one at a time with at least a second between them, and that floor
holds however the server is configured. The User-Agent always ends with the
project identity and an address where a person can be reached.
Every record carries the address of its page on the catalogue it came from, and a card carries the link each catalogue publishes to the same record elsewhere. The catalogues are built by the people who submit and review their records.
This MCP server is an unofficial project, with no affiliation to any of the catalogues it reads.
Privacy
This server collects nothing about you and sends nothing to its author. It runs on your machine, contacts only the catalogues you hold a key for, holds its answers in memory while it runs, and writes nothing to disk. Your keys are read from the environment and sent to their own catalogue alone. PRIVACY.md states what a request carries and which settings change any of it.
Development
npm install
npm run build:fixtures
npm test
npm run checkTests run against generated fixtures and make no network request. The live suite,
npm run test:live, makes one request per route and runs nightly against the
catalogues themselves.
Contributing
Bugs, questions and ideas belong in the issue tracker. Pull requests are welcome; opening an issue first helps agree on the shape of the change. See CONTRIBUTING.md.
License
MIT, see LICENSE. The records belong to the catalogues and to the people who built them.
mcp-stashbox (français)
Un stash-box est un catalogue de métadonnées partagé : il enregistre des scènes, les interprètes qui y sont crédités, les studios qui les ont publiées, et les étiquettes sous lesquelles elles sont rangées, le tout tenu par soumission et relecture. Un catalogue ne contient aucun média — une fiche nomme où quelque chose a été publié et n'en emporte rien — et il identifie un fichier par les empreintes calculées dessus. Cinq catalogues de ce type fonctionnent indépendamment, chacun délivrant sa propre clé à un compte enregistré.
Ce serveur relie un client de conversation à tous à la fois. On peut chercher les scènes, les interprètes, les studios et les étiquettes de chaque catalogue dont on détient une clé, lire une fiche sous forme d'une carte unique assemblée depuis tous les catalogues qui la détiennent, identifier un fichier par ses empreintes, et demander ce que chaque catalogue a été mesuré répondant. Il demande une clé par catalogue, et ne lit que ceux dont il en a une.
Installation
Installation en un clic
Claude Code
claude mcp add stashbox --env STASHBOX_STASHDB_KEY=votre-cle -- npx -y mcp-stashboxClaude Desktop, Cursor, et tout client au format de configuration standard
{
"mcpServers": {
"stashbox": {
"command": "npx",
"args": ["-y", "mcp-stashbox"],
"env": {
"STASHBOX_STASHDB_KEY": "votre-cle"
}
}
}
}Node 24 ou plus récent est nécessaire. Posez une clé par catalogue à lire ; les autres sont nommés comme absents de chaque réponse.
Avec Docker
{
"mcpServers": {
"stashbox": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"STASHBOX_STASHDB_KEY",
"ghcr.io/smeet666/mcp-stashbox:2.0.1"
]
}
}
}-i garde l'entrée standard ouverte, qui est le canal du protocole, et -t est
omis parce qu'un TTY réécrit le flux. Le conteneur a besoin d'un accès HTTPS
sortant vers les catalogues dont vous détenez les clés, et de ces clés prises
dans votre environnement : aucun volume, aucun port.
Bundle, sans npm
Téléchargez mcp-stashbox-2.0.1.mcpb depuis
la dernière publication
et ouvrez-le. Un client qui gère les bundles MCP l'installe seul, sans npm à
lancer. Les clés se posent toujours dans la configuration du client.
Ce qu'on peut demander
« Quels catalogues est-ce que je lis réellement ? »
« Trouve les interprètes crédités sous ce nom. »
« Lis-moi la fiche de ce studio. »
« Qu'est-ce que ce fichier ? Voici son MD5. »
« Dans quelles scènes ces deux-là ont-ils joué ensemble ? »
Le chemin ordinaire va d'une recherche à une carte : une ligne porte un id
écrit instance:uuid, et l'outil de fiche le lit sur chaque catalogue qui le
détient.
Les catalogues
Catalogue | Adresse | Clé |
StashDB |
|
|
TPDB |
|
|
FansDB |
|
|
PMV Stash |
|
|
JAVStash |
|
|
Ils répondent des surfaces différentes : StashDB répond à toutes les routes que
ce serveur connaît, les autres à moins. get_sources dit ce que chacun a été
mesuré répondant et le jour de la mesure. Un catalogue sans clé est nommé comme
absent de chaque réponse, si bien qu'une réponse portant les lignes de
certains ne se lit jamais comme l'ensemble.
Les outils
Outil | Ce qu'il fait |
| Dit ce que chaque catalogue répond, et quelles clés sont posées. |
| Cherche les scènes de chaque catalogue configuré. |
| Cherche les interprètes. |
| Cherche les studios. |
| Cherche les étiquettes. |
| Lit une scène sous forme d'une carte unique. |
| Lit un interprète sous forme d'une carte unique. |
| Lit un studio sous forme d'une carte unique. |
| Lit une étiquette sous forme d'une carte unique. |
| Identifie un fichier par les empreintes qu'on en détient. |
Chaque recherche prend deux chemins exclusifs. query interroge l'index
textuel de chaque catalogue, qui lit les mots comme une union. Les arguments
typés resserrent comme une intersection. Écrire les deux est refusé.
get_sources
Dit ce que chaque catalogue configuré a été mesuré répondant, et le jour où sa surface a été lue. Il ne joint aucun catalogue et ne prend aucun argument.
En retour : une entrée par catalogue avec son nom, son préfixe d'identifiant, la présence d'une clé dans cette installation, la variable à poser quand il n'y en a pas, et les routes auxquelles il répond. La présence d'une clé est un fait sur cette installation et ne change rien à ce que le catalogue fait.
search_scenes
Cherche les scènes.
Argument | Type | Requis | Ce qu'il fait |
| chaîne | non | Des mots pour l'index textuel de chaque catalogue. |
| chaîne | non | Des mots que porte un titre. |
| chaîne | non | La référence propre du studio pour la publication. |
| chaîne | non | Un autre titre sous lequel elle est connue. |
| un jour de calendrier | non | La date de publication à comparer. |
|
| non | Comment cette date est lue. |
| liste d'identifiants | non | Les interprètes qui y sont crédités. |
| liste d'identifiants | non | Les studios qui l'ont publiée. |
| un identifiant | non | Un studio sous lequel le studio éditeur se range. |
| liste d'identifiants | non | Les étiquettes sous lesquelles elle est rangée. |
|
| non | Comment une liste d'identifiants est lue. |
|
| non | L'ordre qu'applique le catalogue. |
|
| non | Le sens de cet ordre. |
| entier, 1 à 1000 | non | Quelle page de l'ordre propre à chaque catalogue. |
| entier, 1 à 100 | non | Lignes que porte une page d'un catalogue. |
| liste de catalogues | non | Ne lire que ces catalogues. |
En retour : des lignes portant l'id écrit instance:uuid, que get_scene
reprend, et ce qui nomme la fiche. Une ligne laisse à la carte le synopsis, les
listes de liens et les horodatages d'édition, dont aucun ne distingue deux
publications. La réponse dit par catalogue laquelle des trois il a
rencontrées : un échec, un catalogue que personne n'a interrogé, ou un vide
qu'il a établi. Les comptes ne sont jamais additionnés entre catalogues. Une
recherche écrite avec des mots seuls lit les premières lignes que rend chaque
index textuel, ces routes ne prenant aucune page.
search_performers
Cherche les interprètes.
Argument | Type | Requis | Ce qu'il fait |
| chaîne | non | Des mots pour l'index textuel. |
| chaîne | non | Des mots que porte un nom. |
| chaîne | non | Un autre nom sous lequel ils sont connus. |
| chaîne | non | Ce que le catalogue ajoute pour en distinguer deux. |
| une des valeurs que le catalogue enregistre | non | Le genre que le catalogue enregistre. |
| un code pays à deux lettres | non | Le pays que le catalogue enregistre. |
| une des valeurs que le catalogue enregistre | non | L'ethnicité que le catalogue enregistre. |
| entier, 1800 à 2200 | non | L'année de naissance. |
| entier, 1800 à 2200 | non | L'année où une carrière s'est ouverte. |
| entier, 1800 à 2200 | non | L'année où une carrière s'est close. |
| un identifiant | non | Quelqu'un à côté de qui ils sont crédités. |
| un identifiant | non | Un studio sur lequel ils sont crédités. |
|
| non | L'ordre qu'applique le catalogue. |
|
| non | Le sens de cet ordre. |
| entier, 1 à 1000 | non | Quelle page. |
| entier, 1 à 100 | non | Lignes que porte une page d'un catalogue. |
| liste de catalogues | non | Ne lire que ces catalogues. |
alias est déclaré et jamais envoyé. Aucune route à facettes ne l'applique :
une requête qui le porte répond aussi large qu'une requête sans lui, donc il est
laissé de côté et la réponse le nomme comme un resserrement que personne n'a
reçu.
En retour : les lignes et la comptabilité par catalogue que rend
search_scenes.
search_studios
Cherche les studios.
Argument | Type | Requis | Ce qu'il fait |
| chaîne | non | Des mots pour l'index textuel. |
| chaîne | non | Des mots que porte un nom. |
| un identifiant | non | Un studio sous lequel il se range. |
| booléen | non | S'il se range sous un autre. |
|
| non | L'ordre qu'applique le catalogue. |
|
| non | Le sens de cet ordre. |
| entier, 1 à 1000 | non | Quelle page. |
| entier, 1 à 100 | non | Lignes que porte une page d'un catalogue. |
| liste de catalogues | non | Ne lire que ces catalogues. |
En retour : les lignes et la comptabilité par catalogue de search_scenes.
search_tags
Cherche les étiquettes.
Argument | Type | Requis | Ce qu'il fait |
| chaîne | non | Des mots pour l'index textuel. |
| chaîne | non | Des mots que porte un nom. |
| un identifiant | non | Une catégorie dont l'étiquette relève. |
|
| non | L'ordre qu'applique le catalogue. |
|
| non | Le sens de cet ordre. |
| entier, 1 à 1000 | non | Quelle page. |
| entier, 1 à 100 | non | Lignes que porte une page d'un catalogue. |
| liste de catalogues | non | Ne lire que ces catalogues. |
En retour : les lignes et la comptabilité par catalogue de search_scenes.
get_scene
Lit une scène sous forme d'une carte unique.
Argument | Type | Requis | Ce qu'il fait |
| un identifiant écrit | oui | La fiche à lire. |
| parmi | non | Les blocs lus à côté de la carte. |
| liste de catalogues | non | Ne lire que ces catalogues. |
| liste de catalogues | non | L'ordre préféré là où ils divergent. |
En retour : une carte, lue sur chaque catalogue qui détient la fiche et atteinte par le lien que chacun publie vers la même fiche ailleurs. Chaque valeur nomme les catalogues qui l'ont dite, et là où ils divergent, la lecture que personne n'a préférée est publiée à côté de celle qui l'emporte. Omis, l'ordre propre du registre s'applique, et chaque carte énonce l'ordre appliqué.
get_performer
Lit un interprète sous forme d'une carte unique.
Argument | Type | Requis | Ce qu'il fait |
| un identifiant écrit | oui | La fiche à lire. |
| parmi | non | Les blocs lus à côté de la carte. |
| liste de catalogues | non | Ne lire que ces catalogues. |
| liste de catalogues | non | L'ordre préféré là où ils divergent. |
studios est la table entière des studios sur lesquels ils sont crédités, qui
fait des centaines de lignes.
En retour : la carte que rend get_scene, pour un interprète.
get_studio
Lit un studio sous forme d'une carte unique.
Argument | Type | Requis | Ce qu'il fait |
| un identifiant écrit | oui | La fiche à lire. |
| liste de catalogues | non | Ne lire que ces catalogues. |
| liste de catalogues | non | L'ordre préféré là où ils divergent. |
En retour : la carte que rend get_scene, pour un studio.
get_tag
Lit une étiquette sous forme d'une carte unique.
Argument | Type | Requis | Ce qu'il fait |
| un identifiant écrit | oui | La fiche à lire. |
| liste de catalogues | non | Ne lire que ces catalogues. |
| liste de catalogues | non | L'ordre préféré là où ils divergent. |
En retour : la carte que rend get_scene, pour une étiquette.
find_by_fingerprint
Identifie un fichier par les empreintes qu'on en détient.
Argument | Type | Requis | Ce qu'il fait |
| une liste de | oui | Les empreintes à chercher. |
| parmi | non | Les blocs lus à côté de chaque carte. Un appel rend une carte par fiche atteinte, donc un bloc demandé ici parvient au lecteur une fois par correspondance. |
| liste de catalogues | non | Ne lire que ces catalogues. |
| liste de catalogues | non | L'ordre préféré là où ils divergent. |
MD5 et OSHASH nomment les octets d'un fichier. PHASH énonce une ressemblance, qu'un ré-encodage, un recadrage ou une autre scène du même tournage peuvent satisfaire : lisez une correspondance PHASH comme une ressemblance plutôt que comme une identité.
En retour : chaque fiche atteinte, rendue comme une carte lue sur chaque catalogue qui la détient.
Ce qu'une réponse dit des catalogues
Chaque réponse rend compte de chaque catalogue séparément, parce que les fondre perdrait ce dont un appelant a besoin. Un catalogue qui a échoué, un que personne n'a interrogé et un qui a répondu vide sont trois choses différentes, et elles sont rapportées comme trois. Les comptes restent à côté du catalogue qui les a produits et ne sont jamais additionnés. Sur une carte, chaque valeur nomme les catalogues qui l'ont dite, et un désaccord est publié plutôt que tranché en silence.
Configuration
Une clé par catalogue, et tout le reste facultatif. Tout se pose dans le bloc
env de la configuration du client.
Variable | Défaut | Ce qu'elle fait |
| aucun | La clé que StashDB délivre à votre compte. |
| aucun | La clé que TPDB délivre à votre compte. |
| aucun | La clé que FansDB délivre à votre compte. |
| aucun | La clé que PMV Stash délivre à votre compte. |
| aucun | La clé que JAVStash délivre à votre compte. |
| l'identité du projet | Nomme votre application auprès des catalogues, avec une adresse où joindre une personne. |
|
| Écart entre deux requêtes, de 1000 à 60000. |
|
| Délai d'une requête, de 1 à 600000. |
|
| Tentatives après un échec passager, de 0 à 10. |
|
| Durée pendant laquelle une réponse reste en mémoire, de 0 à 86400000. |
|
| Réponses gardées en mémoire à la fois, de 1 à 100000. |
|
|
|
Chaque catalogue délivre sa clé à un compte enregistré, dans les réglages de ce compte. Ce serveur n'embarque aucune clé, et chacun apporte les siennes. Une valeur hors de sa plage retombe sur le défaut, et la raison est écrite sur la sortie d'erreur.
Erreurs
Chaque échec porte un des six codes, un message, et quand cela aide une indication du geste suivant.
Code | Ce qui s'est passé | Que faire |
| Un catalogue a répondu, et n'a pas cette fiche. | Vérifiez l'identifiant avec une recherche. |
| Les arguments ont été refusés avant toute requête. | Lisez le message, qui nomme l'argument. |
| Un catalogue demande à ce client de ralentir. | Attendez, puis rappelez avec les mêmes arguments. La fiche est toujours là. |
| Une réponse est arrivée dans une forme illisible ici. | Signalez-le sur le suivi d'incidents. |
| La requête n'a pas abouti. | Réessayez sous peu. |
| La requête a dépassé son délai. | Augmentez |
Un catalogue qui échoue est rapporté catalogue par catalogue plutôt que de faire échouer toute la réponse, donc un catalogue silencieux n'en cache jamais d'autres.
Comme bibliothèque
La couche qui lit les catalogues est publiée seule, avec son rythme, son cache et ses erreurs, sans protocole attaché.
import { Catalogues } from "mcp-stashbox/client";
const client = new Catalogues();
const read = await client.searchPerformers({ name: "example", limit: 5 });
console.log(read.data.rows.length, read.cached);Chaque lecture répond { data, cached }, et lève une erreur portant un des six
codes. Le plancher d'une seconde entre deux requêtes tient également ici.
Rythme et attribution
Les requêtes partent une à une avec au moins une seconde entre elles, et ce
plancher tient quelle que soit la configuration. Le User-Agent se termine
toujours par l'identité du projet et une adresse où joindre une personne.
Chaque fiche porte l'adresse de sa page sur le catalogue d'où elle vient, et une carte porte le lien que chaque catalogue publie vers la même fiche ailleurs. Les catalogues sont bâtis par ceux qui soumettent et relisent leurs fiches.
Ce MCP est un projet non officiel, sans affiliation à aucun des catalogues qu'il lit.
Confidentialité
Ce serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur votre machine, ne joint que les catalogues dont vous détenez une clé, garde ses réponses en mémoire le temps qu'il tourne, et n'écrit rien sur le disque. Vos clés sont lues dans l'environnement et envoyées à leur seul catalogue. PRIVACY.md dit ce qu'une requête emporte et quels réglages changent cela.
Développement
npm install
npm run build:fixtures
npm test
npm run checkLes tests s'exécutent sur des fixtures engendrées et n'émettent aucune requête.
La suite en direct, npm run test:live, émet une requête par route et tourne
chaque nuit contre les catalogues eux-mêmes.
Contribuer
Les anomalies, les questions et les idées ont leur place dans le suivi d'incidents. Les propositions de modification sont bienvenues ; ouvrir un ticket d'abord aide à s'accorder sur la forme du changement. Voir CONTRIBUTING.md.
Licence
MIT, voir LICENSE. Les fiches appartiennent aux catalogues et à ceux qui les ont bâties.
Available Tools
10 toolsfind_by_fingerprintFind scenes by fingerprintARead-only
Identify a file from the hashes held for it, across every configured stash-box catalogue. MD5 and OSHASH name the bytes of a file; PHASH states a likeness a re-encode, a crop or another scene from one shoot can satisfy. Each record reached is answered as one card, read on every catalogue that holds it.
| Name | Required | Description | Default |
|---|---|---|---|
| prefer | No | The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied. | |
| sources | No | ||
| sections | No | The blocks read beside the record's own fields, which come back whatever is written here. Each name adds a block, and 'basic' asks for those fields alone. | |
| fingerprints | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| asked | Yes | |
| notes | Yes | |
| cached | No | |
| matches | Yes | |
| unmatched | Yes | The hashes put to a catalogue that answered, which reached no record there. A catalogue named as unasked says nothing about them either way. |
| per_source | Yes | |
| match_count | Yes | One per card here. Every hash that reached a record names it on one card, and a record reached both by an exact hash and by a perceptual one stands as two, since the two claim different things about it. Read records_named for how many files were named, which counts each once. |
| not_searched | Yes | Hashes and the catalogues that answered without searching their algorithm, so they were never put to those. Nobody looked there, which is no evidence about the files behind them. |
| resemblances | Yes | Matches a perceptual hash reached, each a likeness and no claim about any file's bytes. |
| unattributed | Yes | Records the catalogues answered with that carry none of the hashes asked. Which hash reached them is unknown, so they stand as no match and are counted apart. |
| records_named | Yes | Distinct records an exact hash named. Two hashes reaching one record count once, and a perceptual match names no record. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already disclose readOnlyHint and openWorldHint, so the description needs less safety disclosure. It adds behavioral value by stating the search spans every configured catalogue and that each matched record is returned as one card per catalogue that holds it, and it explains the semantic distinction between byte-exact and likeness hashes.
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?
The description is compact and front-loaded with the main action, and it packs useful hash semantics into just three sentences. The wording is somewhat stylized and roundabout ('answered as one card, read on every catalogue that holds it'), but it remains appropriately sized and readable.
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?
Given the output schema exists, the description does not need to detail return record shapes, and it addresses the central behaviors: catalogue-wide scanning, hash algorithm meanings, and per-catalogue cards. It leaves some nuance about the 'sources' parameter implicit, yet the parseable schema descriptions and annotation still make this tool usable without large gaps.
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?
The schema covers about half of the parameters with descriptions, and the description adds meaning beyond it—especially the algorithm semantics: MD5/OSHASH name file bytes, while PHASH captures a re-encode/crop similarity. This helps an agent choose correct fingerprint values, though sources and sections still rely heavily on schema names and the existing descriptions.
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?
Description clearly states the tool's function: identify a file/scene from hashes across every configured stash-box catalogue. It uses a specific verb and resource ('identify a file', 'hashes', 'catalogue'), and the fingerprint-based scope distinguishes it from siblings like search_scenes and get_scene.
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 creates clear context: use this when you have hashes and want to identify a matching file across catalogues, with MD5/OSHASH for byte-exact matches and PHASH for likeness. It does not explicitly name alternatives or say 'use search_scenes instead', so it falls short of a perfect score, but the intended usage is unmistakable.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_performerGet one performerARead-only
Read one performer from the catalogue its identifier names, written instance:uuid. The answer is one card, read on every catalogue that holds the record and reached by the link each of them publishes to the same record elsewhere. Every value names the catalogues that said it, and where they disagree the reading nobody preferred is published beside the one that won. Name 'sources' to read one catalogue alone.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| prefer | No | The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied. | |
| sources | No | ||
| sections | No | The blocks read beside the record's own fields, which come back whatever is written here. Each name adds a block, and 'basic' asks for those fields alone. 'studios' is the whole table of studios they are credited on, which runs to hundreds of rows. |
Output Schema
| Name | Required | Description |
|---|---|---|
| card | Yes | |
| cached | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds meaningful context beyond the annotations: it explains that the tool merges records from multiple catalogues, presents disagreement resolution ("where they disagree the reading nobody preferred is published beside the one that won"), and the 'prefer' parameter's effect. While annotations declare readOnlyHint=true and openWorldHint=true, the description's catalogue-merge explanation is distinctive. However, the phrasing is obfuscated — terms like 'card', 'reading', and 'nobody preferred' are used in non-standard ways without being explicitly defined.
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?
The description is a single dense paragraph that is somewhat RAMBLING. The first sentence introduces the core purpose, but the subsequent three sentences are long, nested, and contain an extended metaphor that requires significant cognitive overhead. Some sentences are 40+ words and include unusual phrasings like 'the answer is one card, read on every catalogue that holds the record and reached by the link each of them publishes to the same record elsewhere.' The entire description could be more concise with clearer, plainer language, lending to a lower score on structure.
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?
For a tool with no output schema, the description makes a fair effort. It explains the merged-catalogue output, the role of the 'prefer' parameter in resolving disagreements, and the semantics of the 'sources' parameter. The tool's complexity is medium, given the merge logic. The description doesn't lack for explaining the returned data; the main issue is the overly florid language which, while adding context, requires effort to parse. Given the presence of an output schema (as evidenced by 'Has output schema: true'), the threshold for completeness is lowered.
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?
With 50% schema coverage, the description partially helps: it explains the 'sources' parameter and mentions the 'prefer' ordering, but it doesn't cover the 'sections' parameter at all and describes 'id' only by example ('instance:uuid'). The 'prefer' parameter is mentioned but not by name. The id parameter's uuid format is partially covered by the schema's pattern, and the description adds the notion of a qualified instance:uuid. This adds some value but doesn't fully compensate for the 50% coverage gap, hitting the baseline of 3.
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 begins with a specific transitive verb and object: "Read one performer from the catalogue its identifier names, written instance:uuid." This clearly states the tool retrieves a single performer by ID, distinguishing it from siblings like search_performers (which searches). However, the sentence is grammatically awkward ('catalogue its identifier names'), and the latter half of the description shifts into a confusing metaphor about cards, catalogues, readings, and disagreements that obscures rather than clarifies the tool's core purpose.
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 contrasts with the alternative 'sources' parameter: "Name 'sources' to read one catalogue alone." This tells users when to use the sources parameter versus the default merged view. While it doesn't explicitly name sibling tools like get_sources, it clearly explains the two modes of operation. However, it doesn't explicitly say when to prefer this over search_performers, though the read-vs-search distinction is implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sceneGet one sceneARead-only
Read one scene from the catalogue its identifier names, written instance:uuid. The answer is one card, read on every catalogue that holds the record and reached by the link each of them publishes to the same record elsewhere. Every value names the catalogues that said it, and where they disagree the reading nobody preferred is published beside the one that won. Name 'sources' to read one catalogue alone.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| prefer | No | The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied. | |
| sources | No | ||
| sections | No | The blocks read beside the record's own fields, which come back whatever is written here. Each name adds a block, and 'basic' asks for those fields alone. |
Output Schema
| Name | Required | Description |
|---|---|---|
| card | Yes | |
| cached | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description provides behavioral context beyond the readOnlyHint annotation by explaining that the tool aggregates across catalogues, shows which catalogue contributed each value, discloses how disagreements are handled (the 'nobody preferred' reading published beside the winner), and notes that 'sources' restricts to one catalogue. This adds meaningful behavioral detail, though the phrasing is convoluted.
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?
The description is only three sentences, but they are densely packed and use awkward, non-standard phrasing (e.g., 'the reading nobody preferred'). It lacks front-loading of the core purpose and uses more words than necessary to convey the aggregation and conflict behavior.
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?
For a tool with aggregation and conflict resolution behavior, the description covers the main concepts but is cryptic. It doesn't explain the 'prefer' and 'sections' parameters, and the existing prose may confuse more than clarify. An output schema exists, so return values are covered elsewhere, but the description itself leaves gaps for a smooth invocation.
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?
The description adds meaning for the 'id' parameter (identifying the scene as instance:uuid) and the 'sources' parameter (read one catalogue alone). However, it does not mention the 'prefer' or 'sections' parameters. With schema coverage at 50%, the description partially compensates but leaves important parameters undocumented in prose.
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 action 'Read one scene' and the resource (a scene identified by an instance:uuid), distinguishing it from search_scenes which searches. However, the phrasing 'from the catalogue its identifier names' is awkward, making it slightly less clear than a straightforward 'retrieve a single scene by ID'.
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 have a specific scene identifier (read one scene), but it does not explicitly compare to alternatives like search_scenes or get_sources. It does mention using the 'sources' parameter to read one catalogue alone, which is parameter-level guidance, not tool-selection guidance. Overall, usage context is implied rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_sourcesWhat each catalogue answersARead-only
What each configured stash-box catalogue was measured answering, and the day its surface was read from it. Whether a key is held for a catalogue is a fact about this install and changes nothing about what the catalogue does. Reaches no catalogue and takes no argument.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| sources | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds valuable context beyond the readOnlyHint: it states 'Reaches no catalogue,' clarifying that the tool performs no network calls to external sources. It also explains that the key-holding fact is local and does not affect catalogue behavior, which is useful behavioral transparency.
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?
The description is two sentences, front-loads the purpose, and every sentence adds value. The second sentence clarifies an important nuance about key holding without being verbose.
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?
For a zero-parameter tool with an output schema, the description covers the semantic meaning of the data (what catalogues answer, reading date, location of configuration facts) and explicitly states it reaches no catalogue. This is complete for the tool's complexity.
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?
The tool has zero parameters, and the description explicitly confirms 'takes no argument.' While there's no parameter detail to add, this meets the baseline for a zero-parameter tool.
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 what the tool does: it reports what each configured stash-box catalogue was measured answering and the date its surface was read. This is distinct from the sibling search/get tools, which operate on specific entities. The verb and resource are evident.
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 (e.g., to inspect catalogue capabilities and reading dates) but does not explicitly say when to use this tool versus alternatives. It mentions 'Reaches no catalogue' which hints at safety but lacks explicit exclusions or alternative references.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_studioGet one studioARead-only
Read one studio from the catalogue its identifier names, written instance:uuid. The answer is one card, read on every catalogue that holds the record and reached by the link each of them publishes to the same record elsewhere. Every value names the catalogues that said it, and where they disagree the reading nobody preferred is published beside the one that won. Name 'sources' to read one catalogue alone.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| prefer | No | The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied. | |
| sources | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| card | Yes | |
| cached | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds behavioral detail beyond the readOnlyHint and openWorldHint annotations, explaining how disagreements between catalogues are handled and how sources can limit reads. It discloses the aggregation behavior and preference logic, which annotations don't provide.
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?
The description is a single long sentence with complex phrasing, making it harder to parse. It packs information but lacks clear structure or front-loading of key details.
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 explains the core read behavior, conflict resolution, and sources limitation, which is substantial for a read tool with an output schema. It doesn't detail output format, but the output schema likely covers that, so the description is reasonably 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?
The schema covers only 'prefer' with a description, while 'id' and 'sources' lack descriptions. The description explains 'id' format (instance:uuid) and 'sources', but 'prefer' semantics are already in schema. With 33% coverage, description partially compensates but doesn't fully detail 'sources' usage beyond a brief mention.
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 states it reads one studio from the catalogue by identifier, which is a specific verb and resource. However, it doesn't explicitly distinguish from siblings like get_scene or get_performer, though the distinct resource (studio) is clear.
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 the tool is for reading a single studio given an ID, and it mentions the 'sources' parameter to read from one catalogue alone, giving some usage context. It doesn't explicitly rule out alternatives, but the context of studio reading is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_tagGet one tagARead-only
Read one tag from the catalogue its identifier names, written instance:uuid. The answer is one card, read on every catalogue that holds the record and reached by the link each of them publishes to the same record elsewhere. Every value names the catalogues that said it, and where they disagree the reading nobody preferred is published beside the one that won. Name 'sources' to read one catalogue alone.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | ||
| prefer | No | The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied. | |
| sources | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| card | Yes | |
| cached | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds significant behavior beyond the readOnlyHint and openWorldHint annotations: it explains that results are read across every catalogue holding the record, that each value names its source catalogues, that disagreements surface the non-preferred reading alongside the winner, and that sources restricts reads to one catalogue. No contradiction with annotations.
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?
The description is not overly long and front-loads the core action, but the prose is dense and grammatically awkward ('the catalogue its identifier names', 'reached by the link each of them publishes'), which hurts readability. It could communicate the same behavior in clearer, more compact language.
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?
For a multi-catalogue read with an output schema and read-only annotations, the description covers the key behavioral wrinkles: cross-catalogue resolution, per-value provenance, disagreement handling, and the sources restriction. It does not discuss not-found or error cases, but the main weakness is that the unusual wording obscures rather than clarifies context that is otherwise present.
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 low (33%), so the description partially compensates: 'written instance:uuid' clarifies the id format, 'Name sources to read one catalogue alone' explains the sources parameter, and the disagreement/preference text gives meaning to prefer. Still, it does not systematically document each parameter, and the prefer behavior is only conveyed indirectly.
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 opens with 'Read one tag from the catalogue its identifier names, written instance:uuid,' which identifies the action (read) and resource (tag) and indicates lookup by identifier. However, the phrasing is convoluted and could more directly distinguish this from sibling tools like search_tags or get_sources, though the singular 'one tag' helps.
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?
Usage is implied by the verb 'Read one tag' and the mention of identifier-based lookup, but the description does not explicitly say when to prefer this tool over alternatives such as search_tags or get_sources. The only direct usage hint is 'Name sources to read one catalogue alone,' which guides a parameter rather than tool selection.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_performersSearch performersARead-only
Search performers across every configured stash-box catalogue. Two exclusive paths: 'query' runs each catalogue's own text index, which reads the words as a union, and the typed arguments narrow as an intersection. Writing both is refused. The answer says per catalogue which of three it met: a failure, a catalogue nobody asked, and an emptiness it established. Counts are never added across them.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| page | No | Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page. | |
| sort | No | ||
| alias | No | No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received. | |
| limit | No | How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one. | |
| query | No | Words for the catalogue's own text index, which reads them as a union. It is exclusive with the typed arguments, which narrow as an intersection. | |
| gender | No | ||
| country | No | ||
| sources | No | ||
| direction | No | ||
| ethnicity | No | ||
| studio_id | No | ||
| birth_year | No | ||
| disambiguation | No | ||
| performed_with | No | ||
| career_end_year | No | No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received. | |
| career_start_year | No | No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received. |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| cached | No | Replayed from this client's store. |
| window | No | The page and the row limit this answer was read at, absent where no catalogue answered. |
| results | Yes | |
| ordering | Yes | How the rows were laid out, which a reader needs before reading the first. |
| per_source | Yes | |
| result_count | Yes | How many rows this page carries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations (readOnlyHint: true, openWorldHint: true) already establish safety and open-world semantics. The description goes well beyond, revealing exclusive paths, the refusal when both are written, per-catalogue response states ('a failure, a catalogue nobody asked, and an emptiness it established'), non-summation across catalogues, and the behavior of no-op parameters like alias.
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?
Remarkably compact for a description covering such conceptual terrain, front-loading purpose and then detailing behavior efficiently. The unusual phrasing 'a catalogue nobody asked, and an emptiness it established' is striking but slightly confusing without clearer syntax, and the description remains dense yet occasionally opaque, costing it a point.
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?
For a complex tool with 17 parameters, multi-catalogue fan-out, and exclusive query modes, the description captures the critical behaviors—union vs intersection semantics, exclusivity rules, per-catalogue response consistency—that an agent must understand to invoke it correctly. The output schema correctly handles return-value details.
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 only 35%, so the description carries a heavy burden. It delivers an excellent conceptual framework explaining 'query' (union) vs typed args (intersection) and exclusivity, but with 10 of 17 parameters undocumented and uncovered by the main description, it cannot fully compensate. The framework elegantly covers the crucial conceptual model without resolving all gaps.
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?
'Search performers across every configured stash-box catalogue' precisely states verb, resource, and the distinguishing scope of multi-catalogue search. It clearly differentiates from sibling search tools by stressing 'across every configured stash-box catalogue,' something search_scenes, search_studios, and search_tags would not do.
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 gives strong internal guidance on when to use the 'query' path vs typed arguments (union vs intersection, mutually exclusive). It implicitly communicates when to use the tool but never explicitly names alternatives or states conditions for choosing it over get_performer or sibling search tools, leaving the exclusion logic unexplained.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_scenesSearch scenesARead-only
Search scenes across every configured stash-box catalogue. Two exclusive paths: 'query' runs each catalogue's own text index, which reads the words as a union, and the typed arguments narrow as an intersection. Writing both is refused. The answer says per catalogue which of three it met: a failure, a catalogue nobody asked, and an emptiness it established. Counts are never added across them.
| Name | Required | Description | Default |
|---|---|---|---|
| code | No | ||
| date | No | ||
| page | No | Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page. | |
| sort | No | ||
| alias | No | No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received. | |
| limit | No | How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one. | |
| match | No | How performer_ids and tag_ids are read, which are the only arguments it governs. 'all', the default, asks for scenes carrying every identifier of a list; 'any' for scenes carrying at least one, which answers counts an order of magnitude wider. A scene names one studio, so studio_ids asks for any of its identifiers under both readings. The lists narrow against each other as an intersection either way. | |
| query | No | Words for the catalogue's own text index, which reads them as a union. It is exclusive with the typed arguments, which narrow as an intersection. | |
| title | No | ||
| sources | No | ||
| tag_ids | No | ||
| direction | No | ||
| studio_ids | No | ||
| date_compare | No | These catalogues compare a date against one bound and answer no range, so the comparison is written rather than assumed. | |
| performer_ids | No | ||
| parent_studio_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| cached | No | Replayed from this client's store. |
| window | No | The page and the row limit this answer was read at, absent where no catalogue answered. |
| results | Yes | |
| ordering | Yes | How the rows were laid out, which a reader needs before reading the first. |
| per_source | Yes | |
| result_count | Yes | How many rows this page carries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=true, so the description doesn't need to repeat safety. It adds valuable behavioral context: the per-catalogue response structure (failure, nobody asked, emptiness), the refusal of both paths, and the non-aggregation of counts. This goes beyond annotations, though it could mention pagination behavior more explicitly.
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?
The description is concise and front-loaded with the core purpose. It uses clear, structured language to explain the two paths and result semantics. However, some phrasing is slightly convoluted (e.g., 'an emptiness it established'), which could be clearer, but overall it's efficient.
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?
Given the tool's complexity (16 parameters, multiple catalogues, exclusive paths), the description covers the essential behavioral aspects: the two paths, per-catalogue results, and non-aggregation. It doesn't detail every parameter, but the output schema exists and the description focuses on the most critical usage rules. It's adequate for an agent to use correctly, though more parameter-specific guidance would improve completeness.
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 only 38%, so the description must compensate. It does explain key parameters like query, match, and limit, and clarifies the exclusive nature of query vs. typed arguments. However, many parameters (code, date, title, sources, direction, etc.) lack description-level detail, relying on schema names. The description adds meaning for the most complex interactions but not all.
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 tool searches scenes across every configured stash-box catalogue, with two exclusive search paths. It distinguishes itself from sibling tools like search_performers, search_studios, and search_tags by specifying the resource (scenes) and the multi-catalogue scope.
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 explains the two exclusive paths (query vs. typed arguments) and when to use each, noting that writing both is refused. It also clarifies that counts are never added across catalogues, guiding the agent on how to interpret results. This provides clear usage context and exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_studiosSearch studiosARead-only
Search studios across every configured stash-box catalogue. Two exclusive paths: 'query' runs each catalogue's own text index, which reads the words as a union, and the typed arguments narrow as an intersection. Writing both is refused. The answer says per catalogue which of three it met: a failure, a catalogue nobody asked, and an emptiness it established. Counts are never added across them.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| page | No | Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page. | |
| sort | No | ||
| limit | No | How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one. | |
| query | No | Words for the catalogue's own text index, which reads them as a union. It is exclusive with the typed arguments, which narrow as an intersection. | |
| sources | No | ||
| direction | No | ||
| parent_id | No | ||
| has_parent | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| cached | No | Replayed from this client's store. |
| window | No | The page and the row limit this answer was read at, absent where no catalogue answered. |
| results | Yes | |
| ordering | Yes | How the rows were laid out, which a reader needs before reading the first. |
| per_source | Yes | |
| result_count | Yes | How many rows this page carries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare readOnlyHint and openWorldHint. The description adds substantial behavioral context: query vs typed-argument exclusivity, refusal when both are written, per-catalogue outcome classification, and no cross-catalogue count aggregation. No contradiction with annotations.
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?
Five sentences, front-loaded with purpose, followed by compact behavioral rules. The phrasing is somewhat cryptic ('a failure, a catalogue nobody asked, and an emptiness it established') but every sentence contributes meaningful information without padding.
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?
For a complex tool with 9 parameters, two search modes, and an output schema, the description covers the essential behavioral complexity: exclusivity, per-catalogue results, and count handling. It leaves some parameter semantics to the schema and lacks tool-selection context, but overall it is adequate and useful.
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 only 33% (3 of 9 parameters). The description adds important semantics for query (union), typed arguments (intersection, exclusivity), and per-catalogue page/limit behavior. However, it does not explain name, sort, direction, parent_id, has_parent, or sources beyond what schema enums and patterns already imply.
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 opens with 'Search studios across every configured stash-box catalogue' – a specific verb, resource, and scope. This clearly distinguishes the tool from sibling searches for scenes, performers, and tags, and from get_studio.
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 explains internal usage rules ('Two exclusive paths', 'Writing both is refused') but gives no guidance on when to choose this tool over alternatives like get_studio for exact IDs or search_scenes for scene queries. It neither names alternatives nor states exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_tagsSearch tagsARead-only
Search tags across every configured stash-box catalogue. Two exclusive paths: 'query' runs each catalogue's own text index, which reads the words as a union, and the typed arguments narrow as an intersection. Writing both is refused. The answer says per catalogue which of three it met: a failure, a catalogue nobody asked, and an emptiness it established. Counts are never added across them.
| Name | Required | Description | Default |
|---|---|---|---|
| name | No | ||
| page | No | Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page. | |
| sort | No | ||
| limit | No | How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one. | |
| query | No | Words for the catalogue's own text index, which reads them as a union. It is exclusive with the typed arguments, which narrow as an intersection. | |
| sources | No | ||
| direction | No | ||
| category_id | No |
Output Schema
| Name | Required | Description |
|---|---|---|
| notes | Yes | |
| cached | No | Replayed from this client's store. |
| window | No | The page and the row limit this answer was read at, absent where no catalogue answered. |
| results | Yes | |
| ordering | Yes | How the rows were laid out, which a reader needs before reading the first. |
| per_source | Yes | |
| result_count | Yes | How many rows this page carries. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds behavioral details beyond the readOnly and openWorld annotations: it explains the exclusive query/typed-argument modes, that mixing them is refused, the per-catalogue response statuses, and that counts are never aggregated. While oddly phrased, these are meaningful and do not contradict annotations.
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?
The description is short but not concise in meaning. Phrases like 'a catalogue nobody asked, and an emptiness it established' are cryptic and obscure the intended behavior. The front-loaded purpose is clear, but the subsequent wording is convoluted and could be made more direct.
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?
Given the tool's complexity (8 optional parameters, exclusive modes, multi-catalogue behavior), the description covers the main paths and response characteristics. However, it leaves gaps such as what happens with no parameters, the meaning of the 'sources' parameter, and the precise interpretation of the three statuses. The presence of an output schema covers return values, so this is only moderately 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 description coverage is only 38%, so the description compensates by explaining that 'query' uses each catalogue's text index with union semantics, while typed arguments narrow as an intersection, and that the two are mutually exclusive. This gives group-level meaning to structured parameters, though individual parameters like name and category_id are not further clarified.
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 opens with 'Search tags across every configured stash-box catalogue,' which clearly identifies the verb (search), resource (tags), and scope (all catalogues). This distinguishes it from sibling tools like search_scenes, search_performers, or get_tag.
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 this is the tool for searching tags across all catalogues, but it does not explicitly state when to use this tool versus alternatives like get_tag. It does provide internal usage constraints by explaining the two exclusive paths and that mixing them is refused, which guides invocation but not tool selection.
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.
9 tool updates
v1.0.0- Changed
find_by_fingerprint6 fields changed- changed
Input schema / properties / prefer / descriptionPrevious value: -"The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied."New value: +"The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied." - changed
Output schema / properties / match_count / descriptionPrevious value: -"One per record reached, whatever number of hashes reached it."New value: +"One per card here. Every hash that reached a record names it on one card, and a record reached both by an exact hash and by a perceptual one stands as two, since the two claim different things about it. Read records_named for how many files were named, which counts each once." - changed
Output schema / properties / matches / items / properties / scene / properties / held_by / descriptionPrevious value: -"Every catalogue asked, with the identifier the record carries there."New value: +"Every catalogue asked, with the identifier the record carries there. This is where what each of them did with the record is read: the state says which of the three it met, and the reason says why it was not asked or what went wrong." - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What its own index holds for this question, the rows on this page included."New value: +"What its own index holds for the question it received, the rows on this page included. A narrowing it could not receive, or received in part, leaves that question wider than the one written." - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something."New value: +"Narrowings that shaped no part of the request this catalogue received, whether its route declares no field for one or nothing written was left for one to decide." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_part / descriptionAdded value: +"Narrowings it received short of what was written, the rest of the list naming records another catalogue minted. It answered a wider question than the one asked."
- Changed
get_performer3 fields changed- changed
Input schema / properties / prefer / descriptionPrevious value: -"The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied."New value: +"The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied." - changed
Output schema / properties / card / properties / held_by / descriptionPrevious value: -"Every catalogue asked, with the identifier the record carries there."New value: +"Every catalogue asked, with the identifier the record carries there. This is where what each of them did with the record is read: the state says which of the three it met, and the reason says why it was not asked or what went wrong." - removed
Output schema / properties / per_sourceRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "algorithms_not_searched": { - "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", - "items": { - "type": "string" - }, - "type": "array" - }, - "count": { - "description": "Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes.", - "type": "number" - }, - "error": { - "enum": [ - "not_found", - "invalid_input", - "rate_limited", - "parse_failure", - "network_error", - "timeout" - ], - "type": "string" - }, - "index_total": { - "description": "What its own index holds for this question, the rows on this page included.", - "type": "number" - }, - "index_total_over_any_word": { - "description": "The total counts rows carrying any word of the query, its text index reading them apart.", - "type": "boolean" - }, - "moment": { - "description": "Which moment failed.", - "type": "string" - }, - "name": { - "description": "The name that catalogue calls itself.", - "type": "string" - }, - "narrowings_naming_no_record": { - "description": "Narrowings written only with identifiers another catalogue minted.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_not_received": { - "description": "Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_received_in_part": { - "items": { - "type": "string" - }, - "type": "array" - }, - "reason": { - "description": "Why it was not asked, or what went wrong.", - "type": "string" - }, - "records": { - "description": "Distinct records behind its rows.", - "type": "number" - }, - "skipped": { - "description": "Rows it answered with that this client could not read and left out.", - "type": "number" - }, - "source": { - "description": "The catalogue this row is about.", - "type": "string" - }, - "state": { - "description": "'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world.", - "enum": [ - "answered", - "failed", - "absent" - ], - "type": "string" - }, - "unattributed": { - "description": "Records it answered with while carrying none of what was asked about.", - "type": "number" - } - }, - "required": [ - "source", - "state" - ], - "type": "object" - }, - "type": "array" -}
- Changed
get_scene3 fields changed- changed
Input schema / properties / prefer / descriptionPrevious value: -"The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied."New value: +"The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied." - changed
Output schema / properties / card / properties / held_by / descriptionPrevious value: -"Every catalogue asked, with the identifier the record carries there."New value: +"Every catalogue asked, with the identifier the record carries there. This is where what each of them did with the record is read: the state says which of the three it met, and the reason says why it was not asked or what went wrong." - removed
Output schema / properties / per_sourceRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "algorithms_not_searched": { - "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", - "items": { - "type": "string" - }, - "type": "array" - }, - "count": { - "description": "Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes.", - "type": "number" - }, - "error": { - "enum": [ - "not_found", - "invalid_input", - "rate_limited", - "parse_failure", - "network_error", - "timeout" - ], - "type": "string" - }, - "index_total": { - "description": "What its own index holds for this question, the rows on this page included.", - "type": "number" - }, - "index_total_over_any_word": { - "description": "The total counts rows carrying any word of the query, its text index reading them apart.", - "type": "boolean" - }, - "moment": { - "description": "Which moment failed.", - "type": "string" - }, - "name": { - "description": "The name that catalogue calls itself.", - "type": "string" - }, - "narrowings_naming_no_record": { - "description": "Narrowings written only with identifiers another catalogue minted.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_not_received": { - "description": "Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_received_in_part": { - "items": { - "type": "string" - }, - "type": "array" - }, - "reason": { - "description": "Why it was not asked, or what went wrong.", - "type": "string" - }, - "records": { - "description": "Distinct records behind its rows.", - "type": "number" - }, - "skipped": { - "description": "Rows it answered with that this client could not read and left out.", - "type": "number" - }, - "source": { - "description": "The catalogue this row is about.", - "type": "string" - }, - "state": { - "description": "'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world.", - "enum": [ - "answered", - "failed", - "absent" - ], - "type": "string" - }, - "unattributed": { - "description": "Records it answered with while carrying none of what was asked about.", - "type": "number" - } - }, - "required": [ - "source", - "state" - ], - "type": "object" - }, - "type": "array" -}
- Changed
get_studio3 fields changed- changed
Input schema / properties / prefer / descriptionPrevious value: -"The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied."New value: +"The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied." - changed
Output schema / properties / card / properties / held_by / descriptionPrevious value: -"Every catalogue asked, with the identifier the record carries there."New value: +"Every catalogue asked, with the identifier the record carries there. This is where what each of them did with the record is read: the state says which of the three it met, and the reason says why it was not asked or what went wrong." - removed
Output schema / properties / per_sourceRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "algorithms_not_searched": { - "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", - "items": { - "type": "string" - }, - "type": "array" - }, - "count": { - "description": "Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes.", - "type": "number" - }, - "error": { - "enum": [ - "not_found", - "invalid_input", - "rate_limited", - "parse_failure", - "network_error", - "timeout" - ], - "type": "string" - }, - "index_total": { - "description": "What its own index holds for this question, the rows on this page included.", - "type": "number" - }, - "index_total_over_any_word": { - "description": "The total counts rows carrying any word of the query, its text index reading them apart.", - "type": "boolean" - }, - "moment": { - "description": "Which moment failed.", - "type": "string" - }, - "name": { - "description": "The name that catalogue calls itself.", - "type": "string" - }, - "narrowings_naming_no_record": { - "description": "Narrowings written only with identifiers another catalogue minted.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_not_received": { - "description": "Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_received_in_part": { - "items": { - "type": "string" - }, - "type": "array" - }, - "reason": { - "description": "Why it was not asked, or what went wrong.", - "type": "string" - }, - "records": { - "description": "Distinct records behind its rows.", - "type": "number" - }, - "skipped": { - "description": "Rows it answered with that this client could not read and left out.", - "type": "number" - }, - "source": { - "description": "The catalogue this row is about.", - "type": "string" - }, - "state": { - "description": "'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world.", - "enum": [ - "answered", - "failed", - "absent" - ], - "type": "string" - }, - "unattributed": { - "description": "Records it answered with while carrying none of what was asked about.", - "type": "number" - } - }, - "required": [ - "source", - "state" - ], - "type": "object" - }, - "type": "array" -}
- Changed
get_tag3 fields changed- changed
Input schema / properties / prefer / descriptionPrevious value: -"The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied."New value: +"The order the catalogues are preferred in where they disagree on a field. Left out, the registry's own order stands, and every card states the order applied." - changed
Output schema / properties / card / properties / held_by / descriptionPrevious value: -"Every catalogue asked, with the identifier the record carries there."New value: +"Every catalogue asked, with the identifier the record carries there. This is where what each of them did with the record is read: the state says which of the three it met, and the reason says why it was not asked or what went wrong." - removed
Output schema / properties / per_sourceRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "algorithms_not_searched": { - "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", - "items": { - "type": "string" - }, - "type": "array" - }, - "count": { - "description": "Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes.", - "type": "number" - }, - "error": { - "enum": [ - "not_found", - "invalid_input", - "rate_limited", - "parse_failure", - "network_error", - "timeout" - ], - "type": "string" - }, - "index_total": { - "description": "What its own index holds for this question, the rows on this page included.", - "type": "number" - }, - "index_total_over_any_word": { - "description": "The total counts rows carrying any word of the query, its text index reading them apart.", - "type": "boolean" - }, - "moment": { - "description": "Which moment failed.", - "type": "string" - }, - "name": { - "description": "The name that catalogue calls itself.", - "type": "string" - }, - "narrowings_naming_no_record": { - "description": "Narrowings written only with identifiers another catalogue minted.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_not_received": { - "description": "Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something.", - "items": { - "type": "string" - }, - "type": "array" - }, - "narrowings_received_in_part": { - "items": { - "type": "string" - }, - "type": "array" - }, - "reason": { - "description": "Why it was not asked, or what went wrong.", - "type": "string" - }, - "records": { - "description": "Distinct records behind its rows.", - "type": "number" - }, - "skipped": { - "description": "Rows it answered with that this client could not read and left out.", - "type": "number" - }, - "source": { - "description": "The catalogue this row is about.", - "type": "string" - }, - "state": { - "description": "'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world.", - "enum": [ - "answered", - "failed", - "absent" - ], - "type": "string" - }, - "unattributed": { - "description": "Records it answered with while carrying none of what was asked about.", - "type": "number" - } - }, - "required": [ - "source", - "state" - ], - "type": "object" - }, - "type": "array" -}
- Changed
search_performers32 fields changed- added
Input schema / properties / alias / descriptionAdded value: +"No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received." - added
Input schema / properties / career_end_year / descriptionAdded value: +"No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received." - added
Input schema / properties / career_start_year / descriptionAdded value: +"No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received." - changed
Input schema / properties / limit / descriptionPrevious value: -"How many rows one page of one catalogue carries. An answer holding several catalogues carries up to this many from each of them, since their pages are their own and nothing here interleaves them into one."New value: +"How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one." - changed
Input schema / properties / page / descriptionPrevious value: -"The page of each catalogue's own order. A route that reads words alone takes no page, and the answer reports it as one that catalogue did not receive."New value: +"Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page." - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What its own index holds for this question, the rows on this page included."New value: +"What its own index holds for the question it received, the rows on this page included. A narrowing it could not receive, or received in part, leaves that question wider than the one written." - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something."New value: +"Narrowings that shaped no part of the request this catalogue received, whether its route declares no field for one or nothing written was left for one to decide." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_part / descriptionAdded value: +"Narrowings it received short of what was written, the rest of the list naming records another catalogue minted. It answered a wider question than the one asked." - added
Output schema / properties / results / items / properties / aliasesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / also_held_atAdded value: +{ + "description": "The same record on another catalogue, read from a link this one publishes to it. A shared name is no join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / birth_dateAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "precision": { + "description": "How much of the date the catalogue holds: a month or a year can be all of it.", + "enum": [ + "day", + "month", + "year" + ], + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "value", + "precision" + ], + "type": "object" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / birth_date_unreadableAdded value: +{ + "description": "A birth date was published in a shape this client could not read, so the null above is a reading that failed rather than a date nobody entered.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / career_end_yearAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / career_start_yearAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / countryAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / death_dateAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "precision": { + "description": "How much of the date the catalogue holds: a month or a year can be all of it.", + "enum": [ + "day", + "month", + "year" + ], + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "value", + "precision" + ], + "type": "object" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / death_date_unreadableAdded value: +{ + "description": "The same, for the date of death.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / disambiguationAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "What its catalogue tells two people of one name apart by." +} - added
Output schema / properties / results / items / properties / genderAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / linked_unfollowedAdded value: +{ + "description": "A link to another catalogue naming the record by something this client cannot address. The link is written, and following it is what failed.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "source", + "url" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / merged_idsAdded value: +{ + "description": "Identifiers folded into this record, each of which still addresses it.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / merged_intoAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The record this one was folded into, where its catalogue publishes one." +} - added
Output schema / properties / results / items / properties / nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / pending_editsAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Edits open against it, null where its catalogue publishes no such count." +} - added
Output schema / properties / results / items / properties / pending_edits_unreadableAdded value: +{ + "description": "The count of open edits came back in a shape this client could not read.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / retrieved_atAdded value: +{ + "description": "The moment this client read it there.", + "type": "string" +} - added
Output schema / properties / results / items / properties / rows_skippedAdded value: +{ + "description": "Entries of this record's own lists that could not be read and are left out.", + "type": "number" +} - added
Output schema / properties / results / items / properties / rows_skipped_inAdded value: +{ + "description": "Which of its lists lost them.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / scene_countAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Scenes this one catalogue indexes them on, which measures its coverage and no career. Never added to another catalogue's." +} - added
Output schema / properties / results / items / properties / status / descriptionAdded value: +"What the identifier addresses on that catalogue now. A folded record answers with a marker, which describes the record and never what it once named." - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "id", - "source", - "source_url", - "status" -]New value: +[ + "id", + "source", + "source_url", + "retrieved_at", + "status", + "also_held_at", + "linked_unfollowed", + "name", + "disambiguation", + "aliases", + "gender", + "country", + "birth_date", + "death_date", + "career_start_year", + "career_end_year", + "scene_count", + "merged_into", + "merged_ids" +] - changed
Output schema / properties / window / descriptionPrevious value: -"The page a catalogue paged through, absent where none was given one."New value: +"The page and the row limit this answer was read at, absent where no catalogue answered."
- Changed
search_scenes26 fields changed- added
Input schema / properties / alias / descriptionAdded value: +"No catalogue's faceted route applies it, though every faceted input declares it: a request carrying it answers as wide as one carrying none, so it is never sent and the answer names it as a narrowing nobody received." - changed
Input schema / properties / limit / descriptionPrevious value: -"How many rows one page of one catalogue carries. An answer holding several catalogues carries up to this many from each of them, since their pages are their own and nothing here interleaves them into one."New value: +"How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one." - added
Input schema / properties / match / descriptionAdded value: +"How performer_ids and tag_ids are read, which are the only arguments it governs. 'all', the default, asks for scenes carrying every identifier of a list; 'any' for scenes carrying at least one, which answers counts an order of magnitude wider. A scene names one studio, so studio_ids asks for any of its identifiers under both readings. The lists narrow against each other as an intersection either way." - changed
Input schema / properties / page / descriptionPrevious value: -"The page of each catalogue's own order. A route that reads words alone takes no page, and the answer reports it as one that catalogue did not receive."New value: +"Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page." - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What its own index holds for this question, the rows on this page included."New value: +"What its own index holds for the question it received, the rows on this page included. A narrowing it could not receive, or received in part, leaves that question wider than the one written." - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something."New value: +"Narrowings that shaped no part of the request this catalogue received, whether its route declares no field for one or nothing written was left for one to decide." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_part / descriptionAdded value: +"Narrowings it received short of what was written, the rest of the list naming records another catalogue minted. It answered a wider question than the one asked." - added
Output schema / properties / results / items / properties / also_held_atAdded value: +{ + "description": "The same record on another catalogue, read from a link this one publishes to it. A shared name is no join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / codeAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The studio's own reference for the release." +} - added
Output schema / properties / results / items / properties / duration_secondsAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "The runtime that catalogue holds, which is what separates two cuts of one title." +} - added
Output schema / properties / results / items / properties / linked_unfollowedAdded value: +{ + "description": "A link to another catalogue naming the record by something this client cannot address. The link is written, and following it is what failed.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "source", + "url" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / pending_editsAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Edits open against it, null where its catalogue publishes no such count." +} - added
Output schema / properties / results / items / properties / pending_edits_unreadableAdded value: +{ + "description": "The count of open edits came back in a shape this client could not read.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / performersAdded value: +{ + "items": { + "additionalProperties": false, + "properties": { + "creditedAs": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "The name this release printed, where it differs." + }, + "disambiguation": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "established", + "merged", + "deleted" + ], + "type": "string" + } + }, + "required": [ + "id", + "name", + "creditedAs", + "disambiguation", + "status" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / production_date_unreadableAdded value: +{ + "description": "The same, for the production date, which get_scene answers with.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / release_dateAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "precision": { + "description": "How much of the date the catalogue holds: a month or a year can be all of it.", + "enum": [ + "day", + "month", + "year" + ], + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "value", + "precision" + ], + "type": "object" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / release_date_unreadableAdded value: +{ + "description": "A release date was published in a shape this client could not read, so the null above is a reading that failed rather than a date nobody entered.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / retrieved_atAdded value: +{ + "description": "The moment this client read it there.", + "type": "string" +} - added
Output schema / properties / results / items / properties / rows_skippedAdded value: +{ + "description": "Entries of this record's own lists that could not be read and are left out.", + "type": "number" +} - added
Output schema / properties / results / items / properties / rows_skipped_inAdded value: +{ + "description": "Which of its lists lost them.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / status / descriptionAdded value: +"What the identifier addresses on that catalogue now. A folded record answers with a marker, which describes the record and never what it once named." - added
Output schema / properties / results / items / properties / studioAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parent": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "parentWithdrawn": { + "type": "boolean" + }, + "status": { + "enum": [ + "established", + "merged", + "deleted" + ], + "type": "string" + } + }, + "required": [ + "id", + "name", + "parent", + "status" + ], + "type": "object" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / tagsAdded value: +{ + "description": "Each tag with the identifier the next call takes. What its catalogue filed it under is a fact about the tag, which get_tag answers.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "name" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / titleAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "id", - "source", - "source_url", - "status" -]New value: +[ + "id", + "source", + "source_url", + "retrieved_at", + "status", + "also_held_at", + "linked_unfollowed", + "title", + "code", + "duration_seconds", + "release_date", + "studio", + "performers", + "tags" +] - changed
Output schema / properties / window / descriptionPrevious value: -"The page a catalogue paged through, absent where none was given one."New value: +"The page and the row limit this answer was read at, absent where no catalogue answered."
- Changed
search_studios19 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"How many rows one page of one catalogue carries. An answer holding several catalogues carries up to this many from each of them, since their pages are their own and nothing here interleaves them into one."New value: +"How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one." - changed
Input schema / properties / page / descriptionPrevious value: -"The page of each catalogue's own order. A route that reads words alone takes no page, and the answer reports it as one that catalogue did not receive."New value: +"Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page." - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What its own index holds for this question, the rows on this page included."New value: +"What its own index holds for the question it received, the rows on this page included. A narrowing it could not receive, or received in part, leaves that question wider than the one written." - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something."New value: +"Narrowings that shaped no part of the request this catalogue received, whether its route declares no field for one or nothing written was left for one to decide." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_part / descriptionAdded value: +"Narrowings it received short of what was written, the rest of the list naming records another catalogue minted. It answered a wider question than the one asked." - added
Output schema / properties / results / items / properties / aliasesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / also_held_atAdded value: +{ + "description": "The same record on another catalogue, read from a link this one publishes to it. A shared name is no join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / linked_unfollowedAdded value: +{ + "description": "A link to another catalogue naming the record by something this client cannot address. The link is written, and following it is what failed.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "source", + "url" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / parentAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "status": { + "enum": [ + "established", + "merged", + "deleted" + ], + "type": "string" + } + }, + "required": [ + "id", + "name", + "status" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The studio this one sits under, where its catalogue records one." +} - added
Output schema / properties / results / items / properties / pending_editsAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Edits open against it, null where its catalogue publishes no such count." +} - added
Output schema / properties / results / items / properties / pending_edits_unreadableAdded value: +{ + "description": "The count of open edits came back in a shape this client could not read.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / retrieved_atAdded value: +{ + "description": "The moment this client read it there.", + "type": "string" +} - added
Output schema / properties / results / items / properties / rows_skippedAdded value: +{ + "description": "Entries of this record's own lists that could not be read and are left out.", + "type": "number" +} - added
Output schema / properties / results / items / properties / rows_skipped_inAdded value: +{ + "description": "Which of its lists lost them.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / scene_countAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Scenes this one catalogue indexes on it, null where it publishes no such count. Never added to another catalogue's." +} - added
Output schema / properties / results / items / properties / status / descriptionAdded value: +"What the identifier addresses on that catalogue now. A folded record answers with a marker, which describes the record and never what it once named." - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "id", - "source", - "source_url", - "status" -]New value: +[ + "id", + "source", + "source_url", + "retrieved_at", + "status", + "also_held_at", + "linked_unfollowed", + "name", + "aliases", + "parent", + "scene_count" +] - changed
Output schema / properties / window / descriptionPrevious value: -"The page a catalogue paged through, absent where none was given one."New value: +"The page and the row limit this answer was read at, absent where no catalogue answered."
- Changed
search_tags19 fields changed- changed
Input schema / properties / limit / descriptionPrevious value: -"How many rows one page of one catalogue carries. An answer holding several catalogues carries up to this many from each of them, since their pages are their own and nothing here interleaves them into one."New value: +"How many rows one page of one catalogue carries. An answer holding several carries up to this many from each: their pages are their own and nothing here interleaves them into one." - changed
Input schema / properties / page / descriptionPrevious value: -"The page of each catalogue's own order. A route that reads words alone takes no page, and the answer reports it as one that catalogue did not receive."New value: +"Which page of its own order every catalogue asked is read at, counted from 1. A search written with words alone reads the first rows each text index answers with, since those routes take no page." - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What its own index holds for this question, the rows on this page included."New value: +"What its own index holds for the question it received, the rows on this page included. A narrowing it could not receive, or received in part, leaves that question wider than the one written." - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something."New value: +"Narrowings that shaped no part of the request this catalogue received, whether its route declares no field for one or nothing written was left for one to decide." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_part / descriptionAdded value: +"Narrowings it received short of what was written, the rest of the list naming records another catalogue minted. It answered a wider question than the one asked." - added
Output schema / properties / results / items / properties / aliasesAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / also_held_atAdded value: +{ + "description": "The same record on another catalogue, read from a link this one publishes to it. A shared name is no join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / categoryAdded value: +{ + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "group": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + }, + "id": { + "type": "string" + }, + "name": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "id", + "name", + "group" + ], + "type": "object" + }, + { + "type": "null" + } + ], + "description": "The table its catalogue sorts the tag under, and the group that table covers." +} - added
Output schema / properties / results / items / properties / descriptionAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / linked_unfollowedAdded value: +{ + "description": "A link to another catalogue naming the record by something this client cannot address. The link is written, and following it is what failed.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "required": [ + "source", + "url" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / nameAdded value: +{ + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] +} - added
Output schema / properties / results / items / properties / pending_editsAdded value: +{ + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ], + "description": "Edits open against it, null where its catalogue publishes no such count." +} - added
Output schema / properties / results / items / properties / pending_edits_unreadableAdded value: +{ + "description": "The count of open edits came back in a shape this client could not read.", + "type": "boolean" +} - added
Output schema / properties / results / items / properties / retrieved_atAdded value: +{ + "description": "The moment this client read it there.", + "type": "string" +} - added
Output schema / properties / results / items / properties / rows_skippedAdded value: +{ + "description": "Entries of this record's own lists that could not be read and are left out.", + "type": "number" +} - added
Output schema / properties / results / items / properties / rows_skipped_inAdded value: +{ + "description": "Which of its lists lost them.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / results / items / properties / status / descriptionAdded value: +"What the identifier addresses on that catalogue now. A folded record answers with a marker, which describes the record and never what it once named." - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "id", - "source", - "source_url", - "status" -]New value: +[ + "id", + "source", + "source_url", + "retrieved_at", + "status", + "also_held_at", + "linked_unfollowed", + "name", + "description", + "aliases", + "category" +] - changed
Output schema / properties / window / descriptionPrevious value: -"The page a catalogue paged through, absent where none was given one."New value: +"The page and the row limit this answer was read at, absent where no catalogue answered."
10 tool updates
v0.9.0- Changed
find_by_fingerprint57 fields changed- removed
Input schema / properties / fingerprints / descriptionRemoved value: -"Every fingerprint held for one file. A file carries one hash per algorithm, so a longer list is an inventory rather than a question." - added
Input schema / properties / fingerprints / items / additionalPropertiesAdded value: +false - removed
Input schema / properties / fingerprints / items / properties / hash / descriptionRemoved value: -"The fingerprint as the hashing tool produced it." - added
Input schema / properties / fingerprints / items / properties / hash / minLengthAdded value: +1 - changed
Input schema / properties / fingerprints / maxItemsPrevious value: -10New value: +25 - added
Input schema / properties / preferAdded value: +{ + "description": "The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied.", + "items": { + "enum": [ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" + ], + "type": "string" + }, + "maxItems": 5, + "minItems": 1, + "type": "array" +} - added
Input schema / properties / sectionsAdded value: +{ + "description": "The blocks read beside the record's own fields, which come back whatever is written here. Each name adds a block, and 'basic' asks for those fields alone.", + "items": { + "enum": [ + "basic", + "fingerprints", + "images" + ], + "type": "string" + }, + "maxItems": 3, + "minItems": 1, + "type": "array" +} - removed
Input schema / properties / sources / descriptionRemoved value: -"Narrow to named catalogues. Every configured catalogue is asked by default." - added
Input schema / properties / sources / items / enumAdded value: +[ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" +] - added
Input schema / properties / sources / maxItemsAdded value: +5 - added
Input schema / properties / sources / minItemsAdded value: +1 - added
Output schema / properties / cachedAdded value: +{ + "type": "boolean" +} - changed
Output schema / properties / match_count / descriptionPrevious value: -"Matches returned: one per scene per fingerprint it carries."New value: +"One per record reached, whatever number of hashes reached it." - removed
Output schema / properties / matches / items / properties / algorithmRemoved value: -{ - "enum": [ - "MD5", - "OSHASH", - "PHASH" - ], - "type": "string" -} - removed
Output schema / properties / matches / items / properties / fingerprintRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "algorithm": { - "enum": [ - "MD5", - "OSHASH", - "PHASH" - ], - "type": "string" - }, - "contested": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "Whether the reports reach the submissions. Null where the catalogue publishes no report count: a fingerprint nobody has disputed and one on a catalogue that counts no disputes are different things." - }, - "duration_seconds": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "hash": { - "type": "string" - }, - "reports": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "How many people reported against it. Null on a catalogue that records no reports, which is an unknown contest and never an absence of one." - }, - "submissions": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "How many people entered this fingerprint. Null where the catalogue counts none." - } - }, - "required": [ - "algorithm", - "hash", - "duration_seconds", - "submissions", - "reports", - "contested" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - changed
Output schema / properties / matches / items / properties / match_kind / descriptionPrevious value: -"'exact_file' means the same file, byte for byte. 'perceptual_similarity' means images that resemble each other, which covers a re-encode, a crop and a different scene from one shoot, and is no evidence that two files are the same."New value: +"'exact_file' names the same bytes. 'perceptual_similarity' covers a re-encode, a crop and another scene from one shoot, and is no evidence that two files are the same." - added
Output schema / properties / matches / items / properties / matched_byAdded value: +{ + "description": "Every hash that reached this record, each naming the catalogues it reached it on. A hash a catalogue does not search reached nothing there.", + "items": { + "additionalProperties": false, + "properties": { + "algorithm": { + "enum": [ + "MD5", + "OSHASH", + "PHASH" + ], + "type": "string" + }, + "hash": { + "type": "string" + }, + "sources": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "hash", + "algorithm", + "sources" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / matches / items / properties / scene / properties / countsAdded value: +{ + "additionalProperties": { + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "state": { + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "source", + "value", + "state" + ], + "type": "object" + }, + "type": "array" + }, + "description": "Counts, one entry per catalogue asked and never added together. A null is read with the state beside it: a catalogue that publishes no such count, one that could not answer, and one nobody asked are three different facts.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / matches / items / properties / scene / properties / fieldsAdded value: +{ + "additionalProperties": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "agreed_by": { + "description": "The catalogues that published this reading. Two agreeing is evidence of its own.", + "items": { + "type": "string" + }, + "type": "array" + }, + "disagreed": { + "description": "The readings nobody preferred, published rather than dropped: choosing between them is a policy, and a policy applied in silence is a claim nobody can check.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "value": {} + }, + "required": [ + "source", + "value" + ], + "type": "object" + }, + "type": "array" + }, + "value": { + "description": "The reading the preference named, null where none published one." + } + }, + "required": [ + "value", + "agreed_by" + ], + "type": "object" + }, + { + "items": { + "additionalProperties": false, + "properties": { + "also_at": { + "description": "What this same record is called on another catalogue, from a link joining the two.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "published_by": { + "description": "Every catalogue that published this very record. A shared name is no join, so an entry one catalogue minted names that one.", + "items": { + "type": "string" + }, + "type": "array" + }, + "same_name_as": { + "description": "A record another catalogue published under a matching name. A resemblance, never a join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "value": {} + }, + "required": [ + "value", + "published_by" + ], + "type": "object" + }, + "type": "array" + } + ] + }, + "description": "Each field of the record. A scalar carries the catalogues that said it; a list is the union, each entry naming who published it.", + "propertyNames": { + "type": "string" + }, + "type": "object" +} - added
Output schema / properties / matches / items / properties / scene / properties / held_byAdded value: +{ + "description": "Every catalogue asked, with the identifier the record carries there.", + "items": { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "retrieved_at": { + "description": "The moment this client read it there.", + "type": "string" + }, + "source": { + "type": "string" + }, + "source_url": { + "description": "The address it was read at, which credits it.", + "type": "string" + }, + "state": { + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "status": { + "description": "What the identifier addresses there now. A folded record is held elsewhere.", + "enum": [ + "established", + "merged", + "deleted" + ], + "type": "string" + } + }, + "required": [ + "source", + "state" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / matches / items / properties / scene / properties / idRemoved value: -{ - "type": "string" -} - added
Output schema / properties / matches / items / properties / scene / properties / kindAdded value: +{ + "type": "string" +} - added
Output schema / properties / matches / items / properties / scene / properties / notesAdded value: +{ + "description": "What this answer does not establish, which is what makes it safe to act on.", + "items": { + "type": "string" + }, + "type": "array" +} - removed
Output schema / properties / matches / items / properties / scene / properties / performersRemoved value: -{ - "items": { - "type": "string" - }, - "type": "array" -} - added
Output schema / properties / matches / items / properties / scene / properties / preferredAdded value: +{ + "description": "The order the readings were preferred in. This is the policy that was applied, whether or not every catalogue in it answered.", + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / matches / items / properties / scene / properties / read_fromAdded value: +{ + "description": "The catalogues that answered, in the order they were preferred. A policy and an outcome are two facts, and a reader deciding whether to change the policy needs both.", + "items": { + "type": "string" + }, + "type": "array" +} - removed
Output schema / properties / matches / items / properties / scene / properties / release_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / matches / items / properties / scene / properties / sourceRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / matches / items / properties / scene / properties / source_urlRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / matches / items / properties / scene / properties / studioRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / matches / items / properties / scene / properties / titleRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - changed
Output schema / properties / matches / items / properties / scene / requiredPrevious value: -[ - "id", - "source", - "title", - "release_date", - "studio", - "performers", - "source_url" -]New value: +[ + "kind", + "fields", + "counts", + "held_by", + "preferred", + "read_from", + "notes" +] - changed
Output schema / properties / matches / items / requiredPrevious value: -[ - "algorithm", - "match_kind", - "scene", - "fingerprint" -]New value: +[ + "scene", + "matched_by", + "match_kind" +] - added
Output schema / properties / not_searchedAdded value: +{ + "description": "Hashes and the catalogues that answered without searching their algorithm, so they were never put to those. Nobody looked there, which is no evidence about the files behind them.", + "items": { + "additionalProperties": false, + "properties": { + "algorithm": { + "type": "string" + }, + "hash": { + "type": "string" + }, + "sources": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "hash", + "algorithm", + "sources" + ], + "type": "object" + }, + "type": "array" +} - added
Output schema / properties / per_source / items / properties / algorithms_not_searchedAdded value: +{ + "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / properties / count / descriptionPrevious value: -"Rows this catalogue contributed to this page."New value: +"Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes." - added
Output schema / properties / per_source / items / properties / error / enumAdded value: +[ + "not_found", + "invalid_input", + "rate_limited", + "parse_failure", + "network_error", + "timeout" +] - removed
Output schema / properties / per_source / items / properties / fields_searchedRemoved value: -{ - "description": "The fields the text index read. Absent where no text index was consulted.", - "items": { - "type": "string" - }, - "type": "array" -} - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What this catalogue's index holds for the question, beyond the page returned."New value: +"What its own index holds for this question, the rows on this page included." - added
Output schema / properties / per_source / items / properties / index_total_over_any_wordAdded value: +{ + "description": "The total counts rows carrying any word of the query, its text index reading them apart.", + "type": "boolean" +} - changed
Output schema / properties / per_source / items / properties / moment / descriptionPrevious value: -"Which step failed, for a catalogue that failed."New value: +"Which moment failed." - added
Output schema / properties / per_source / items / properties / name / descriptionAdded value: +"The name that catalogue calls itself." - added
Output schema / properties / per_source / items / properties / narrowings_naming_no_recordAdded value: +{ + "description": "Narrowings written only with identifiers another catalogue minted.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue could not receive. A row of its satisfying one of them does so by chance."New value: +"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_partAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / per_source / items / properties / reason / descriptionAdded value: +"Why it was not asked, or what went wrong." - added
Output schema / properties / per_source / items / properties / recordsAdded value: +{ + "description": "Distinct records behind its rows.", + "type": "number" +} - added
Output schema / properties / per_source / items / properties / skipped / descriptionAdded value: +"Rows it answered with that this client could not read and left out." - added
Output schema / properties / per_source / items / properties / source / descriptionAdded value: +"The catalogue this row is about." - changed
Output schema / properties / per_source / items / properties / state / descriptionPrevious value: -"'answered' is a catalogue that looked, and a count of zero there means it found nothing. 'failed' could not answer. 'absent' was never asked. An answer holding rows from some catalogues is no evidence about the others."New value: +"'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world." - added
Output schema / properties / per_source / items / properties / unattributed / descriptionAdded value: +"Records it answered with while carrying none of what was asked about." - added
Output schema / properties / records_namedAdded value: +{ + "description": "Distinct records an exact hash named. Two hashes reaching one record count once, and a perceptual match names no record.", + "type": "number" +} - added
Output schema / properties / resemblancesAdded value: +{ + "description": "Matches a perceptual hash reached, each a likeness and no claim about any file's bytes.", + "type": "number" +} - removed
Output schema / properties / scenes_matchedRemoved value: -{ - "description": "Distinct scenes behind those matches.", - "type": "number" -} - changed
Output schema / properties / unattributed / descriptionPrevious value: -"Scenes a catalogue answered with while returning none of the fingerprints asked for. Which hash reached them is unknown, which is kept apart from a catalogue that found nothing."New value: +"Records the catalogues answered with that carry none of the hashes asked. Which hash reached them is unknown, so they stand as no match and are counted apart." - added
Output schema / properties / unmatchedAdded value: +{ + "description": "The hashes put to a catalogue that answered, which reached no record there. A catalogue named as unasked says nothing about them either way.", + "items": { + "additionalProperties": false, + "properties": { + "algorithm": { + "type": "string" + }, + "hash": { + "type": "string" + } + }, + "required": [ + "hash", + "algorithm" + ], + "type": "object" + }, + "type": "array" +} - changed
Output schema / requiredPrevious value: -[ - "asked", - "matches", - "match_count", - "scenes_matched", - "unattributed", - "per_source", - "notes" -]New value: +[ + "matches", + "match_count", + "records_named", + "resemblances", + "unattributed", + "unmatched", + "not_searched", + "asked", + "per_source", + "notes" +]
- Changed
get_performer43 fields changed- removed
Input schema / properties / id / descriptionRemoved value: -"Identifier as returned by another tool, such as stashdb:<uuid>." - added
Input schema / properties / id / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - added
Input schema / properties / preferAdded value: +{ + "description": "The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied.", + "items": { + "enum": [ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" + ], + "type": "string" + }, + "maxItems": 5, + "minItems": 1, + "type": "array" +} - changed
Input schema / properties / sections / descriptionPrevious value: -"Which blocks to load. Defaults to basic."New value: +"The blocks read beside the record's own fields, which come back whatever is written here. Each name adds a block, and 'basic' asks for those fields alone. 'studios' is the whole table of studios they are credited on, which runs to hundreds of rows." - changed
Input schema / properties / sections / items / enumPrevious value: -[ - "basic", - "appearance", - "images", - "scenes", - "studios" -]New value: +[ + "basic", + "appearance", + "images", + "studios" +] - added
Input schema / properties / sections / maxItemsAdded value: +4 - added
Input schema / properties / sections / minItemsAdded value: +1 - added
Input schema / properties / sourcesAdded value: +{ + "items": { + "enum": [ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" + ], + "type": "string" + }, + "maxItems": 5, + "minItems": 1, + "type": "array" +} - removed
Output schema / descriptionRemoved value: -"A performer when 'status' is 'established'. When it is 'deleted' or 'merged' this is a marker: it carries the identifier, the catalogue, the successor, the identifiers folded in and the former name, and no field describing a person." - removed
Output schema / properties / aliasesRemoved value: -{ - "description": "Stage names and variant spellings alike.", - "items": { - "type": "string" - }, - "type": "array" -} - removed
Output schema / properties / appearanceRemoved value: -{ - "additionalProperties": false, - "properties": { - "band_size": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "breast_type": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "cup_size": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "ethnicity": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "eye_color": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "hair_color": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "height_cm": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "hip_size": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "piercings": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ] - }, - "tattoos": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ] - }, - "waist_size": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "ethnicity", - "eye_color", - "hair_color", - "height_cm", - "breast_type", - "cup_size", - "band_size", - "waist_size", - "hip_size", - "tattoos", - "piercings" - ], - "type": "object" -} - removed
Output schema / properties / birth_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - added
Output schema / properties / cachedAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / cardAdded value: +{ + "additionalProperties": false, + "properties": { + "counts": { + "additionalProperties": { + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "state": { + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "source", + "value", + "state" + ], + "type": "object" + }, + "type": "array" + }, + "description": "Counts, one entry per catalogue asked and never added together. A null is read with the state beside it: a catalogue that publishes no such count, one that could not answer, and one nobody asked are three different facts.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "fields": { + "additionalProperties": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "agreed_by": { + "description": "The catalogues that published this reading. Two agreeing is evidence of its own.", + "items": { + "type": "string" + }, + "type": "array" + }, + "disagreed": { + "description": "The readings nobody preferred, published rather than dropped: choosing between them is a policy, and a policy applied in silence is a claim nobody can check.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "value": {} + }, + "required": [ + "source", + "value" + ], + "type": "object" + }, + "type": "array" + }, + "value": { + "description": "The reading the preference named, null where none published one." + } + }, + "required": [ + "value", + "agreed_by" + ], + "type": "object" + }, + { + "items": { + "additionalProperties": false, + "properties": { + "also_at": { + "description": "What this same record is called on another catalogue, from a link joining the two.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "published_by": { + "description": "Every catalogue that published this very record. A shared name is no join, so an entry one catalogue minted names that one.", + "items": { + "type": "string" + }, + "type": "array" + }, + "same_name_as": { + "description": "A record another catalogue published under a matching name. A resemblance, never a join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "value": {} + }, + "required": [ + "value", + "published_by" + ], + "type": "object" + }, + "type": "array" + } + ] + }, + "description": "Each field of the record. A scalar carries the catalogues that said it; a list is the union, each entry naming who published it.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "held_by": { + "description": "Every catalogue asked, with the identifier the record carries there.", + "items": { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "retrieved_at": { + "description": "The moment this client read it there.", + "type": "string" + }, + "source": { + "type": "string" + }, + "source_url": { + "description": "The address it was read at, which credits it.", + "type": "string" + }, + "state": { + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "status": { + "description": "What the identifier addresses there now. A folded record is held elsewhere.", + "enum": [ + "established", + "merged", + "deleted" + ], + "type": "string" + } + }, + "required": [ + "source", + "state" + ], + "type": "object" + }, + "type": "array" + }, + "kind": { + "type": "string" + }, + "notes": { + "description": "What this answer does not establish, which is what makes it safe to act on.", + "items": { + "type": "string" + }, + "type": "array" + }, + "preferred": { + "description": "The order the readings were preferred in. This is the policy that was applied, whether or not every catalogue in it answered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "read_from": { + "description": "The catalogues that answered, in the order they were preferred. A policy and an outcome are two facts, and a reader deciding whether to change the policy needs both.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "kind", + "fields", + "counts", + "held_by", + "preferred", + "read_from", + "notes" + ], + "type": "object" +} - removed
Output schema / properties / career_end_yearRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / career_start_yearRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / countryRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / createdRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / death_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / disambiguationRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Free text telling two people apart. It reads and never parses." -} - removed
Output schema / properties / former_nameRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Present on a marker: the name the record carried." -} - removed
Output schema / properties / genderRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / idRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / imagesRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "height": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "url": { - "description": "The address of the image. The image itself is never returned.", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "url", - "width", - "height" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / merged_idsRemoved value: -{ - "items": { - "type": "string" - }, - "type": "array" -} - removed
Output schema / properties / merged_intoRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Present on a marker: the identifier that continues this record." -} - removed
Output schema / properties / nameRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / notesRemoved value: -{ - "items": { - "type": "string" - }, - "type": "array" -} - added
Output schema / properties / per_sourceAdded value: +{ + "items": { + "additionalProperties": false, + "properties": { + "algorithms_not_searched": { + "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", + "items": { + "type": "string" + }, + "type": "array" + }, + "count": { + "description": "Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes.", + "type": "number" + }, + "error": { + "enum": [ + "not_found", + "invalid_input", + "rate_limited", + "parse_failure", + "network_error", + "timeout" + ], + "type": "string" + }, + "index_total": { + "description": "What its own index holds for this question, the rows on this page included.", + "type": "number" + }, + "index_total_over_any_word": { + "description": "The total counts rows carrying any word of the query, its text index reading them apart.", + "type": "boolean" + }, + "moment": { + "description": "Which moment failed.", + "type": "string" + }, + "name": { + "description": "The name that catalogue calls itself.", + "type": "string" + }, + "narrowings_naming_no_record": { + "description": "Narrowings written only with identifiers another catalogue minted.", + "items": { + "type": "string" + }, + "type": "array" + }, + "narrowings_not_received": { + "description": "Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something.", + "items": { + "type": "string" + }, + "type": "array" + }, + "narrowings_received_in_part": { + "items": { + "type": "string" + }, + "type": "array" + }, + "reason": { + "description": "Why it was not asked, or what went wrong.", + "type": "string" + }, + "records": { + "description": "Distinct records behind its rows.", + "type": "number" + }, + "skipped": { + "description": "Rows it answered with that this client could not read and left out.", + "type": "number" + }, + "source": { + "description": "The catalogue this row is about.", + "type": "string" + }, + "state": { + "description": "'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world.", + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "unattributed": { + "description": "Records it answered with while carrying none of what was asked about.", + "type": "number" + } + }, + "required": [ + "source", + "state" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / retrieved_atRemoved value: -{ - "description": "When this record came off the catalogue, ISO 8601.", - "type": "string" -} - removed
Output schema / properties / scene_countRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Scenes this catalogue has indexed crediting this performer. A settled record naming a career spanning decades can report zero, which measures coverage and states nothing about a person's work. Null on a marker, since the count belongs to the record that continues it." -} - removed
Output schema / properties / scene_count_meansRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / scenesRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "release_date": { - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ] - }, - "source_url": { - "type": "string" - }, - "studio": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "title": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "id", - "title", - "release_date", - "studio", - "source_url" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / scenes_totalRemoved value: -{ - "description": "What the catalogue holds behind the one page this section shows.", - "type": "number" -} - removed
Output schema / properties / scenes_unavailableRemoved value: -{ - "description": "Why the section is missing, when it was asked for and could not be read. Its absence then says nothing about what the catalogue holds.", - "type": "string" -} - removed
Output schema / properties / sourceRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / source_urlRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / statusRemoved value: -{ - "description": "'established' is a record the catalogue holds. 'merged' and 'deleted' are markers: the identifier resolves, and what comes back describes the record rather than the thing it once named.", - "enum": [ - "established", - "deleted", - "merged" - ], - "type": "string" -} - removed
Output schema / properties / studiosRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "sceneCount": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "id", - "name", - "sceneCount" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / studios_totalRemoved value: -{ - "description": "How many the record credits, where the section shows a page of them.", - "type": "number" -} - removed
Output schema / properties / updatedRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / urlsRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "site_category": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "The catalogue's own category for that site. Null on a catalogue that publishes no table of sites, since borrowing a neighbour's would sort a link by a taxonomy this catalogue never applied." - }, - "site_name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "required": [ - "url", - "site_name", - "site_category" - ], - "type": "object" - }, - "type": "array" -} - changed
Output schema / requiredPrevious value: -[ - "id", - "source", - "source_url", - "retrieved_at", - "status", - "notes" -]New value: +[ + "card" +]
- Changed
get_scene37 fields changed- removed
Input schema / properties / id / descriptionRemoved value: -"Identifier as returned by another tool, such as stashdb:<uuid>." - added
Input schema / properties / id / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - added
Input schema / properties / preferAdded value: +{ + "description": "The order the catalogues are preferred in where they disagree on a field. Left out, the order the registry declares stands. Every card states the order that was applied.", + "items": { + "enum": [ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" + ], + "type": "string" + }, + "maxItems": 5, + "minItems": 1, + "type": "array" +} - changed
Input schema / properties / sections / descriptionPrevious value: -"Which blocks to load. Defaults to basic."New value: +"The blocks read beside the record's own fields, which come back whatever is written here. Each name adds a block, and 'basic' asks for those fields alone." - added
Input schema / properties / sections / maxItemsAdded value: +3 - added
Input schema / properties / sections / minItemsAdded value: +1 - added
Input schema / properties / sourcesAdded value: +{ + "items": { + "enum": [ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" + ], + "type": "string" + }, + "maxItems": 5, + "minItems": 1, + "type": "array" +} - removed
Output schema / descriptionRemoved value: -"A scene when 'status' is 'established'. When it is 'deleted' or 'merged' this is a marker: it carries the identifier, the catalogue, the successor and the former title, and no field describing a scene." - added
Output schema / properties / cachedAdded value: +{ + "type": "boolean" +} - added
Output schema / properties / cardAdded value: +{ + "additionalProperties": false, + "properties": { + "counts": { + "additionalProperties": { + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "state": { + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "value": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "source", + "value", + "state" + ], + "type": "object" + }, + "type": "array" + }, + "description": "Counts, one entry per catalogue asked and never added together. A null is read with the state beside it: a catalogue that publishes no such count, one that could not answer, and one nobody asked are three different facts.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "fields": { + "additionalProperties": { + "anyOf": [ + { + "additionalProperties": false, + "properties": { + "agreed_by": { + "description": "The catalogues that published this reading. Two agreeing is evidence of its own.", + "items": { + "type": "string" + }, + "type": "array" + }, + "disagreed": { + "description": "The readings nobody preferred, published rather than dropped: choosing between them is a policy, and a policy applied in silence is a claim nobody can check.", + "items": { + "additionalProperties": false, + "properties": { + "source": { + "type": "string" + }, + "value": {} + }, + "required": [ + "source", + "value" + ], + "type": "object" + }, + "type": "array" + }, + "value": { + "description": "The reading the preference named, null where none published one." + } + }, + "required": [ + "value", + "agreed_by" + ], + "type": "object" + }, + { + "items": { + "additionalProperties": false, + "properties": { + "also_at": { + "description": "What this same record is called on another catalogue, from a link joining the two.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "published_by": { + "description": "Every catalogue that published this very record. A shared name is no join, so an entry one catalogue minted names that one.", + "items": { + "type": "string" + }, + "type": "array" + }, + "same_name_as": { + "description": "A record another catalogue published under a matching name. A resemblance, never a join.", + "items": { + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "source": { + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, + "type": "array" + }, + "value": {} + }, + "required": [ + "value", + "published_by" + ], + "type": "object" + }, + "type": "array" + } + ] + }, + "description": "Each field of the record. A scalar carries the catalogues that said it; a list is the union, each entry naming who published it.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "held_by": { + "description": "Every catalogue asked, with the identifier the record carries there.", + "items": { + "additionalProperties": false, + "properties": { + "error": { + "type": "string" + }, + "id": { + "type": "string" + }, + "reason": { + "type": "string" + }, + "retrieved_at": { + "description": "The moment this client read it there.", + "type": "string" + }, + "source": { + "type": "string" + }, + "source_url": { + "description": "The address it was read at, which credits it.", + "type": "string" + }, + "state": { + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "status": { + "description": "What the identifier addresses there now. A folded record is held elsewhere.", + "enum": [ + "established", + "merged", + "deleted" + ], + "type": "string" + } + }, + "required": [ + "source", + "state" + ], + "type": "object" + }, + "type": "array" + }, + "kind": { + "type": "string" + }, + "notes": { + "description": "What this answer does not establish, which is what makes it safe to act on.", + "items": { + "type": "string" + }, + "type": "array" + }, + "preferred": { + "description": "The order the readings were preferred in. This is the policy that was applied, whether or not every catalogue in it answered.", + "items": { + "type": "string" + }, + "type": "array" + }, + "read_from": { + "description": "The catalogues that answered, in the order they were preferred. A policy and an outcome are two facts, and a reader deciding whether to change the policy needs both.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "required": [ + "kind", + "fields", + "counts", + "held_by", + "preferred", + "read_from", + "notes" + ], + "type": "object" +} - removed
Output schema / properties / codeRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "The studio's own reference, in whatever form it publishes." -} - removed
Output schema / properties / createdRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / detailsRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / directorRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Free text, which can name several people." -} - removed
Output schema / properties / duration_secondsRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / fingerprint_countRemoved value: -{ - "additionalProperties": { - "type": "number" - }, - "propertyNames": { - "type": "string" - }, - "type": "object" -} - removed
Output schema / properties / fingerprintsRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "algorithm": { - "enum": [ - "MD5", - "OSHASH", - "PHASH" - ], - "type": "string" - }, - "contested": { - "anyOf": [ - { - "type": "boolean" - }, - { - "type": "null" - } - ], - "description": "Whether the reports reach the submissions. Null where the catalogue publishes no report count: a fingerprint nobody has disputed and one on a catalogue that counts no disputes are different things." - }, - "duration_seconds": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "hash": { - "type": "string" - }, - "reports": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "How many people reported against it. Null on a catalogue that records no reports, which is an unknown contest and never an absence of one." - }, - "submissions": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "How many people entered this fingerprint. Null where the catalogue counts none." - } - }, - "required": [ - "algorithm", - "hash", - "duration_seconds", - "submissions", - "reports", - "contested" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / fingerprints_heldRemoved value: -{ - "description": "How many the record holds, where the section shows a page of them.", - "type": "number" -} - removed
Output schema / properties / former_titleRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Present on a marker: the title the record carried." -} - removed
Output schema / properties / idRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / imagesRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "height": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - }, - "url": { - "description": "The address of the image. The image itself is never returned.", - "type": "string" - }, - "width": { - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "url", - "width", - "height" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / merged_intoRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "Present on a marker: the identifier that continues this record." -} - removed
Output schema / properties / notesRemoved value: -{ - "items": { - "type": "string" - }, - "type": "array" -} - added
Output schema / properties / per_sourceAdded value: +{ + "items": { + "additionalProperties": false, + "properties": { + "algorithms_not_searched": { + "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", + "items": { + "type": "string" + }, + "type": "array" + }, + "count": { + "description": "Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes.", + "type": "number" + }, + "error": { + "enum": [ + "not_found", + "invalid_input", + "rate_limited", + "parse_failure", + "network_error", + "timeout" + ], + "type": "string" + }, + "index_total": { + "description": "What its own index holds for this question, the rows on this page included.", + "type": "number" + }, + "index_total_over_any_word": { + "description": "The total counts rows carrying any word of the query, its text index reading them apart.", + "type": "boolean" + }, + "moment": { + "description": "Which moment failed.", + "type": "string" + }, + "name": { + "description": "The name that catalogue calls itself.", + "type": "string" + }, + "narrowings_naming_no_record": { + "description": "Narrowings written only with identifiers another catalogue minted.", + "items": { + "type": "string" + }, + "type": "array" + }, + "narrowings_not_received": { + "description": "Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something.", + "items": { + "type": "string" + }, + "type": "array" + }, + "narrowings_received_in_part": { + "items": { + "type": "string" + }, + "type": "array" + }, + "reason": { + "description": "Why it was not asked, or what went wrong.", + "type": "string" + }, + "records": { + "description": "Distinct records behind its rows.", + "type": "number" + }, + "skipped": { + "description": "Rows it answered with that this client could not read and left out.", + "type": "number" + }, + "source": { + "description": "The catalogue this row is about.", + "type": "string" + }, + "state": { + "description": "'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world.", + "enum": [ + "answered", + "failed", + "absent" + ], + "type": "string" + }, + "unattributed": { + "description": "Records it answered with while carrying none of what was asked about.", + "type": "number" + } + }, + "required": [ + "source", + "state" + ], + "type": "object" + }, + "type": "array" +} - removed
Output schema / properties / performersRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "credited_as": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "The name printed on this release, where it differs from the performer's own. It travels beside that name and never in place of it." - }, - "disambiguation": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "required": [ - "id", - "name", - "credited_as", - "disambiguation" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / production_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ], - "description": "When the scene was made, which is a different question from when it was published. Rarely recorded, and never filled in from the release date." -} - removed
Output schema / properties / release_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ], - "description": "When the scene was published." -} - removed
Output schema / properties / retrieved_atRemoved value: -{ - "description": "When this record came off the catalogue, ISO 8601.", - "type": "string" -} - removed
Output schema / properties / sourceRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / source_urlRemoved value: -{ - "type": "string" -} - removed
Output schema / properties / statusRemoved value: -{ - "description": "'established' is a record the catalogue holds. 'merged' and 'deleted' are markers: the identifier resolves, and what comes back describes the record rather than the thing it once named.", - "enum": [ - "established", - "deleted", - "merged" - ], - "type": "string" -} - removed
Output schema / properties / studioRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "parent": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - } - }, - "required": [ - "id", - "name", - "parent" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / tagsRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "category": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] - }, - "id": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "required": [ - "id", - "name", - "category" - ], - "type": "object" - }, - "type": "array" -} - removed
Output schema / properties / titleRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / updatedRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / urlsRemoved value: -{ - "items": { - "additionalProperties": false, - "properties": { - "site_category": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "description": "The catalogue's own category for that site. Null on a catalogue that publishes no table of sites, since borrowing a neighbour's would sort a link by a taxonomy this catalogue never applied." - }, - "site_name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "required": [ - "url", - "site_name", - "site_category" - ], - "type": "object" - }, - "type": "array" -} - changed
Output schema / requiredPrevious value: -[ - "id", - "source", - "source_url", - "retrieved_at", - "status", - "notes" -]New value: +[ + "card" +]
- Added
get_sources - Added
get_studio - Added
get_tag - Changed
search_performers61 fields changed- added
Input schema / properties / aliasAdded value: +{ + "minLength": 1, + "type": "string" +} - added
Input schema / properties / birth_yearAdded value: +{ + "maximum": 2200, + "minimum": 1800, + "type": "integer" +} - added
Input schema / properties / career_end_yearAdded value: +{ + "maximum": 2200, + "minimum": 1800, + "type": "integer" +} - added
Input schema / properties / career_start_yearAdded value: +{ + "maximum": 2200, + "minimum": 1800, + "type": "integer" +} - removed
Input schema / properties / country / descriptionRemoved value: -"Two-letter country code." - added
Input schema / properties / country / patternAdded value: +"^[A-Za-z]{2}$" - removed
Input schema / properties / disambiguation / descriptionRemoved value: -"Free text a catalogue uses to tell two people of one name apart." - added
Input schema / properties / disambiguation / minLengthAdded value: +1 - added
Input schema / properties / ethnicityAdded value: +{ + "enum": [ + "UNKNOWN", + "CAUCASIAN", + "BLACK", + "ASIAN", + "INDIAN", + "LATIN", + "MIDDLE_EASTERN", + "MIXED", + "OTHER" + ], + "type": "string" +} - added
Input schema / properties / genderAdded value: +{ + "enum": [ + "UNKNOWN", + "MALE", + "FEMALE", + "TRANSGENDER_MALE", + "TRANSGENDER_FEMALE", + "INTERSEX", + "NON_BINARY" + ], + "type": "string" +} - added
Input schema / properties / limit / descriptionAdded value: +"How many rows one page of one catalogue carries. An answer holding several catalogues carries up to this many from each of them, since their pages are their own and nothing here interleaves them into one." - added
Input schema / properties / name / minLengthAdded value: +1 - added
Input schema / properties / page / descriptionAdded value: +"The page of each catalogue's own order. A route that reads words alone takes no page, and the answer reports it as one that catalogue did not receive." - changed
Input schema / properties / page / maximumPrevious value: -10000New value: +1000 - removed
Input schema / properties / performed_with / descriptionRemoved value: -"Namespaced identifier of another performer." - added
Input schema / properties / performed_with / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - changed
Input schema / properties / query / descriptionPrevious value: -"Free text matched against names and aliases."New value: +"Words for the catalogue's own text index, which reads them as a union. It is exclusive with the typed arguments, which narrow as an intersection." - added
Input schema / properties / query / minLengthAdded value: +1 - changed
Input schema / properties / sort / enumPrevious value: -[ - "name", - "birthdate", - "scene_count", - "created", - "updated" -]New value: +[ + "name", + "birthdate", + "deathdate", + "scene_count", + "career_start_year", + "debut", + "last_scene", + "popularity", + "created_at", + "updated_at" +] - added
Input schema / properties / sources / items / enumAdded value: +[ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" +] - added
Input schema / properties / sources / maxItemsAdded value: +5 - added
Input schema / properties / sources / minItemsAdded value: +1 - removed
Input schema / properties / studio_id / descriptionRemoved value: -"Namespaced studio identifier." - added
Input schema / properties / studio_id / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - changed
Output schema / properties / cached / descriptionPrevious value: -"Present when the answer came from the in-memory store."New value: +"Replayed from this client's store." - changed
Output schema / properties / ordering / descriptionPrevious value: -"How the order was built. The catalogues publish no score in common, so rows interleave and nothing is ranked across them."New value: +"How the rows were laid out, which a reader needs before reading the first." - added
Output schema / properties / per_source / items / properties / algorithms_not_searchedAdded value: +{ + "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / properties / count / descriptionPrevious value: -"Rows this catalogue contributed to this page."New value: +"Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes." - added
Output schema / properties / per_source / items / properties / error / enumAdded value: +[ + "not_found", + "invalid_input", + "rate_limited", + "parse_failure", + "network_error", + "timeout" +] - removed
Output schema / properties / per_source / items / properties / fields_searchedRemoved value: -{ - "description": "The fields the text index read. Absent where no text index was consulted.", - "items": { - "type": "string" - }, - "type": "array" -} - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What this catalogue's index holds for the question, beyond the page returned."New value: +"What its own index holds for this question, the rows on this page included." - added
Output schema / properties / per_source / items / properties / index_total_over_any_wordAdded value: +{ + "description": "The total counts rows carrying any word of the query, its text index reading them apart.", + "type": "boolean" +} - changed
Output schema / properties / per_source / items / properties / moment / descriptionPrevious value: -"Which step failed, for a catalogue that failed."New value: +"Which moment failed." - added
Output schema / properties / per_source / items / properties / name / descriptionAdded value: +"The name that catalogue calls itself." - added
Output schema / properties / per_source / items / properties / narrowings_naming_no_recordAdded value: +{ + "description": "Narrowings written only with identifiers another catalogue minted.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue could not receive. A row of its satisfying one of them does so by chance."New value: +"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_partAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / per_source / items / properties / reason / descriptionAdded value: +"Why it was not asked, or what went wrong." - added
Output schema / properties / per_source / items / properties / recordsAdded value: +{ + "description": "Distinct records behind its rows.", + "type": "number" +} - added
Output schema / properties / per_source / items / properties / skipped / descriptionAdded value: +"Rows it answered with that this client could not read and left out." - added
Output schema / properties / per_source / items / properties / source / descriptionAdded value: +"The catalogue this row is about." - changed
Output schema / properties / per_source / items / properties / state / descriptionPrevious value: -"'answered' is a catalogue that looked, and a count of zero there means it found nothing. 'failed' could not answer. 'absent' was never asked. An answer holding rows from some catalogues is no evidence about the others."New value: +"'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world." - added
Output schema / properties / per_source / items / properties / unattributed / descriptionAdded value: +"Records it answered with while carrying none of what was asked about." - removed
Output schema / properties / queryRemoved value: -{ - "type": "string" -} - added
Output schema / properties / result_count / descriptionAdded value: +"How many rows this page carries." - changed
Output schema / properties / results / items / additionalPropertiesPrevious value: -falseNew value: +{} - removed
Output schema / properties / results / items / properties / aliasesRemoved value: -{ - "items": { - "type": "string" - }, - "type": "array" -} - removed
Output schema / properties / results / items / properties / birth_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / career_end_yearRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / career_start_yearRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / countryRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / disambiguationRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - added
Output schema / properties / results / items / properties / id / descriptionAdded value: +"Written instance:uuid. The catalogue travels with the uuid." - removed
Output schema / properties / results / items / properties / nameRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / scene_countRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ], - "description": "Scenes that catalogue has indexed crediting this performer." -} - added
Output schema / properties / results / items / properties / source_url / descriptionAdded value: +"The address it was read from. Credit the catalogue and link it." - removed
Output schema / properties / results / items / properties / status / descriptionRemoved value: -"'established' is a record the catalogue holds. 'merged' and 'deleted' are markers: the identifier resolves, and what comes back describes the record rather than the thing it once named." - changed
Output schema / properties / results / items / properties / status / enumPrevious value: -[ - "established", - "deleted", - "merged" -]New value: +[ + "established", + "merged", + "deleted" +] - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "id", - "source", - "name", - "disambiguation", - "aliases", - "country", - "birth_date", - "career_start_year", - "career_end_year", - "scene_count", - "status", - "source_url" -]New value: +[ + "id", + "source", + "source_url", + "status" +] - changed
Output schema / properties / window / descriptionPrevious value: -"The window this answer covers, per catalogue. An emptiness here is an emptiness inside that window."New value: +"The page a catalogue paged through, absent where none was given one." - changed
Output schema / requiredPrevious value: -[ - "results", - "result_count", - "ordering", - "per_source", - "notes" -]New value: +[ + "results", + "result_count", + "per_source", + "ordering", + "notes" +]
- Changed
search_scenes64 fields changed- added
Input schema / properties / aliasAdded value: +{ + "minLength": 1, + "type": "string" +} - removed
Input schema / properties / code / descriptionRemoved value: -"The studio's own reference for the scene." - added
Input schema / properties / code / minLengthAdded value: +1 - added
Input schema / properties / dateAdded value: +{ + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "type": "string" +} - added
Input schema / properties / date_compareAdded value: +{ + "description": "These catalogues compare a date against one bound and answer no range, so the comparison is written rather than assumed.", + "enum": [ + "on", + "before", + "after" + ], + "type": "string" +} - removed
Input schema / properties / date_fromRemoved value: -{ - "description": "Released strictly after this date, as YYYY-MM-DD. A catalogue takes one date comparison at a time, so giving both bounds sends this one and reports the other as not received.", - "type": "string" -} - removed
Input schema / properties / date_toRemoved value: -{ - "description": "Released strictly before this date, as YYYY-MM-DD.", - "type": "string" -} - added
Input schema / properties / limit / descriptionAdded value: +"How many rows one page of one catalogue carries. An answer holding several catalogues carries up to this many from each of them, since their pages are their own and nothing here interleaves them into one." - removed
Input schema / properties / match / descriptionRemoved value: -"How a list of identifiers reads. 'all' returns scenes carrying every one of them and is the default; 'any' returns scenes carrying at least one." - added
Input schema / properties / page / descriptionAdded value: +"The page of each catalogue's own order. A route that reads words alone takes no page, and the answer reports it as one that catalogue did not receive." - changed
Input schema / properties / page / maximumPrevious value: -10000New value: +1000 - added
Input schema / properties / parent_studio_idAdded value: +{ + "pattern": "^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + "type": "string" +} - removed
Input schema / properties / performer_ids / descriptionRemoved value: -"Namespaced performer identifiers." - added
Input schema / properties / performer_ids / items / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - added
Input schema / properties / performer_ids / maxItemsAdded value: +25 - added
Input schema / properties / performer_ids / minItemsAdded value: +1 - changed
Input schema / properties / query / descriptionPrevious value: -"Free text matched against a scene's own fields."New value: +"Words for the catalogue's own text index, which reads them as a union. It is exclusive with the typed arguments, which narrow as an intersection." - added
Input schema / properties / query / minLengthAdded value: +1 - changed
Input schema / properties / sort / enumPrevious value: -[ - "title", - "date", - "duration", - "created", - "updated" -]New value: +[ + "title", + "date", + "duration", + "trending", + "popularity", + "created_at", + "updated_at" +] - added
Input schema / properties / sources / items / enumAdded value: +[ + "stashdb", + "tpdb", + "fansdb", + "pmv", + "javstash" +] - added
Input schema / properties / sources / maxItemsAdded value: +5 - added
Input schema / properties / sources / minItemsAdded value: +1 - added
Input schema / properties / studio_ids / items / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - added
Input schema / properties / studio_ids / maxItemsAdded value: +25 - added
Input schema / properties / studio_ids / minItemsAdded value: +1 - added
Input schema / properties / tag_ids / items / patternAdded value: +"^(?:(?:stashdb|tpdb|fansdb|pmv|javstash):)?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - added
Input schema / properties / tag_ids / maxItemsAdded value: +25 - added
Input schema / properties / tag_ids / minItemsAdded value: +1 - added
Input schema / properties / title / minLengthAdded value: +1 - changed
Output schema / properties / cached / descriptionPrevious value: -"Present when the answer came from the in-memory store."New value: +"Replayed from this client's store." - changed
Output schema / properties / ordering / descriptionPrevious value: -"How the order was built. The catalogues publish no score in common, so rows interleave and nothing is ranked across them."New value: +"How the rows were laid out, which a reader needs before reading the first." - added
Output schema / properties / per_source / items / properties / algorithms_not_searchedAdded value: +{ + "description": "Fingerprint algorithms its lookup does not search, so they were never put to it.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / properties / count / descriptionPrevious value: -"Rows this catalogue contributed to this page."New value: +"Rows it contributed. Never added to another catalogue's: they index corpora that overlap by an amount none of them publishes." - added
Output schema / properties / per_source / items / properties / error / enumAdded value: +[ + "not_found", + "invalid_input", + "rate_limited", + "parse_failure", + "network_error", + "timeout" +] - removed
Output schema / properties / per_source / items / properties / fields_searchedRemoved value: -{ - "description": "The fields the text index read. Absent where no text index was consulted.", - "items": { - "type": "string" - }, - "type": "array" -} - changed
Output schema / properties / per_source / items / properties / index_total / descriptionPrevious value: -"What this catalogue's index holds for the question, beyond the page returned."New value: +"What its own index holds for this question, the rows on this page included." - added
Output schema / properties / per_source / items / properties / index_total_over_any_wordAdded value: +{ + "description": "The total counts rows carrying any word of the query, its text index reading them apart.", + "type": "boolean" +} - changed
Output schema / properties / per_source / items / properties / moment / descriptionPrevious value: -"Which step failed, for a catalogue that failed."New value: +"Which moment failed." - added
Output schema / properties / per_source / items / properties / name / descriptionAdded value: +"The name that catalogue calls itself." - added
Output schema / properties / per_source / items / properties / narrowings_naming_no_recordAdded value: +{ + "description": "Narrowings written only with identifiers another catalogue minted.", + "items": { + "type": "string" + }, + "type": "array" +} - changed
Output schema / properties / per_source / items / properties / narrowings_not_received / descriptionPrevious value: -"Narrowings this catalogue could not receive. A row of its satisfying one of them does so by chance."New value: +"Narrowings this catalogue cannot receive. This is the one field that says a catalogue cannot do something." - added
Output schema / properties / per_source / items / properties / narrowings_received_in_partAdded value: +{ + "items": { + "type": "string" + }, + "type": "array" +} - added
Output schema / properties / per_source / items / properties / reason / descriptionAdded value: +"Why it was not asked, or what went wrong." - added
Output schema / properties / per_source / items / properties / recordsAdded value: +{ + "description": "Distinct records behind its rows.", + "type": "number" +} - added
Output schema / properties / per_source / items / properties / skipped / descriptionAdded value: +"Rows it answered with that this client could not read and left out." - added
Output schema / properties / per_source / items / properties / source / descriptionAdded value: +"The catalogue this row is about." - changed
Output schema / properties / per_source / items / properties / state / descriptionPrevious value: -"'answered' is a catalogue that looked, and a count of zero there means it found nothing. 'failed' could not answer. 'absent' was never asked. An answer holding rows from some catalogues is no evidence about the others."New value: +"'answered' looked, and a count of zero means it found nothing. 'failed' could not answer, and states nothing about what it holds. 'absent' was never asked. Only the first is evidence about the world." - added
Output schema / properties / per_source / items / properties / unattributed / descriptionAdded value: +"Records it answered with while carrying none of what was asked about." - removed
Output schema / properties / queryRemoved value: -{ - "type": "string" -} - changed
Output schema / properties / result_count / descriptionPrevious value: -"Rows returned here. Counts are never added across catalogues."New value: +"How many rows this page carries." - changed
Output schema / properties / results / items / additionalPropertiesPrevious value: -falseNew value: +{} - removed
Output schema / properties / results / items / properties / duration_secondsRemoved value: -{ - "anyOf": [ - { - "type": "number" - }, - { - "type": "null" - } - ] -} - added
Output schema / properties / results / items / properties / id / descriptionAdded value: +"Written instance:uuid. The catalogue travels with the uuid." - removed
Output schema / properties / results / items / properties / performersRemoved value: -{ - "items": { - "type": "string" - }, - "type": "array" -} - removed
Output schema / properties / results / items / properties / release_dateRemoved value: -{ - "anyOf": [ - { - "additionalProperties": false, - "properties": { - "precision": { - "description": "How much of the date a cataloguer entered. A value recorded to the year carries no month and no day, and reading it as one would claim a precision nobody entered.", - "enum": [ - "day", - "month", - "year" - ], - "type": "string" - }, - "value": { - "description": "The date exactly as the catalogue publishes it.", - "type": "string" - } - }, - "required": [ - "value", - "precision" - ], - "type": "object" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / retrieved_atRemoved value: -{ - "description": "When this record came off the catalogue, ISO 8601.", - "type": "string" -} - added
Output schema / properties / results / items / properties / source_url / descriptionAdded value: +"The address it was read from. Credit the catalogue and link it." - removed
Output schema / properties / results / items / properties / status / descriptionRemoved value: -"'established' is a record the catalogue holds. 'merged' and 'deleted' are markers: the identifier resolves, and what comes back describes the record rather than the thing it once named." - changed
Output schema / properties / results / items / properties / status / enumPrevious value: -[ - "established", - "deleted", - "merged" -]New value: +[ + "established", + "merged", + "deleted" +] - removed
Output schema / properties / results / items / properties / studioRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - removed
Output schema / properties / results / items / properties / titleRemoved value: -{ - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ] -} - changed
Output schema / properties / results / items / requiredPrevious value: -[ - "id", - "source", - "title", - "release_date", - "duration_seconds", - "studio", - "performers", - "status", - "retrieved_at", - "source_url" -]New value: +[ + "id", + "source", + "source_url", + "status" +] - changed
Output schema / properties / window / descriptionPrevious value: -"The window this answer covers, per catalogue. An emptiness here is an emptiness inside that window."New value: +"The page a catalogue paged through, absent where none was given one." - changed
Output schema / requiredPrevious value: -[ - "results", - "result_count", - "ordering", - "per_source", - "notes" -]New value: +[ + "results", + "result_count", + "per_source", + "ordering", + "notes" +]
- Added
search_studios - Added
search_tags
4 tool updates
v0.2.1- Changed
find_by_fingerprint5 fields changed- added
Output schema / properties / match_count / descriptionAdded value: +"Matches returned: one per scene per fingerprint it carries." - added
Output schema / properties / per_source / items / properties / skippedAdded value: +{ + "type": "number" +} - added
Output schema / properties / per_source / items / properties / unattributedAdded value: +{ + "type": "number" +} - added
Output schema / properties / scenes_matchedAdded value: +{ + "description": "Distinct scenes behind those matches.", + "type": "number" +} - changed
Output schema / requiredPrevious value: -[ - "asked", - "matches", - "match_count", - "unattributed", - "per_source", - "notes" -]New value: +[ + "asked", + "matches", + "match_count", + "scenes_matched", + "unattributed", + "per_source", + "notes" +]
- Changed
get_performer1 field changed- added
Output schema / properties / studios_totalAdded value: +{ + "description": "How many the record credits, where the section shows a page of them.", + "type": "number" +}
- Changed
search_performers9 fields changed- removed
Input schema / properties / career_end_yearRemoved value: -{ - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" -} - removed
Input schema / properties / career_start_yearRemoved value: -{ - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" -} - changed
Input schema / properties / page / maximumPrevious value: -9007199254740991New value: +10000 - added
Output schema / properties / cachedAdded value: +{ + "description": "Present when the answer came from the in-memory store.", + "type": "boolean" +} - added
Output schema / properties / per_source / items / properties / skippedAdded value: +{ + "type": "number" +} - added
Output schema / properties / per_source / items / properties / unattributedAdded value: +{ + "type": "number" +} - removed
Output schema / properties / query / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / query / typeAdded value: +"string" - changed
Output schema / requiredPrevious value: -[ - "query", - "results", - "result_count", - "ordering", - "per_source", - "notes" -]New value: +[ + "results", + "result_count", + "ordering", + "per_source", + "notes" +]
- Changed
search_scenes10 fields changed- changed
Input schema / properties / date_from / descriptionPrevious value: -"Earliest release date, as YYYY-MM-DD."New value: +"Released strictly after this date, as YYYY-MM-DD. A catalogue takes one date comparison at a time, so giving both bounds sends this one and reports the other as not received." - changed
Input schema / properties / date_to / descriptionPrevious value: -"Latest release date, as YYYY-MM-DD."New value: +"Released strictly before this date, as YYYY-MM-DD." - added
Input schema / properties / matchAdded value: +{ + "description": "How a list of identifiers reads. 'all' returns scenes carrying every one of them and is the default; 'any' returns scenes carrying at least one.", + "enum": [ + "all", + "any" + ], + "type": "string" +} - changed
Input schema / properties / page / maximumPrevious value: -9007199254740991New value: +10000 - added
Output schema / properties / cachedAdded value: +{ + "description": "Present when the answer came from the in-memory store.", + "type": "boolean" +} - added
Output schema / properties / per_source / items / properties / skippedAdded value: +{ + "type": "number" +} - added
Output schema / properties / per_source / items / properties / unattributedAdded value: +{ + "type": "number" +} - removed
Output schema / properties / query / anyOfRemoved value: -[ - { - "type": "string" - }, - { - "type": "null" - } -] - added
Output schema / properties / query / typeAdded value: +"string" - changed
Output schema / requiredPrevious value: -[ - "query", - "results", - "result_count", - "ordering", - "per_source", - "notes" -]New value: +[ + "results", + "result_count", + "ordering", + "per_source", + "notes" +]
5 tool updates
v0.1.1- First observed
find_by_fingerprint - First observed
get_performer - First observed
get_scene - First observed
search_performers - First observed
search_scenes
TDQS
Scored across 10 tools
Each tool targets a distinct entity (scene, performer, studio, tag) and a distinct action (search vs get), plus find_by_fingerprint and get_sources serve unique purposes. No overlap between tools; clear separation prevents misselection.
All tool names follow a consistent verb_noun pattern: search_<entity> for searching, get_<entity> for retrieval, plus get_sources and find_by_fingerprint. The pattern is uniform across all tools.
10 tools is well-scoped for a read-only search and retrieval server covering four entity types, metadata sourcing, and fingerprint lookup. Each tool earns its place without bloat.
The server provides search and retrieval for all core entities (scene, performer, studio, tag), metadata via get_sources, and a fingerprint lookup. For a read-only catalogue interface, the surface is complete with no obvious gaps.
Maintenance
Related MCP Connectors
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
An MCP server that provides tools to discover and retrieve podcast episodes transcripts.
MCP server for Russian books search, details, and recommendation candidates.
Public MCP server for summaries, DNS lookup, catalog, replies, and JSON checks.
Related MCP Servers
- AlicenseBqualityDmaintenanceAn MCP server for obtaining the README content, metadata and search function of CPAN packages.31Apache 2.0
- AlicenseNot gradedqualityDmaintenanceA comprehensive MCP server for querying the MusicBrainz database, providing tools to search for artists, releases, recordings, and browse music metadata.4MIT
- AlicenseCqualityCmaintenanceAn MCP server for querying and analyzing a Stash instance with advanced filters, caching, and personalized recommendations.712Apache 2.0
- FlicenseNot gradedqualityCmaintenanceA remote MCP server for the Arxpot processing core, enabling music search, metadata retrieval, and download management with remote storage delivery.-