{
  "markdown": "<img src=\"assets/icon-128.png\" alt=\"\" width=\"96\" align=\"right\">\n\n# mcp-imslp\n\n[![npm](https://img.shields.io/npm/v/mcp-imslp.svg)](https://www.npmjs.com/package/mcp-imslp)\n[![CI](https://github.com/smeet666/mcp-imslp/actions/workflows/ci.yml/badge.svg)](https://github.com/smeet666/mcp-imslp/actions/workflows/ci.yml)\n[![license](https://img.shields.io/npm/l/mcp-imslp.svg)](./LICENSE)\n[![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-6E56CF)](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.smeet666/mcp-imslp)\n[![Glama](https://glama.ai/mcp/servers/smeet666/mcp-imslp/badges/score.svg)](https://glama.ai/mcp/servers/smeet666/mcp-imslp)\n[![M8ven](https://m8ven.ai/badge/mcp/smeet666-mcp-imslp-7cehun?variant=verified)](https://m8ven.ai/mcp/smeet666-mcp-imslp-7cehun)\n[![LobeHub](https://lobehub.com/badge/mcp/smeet666-mcp-imslp)](https://lobehub.com/mcp/smeet666-mcp-imslp)\n[![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=imslp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm1jcC1pbXNscCJdfQ%3D%3D)\n[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=imslp&config=%7B%22name%22%3A%22imslp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mcp-imslp%22%5D%7D)\n\n[IMSLP](https://imslp.org), the International Music Score Library Project, is\nalso called the Petrucci Music Library. It is a free library of classical music\nrun by volunteers, holding the sheet music, the parts, the arrangements and the\nrecordings of works whose copyright has lapsed, together with what its pages say\nabout each composer. It reviews the copyright of every score for Canada, the\nUnited States and the European Union separately, and publishes its pages under\n[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).\n\nThis server connects a chat client to that library. You can search the works and\nthe people it catalogues, read a work with its opus number, its key, its\ninstrumentation and the year it was written, page through the editions a work\nholds with their publishers, editors and copyright terms, read what the library\nsays about a composer, and browse a genre, a key or an instrumentation. It reads\nthe library and links to it, and it needs no API key and no account.\n\n_[Version française](#mcp-imslp-français)_\n\n---\n\n## Install\n\n**One-click install**\n\n[![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=imslp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm1jcC1pbXNscCJdfQ%3D%3D)\n[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=imslp&config=%7B%22name%22%3A%22imslp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mcp-imslp%22%5D%7D)\n\n**Claude Code**\n\n```bash\nclaude mcp add imslp -- npx -y mcp-imslp\n```\n\n**Claude Desktop, Cursor, and any client using the standard config format**\n\n```json\n{\n  \"mcpServers\": {\n    \"imslp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-imslp\"]\n    }\n  }\n}\n```\n\nNode 24 or later is required, and no environment variable has to be set.\n\n### With Docker\n\n```json\n{\n  \"mcpServers\": {\n    \"imslp\": {\n      \"command\": \"docker\",\n      \"args\": [\"run\", \"-i\", \"--rm\", \"ghcr.io/smeet666/mcp-imslp:1.0.1\"]\n    }\n  }\n}\n```\n\n`-i` keeps stdin open, which is where the protocol travels, and `-t` is left out\nbecause a TTY rewrites the stream. The container needs outbound HTTPS to\n`imslp.org`, and nothing else: no volume, no port, no credential.\n\n### Bundle, without npm\n\nDownload `mcp-imslp-1.0.1.mcpb` from\n[the latest release](https://github.com/smeet666/mcp-imslp/releases/latest) and\nopen it. A client that supports MCP bundles installs it on its own, with no npm\nand no configuration file to edit. The bundle carries its dependencies, so\nnothing is fetched at install time.\n\n## What you can ask\n\n- \"What does IMSLP hold of Chopin's nocturnes?\"\n- \"Read me the page of Erik Satie and tell me when he lived.\"\n- \"List the editions of Debussy's Clair de lune, with who published each one.\"\n- \"Is the Henle edition of that piece free to use in the United States?\"\n- \"Show me works for solo cello in the library.\"\n\nThe ordinary path runs from a search to a work: `search_works` names the page of\na work, and `get_work` reads that page. The same holds for a person, from\n`search_people` to `get_person` or `list_person_works`.\n\n## Tools\n\n| Tool                | What it does                                                                 |\n| ------------------- | ---------------------------------------------------------------------------- |\n| `search_works`      | Finds the page of a work by title, composer or words on the page.            |\n| `search_people`     | Finds the category a composer, editor, arranger or performer is filed under. |\n| `get_work`          | Reads one work: its facets, its sections and its copyright terms.            |\n| `list_work_files`   | Pages through the editions a work holds, with their files.                   |\n| `list_person_works` | Reads the works the library files under one person.                          |\n| `get_person`        | Reads what the library holds about one person.                               |\n| `browse_category`   | Reads the works filed under a genre, a key or an instrumentation.            |\n\nA work is addressed by the title of its page, written `Work (Composer)`, as in\n`Nocturnes, Op.9 (Chopin, Frédéric)`. A person is addressed by a category,\nwritten `Category:Surname, Forename`. Both come back from a search, and the\n`Category:` prefix may be left out.\n\nThe library titles a work in the language its composer used, so `Die Zauberflöte`\nfinds the opera where `The Magic Flute` finds the pages written about it. A thin\nanswer for a famous work is a sign the title is in another language.\n\n### `search_works`\n\nSearches the pages of the works for words appearing anywhere on them, so a title,\na composer or a dedication all find the works carrying them.\n\n| Argument | Type                            | Required | What it does                                             |\n| -------- | ------------------------------- | -------- | -------------------------------------------------------- |\n| `query`  | string, 1 to 300 characters     | yes      | What to look for across the pages of the works.          |\n| `limit`  | integer, 1 to 50, default `10`  | no       | Rows to serve.                                           |\n| `offset` | integer, 0 or more, default `0` | no       | Rows to skip, using the `next_offset` of a prior answer. |\n\n**In return:** rows carrying `page`, which `get_work` takes; `work` and\n`composer`, read off that title; `page_url`; `snippet`, the words around the\nmatch; `size_bytes`, `words` and `last_edited` as the library states them. The\nenvelope carries `returned`, `has_more` and `next_offset`, which is the offset to\nread on from. `total` is always `null`: the library publishes no count of what a\nsearch matched. `composer` is `null` on a title written outside the\n`Work (Composer)` form, and `snippet` is `null` on a row the search summarised\nwith nothing.\n\n### `search_people`\n\nFinds the composers, editors, arrangers and performers by name. The library\nwrites a name its own way, surname first, so searching finds a person where\nguessing at the spelling reaches nothing.\n\n| Argument | Type                            | Required | What it does                                             |\n| -------- | ------------------------------- | -------- | -------------------------------------------------------- |\n| `query`  | string, 1 to 300 characters     | yes      | The name to look for among the people of the library.    |\n| `limit`  | integer, 1 to 50, default `10`  | no       | Rows to serve.                                           |\n| `offset` | integer, 0 or more, default `0` | no       | Rows to skip, using the `next_offset` of a prior answer. |\n\n**In return:** rows carrying `category`, which `get_person` and\n`list_person_works` take; `name` without the prefix; `page_url`; `snippet`; and\n`redirect_to`. A row with a `redirect_to` stands for another category and holds\nno works of its own, so follow the category it names. The envelope is the one\n`search_works` returns, and `total` is `null` here for the same reason.\n\n### `get_work`\n\nReads one work: its title and alternative titles, the composer, the opus and\ncatalogue numbers, the year of composition and of first publication, the\ndedication, the key, the language, the librettist, the instrumentation, the\nmovements, the first performance, the style and the period.\n\n| Argument | Type                        | Required   | What it does                                                |\n| -------- | --------------------------- | ---------- | ----------------------------------------------------------- |\n| `page`   | string, 1 to 300 characters | one of two | The page title, written `Work (Composer)`.                  |\n| `pageid` | integer, positive           | one of two | The page id a search returned, as an alternative to `page`. |\n\n**In return:** every facet above, each `null` when the page leaves it empty, and\neach in the wording the page used, so `ca.1830` stays `ca.1830`. Beside them come\n`genre_categories`, which `browse_category` takes; `external_links` and\n`authorities`, the records of the work at VIAF, LCCN, WorldCat, BNF and GND;\n`sections`, with the number of entries the site counts in each; and\n`copyright_summary`, one entry per distinct statement, with the number of\neditions carrying it. `editions` holds every edition with its files, and turns\n`null` with `editions_truncated` true when the work holds more than five, which\n`list_work_files` then pages through. `redirected_from` names the title asked\nfor when it led here, and `pageid` is `null` for a work addressed by title.\n\n### `list_work_files`\n\nReads the scores and the recordings of a work, edition by edition. An edition is\na set of files published under one set of terms: the publisher, the editor and\nthe copyright statement belong to the edition, and the files sit under it. A\nblock of recordings carries performers and no copyright statement.\n\n| Argument  | Type                            | Required   | What it does                                                                                                                                     |\n| --------- | ------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `page`    | string, 1 to 300 characters     | one of two | The page title, written `Work (Composer)`.                                                                                                       |\n| `pageid`  | integer, positive               | one of two | The page id a search returned, as an alternative to `page`.                                                                                      |\n| `section` | string, 1 to 80 characters      | no         | One section of the page, in its own wording: `Scores`, `Parts`, `Recordings`, `Arrangements and Transcriptions`. Matched without regard to case. |\n| `limit`   | integer, 1 to 100, default `10` | no         | Editions to serve.                                                                                                                               |\n| `offset`  | integer, 0 or more, default `0` | no         | Editions to skip.                                                                                                                                |\n\n**In return:** `editions`, each with its `section`, `publisher_info`, `editor`,\n`copyright` and `files`. A file carries `imslp_id`, `description`, `format` and\n`format_code`, `pages`, `size_bytes`, `downloads`, `rating`, `uploader`,\n`uploaded_on`, the sigla and name of the library that scanned it, and `blocked`,\nwhich is true while IMSLP reviews the copyright of that file. `downloads` is\n`null` on an entry printing no counter, and `rating` is `null` when nobody has\nvoted. Alongside come `editions_on_page`, `editions_in_section`, `returned`,\n`has_more` and `sections`. A `section` matching nothing comes back with the\nsections the page does hold, so a restriction never reads as a work without\nscores.\n\n### `list_person_works`\n\nReads the works the library files under one person: what a composer wrote, and\nalso what an editor, an arranger or a performer is credited on.\n\n| Argument   | Type                            | Required | What it does                                                    |\n| ---------- | ------------------------------- | -------- | --------------------------------------------------------------- |\n| `category` | string, 1 to 300 characters     | yes      | The person's category, written `Category:Surname, Forename`.    |\n| `limit`    | integer, 1 to 100, default `25` | no       | Rows to serve.                                                  |\n| `cursor`   | string, 1 to 500 characters     | no       | The `cursor` a prior answer named, passed back as it was given. |\n\n**In return:** rows carrying `page`, `work`, `composer`, `pageid` and `page_url`,\nwith `has_more` and `cursor` to read on. `total` is always `null`: the library\npublishes no count of what a category holds. A category the library does not hold\nanswers the way an empty one does, so an answer with no rows is a reason to check\nthe spelling with `search_people`.\n\n### `get_person`\n\nReads what the library holds about one person: the name as its page prints it,\nthe life dates it states, the other names it files them under, the registers\nholding a record of them, and the addresses it points to off the site.\n\n| Argument   | Type                        | Required | What it does                                                 |\n| ---------- | --------------------------- | -------- | ------------------------------------------------------------ |\n| `category` | string, 1 to 300 characters | yes      | The person's category, written `Category:Surname, Forename`. |\n\n**In return:** `category`, `catalogued_as` with the surname first, `name` as the\npage prints it, `life_dates` in the wording the page used, `alternative_names`\nand `aliases` as published lines, `authorities` with the register and the\nidentifier at VIAF, LCCN, WorldCat, BNF and GND, `external_links`, and\n`page_url`. `life_dates` is `null` on a page stating none. This reads the person;\n`list_person_works` reads the works.\n\n### `browse_category`\n\nReads the works filed under one category: a genre, a key, an instrumentation or a\nperiod. `get_work` hands these names back for a work under `genre_categories`,\nand passing one of those reaches a category the library holds.\n\n| Argument   | Type                            | Required | What it does                                                                              |\n| ---------- | ------------------------------- | -------- | ----------------------------------------------------------------------------------------- |\n| `category` | string, 1 to 300 characters     | yes      | The category to read, in the library's wording: `For piano`, `Nocturnes`, `B-flat minor`. |\n| `limit`    | integer, 1 to 100, default `25` | no       | Rows to serve.                                                                            |\n| `cursor`   | string, 1 to 500 characters     | no       | The `cursor` a prior answer named, passed back as it was given.                           |\n\n**In return:** the rows `list_person_works` returns, with the same `has_more` and\n`cursor`, and `total` at `null`. The library reads one category at a time, so a\nquestion naming both a genre and an instrument is answered by browsing one of\nthem and reading the other off each work with `get_work`.\n\n## Copyright status\n\nA score on IMSLP carries a status per jurisdiction, and the library reviews\nCanada, the United States and the European Union. A file reading\n`Public Domain - Non-PD US` is free in Canada and the European Union and\nprotected in the United States. This server reports the status as published, per\njurisdiction, under `copyright_summary` on a work and under `copyright` on an\nedition, with `restrictions` naming the places a statement excludes. An empty\n`restrictions` says nothing about the countries IMSLP leaves out of its review.\n\n## Configuration\n\nEvery variable is optional. Set them in the `env` block of your client config.\n\n| Variable                  | Default              | What it does                                                                                         |\n| ------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------- |\n| `IMSLP_USER_AGENT`        | the project identity | Names your application. The project identity is appended so IMSLP can reach a person.                |\n| `IMSLP_MIN_INTERVAL_MS`   | `2500`               | Gap between two requests, from 2000 to 60000. A figure under the floor is refused and this one used. |\n| `IMSLP_TIMEOUT_MS`        | `30000`              | Deadline for one request, from 1000 to 120000.                                                       |\n| `IMSLP_MAX_RETRIES`       | `3`                  | Attempts after a transient failure, from 0 to 10.                                                    |\n| `IMSLP_CACHE_TTL_MS`      | `900000`             | How long a page stays in memory, from 0 to 86400000.                                                 |\n| `IMSLP_CACHE_MAX_ENTRIES` | `100`                | Pages held in memory at once, from 0 to 10000.                                                       |\n| `IMSLP_LOG_LEVEL`         | `error`              | `silent`, `error`, `info` or `debug`, written to stderr.                                             |\n\nA value outside its range falls back to the default, and the reason is written to\nstderr.\n\n## Errors\n\nEvery failure carries one of six codes, a message, and where it helps a hint\nnaming the next move.\n\n| Code            | What happened                                           | What to do                                                                                                          |\n| --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| `not_found`     | IMSLP answered, and the page asked for is absent.       | Check the title with `search_works`.                                                                                |\n| `invalid_input` | The arguments were refused before any request went out. | Read the message, which names the argument.                                                                         |\n| `rate_limited`  | IMSLP asked this client to slow down.                   | Wait the number of seconds the hint names and call again with the same arguments. The work is still in the library. |\n| `parse_failure` | The page loaded and the expected content was absent.    | Report it at [the issue tracker](https://github.com/smeet666/mcp-imslp/issues).                                     |\n| `network_error` | The request did not complete.                           | Try again shortly.                                                                                                  |\n| `timeout`       | The request passed its deadline.                        | Raise `IMSLP_TIMEOUT_MS`, or ask for fewer rows.                                                                    |\n\n## As a library\n\nThe layer reading IMSLP is published on its own, with its pacing, its cache and\nits errors, and with no protocol attached.\n\n```ts\nimport { ImslpClient } from \"mcp-imslp/client\";\n\nconst client = new ImslpClient();\nconst { data, cached } = await client.getWork({ page: \"Nocturnes, Op.9 (Chopin, Frédéric)\" });\nconsole.log(data.title, data.copyright_summary, cached);\n```\n\n`renderPage`, `getWork`, `search`, `categoryMembers` and `getPerson` each answer\n`{ data, cached }`, and throw an `ImslpError` carrying one of the six codes. The\ntwo-second floor between requests holds here as well.\n\n## Pacing and attribution\n\nIMSLP publishes `Crawl-delay: 2` in its robots.txt, so requests go out one at a\ntime with at least two seconds between them, and that floor holds however the\nserver is configured. The `User-Agent` always ends with the project identity and\nan address where a person can be reached.\n\nReads go through the MediaWiki API at `/api.php` and through the listing endpoint\nIMSLP documents on its own `IMSLP:API` page. The robots.txt disallows\n`/index.php`, `/images/`, `/imglnks/`, `/wiki/File:`, `/works` and `/library/`,\nand this server builds no address under any of them: it hands back the link to\nthe work page, which is what an answer credits.\n\nThe library publishes its pages under\n[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/), so anything\nshown from this server credits IMSLP and links the page it came from.\n\n## Privacy\n\nThis server collects nothing about you and sends nothing to its author. It runs\non your machine, contacts `imslp.org` and nothing else, holds its answers in memory\nwhile it runs, and writes nothing to disk.\n[PRIVACY.md](PRIVACY.md) states what a request carries and which settings change\nany of it.\n\n## Development\n\n```bash\nnpm install\nnpm run build:fixtures\nnpm test\nnpm run check\n```\n\nTests run against generated fixtures and make no network request. The live suite,\n`npm run test:live`, makes one request per route and runs nightly against the\nsite itself.\n\n## Contributing\n\nIssues and pull requests are welcome at\n[the repository](https://github.com/smeet666/mcp-imslp). See\n[CONTRIBUTING.md](CONTRIBUTING.md).\n\n## License\n\nMIT, see [LICENSE](LICENSE). The catalogue and the pages belong to IMSLP and its\ncontributors, published under CC BY-SA 4.0.\n\n---\n\n<a name=\"mcp-imslp-français\"></a>\n\n# mcp-imslp (français)\n\n_[English version](#mcp-imslp)_\n\n[IMSLP](https://imslp.org), l'International Music Score Library Project, s'appelle\naussi la Petrucci Music Library. C'est une bibliothèque libre de musique classique\ntenue par des bénévoles, qui rassemble les partitions, les parties séparées, les\narrangements et les enregistrements des œuvres tombées dans le domaine public,\navec ce que ses pages disent de chaque compositeur. Elle vérifie les droits de\nchaque partition pour le Canada, les États-Unis et l'Union européenne séparément,\net publie ses pages sous\n[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).\n\nCe serveur relie un client de conversation à cette bibliothèque. On peut y\nchercher les œuvres et les personnes qu'elle catalogue, lire une œuvre avec son\nnuméro d'opus, sa tonalité, son instrumentation et son année de composition,\nparcourir les éditions d'une œuvre avec leurs éditeurs et leurs conditions de\ndroits, lire ce que la bibliothèque dit d'un compositeur, et explorer un genre,\nune tonalité ou une instrumentation. Il lit la bibliothèque et renvoie vers elle,\nsans clé d'API ni compte.\n\n## Installation\n\n**Installation en un clic**\n\n[![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=imslp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm1jcC1pbXNscCJdfQ%3D%3D)\n[![Install in VS Code](https://img.shields.io/badge/VS_Code-Install-0098FF?style=flat&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=imslp&config=%7B%22name%22%3A%22imslp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22mcp-imslp%22%5D%7D)\n\n**Claude Code**\n\n```bash\nclaude mcp add imslp -- npx -y mcp-imslp\n```\n\n**Claude Desktop, Cursor, et tout client au format de configuration standard**\n\n```json\n{\n  \"mcpServers\": {\n    \"imslp\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mcp-imslp\"]\n    }\n  }\n}\n```\n\nNode 24 ou plus récent est nécessaire, et aucune variable d'environnement n'est à\nrenseigner.\n\n### Avec Docker\n\n```json\n{\n  \"mcpServers\": {\n    \"imslp\": {\n      \"command\": \"docker\",\n      \"args\": [\"run\", \"-i\", \"--rm\", \"ghcr.io/smeet666/mcp-imslp:1.0.1\"]\n    }\n  }\n}\n```\n\n`-i` garde l'entrée standard ouverte, qui est le canal du protocole, et `-t` est\nomis parce qu'un TTY réécrit le flux. Le conteneur a besoin d'un accès HTTPS\nsortant vers `imslp.org`, et de rien d'autre : aucun volume, aucun port, aucun\nidentifiant.\n\n### Bundle, sans npm\n\nTéléchargez `mcp-imslp-1.0.1.mcpb` depuis\n[la dernière publication](https://github.com/smeet666/mcp-imslp/releases/latest)\net ouvrez-le. Un client qui gère les bundles MCP l'installe seul, sans npm et\nsans fichier de configuration à modifier. Le bundle emporte ses dépendances, donc\nrien n'est téléchargé à l'installation.\n\n## Ce qu'on peut demander\n\n- « Qu'est-ce qu'IMSLP a des nocturnes de Chopin ? »\n- « Lis-moi la page d'Erik Satie et dis-moi quand il a vécu. »\n- « Liste les éditions du Clair de lune de Debussy, avec qui a publié chacune. »\n- « Est-ce que l'édition Henle de cette pièce est libre aux États-Unis ? »\n- « Montre-moi des œuvres pour violoncelle seul dans la bibliothèque. »\n\nLe chemin ordinaire va d'une recherche à une œuvre : `search_works` nomme la page\nd'une œuvre, et `get_work` lit cette page. Il en va de même pour une personne, de\n`search_people` vers `get_person` ou `list_person_works`.\n\n## Les outils\n\n| Outil               | Ce qu'il fait                                                          |\n| ------------------- | ---------------------------------------------------------------------- |\n| `search_works`      | Trouve la page d'une œuvre par son titre, son compositeur ou ses mots. |\n| `search_people`     | Trouve la catégorie sous laquelle une personne est classée.            |\n| `get_work`          | Lit une œuvre : ses caractéristiques, ses sections et ses droits.      |\n| `list_work_files`   | Parcourt les éditions d'une œuvre, avec leurs fichiers.                |\n| `list_person_works` | Lit les œuvres que la bibliothèque classe sous une personne.           |\n| `get_person`        | Lit ce que la bibliothèque contient sur une personne.                  |\n| `browse_category`   | Lit les œuvres classées sous un genre, une tonalité, une formation.    |\n\nUne œuvre s'adresse par le titre de sa page, écrit `Œuvre (Compositeur)`, comme\n`Nocturnes, Op.9 (Chopin, Frédéric)`. Une personne s'adresse par une catégorie,\nécrite `Category:Nom, Prénom`. Les deux viennent d'une recherche, et le préfixe\n`Category:` peut être omis.\n\nLa bibliothèque titre une œuvre dans la langue de son compositeur, donc\n`Die Zauberflöte` trouve l'opéra là où `La Flûte enchantée` trouve les pages\nécrites à son sujet. Une réponse maigre sur une œuvre célèbre est le signe d'un\ntitre dans une autre langue.\n\n### `search_works`\n\nCherche dans les pages des œuvres les mots qui y figurent, où qu'ils soient :\nun titre, un compositeur ou une dédicace trouvent les œuvres qui les portent.\n\n| Argument | Type                          | Requis | Ce qu'il fait                                                    |\n| -------- | ----------------------------- | ------ | ---------------------------------------------------------------- |\n| `query`  | chaîne, 1 à 300 caractères    | oui    | Ce qu'on cherche dans les pages des œuvres.                      |\n| `limit`  | entier, 1 à 50, défaut `10`   | non    | Lignes à servir.                                                 |\n| `offset` | entier, 0 ou plus, défaut `0` | non    | Lignes à sauter, avec le `next_offset` d'une réponse précédente. |\n\n**En retour :** des lignes portant `page`, que `get_work` reprend ; `work` et\n`composer`, lus sur ce titre ; `page_url` ; `snippet`, les mots autour de la\ncorrespondance ; `size_bytes`, `words` et `last_edited` tels que la bibliothèque\nles publie. L'enveloppe porte `returned`, `has_more` et `next_offset`, l'offset\nd'où reprendre. `total` vaut toujours `null` : la bibliothèque ne publie aucun\ncompte de ce qu'une recherche a trouvé. `composer` vaut `null` sur un titre écrit\nhors de la forme `Œuvre (Compositeur)`, et `snippet` vaut `null` sur une ligne\nque la recherche n'a résumée par rien.\n\n### `search_people`\n\nTrouve les compositeurs, éditeurs, arrangeurs et interprètes par leur nom. La\nbibliothèque écrit un nom à sa façon, patronyme d'abord, donc la recherche trouve\nune personne là où une orthographe devinée n'atteint rien.\n\n| Argument | Type                          | Requis | Ce qu'il fait                                                    |\n| -------- | ----------------------------- | ------ | ---------------------------------------------------------------- |\n| `query`  | chaîne, 1 à 300 caractères    | oui    | Le nom cherché parmi les personnes de la bibliothèque.           |\n| `limit`  | entier, 1 à 50, défaut `10`   | non    | Lignes à servir.                                                 |\n| `offset` | entier, 0 ou plus, défaut `0` | non    | Lignes à sauter, avec le `next_offset` d'une réponse précédente. |\n\n**En retour :** des lignes portant `category`, que `get_person` et\n`list_person_works` reprennent ; `name` sans le préfixe ; `page_url` ; `snippet` ;\net `redirect_to`. Une ligne portant un `redirect_to` tient lieu d'une autre\ncatégorie et ne contient aucune œuvre, donc suivez la catégorie qu'elle nomme.\nL'enveloppe est celle de `search_works`, et `total` y vaut `null` pour la même\nraison.\n\n### `get_work`\n\nLit une œuvre : son titre et ses titres alternatifs, le compositeur, les numéros\nd'opus et de catalogue, l'année de composition et celle de première publication,\nla dédicace, la tonalité, la langue, le librettiste, l'instrumentation, les\nmouvements, la création, le style et la période.\n\n| Argument | Type                       | Requis        | Ce qu'il fait                                     |\n| -------- | -------------------------- | ------------- | ------------------------------------------------- |\n| `page`   | chaîne, 1 à 300 caractères | l'un des deux | Le titre de la page, écrit `Œuvre (Compositeur)`. |\n| `pageid` | entier, positif            | l'un des deux | L'identifiant de page rendu par une recherche.    |\n\n**En retour :** chacune des caractéristiques ci-dessus, `null` quand la page la\nlaisse vide, et dans les termes de la page, donc `ca.1830` reste `ca.1830`.\nÀ côté viennent `genre_categories`, que `browse_category` reprend ;\n`external_links` et `authorities`, les notices de l'œuvre au VIAF, à la LCCN, à\nWorldCat, à la BNF et à la GND ; `sections`, avec le nombre d'entrées que le site\ncompte dans chacune ; et `copyright_summary`, une entrée par mention distincte,\navec le nombre d'éditions qui la portent. `editions` contient chaque édition et\nses fichiers, et passe à `null` avec `editions_truncated` à vrai au-delà de cinq\néditions, que `list_work_files` parcourt alors. `redirected_from` nomme le titre\ndemandé quand il a mené ici, et `pageid` vaut `null` pour une œuvre adressée par\nson titre.\n\n### `list_work_files`\n\nLit les partitions et les enregistrements d'une œuvre, édition par édition. Une\nédition est un ensemble de fichiers publiés sous les mêmes conditions :\nl'éditeur, le réviseur et la mention de droits appartiennent à l'édition, et les\nfichiers se rangent dessous. Un bloc d'enregistrements porte des interprètes et\naucune mention de droits.\n\n| Argument  | Type                          | Requis        | Ce qu'il fait                                                                                                                               |\n| --------- | ----------------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |\n| `page`    | chaîne, 1 à 300 caractères    | l'un des deux | Le titre de la page, écrit `Œuvre (Compositeur)`.                                                                                           |\n| `pageid`  | entier, positif               | l'un des deux | L'identifiant de page rendu par une recherche.                                                                                              |\n| `section` | chaîne, 1 à 80 caractères     | non           | Une section de la page, dans ses propres termes : `Scores`, `Parts`, `Recordings`, `Arrangements and Transcriptions`. La casse est ignorée. |\n| `limit`   | entier, 1 à 100, défaut `10`  | non           | Éditions à servir.                                                                                                                          |\n| `offset`  | entier, 0 ou plus, défaut `0` | non           | Éditions à sauter.                                                                                                                          |\n\n**En retour :** `editions`, chacune avec sa `section`, son `publisher_info`, son\n`editor`, son `copyright` et ses `files`. Un fichier porte `imslp_id`,\n`description`, `format` et `format_code`, `pages`, `size_bytes`, `downloads`,\n`rating`, `uploader`, `uploaded_on`, le sigle et le nom de la bibliothèque qui l'a\nnumérisé, et `blocked`, vrai tant qu'IMSLP vérifie les droits de ce fichier.\n`downloads` vaut `null` sur une entrée sans compteur, et `rating` vaut `null`\nquand personne n'a voté. Viennent aussi `editions_on_page`,\n`editions_in_section`, `returned`, `has_more` et `sections`. Une `section` qui ne\ncorrespond à rien revient avec les sections que la page contient, donc une\nrestriction ne se lit jamais comme une œuvre sans partition.\n\n### `list_person_works`\n\nLit les œuvres que la bibliothèque classe sous une personne : ce qu'un\ncompositeur a écrit, et aussi ce sur quoi un réviseur, un arrangeur ou un\ninterprète est crédité.\n\n| Argument   | Type                         | Requis | Ce qu'il fait                                                   |\n| ---------- | ---------------------------- | ------ | --------------------------------------------------------------- |\n| `category` | chaîne, 1 à 300 caractères   | oui    | La catégorie de la personne, écrite `Category:Nom, Prénom`.     |\n| `limit`    | entier, 1 à 100, défaut `25` | non    | Lignes à servir.                                                |\n| `cursor`   | chaîne, 1 à 500 caractères   | non    | Le `cursor` nommé par une réponse précédente, redonné tel quel. |\n\n**En retour :** des lignes portant `page`, `work`, `composer`, `pageid` et\n`page_url`, avec `has_more` et `cursor` pour poursuivre. `total` vaut toujours\n`null` : la bibliothèque ne publie aucun compte de ce que contient une catégorie.\nUne catégorie qu'elle ne contient pas répond comme une catégorie vide, donc une\nréponse sans ligne invite à vérifier l'orthographe avec `search_people`.\n\n### `get_person`\n\nLit ce que la bibliothèque contient sur une personne : le nom tel que sa page\nl'imprime, les dates de vie qu'elle indique, les autres noms sous lesquels elle\nla classe, les registres qui en tiennent une notice, et les adresses vers\nlesquelles elle renvoie hors du site.\n\n| Argument   | Type                       | Requis | Ce qu'il fait                                               |\n| ---------- | -------------------------- | ------ | ----------------------------------------------------------- |\n| `category` | chaîne, 1 à 300 caractères | oui    | La catégorie de la personne, écrite `Category:Nom, Prénom`. |\n\n**En retour :** `category`, `catalogued_as` avec le patronyme d'abord, `name` tel\nque la page l'imprime, `life_dates` dans les termes de la page,\n`alternative_names` et `aliases` comme lignes publiées, `authorities` avec le\nregistre et l'identifiant au VIAF, à la LCCN, à WorldCat, à la BNF et à la GND,\n`external_links`, et `page_url`. `life_dates` vaut `null` sur une page qui n'en\nindique aucune. Cet outil lit la personne ; `list_person_works` lit les œuvres.\n\n### `browse_category`\n\nLit les œuvres classées sous une catégorie : un genre, une tonalité, une\ninstrumentation ou une période. `get_work` rend ces noms pour une œuvre sous\n`genre_categories`, et en redonner un atteint une catégorie que la bibliothèque\ncontient.\n\n| Argument   | Type                         | Requis | Ce qu'il fait                                                                                       |\n| ---------- | ---------------------------- | ------ | --------------------------------------------------------------------------------------------------- |\n| `category` | chaîne, 1 à 300 caractères   | oui    | La catégorie à lire, dans les termes de la bibliothèque : `For piano`, `Nocturnes`, `B-flat minor`. |\n| `limit`    | entier, 1 à 100, défaut `25` | non    | Lignes à servir.                                                                                    |\n| `cursor`   | chaîne, 1 à 500 caractères   | non    | Le `cursor` nommé par une réponse précédente, redonné tel quel.                                     |\n\n**En retour :** les lignes que rend `list_person_works`, avec les mêmes\n`has_more` et `cursor`, et `total` à `null`. La bibliothèque lit une catégorie à\nla fois, donc une question nommant un genre et un instrument se répond en\nparcourant l'un et en lisant l'autre sur chaque œuvre avec `get_work`.\n\n## Le statut de droits\n\nUne partition porte sur IMSLP un statut par juridiction, et la bibliothèque\nvérifie le Canada, les États-Unis et l'Union européenne. Un fichier marqué\n`Public Domain - Non-PD US` est libre au Canada et dans l'Union européenne, et\nprotégé aux États-Unis. Ce serveur rend le statut tel qu'il est publié,\njuridiction par juridiction, sous `copyright_summary` pour une œuvre et sous\n`copyright` pour une édition, avec `restrictions` qui nomme les endroits qu'une\nmention exclut. Un `restrictions` vide n'affirme rien sur les pays qu'IMSLP\nlaisse hors de sa vérification.\n\n## Configuration\n\nChaque variable est facultative. Elles se posent dans le bloc `env` de la\nconfiguration du client.\n\n| Variable                  | Défaut               | Ce qu'elle fait                                                                                            |\n| ------------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------- |\n| `IMSLP_USER_AGENT`        | l'identité du projet | Nomme votre application. L'identité du projet est ajoutée pour qu'IMSLP puisse joindre une personne.       |\n| `IMSLP_MIN_INTERVAL_MS`   | `2500`               | Écart entre deux requêtes, de 2000 à 60000. Une valeur sous le plancher est refusée au profit de celle-ci. |\n| `IMSLP_TIMEOUT_MS`        | `30000`              | Délai d'une requête, de 1000 à 120000.                                                                     |\n| `IMSLP_MAX_RETRIES`       | `3`                  | Tentatives après un échec passager, de 0 à 10.                                                             |\n| `IMSLP_CACHE_TTL_MS`      | `900000`             | Durée pendant laquelle une page reste en mémoire, de 0 à 86400000.                                         |\n| `IMSLP_CACHE_MAX_ENTRIES` | `100`                | Pages gardées en mémoire à la fois, de 0 à 10000.                                                          |\n| `IMSLP_LOG_LEVEL`         | `error`              | `silent`, `error`, `info` ou `debug`, écrit sur la sortie d'erreur.                                        |\n\nUne valeur hors de sa plage retombe sur le défaut, et la raison est écrite sur la\nsortie d'erreur.\n\n## Erreurs\n\nChaque échec porte un des six codes, un message, et quand cela aide une\nindication du geste suivant.\n\n| Code            | Ce qui s'est passé                                 | Que faire                                                                                                        |\n| --------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |\n| `not_found`     | IMSLP a répondu, et la page demandée est absente.  | Vérifiez le titre avec `search_works`.                                                                           |\n| `invalid_input` | Les arguments ont été refusés avant toute requête. | Lisez le message, qui nomme l'argument.                                                                          |\n| `rate_limited`  | IMSLP demande à ce client de ralentir.             | Attendez les secondes indiquées et rappelez avec les mêmes arguments. L'œuvre est toujours dans la bibliothèque. |\n| `parse_failure` | La page a chargé et le contenu attendu est absent. | Signalez-le sur [le suivi d'incidents](https://github.com/smeet666/mcp-imslp/issues).                            |\n| `network_error` | La requête n'a pas abouti.                         | Réessayez sous peu.                                                                                              |\n| `timeout`       | La requête a dépassé son délai.                    | Augmentez `IMSLP_TIMEOUT_MS`, ou demandez moins de lignes.                                                       |\n\n## Comme bibliothèque\n\nLa couche qui lit IMSLP est publiée seule, avec son rythme, son cache et ses\nerreurs, sans protocole attaché.\n\n```ts\nimport { ImslpClient } from \"mcp-imslp/client\";\n\nconst client = new ImslpClient();\nconst { data, cached } = await client.getWork({ page: \"Nocturnes, Op.9 (Chopin, Frédéric)\" });\nconsole.log(data.title, data.copyright_summary, cached);\n```\n\n`renderPage`, `getWork`, `search`, `categoryMembers` et `getPerson` répondent\nchacun `{ data, cached }`, et lèvent une `ImslpError` portant un des six codes.\nLe plancher de deux secondes entre deux requêtes tient également ici.\n\n## Rythme et attribution\n\nIMSLP publie `Crawl-delay: 2` dans son robots.txt, donc les requêtes partent une\nà une avec au moins deux secondes entre elles, et ce plancher tient quelle que\nsoit la configuration. Le `User-Agent` se termine toujours par l'identité du\nprojet et une adresse où joindre une personne.\n\nLes lectures passent par l'API MediaWiki `/api.php` et par le point d'entrée\nqu'IMSLP documente sur sa page `IMSLP:API`. Le robots.txt interdit `/index.php`,\n`/images/`, `/imglnks/`, `/wiki/File:`, `/works` et `/library/`, et ce serveur ne\nconstruit aucune adresse sous ces chemins : il rend le lien de la page de\nl'œuvre, qui est ce qu'une réponse crédite.\n\nLa bibliothèque publie ses pages sous\n[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/), donc tout ce que\nce serveur rend attribue à IMSLP et renvoie vers la page d'origine.\n\n## Confidentialité\n\nCe serveur ne collecte rien sur vous et n'envoie rien à son auteur. Il tourne sur\nvotre machine, ne joint que `imslp.org`, garde ses réponses en mémoire le temps qu'il\ntourne, et n'écrit rien sur le disque. [PRIVACY.md](PRIVACY.md) dit ce qu'une\nrequête emporte et quels réglages changent cela.\n\n## Développement\n\n```bash\nnpm install\nnpm run build:fixtures\nnpm test\nnpm run check\n```\n\nLes tests s'exécutent sur des fixtures engendrées et n'émettent aucune requête.\nLa suite en direct, `npm run test:live`, émet une requête par route et tourne\nchaque nuit contre le site lui-même.\n\n## Contribuer\n\nLes tickets et les propositions de modification sont bienvenus sur\n[le dépôt](https://github.com/smeet666/mcp-imslp). Voir\n[CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Licence\n\nMIT, voir [LICENSE](LICENSE). Le catalogue et les pages appartiennent à IMSLP et\nà ses contributeurs, publiés sous CC BY-SA 4.0.\n",
  "bytes": 43687,
  "sha": "fcf7ad0183682c1c0c59abdcadfa7db36cb8996f1d2125e9346e7a421bd0e50f",
  "repo_slug": "smeet666/mcp-imslp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_smeet666_mcp_imslp_23e7fb03/readme"
}