mcp-imslp
MCP serverMediaYour AI can look up musical works, find scores and editions, and check copyright status in IMSLP, the Petrucci Music Library. This app gives your AI read access to that library. No API key is required.
Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.
Add the app, then ask your AI to look up a piece of music you are interested in. It can pull up scores, editions, and copyright details, and no API key is needed.
What your AI can do with it
- Look up musical works in the library
- Find scores for a work
- List the editions available for a work
- Check the copyright status of a work or edition
- Read details about works and scores
From the project's README
As published by smeet666/mcp-imslp in README.md.
IMSLP, the International Music Score Library Project, is also called the Petrucci Music Library. It is a free library of classical music run by volunteers, holding the sheet music, the parts, the arrangements and the recordings of works whose copyright has lapsed, together with what its pages say about each composer. It reviews the copyright of every score for Canada, the United States and the European Union separately, and publishes its pages under CC BY-SA 4.0.
This server connects a chat client to that library. You can search the works and the people it catalogues, read a work with its opus number, its key, its instrumentation and the year it was written, page through the editions a work holds with their publishers, editors and copyright terms, read what the library says about a composer, and browse a genre, a key or an instrumentation. It reads the library and links to it, and it needs no API key and no account.
Version française
Install
One-click install
Claude Code
claude mcp add imslp -- npx -y mcp-imslp
Claude Desktop, Cursor, and any client using the standard config format
{
"mcpServers": {
"imslp": {
"command": "npx",
"args": ["-y", "mcp-imslp"]
}
}
}
Node 24 or later is required, and no environment variable has to be set.
With Docker
{
"mcpServers": {
"imslp": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/smeet666/mcp-imslp:1.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
imslp.org, and nothing else: no volume, no port, no credential.
Bundle, without npm
Download mcp-imslp-1.0.1.mcpb from
the latest release and
open it. A client that supports MCP bundles installs it on its own, with no npm
and no configuration file to edit. The bundle carries its dependencies, so
nothing is fetched at install time.
What you can ask
- "What does IMSLP hold of Chopin's nocturnes?"
- "Read me the page of Erik Satie and tell me when he lived."
- "List the editions of Debussy's Clair de lune, with who published each one."
- "Is the Henle edition of that piece free to use in the United States?"
- "Show me works for solo cello in the library."
The ordinary path runs from a search to a work: search_works names the page of
a work, and get_work reads that page. The same holds for a person, from
search_people to get_person or list_person_works.
Tools
| Tool | What it does |
|---|---|
search_works | Finds the page of a work by title, composer or words on the page. |
search_people | Finds the category a composer, editor, arranger or performer is filed under. |
get_work | Reads one work: its facets, its sections and its copyright terms. |
list_work_files | Pages through the editions a work holds, with their files. |
list_person_works | Reads the works the library files under one person. |
get_person | Reads what the library holds about one person. |
browse_category | Reads the works filed under a genre, a key or an instrumentation. |
A work is addressed by the title of its page, written Work (Composer), as in
Nocturnes, Op.9 (Chopin, Frédéric). A person is addressed by a category,
written Category:Surname, Forename. Both come back from a search, and the
Category: prefix may be left out.
The library titles a work in the language its composer used, so Die Zauberflöte
finds the opera where The Magic Flute finds the pages written about it. A thin
answer for a famous work is a sign the title is in another language.
search_works
Searches the pages of the works for words appearing anywhere on them, so a title, a composer or a dedication all find the works carrying them.
| Argument | Type | Required | What it does |
|---|---|---|---|
query | string, 1 to 300 characters | yes | What to look for across the pages of the works. |
limit | integer, 1 to 50, default 10 | no | Rows to serve. |
offset | integer, 0 or more, default 0 | no | Rows to skip, using the next_offset of a prior answer. |
In return: rows carrying page, which get_work takes; work and
composer, read off that title; page_url; snippet, the words around the
match; size_bytes, words and last_edited as the library states them. The
envelope carries returned, has_more and next_offset, which is the offset to
read on from. total is always null: the library publishes no count of what a
search matched. composer is null on a title written outside the
Work (Composer) form, and snippet is null on a row the search summarised
with nothing.
search_people
Finds the composers, editors, arrangers and performers by name. The library writes a name its own way, surname first, so searching finds a person where guessing at the spelling reaches nothing.
| Argument | Type | Required | What it does |
|---|---|---|---|
query | string, 1 to 300 characters | yes | The name to look for among the people of the library. |
limit | integer, 1 to 50, default 10 | no | Rows to serve. |
offset | integer, 0 or more, default 0 | no | Rows to skip, using the next_offset of a prior answer. |
In return: rows carrying category, which get_person and
list_person_works take; name without the prefix; page_url; snippet; and
redirect_to. A row with a redirect_to stands for another category and holds
no works of its own, so follow the category it names. The envelope is the one
search_works returns, and total is null here for the same reason.
get_work
Reads one work: its title and alternative titles, the composer, the opus and catalogue numbers, the year of composition and of first publication, the dedication, the key, the language, the librettist, the instrumentation, the movements, the first performance, the style and the period.
| Argument | Type | Required | What it does |
|---|---|---|---|
page | string, 1 to 300 characters | one of two | The page title, written Work (Composer). |
pageid | integer, positive | one of two | The page id a search returned, as an alternative to page. |
In return: every facet above, each null when the page leaves it empty, and
each in the wording the page used, so ca.1830 stays ca.1830. Beside them come
genre_categories, which browse_category takes; external_links and
authorities, the records of the work at VIAF, LCCN, WorldCat, BNF and GND;
sections, with the number of entries the site counts in each; and
copyright_summary, one entry per distinct statement, with the number of
editions carrying it. editions holds every edition with its files, and turns
null with editions_truncated true when the work holds more than five, which
list_work_files then pages through. redirected_from names the title asked
for when it led here, and pageid is null for a work addressed by title.
list_work_files
Reads the scores and the recordings of a work, edition by edition. An edition is a set of files published under one set of terms: the publisher, the editor and the copyright statement belong to the edition, and the files sit under it. A block of recordings carries performers and no copyright statement.
| Argument | Type | Required | What it does |
|---|---|---|---|
page | string, 1 to 300 characters | one of two | The page title, written Work (Composer). |
pageid | integer, positive | one of two | The page id a search returned, as an alternative to page. |
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. |
limit | integer, 1 to 100, default 10 | no | Editions to serve. |
offset | integer, 0 or more, default 0 | no | Editions to skip. |
In return: editions, each with its section, publisher_info, editor,
copyright and files. A file carries imslp_id, description, format and
format_code, pages, size_bytes, downloads, rating, uploader,
uploaded_on, the sigla and name of the library that scanned it, and blocked,
which is true while IMSLP reviews the copyright of that file. downloads is
null on an entry printing no counter, and rating is null when nobody has
voted. Alongside come editions_on_page, editions_in_section, returned,
has_more and sections. A section matching nothing comes back with the
sections the page does hold, so a restriction never reads as a work without
scores.
list_person_works
Reads the works the library files under one person: what a composer wrote, and also what an editor, an arranger or a performer is credited on.
| Argument | Type | Required | What it does |
|---|---|---|---|
category | string, 1 to 300 characters | yes | The person's category, written Category:Surname, Forename. |
limit | integer, 1 to 100, default 25 | no | Rows to serve. |
cursor | string, 1 to 500 characters | no | The cursor a prior answer named, passed back as it was given. |
In return: rows carrying page, work, composer, pageid and page_url,
with has_more and cursor to read on. total is always null: the library
publishes no count of what a category holds. A category the library does not hold
answers the way an empty one does, so an answer with no rows is a reason to check
the spelling with search_people.
get_person
Reads what the library holds about one person: the name as its page prints it, the life dates it states, the other names it files them under, the registers holding a record of them, and the addresses it points to off the site.
| Argument | Type | Required | What it does |
|---|---|---|---|
category | string, 1 to 300 characters | yes | The person's category, written Category:Surname, Forename. |
In return: category, catalogued_as with the surname first, name as the
page prints it, life_dates in the wording the page used, alternative_names
and aliases as published lines, authorities with the register and the
identifier at VIAF, LCCN, WorldCat, BNF and GND, external_links, and
page_url. life_dates is null on a page stating none. This reads the person;
list_person_works reads the works.
browse_category
Reads the works filed under one category: a genre, a key, an instrumentation or a
period. get_work hands these names back for a work under genre_categories,
and passing one of those reaches a category the library holds.
| Argument | Type | Required | What it does |
|---|---|---|---|
category | string, 1 to 300 characters | yes | The category to read, in the library's wording: For piano, Nocturnes, B-flat minor. |
limit | integer, 1 to 100, default 25 | no | Rows to serve. |
cursor | string, 1 to 500 characters | no | The cursor a prior answer named, passed back as it was given. |
In return: the rows list_person_works returns, with the same has_more and
cursor, and total at null. The library reads one category at a time, so a
question naming both a genre and an instrument is answered by browsing one of
them and reading the other off each work with get_work.
Copyright status
A score on IMSLP carries a status per jurisdiction, and the library reviews
Canada, the United States and the European Union. A file reading
Public Domain - Non-PD US is free in Canada and the European Union and
protected in the United States. This server reports the status as published, per
jurisdiction, under copyright_summary on a work and under copyright on an
edition, with restrictions naming the places a statement excludes. An empty
restrictions says nothing about the countries IMSLP leaves out of its review.
Configuration
Every variable is optional. Set them in the env block of your client config.
| Variable | Default | What it does |
|---|---|---|
IMSLP_USER_AGENT | the project identity | Names your application. The project identity is appended so IMSLP can reach a person. |
IMSLP_MIN_INTERVAL_MS | 2500 | Gap between two requests, from 2000 to 60000. A figure under the floor is refused and this one used. |
IMSLP_TIMEOUT_MS | 30000 | Deadline for one request, from 1000 to 120000. |
IMSLP_MAX_RETRIES | 3 | Attempts after a transient failure, from 0 to 10. |
IMSLP_CACHE_TTL_MS | 900000 | How long a page stays in memory, from 0 to 86400000. |
IMSLP_CACHE_MAX_ENTRIES | 100 | Pages held in memory at once, from 0 to 10000. |
IMSLP_LOG_LEVEL | error | silent, error, info or debug, written to stderr. |
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 |
|---|---|---|
not_found | IMSLP answered, and the page asked for is absent. | Check the title with search_works. |
invalid_input | The arguments were refused before any request went out. | Read the message, which names the argument. |
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. |
parse_failure | The page loaded and the expected content was absent. | Report it at the issue tracker. |
network_error | The request did not complete. | Try again shortly. |
timeout | The request passed its deadline. | Raise IMSLP_TIMEOUT_MS, or ask for fewer rows. |
As a library
The layer reading IMSLP is published on its own, with its pacing, its cache and its errors, and with no protocol attached.
import { ImslpClient } from "mcp-imslp/client";
const client = new ImslpClient();
const { data, cached } = await client.getWork({ page: "Nocturnes, Op.9 (Chopin, Frédéric)" });
console.log(data.title, data.copyright_summary, cached);
renderPage, getWork, search, categoryMembers and getPerson each answer
{ data, cached }, and throw an ImslpError carrying one of the six codes. The
two-second floor between requests holds here as well.
Pacing and attribution
IMSLP publishes Crawl-delay: 2 in its robots.txt, so requests go out one at a
time with at least two seconds 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.
Reads go through the MediaWiki API at /api.php and through the listing endpoint
IMSLP documents on its own IMSLP:API page. The robots.txt disallows
/index.php, /images/, /imglnks/, /wiki/File:, /works and /library/,
and this server builds no address under any of them: it hands back the link to
the work page, which is what an answer credits.
The library publishes its pages under CC BY-SA 4.0, so anything shown from this server credits IMSLP and links the page it came from.
Privacy
This server collects nothing about you and sends nothing to its author. It runs
on your machine, contacts imslp.org and nothing else, holds its answers in memory
while it runs, and writes nothing to disk.
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
site itself.
Contributing
Issues and pull requests are welcome at the repository. See CONTRIBUTING.md.
License
MIT, see LICENSE. The catalogue and the pages belong to IMSLP and its contributors, published under CC BY-SA 4.0.
mcp-imslp (français)
English version
IMSLP, l'International Music Score Library Project, s'appelle aussi la Petrucci Music Library. C'est une bibliothèque libre de musique classique tenue par des bénévoles, qui rassemble les partitions, les parties séparées, les arrangements et les enregistrements des œuvres tombées dans le domaine public, avec ce que ses pages disent de chaque compositeur. Elle vérifie les droits de chaque partition pour le Canada, les États-Unis et l'Union européenne séparément, et publie ses pages sous CC BY-SA 4.0.
Ce serveur relie un client de conversation à cette bibliothèque. On peut y chercher les œuvres et les personnes qu'elle catalogue, lire une œuvre avec son numéro d'opus, sa tonalité, son instrumentation et son année de composition, parcourir les éditions d'une œuvre avec leurs éditeurs et leurs conditions de droits, lire ce que la bibliothèque dit d'un compositeur, et explorer un genre, une tonalité ou une instrumentation. Il lit la bibliothèque et renvoie vers elle, sans clé d'API ni compte.
Installation
Installation en un clic
Claude Code
claude mcp add imslp -- npx -y mcp-imslp
Claude Desktop, Cursor, et tout client au format de configuration standard
Shortened here. Read the whole README on GitHub.
Signals
- Last commit
- Sep 2026
- Weekly downloads
- 83
Advanced
- Delivery
- mcp-imslp MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
- Catalog kind
- mcp-server
- Gateway key
io-github-smeet666-mcp-imslp- Source
- github.com/smeet666/mcp-imslp