Manual › freemapper-courseplan
3. With an AI assistant
The assistant sets the courses for you. For this, freemapper-courseplan is an "MCP server", a tool that AI assistants can use. The program has no AI of its own. The assistant calls the same editing functions as the command line, and each call saves into your project file.
Set it up once
- Claude Desktop:
Open Settings > Developer > Edit Config.
Add this to the file
claude_desktop_config.json, with your own path (on Windows with doubled backslashes\\):{ "mcpServers": { "freemapper-courseplan": { "command": "/Users/anna/Documents/freemapper-courseplan/freemapper-courseplan", "args": ["mcp"] } } }Restart Claude Desktop.
- Claude Code: type
claude mcp add freemapper-courseplan -- /path/to/freemapper-courseplan mcpin a terminal. - Other assistants: any assistant with MCP support takes the same entry: the program and the argument
mcp.
The assistant reads the tool texts in the language of your system. To fix the language, write "--lang", "en" before "mcp" in the arguments, or set FREEMAPPER_LANG=en in the environment of the program. Tool names and field names stay English in every language.
Let the assistant set it up
The archive holds, besides the program, the READMEs, the licence, this manual and AGENTS.md, instructions for AI assistants. An assistant that can run commands on your computer (such as Claude Code or Codex) downloads the program, checks and unpacks it by the steps in that file and then works through the command line; it needs no registration. Only an assistant that cannot run commands needs the registration described above. Tell it:
Read AGENTS.md from github.com/HenryJobst/freemapper-courseplan and set up freemapper-courseplan.
On a Mac it removes the block on unsigned programs and tells you so. Depending on its settings it asks before each command. Claude Desktop runs no commands; there you register the program by hand as described above.
Set a course
The assistant does not see the map. It can ask for the extent of the map and, for a point, for the map objects around it, each with its symbol number, its kind (point, line, area) and its distance. It knows no names such as "south door" or "gym", and the query does not tell it where exactly an object lies. So give it positions, in map millimetres or relative to the extent of the map, for example:
Start an event from ~/maps/hall.omap. Put the start a quarter of the way in from the left edge, at half the height, and the finish 10 mm to the right of it. Add three controls spread over the map and tell me for each which map objects lie within 2 mm. Make a course "Short" from them for class D10, place the control numbers and tell me the length.
The assistant then works like this:
- It starts the event. The project file lands next to the map, named
hall.courseplan.toml. - It asks for the extent of the map, that is its smallest and largest corner in map millimetres, the scale and the map kind, and works out the positions from it.
- It adds the start, the finish and the controls at these positions.
- For each control it asks for the map objects nearby and tells you their symbol numbers and distances, such as "symbol 521, area, 0.4 mm". Look up what a symbol means in the symbol set of the map.
- It adds the course, places the control numbers and reads the length and the climb.
You can also continue in a later chat. Give the assistant the path of the project file and it picks up from there.
What to know
- Paths are absolute or start with
~. A relative path is refused, because the assistant does not know the working folder of the program. - Every change is saved at once. If the program refuses a change, for example a point outside the map or a code that is taken, the file stays as it was and the assistant gets the reason.
- If the project file changed after the assistant read it, for example because you moved a control in the editor, the program writes nothing. The assistant gets "
changed on disk after it was read, so nothing was written. Read the project again and repeat the change" and repeats the change with the new state. - Positions are in map millimetres at map scale, with y pointing down. Look at every control position yourself on the map.
checktests distances, angles and codes against the rules (chapter 8), but not whether the control stands at the right feature.- The program places control numbers only when the assistant asks for it. If the assistant then changes legs or the order, it has to have them placed again. A number placement set for a control or in a course stays.
- Automatic placement puts a number behind the circle, in line with the leg that leads to the control. If the course hardly turns, the next leg runs there, and the number goes outside, at the tip of the angle. It stands on a point or line symbol of the map only when no free place is left; it may cover areas. If you dislike the position, set it yourself.
- The number stands 0.5 to 5 mm from the circle (distance from the circle line to the number box, rule
wkb2026.number.gap). Automatic placement starts at 0.5 mm and moves outward in steps of 0.5 mm before it turns away from the preferred direction.checkwarns when a stored gap lies outside the range, for example a number set by hand at 6 mm.- When the order of a course changes, every leg that now runs to another stop loses its bends and its taped flag. The assistant gets a list of these legs.
- Individual and score courses can be edited (see Score courses below).
infogives the length of individual courses; for a score course it gives controls, points and time limit, andscore lengthmeasures it. Team and relay courses cannot be edited yet; the program says so.- The project file is plain TOML.
freemapper-courseplan info <project>shows what the assistant did, andlist controls|courses|specials|classes <project>lists the details;map extent <project>andmap near <project> --at X,Y --radius MMreport the map. All of them take--json.Score courses
On a score course the runners visit the controls in any order within a time limit. Each control is worth points, and every started minute over the limit costs points. These commands build such a course;
checktests it (section 8).freemapper-courseplan course add hall.courseplan.toml Score45 --kind score --start S1 --finish F1 --time-limit 45 freemapper-courseplan score controls hall.courseplan.toml Score45 31,32,43,54 freemapper-courseplan score link add hall.courseplan.toml Score45 31 54 freemapper-courseplan score settings hall.courseplan.toml --value-max 5 --good-pace 4:10 freemapper-courseplan score course set hall.courseplan.toml Score45 --penalty 6- The code carries the value: the second digit of a two-digit code, the last digit of a longer one. So 43 is worth 3 points and 54 is worth 4.
score controlsreads the value from the code. A value you write after=, as in43=3, has to match it; if it differs, the program refuses and says to change the code.score valuefollows the same rule, so it only repairs a value in the file that no longer matches its code. A code that carries no value, such asA1, needs one:A1=2. - Values run from 1 to the highest value of the event, 5 by default and at most 9 (
score settings --value-max). - Without
--penaltythe course stores no penalty and uses the default: the highest control value of the course, at least the highest value of the event, so a minute over never pays off. The default follows the controls.score course set --penalty Nstores your own number, and--no-penaltygoes back to the default. - Changing a control's code (
control code) gives it the value of the new code. A code that carries no value keeps the old value, andcheckreports the difference. - A link names two controls that the runner has to visit directly one after the other, in either direction. A runner who does not is disqualified. A control takes at most two links, and links may not close a ring. A control with a link stays in the course until you remove the link (
score link remove). score settingswrites the[score]table of the event: value range, the target bands (--good-band 87-93,--very-good-band 95-100, in percent), their tolerance (--band-tolerance 3, 0 to 10 percentage points), the paces of a good and a very good runner (seconds per kilometre, or4:10), the share of hard controls and the seed. Four distances in metres on the ground decide where controls stand (ADR 0072):--value1-min(150 m) is the least distance of a control from start and finish, where value 1 begins;--min-gap(100 m, not lower) the least distance between two controls;--high-value-gap(200 m, at least--min-gap) the least distance between two high-valued controls, the upper four tenths of the values (4 and 5 at a highest value of 5);--max-reachthe farthest distance from the start. Without it the reach is automatic, a quarter of the ground the good runner covers in the time limit (900 m at 15 minutes and 4:10 min/km);--auto-reachreturns to that. A distance is a whole number of metres.- Each of these commands takes
--jsonand prints what the MCP tool of the same name answers (add_score_course,set_score_controls,set_control_value,add_score_link,remove_score_link,set_score_settings,set_score_course_details). The editor runs the same operations, and undo takes each back. - Bends, legs, control numbers and
course controlsbelong to individual courses; a score course refuses them. Classes, print scale,course renameandcourse removework on both. Team and relay courses cannot be edited yet. - IOF XML export writes score courses too (see Export below). The Purple Pen export still skips them and says so. The editor draws score courses and has a Score panel (see "The Score panel" in the editor chapter).
Lists and evaluation sheet
freemapper-courseplan export hall.courseplan.toml --course Score45 --csv score45.csv --sheet score45.md--csvwrites one row per control:code,value,x_mm,y_mm, ordered by code, with positions in map millimetres at map scale (y points down) and a dot as decimal separator.--sheetwrites the evaluation sheet as Markdown, a text that prints well as it is: the time limit, the penalty per started minute (the stored one or the default), the rule that the faster runner wins on equal points, the mandatory links with the rule that a runner who does not run a link directly is disqualified, and a table of the controls with their values. Both need--courseand refuse a course that is not a score course. An existing file is replaced. The tools areexport_score_csvandexport_score_sheet, withproject,courseandpath(ending in.csvor.md). The decisions are in ADR 0070.Suggestions, draft and length
These three commands read the map and search routes on a raster of its symbols (no elevation), so each takes a few seconds on a large map. The start and the finish must lie outside buildings and blocked areas; if one does not, the program says the start or finish cannot be reached.
freemapper-courseplan score suggest hall.courseplan.toml --limit 30 freemapper-courseplan score draft hall.courseplan.toml Score45 --time-limit 45 freemapper-courseplan score draft hall.courseplan.toml Score45 --time-limit 45 --apply freemapper-courseplan score length hall.courseplan.toml Score45score suggestlists control sites, best route choice first, each with its position, the symbol code of the map feature, route choice and difficulty from 0 to 1, the time from the start for the good runner and the value it would get. It writes nothing.--limitsets the number (60 by default, at most 200),--min-distancethe smallest distance in map millimetres (by default the settingmin_gap_m, 100 m on the ground). Sites closer thanvalue1_min_mto the start or finish are dropped, and with--courseso are sites beyond the reach from the start.score draftalso keeps the gap between high-valued controls;checkwarns when a course falls below it or below the minimum gap.--course NAMEtakes the start and finish of that score course (--startand--finishstill win); without it the first start and finish of the event count. Sites in out-of-bounds and dangerous areas, on or inside map areas with the symbol 520, 709 or 714, and within 4 m of a forbidden route, are excluded. With--jsonand over MCP each candidate also has a freecodethat carries its value. It isnullwhen no free code is left: only the best-ranked candidates get one.score draftpicks controls from these sites and gives them values and codes, so that the good runner scores 87 to 93 percent of all points by default within the limit and the very good runner takes every control and finishes after 95 to 100 percent of it by default. Both bands are targets, and so is their tolerance: a result up to 3 percentage points outside a band, by default, counts as near the target. The bands and the tolerance are settings of the event. Without--applyit only prints the draft. With--applyit adds the controls and creates the score course, or replaces the controls of an existing score course and removes the controls that only it used. A course of another kind is refused. The editor can undo the change.--link "X,Y;X,Y"makes two positions a mandatory link; each must be the position of a candidate fromscore suggest, run with a--limitof at least 60, the pool the draft uses. Repeat the option for more links.- Each runner ends up in the band, near the target or missed. In the band and near the target are successes: the command exits with 0, and for a draft near the target it names the deviation, for example "1.7 percentage points above the band ... within the tolerance of 3 percentage points". A draft that misses a band beyond the tolerance exits with 1. It names each missed band with the size of the miss, and one hint: a longer or shorter limit, more suitable sites, free codes (the control count is capped by the code space: raise the highest value with
score settings --value-max, 9 at most, or free codes on the map), a faster good runner, or that these inputs cannot work. score lengthanalyses a course that exists, for example after you moved a control, and reports the same bands and deviations and says in which of the three states the course is. It never changes the course.- The same answers come from the tools
score_suggest,score_draftandscore_length;--jsonprints them. The draft is the same on every run for the same project. score reject --at X,Yrejects a site: you know the place is wrong, for example a private yard or a corner nobody sees. The position goes into the project file (keyrejected_sites), andscore suggest,score draftand the candidate list of the editor leave it out from then on. A candidate within 3 m on the ground counts as the same site.score unreject --at X,Ytakes it back and fails when the site is not rejected. A control that already stands there stays. The editor has a "Reject" button beside "Accept" in the candidate table; undo takes it back. The tools arereject_siteandunreject_sitewithprojectandat, and--jsonprints their answer:at,addedorremoved, andrejected_sites, the number of rejected sites. The decision is ADR 0089.
- Positions are in map millimetres at map scale, with y pointing down. Look at every control position yourself on the map.