Skip to main content
Glama
NMAH Military Collection MCP

NMAH Military Collection MCP

by yixuan-liu

NMAH Military Collection MCP

An independent, read-only FastMCP server for searching the Armed Forces History collections at the National Museum of American History (NMAH) through Smithsonian Open Access. The Playground runs on loopback for local use.

Try the Playground

The Playground lets you try all three MCP tools in your browser. From the project root, run this command with Python 3.13 or newer and uv:

uv run --locked python playground/server.py

Open the loopback URL printed in the terminal. If you have no key configured, select API key required, enter your Smithsonian Open Access key, and choose Verify and apply. You can view the tools and their schemas before entering a key. Get a key from the Smithsonian EDAN signup form.

To start with your existing ignored .env file, run:

uv run --locked --env-file .env python playground/server.py

The Playground reads SMITHSONIAN_API_KEY from its startup environment. A key verified in the browser overrides that default for this local Playground process, including its other tabs. Use project default clears the override. Browser-entered keys stay in process memory and are cleared when the Playground stops. Press Ctrl+C in the terminal to stop it.

To try all three tools, search for nmah_446333, select Inspect on the matching record, then use Get metadata and Get image assets to check the catalog details, image sources, and usage rights.

Related MCP server: Smithsonian MCP Server

Setup and startup

Use Python 3.13 or newer. The locked sync installs the package from src/.

uv sync --locked
uv run --locked fastmcp inspect src/nmah_military_collection_mcp/server.py --format mcp

Get an API key from the Smithsonian EDAN signup form. For a new local setup, copy .env.example to .env and fill in SMITHSONIAN_API_KEY. If you already have a .env, use that file.

cp -n .env.example .env
uv run --locked --env-file .env python -m nmah_military_collection_mcp

Keep credentials in the ignored .env or supply SMITHSONIAN_API_KEY through the MCP client's environment. The server sends the key to the official API through the X-Api-Key header.

Connect an MCP client

After uv sync --locked, keep your key in the clone's ignored .env. In the examples below, replace both paths with absolute paths to your copy of uv and your clone. Merge the server entry into your existing client configuration.

Codex (~/.codex/config.toml)

[mcp_servers.nmah-military-collection]
command = "/absolute/path/to/uv"
args = ["run", "--locked", "--directory", "/absolute/path/to/nmah-military-collection-mcp", "--env-file", ".env", "python", "-m", "nmah_military_collection_mcp"]

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "nmah-military-collection": {
      "command": "/absolute/path/to/uv",
      "args": [
        "run",
        "--locked",
        "--directory",
        "/absolute/path/to/nmah-military-collection-mcp",
        "--env-file",
        ".env",
        "python",
        "-m",
        "nmah_military_collection_mcp"
      ]
    }
  }
}

Restart the client. In Codex, use codex mcp list to confirm registration. Then check tool discovery and ask the client to find nmah_446333 with search_nmah_military_objects.

Logging

Set LOG_LEVEL=INFO in the root .env to control application logs for both the MCP server and the Playground. Use either startup command with --env-file .env, and restart after changing the value. If an MCP client starts the server, use that same launch option or supply LOG_LEVEL in the client's environment. The Playground forwards the setting to its MCP subprocess.

INFO is the default for unset, blank, or invalid values. Use DEBUG for upstream request timing and pagination details, or WARNING, ERROR, or CRITICAL for quieter output. Application logs go to stderr. MCP stdout carries protocol messages. Set FASTMCP_LOG_LEVEL separately to change FastMCP framework logging. Application logs omit credentials, request headers, search text, full request URLs, and response bodies.

Tools

The server exposes three read-only tools.

  • search_nmah_military_objects

  • get_nmah_military_object_metadata

  • get_nmah_military_object_image_assets

Search objects

Parameter

Default

Behavior

query

Empty string

Keyword expression. Empty or whitespace-only input becomes q=*.

branch

null

Army, Navy, Air Force, Marine Corps, or Coast Guard.

rows

10

Page size from 1 to 50.

offset

0

Non-negative starting position, passed as API start.

date

null

One decade label from 1770s through 2000s.

has_images

false

When true, adds online_media_type:Images; image URLs and CC0 rights are not guaranteed.

{
  "query": "cap",
  "branch": "Army",
  "date": "1910s",
  "has_images": true,
  "rows": 10,
  "offset": 0
}

Object names, models, people, and other descriptive terms go in query. The fixed fqs include unit_code:NMAH, type:edanmdm, and the escaped Military and Society: Armed Forces History set-name prefix. Filters are sent as a JSON array encoded into the API's fqs parameter. Sorting uses id.

Branch matching checks title and object_type, ignoring case. The aliases USA, USN, USAF, USAAF, USMC, USCG, WAC, WAAC, USNR, U.S.N., U.S.C.G., U.S.M.C., and Marine require boundaries at field edges or characters outside ASCII letters, digits, and underscore. Dots in aliases are matched literally. Other aliases use substring matching. Army also includes Military Academy. Navy also includes Naval Academy, Midshipman, and Naval Reserve. Air Force also includes Army Air Forces and Air Corps. These are overlapping research groups. Preserve historical names and verify attribution against the original record.

