Back to Browse

Stashbox MCP Server

Developer ToolsLow Risk10.0Local
Free

Search scenes, performers, studios and tags across the public stash-box catalogues.

About

Search scenes, performers, studios and tags across the public stash-box catalogues.

Security Report

10.0
Low Risk10.0Low Risk

Valid MCP server (3 strong, 2 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry. Trust signals: trusted author (36/37 approved).

9 files analyzed · 1 issue found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Required

This plugin requests these system permissions. Most are normal for its category.

env_vars

Check that this permission is expected for this type of plugin.

What You'll Need

Set these up before or after installing:

API key for StashDB. A catalogue with no key is named as absent from every answer.Optional

Environment variable: STASHBOX_STASHDB_KEY

API key for ThePornDB. A catalogue with no key is named as absent from every answer.Optional

Environment variable: STASHBOX_TPDB_KEY

API key for FansDB. A catalogue with no key is named as absent from every answer.Optional

Environment variable: STASHBOX_FANSDB_KEY

API key for PMV Stash. A catalogue with no key is named as absent from every answer.Optional

Environment variable: STASHBOX_PMV_KEY

API key for JAVStash. A catalogue with no key is named as absent from every answer.Optional

Environment variable: STASHBOX_JAVSTASH_KEY

Identify your own client. This project's identifier and a contact address stay appended, so a catalogue can always reach a human.Optional

Environment variable: SB_USER_AGENT

Minimum milliseconds between requests to one catalogue. Default and floor 1000, ceiling 60000. The floor cannot be lowered.Optional

Environment variable: SB_MIN_INTERVAL_MS

Deadline for one request, in milliseconds. Default 20000.Optional

Environment variable: SB_TIMEOUT_MS

Retries on a busy catalogue. Default 3.Optional

Environment variable: SB_MAX_RETRIES

In-memory cache lifetime, in milliseconds. Default 300000. Set 0 to turn it off.Optional

Environment variable: SB_CACHE_TTL_MS

In-memory cache size, in entries. Default 500.Optional

Environment variable: SB_CACHE_MAX_ENTRIES

error, info or debug. Default error. Logs go to stderr.Optional

Environment variable: SB_LOG_LEVEL

How to Install

Add this to your MCP configuration file:

{
  "mcpServers": {
    "io-github-smeet666-mcp-stashbox": {
      "env": {
        "SB_LOG_LEVEL": "your-sb-log-level-here",
        "SB_TIMEOUT_MS": "your-sb-timeout-ms-here",
        "SB_USER_AGENT": "your-sb-user-agent-here",
        "SB_MAX_RETRIES": "your-sb-max-retries-here",
        "SB_CACHE_TTL_MS": "your-sb-cache-ttl-ms-here",
        "STASHBOX_PMV_KEY": "your-stashbox-pmv-key-here",
        "STASHBOX_TPDB_KEY": "your-stashbox-tpdb-key-here",
        "SB_MIN_INTERVAL_MS": "your-sb-min-interval-ms-here",
        "STASHBOX_FANSDB_KEY": "your-stashbox-fansdb-key-here",
        "SB_CACHE_MAX_ENTRIES": "your-sb-cache-max-entries-here",
        "STASHBOX_STASHDB_KEY": "your-stashbox-stashdb-key-here",
        "STASHBOX_JAVSTASH_KEY": "your-stashbox-javstash-key-here"
      },
      "args": [
        "-y",
        "mcp-stashbox"
      ],
      "command": "npx"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

mcp-stashbox

npm CI license MCP Registry M8ven LobeHub Install in Cursor Install in VS Code

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.

Version française


Install

One-click install

Install in Cursor Install in VS Code

Claude Code

claude mcp add stashbox --env STASHBOX_STASHDB_KEY=your-key -- npx -y mcp-stashbox

Claude 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.1.0"
      ]
    }
  }
}

-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.1.0.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.

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

CatalogueAddressKey
StashDBstashdb.orgSTASHBOX_STASHDB_KEY
TPDBtheporndb.netSTASHBOX_TPDB_KEY
FansDBfansdb.ccSTASHBOX_FANSDB_KEY
PMV Stashpmvstash.orgSTASHBOX_PMV_KEY
JAVStashjavstash.orgSTASHBOX_JAVSTASH_KEY

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

ToolWhat it does
get_sourcesStates what each catalogue answers, and which keys are held.
search_scenesSearches the scenes of every configured catalogue.
search_performersSearches the performers.
search_studiosSearches the studios.
search_tagsSearches the tags.
get_sceneReads one scene as a single card.
get_performerReads one performer as a single card.
get_studioReads one studio as a single card.
get_tagReads one tag as a single card.
find_by_fingerprintIdentifies 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.

