indico-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@indico-mcp Search for categories related to workshops"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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 configured instance names and the current default instance |
| Find categories by name; returns ID, breadcrumb path, and event count |
| Search event titles across the whole instance; each result includes |
| List direct subcategories of a category by ID; works without the REST API |
| List events in a category, filtered by date range and keyword |
| All contributions from every event in a category within a date range, in a single API call |
| Full event metadata including all contributions |
| Paginated contributions ( |
| Session structure with nested contributions (full agenda view) |
| Full-text search across events |
| Category name, description, and direct subcategories with names |
| Paginated attachments ( |
| Download an attachment file to disk given its download URL |
| List known room booking sites for this instance |
| Scan reservation history to build a local room catalogue |
| Find rooms by name and get their numeric IDs (needed for booking) |
| List rooms not already booked in a given time window |
| List all confirmed reservations in a location within a time window |
| 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+
Installation
git clone <repo>
cd indico-mcp
uv syncConfiguration
Copy the example env file and fill in your details:
cp .env.example .envSingle instance
INDICO_BASE_URL=https://indico.fysik.su.se
INDICO_TOKEN=indp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxMultiple 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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxINDICO_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 3For 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
Log in to your Indico instance
Go to My Profile → Personal Tokens (or
/user/tokens/)Click Create token
Give it a name and enable the Classic API (read) scope — this grants read access to the event export API
If you also need write access (creating events, bookings), enable Classic API (read and write) instead
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: trueinlist_event_attachmentsoutput 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 APIread 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 toolsbook_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
Scored across 19 tools
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.
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.
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.
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
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
MCP server exposing the Backtest360 engine API as tools for AI agents.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
MCP server for uagents documentation, generated by doc2mcp.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceA read-only MCP server that exposes iCalendar feeds as queryable tools for LLM agents, enabling calendar event retrieval and filtering.MIT
- AlicenseAqualityDmaintenanceMCP server that connects AI agents to calendars, bookings, and scheduling polls via the Model Context Protocol.227 npmMIT
- AlicenseNot gradedqualityNot gradedmaintenanceMCP server that lets AI agents manage Google Calendar -- create events, check availability, and manage schedules.MIT
- FlicenseNot gradedqualityBmaintenanceMCP server for Radicale/CalDAV servers, enabling AI agents to manage calendars and task lists by listing, creating, and deleting events and reminders.-