io.github.cyanheads/art-institute-chicago-mcp-server

Official registry Search and web

Search the Art Institute of Chicago collection: artworks, artists, exhibitions, and audio guides.

What can you do with io.github.cyanheads/art-institute-chicago-mcp-server?

  • 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_display stays the authority
  • 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's license_text verbatim; description_attribution when CC BY text is returned; source_citation on a...
  • Partial results instead of failures: artic_get_artworks reports missing_ids and deferred_ids, and when a secondary lookup (related media, artist counts) fails, the primary records still return with a notice naming what is missing
  • Paging that names the next move: totalCount, has_more, next_page, and a notice for the next page, the 1,000-match ceiling, or the filter to loosen after zero hits

Install

Add this to your client's MCP configuration (Claude Desktop: claude_desktop_config.json; Cursor: .cursor/mcp.json; VS Code and others use the same "mcpServers" shape).

Remote (streamable-http)
Claude Code
claude mcp add --transport http art-institute-chicago-mcp-server 'https://art-institute-chicago.caseyjhand.com/mcp'
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "art-institute-chicago-mcp-server": {
            "type": "http",
            "url": "https://art-institute-chicago.caseyjhand.com/mcp"
        }
    }
}
NPM package @cyanheads/art-institute-chicago-mcp-server
Claude Code
claude mcp add art-institute-chicago-mcp-server -e 'AIC_CONTACT=<AIC_CONTACT>' -e 'AIC_REQUESTS_PER_MINUTE=50' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/art-institute-chicago-mcp-server@0.3.0
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "art-institute-chicago-mcp-server": {
            "command": "npx",
            "args": [
                "-y",
                "@cyanheads/art-institute-chicago-mcp-server@0.3.0"
            ],
            "env": {
                "AIC_CONTACT": "<AIC_CONTACT>",
                "AIC_REQUESTS_PER_MINUTE": "50",
                "MCP_LOG_LEVEL": "info"
            }
        }
    }
}

Environment variables

  • AIC_CONTACT Contact the museum can reach (an email or URL, printable ASCII), sent in the AIC-User-Agent header on every request. Defaults to the repository URL; set your own for any deployment.
  • AIC_REQUESTS_PER_MINUTE Outbound request budget per minute for api.artic.edu (1-600). Defaults to 50, under the published 60 per minute anonymous limit.
  • MCP_LOG_LEVEL Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').
NPM package @cyanheads/art-institute-chicago-mcp-server
Claude Code
claude mcp add art-institute-chicago-mcp-server -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_AUTH_MODE=none' -e 'AIC_CONTACT=<AIC_CONTACT>' -e 'AIC_REQUESTS_PER_MINUTE=50' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/art-institute-chicago-mcp-server@0.3.0
JSON configuration (Claude Desktop, Cursor, VS Code)
{
    "mcpServers": {
        "art-institute-chicago-mcp-server": {
            "command": "npx",
            "args": [
                "-y",
                "@cyanheads/art-institute-chicago-mcp-server@0.3.0"
            ],
            "env": {
                "MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
                "MCP_HTTP_HOST": "127.0.0.1",
                "MCP_HTTP_PORT": "3010",
                "MCP_HTTP_ENDPOINT_PATH": "/mcp",
                "MCP_AUTH_MODE": "none",
                "AIC_CONTACT": "<AIC_CONTACT>",
                "AIC_REQUESTS_PER_MINUTE": "50",
                "MCP_LOG_LEVEL": "info"
            }
        }
    }
}

Environment variables

  • MCP_TRANSPORT_TYPE Selects the HTTP transport.
  • MCP_HTTP_HOST The hostname for the HTTP server.
  • MCP_HTTP_PORT The port to run the HTTP server on.
  • MCP_HTTP_ENDPOINT_PATH The endpoint path for the MCP server.
  • MCP_AUTH_MODE Authentication mode to use: 'none', 'jwt', or 'oauth'.
  • AIC_CONTACT Contact the museum can reach (an email or URL, printable ASCII), sent in the AIC-User-Agent header on every request. Defaults to the repository URL; set your own for any deployment.
  • AIC_REQUESTS_PER_MINUTE Outbound request budget per minute for api.artic.edu (1-600). Defaults to 50, under the published 60 per minute anonymous limit.
  • MCP_LOG_LEVEL Sets the minimum log level for output (e.g., 'debug', 'info', 'warn').

