Skip to main content
Glama
eventor-mcp

eventor-mcp

by bjorges

eventor-mcp

An MCP server that exposes the Norwegian/Swedish orienteering federation's Eventor REST API as typed tools, so an MCP client (e.g. Claude) can query events, clubs, entries and competitors -- and optionally publish start/result lists -- without ever seeing your API key or Eventor password.

Why this exists

Eventor's own API documentation (the "Hämta data via API" guide) only documents authentication and one example call. The actual endpoint shapes used here were taken from a real, working integration (ttime.pl) against this API.

Related MCP server: Evento Public MCP Server

Security model

  • Credentials (EVENTOR_API_KEY, EVENTOR_USERNAME, EVENTOR_PASSWORD) are read only from environment variables, by the server process itself.

  • They are never included in any tool result, logged, or written to disk by this project.

  • .env is git-ignored -- only .env.example (with empty values) is committed.

  • An MCP client (including an LLM) only ever calls tools like list_events(from_date, to_date); it never handles the raw key/password.

If you ever paste your API key into a chat, file, or issue, treat it as compromised and regenerate it in Eventor under Klubben -> Klubbinnstillinger.

Setup

Requires uv.

uv sync --extra dev
cp .env.example .env   # then fill in your own values in .env

uv sync creates .venv and installs everything pinned in uv.lock. Run project commands with uv run <command> (e.g. uv run pytest), or activate the environment yourself (.venv\Scripts\activate on Windows, source .venv/bin/activate on macOS/Linux) if you prefer.

Two separate credential types are used by Eventor's API:

Credential

Used for

Where to get it

EVENTOR_API_KEY

list_events, list_organisations

Eventor -> Klubben -> Klubbinnstillinger (club admin only)

EVENTOR_USERNAME / EVENTOR_PASSWORD

get_event_classes, get_event_entries, get_entry_changes, get_competitors, import_startlist, import_resultlist

Your personal Eventor login

Running

uv run python -m eventor_mcp.server

This starts the server on stdio, ready for an MCP client to connect.

Registering with Claude Code