ArgumentTypeRequiredWhat it does
querystringnoWords for each catalogue's own text index.
titlestringnoWords a title carries.
codestringnoThe studio's own reference for the release.
aliasstringnoAnother title the release is known by.
datea calendar daynoThe release date to compare against.
date_compareon, before or afternoHow that date is read.
performer_idslist of identifiersnoPerformers credited on it.
studio_idslist of identifiersnoStudios that released it.
parent_studio_idan identifiernoA studio the releasing studio sits under.
tag_idslist of identifiersnoTags it is filed under.
matchall or anynoHow a list of identifiers is read.
sorttitle, date, duration, trending, popularity, created_at or updated_atnoThe order the catalogue applies.
directionasc or descnoWhich way that order runs.
pageinteger, 1 to 1000noWhich page of each catalogue's own order.
limitinteger, 1 to 100noRows one page of one catalogue carries.
sourceslist of cataloguesnoRead 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.

ArgumentTypeRequiredWhat it does
querystringnoWords for each catalogue's own text index.
namestringnoWords a name carries.
aliasstringnoAnother name they are known by.
disambiguationstringnoWhat the catalogue adds to tell two apart.
genderone of the values the catalogue recordsnoThe gender the catalogue records.
countrya two-letter country codenoThe country the catalogue records.
ethnicityone of the values the catalogue recordsnoThe ethnicity the catalogue records.
birth_yearinteger, 1800 to 2200noThe year of birth.
career_start_yearinteger, 1800 to 2200noThe year a career opened.
career_end_yearinteger, 1800 to 2200noThe year a career closed.
performed_withan identifiernoSomeone they are credited alongside.
studio_idan identifiernoA studio they are credited on.
sortname, birthdate, deathdate, scene_count, career_start_year, debut, last_scene, popularity, created_at or updated_atnoThe order the catalogue applies.
directionasc or descnoWhich way that order runs.
pageinteger, 1 to 1000noWhich page.
limitinteger, 1 to 100noRows one page of one catalogue carries.
sourceslist of cataloguesnoRead 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.

ArgumentTypeRequiredWhat it does
querystringnoWords for each catalogue's own text index.
namestringnoWords a name carries.
parent_idan identifiernoA studio it sits under.
has_parentbooleannoWhether it sits under another at all.
sortname, created_at or updated_atnoThe order the catalogue applies.
directionasc or descnoWhich way that order runs.
pageinteger, 1 to 1000noWhich page.
limitinteger, 1 to 100noRows one page of one catalogue carries.
sourceslist of cataloguesnoRead these catalogues alone.

In return: the rows and the per-catalogue accounting search_scenes returns.

search_tags

Searches the tags.

ArgumentTypeRequiredWhat it does
querystringnoWords for each catalogue's own text index.
namestringnoWords a name carries.
category_idan identifiernoA category the tag belongs to.
sortname, created_at or updated_atnoThe order the catalogue applies.
directionasc or descnoWhich way that order runs.
pageinteger, 1 to 1000noWhich page.
limitinteger, 1 to 100noRows one page of one catalogue carries.
sourceslist of cataloguesnoRead these catalogues alone.

In return: the rows and the per-catalogue accounting search_scenes returns.

get_scene

Reads one scene as a single card.

ArgumentTypeRequiredWhat it does
idan identifier written instance:uuidyesThe record to read.
sectionsany of basic, fingerprints, imagesnoThe blocks read beside the card.
sourceslist of cataloguesnoRead these catalogues alone.
preferlist of cataloguesnoThe 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.

ArgumentTypeRequiredWhat it does
idan identifier written instance:uuidyesThe record to read.
sectionsany of basic, appearance, images, studiosnoThe blocks read beside the card.
sourceslist of cataloguesnoRead these catalogues alone.
preferlist of cataloguesnoThe 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.

ArgumentTypeRequiredWhat it does
idan identifier written instance:uuidyesThe record to read.
sourceslist of cataloguesnoRead these catalogues alone.
preferlist of cataloguesnoThe order preferred where they disagree.

In return: the card get_scene returns, for a studio.

get_tag

Reads one tag as a single card.

ArgumentTypeRequiredWhat it does
idan identifier written instance:uuidyesThe record to read.
sourceslist of cataloguesnoRead these catalogues alone.
preferlist of cataloguesnoThe 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.

