Skip to main content
Glama

Rijksmuseum MCP Server

by r-huijts
MIT License
14
58
  • Apple
  • Linux
ToolHandler.ts6.92 kB
import { CallToolRequest } from "@modelcontextprotocol/sdk/types.js"; import { RijksmuseumApiClient } from "../api/RijksmuseumApiClient.js"; import { ErrorHandler } from "../error/ErrorHandler.js"; import { isSearchArtworkArguments, isOpenImageArguments, isGetUserSetDetailsArguments } from "../utils/typeGuards.js"; import { SystemIntegration } from "../utils/SystemIntegration.js"; import { GetUserSetsArguments, GetUserSetDetailsArguments } from "../types.js"; export class ToolHandler { constructor(private apiClient: RijksmuseumApiClient) {} async handleToolRequest(request: CallToolRequest) { try { switch (request.params.name) { case "search_artwork": return await this.handleSearchArtwork(request); case "get_artwork_details": return await this.handleGetArtworkDetails(request); case "get_artwork_image": return await this.handleGetArtworkImage(request); case "get_user_sets": return await this.handleGetUserSets(request); case "get_user_set_details": return await this.handleGetUserSetDetails(request); case "open_image_in_browser": return await this.handleOpenImageInBrowser(request); case "get_artist_timeline": return await this.handleGetArtistTimeline(request); default: throw new Error(`Unknown tool: ${request.params.name}`); } } catch (error) { ErrorHandler.handleError(error); } } private async handleSearchArtwork(request: CallToolRequest) { if (!isSearchArtworkArguments(request.params.arguments)) { throw new Error("Invalid arguments for search_artwork"); } const artworks = await this.apiClient.searchArtworks(request.params.arguments); return { content: [{ type: "text", text: JSON.stringify({ count: artworks.length, artworks: artworks }, null, 2) }] }; } private async handleGetArtworkDetails(request: CallToolRequest) { const { objectNumber, culture = 'en' } = request.params.arguments as { objectNumber: string; culture?: 'nl' | 'en' }; ErrorHandler.validateRequiredParam(objectNumber, 'objectNumber'); const details = await this.apiClient.getArtworkDetails(objectNumber, culture); return { content: [{ type: "text", text: JSON.stringify(details, null, 2) }] }; } private async handleGetArtworkImage(request: CallToolRequest) { const { objectNumber, culture = 'en' } = request.params.arguments as { objectNumber: string; culture?: 'nl' | 'en' }; ErrorHandler.validateRequiredParam(objectNumber, 'objectNumber'); const imageData = await this.apiClient.getArtworkImageTiles(objectNumber, culture); // Format the response to be more readable and include summary information const summary = { totalLevels: imageData.levels.length, zoomLevels: imageData.levels.map(level => ({ name: level.name, resolution: `${level.width}x${level.height}`, tilesCount: level.tiles.length })), details: imageData }; return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] }; } private async handleGetUserSets(request: CallToolRequest) { const { page = 0, pageSize = 10, culture = 'en' } = request.params.arguments as GetUserSetsArguments; const userSetsResponse = await this.apiClient.getUserSets({ page, pageSize, culture }); // Format the response to be more readable const summary = { totalSets: userSetsResponse.count, currentPage: page, pageSize: pageSize, fetchedSets: userSetsResponse.userSets.length, queryTimeMs: userSetsResponse.elapsedMilliseconds, sets: userSetsResponse.userSets.map(set => ({ id: set.id, name: set.name, description: set.description, itemCount: set.count, creator: set.user.name, createdOn: set.createdOn, updatedOn: set.updatedOn, links: set.links })) }; return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] }; } private async handleGetUserSetDetails(request: CallToolRequest) { if (!isGetUserSetDetailsArguments(request.params.arguments)) { throw new Error("Invalid arguments for get_user_set_details"); } const { setId, culture = 'en', page = 0, pageSize = 25 } = request.params.arguments; ErrorHandler.validateRequiredParam(setId, 'setId'); const setDetails = await this.apiClient.getUserSetDetails({ setId, culture, page, pageSize }); // Format the response to be more readable const summary = { setInfo: { id: setDetails.userSet.id, name: setDetails.userSet.name, description: setDetails.userSet.description, type: setDetails.userSet.type, totalItems: setDetails.userSet.count, creator: setDetails.userSet.user.name, createdOn: setDetails.userSet.createdOn, updatedOn: setDetails.userSet.updatedOn }, items: setDetails.userSet.setItems.map(item => ({ objectNumber: item.objectNumber, links: item.links, imageInfo: item.image ? { dimensions: `${item.image.width}x${item.image.height}`, url: item.image.cdnUrl } : null })), pagination: { currentPage: page, pageSize: pageSize, fetchedItems: setDetails.userSet.setItems.length }, queryTimeMs: setDetails.elapsedMilliseconds }; return { content: [{ type: "text", text: JSON.stringify(summary, null, 2) }] }; } private async handleOpenImageInBrowser(request: CallToolRequest) { if (!isOpenImageArguments(request.params.arguments)) { throw new Error("Invalid arguments for open_image_in_browser"); } try { await SystemIntegration.openInBrowser(request.params.arguments.imageUrl); return { content: [{ type: "text", text: `Successfully opened image in browser: ${request.params.arguments.imageUrl}` }] }; } catch (error) { return { content: [{ type: "text", text: `Failed to open image in browser: ${error instanceof Error ? error.message : String(error)}` }], isError: true }; } } private async handleGetArtistTimeline(request: CallToolRequest) { const { artist, maxWorks = 10 } = request.params.arguments as { artist: string; maxWorks?: number }; ErrorHandler.validateRequiredParam(artist, 'artist'); const timelineArtworks = await this.apiClient.getArtistTimeline(artist, maxWorks); return { content: [{ type: "text", text: JSON.stringify({ artist, works: timelineArtworks }, null, 2) }] }; } }

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/r-huijts/rijksmuseum-mcp'

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