champollion-mcp-server 0.3.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,72 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 (2026-10-18) — on rails
4
+
5
+ ### Fixed — found by the release candidate's packaged walk (2026-10-18)
6
+
7
+ - **`model_in_use` on `project_status`**: once a model is served, its try page
8
+ and the `champollion connect` command that puts it into an app or website
9
+ travel with the improve rounds' steps. The journey goes on to round 1 after
10
+ serving, so it never "finishes", and an agent on the rails never heard of
11
+ connect. The rails instructions and the agents' MCP page say what to do
12
+ with it (tell the person once; they run connect when they want it).
13
+
14
+ - **The rails profile is the default.** `npx champollion-mcp-server` now
15
+ starts the six-tool rails profile (start a language project, read its
16
+ next step, run it, add data, try the model, explain). Every other tool is
17
+ in the full profile: `--profile full` or `CHAMPOLLION_MCP_PROFILE=full`.
18
+ A client configured for the old default adds `--profile full`.
19
+ - **The rails tools.** `start_project` (a project folder with a place for
20
+ everything; a private-use code like `qaa` needs `target_name`),
21
+ `project_status` (THE next step, read from the project's files; with
22
+ `wait_seconds` it waits up to 120 s for a running step), `run_next_step`,
23
+ `add_data`, `try_translation` and `explain` (the rails, privacy, quality,
24
+ the step modes, stepping off the rails). Steps run as the same
25
+ `champollion` and `nmt-forge` commands a person types. The instructions
26
+ (`instructions-rails.md`) stay under 7,500 characters.
27
+ - **The people decide.** `run_next_step` runs only the step `project_status`
28
+ just named (the step ids must match) and refuses a step only a person can
29
+ take, or one that is waiting. A step that asks the data's holders runs
30
+ only with their answers, checked against what the step asks and put
31
+ into the command's arguments, never a shell string; an install or training
32
+ step runs only with `confirmed: true`. A step that failed twice in a row
33
+ runs again only when the person says so (`confirmed: true`); the refusal
34
+ hands back its last output.
35
+ - **Only the rails' own commands run:** `champollion`, `nmt-forge`, and
36
+ `uv tool install … nmt-forge` (`--python`, `--reinstall`, `--upgrade`).
37
+ Pipes, `;`, redirects, `$` and backticks are refused; a `#` starting a word
38
+ is a comment, as in a shell. Every command is a bounded process (10
39
+ minutes; 15 for the forge install). If `uv` is missing, the step is refused
40
+ with where the person installs it — the server never downloads or runs an
41
+ installer. When the installed `nmt-forge` is older than the rails need,
42
+ the tools step says both versions and offers the `--upgrade` install, for
43
+ the person to confirm.
44
+ - **`add_data`** brings in spreadsheets (.xlsx, .ods, .csv), .tsv/.txt files,
45
+ Word .docx tables, Toolbox and LIFT (FieldWorks) dictionaries, ELAN .eaf
46
+ files and, only when the person asks, USFM scripture: paired verse by verse
47
+ with the other language's text (`usfm_source`), held back from training
48
+ until its holders say so. `into` is `parallel`, `test` or `lexicon` (a word
49
+ list: the rails measure what looking words up scores). It reports counts
50
+ and columns, never sentences.
51
+ - **`try_translation`** translates up to 10 of the person's sentences with
52
+ the model the project serves on this computer (loopback only). What a
53
+ model writes is never relayed when it learned from local-only sentences or
54
+ its training terms were never recorded: the sentences are not sent, and
55
+ the agent gets the model's own page for the person to open. When the server asks for an
56
+ access token, the person tries it on that page; a token is never handed to
57
+ an agent.
58
+ - Full profile: `run_benchmark` takes method `lexicon` (the dictionary floor;
59
+ `model` is the dictionary file and is required, `attest_local_transport`
60
+ does not apply). `forge_status` in `serving` names
61
+ `npx champollion connect <model dir> --app <the app folder>` and says a
62
+ model trained on local-only sentences needs `--holders-agreed-by`, a forge
63
+ flag no tool ever passes: it is the holders' own answer, never an agent's.
64
+ - Tests: a scripted person (no LLM) walks the rails profile from an empty
65
+ folder to an evaluated model, to a private-use language and to a real one
66
+ by name; the same walk records the site's demo and runs from the packages
67
+ as they would ship. A lockstep test holds the `champollion` dependency
68
+ floor to the CLI in this tree.
69
+
3
70
  ## 0.3.2 (2026-10-07)