ArgumentTypeRequiredWhat it does
fingerprintsa list of { hash, algorithm }, the algorithm MD5, OSHASH or PHASHyesThe hashes to look up.
sectionsany of basic, fingerprints, imagesnoThe 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.
sourceslist of cataloguesnoRead these catalogues alone.
preferlist of cataloguesnoThe 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.

VariableDefaultWhat it does
STASHBOX_STASHDB_KEYnoneThe key StashDB issues to your account.
STASHBOX_TPDB_KEYnoneThe key TPDB issues to your account.
STASHBOX_FANSDB_KEYnoneThe key FansDB issues to your account.
STASHBOX_PMV_KEYnoneThe key PMV Stash issues to your account.
STASHBOX_JAVSTASH_KEYnoneThe key JAVStash issues to your account.
SB_USER_AGENTthe project identityNames your application to the catalogues, with an address where a person can be reached.
SB_MIN_INTERVAL_MS1000Gap between two requests, from 1000 to 60000.
SB_TIMEOUT_MS20000Deadline for one request, from 1 to 600000.
SB_MAX_RETRIES3Attempts after a transient failure, from 0 to 10.
SB_CACHE_TTL_MS300000How long an answer stays in memory, from 0 to 86400000.
SB_CACHE_MAX_ENTRIES500Answers held in memory at once, from 1 to 100000.
SB_LOG_LEVELerrorsilent, error, info or debug, written to stderr.

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.

CodeWhat happenedWhat to do
not_foundA catalogue answered, and holds no such record.Check the identifier with a search.
invalid_inputThe arguments were refused before any request went out.Read the message, which names the argument.
rate_limitedA catalogue asked this client to slow down.Wait, then call again with the same arguments. The record is still there.
parse_failureAn answer arrived in a shape this client cannot read.Report it at the issue tracker.
network_errorThe request did not complete.Try again shortly.
timeoutThe request passed its deadline.Raise SB_TIMEOUT_MS, or ask for fewer rows.

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 check

Tests 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)

English version

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

Install in Cursor Install in VS Code

Claude Code

claude mcp add stashbox --env STASHBOX_STASHDB_KEY=votre-cle -- npx -y mcp-stashbox

Claude 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.1.0"
      ]
    }
  }
}

-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.1.0.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

CatalogueAdresseClé
StashDBstashdb.orgSTASHBOX_STASHDB_KEY
TPDBtheporndb.netSTASHBOX_TPDB_KEY
FansDBfansdb.ccSTASHBOX_FANSDB_KEY
PMV Stashpmvstash.orgSTASHBOX_PMV_KEY
JAVStashjavstash.orgSTASHBOX_JAVSTASH_KEY

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

OutilCe qu'il fait
get_sourcesDit ce que chaque catalogue répond, et quelles clés sont posées.
search_scenesCherche les scènes de chaque catalogue configuré.
search_performersCherche les interprètes.
search_studiosCherche les studios.
search_tagsCherche les étiquettes.
get_sceneLit une scène sous forme d'une carte unique.
get_performerLit un interprète sous forme d'une carte unique.
get_studioLit un studio sous forme d'une carte unique.
get_tagLit une étiquette sous forme d'une carte unique.
find_by_fingerprintIdentifie 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.

ArgumentTypeRequisCe qu'il fait
querychaînenonDes mots pour l'index textuel de chaque catalogue.
titlechaînenonDes mots que porte un titre.
codechaînenonLa référence propre du studio pour la publication.
aliaschaînenonUn autre titre sous lequel elle est connue.
dateun jour de calendriernonLa date de publication à comparer.
date_compareon, before ou afternonComment cette date est lue.
performer_idsliste d'identifiantsnonLes interprètes qui y sont crédités.
studio_idsliste d'identifiantsnonLes studios qui l'ont publiée.
parent_studio_idun identifiantnonUn studio sous lequel le studio éditeur se range.
tag_idsliste d'identifiantsnonLes étiquettes sous lesquelles elle est rangée.
matchall ou anynonComment une liste d'identifiants est lue.
sorttitle, date, duration, trending, popularity, created_at ou updated_atnonL'ordre qu'applique le catalogue.
directionasc ou descnonLe sens de cet ordre.
pageentier, 1 à 1000nonQuelle page de l'ordre propre à chaque catalogue.
limitentier, 1 à 100nonLignes que porte une page d'un catalogue.
sourcesliste de cataloguesnonNe 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.

Documentation truncated — see the full README on GitHub.

Reviews

No reviews yet

Be the first to review this server!