Back to Browse

Amira MCP Server

Developer ToolsLow Risk10.0MCP RegistryRemote
Free

Server data from the Official MCP Registry

Read-only access to AMIRA, the Africa Multiple research-data platform on Omeka S.

About

Read-only access to AMIRA, the Africa Multiple research-data platform on Omeka S.

Remote endpoints: streamable-http: https://data.africamultiple.uni-bayreuth.de/mcp

Security Report

10.0
Low Risk10.0Low Risk

Valid MCP server (4 strong, 2 medium validity signals). No known CVEs in dependencies. Imported from the Official MCP Registry. Trust signals: 4 highly-trusted packages.

37 tools verified · Open access · No issues found

Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.

Permissions Found in Source Code

Found by scanning the linked source code. This listing connects to a hosted endpoint, so none of this runs on your machine: it describes what the server software does where it is hosted.

file_system

Applies to the server that hosts this plugin, not to your machine.

HTTP Network Access

Connects to external APIs or services over the internet.

env_vars

Applies to the server that hosts this plugin, not to your machine.

How to Connect

Remote Plugin

No local installation needed. Your AI client connects to the remote endpoint directly.

Add this to your MCP configuration to connect:

{
  "mcpServers": {
    "io-github-am-digital-research-environment-amira-mcp-server": {
      "url": "https://data.africamultiple.uni-bayreuth.de/mcp"
    }
  }
}

Documentation

View on GitHub

From the project's GitHub README.

AMIRA MCP Server

CI Release Data refresh License: MIT MCP 2026-07-28

Read-only Model Context Protocol server for the Africa Multiple Interactive Research Atlas (AMIRA), the research-data platform of the Africa Multiple Cluster of Excellence at the University of Bayreuth. AMIRA is published on the cluster's public Omeka S site at data.africamultiple.uni-bayreuth.de.

AMIRA is built and maintained by the Cluster's Digital Research Environment (DRE), its digital infrastructure unit. The DRE designs, builds, and maintains the data systems that connect researchers across the Africa Multiple Research Centres (AMRCs) and partner institutions worldwide. Curation and description are joint efforts with partners at the AMRCs in Université Joseph Ki-Zerbo, Rhodes University, the University of Lagos, and Moi University. Bayreuth hosts the coordinating DRE infrastructure and metadata layer. Federal University of Bahia is a privileged partner, not an AMRC.

Storage stays distributed by default: data remains in its local repository, while Bayreuth holds the metadata layer that points to it. Research data becomes findable without being relocated. High-quality metadata makes researchers' data more discoverable and their work more visible to a wider community of peers.

This server exposes AMIRA's projects, thematic research sections, ~4,000 digitised research items, people, institutions, groups, collections, the cluster bibliography with searchable full text (extracted from the open-access PDFs) and its journals, podcast episodes with transcripts, and the cluster's YouTube videos with searchable transcripts — as 36 core tools an LLM can query, five research-workflow prompts, and resources for citable records and bulk exports. From one MCP interface, clients can move across records and the places, languages, and subjects that connect them.

Every record carries an amira_url — its public page on the Omeka S site (…/s/amira/item/<id>) — so findings can be cited as links back to the source. AMIRA focuses on the Cluster's research data. For news, events, and general information about the Africa Multiple Cluster of Excellence, visit africamultiple.uni-bayreuth.de.

Coverage checked 6 October 2026: 3,975 research items (1,419 with digitised media), 93 projects, 555 publications (60 with extracted full text), 87 journals, 43 podcast episodes, 140 videos and 3,085 subject authorities (646 Library of Congress headings). Counts are a dated snapshot; use get_collection_overview for what your running server actually holds. See the publication guide, the changelog for what each release changed, and the roadmap for further improvements.

How it gets its data — and why nothing else is needed

