Skip to main content
Glama
indico-mcp

indico-mcp

by cohm

indico-mcp

An MCP (Model Context Protocol) server for the Indico meeting agenda system, letting AI agents search event categories, browse agendas, and extract contribution and session details.

Works with any Indico instance — configure multiple instances simultaneously and switch between them per tool call.

Tools

Tool

Description

list_instances

List configured instance names and the current default instance

search_categories

Find categories by name; returns ID, breadcrumb path, and event count

find_events_by_title

Search event titles across the whole instance; each result includes category_id — useful for discovering which category a meeting series belongs to

browse_category

List direct subcategories of a category by ID; works without the REST API

search_category_events

List events in a category, filtered by date range and keyword

get_category_contributions

All contributions from every event in a category within a date range, in a single API call

get_event_details

Full event metadata including all contributions

get_event_contributions

Paginated contributions (items + pagination): speakers, abstract, duration, track, session

get_event_sessions

Session structure with nested contributions (full agenda view)

search_events_by_keyword

Full-text search across events

list_category_info

Category name, description, and direct subcategories with names

list_event_attachments

Paginated attachments (items + pagination) for an event or contribution

download_attachment

Download an attachment file to disk given its download URL

list_room_locations

List known room booking sites for this instance

discover_rooms

Scan reservation history to build a local room catalogue

search_rooms

Find rooms by name and get their numeric IDs (needed for booking)

find_available_rooms

List rooms not already booked in a given time window

get_room_reservations

List all confirmed reservations in a location within a time window

book_room

Create a room booking (requires Classic API write scope; same-day only)

All tools accept an optional instance parameter to select which Indico server to query.

Related MCP server: @calmesh/mcp-server

Requirements

  • Python 3.11+

  • uv

Installation

git clone <repo>
cd indico-mcp
uv sync

Configuration

Copy the example env file and fill in your details:

cp .env.example .env

Single instance

INDICO_BASE_URL=https://indico.fysik.su.se
INDICO_TOKEN=indp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Multiple instances

INDICO_INSTANCES=cern,su
INDICO_DEFAULT=su

INDICO_CERN_URL=https://indico.cern.ch
INDICO_CERN_TOKEN=indp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

INDICO_SU_URL=https://indico.fysik.su.se
INDICO_SU_TOKEN=indp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

INDICO_DEFAULT sets which instance is used when no instance= argument is passed to a tool. Tokens are optional for public-only access.

Room booking setup

Room booking tools (search_rooms, find_available_rooms, book_room) require knowing the site name as configured in Indico's room booking module. These names are not discoverable via the API — you must look them up in the Indico room booking interface (they appear as location headings, e.g. "AlbaNova Main Building", "Albano Building 3").

1. Configure site names

Add the known site names to your .env:

INDICO_SU_ROOM_LOCATIONS=AlbaNova Main Building,Albano Building 3

For single-instance: INDICO_ROOM_LOCATIONS=Main Building,Building 40

2. Run room discovery

Once site names are configured, run discover_rooms once (e.g. by asking your agent to do it). This scans reservation history across all configured sites and saves a local room catalogue to ~/.indico_mcp/{instance}_rooms.json (override the directory with INDICO_ROOMS_CACHE_DIR).

After discovery, search_rooms finds rooms by partial name across all catalogued sites without needing a location argument. Rooms with no booking history are found on-the-fly via Indico's room name search endpoint, so discovery does not need to be exhaustive.

Re-run discover_rooms periodically (or whenever new rooms are added) to keep the catalogue fresh. The agent will offer to run it automatically if the cache file is missing when a room operation is requested.

Getting a token

  1. Log in to your Indico instance

  2. Go to My Profile → Personal Tokens (or /user/tokens/)

  3. Click Create token

  4. Give it a name and enable the Classic API (read) scope — this grants read access to the event export API

  5. If you also need write access (creating events, bookings), enable Classic API (read and write) instead

  6. Copy the generated token (starts with indp_)

Note on scope naming: The scope is labelled "Classic API" in the UI but this refers to Indico's HTTP Export API (/export/), which is the only way to programmatically read event and contribution data. Despite the name, using it with a modern Bearer token (indp_...) is the current recommended approach — the deprecated system is the old API key (a separate, shorter token found under My Profile → HTTP API).

Connecting to Claude

Run claude add indico -- uv run --directory /path/to/indico-mcp indico-mcp or manually add to your Claude config (~/.claude.json or Claude Desktop settings):

