Skip to main content
Glama

readarr_review_setup

Analyze Readarr book management setup to review settings and identify configuration improvements for quality profiles, download clients, storage, and indexers.

Instructions

Get comprehensive configuration review for Readarr (Books). Returns all settings for analysis: quality profiles, download clients, naming, storage, indexers, health warnings, and more. Use this to analyze the setup and suggest improvements.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • src/index.ts:163-170 (registration)
    Registration of the readarr_review_setup tool. This block dynamically creates the tool definition (name, description, input schema with no parameters) inside the addConfigTools function, which is called for Readarr at line 178 if configured.
    name: `${serviceName}_review_setup`, description: `Get comprehensive configuration review for ${displayName}. Returns all settings for analysis: quality profiles, download clients, naming, storage, indexers, health warnings, and more. Use this to analyze the setup and suggest improvements.`, inputSchema: { type: "object" as const, properties: {}, required: [], }, }
  • Handler implementation for readarr_review_setup tool. Matches the tool name, extracts serviceName='readarr', fetches comprehensive config data via ReadarrClient methods (status/health/quality profiles/storage/download clients/naming/media mgmt/tags/indexers/metadata profiles), constructs a detailed review object, and returns it as JSON.
    case "sonarr_review_setup": case "radarr_review_setup": case "lidarr_review_setup": case "readarr_review_setup": { const serviceName = name.split('_')[0] as keyof typeof clients; const client = clients[serviceName]; if (!client) throw new Error(`${serviceName} not configured`); // Gather all configuration data const [status, health, qualityProfiles, qualityDefinitions, downloadClients, naming, mediaManagement, rootFolders, tags, indexers] = await Promise.all([ client.getStatus(), client.getHealth(), client.getQualityProfiles(), client.getQualityDefinitions(), client.getDownloadClients(), client.getNamingConfig(), client.getMediaManagement(), client.getRootFoldersDetailed(), client.getTags(), client.getIndexers(), ]); // For Lidarr/Readarr, also get metadata profiles let metadataProfiles = null; if (serviceName === 'lidarr' && clients.lidarr) { metadataProfiles = await clients.lidarr.getMetadataProfiles(); } else if (serviceName === 'readarr' && clients.readarr) { metadataProfiles = await clients.readarr.getMetadataProfiles(); } const review = { service: serviceName, version: status.version, appName: status.appName, platform: { os: status.osName, isDocker: status.isDocker, }, health: { issueCount: health.length, issues: health, }, storage: { rootFolders: rootFolders.map(f => ({ path: f.path, accessible: f.accessible, freeSpace: formatBytes(f.freeSpace), freeSpaceBytes: f.freeSpace, unmappedFolderCount: f.unmappedFolders?.length || 0, })), }, qualityProfiles: qualityProfiles.map(p => ({ id: p.id, name: p.name, upgradeAllowed: p.upgradeAllowed, cutoff: p.cutoff, allowedQualities: p.items .filter(i => i.allowed) .map(i => i.quality?.name || i.name || (i.items?.map(q => q.quality.name).join(', '))) .filter(Boolean), customFormatsWithScores: p.formatItems?.filter(f => f.score !== 0).length || 0, minFormatScore: p.minFormatScore, })), qualityDefinitions: qualityDefinitions.map(d => ({ quality: d.quality.name, minSize: d.minSize + ' MB/min', maxSize: d.maxSize === 0 ? 'unlimited' : d.maxSize + ' MB/min', preferredSize: d.preferredSize + ' MB/min', })), downloadClients: downloadClients.map(c => ({ name: c.name, type: c.implementationName, protocol: c.protocol, enabled: c.enable, priority: c.priority, })), indexers: indexers.map(i => ({ name: i.name, protocol: i.protocol, enableRss: i.enableRss, enableAutomaticSearch: i.enableAutomaticSearch, enableInteractiveSearch: i.enableInteractiveSearch, priority: i.priority, })), naming: naming, mediaManagement: { recycleBin: mediaManagement.recycleBin || 'not set', recycleBinCleanupDays: mediaManagement.recycleBinCleanupDays, downloadPropersAndRepacks: mediaManagement.downloadPropersAndRepacks, deleteEmptyFolders: mediaManagement.deleteEmptyFolders, copyUsingHardlinks: mediaManagement.copyUsingHardlinks, importExtraFiles: mediaManagement.importExtraFiles, extraFileExtensions: mediaManagement.extraFileExtensions, }, tags: tags.map(t => t.label), ...(metadataProfiles && { metadataProfiles }), }; return { content: [{ type: "text", text: JSON.stringify(review, null, 2), }], }; }
  • ReadarrClient class that provides all the API client methods invoked by the review_setup handler, including getStatus, getHealth, getQualityProfiles, getQualityDefinitions, getDownloadClients, getNamingConfig, getMediaManagement, getRootFoldersDetailed, getTags, getIndexers, and getMetadataProfiles.
    export class ReadarrClient extends ArrClient { constructor(config: ArrConfig) { super('readarr', config); this.apiVersion = 'v1'; } /** * Get all authors */ async getAuthors(): Promise<Author[]> { return this['request']<Author[]>('/author'); } /** * Get a specific author */ async getAuthorById(id: number): Promise<Author> { return this['request']<Author>(`/author/${id}`); } /** * Search for authors */ async searchAuthors(term: string): Promise<SearchResult[]> { return this['request']<SearchResult[]>(`/author/lookup?term=${encodeURIComponent(term)}`); } /** * Add an author */ async addAuthor(author: Partial<Author> & { foreignAuthorId: string; rootFolderPath: string; qualityProfileId: number; metadataProfileId: number }): Promise<Author> { return this['request']<Author>('/author', { method: 'POST', body: JSON.stringify({ ...author, monitored: author.monitored ?? true, addOptions: { searchForMissingBooks: true, }, }), }); } /** * Get books for an author */ async getBooks(authorId?: number): Promise<Book[]> { const url = authorId ? `/book?authorId=${authorId}` : '/book'; return this['request']<Book[]>(url); } /** * Get a specific book */ async getBookById(id: number): Promise<Book> { return this['request']<Book>(`/book/${id}`); } /** * Search for missing books for an author */ async searchMissingBooks(authorId: number): Promise<{ id: number }> { return this['request']<{ id: number }>('/command', { method: 'POST', body: JSON.stringify({ name: 'AuthorSearch', authorId, }), }); } /** * Search for a specific book */ async searchBook(bookIds: number[]): Promise<{ id: number }> { return this['request']<{ id: number }>('/command', { method: 'POST', body: JSON.stringify({ name: 'BookSearch', bookIds, }), }); } /** * Get calendar (upcoming book releases) */ async getCalendar(start?: string, end?: string): Promise<Book[]> { const params = new URLSearchParams(); if (start) params.append('start', start); if (end) params.append('end', end); const query = params.toString() ? `?${params.toString()}` : ''; return this['request']<Book[]>(`/calendar${query}`); } /** * Get metadata profiles */ async getMetadataProfiles(): Promise<MetadataProfile[]> { return this['request']<MetadataProfile[]>('/metadataprofile'); } }
  • src/index.ts:178-178 (registration)
    Conditional registration call for Readarr tools, including readarr_review_setup, only if Readarr client is configured (lines 79-81).
    if (clients.readarr) addConfigTools('readarr', 'Readarr (Books)');

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aplaceforallmystuff/mcp-arr'

If you have feedback or need assistance with the MCP directory API, please join our Discord server