The server is self-contained and offline-first. It reads from two sources:

  1. A complete data snapshot bundled inside the .mcpb — crawled from the public Omeka S REST API at build time and transformed into compact typed records, so the server works fully offline with zero setup.
  2. (optional, on by default) the Omeka S API over HTTPS. At startup and daily, probes compare item and item-set modification signatures and totals. A changed signal triggers a complete crawl; a weekly forced crawl also catches vocabulary-only edits. A writer lock, immutable generations and an atomic pointer preserve the previous usable snapshot if refresh fails. The API origin and installation path partition the cache and must match the manifest before its records can be served under that site's citation URLs.

End users need no API key, no credentials, and no VPN — it's the same openly published data that powers the public site.

Tools

Call get_collection_overview first to scope the data, then drill in.

ToolPurpose
get_collection_overviewCounts and breakdowns across the whole collection + snapshot freshness
search_research_itemsFind items by keyword (every word must match; quote a phrase), subject, location (hierarchy-aware), country (exact, with aliases such as Côte d'Ivoire = Ivory Coast), contributor, project, section, university, resource type, format/genre, language, year, has_media, added_since / modified_since, mentions_country (ILAM articles whose full text names a country; see below); `export=csv
get_research_itemFull metadata for one item (by Omeka id or typed id): typed dates, roles with the affiliation at the time, places with their region/country chain, provenance as linked institutions, typed identifiers, collections, related items, media files and the IIIF manifest — plus a generated citation and a BibTeX/RIS/CSL-JSON export (citation_format). ILAM African Music articles add closest_in_content and full_text_map (see below)
search_projects / get_projectProjects by keyword or acronym, university, section, PI, member, funder — detail with item breakdown, top subjects and sample items
list_research_sections / get_research_sectionThematic sections with funding phases (AM 1.0 / AM 2.0), PIs, counts, projects; detail by name or id
search_persons / get_personPeople (either name order works) — profile with GND and other authority identifiers, project and research-section roles, items, publications, podcasts, videos and top collaborators, with a coverage_note on how to read the counts. A name that matches nobody returns near-miss suggestions ("Rudigr Seeman" → Seesemann, Rüdiger); get_person lists them in its not_found error
list_institutions / get_institution / list_cluster_partners / list_groupsOrganisations (institutions, Africa Multiple partner categories, and research groups) by name, acronym or id, their projects, people and items
list_subjectsSubject headings ranked by item frequency, each marked as a Library of Congress heading (with its id.loc.gov URI) or a free tag (`vocabulary=lcsh
list_locationsEvery place — countries and the places under them in one flat list (hierarchy rolled up) — ranked by item count, with coordinates and Wikidata ids. Each row's level is country or place (a city, region or site, with its country); filter by country, name, near (radius) or bbox
list_collectionsCollections ranked by research-item count. A collection is a research project: cited by the project page, with the item set(s) filing it (item_sets, collection code) and IIIF collection links — pair with the collection filter, which takes a project id, item-set id or code
list_categoriesFacet values: formats/genres, languages, resource types
list_yearsDate histogram of research items by year or decade — coverage over time, most-covered year/decade
search_publications / get_publicationFilter the bibliography by author, subject, language, type, venue, year, date added and full-text availability; search abstracts in every language and extracted PDF text. Detail adds every abstract with its language, linked authorities, conference/extent/access/thesis metadata. citation_format selects BibTeX/RIS/CSL-JSON per page; export links the whole result as one file
list_publication_facetsCounts by type, year, language, subject, author/editor or venue across the complete filtered bibliography; ranked and paginated
list_journalsThe journals the cluster publishes in, ranked by publication count, with ISSN and country — pair with the venue filter
find_relatedCross-entity discovery: pivot from a subject/place/person/project to co-occurring entities (incl. publications), from an ILAM African Music article (entity_type=research_item) to its closest articles by full text, or from any embedded record (entity_type=record) to its closest records by meaning and what they share
search_podcasts / get_podcastCluster podcast episodes with searchable transcripts, filterable by language; detail gives the duration, audio file and the model that generated the transcript; transcript text is opt-in
search_videos / get_videoThe cluster's YouTube videos — full-text search over transcripts (match snippets; thumbnail; transcript opt-in on detail)
resolve_entityResolve names or typed IDs (either vocabulary: item:9902 = research_item:9902); return separate candidates for homonyms and unreconciled literals. A query that resolves to nothing, untyped or with type=person, returns near-miss person suggestions
get_entity_graphBounded one-hop graph with distinct explicit links/co-occurrences and paginated cited evidence
get_text_passagesFind passages in selected publication/video/podcast records, with exact original-text offsets
compare_collectionsCompare 2–4 projects or collections using common filters, denominators and missingness
get_data_qualitySnapshot coverage, missing metadata and unresolved-reference counts
get_snapshot_changesPage added, updated or deleted records across retained snapshots from the same instance
get_fulltext_mapThe ILAM journal African Music read through its private full text, from public derived counts: content groups (AI-labelled) with their articles, the countries articles name, and word rates per year for up to six words
get_similar_itemsRecords whose catalogue descriptions read most alike, across research items, publications, videos, podcasts, projects and sections, with cosine scores and amira_urls, each saying what the pair also shares (shared subjects, places, people, project) or basis: "wording_only"; types narrows the result, and a record without neighbours says why (low_signal, not_in_release). The detail tools take include_similar for the same, five at a time. See Semantic neighbours
get_semantic_mapThe collection as unnamed groups of records close in meaning: each group's size, type mix, top subjects, countries and projects, and the records nearest its centre; group lists one group's records, id finds the group a record falls in

Prompts and resources

Five prompts package multi-step research workflows. Hosts show them as slash commands; their arguments autocomplete from the snapshot's own authority names, and each restates the citation rules. They are listed through prompts/list, so they add nothing to the per-turn tool payload.

PromptArgumentsWorkflow
literature_reviewtopic, year_from?, year_to?Facets, publications, full-text passages and related research data on a topic
project_dossierprojectTeam, holdings, periods, places and connections of one project
person_profilenameDisambiguation, profile, graph and publication list of one person, presented as AMIRA's records rather than an assessment of the person
place_reportplaceItems, projects, subjects and periods for a country, city or region
transcript_evidencequeryQuoted passages from transcripts and open-access full texts

Resources expose data outside the tool calls:

URIContent
amira://record/{kind}/{id}Any record as citable JSON — the same projection as its get_* tool; kinds follow resolve_entity ids (research_item, publication, person, location, subject, …)
amira://export/{corpus}/{format}/{query}A filtered export (CSV/JSONL for research items; CSV/JSONL/BibTeX/RIS/CSL-JSON for publications). The search tools return these links as resource_links when called with export
amira://datasetA schema.org Dataset description of the served snapshot: counts, coverage, licence mix, funder, how to cite it and the vector space behind get_similar_items (amira:semantic_space)

Profiles and research workflows

AMIRA_TOOL_PROFILE=full is the default: 36 core tools, or 38 over HTTP. research, discovery and visualization expose smaller documented subsets (23/12/14 core tools respectively; HTTP adds search and fetch). Profiles are fixed at startup; they reduce discovery cost, not access permissions. Tools outside a profile are removed, and so are the apps that render them. See src/toolProfiles.ts for the exact lists.

Typed ids work everywhere in either vocabulary (item: / research_item:, pub: / publication:, section:, person:, organisation:, project:, video:, podcast:), including the id of every get_* tool. Paged results stop early, with response_limited: true and a correct next_offset, when a page would exceed 40,000 characters — Claude Code moves larger tool results out of the conversation. Error results carry the error as JSON text only. Structured content travels only where a client reads it: on the tools with an output schema and on those that render an app; elsewhere the result is its compact JSON text alone, instead of the same bytes twice. Control and private-use characters are scrubbed from every result, and a call that has not answered within 60 seconds returns a deadline_exceeded error.

Use resolve_entity before graph traversal; pass its typed id unchanged to get_entity_graph. Graph edges count distinct source records, separate each corpus, and distinguish catalogue links from co-occurrence. types (for example ["person"]) keeps only neighbours of those types and applies before the bounds; neighbour_types counts every neighbour by type either way. Neither co-occurrence nor a shared subject establishes collaboration. Follow an edge with edge_id and the returned snapshot_id for stable evidence paging. Graphs cap at 100 nodes, 200 edges and 42,000 JSON bytes, so check truncated.

get_text_passages takes 1–10 publication:ID (or pub:ID), video:ID or podcast:ID identifiers. It returns at most 20 passages per page, original UTF-16 offsets and source citations; every match is reachable through next_offset (scanned_matches_capped reports a document with more than 10,000 matches). These are extracted-text offsets, not PDF page numbers or audio timestamps. get_snapshot_changes requires two local same-source generations; it reports available history instead of inventing a diff.

The ILAM full-text map

The full text of ILAM's journal African Music (item set 27724, about 1,000 articles from 1954 on) is private in Omeka, so the public API this server crawls never carries it, and nothing here reads or quotes it. DRE Visualizations computed a map from it offline and committed only derived, public files: ids, map coordinates, content groups, nearest-neighbour scores, per-article country counts and aggregate word counts per year. The crawl fetches two of them at a pinned release tag (v2.48.0, FULLTEXT_SOURCE in src/fulltextMaps.ts), validates their shape, keeps only the declared fields, and stores them beside the snapshot as fulltext_maps.json. If GitHub is unreachable, the snapshot ships without the map and its surfaces answer fulltext_map_unavailable. Every result is filtered to research items present in the snapshot and filed in the item set, and cites them by amira_url.

SurfaceWhat it adds
get_research_itemFor an article on the map: closest_in_content (its 8 closest articles by full-text embedding, google/embeddinggemma-2, with cosine scores) and full_text_map (its content group, the countries its text names). An ILAM article off the map says so
find_related entity_type=research_itemThe same neighbours, with the subjects, people, countries and formats they share
mentions_country filterArticles whose full text names a country at least three times; each row carries country_mentions. Shared by search_research_items, list_years, list_locations, compare_collections and exports
get_fulltext_mapThe groups with sample articles, the countries named across the journal, a group's articles (group), or word rates per 10,000 words per year (words, up to six)

Limits, stated in every response: group names are AI-generated (Claude Opus 5.5, from the titles of each group's articles) and experimental; judge a group by its articles. Neighbour scores sit in a narrow band (about 0.90 to 0.97), so ranks say more than raw values. Countries come from a gazetteer, with three mentions minimum and journal addresses excluded; a mention is not a subject. Word counts cover words found in at least ten articles, single word forms without stemming; years with few words are marked sparse. 657 articles are on the map: articles without full text, and those excluded as shared section text, journal furniture or under 300 words, are not. These surfaces need the descriptive exposure level. Searching passages of the text itself is a separate decision, with ILAM and Rhodes University, and is not offered.

Example questions it can answer

You ask…The model uses…
"What does the collection hold on Islam?"list_subjects keyword=Islam → search_research_items subject=Islam
"Show me all the French-language items"search_research_items language=French (codes fr/fra/legacy fre work too)
"What has Ulli Beier contributed?"get_person name="Ulli Beier" (resolves to 'Beier, Ulli')
"Which items come from Nigeria?"search_research_items location=Nigeria (Lagos items count too — location walks the place hierarchy) or country=Nigeria (exact: Niger stays out)
"What does AMIRA hold from Côte d'Ivoire?"search_research_items country="Côte d'Ivoire" (the authority stores "Ivory Coast"; common aliases resolve)
"What has been added since September?"search_research_items added_since=2026-09-01 (or search_publications added_since=…)
"Which African Music articles write about Ghana, and when?"search_research_items mentions_country=Ghana → list_years filters={mentions_country: "Ghana"}
"How did African Music use the word kwaito over time?"get_fulltext_map words=["kwaito"] (rates per 10,000 words; sparse years flagged)
"Which articles are closest in content to this ILAM article?"get_research_item (closest_in_content) or find_related entity_type=research_item
"Which Lagos items have digitised images?"search_research_items location=Lagos has_media=true → get_research_item for the files and IIIF manifest
"Give me all Islam-related items as a spreadsheet"search_research_items subject=Islam export=csv → read the returned amira://export/… link
"What audio recordings are in the ILAM collection?"search_research_items project_id=37700 resource_type=Audio
"In which talks does anyone discuss decoloniality?"search_videos keyword=decolonial (matches inside transcripts, flagged matched_in)
"Which cluster publications discuss migration control — and what do they actually say?"search_publications keyword="migration control" (matches inside the extracted full text, flagged matched_in) → get_publication include_fulltext=true
"How many French-language publications are there, and of which types?"list_publication_facets facet=type language=fr → search_publications language=fr
"Export the French-language bibliography for my reference manager"search_publications language=fr citation_format=ris → follow next_offset; combine the complete ris records
"Which journals does the cluster publish in?"list_journals (ranked by publication count)
"What themes travel with Architecture across projects?"find_related entity_type=subject value=Architecture
"When was this photograph taken?"get_research_item → typed dates (created/collected/issued/…)
"Give me a citation for this item — and the BibTeX"get_research_item → generated_citation + bibtex (citation_format=ris/csl-json for a reference manager)
"Which decade does the collection cover most?"list_years bucket=decade sort=count

Companion skill

A research-workflow skill ships in .claude/skills/amira-mcp/: cluster context, a query workflow, a tool-by-task map, the citation discipline, and coverage caveats. Install it either way:

  • Download amira-mcp-skill.zip from the releases page and unzip it into your Claude skills directory (e.g. ~/.claude/skills/) — it expands to ~/.claude/skills/amira-mcp/.
  • Or copy the .claude/skills/amira-mcp/ folder from this repo there.

…or let the server hand it over (Skills over MCP)

Official extension; host support varies. SEP-2640 is now Final. The server follows the published Skills extension, including SHA-256 digests and byte sizes for every file. The zip above remains available for clients without extension support. The research tools and citation contract work either way.

The server also serves that same skill over the connection, following the SEP-2640 extension (io.modelcontextprotocol/skills). Nothing to download and nothing to keep in sync: a host that implements the extension discovers the skill on connect, and the copy it gets is the one built into the running server rather than a zip that may predate the tool surface it describes. It is the only route that reaches the remote HTTP surface — ChatGPT, Claude.ai connectors and the APIs have no local skills directory.

MethodWhat it returns
skills/listThe catalog: skill://amira-mcp/SKILL.md, its frontmatter, and a SHA-256 digest and byte size for every file
skills/getOne skill by URI, to refresh digests without re-listing
resources/readAny single file — SKILL.md or a reference — read on demand
resources/directory/readOne directory level at a time (skill://amira-mcp, skill://amira-mcp/references)

Cost when unused: near zero. Skill text is never injected into any tool description, so the tools/list payload — the part re-sent every turn — is byte-identical whether or not a host supports the extension. The instructions add one sentence naming skill://amira-mcp/SKILL.md, and resources/list names every skill file with a title and a one-line summary, so a host without the extension (Claude Desktop, Claude Code) can still find the workflow and its three references and read them with resources/read. Disclosure timing stays a host decision; the reference files load only when something actually reads them.

Set AMIRA_SKILLS=0 to drop the capability and the three methods entirely.

Install (end users)

Download amira-mcp-server.mcpb from the releases page and double-click it. Claude Desktop shows an install dialog; click Install. No further configuration is required. The latest tagged release carries the current data; the rolling data-latest pre-release always tracks the freshest site snapshot.

Use it from ChatGPT, the API, or any remote client

The .mcpb is the local, offline option for Claude Desktop. The same server can also run as a remote Streamable HTTP endpoint — one HTTPS URL that ChatGPT, Claude (web + desktop remote connectors), the OpenAI and Anthropic APIs, Cursor, VS Code and other clients connect to by pasting a URL (no download, always-fresh data). The remote surface serves the same 36 tools plus the OpenAI-compatible search / fetch tools for ChatGPT research integrations (36 total). Access is unauthenticated — the data is public and read-only.

search takes plain keywords (matched term-by-term, not as an exact phrase), with optional limit and types to keep the result set tight, and returns ranked hits across research items, the bibliography (reaching into publication full text), podcasts, videos, projects and research sections. The url on search results and fetch documents is always the AMIRA/Omeka public record page; DOI, YouTube/watch, and podcast/listen URLs are kept as secondary metadata/text links. fetch returns one record's full text by the id search hands back — for videos and podcasts the transcript is omitted by default (metadata + description only, since a full one can run to tens of thousands of characters), and include_transcript=true pulls it in — paged with transcript_offset / transcript_max_chars (the same names get_video / get_podcast use). Publication full text works the same way (include_fulltext=true, paged with fulltext_offset / fulltext_max_chars, matching get_publication), and max_chars caps the whole text body. The appended window is sized against what max_chars leaves after the metadata header, so *_returned_chars is exactly what landed in text and the next page starts at offset + returned_chars with no gap.

npm run build && npm run start:http     # → http://localhost:8787/mcp
# or, self-contained, via Docker:
docker build -t amira-mcp . && docker run -p 8787:8787 amira-mcp

Endpoints: POST /mcp (the MCP endpoint), GET /healthz and GET /metrics (request count and p50/p95 latency of the last 256 /mcp requests, rate-limit buckets, refresh state). Bind with PORT / HOST. Every release also publishes the tested image, with build provenance, as ghcr.io/am-digital-research-environment/amira-mcp-server:<version> (and :latest), so a deployment updates by pulling a tag.

  • ChatGPT → enable Developer mode under Settings → Security and login, then add your server URL from ChatGPT Plugins. Research integrations use search + fetch; see the current OpenAI setup guide.

  • Claude (web or desktop) → Settings → Connectors → Add custom connector → https://<your-host>/mcp.

  • OpenAI API (Responses) — point the mcp tool at the endpoint:

    { "type": "mcp", "server_label": "amira",
      "server_url": "https://<your-host>/mcp",
      "allowed_tools": ["search", "fetch"], "require_approval": "never" }
    

Deploy (self-hosted, e.g. alongside the amira site)

The multi-stage Dockerfile crawls a fresh snapshot at build time and runs the self-contained bundle (no node_modules at runtime). On a Linux host you can equally run it under systemd behind a reverse proxy:

# /etc/systemd/system/amira-mcp.service
[Service]
ExecStart=/usr/bin/node /opt/amira-mcp/server/http.js
Environment=PORT=8787 HOST=127.0.0.1 AMIRA_LIVE_REFRESH=true
Restart=always
User=www-data
location /mcp { proxy_pass http://127.0.0.1:8787/mcp; proxy_buffering off; client_max_body_size 64k; }

proxy_buffering off keeps the Streamable-HTTP/SSE responses flowing. The refresh uses AMIRA_SITE_BASE (the public HTTPS API by default), even when co-located with Omeka. It performs a full crawl when changes are detected; co-location does not eliminate API load. The server caps request bodies at 64 KiB and rate-limit state at 10,000 clients. Configure connection, request-rate and concurrency limits at the reverse proxy for public deployments; the in-process limiter is a courtesy control. Shutdown cancels refresh work and gives HTTP connections a ten-second drain window.

For a build that consumes a previously reviewed data/ snapshot without calling Omeka during the build:

docker build --build-arg SNAPSHOT_STAGE=bundled -t amira-mcp .

The default SNAPSHOT_STAGE=fetch still crawls fresh data. Both stages validate the snapshot. The image pins its base digest; npm ci uses the committed lockfile.

Develop / rebuild

npm ci
npm run fetch-data    # crawl the public Omeka API -> ./data snapshot (~1 min)
npm run typecheck     # tsc --noEmit
npm run build         # esbuild -> server/{index,http,fetchCli,lib}.js
npm test              # unit tests: transform fixtures, folding, snapshot + store lifecycle,
                      # and the full tool layer against a fixture snapshot via
                      # InMemoryTransport (offline)
npm run test:live     # integration tests against the live API (network)
npm run smoke         # test/smoke/stdio.mjs: spawn the stdio server, exercise every tool offline
npm run smoke:http    # test/smoke/http.mjs: spawn the HTTP server on a free port: search/fetch +
                      # parity, CORS preflight for both protocol revisions, the rate limiter
npm run weigh         # token budget report (needs ./data — run fetch-data first)
npm run benchmark     # offline latency baseline against ./data (30 warm samples per query)
npm run lint          # Biome, lint rules only (biome.json)
npm run evaluate-retrieval -- --judgments judgments.json --output report.json
                      # precision, recall, nDCG and MRR at k against researcher-authored judgments

Token budgets

Context is the scarcest resource a client has, so both halves of what this server spends are measured and gated (scripts/weigh.mjs, estimating tokens as bytes/4 — comparable to itself, not to a billing statement):

whatbudgetdrift
Surfacethe tools/list payload, re-sent every turn14,000 tok per full-profile transportfails over 3 %
Responseseach tool called at its maximum limit20,000 tok and < 45,000 characters per callwarns over 10 %

No tool description is derived from the snapshot, so the surface is a pure function of the code: test/unit/budget.test.mjs gates it offline against no data at all, and asserts that the empty-store measurement still matches the baseline recorded against the full snapshot. Response weight needs real data, so npm run weigh -- --check runs in the smoke job instead. Drift there is only a warning — a routine npm run fetch-data must not red the build — while the absolute cap (Claude Code truncates tool results at 25,000 tokens) is fatal, as is any probe whose call failed, since a structured refusal is ~60 tokens and would otherwise sail under every ceiling.

Version 1.18 deliberately raises the full-profile ceiling to 14,000 estimated tokens to accommodate six tools and useful output schemas. Smaller profile gates are 10,100 (research, raised from 10,000 for resolve_entity's near-miss fields), 6,500 (discovery) and 9,500 (visualization). The guidance-off surfaces of the evaluation ablation are measured and gated like the shipped ones. The committed baseline records the exact surface size, text bytes, serialized wire bytes (text and structured content both count) and the snapshot it was measured on. Every response must also stay under 45,000 characters: Claude Code stores any tool result over 50,000 characters in a file instead of the conversation. Paged results and the graph are byte-bounded to fit.

When a change legitimately grows either number:

npm run weigh -- --update    # rewrites test/token-baseline.json

Commit the diff. That is the point of the file: the cost of a new tool or a richer result summary lands in code review as a number instead of arriving unnoticed.

Pack the extension:

npm run prepack-mcpb                       # clean + typecheck + build + test + smoke
npm run validate:manifest
npm run pack-mcpb                          # -> amira-mcp-server.mcpb

scripts/census.mjs re-runs the property census behind the field mapping (scripts/census-report.json) — rerun and diff it if the instance's templates change. The design decisions behind it are in ROADMAP.md; CHANGELOG.md records each release.

Continuous delivery

Three GitHub Actions cover pull requests and distribution (the two distribution workflows crawl the public API — no credentials):

  • CI (.github/workflows/ci.yml) — on pull requests and pushes to main: type-checks and lints once, then runs the offline unit suite (including the tool-surface token budget) on Node.js 22.0.0, 22, 24 and 26 on Linux and Node.js 24 on Windows, then exercises both MCP transports, weighs every tool's response at its maximum limit, and validates the MCPB manifest. The complete dependency audit fails on high or critical advisories. The packed .mcpb is then unpacked on Windows and macOS and smoke-tested there, so CI runs the bytes users install. A separate container job builds an offline fixture image and checks health. Windows CI also packs a fixture extension and checks its file list. A non-blocking job runs the official MCP conformance suite against the HTTP server. Actions are pinned to commit SHAs; every job has a timeout.

  • Release (.github/workflows/release.yml) — on a pushed v* tag: refuses a version already published on GitHub or the MCP Registry (released bytes are never replaced; fix a release with a new version), checks the tag against package.json, crawls a fresh snapshot, runs unit, live and smoke tests, packs the .mcpb, zips the companion skill (amira-mcp-skill.zip) and builds the image in a read-only job that records the SHA-256 of each. The publish job verifies those bytes, repeats the guard, attests their build provenance, and creates the release as a draft with the version's CHANGELOG.md section as its notes before making it public. Non-blocking jobs push the tested image to GHCR with an attestation and publish server.json to the MCP Registry with a pinned, checksum-verified publisher.

  • Refresh data snapshot (.github/workflows/refresh-data.yml) — weekly and on demand; rebuilds only when the transformed snapshot content hash changes, including item-set and vocabulary changes, updating the rolling data-latest pre-release.

Publishing from Actions requires a writable token. If the organization disables write permissions for the default workflow token, either (a) enable Read and write permissions under Organization → Settings → Actions → General → Workflow permissions, or (b) add a repo/org secret RELEASE_TOKEN (a PAT or GitHub App token with contents: write). The workflows use secrets.RELEASE_TOKEN when present and fall back to the default token.

Semantic neighbours

get_similar_items finds records whose catalogue descriptions read most alike, across research items, publications, videos, podcasts, projects and research sections. The vectors come from the versioned DRE Visualizations releases semantic-embeddings-v1-<build>: google/embeddinggemma-2, 768 dimensions, L2-normalised, built offline from the public API only (title, abstract or description, subjects and places; never full text).

The crawl downloads the newest release (or the one AMIRA_SEMANTIC_RELEASE pins), verifies the model, dimensions, normalisation, row count, byte length, SHA-256 and row norms against its manifest, and refuses any mismatch, so two vector spaces never mix. It then computes, for every record present in the snapshot, its ten closest records of each type, and stores those (semantic_neighbours.json), never the ~15 MB of vectors: no model and no vector runs at request time. Records absent from the snapshot are dropped before ranking, and lists are filtered again at load, so a neighbour is always a record the snapshot can cite. When no release verifies, the crawl keeps the neighbours already served. A new release is picked up by the next crawl, which a change on the Omeka site or the weekly forced crawl (AMIRA_FULL_REFRESH_HOURS) triggers: a release alone does not start one. get_data_quality reports the release and its coverage per type: records with neighbours, low-signal records (too little own text, excluded by design) and records the build predates.

Scores are cosine similarities of descriptions: similar wording is not evidence of a documented relation. So every neighbour is explained from the catalogue links: shared names up to three subjects, places (a place shares its country), people or the project the pair also shares (research sections are too broad to count), with shared_counts when there are more, and basis: "wording_only" marks a pair that shares none of them. get_research_item, get_publication, get_podcast, get_video and get_project add the same five neighbours as a similar block with include_similar: true; a record without neighbours carries the reason there instead of an error. find_related entity_type=record pivots on the same neighbours: the projects, sections, subjects, people, countries and formats that the seed's 20 closest records share, counted per corpus.

The same vectors give get_semantic_map: the crawl groups the records by seeded spherical k-means (about a hundred per group, at most 30 groups, low- signal records left out) and stores each group's member ids, nearest the centre first. The groups are unnamed on purpose. The tool describes each one by its type mix and its most frequent subjects, countries and projects, with the records nearest its centre, so a group is characterised from its contents rather than given an invented label. Group ids are renumbered per build (1 = the largest).

Configuration (environment / extension settings)

Documentation truncated — see the full README on GitHub.

Reviews

No reviews yet

Be the first to review this server!