This repo ships a project-scoped .mcp.json, so opening this folder in Claude Code (or the Claude desktop app's Code tab) offers to enable the eventor server automatically -- you'll see a one-time approval prompt (project MCP servers can run arbitrary commands, so Claude Code always asks first). It runs uv run python -m eventor_mcp.server with this project directory as its working directory, so it picks up your local .env on its own; no credentials are stored in .mcp.json.

To register it globally instead (available from any directory, not just this one), use the CLI:

claude mcp add eventor -- uv run --directory /path/to/eventor-mcp python -m eventor_mcp.server

Either way, credentials come from your environment / local .env -- never hardcode them anywhere in this repo.

Installing as a standalone tool (no clone needed)

If you just want to run the server -- not modify or test the code -- you don't need a checkout at all:

uv tool install git+https://github.com/bjorges/eventor-mcp

This puts an eventor-mcp command on your PATH, in its own isolated environment. There's no project directory here for .env auto-loading to find, so set credentials as real environment variables instead (see .env.example for the full list), then register it:

claude mcp add eventor -- eventor-mcp

Upgrade with uv tool upgrade eventor-mcp, remove with uv tool uninstall eventor-mcp.

If uv warns that its tool bin directory isn't on your PATH, run uv tool update-shell and restart your shell before eventor-mcp (or claude mcp add ... -- eventor-mcp) will resolve.

Tools

Tool

Auth

Notes

list_events(from_date, to_date, organisation_ids=None)

API key

Dates as YYYY-MM-DD

list_organisations()

API key

All clubs/districts/federations

get_event_classes(event_id)

Username/password

Classes for one event

get_event_entries(event_id)

Username/password

Full entries: name, class, club, control cards (a list -- Emit, emiTag and SI are distinct systems and a person can have more than one)

get_entry_changes(event_id)

Username/password

Just entry/modify timestamps

get_competitors(organisation_ids)

Username/password

Registered club members

import_startlist(iof_xml)

Username/password

Writes to Eventor -- publishes a start list

import_resultlist(iof_xml)

Username/password

Writes to Eventor -- publishes results

import_startlist and import_resultlist expect a complete IOF XML 3.0 document as a string; this project does not (yet) build that XML for you.

Testing

uv run pytest

Limitations / TODO

  • No XML-building helpers for import_startlist/import_resultlist yet -- callers must supply valid IOF XML 3.0.

  • No caching, even though Eventor's usage terms ask integrations to cache frequently-fetched data (e.g. club member lists) client-side.

  • Only tested against the Norwegian instance (eventor.orientering.no); the Swedish instance (eventor.orientering.se) uses the same API shape via EVENTOR_BASE_URL.

Available Tools

8 tools
get_competitorsB

Get all registered competitors for one or more clubs.

Args: organisation_ids: Comma-separated Eventor organisation id(s).

Requires EVENTOR_USERNAME and EVENTOR_PASSWORD.

Inputs: organisation_ids (required). Returns: result

get_entry_changesB

Get entry/modification timestamps for a specific event (who entered or changed their entry, and when), without full competitor details.

Requires EVENTOR_USERNAME and EVENTOR_PASSWORD.

Inputs: event_id (required). Returns: result

get_event_classesA

Get the competition classes defined for a specific event.

Requires EVENTOR_USERNAME and EVENTOR_PASSWORD (a personal Eventor login) -- this endpoint does not accept the club API key.

Inputs: event_id (required). Returns: result

get_event_entriesB

Get all entries for a specific event: competitor name, class, club and control card number.

Requires EVENTOR_USERNAME and EVENTOR_PASSWORD.

Inputs: event_id (required). Returns: result

import_resultlistA

Upload a result list to Eventor.

THIS WRITES DATA: it publishes results. Only call this after the user has explicitly confirmed they want to publish these exact results.

Args: iof_xml: A complete IOF XML 3.0 ResultList document as a string.

Requires EVENTOR_USERNAME and EVENTOR_PASSWORD.

Inputs: iof_xml (required). Returns: result

import_startlistA

Upload a start list to Eventor.

THIS WRITES DATA: it changes what is published in Eventor. Only call this after the user has explicitly confirmed they want to publish this exact start list -- treat it like any other irreversible "publish" action, not a routine read.

Args: iof_xml: A complete IOF XML 3.0 StartList document as a string.

Requires EVENTOR_USERNAME and EVENTOR_PASSWORD.

Inputs: iof_xml (required). Returns: result

list_eventsA

List events (competitions/activities) from Eventor within a date range.

Args: from_date: Start date, format YYYY-MM-DD. to_date: End date, format YYYY-MM-DD. organisation_ids: Optional comma-separated Eventor organisation id(s) to filter by (a club or district). Omit for all events in range.

Requires EVENTOR_API_KEY (a club API key from Eventor -> Klubben -> Klubbinnstillinger).

Inputs: to_date (required), from_date (required), organisation_ids. Returns: result

list_organisationsA

List all organisations (clubs, districts, federations) known to Eventor.

Requires EVENTOR_API_KEY.

Inputs: no named parameters. 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 8 tools

    Disambiguation4/5

    Most tools target distinct resources (events, organisations, classes, imports), and the descriptions clarify scope. However, get_event_entries, get_competitors, and get_entry_changes all concern 'who is participating' and differ mainly by scope (event vs club vs timestamps), which could cause occasional misselection.

    Naming Consistency5/5

    All eight tools follow a clean snake_case verb_noun pattern using get_, list_, and import_ prefixes consistently. The convention is predictable and easy to reason about.

    Tool Count5/5

    Eight tools is well-scoped for an orienteering event data server, with a sensible split between read operations and two publish/import writes. Each tool clearly earns its place.

    Completeness4/5

    The surface covers the core lifecycle: listing events/organisations, reading classes, entries, competitors, and publishing start/result lists. Minor gaps exist, such as no single-event detail fetch and no update/delete of entries, but agents can work around these.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers