Jellyseerr MCP Server
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., "@Jellyseerr MCP Serversearch for the latest Marvel movies"
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.
ArrChestra

ArrChestra connects your MCP client to Seerr/Jellyseerr, Sonarr, Radarr, NZBGet, SABnzbd and NZBHydra2. You can request movies and shows, check upcoming episodes, change monitoring, and pause or retry downloads.
Each service is optional. Configure the ones you use; ArrChestra starts in read-only mode.
What you can do
Service | Supported operations |
Seerr / Jellyseerr | Media search, requests and request status |
Radarr | Movie lookup and additions, monitoring, release calendars, release searches, queue/history and import diagnostics |
Sonarr | Series and episode lookup, season monitoring, missing episodes, upcoming airings, searches, queue/history and individual queue removal |
NZBGet / SABnzbd | Download status, queue/history, categories, pause/resume and targeted retries |
NZBHydra2 | Release searches and submission of a selected result to either downloader |
There are no tools for deleting library titles, purging whole queues, restarting services, changing service configuration or running scripts.
Related MCP server: Overseerr MCP Server
Quick start
Requires Python 3.10+ and uv. From a checkout:
cp .env.example .env
# Uncomment and fill in the services you want to connect.
chmod 600 .env
uv run arrchestra-mcpA minimal .env for Sonarr:
SONARR_URL=https://sonarr.example.com
SONARR_API_KEY=replace-me
MCP_READ_ONLY=trueThe server uses stdio. Your MCP client launches the process and owns stdin/stdout; logs go to stderr. If you use an existing Python environment, pip install . installs the same arrchestra-mcp command.
Both module entrypoints work:
python -m arrchestra_mcp
python -m jellyseerr_mcp # compatibility with earlier installationsConfiguration
Environment variables override .env in the working directory. Restart after changing settings. There is no YAML loader.
Service | Required settings |
Seerr / Jellyseerr |
|
Radarr |
|
Sonarr |
|
NZBGet |
|
SABnzbd |
|
NZBHydra2 |
|
Only configured services appear in your client's tool list. Configure at least one service. If a service is missing required settings, startup fails and lists what's missing. You can connect one instance of each service, including both NZBGet and SABnzbd.
Use each service's base URL, without an API suffix, credentials or query string. Paths such as https://media.example.com/sonarr are supported. Use HTTPS where possible; certificate verification is always enabled. SABnzbd requires its full API key, not the add-only NZB key.
Setting | Default | Purpose |
|
| Per-request timeout in seconds, positive and at most 300 |
| Shared timeout | Override for one service, including |
|
| Timeout for release/target search, which waits on every enabled indexer |
| Search timeout | Override for Radarr or Sonarr; |
|
| Diagnostic log level |
|
| Block mutations |
|
| Permit that service's operations when read-only mode is off |
|
| Optional legacy |
| Empty | Trusted indexer origins for NZB retrieval redirects |
Store API keys and passwords in environment variables or a protected .env file. Don't put them in prompts or commit them. .env files are ignored by Git and excluded from Docker builds.
Allowing changes
To allow changes, turn off read-only mode and enable writes for each service you want to control:
MCP_READ_ONLY=false
SONARR_ALLOW_WRITES=true
RADARR_ALLOW_WRITES=true
NZBGET_ALLOW_WRITES=trueFor Seerr, use JELLYSEERR_ALLOW_WRITES=true; for SABnzbd, use SABNZBD_ALLOW_WRITES=true. Downloading a Hydra result requires write permission for the chosen downloader.
If a write times out, it may still have succeeded. ArrChestra won't retry it automatically. Check the queue, history or command status before trying again. If a submission was accepted but its status check failed, the response still includes the job ID.
Connecting an MCP client
For clients that use mcpServers:
{
"mcpServers": {
"arrchestra": {
"command": "uv",
"args": ["run", "--directory", "/path/to/arrchestra", "arrchestra-mcp"],
"env": {
"SONARR_URL": "https://sonarr.example.com",
"SONARR_API_KEY": "replace-me",
"MCP_READ_ONLY": "true"
}
}
}
}Hermes
Hermes uses mcp_servers in ~/.hermes/config.yaml:
mcp_servers:
arrchestra:
command: uv
args: [run, --directory, /path/to/arrchestra, arrchestra-mcp]
supports_parallel_tool_calls: trueKeep the service settings in ArrChestra's .env. Hermes also supports per-server tools.include and tools.exclude filters.
OpenClaw
OpenClaw's MCP registry uses mcp.servers. Set the same stdio command and arguments in that entry. openclaw mcp probe checks a saved server's connection and tool discovery. Consult the documentation for your installed version: runtime adapters differ, and this registry is separate from mcporter.
Hermes and OpenClaw configuration follows their documentation; end-to-end sessions with those clients aren't covered by this project's tests.
Using ArrChestra
Ask your client | What it uses |
"Find Arrival in Seerr and request it." | Seerr search and request tools |
"Which episodes in my Sonarr library air this week?" | Sonarr calendar |
"When is the next monitored episode of Severance?" | Sonarr library lookup and next-up episodes |
"Stop monitoring season 1 of Severance. Leave the other seasons alone." | Sonarr season monitoring |
"Why hasn't Arrival imported? Check Radarr's queue and history." | Radarr queue, history and import diagnostics |
"Show the SABnzbd queue, then pause the download I choose." | Downloader queue and per-job pause |
Calendars, lookups and status checks work in read-only mode. Requests, monitoring changes, searches that trigger downloads, and download controls need write permission. get_services shows which services are connected and whether writes are enabled.
Requesting and adding media
request_media creates a request in Seerr and uses its approval workflow. TV requests default to season 1; specify seasons to request others.
You can also add media directly with radarr_add_movie or sonarr_add_series. These bypass Seerr. They require a quality profile and root folder from radarr_get_options or sonarr_get_options, and don't start a search automatically.
IDs depend on the operation: Seerr requests and Radarr additions use TMDB IDs; Sonarr additions use TVDB IDs. Once a title is in your library, monitoring and search tools use its Sonarr or Radarr ID. Episode tools use Sonarr episode IDs and check that they belong to the selected series.
Sonarr monitoring changes only the selected seasons or episodes. Season and episode changes require separate calls.
Upcoming episodes and movie releases
sonarr_get_calendar and radarr_get_calendar take start and end as ISO dates or timestamps, with a maximum window of 90 days. The Sonarr calendar includes series titles. Both calendars cover the library, not a single title; results include hasFile to show whether the episode or movie is already downloaded.
For one show's upcoming episodes, use sonarr_get_next_up with its Sonarr series_id. It lists monitored episodes in air-date order, starting from now. Set since to use a different date, or include_unmonitored=true to include episodes you aren't monitoring.
Searches and downloads
radarr_search_movie and sonarr_search_episodes start a search in Radarr or Sonarr, which may download a matching release. Sonarr accepts up to 100 episode IDs from one series per call. The response includes a command ID; check it with radarr_get_command or sonarr_get_command to see whether the search finished. A finished search doesn't mean the download or import is complete.
To choose a release yourself, use radarr_get_releases or sonarr_get_releases, then the matching grab_release tool. Sonarr requires an episode or season. These searches use indexer quota, and grabs refuse releases that are stale or rejected by the service.
Downloader tools require backend="nzbget" or backend="sabnzbd". Pause and resume take either a job_id from that downloader's queue or whole_queue=true. Omitting both is an error. Sonarr/Radarr queue and history entries include downloadId for matching a download to its job. NZBGet IDs are positive integer strings; SABnzbd IDs are opaque strings.
downloads_retry retries one failed history item. NZBGet downloads it again and may replace the failed partial files.
sonarr_remove_queue_item clears one Sonarr queue entry by its queue item_id. By default it leaves the downloader's files alone and doesn't blocklist the release. remove_from_client=true also requests removal from the downloader and may delete its files; blocklist=true marks the release failed. Queue removal requires Sonarr write permission.
Downloading from NZBHydra2
nzbhydra_search searches your indexers through Hydra. nzbhydra_get_capabilities lists the supported filters and categories.
To send a result to a downloader, call nzbhydra_download_result with its result_id, a backend and a configured downloader category. ArrChestra fetches the NZB and submits it to that downloader. The category's existing post-processing settings still apply.
This bypasses Sonarr/Radarr release selection. It doesn't fulfill a Seerr request or guarantee that the download will be imported into your library.
If NZB retrieval redirects to an indexer, add that indexer's origin to NZBHYDRA_ALLOWED_REDIRECT_ORIGINS. ArrChestra doesn't forward Hydra credentials or cookies to a different origin, follow HTTPS-to-HTTP redirects, or follow same-origin redirects outside the configured service path.
Group | Tools |
Common |
|
Seerr |
|
Radarr |
|
Sonarr |
|
Downloaders |
|
NZBHydra |
|
The Hydra submission tool appears only when a downloader is configured. Downloader calls always require an explicit backend.
Your MCP client's tools/list response includes the argument schemas. Most lists default to 25 results, up to 100 per page. Sonarr/Radarr queue, history and Sonarr missing episodes use page_number; other paged lists use offset. APIs that return a whole list are paged locally.
Docker
docker build -t arrchestra-mcp .
docker run --rm -i --env-file .env arrchestra-mcpThe image runs as a non-root user. Use -i, not -t, for stdio clients. Pass credentials when the container starts; no media-service credentials are needed to build the image.
GitHub Actions builds pull requests without publishing an image. Pushes to main, release tags matching v*.*.*, and manual workflow runs publish to ghcr.io/aserper/arrchestra using GitHub's job token.
Limits and compatibility
Stdio only. SSE and HTTP are disabled until server authentication is implemented. Don't expose the process through an unauthenticated network bridge.
Responses are limited to 8 MiB. NZBs/XML must be UTF-8 and stay within 4 MiB, 50,000 elements and depth 32. DTD/entities and compressed responses are refused. Large replies fail with a limit error.
Writes to the same service run one at a time within this process. Reads can continue while a write is running. Other processes and clients can still make concurrent changes.
Treat release titles and backend messages as data, not instructions to the agent.
Earlier Jellyseerr installations can keep using python -m jellyseerr_mcp, search_media, request_media and get_request. Requests now require write permission. The optional legacy raw_request tool only accepts GET status, without parameters or a body. ping returns arrchestra-mcp as the service name.
Development
uv run --extra test pytest -qTests use httpx.MockTransport for service API calls, MCP sessions for tool behavior, and subprocesses for stdio. They cover permissions, IDs, both Hydra downloaders, redirects, response limits, timeouts and concurrent calls.
API tests target Sonarr/Radarr /api/v3, NZBGet v18+, SABnzbd 5.x and NZBHydra's public Newznab API. They don't check compatibility with every server release. Use disposable media and queues when testing writes against a live service.
Implementation notes cover the code structure and follow-up work. Changes to API operations should include a mock contract test.
Available Tools
5 toolsget_requestBRead-onlyIdempotent
Inspect an existing Seerr request by its request ID.
| Name | Required | Description | Default |
|---|---|---|---|
| request_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint, so the safety profile is covered. The description adds only that the request must be 'existing,' but omits return format, error behavior, and any auth or rate-limit context. With annotations carrying the main burden, this is minimally sufficient.
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?
A single front-loaded sentence with zero wasted words. It states the action, resource, and identifying parameter immediately.
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 tool is simple and annotations are rich, but with no output schema and 0% schema description coverage, the description should clarify return values or error behavior when a request ID is invalid. It leaves those gaps unaddressed, making it adequate but incomplete.
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 0%, so the description must compensate for the undocumented parameter. Instead it merely restates 'by its request ID,' which duplicates the schema's property name and title without adding format, source, or semantic detail.
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?
States a specific verb ('Inspect'), resource ('Seerr request'), and identifier ('request ID'), making the basic action clear. However, it does not differentiate this tool from siblings like request_media or search_media, so an agent must infer the distinction without explicit guidance.
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?
Provides no when-to-use guidance, prerequisites, or alternatives. The description implies you need an existing request ID but does not say when to choose this tool over request_media or search_media, leaving routing entirely to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_servicesARead-onlyIdempotent
List configured services and effective write permissions; no secrets.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and open-world behavior, so the safety bar is low. The description still adds genuine value beyond them: it discloses the return content (configured services plus effective write permissions) and asserts 'no secrets', telling the agent it can call this without credential-leak concerns and that the response is about permissions rather than sensitive data.
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?
A single, front-loaded clause with zero wasted words; the resource is named first and the scope caveat follows immediately. Nothing could be cut without losing meaning.
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?
With no output schema, the description must at least characterize the return, which it does ('configured services and effective write permissions'). It does not sketch the shape of the response or ordering, but for a no-argument inventory read with rich safety annotations, it is sufficient for correct 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 tool takes zero parameters, so there is nothing for the description to clarify; per the rubric a 0-parameter tool gets the baseline of 4. The description correctly does not invent parameter detail.
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?
Specific verb ('List') plus specific resource ('configured services and effective write permissions'), with a clarifying scope note ('no secrets'). The siblings (request_media, ping, search_media, get_request) are unrelated in subject matter, so no disambiguation is needed and the purpose reads unambiguously.
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 only implied — it is evidently the service-inventory read used when an agent needs to know what services exist and what write rights it has. There is no explicit when-to-use, when-not-to-use, or naming of an alternative tool, so guidance stays at the minimum-viable level.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pingARead-onlyIdempotent
Cheap MCP liveness check; does not probe backends.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, openWorldHint, and destructiveHint=false, so the safety profile is covered elsewhere. The description nonetheless adds genuinely new behavioral context: the call is cheap and explicitly does not touch backends, which matters for an agent deciding whether a liveness probe can have side effects.
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?
A single clause-pair sentence with zero waste, front-loading the core purpose and appending the one useful limitation. Nothing could be cut without losing information.
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, no-output-schema health probe, the description covers what the tool is and how far its assurance extends. The only mild gap is that the success/failure signal shape is left implicit, which matters slightly given there is no output schema.
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 takes zero parameters, which is the baseline-4 case; the schema description coverage is 100% and there is nothing for the description to disambiguate.
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?
States a specific verb+resource: an MCP liveness check, and adds a precise boundary ('does not probe backends') so the agent knows this is a shallow health probe, not a backend diagnostic. It does not name siblings, but the sibling set (media/service tools) is functionally unrelated, so differentiation is less critical.
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?
'Cheap ... liveness check' implies the context: use it as a lightweight preflight rather than a real request. The 'does not probe backends' clause implicitly tells the agent not to rely on it for backend reachability, but no explicit when-not or alternative tool is named.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
request_mediaA
Create a Seerr request (preserves its approval workflow). Movie or TV TMDB ID; TV seasons default to [1]. Requires Seerr writes.
| Name | Required | Description | Default |
|---|---|---|---|
| is_4k | No | ||
| seasons | No | ||
| media_id | Yes | ||
| media_type | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly=false and idempotent=false; the description adds real context beyond them by noting the approval workflow is preserved and that Seerr write permission is required. It still omits duplicate-request behavior and what the call returns.
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?
Three tight fragments, front-loaded with the action, then parameters, then prerequisite. No filler or redundancy.
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?
Covers the action, key parameters, and the write prerequisite, but with no output schema the description should say what a successful request returns (e.g., a request ID) and it leaves is_4k undocumented, so it is only partially complete for a 4-param mutation.
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 0% schema coverage the description must carry the burden: it documents media_id as a TMDB ID, media_type as movie or TV, and seasons defaulting to [1]. It omits is_4k entirely and does not pin down accepted media_type strings or the seasons semantics for movies.
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?
States a specific verb+resource ('Create a Seerr request') and clarifies the domain (Seerr media requests) which separates it from read-only siblings like get_request and search_media. It does not name a sibling explicitly, so it lands just short of the top mark.
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 (request media when you want Seerr to procure it) and it states the prerequisite 'Requires Seerr writes', but there is no explicit when-to-use vs. when-not, and no guidance on choosing among siblings such as get_request or search_media.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_mediaCRead-onlyIdempotent
Search Seerr/Jellyseerr by text query.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so the safety profile is fully covered. The description adds nothing behavioral beyond that – no rate limits, no source-service caveats, no indication of what a search returns.
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?
A single front-loaded sentence with no waste. It is arguably too terse for the information it should carry, but the structure itself is clean.
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 search entry-point tool with no output schema, the description says nothing about result shape, pagination, or the relationship to request_media. An agent can call it, but cannot predict what it will get back.
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 0%, but with only one parameter the description does clarify that the 'query' is a free-text search string rather than an ID or filter. That is useful but minimal compensation for the undocumented parameter.
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?
States a specific verb (Search) and resource (Seerr/Jellyseerr media), so an agent knows the domain and action. It does not differentiate itself from siblings like get_request or request_media, but the core purpose is unambiguous.
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?
No guidance on when to use this versus request_media or get_request, nor any prerequisites. An agent must infer that this is the discovery step that precedes requesting media.
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.
5 tool updates
v0.2.0- First observed
get_request - First observed
get_services - First observed
ping - First observed
request_media - First observed
search_media
TDQS
Scored across 5 tools
Each tool targets a distinct operation: creating a request, health check, listing services, searching, and inspecting a single request. There is no meaningful overlap between any pair.
request_media, get_services, search_media, and get_request follow a clear verb_noun pattern. The lone 'ping' is a bare verb, a minor deviation but a conventional one for a liveness check.
Five tools is a well-scoped set for a Jellyseerr integration, covering the essential search/request/inspect flow plus diagnostics without redundant entries. Every tool earns its place.
The surface covers search, create, and single-request inspection, but notable operations are missing: listing/filtering requests, cancelling or deleting a request, and approving/declining pending requests. Agents hit dead ends for anything beyond one-at-a-time request handling.
Maintenance
Related MCP Connectors
- JellypodOAuthcom.jellypod
Create, import, and publish Jellypod podcast episodes from your AI assistant.
Manage Superlist tasks and lists in plain language from any MCP-compatible AI agent.
Search, read and create Linear issues, projects, teams and cycles.
Manage SRG+ hubs, channels, content, assets, users, and workspaces from any MCP-aware AI agent.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Overseerr API to manage movie and TV show requests, allowing users to check server status and filter requests by various criteria.MIT
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Overseerr API to manage movie and TV show requests, allowing users to check server status and filter media requests by various criteria.1MIT
- FlicenseNot gradedqualityDmaintenanceEnables interaction with Overseerr media request management through natural language. Supports searching for movies/TV shows, managing media requests, approving/denying requests, and monitoring server status.-
- AlicenseAqualityAmaintenanceEnables AI assistants to interact with Overseerr for automated media discovery, requests, and management in your Plex ecosystem, including searching for movies/TV shows, requesting media, checking request status, and managing approvals.6165 npm24MIT