About art-institute-chicago-mcp-server
art-institute-chicago-mcp-server exposes the Art Institute of Chicago collection to MCP clients seeking artwork, artist, and exhibition information. It also searches audio-guide content and carries source and rights information with results.
Key features
- 01Search artworks with multiple filters
- 02Fetch artwork details, provenance, and image links
- 03Look up artists and exhibitions
- 04Search audio-guide transcripts
- 05Inspect filter vocabulary and artwork counts
Use cases
- 01Find collection works by subject or date
- 02Check artwork provenance and image rights
- 03Explore artist details and sample works
- 04Search exhibition records or audio-guide content
MCP support
The MCP server provides clients with search tools for artworks, artists, exhibitions, audio guides, and filter vocabulary, plus an artwork JSON resource. It supports stdio and Streamable HTTP.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://art-institute-chicago.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-art-institute-chicago https://art-institute-chicago.caseyjhand.com/mcp{"mcpServers":{"cyanheads-art-institute-chicago":{"type":"http","url":"https://art-institute-chicago.caseyjhand.com/mcp"}}}Open in Cursor{"servers":{"cyanheads-art-institute-chicago":{"type":"http","url":"https://art-institute-chicago.caseyjhand.com/mcp"}}}Open in VS Code{"mcpServers":{"cyanheads-art-institute-chicago":{"type":"http","url":"https://art-institute-chicago.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-art-institute-chicago":{"serverUrl":"https://art-institute-chicago.caseyjhand.com/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
6| Parameter | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page to return (1-based); page times the applied limit may not exceed 1,000. |
| sort | enum | No | relevance ranks by query text, or by museum popularity without it; date_asc and date_desc sort by start year.relevance | date_asc | date_desc |
| limit | integer | No | Requested rows per page (0-1,000); at most 12 are returned. 0 returns only totalCount and facets. |
| query | string | No | Text matched against titles, artists, descriptions, provenance, and other catalog fields; every word must match. Supports "exact phrase", -exclude, and a | b. |
| style | string | No | Style title as artic_lookup_vocabulary lists it (case ignored), such as Impressionism; matches preferred or alternate styles. |
| theme | string | No | Theme title as artic_lookup_vocabulary lists it (case ignored), such as Women artists. |
| artist | string | No | Artist or culture name matched against every credited artist (all words must match). |
| facets | array | No | Facet counts to compute over the filtered set (top 15 values each): department, artwork_type, style, subject, classification, place_of_origin, artist. An array or a comma-separated string. |
| gallery | string | No | Gallery where the work is on view, such as Gallery 240 (a bare 240 is read as Gallery 240). Only on-view works carry a gallery. |
| subject | string | No | Subject title as artic_lookup_vocabulary lists it (case ignored). |
| year_to | integer | No | Latest year; a work matches when its date span overlaps year_from to year_to. Negative for BCE. |
| material | string | No | Material title as artic_lookup_vocabulary lists it (case ignored), such as ink or gold leaf. |
| artist_id | integer | No | Agent id from artic_search_artists or an artist facet row; matches preferred and other credits. |
| has_image | boolean | No | Only works with an image. |
| technique | string | No | Technique title as artic_lookup_vocabulary lists it (case ignored), such as black-and-white photography or plain weaving. |
| year_from | integer | No | Earliest year; a work matches when its date span overlaps year_from to year_to. Negative for BCE. |
| department | string | No | Department title exactly as artic_lookup_vocabulary lists it (case ignored), such as Prints and Drawings. |
| artwork_type | string | No | Artwork type title as artic_lookup_vocabulary lists it (case ignored), such as Painting or Print. |
| on_view_only | boolean | No | Only works on view at the museum now. |
| classification | string | No | Classification title as artic_lookup_vocabulary lists it (case ignored), such as oil on canvas or etching. |
| place_of_origin | string | No | Place of origin as artic_lookup_vocabulary lists it (case ignored), such as france. |
| public_domain_only | boolean | No | Only public-domain works, whose images are CC0. |
Details
From the project README@cyanheads/art-institute-chicago-mcp-server
Search the Art Institute of Chicago collection: artworks, artists, exhibitions, and audio guides via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://art-institute-chicago.caseyjhand.com/mcp
Overview
The Art Institute of Chicago's collection: about 133,000 artworks, plus artists, exhibitions, and audio-guide stops. Search with filters and facet counts, read full records with provenance and IIIF image URLs, and search audio-guide transcripts. No API key required.
Tools
| Tool | Description |
|---|---|
artic_search_artworks | Search artworks by text and filters, with facet counts and date sorting |
artic_get_artworks | Fetch full records for up to 10 artworks: description, provenance, histories, image URLs with rights status, related media |
artic_search_artists | Find artists, cultures, and organizations by name or id, with available Getty ULAN identifiers, life dates, artwork counts, and sample works |
artic_search_exhibitions | Search past, current, and upcoming exhibitions by text, date, artwork id, or artist id |
artic_search_audio_guide | Search audio-guide stops by text: title, MP3 URL, and transcript |
artic_lookup_vocabulary | List the values a search filter accepts, with artwork counts |
Resources
| Resource | Description |
|---|---|
artic://artworks/{id} | One artwork record as JSON, with the API license text and description attribution |
artic_get_artworks returns the same record for clients that don't surface resources.
Capability reference
Searches and vocabulary lookup accept limits up to 1,000, and the response states the cap actually applied: artworks 12, artist query and exhibitions 25, audio guide 2, vocabulary 100. Keep the same requested limit when following next_page; page times applied size must stay within 1,000. At the vocabulary cap, narrow with contains.
artic_search_artworks tool
query(every word must match;"exact phrase",-exclude, anda | bwork) plus filtersartist,artist_id,department,artwork_type,style,subject,classification,material,technique,theme,place_of_origin,gallery,year_from/year_to(date-span overlap, negative for BCE),public_domain_only,on_view_only, andhas_image, combined with AND- Up to 12 rows per page (default 10), within the first 1,000 matches;
sortisrelevance,date_asc, ordate_desc, andsort_appliedreportspopularitywhen relevance had no query text facetsadds the top 15 values for up to seven fields (artistcounts every credited artist and rows carryartist_id);limit: 0returns counts only
artic_get_artworks tool
idstakes up to 1,000 entries (comma strings up to 100,000 characters); blanks and duplicates are dropped and artwork page URLs are read as ids. The first 10 are fetched;sectionschooses from description and provenance (default), exhibition_history, publication_history, and catalogue- Records return in request order.
missing_idslists ids the museum doesn't have;deferred_idslists records held back by the response budget plus ids not yet tried, to pass to the same tool again include_related_media(default on) loads up to 20 linked lectures and audio stops;description_attributionappears whenever CC BY description text is returned
artic_search_artists tool
query(all name words must match) orids, not both; ids takes up to 1,000 entries (comma strings up to 100,000 characters) and the first 25 are fetched. Query mode addsartists_only(defaulttrue),born_from/born_to, and up to 25 agents per page- Each agent carries
artwork_count, up to threesample_works(the museum's highlights first), life years, andalt_names; ids mode reportsmissing_idsanddeferred_idsto pass as ids in the next call ulan_idis the Getty ULAN identifier when the museum records one; coverage is sparse, and it is distinct from the museumidused by other tools
artic_search_exhibitions tool
query,when(current,upcoming,past, or the defaultany),date_from/date_to(YYYY-MM-DD, matched by run overlap), and exactartwork_id/artist_idfilters, combined with AND; up to 25 per page within the first 1,000 matches- Relationship filters cover recorded museum links; absent links do not prove an artwork or artist never appeared in a show
sortisrelevance,start_desc, orstart_asc, defaulting to relevance with a query andstart_descwithout;statusis the museum's label and doesn't say whether a show is open (whendoes)- Rows carry dates, gallery, summary, web page, image,
artist_ids, and theartworksshown, when the museum lists them
artic_search_audio_guide tool
queryis required and matches stop titles and transcripts (every word); up to two whole stops per page (default 2)- Each stop has
title,audio_url(MP3), andtranscriptbut no artwork id; every response carrieslicense_textandsource_citation(content is for noncommercial educational and personal use)
artic_lookup_vocabulary tool
vocabularyis one ofdepartment,artwork_type,style,subject,classification,material,technique,theme,place_of_origin, orgallery; optionalcontainssubstring (ignoring case and accents, soappliquefindsappliqué (technique)),public_domain_only, and up to 100 values (default 25)- Values come back most common first with
artwork_count, in the exact form theartic_search_artworksfilter of the same name accepts;filter_paramnames that filter
artic://artworks/{id} resource
{ artwork, license_text, description_attribution?, notice? }asapplication/json, whereartworkis theartic_get_artworksrecord with its default sections and related media- An unknown id fails as
artwork_not_found; ids come fromartic_search_artworks
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Art Institute-specific:
- Keyless access to the Art Institute of Chicago public API (
api.artic.edu/api/v1); IIIF image URLs are built from each record (843 px for every image, 1686 px and a IIIF manifest for public-domain works), never fetched - One shared request pacer under the API's published limit of 60 requests a minute, retries for transient failures inside a 20-second deadline, and an in-process response cache (records 6 hours, searches 15 minutes, vocabularies 24 hours)
- Text search requires every word to match, so totals are real and a miss reads as zero hits; results keep the museum's relevance order
- Placeholder years in the museum's data (outside −8000 to 2100) are left out of output, year filters, and date sorts;
date_displaystays the authority
Agent-friendly output:
- Rights travel with the data:
image.rights(public_domain/in_copyright) on every image, with the 1686 px URL only where reuse is allowed; the API'slicense_textverbatim;description_attributionwhen CC BY text is returned;source_citationon audio-guide results - Partial results instead of failures:
artic_get_artworksreportsmissing_idsanddeferred_ids, and when a secondary lookup (related media, artist counts) fails, the primary records still return with anoticenaming what is missing - Paging that names the next move:
totalCount,has_more,next_page, and anoticefor the next page, the 1,000-match ceiling, or the filter to loosen after zero hits
Data and licensing
The Art Institute of Chicago licenses its API data by surface, and the server passes the API's own license_text through with every artwork, artist, exhibition, and audio-guide result:
| Content | Terms |
|---|---|
| Artwork metadata, artists, exhibitions, vocabulary terms, related media | CC0 |
Artwork description text | CC BY 4.0: credit the Art Institute of Chicago and cite the record's web_url |
| Artwork images | Reusable only for public-domain works (image.rights: "public_domain", CC0); other images need a rights check |
| Audio-guide content | Noncommercial educational and personal use, with copyright notices kept and the source cited |
This server is an independent project and is not affiliated with or endorsed by the Art Institute of Chicago.
Known limitations
- Only the first 1,000 matches of any search are reachable without an authenticated key. Broad questions need filters or facets.
- The API's limit of 60 requests a minute is per egress IP, so every client behind one IP shares it. A call that cannot start within its 20-second budget fails as
rate_limited. - Curatorial text is sparse: about 10% of artworks have a
description, and in a general sample 94% lack provenance. About 1% of artists have a biography, and there is no nationality field, onlyartist_displayprose. - About 15 artworks carry placeholder years and about 4,900 carry no dates; neither matches a year filter.
- Some vocabulary titles are stored cut at 40 characters in the museum's data (
gelatin silver (developing-out-paper) pr). Pass values asartic_lookup_vocabularylists them. - The API's firewall refuses any request whose text contains markup such as
<script>; the call fails asrequest_blocked. - About 4% of exhibitions list their artworks.
- Audio-guide stops have no artwork link. Some titles are file names, and some transcripts are in Spanish.
- Image URLs can stop resolving when the museum unpublishes or replaces an image, and relevance order follows the museum's ranking, which may shift.
Getting started
Public Hosted Instance
A public instance is available at https://art-institute-chicago.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own config shape.
For Claude Code:
claude mcp add --transport http art-institute-chicago-mcp-server https://art-institute-chicago.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"art-institute-chicago-mcp-server": {
"type": "streamableHttp",
"url": "https://art-institute-chicago.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.json):
{
"mcpServers": {
"art-institute-chicago-mcp-server": {
"url": "https://art-institute-chicago.caseyjhand.com/mcp"
}
}
}
Claude Desktop adds remote servers under Settings → Connectors.
Every caller of the hosted instance shares one api.artic.edu request budget of 50 requests a minute. For sustained use, run your own instance.
Self-Hosted / Local
Add the following to your MCP client configuration file. No API key is needed; AIC_CONTACT tells the museum how to reach you (see Configuration).
{
"mcpServers": {
"art-institute-chicago-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/art-institute-chicago-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"AIC_CONTACT": "[email protected]"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"art-institute-chicago-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/art-institute-chicago-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"AIC_CONTACT": "[email protected]"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"art-institute-chicago-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "-e", "[email protected]", "ghcr.io/cyanheads/art-institute-chicago-mcp-server:latest"]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- No API key or account. The Art Institute API asks clients to identify themselves with a contact; set
AIC_CONTACTto an email or URL.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/art-institute-chicago-mcp-server.git
- Navigate into the directory:
cd art-institute-chicago-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env and set AIC_CONTACT
Configuration
| Variable | Description | Default |
|---|---|---|
AIC_CONTACT | Email or URL the museum can reach, sent in the AIC-User-Agent header. Printable ASCII only; the server refuses to start otherwise. Set your own for any deployment. | https://github.com/cyanheads/art-institute-chicago-mcp-server |
AIC_REQUESTS_PER_MINUTE | Outbound requests per minute to api.artic.edu, 1–600. Stays under the API's published limit of 60; raise it only if the museum grants a higher one. | 50 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. Overrides the server's declared posture. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for every server setting and the common framework overrides.
Running the server
Local development
-
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdio -
Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more bun run test # Runs the test suite
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point: registers the tools and resource, sets the server instructions, and starts and stops the API service. |
src/config | AIC_CONTACT and AIC_REQUESTS_PER_MINUTE parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts) and the artwork output schema they share. |
src/mcp-server/resources | The artic://artworks/{id} resource. |
src/services/aic | Art Institute API client: pacing, retries, response cache, HTML-to-text, IIIF URL construction, and the artwork and artist record builders. |
tests/ | Unit and integration tests, mirroring the src/ structure. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor logging,ctx.enrichfor paging and notices - Register new tools and resources in the barrels under
src/mcp-server/*/definitions/index.ts - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.