4
71
 
5
72
  Requires champollion 0.5.3. The `translate` tool's default model is the
package/LICENSE CHANGED
@@ -130,4 +130,4 @@ software under these terms.
130
130
  **Use** means anything you do with the software requiring one
131
131
  of your licenses.
132
132
 
133
- Required Notice: Copyright Curtis Forbes — Champollion (https://champollion.dev)
133
+ Required Notice: Copyright Epistemic Toolworks™ (Game Day Suits Ltd.) — Champollion (https://champollion.dev)
package/README.md CHANGED
@@ -2,15 +2,24 @@
2
2
 
3
3
  MCP (Model Context Protocol) server for Champollion. Lets an AI agent take a person from "we want machine translation for our language" to a measured, deployable model — find the language and what exists for it, keep the community's data private, baseline existing models (including ones on your own machine), train with guardrails, prove the result, and translate — plus browse and run the public benchmark queue.
4
4
 
5
- > **Champollion** is infrastructure for trustworthy machine-translation evaluation across every language — source-available and free for noncommercial use (the evaluation harness and shared registries are open source) — the test sets and the map that show who can translate what, how good each method is, and where the gaps are. Public benchmarks on open data rank every method (human and machine); sovereign benchmarks are secret community-owned test sets we never see. The infrastructure is source-available and singly stewarded; the test sets and the methods for a community's language belong to that community — designed to work with communities, never hosting their corpora. This server is the agent-facing door into that network ([champollion.dev/docs/network](https://champollion.dev/docs/network/)). This server itself is PolyForm Noncommercial 1.0.0 (see [LICENSE](LICENSE)): free for noncommercial use; using it for a commercial purpose is not covered by this license. Who is covered, in plain words with examples: [Who may use this](https://champollion.dev/docs/getting-started/who-may-use-this).
5
+ > **Champollion — on rails, with guardrails.** Machine translation development and deployment on rails, built with low-resource and Indigenous language communities in mind. This server is the agent's door onto the rails: its default profile gives a small agent six tools that walk a project from an empty folder to a measured model on the person's own computer, telling the agent to ask the person every question only they can answer (the server cannot check that it did), and reporting counts and columns — never a sentence from a local-only file. The rails promise process, not quality. This server itself is PolyForm Noncommercial 1.0.0 (see [LICENSE](LICENSE)): source-available and free for noncommercial use; using it for a commercial purpose is not covered by this license. Who is covered, in plain words with examples: [Who may use this](https://champollion.dev/docs/getting-started/who-may-use-this).
6
6
 
7
7
  ## What it does
8
8
 
9
- When connected to an agent (Claude Code, Cursor, …), the server exposes tools, resources, and prompts built around one flow: **"let's build a model for our language — how do we start?"** — discover what exists → protect your own data → baseline → build → prove → deploy. (Contributing compute to the public benchmark is the second flow.)
9
+ It has two profiles:
10
+
11
+ - **rails** (the default) — six tools that walk one language project end to
12
+ end on the person's own computer, with instructions a small agent can hold
13
+ ([below](#the-rails-profile--the-default)).
14
+ - **full** — every tool listed under [Tools](#tools), for the whole network
15
+ (atlas, benchmarks, forge, publishing): `npx champollion-mcp-server --profile full`
16
+ (or `CHAMPOLLION_MCP_PROFILE=full`).
17
+
18
+ When connected to an agent (Claude Code, Cursor, …) with the full profile, the server exposes tools, resources, and prompts built around one flow: **"let's build a model for our language — how do we start?"** — discover what exists → protect your own data → baseline → build → prove → deploy. (Contributing compute to the public benchmark is the second flow.)
10
19
 
11
20
  ### Tools
12
21
 
13
- Arguments: `name` is required, `name?` is optional. Every tool that takes ONE language also accepts it as `language` — `code` / `language` means either name works (pass one). The original names keep working.
22
+ These are the full profile's (`--profile full`). Arguments: `name` is required, `name?` is optional. Every tool that takes ONE language also accepts it as `language` — `code` / `language` means either name works (pass one). The original names keep working.
14
23
 
15
24
  #### Discover — what exists for a language
16
25
 
@@ -105,6 +114,28 @@ npm test # run unit tests
105
114
  npm start # start on stdio (for agent connection)
106
115
  ```
107
116
 
117
+ ### The rails profile — the default
118
+
119
+ ```bash
120
+ npx champollion-mcp-server # the rails profile
121
+ npx champollion-mcp-server --profile full # every tool (or CHAMPOLLION_MCP_PROFILE=full)
122
+ ```
123
+
124
+ Six tools and short instructions (`instructions-rails.md`, under 7,500
125
+ characters) that walk one language project end to end on the person's own
126
+ computer: `start_project`, `project_status` (THE next step, read from the
127
+ project's files), `run_next_step`, `add_data` (spreadsheets, Word tables,
128
+ .csv/.tsv, Toolbox and LIFT dictionaries, ELAN files, and USFM scripture on
129
+ request — counts and columns reported, never sentences), `try_translation`
130
+ (the served model on a few of the person's sentences — a model that learned
131
+ from local-only sentences is never relayed to the agent; the person gets its
132
+ page instead) and `explain`. The
133
+ people decide: `run_next_step` refuses a step only a person can take, runs a
134
+ step that asks the data's holders only with their answers, an install or
135
+ training step only with `confirmed: true`, and only the command
136
+ `project_status` just named. Every command is a bounded `champollion` or
137
+ `nmt-forge` process.
138
+
108
139
  ## Connect to your agent
109
140
 
110
141
  ### Claude Code / Antigravity
package/bin/server.js CHANGED
@@ -6,9 +6,11 @@
6
6
  * an AI agent's MCP client (Claude Code, Antigravity, Cursor, etc.)
7
7
  * or run directly for testing:
8
8
  *
9
- * node bin/server.js
9
+ * node bin/server.js # the rails profile (the default)
10
+ * node bin/server.js --profile full # every tool
10
11
  *
11
- * The server exposes read-only tools for discovering what exists for a
12
+ * The rails profile walks one language project end to end with six tools.
13
+ * The full profile exposes read-only tools for discovering what exists for a
12
14
  * language (cited cards, benchmarks, results, contests), plus action tools
13
15
  * that run benchmarks, drive nmt-forge training and translate — every one
14
16
  * that spends or publishes requires the user's explicit confirmation.
@@ -16,5 +18,9 @@
16
18
 
17
19
  import { createServer } from '../src/index.js';
18
20
 
19
- const server = await createServer();
21
+ // --profile rails (the default): the six-tool rails profile (src/rails.js);
22
+ // --profile full: every tool.
23
+ const i = process.argv.indexOf('--profile');
24
+ const profile = i >= 0 ? process.argv[i + 1] : undefined;
25
+ const server = await createServer(profile ? { profile } : undefined);
20
26
  await server.start();
@@ -0,0 +1,129 @@
1
+ # Champollion — machine translation on rails
2
+
3
+ You help people build a translation method for their language, on their own
4
+ computer. The rails name every step; you walk them, and the people decide.
5
+
6
+ ## The loop
7
+
8
+ 1. No project yet → `start_project` (folder; `from` = the language they will
9
+ put in, `to` = the language they want out; a private-use code like `qaa`
10
+ needs its name). Pass each language exactly as the person named it — never
11
+ a name or code they did not say; a language they know no code for is `qaa`
12
+ with their name. No name for it yet? Ask what they want to call it for
13
+ now — their choice, never yours. Ask the person what the translations are for and pass it
14
+ as `purpose`. The first call makes nothing: say its `ask_first` to the
15
+ person, then make the call it gives for their answer.
16
+ 2. `project_status` → read `step.mode`, `step.say`, `step.why`. `step.call`
17
+ is the exact tool call that takes the step (`step.command` is what the
18
+ rails run — never a tool name); a `<…>` in it is a slot for the person's
19
+ answer or file, never to be sent as it is.
20
+ 3. Do what the mode says:
21
+ - **auto** — `run_next_step` with the `step_id`.
22
+ - **confirm** — tell the person what it does (installs, training), ask
23
+ yes/no; only on yes, `run_next_step` with `confirmed: true`. On "not
24
+ now", `run_next_step` with `declined: true`: nothing runs, and it is not
25
+ asked again this session. (When "not-now" is one of a question's own
26
+ answers, pass it in `answers` instead: it is recorded.)
27
+ - **ask_user** — ask the person `step.question` in plain words; pass their
28
+ answers, by the keys in `step.answers`, to `run_next_step`. Never answer
29
+ for them, never guess, never fill an answer from a file.
30
+ - **wait** — something is running. Make `step.call` (`project_status`,
31
+ waiting up to 2 minutes) and repeat it while the step stays `wait`. Only
32
+ if the tools said it takes hours, tell the person when to ask you again.
33
+ - **use_tool** — YOU do it now, with `step.tool` (e.g. `add_data` for the
34
+ files the person named), as `step.say` says. Do not hand it back.
35
+ - **human_only** — a person does it: tell them what `step.say` says.
36
+ 4. Back to 2. After any change you or the person made by hand, call
37
+ `project_status` again — the next step is always read from the files.
38
+ 5. No step left (`step: null`) → the rails are done: tell the person what
39
+ `finished` says (where to try the model, what they could do next).
40
+
41
+ ## Bringing in data
42
+
43
+ `add_data` takes each file the person names WHERE IT IS — any path; never ask
44
+ them to move or copy it (`inbox/` is only a place they may drop files). Call
45
+ it with just the path first: it reads the layout itself (a header row naming
46
+ the languages decides the columns) and asks only when it cannot tell — don't
47
+ ask the person about columns or headers before trying. Use `into: "test"` for
48
+ the sentences a fluent speaker checked — the test set no model ever learns
49
+ from. If it asks which column is which language,
50
+ ask the person; never decide from the content. A result with `also` names
51
+ more in the same file (a dictionary's example sentences as pairs) and the
52
+ exact `add_data` call that brings it in — make it unless the person's terms
53
+ for that file rule it out. Every file the person named goes in: if
54
+ `files_not_added` appears, bring each in (its refusal says how) or tell the
55
+ person which is left out and why. Spreadsheets, Word tables,
56
+ Toolbox and LIFT dictionaries and ELAN files all come in this way; a word
57
+ list goes in with `into: "lexicon"` (the rails measure what looking words up
58
+ scores). Scripture (USFM) comes in only if the person asks, with the other
59
+ language's text as `usfm_source`, and no model learns from it until its
60
+ holders say so.
61
+
62
+ ## Privacy — you are an outside service
63
+
64
+ What you read goes to your model provider. Never open, print or summarize the
65
+ person's data files, test sentences or dictionaries — use `add_data`, which
66
+ reports counts and columns only. A file marked local-only stays on this
67
+ computer, and the tools refuse anything that would send it away.
68
+
69
+ ## Trying the model
70
+
71
+ Once the model is served, `try_translation` translates a few of the person's
72
+ own sentences with it. A model that learned from local-only sentences can
73
+ write them back, so for such a model you get only the address of its own page
74
+ — send the person there, and do not ask them to paste its translations to you.
75
+
76
+ ## Making it better
77
+
78
+ A served model is where rounds begin, not where the rails end. Each round: a
79
+ wishlist sheet says what would help most, the people write sentences they want
80
+ translated, the model drafts them onto a sheet,
81
+ the speaker marks each draft right or wrong and fixes the wrong ones, and the
82
+ model learns again. Forge judges each round on the dev sentences and keeps the
83
+ model in use unless the new one is measurably better. The sheets hold their
84
+ sentences: never open them; relay the counts the tools print.
85
+
86
+ Each round ends with a report step (`champollion improve report`). Tell the
87
+ people what it says: the progress first — each number with its unit and what
88
+ it was measured on — then its checks and its next steps, as things they can do.
89
+ Encourage them with what is real; never more than the numbers say.
90
+
91
+ `waiting_for_agreement` lists files whose speakers have not agreed yet — the
92
+ rails go on without them. Ask each one's question once a session, gently; only
93
+ when the person says everyone agreed, make its call with their yes.
94
+
95
+ ## Honesty
96
+
97
+ Pass on every caveat and warning the tools print, word for word. When you tell
98
+ the person what has been done — trained, scored, served — say it from
99
+ `true_now` in `project_status`, and never more than it says. The rails
100
+ promise the person will KNOW how good a model is on their own test sentences —
101
+ not that it is good. A small model trained on a few thousand pairs is usually a
102
+ phrasebook for phrases like the ones it saw. A fluent speaker decides what is
103
+ fit to use. Never call a score good or bad yourself; say what the tool said.
104
+ Then say it in plain words: chrF++ is out of 100 (higher is closer to the
105
+ speaker's own sentences); a range such as 14–22 is how sure the tools are, not
106
+ two results; a model is "the first small model", not its folder name.
107
+
108
+ Once a model is served, `project_status` carries `model_in_use` beside the
109
+ improve rounds' steps: its try page, and `for_the_person` — the command that
110
+ puts it into their app or website (`champollion connect`), which they run when
111
+ they want it. Tell them both once; the rounds go on either way.
112
+
113
+ ## Money and installs
114
+
115
+ Nothing installs, trains, spends or publishes without the person's yes (the
116
+ confirm mode). If a step needs `uv` and it is missing, tell the person to
117
+ install it from https://docs.astral.sh/uv/ — do not download installers.
118
+
119
+ ## When something refuses
120
+
121
+ A refusal says what, why and the fix. Pass it on in plain words and follow
122
+ the fix, or call `project_status`. A step that failed twice is not run again
123
+ until the person decides: tell them what it said, then either they fix it
124
+ (a corrected command in a terminal is fine) or they say to try again
125
+ (`confirmed: true`). `explain` has short texts on the rails,
126
+ privacy, quality, the modes, and stepping off the rails.
127
+
128
+ If they want help from people: "Having trouble, or want us to help with the
129
+ project? Let's talk" — https://champollion.dev. They never need us to finish.
package/instructions.md CHANGED
@@ -10,7 +10,7 @@ for it.
10
10
 
11
11
  ## The flow this server is built for
12
12
 
13
- Someone says: "Let's build a Cree model for our school" or "an Atya phrasebook for the hospital — how do we start?" Walk them through six stages. Each stage has tools. The human-readable version of this flow, with the exact commands, is https://champollion.dev/docs/build-mt-for-your-language — share it with the user.
13
+ Someone says: "Let's build a Cree model for our school" or "an Atya phrasebook for the hospital — how do we start?" Walk them through six stages. Each stage has tools. The human-readable version of this flow, with the exact commands, is https://champollion.dev/docs/agents/shell — share it with the user.
14
14
 
15
15
  | Stage | What it answers | Tools |
16
16
  |---|---|---|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "champollion-mcp-server",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "MCP server for Champollion — takes an agent from \"we want MT for our language\" to a measured model: cited language cards, benchmarks, local and hosted baselines, guarded nmt-forge training, contests, and the public benchmark queue.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  "bin",
12
12
  "src",
13
13
  "instructions.md",
14
+ "instructions-rails.md",
14
15
  "README.md",
15
16
  "CHANGELOG.md",
16
17
  "LICENSE"
@@ -27,7 +28,10 @@
27
28
  "machine-translation",
28
29
  "benchmarking",
29
30
  "low-resource-languages",
30
- "language-cards"
31
+ "language-cards",
32
+ "indigenous-languages",
33
+ "nmt-training",
34
+ "agents"
31
35
  ],
32
36
  "author": "Curtis Forbes",
33
37
  "license": "PolyForm-Noncommercial-1.0.0",
@@ -39,7 +43,7 @@
39
43
  },
40
44
  "dependencies": {
41
45
  "@modelcontextprotocol/sdk": "^1.30.0",
42
- "champollion": "^0.5.3",
46
+ "champollion": "^0.6.0",
43
47
  "zod": "^3.25 || ^4.0"
44
48
  },
45
49
  "repository": {
package/src/index.js CHANGED
@@ -58,6 +58,7 @@ import { selectCorpora, formatCorpora, MAX_LIMIT as CORPORA_MAX_LIMIT } from './
58
58
  import { metricReliability, formatReliability, cardFamilyClaims } from './tools/reliability.js';
59
59
  import { translateTexts, formatTranslateResult, translateResultIsError, TRANSLATE_METHODS } from './tools/translate.js';
60
60
  import { trainingGuardrails, formatTrainingGuardrails } from './tools/training.js';
61
+ import { registerRailsTools } from './rails.js';
61
62
  import {
62
63
  forgeTool, forgeToolsIn, FORGE_SCORING_TIMEOUT_MS, lintNextHint, lintSeverityCounts,
63
64
  compareSummary, compareNextHint, discoverCardCrossCheck, preflightNextHint,
@@ -81,18 +82,47 @@ async function loadInstructions(path) {
81
82
  }
82
83
  }
83
84
 
85
+ /** The rails profile's server: registerRailsTools + instructions-rails.md. */
86
+ async function createRailsServer(here) {
87
+ const instructions = await loadInstructions(resolve(here, '..', 'instructions-rails.md'));
88
+ const { version } = JSON.parse(await readFile(resolve(here, '..', 'package.json'), 'utf-8'));
89
+ const server = new McpServer({ name: 'champollion-rails', version },
90
+ instructions ? { instructions } : undefined);
91
+ registerRailsTools(server);
92
+ return {
93
+ async connect(transport) { await server.connect(transport); },
94
+ async start() {
95
+ await server.connect(new StdioServerTransport());
96
+ process.stderr.write('Champollion MCP server (rails profile) running on stdio\n');
97
+ },
98
+ };
99
+ }
100
+
84
101
  /**
85
- * Create and configure the MCP server with all tool registrations.
102
+ * Create and configure the MCP server.
103
+ *
104
+ * The default profile is `rails` (founder, 2026-10-10): six tools and short
105
+ * instructions that walk a language project end to end. `full` — every
106
+ * tool (queue, atlas, benchmarks, forge, publishing) — is by request:
107
+ * `--profile full` or CHAMPOLLION_MCP_PROFILE=full.
86
108
  *
87
109
  * @returns {{ start: () => Promise<void> }} Server object with a start method.
88
110
  */
89
- export async function createServer() {
111
+ export async function createServer({ profile = process.env.CHAMPOLLION_MCP_PROFILE || 'rails' } = {}) {
90
112
  // Resolve paths relative to this file (for reading instructions.md,
91
113
  // CONTRIBUTING.md, etc.). Computed before server creation because the
92
114
  // server's `instructions` are loaded from disk at construction time.
93
115
  const __dirname = dirname(fileURLToPath(import.meta.url));
94
116
  const repoRoot = resolve(__dirname, '..', '..');
95
117
 
118
+ // The rails profile (2026-10-09; the default since 2026-10-10): six tools
119
+ // and short instructions a small agent can hold — the language-project
120
+ // journey, end to end. The full profile below is unchanged.
121
+ if (profile === 'rails') return createRailsServer(__dirname);
122
+ if (profile !== 'full') {
123
+ throw new Error(`unknown MCP profile "${profile}" (rails or full)`);
124
+ }
125
+
96
126
  // Load the agent behavioral guide and hand it to the SDK as the server's
97
127
  // `instructions`. The SDK surfaces these in the MCP `initialize` result so
98
128
  // connecting clients can show the model server-level guidance. Falls back to