Documentation

From the project's README on GitHub, refreshed weekly. Links open on GitHub.

Contents
  1. @cyanheads/art-institute-chicago-mcp-server
  2. Overview
  3. Capability reference
  4. Features
  5. Data and licensing
  6. Known limitations
  7. Getting started
  8. Configuration
  9. Running the server
  10. Project structure
  11. Development guide
  12. Contributing
  13. License

@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.

6 Tools • 1 Resource

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
ToolDescription
artic_search_artworksSearch artworks by text and filters, with facet counts and date sorting
artic_get_artworksFetch full records for up to 10 artworks: description, provenance, histories, image URLs with rights status, related media
artic_search_artistsFind artists, cultures, and organizations by name or id, with available Getty ULAN identifiers, life dates, artwork counts, and sample works
artic_search_exhibitionsSearch past, current, and upcoming exhibitions by text, date, artwork id, or artist id
artic_search_audio_guideSearch audio-guide stops by text: title, MP3 URL, and transcript
artic_lookup_vocabularyList the values a search filter accepts, with artwork counts
Resources
ResourceDescription
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, and a | b work) plus filters artist, 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, and has_image, combined with AND
  • Up to 12 rows per page (default 10), within the first 1,000 matches; sort is relevance, date_asc, or date_desc, and sort_applied reports popularity when relevance had no query text
  • facets adds the top 15 values for up to seven fields (artist counts every credited artist and rows carry artist_id); limit: 0 returns counts only

artic_get_artworks tool
  • ids takes 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; sections chooses from description and provenance (default), exhibition_history, publication_history, and catalogue
  • Records return in request order. missing_ids lists ids the museum doesn't have; deferred_ids lists 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_attribution appears whenever CC BY description text is returned

artic_search_artists tool
  • query (all name words must match) or ids, not both; ids takes up to 1,000 entries (comma strings up to 100,000 characters) and the first 25 are fetched. Query mode adds artists_only (default true), born_from / born_to, and up to 25 agents per page
  • Each agent carries artwork_count, up to three sample_works (the museum's highlights first), life years, and alt_names; ids mode reports missing_ids and deferred_ids to pass as ids in the next call
  • ulan_id is the Getty ULAN identifier when the museum records one; coverage is sparse, and it is distinct from the museum id used by other tools

artic_search_exhibitions tool
  • query, when (current, upcoming, past, or the default any), date_from / date_to (YYYY-MM-DD, matched by run overlap), and exact artwork_id / artist_id filters, 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
  • sort is relevance, start_desc, or start_asc, defaulting to relevance with a query and start_desc without; status is the museum's label and doesn't say whether a show is open (when does)
  • Rows carry dates, gallery, summary, web page, image, artist_ids, and the artworks shown, when the museum lists them

artic_search_audio_guide tool
  • query is 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), and transcript but no artwork id; every response carries license_text and source_citation (content is for noncommercial educational and personal use)

artic_lookup_vocabulary tool
  • vocabulary is one of department, artwork_type, style, subject, classification, material, technique, theme, place_of_origin, or gallery; optional contains substring (ignoring case and accents, so applique finds appliqué (technique)), public_domain_only, and up to 100 values (default 25)
  • Values come back most common first with artwork_count, in the exact form the artic_search_artworks filter of the same name accepts; filter_param names that filter

