Skip to main content
Glama

get-artwork-by-id

Retrieve detailed information and images for specific artworks from the Art Institute of Chicago collection using unique artwork IDs.

Instructions

Get additional information, including an image if available, about a specific artwork by its ID from the Art Institute of Chicago. Using the value of Artwork ID from the 'search-by-title' tool.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
idYesThe ID of the artwork to retrieve.
includeImageNoWhether to include the artwork image in the response.

Implementation Reference

  • The GetArtworkByIdTool class is the main handler for the 'get-artwork-by-id' tool. It fetches detailed artwork information from the Art Institute of Chicago API, formats it into text, and optionally retrieves and embeds the image.
    export class GetArtworkByIdTool extends BaseTool<typeof artworkByIdSchema, any> {
      public readonly name: string = 'get-artwork-by-id';
      public readonly description: string = `Get additional information, including an image if available, about a specific artwork by its ID`
        + ` from the Art Institute of Chicago. `
        + `Using the value of Artwork ID from the 'search-by-title' tool.`;
    
      public readonly inputSchema = artworkByIdSchema;
      public readonly imageByTitle = new Map<string, string>();
      private readonly fields: string[] = Object.keys(artworkSchema._def.shape());
    
      constructor(private server: McpServer) {
        super();
      }
    
      public async executeCore(input: z.infer<typeof this.inputSchema>) {
        const { id, includeImage } = input;
    
        const url = new URL(`${this.apiBaseUrl}/artworks/${id}`);
        url.searchParams.set('fields', this.fields.join(','));
    
        const parsedResponse = await this.safeApiRequest(
          url,
          { method: 'GET' },
          artworkResponseSchema,
        );
        const artwork = parsedResponse.data;
        const text = this.formatArtworkDetails(artwork, `${parsedResponse.config.iiif_url}`);
    
        const content = [];
        content.push({ type: 'text' as const, text });
        if (includeImage) {
          const image = await this.getArtworkImage(artwork, `${parsedResponse.config.iiif_url}`);
          if (image) {
            content.push(image);
          }
        }
        return { content };
      }
    
      private formatArtworkDetails(artwork: z.infer<typeof artworkSchema>, iiif_url: string) {
        return `Title: ${artwork.title}\n`
          + `${artwork.alt_titles ? `Alt Titles: ${artwork.alt_titles.join(', ')}\n` : ''}`
          + `Artist: ${artwork.artist_display}\n`
          + `Artist ID: ${artwork.artist_id}\n`
          + `Description: ${artwork.description ?? 'No description available'}\n`
          + `Image ID: ${artwork.image_id}\n`
          + `Place of Origin: ${artwork.place_of_origin}\n`
          + `Dimensions: ${artwork.dimensions}\n`
          + `Medium: ${artwork.medium_display}\n`
          + `Credit Line: ${artwork.credit_line}\n`
          + `Department: ${artwork.department_title}\n`
          + `Is On View: ${artwork.is_on_view ? 'Yes' : 'No'}\n`
          + `Main Reference Number: ${artwork.main_reference_number}\n`
          + `Has not been viewed much: ${artwork.has_not_been_viewed_much ? 'Yes' : 'No'}\n`
          + `Date Start: ${artwork.date_start ?? 'N/A'}\n`
          + `Date End: ${artwork.date_end ?? 'N/A'}\n`
          + `Date: ${artwork.date_display ?? 'N/A'}\n`
          + `Fiscal Year: ${artwork.fiscal_year ?? 'N/A'}\n`
          + `Is Public Domain: ${artwork.is_public_domain ? 'Yes' : 'No'}\n`
          + `Gallery: ${artwork.gallery_title ?? 'N/A'}\n`
          + `Artwork Type: ${artwork.artwork_type_title}\n`
          + `Artist Title: ${artwork.artist_title}\n`
          + `Artist Titles: ${artwork.artist_titles.join(', ')}\n`
          + `Style Title: ${artwork.style_title ?? 'N/A'}\n`
          + `Image URL: ${artwork.image_id ? `${iiif_url}/${artwork.image_id}/full/843,/0/default.jpg` : 'N/A'}\n`;
      }
    
      private async getArtworkImage(artwork: z.infer<typeof artworkSchema>, iiif_url: string) {
        if (artwork.image_id) {
          try {
            const imageURL = `${iiif_url}/${artwork.image_id}/full/843,/0/default.jpg`;
            const imageBase64 = await imageToBase64(imageURL);
            const title = `${artwork.title} (${artwork.artist_title})`;
            this.imageByTitle.set(title, imageBase64);
            this.server.server.notification({
              method: 'notifications/resources/list_changed',
            });
            return {
              type: 'image' as const,
              data: imageBase64,
              mimeType: 'image/jpeg',
            };
          }
          catch (error) {
            console.error(`Error fetching image for artwork ${artwork.id}:`, error);
            return {
              type: 'text' as const,
              text: `Error fetching image for artwork ${artwork.id}: ${error}`,
            };
          }
        }
        return null;
      }
    }
  • Input schema for the get-artwork-by-id tool defining parameters id and optional includeImage.
    const artworkByIdSchema = z.object({
      id: z.number().describe('The ID of the artwork to retrieve.'),
      includeImage: z.boolean().optional().default(true).describe('Whether to include the artwork image in the response.'),
    });
  • Zod schemas for single artwork data (artworkSchema) and API response (artworkResponseSchema) used for validation in the tool handler.
    export const artworkSchema = z.object({
      id: z.number(),
      title: z.string(),
      alt_titles: z.array(z.string()).nullable(),
      thumbnail: thumbnailSchema,
      main_reference_number: z.string(),
      has_not_been_viewed_much: z.boolean(),
      date_start: z.number().nullable(),
      date_end: z.number().nullable(),
      date_display: z.string().nullable(),
      artist_display: z.string(),
      place_of_origin: z.string(),
      description: z.string().nullable(),
      dimensions: z.string(),
      medium_display: z.string(),
      credit_line: z.string(),
      fiscal_year: z.number().nullable(),
      is_public_domain: z.boolean(),
      gallery_title: z.string().nullable(),
      artwork_type_title: z.string(),
      is_on_view: z.boolean(),
      department_title: z.string(),
      artist_title: z.string(),
      artist_titles: z.array(z.string()),
      style_title: z.string().nullable(),
      artist_id: z.number(),
      image_id: z.string(),
    });
    
    export const artworkResponseSchema = z.object({
      data: artworkSchema,
      info: apiInfoSchema,
      config: apiConfigSchema,
    });
  • src/index.ts:64-68 (registration)
    Registration of the 'get-artwork-by-id' tool with the MCP server using server.tool().
      this.getArtworkByIdTool.name,
      this.getArtworkByIdTool.description,
      this.getArtworkByIdTool.inputSchema.shape,
      this.getArtworkByIdTool.execute.bind(this.getArtworkByIdTool),
    );
  • safeApiRequest method from BaseTool, used by the handler for safe API calls with Zod validation.
    protected async safeApiRequest<S extends z.ZodType>(
      url: URL | string,
      options: RequestInit,
      schema: S,
    ): Promise<z.infer<S>> {
      const response = await articRateLimiter.fetch(url.toString(), {
        ...options,
        headers: {
          ...this.getDefaultHeaders(),
          ...(options.headers || {}),
        },
      });
    
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
    
      const jsonResponse = await response.json();
      const parsedResponse = schema.safeParse(jsonResponse);
    
      if (!parsedResponse.success) {
        throw new Error(`Invalid response shape: ${JSON.stringify(parsedResponse.error.issues, null, 2)}`);
      }
    
      return parsedResponse.data;
    }

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/mikechao/artic-mcp'

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