{
  "mcpServers": {
    "indico": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/indico-mcp", "indico-mcp"]
    }
  }
}

The .env file in the project directory is loaded automatically. Alternatively, pass configuration directly via the env block in the MCP config:

{
  "mcpServers": {
    "indico": {
      "command": "uv",
      "args": ["run", "--directory", "/path/to/indico-mcp", "indico-mcp"],
      "env": {
        "INDICO_INSTANCES": "cern,su",
        "INDICO_DEFAULT": "su",
        "INDICO_CERN_URL": "https://indico.cern.ch",
        "INDICO_CERN_TOKEN": "indp_xxx",
        "INDICO_SU_URL": "https://indico.fysik.su.se",
        "INDICO_SU_TOKEN": "indp_yyy"
      }
    }
  }
}

Usage examples

# Events in the next month (default instance)
search_category_events(from_date="2025-04-01", to_date="2025-04-30")

# Events in a specific category on CERN Indico
search_category_events(category_id=72, instance="cern")

# First page of contributions for a meeting
get_event_contributions(event_id=1234567, instance="cern")

# Continue with paging using `pagination.next_offset`
get_event_contributions(event_id=1234567, limit=100, offset=0, instance="cern")

# Full session/agenda structure of a conference
get_event_sessions(event_id=9876543, instance="su")

# Full-text search
search_events_by_keyword("dark matter", instance="cern")

# Navigate the category hierarchy
list_category_info(category_id=0, instance="su")

# First page of attachments for an event
list_event_attachments(event_id=1234567, instance="cern")

# Continue with paging using `pagination.next_offset`
list_event_attachments(event_id=1234567, limit=100, offset=0, instance="cern")

# List attachments for a specific contribution
list_event_attachments(event_id=1234567, contribution_id=42, instance="cern")

# Download a file (URL from list_event_attachments output)
download_attachment(download_url="https://indico.cern.ch/event/.../file.pdf")

How it works

The server uses the Indico HTTP Export API (/export/) with detail=contributions and detail=sessions query parameters to retrieve structured agenda data. Authentication uses a standard Authorization: Bearer <token> header. The /api/ endpoints in Indico are write-only (POST); all read operations go through /export/.

File attachments are discovered via the folders structure included in the export API response, which provides direct download URLs, filenames, content types, and sizes. Downloads are authenticated with the same Bearer token and saved locally (to a temp directory by default, or a specified path). Maximum file size is 100 MB.

Data Sensitivity & Privacy

Indico agendas and their attachments may contain sensitive or confidential information — internal meeting minutes, unpublished research results, restricted-access talks, personnel matters, and similar content.

What gets sent to the LLM provider

When an AI assistant calls tools such as get_event_details, get_event_contributions, get_event_sessions, or list_event_attachments, the returned data (titles, abstracts, speaker names, descriptions) is forwarded to the underlying language model. If you are using a cloud-hosted LLM provider (e.g. Anthropic Claude, OpenAI GPT), this data leaves your local machine and is processed by the provider according to their data-handling policies.

Similarly, when download_attachment is used and the AI reads the downloaded file, the file content is forwarded to the LLM provider.

Before processing agenda data with an AI assistant:

  • Check whether the event or category is internal or access-restricted.

  • Verify your organisation's data-handling and AI-usage policies.

  • For confidential or proprietary material, prefer a locally-hosted LLM.

  • Attachments with is_protected: true in list_event_attachments output are explicitly access-restricted by the owner — require explicit user approval before downloading or summarising them.

Token and access control

The Indico API token grants read (and optionally write) access to the instance. Apply the principle of least privilege:

  • Use a read-only token (Classic API read scope) unless write access is explicitly needed.

  • Do not store tokens in version-controlled files (use .env, which is excluded by .gitignore).

  • Rotate tokens if they are accidentally exposed.

Room booking

The book_room tool creates real reservations immediately. Always confirm all booking details with the user before calling book_room with dry_run=False, and never retry automatically on network failures — each retry may create a duplicate booking.

Contributing

Feature requests and bug reports are welcome. Contributions are especially encouraged from Indico users who can test new functionality against a real instance before submitting a pull request — the Indico API has enough instance-to-instance variation that untested changes are hard to review reliably.

Substantial contributions will be recognised by adding the contributor as an author.

License

Copyright (c) 2026 Christian Ohm. MIT License — see the LICENSE file.

Indico itself is also MIT licensed, so this can perhaps be absorbed into that repository.

Available Tools

19 tools
book_roomA

Create a room booking in Indico.