Date filtering matches the catalog's indexed decade labels. A record can have multiple labels, and the indexed date may describe an association rather than manufacture. Image indexing can exist without an image URL in the API response.

Retrieve object metadata

Call get_nmah_military_object_metadata with a bare NMAH record ID:

{
  "record_id": "nmah_463569"
}

Use content.descriptiveNonRepeating.record_ID from a search result. This required string accepts nmah_ followed by digits (maximum 64 characters), not the top-level row ID, a catalog number, or a URL.

The tool requests /content/edanmdm%3A{record_id} with header authentication. It checks the returned record ID, NMAH unit codes, edanmdm type, and an Armed Forces History set-name prefix before returning the entire API response record unchanged. This includes the content metadata and all other record fields; the API transport envelope is not included. Out-of-scope, missing, or unverifiable records return a tool error. No image filter is applied. Image URLs may be absent; no IIIF fallback, summary, or rights inference is added.

List image assets

Call get_nmah_military_object_image_assets with a bare record_id, such as nmah_446333 or nmah_463569. The tool verifies the same collection scope as metadata retrieval. It lists high-resolution Open Access JPEG and TIFF resources first and checks the public Smithsonian IIIF Presentation 2 manifest for matching or additional views. It groups resources by media ID when one can be established. Dimensions describe the listed resource and come from its source.

The media_access field preserves the individual image's Open Access usage.access value or null when unavailable. The usage_label is CC0 only when the image's own access value is CC0. All other values display Usage Conditions Apply. The manifest license URL is reported separately.

Status

Meaning

resolved

Source checks completed and found at least one resource.

partial

A source could not be checked fully. The tool returns any resources already found and explains the gap in warnings.

no_assets

Source checks completed without finding a supported resource.

For local downloads, pass the same record ID to the Smithsonian Collections Skill. Its downloader rechecks the sources, saves the selected files, and records their decoded dimensions and SHA-256 hashes.

Results and pagination

The search response contains total_count, returned, offset, next_offset, the applied query and filters, selected options, candidate aliases, and raw API records in objects. Candidate aliases describe the search configuration.

Keep the same search arguments and use next_offset for the next call when it is set. If next_offset is null, compare offset + returned with total_count. A value at or beyond total_count indicates the end of the reported results. If returned is zero and offset < total_count, treat the empty page as an upstream anomaly and the results as incomplete. Offset pagination reflects the current upstream index.

Architecture

The installed package lives under src/nmah_military_collection_mcp/. The module launcher calls the packaged server.py, which also exposes the application for FastMCP Inspect. The client configuration starts it over stdio.

Module

Responsibility

app.py, context.py

Register tools and manage one shared HTTP client per server lifespan.

tools.py

Define the MCP interface and orchestrate search and metadata retrieval.

query.py, scope.py

Build the search request and verify collection membership on retrieved records.

api_client.py, config.py

Read credentials on demand, send requests, and validate response envelopes.

models.py, errors.py

Share search types and report failures across modules.

Tool discovery works without an API key.

Verification

Run the local checks from the project root after uv sync --locked.

uv run --locked ruff check src tests playground
uv run --locked ruff format --check src tests playground
uv run --locked python -m unittest discover -s tests
uv run --locked python tests/smoke.py

Ruff checks Python files under src/, tests/, and playground/. To apply safe lint fixes, including import sorting, and format the files, run:

uv run --locked ruff check --fix src tests playground
uv run --locked ruff format src tests playground

Run the live comparison separately with a Smithsonian API key:

uv run --locked --env-file .env python tests/smoke.py --live

The unit tests check alias matching, HTTP requests with an injected mock transport, connection cleanup, and metadata scope and failure handling. The metadata fixture is a copy of the saved Smithsonian content response. The stdio smoke check verifies discovery, schemas, instructions, and input validation. Live mode compares search rows and counts and the complete metadata record with direct Open Access API responses. It also checks the image tool against an Open Access JPEG case and an IIIF fallback case.

Interface migration

Use search_nmah_military_objects for collection search. Replace the legacy online_visual_material argument with has_images. Search results include raw API records. Restart the MCP server and refresh tool discovery after upgrading.

Documentation

License

Original project code and documentation are available under the MIT License. See third-party notices for Swagger UI and Smithsonian materials.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      B
      maintenance
      Provides AI assistants with access to search, explore, and analyze over 3 million collection objects from the Smithsonian Institution's museums. Enables finding objects currently on exhibit, retrieving detailed metadata, high-resolution images, and 3D models from America's national museums.
      5
      84 npm
      10
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables searching and exploring millions of items from the Smithsonian Institution's collections including artifacts, artworks, specimens, photographs, and more, using the Smithsonian Open Access API.
      3
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables searching and retrieving detailed information about items from the Smithsonian Open Access collection, including metadata and images.
      273 npm
      MIT