artic://artworks/{id} resource
  • { artwork, license_text, description_attribution?, notice? } as application/json, where artwork is the artic_get_artworks record with its default sections and related media
  • An unknown id fails as artwork_not_found; ids come from artic_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_display stays 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's license_text verbatim; description_attribution when CC BY text is returned; source_citation on audio-guide results
  • Partial results instead of failures: artic_get_artworks reports missing_ids and deferred_ids, and when a secondary lookup (related media, artist counts) fails, the primary records still return with a notice naming what is missing
  • Paging that names the next move: totalCount, has_more, next_page, and a notice for 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:

ContentTerms
Artwork metadata, artists, exhibitions, vocabulary terms, related mediaCC0
Artwork description textCC BY 4.0: credit the Art Institute of Chicago and cite the record's web_url
Artwork imagesReusable only for public-domain works (image.rights: "public_domain", CC0); other images need a rights check
Audio-guide contentNoncommercial 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, only artist_display prose.
  • 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 as artic_lookup_vocabulary lists them.
  • The API's firewall refuses any request whose text contains markup such as <script>; the call fails as request_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. Point any MCP client at it via Streamable HTTP:

{
  "mcpServers": {
    "art-institute-chicago-mcp-server": {
      "type": "streamable-http",
      "url": "https://art-institute-chicago.caseyjhand.com/mcp"
    }
  }
}

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": "you@example.com"
      }
    }
  }
}

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": "you@example.com"
      }
    }
  }
}

Or with Docker:

{
  "mcpServers": {
    "art-institute-chicago-mcp-server": {
      "type": "stdio",
      "command": "docker",
      "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "-e", "AIC_CONTACT=you@example.com", "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_CONTACT to an email or URL.
Installation
  1. Clone the repository:
git clone https://github.com/cyanheads/art-institute-chicago-mcp-server.git
  1. Navigate into the directory:
cd art-institute-chicago-mcp-server
  1. Install dependencies:
bun install
  1. Configure environment:
cp .env.example .env
# edit .env and set AIC_CONTACT

Configuration

VariableDescriptionDefault
AIC_CONTACTEmail 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_MINUTEOutbound 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_TYPETransport: stdio or http.stdio
MCP_HTTP_PORTHTTP server port.3010
MCP_SESSION_MODEHTTP session mode: stateless, stateful, or auto. .env.example and the Docker image set stateless.auto
MCP_AUTH_MODEAuthentication: none, jwt, or oauth.none
MCP_LOG_LEVELLog level (debug, info, warning, error, etc.).info
LOGS_DIRDirectory for log files (Node.js only).<app-root>/logs
STORAGE_PROVIDER_TYPEStorage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1.in-memory
OTEL_ENABLEDEnable 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

DirectoryPurpose
src/index.tscreateApp() entry point: registers the tools and resource, sets the server instructions, and starts and stops the API service.
src/configAIC_CONTACT and AIC_REQUESTS_PER_MINUTE parsing and validation with Zod.
src/mcp-server/toolsTool definitions (*.tool.ts) and the artwork output schema they share.
src/mcp-server/resourcesThe artic://artworks/{id} resource.
src/services/aicArt 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/catch in tool logic
  • Use ctx.log for logging, ctx.enrich for 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.

Frequently asked questions

What is io.github.cyanheads/art-institute-chicago-mcp-server?

Search the Art Institute of Chicago collection: artworks, artists, exhibitions, and audio guides.

How do I install io.github.cyanheads/art-institute-chicago-mcp-server?

It runs as a remote server. In Claude Code: claude mcp add --transport http art-institute-chicago-mcp-server 'https://art-institute-chicago.caseyjhand.com/mcp'. For other clients, paste the JSON configuration from this page into their MCP settings.

Does io.github.cyanheads/art-institute-chicago-mcp-server need an API key?

Its registry entry lists no required key or secret. Some features may still need an account with the service it connects to.

Is io.github.cyanheads/art-institute-chicago-mcp-server open source?

Yes. Its code is on GitHub under the Apache-2.0 license.

Who maintains io.github.cyanheads/art-institute-chicago-mcp-server?

It is published by cyanheads. AI.tn lists it but does not maintain or endorse it.

From the official MCP Registry. Listing is not an endorsement: review a server's code and permissions before giving it access to your data.