Always confirm the full details with the user before calling this tool with dry_run=False, as the booking is created immediately and may be difficult to cancel.

IMPORTANT CONSTRAINTS:

  • The booking must start and end on the same day (Indico API limitation for this endpoint; recurring or multi-day bookings must be made via the web UI).

  • The token must have the 'Classic API (read and write)' scope.

  • Booking dates can be today or in the future; exact restrictions depend on the Indico instance's room booking policy.

RETRY SAFETY: this endpoint is not idempotent. Submitting the same request twice (e.g. after a transient network error) will create two separate bookings. Use dry_run=True to pre-validate before committing, and do not retry automatically on failure.

IMPERSONATION: the booked_for parameter allows booking on behalf of another user. Only use it when the user has explicitly authorised this action.

Returns the booking confirmation from Indico, including the reservation ID if successful. When dry_run=True, returns the parameters that would have been sent without making any API call.

Inputs: room_id (required), from_dt (required), to_dt (required), reason (required), +3 more. Returns: object

browse_categoryA

List the direct subcategories of an Indico category, with event counts.

Works by fetching a batch of recent events from the category and collecting the distinct (category_id, category_title) pairs from their metadata. This is the reliable fallback when the REST API is not available on an instance.

Use this to navigate the category hierarchy: start at the root (0), pick a subcategory, call browse_category again on that ID, and so on until you find the right leaf category to pass to search_category_events or get_category_contributions.

Note: subcategories with no events in the sampled batch may not appear.

Inputs: category_id, instance. Returns: result

discover_roomsA

Scan reservation history to build a room catalogue and save it locally.

Fetches reservations across a broad time window (past 2 years + next 6 months) for each location to discover all rooms that have ever been booked, then saves the catalogue to ~/.indico_mcp/{instance}_rooms.json (override with INDICO_ROOMS_CACHE_DIR). After running this, search_rooms works without needing a location argument.

If no locations are provided and none are configured, ask the user for the site/location names shown in the Indico room booking interface.

Inputs: locations, instance. Returns: result

download_attachmentA

Download a file attachment from Indico and save it locally.

Use list_event_attachments first to get the download_url for the file you want. Returns the local file path, filename, content type, and size.

Files are saved to a temporary directory by default, or to a specified path. Maximum file size: 100 MB.

IMPORTANT — data sensitivity: the file content will be read by the LLM if you open or parse it after downloading. If list_event_attachments showed is_protected: true for this attachment, or if the event appears to contain confidential material, confirm with the user before proceeding.

Inputs: download_url (required), save_to, instance. Returns: object

find_available_roomsA

List rooms that are NOT already booked in a given time window.

Discovers known rooms from reservation history (past year + next 3 months), then checks which are booked in the requested window. Returns rooms with no conflict, each with its numeric id (needed for book_room) and full_name.

Use name_filter to narrow to a specific room or building wing.

Note: room discovery relies on past booking history. Rooms that have never been booked in the past year will not appear in the results even if they are free. If you believe a room is missing, try passing its name (or a fragment) via name_filter, which triggers an additional direct room-name lookup.

Inputs: location (required), date (required), from_time (required), to_time (required), +2 more. Returns: result

find_events_by_titleA

Search for events by title across the entire Indico instance.

Each result includes category_id and category name, making this the most direct way to discover which category a meeting series belongs to when you know part of the event title but not the category ID.

Example: searching 'AlbaNova ATLAS meeting' returns events whose category field reads 'Stockholm' with category_id=1384, immediately revealing where those meetings live so you can pass that ID to other tools.

Inputs: title (required), from_date, to_date, limit, +1 more. Returns: result

get_category_contributionsA

Get contributions from ALL events in a category within an optional date range, in a single API call.

Returns a flat list of contributions, each annotated with event_id, event_title, and event_start so you know which event each contribution belongs to.

This is far more efficient than listing events and calling get_event_contributions for each one. Use it for: finding all talks by a speaker across a series of meetings, analysing themes across colloquia, counting talks matching a title pattern, or any aggregation over multiple events.

The API caps results at 500 contributions per request. Use offset to paginate if needed.

Inputs: category_id (required), from_date, to_date, limit, +3 more. Returns: result

get_event_contributionsA

List contributions for an event.

Each contribution includes: title, speakers, authors, start time, duration, session, track, abstract/description, and room.

Returns both items and pagination metadata so partial results are explicit. Use has_more / next_offset to retrieve additional pages.

Inputs: event_id (required), limit, offset, include_attachments, +1 more. Returns: object

