Skip to main content
Glama
8enSmith

mcp-open-library

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

TableJSON Schema
NameRequiredDescriptionDefault
idTypeYesThe type of identifier used (ISBN, LCCN, OCLC, OLID).
idValueYesThe value of the identifier.

Implementation Reference

  • 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);
Install Server

Other Tools

Related Tools

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/8enSmith/mcp-open-library'

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