get_book_by_id
Retrieve detailed book information by specifying its identifier type (ISBN, LCCN, OCLC, OLID) and value using the Open Library API.
Instructions
Get detailed information about a book using its identifier (ISBN, LCCN, OCLC, OLID).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| idType | Yes | The type of identifier used (ISBN, LCCN, OCLC, OLID). | |
| idValue | Yes | The value of the identifier. |
Input Schema (JSON Schema)
{
"properties": {
"idType": {
"description": "The type of identifier used (ISBN, LCCN, OCLC, OLID).",
"enum": [
"isbn",
"lccn",
"oclc",
"olid"
],
"type": "string"
},
"idValue": {
"description": "The value of the identifier.",
"type": "string"
}
},
"required": [
"idType",
"idValue"
],
"type": "object"
}
Implementation Reference
- src/tools/get-book-by-id/index.ts:33-157 (handler)Implements the core logic for the 'get_book_by_id' tool: validates input arguments, fetches book data from Open Library API via axios, processes and formats the response into BookDetails, handles errors, and returns structured content.export const handleGetBookById = async ( args: unknown, axiosInstance: AxiosInstance, ): Promise<CallToolResult> => { const parseResult = GetBookByIdArgsSchema.safeParse(args); if (!parseResult.success) { const errorMessages = parseResult.error.errors .map((e) => `${e.path.join(".")}: ${e.message}`) .join(", "); throw new McpError( ErrorCode.InvalidParams, `Invalid arguments for get_book_by_id: ${errorMessages}`, ); } const { idType, idValue } = parseResult.data; const apiUrl = `/api/volumes/brief/${idType}/${idValue}.json`; try { const response = await axiosInstance.get<OpenLibraryBookResponse>(apiUrl); // Check if records object exists and is not empty if ( !response.data || !response.data.records || Object.keys(response.data.records).length === 0 ) { return { content: [ { type: "text", text: `No book found for ${idType}: ${idValue}`, }, ], }; } // Get the first record from the records object const recordKey = Object.keys(response.data.records)[0]; const record: OpenLibraryRecord | undefined = response.data.records[recordKey]; if (!record) { // This case should theoretically not happen if the length check passed, but good for safety return { content: [ { type: "text", text: `Could not process book record for ${idType}: ${idValue}`, }, ], }; } const recordData = record.data; const recordDetails = record.details?.details; // Access the nested details const bookDetails: BookDetails = { title: recordData.title, subtitle: recordData.subtitle, authors: recordData.authors?.map((a) => a.name) || [], publishers: recordData.publishers?.map((p) => p.name), publish_date: recordData.publish_date, number_of_pages: recordData.number_of_pages ?? recordDetails?.number_of_pages, // Prefer identifiers from recordData, fallback to recordDetails if necessary isbn_13: recordData.identifiers?.isbn_13 ?? recordDetails?.isbn_13, isbn_10: recordData.identifiers?.isbn_10 ?? recordDetails?.isbn_10, lccn: recordData.identifiers?.lccn ?? recordDetails?.lccn, oclc: recordData.identifiers?.oclc ?? recordDetails?.oclc_numbers, olid: recordData.identifiers?.openlibrary, // Add OLID from identifiers open_library_edition_key: recordData.key, // From recordData open_library_work_key: recordDetails?.works?.[0]?.key, // From nested details cover_url: recordData.cover?.medium, // Use medium cover from recordData info_url: record.details?.info_url ?? recordData.url, // Prefer info_url from details preview_url: record.details?.preview_url ?? recordData.ebooks?.[0]?.preview_url, }; // Clean up undefined fields Object.keys(bookDetails).forEach((key) => { const typedKey = key as keyof BookDetails; if ( bookDetails[typedKey] === undefined || ((typedKey === "authors" || typedKey === "publishers") && Array.isArray(bookDetails[typedKey]) && bookDetails[typedKey].length === 0) ) { delete bookDetails[typedKey]; } }); return { content: [ { type: "text", text: JSON.stringify(bookDetails, null, 2), }, ], }; } catch (error) { let errorMessage = "Failed to fetch book data from Open Library."; if (axios.isAxiosError(error)) { if (error.response?.status === 404) { errorMessage = `No book found for ${idType}: ${idValue}`; } else { errorMessage = `API Error: ${error.response?.statusText ?? error.message}`; } } else if (error instanceof Error) { errorMessage = `Error processing request: ${error.message}`; } console.error("Error in get_book_by_id:", error); // Return error as text content return { content: [ { type: "text", text: errorMessage, }, ], }; } };
- Zod validation schema for the tool's input arguments: idType (enum: isbn, lccn, oclc, olid) and idValue (non-empty string).// Schema for the get_book_by_id tool arguments const GetBookByIdArgsSchema = z.object({ idType: z .string() .transform((val) => val.toLowerCase()) .pipe( z.enum(["isbn", "lccn", "oclc", "olid"], { errorMap: () => ({ message: "idType must be one of: isbn, lccn, oclc, olid", }), }), ), idValue: z.string().min(1, { message: "idValue cannot be empty" }), });
- TypeScript interface defining the structured BookDetails output returned by the handler.export interface BookDetails { title: string; subtitle?: string; authors: string[]; publishers?: string[]; publish_date?: string; number_of_pages?: number; isbn_13?: string[]; isbn_10?: string[]; lccn?: string[]; oclc?: string[]; olid?: string[]; // Add OLID field open_library_edition_key: string; // e.g., "/books/OL24194264M" open_library_work_key?: string; // e.g., "/works/OL15610910W" cover_url?: string; info_url: string; preview_url?: string; }
- src/index.ts:142-162 (registration)Registers the tool in the MCP server's listTools response, providing name, description, and input schema.{ name: "get_book_by_id", description: "Get detailed information about a book using its identifier (ISBN, LCCN, OCLC, OLID).", inputSchema: { type: "object", properties: { idType: { type: "string", enum: ["isbn", "lccn", "oclc", "olid"], description: "The type of identifier used (ISBN, LCCN, OCLC, OLID).", }, idValue: { type: "string", description: "The value of the identifier.", }, }, required: ["idType", "idValue"], }, },
- src/index.ts:180-181 (registration)Routes tool calls named 'get_book_by_id' to the handleGetBookById handler function in the callTool request handler.case "get_book_by_id": return handleGetBookById(args, this.axiosInstance);