get_event_detailsA

Get full metadata for an event, including its list of contributions.

Returns event title, description, dates, location, and all contributions (with speakers, duration, abstract, session assignment).

Inputs: event_id (required), include_attachments, instance. Returns: object

get_event_sessionsA

Get the session structure for an event, with each session's contributions nested inside.

Useful for understanding how an event agenda is organised into parallel tracks or blocks. Each session includes: title, conveners, start/end time, room, and contributions list.

Inputs: event_id (required), include_attachments, instance. Returns: result

get_room_reservationsA

List all confirmed room reservations in a location within a time window.

Each reservation includes: room name, start/end time, who it is booked for, and reason. Requires a token with the 'Classic API' (legacy_api) scope.

Inputs: location (required), from_dt (required), to_dt (required), instance. Returns: result

list_category_infoA

Get metadata about an Indico category: name, description, and subcategory IDs.

Useful for navigating the category hierarchy to find the right category_id before using search_category_events.

Inputs: category_id, instance. Returns: object

list_event_attachmentsA

List all file attachments and links for an event (or a specific contribution).

Returns a flat list of attachments, each with: id, title, filename, content_type, size, download_url, and the folder/contribution/event context it belongs to.

Use this to discover what files (slides, papers, minutes, etc.) are attached to an event or contribution before downloading them with download_attachment.

Returns both items and pagination metadata so partial results are explicit. Use has_more / next_offset to retrieve additional pages.

Inputs: event_id (required), contribution_id, limit, offset, +1 more. Returns: object

list_instancesA

List configured Indico instances and the default instance name.

Use this first if you are unsure which instance values are valid.

Inputs: no named parameters. Returns: object

list_room_locationsA

List all room booking locations (sites) known for this Indico instance.

Returns locations from the rooms cache (built by discover_rooms) if it exists, otherwise returns the locations configured via INDICO_*_ROOM_LOCATIONS. The location name is required by search_rooms, find_available_rooms, get_room_reservations, and book_room.

When reading from the cache the response also includes 'cache_updated' (ISO date the cache was last refreshed) and 'cache_age_days' so the caller can decide whether to re-run discover_rooms.

Inputs: instance. Returns: object

search_categoriesA

Search for Indico categories by name.

Returns matching categories with their ID, title, full breadcrumb path from the root, and event/subcategory counts. Use this to discover the category_id needed by other tools when you don't already know it (e.g. 'OKC Colloquia', 'HGTD Speakers Committee').

Returns up to 10 results. Refine the query if too many or too few results appear.

Inputs: query (required), instance. Returns: result

search_category_eventsA

List events in an Indico category, optionally filtered by date range and keyword.

Returns a list of events with id, title, type, start/end times, location, and URL. Use get_event_details or get_event_contributions to drill into a specific event.

Inputs: category_id, from_date, to_date, keyword, +2 more. Returns: result

search_events_by_keywordA

Full-text search for events matching a keyword.

Uses the Indico search API if available, otherwise falls back to the legacy title-search export endpoint. Returns matching events with id, title, category, dates, and URL.

Inputs: keyword (required), limit, instance. Returns: result

search_roomsA

Find rooms by name and return their numeric IDs.

If a rooms cache exists (built by discover_rooms), searches it across all locations without any API call. Falls back to scanning reservation history live if no cache exists, in which case location is required.

Each result includes the room id (needed by find_available_rooms and book_room), full_name, and location.

Example: search_rooms("Xenon") finds all rooms with "Xenon" in the name across every cached location.

Inputs: name_filter, location, instance. Returns: result

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A3.8/5.0

    Scored across 19 tools

    Disambiguation3/5

    Most tools have distinct purposes, but there is overlap between get_event_details (which includes contributions) and get_event_contributions, and between find_events_by_title and search_events_by_keyword. An agent might select the wrong tool for retrieving contributions or searching events.

    Naming Consistency4/5

    All tool names use snake_case and follow a verb_noun pattern. However, there is some verb variety (list vs get vs search vs find) that could be streamlined, e.g., get_room_reservations vs list_room_locations.

    Tool Count4/5

    With 19 tools, the count is slightly above the typical sweet spot but remains well-scoped for the two main domains (room booking and event/category management). No tool feels redundant.

    Completeness2/5

    The tool set is heavily read-oriented: events, contributions, categories, and attachments only have retrieval/listing tools. Missing create, update, delete operations for events and contributions, and no room booking cancellation. This leaves significant gaps for typical workflows.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers