altalandi-mcp 0.7.7 → 0.7.9
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/README.md +116 -112
- package/card.html +1 -1
- package/format-response.mjs +4 -1
- package/package.json +1 -1
- package/report.mjs +5 -3
- package/server.mjs +5 -5
package/README.md
CHANGED
|
@@ -1,112 +1,116 @@
|
|
|
1
|
-
# altalandi-mcp
|
|
2
|
-
|
|
3
|
-
An [MCP](https://modelcontextprotocol.io) server that exposes
|
|
4
|
-
[Altalandi](https://altalandi.is), native-Icelandic refinement with a
|
|
5
|
-
meaning-preservation check, as a tool any MCP client can call. stdio transport.
|
|
6
|
-
|
|
7
|
-
## Set up (Claude Code or Codex)
|
|
8
|
-
|
|
9
|
-
Connect this computer once. This opens altalandi.is, you approve, and the key
|
|
10
|
-
is stored locally. You never copy it:
|
|
11
|
-
|
|
12
|
-
```bash
|
|
13
|
-
npx altalandi login
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
Then add the server. No key goes into any config file; the server reads the
|
|
17
|
-
one `altalandi login` stored:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
claude mcp add altalandi -s user -- npx -y altalandi-mcp # Claude Code
|
|
21
|
-
codex mcp add altalandi -- npx -y altalandi-mcp # Codex
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- `
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
|
100
|
-
|
|
101
|
-
| `
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
1
|
+
# altalandi-mcp
|
|
2
|
+
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server that exposes
|
|
4
|
+
[Altalandi](https://altalandi.is), native-Icelandic refinement with a
|
|
5
|
+
meaning-preservation check, as a tool any MCP client can call. stdio transport.
|
|
6
|
+
|
|
7
|
+
## Set up (Claude Code or Codex)
|
|
8
|
+
|
|
9
|
+
Connect this computer once. This opens altalandi.is, you approve, and the key
|
|
10
|
+
is stored locally. You never copy it:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx altalandi login
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then add the server. No key goes into any config file; the server reads the
|
|
17
|
+
one `altalandi login` stored:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
claude mcp add altalandi -s user -- npx -y altalandi-mcp@latest # Claude Code
|
|
21
|
+
codex mcp add altalandi -- npx -y altalandi-mcp@latest # Codex
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`@latest` makes each new session use the newest version. Added it without
|
|
25
|
+
`@latest` before? npx keeps its first copy: remove the server
|
|
26
|
+
(`claude mcp remove altalandi`) and add it again with the line above.
|
|
27
|
+
|
|
28
|
+
Ask for Icelandic and mention Altalandi, e.g. *"Write this in proper Icelandic
|
|
29
|
+
with Altalandi."* The agent calls the **`refine_icelandic`** tool.
|
|
30
|
+
|
|
31
|
+
### Without `altalandi login` (CI, services, other clients)
|
|
32
|
+
|
|
33
|
+
Set `ALTALANDI_API_KEY` in the server's environment, from your host's secret
|
|
34
|
+
manager. It takes precedence over a stored login. Mint a key at
|
|
35
|
+
<https://altalandi.is/stjornbord/forritarar>. Never commit a plaintext key in MCP JSON.
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{ "mcpServers": { "altalandi": { "command": "npx", "args": ["-y", "altalandi-mcp@latest"] } } }
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## The tool
|
|
42
|
+
|
|
43
|
+
`refine_icelandic({ text, domain?, register?, sourceLang?, report? })` returns two
|
|
44
|
+
parts. The first is one Markdown block: the verdict and nativeness score, the text
|
|
45
|
+
before and after (Fyrir / Eftir), a numbered list of changes with their reasons,
|
|
46
|
+
why a rewrite was refused if it was, and what the call cost with the balance and
|
|
47
|
+
free units left (Kostnaður). The second is the full refined text to apply. The
|
|
48
|
+
tool tells the agent to show the block to you as it is after every call, so you see
|
|
49
|
+
what changed and what it cost in the terminal or the editor, not only on the website.
|
|
50
|
+
|
|
51
|
+
- `domain`: `general` \| `marketing` \| `data-analysis` \| `childrens-fiction`
|
|
52
|
+
- `register`: `formal` \| `neutral` \| `warm`
|
|
53
|
+
- `sourceLang`: `auto` \| `en` \| `is`
|
|
54
|
+
- `noStore`: `true` means Altalandi never keeps this text. Otherwise free use (the free
|
|
55
|
+
daily units) is kept, with personal data scrubbed, to improve the service; paid use only
|
|
56
|
+
with consent ([privacy](https://altalandi.is/personuvernd#textabanki)).
|
|
57
|
+
- `report`: `true` also writes `./altalandi-reports/altalandi-<time>-<id>.md`
|
|
58
|
+
(input, output, every change with its reason and layer, warnings, verdict).
|
|
59
|
+
Default: on when `ALTALANDI_REPORT=1`. Reports contain the text, so they are opt-in.
|
|
60
|
+
|
|
61
|
+
**Meaning guarantee:** on Icelandic input where the rewrite can't be verified to
|
|
62
|
+
preserve meaning, the tool returns the **original unchanged** and flags it,
|
|
63
|
+
never a silent change.
|
|
64
|
+
|
|
65
|
+
## Proofreading
|
|
66
|
+
|
|
67
|
+
`proofread_icelandic({ text, suggestions?, minPrecision?, noStore? })` fixes only
|
|
68
|
+
proven errors in Icelandic you wrote (punctuation, word boundaries, a misspelt word
|
|
69
|
+
one slip from the right one) and keeps your wording. Everything else comes back as
|
|
70
|
+
numbered **suggestions**, each with its reason and, when known, how sure it is. The
|
|
71
|
+
agent shows you each one and applies only those you accept.
|
|
72
|
+
|
|
73
|
+
- `suggestions`: `offer` (default: you choose) \| `apply` (the kinds of suggestion
|
|
74
|
+
measured right at least `minPrecision` of the time are applied and listed; the rest
|
|
75
|
+
are offered) \| `off` (only the safe fixes).
|
|
76
|
+
- `minPrecision`: 0.5 to 0.99, default 0.9. A suggestion with a digit, or one that
|
|
77
|
+
changes a name, is never applied unasked.
|
|
78
|
+
|
|
79
|
+
Then `record_suggestion_decisions({ resultId, decisions })` tells Altalandi which
|
|
80
|
+
suggestions you used or ignored (numbers only, no text, free). That is how the
|
|
81
|
+
suggestions get better.
|
|
82
|
+
|
|
83
|
+
Only **meaning preserved** is a verified result. **MEANING UNVERIFIED** means the
|
|
84
|
+
check did not run or could not complete; review that output before use. API
|
|
85
|
+
warnings are included under a clear `Warnings:` section in the tool's result.
|
|
86
|
+
|
|
87
|
+
## The Altalandi card
|
|
88
|
+
|
|
89
|
+
In hosts that render MCP Apps (Claude Desktop, claude.ai), results appear in an
|
|
90
|
+
Altalandi card: the text with a copy button, whether the meaning check passed,
|
|
91
|
+
each change with its reason, and for proofreading each suggestion with **Nota**
|
|
92
|
+
and **Hunsa** buttons. The card records the decisions itself. Terminals (Claude
|
|
93
|
+
Code, Codex) get the same text as before. The server offers the card as the
|
|
94
|
+
resource `ui://altalandi/result-v1.html`; both tools return its data as
|
|
95
|
+
`structuredContent`.
|
|
96
|
+
|
|
97
|
+
## Config
|
|
98
|
+
|
|
99
|
+
| env | default | purpose |
|
|
100
|
+
|-----|---------|---------|
|
|
101
|
+
| `ALTALANDI_API_KEY` | (the key from `altalandi login`) | your key (Bearer) |
|
|
102
|
+
| `ALTALANDI_CONFIG_DIR` | `%APPDATA%\altalandi` / `~/.config/altalandi` | where `altalandi login` stored it |
|
|
103
|
+
| `ALTALANDI_BASE_URL` | `https://altalandi.is/api` | API base (override for self-hosting) |
|
|
104
|
+
| `ALTALANDI_REPORT` | — | `1` = write a report for every call |
|
|
105
|
+
| `ALTALANDI_REPORT_DIR` | `./altalandi-reports` (the server's working folder) | where reports go |
|
|
106
|
+
|
|
107
|
+
Each API request has a fixed 300-second deadline, including the response body,
|
|
108
|
+
so a stalled provider cannot leave the tool hanging.
|
|
109
|
+
|
|
110
|
+
## Cost
|
|
111
|
+
|
|
112
|
+
Every account gets 2,000 free units a day on the standard writer (about 2,000
|
|
113
|
+
words); past that, calls are paid from prepaid credit in krónur, and a new account
|
|
114
|
+
also starts with 500 kr. of trial credit. See <https://altalandi.is/verd>.
|
|
115
|
+
|
|
116
|
+
MIT licensed. Requires Node ≥ 18.
|
package/card.html
CHANGED
|
@@ -553,7 +553,7 @@
|
|
|
553
553
|
return (x < 0 ? "-" : "") + whole.replace(/\B(?=(\d{3})+(?!\d))/g, ".") + (frac && Number(frac) ? "," + frac : "");
|
|
554
554
|
}
|
|
555
555
|
const isk = (n) => `${num(n, Number(n) % 1 ? 2 : 0)} kr.`;
|
|
556
|
-
const LABEL = { both: "Tvær athuganir sammála", one: "Ein athugun", rule: "Regla
|
|
556
|
+
const LABEL = { both: "Tvær athuganir sammála", one: "Ein athugun", rule: "Regla" };
|
|
557
557
|
const KIND = { L1: "Orðalag", L2: "Málfarsvilla", L3: "Yfirferð" };
|
|
558
558
|
function announce(msg) { const live = $("live"); live.textContent = ""; if (msg) setTimeout(() => { live.textContent = msg; }, 40); }
|
|
559
559
|
const quote = (s) => `„${(s || "").trim()}“`;
|
package/format-response.mjs
CHANGED
|
@@ -50,11 +50,13 @@ export function formatRefineResponse(data) {
|
|
|
50
50
|
if (warnings.length) {
|
|
51
51
|
lines.push("", "Warnings:", ...warnings.map((warning) => `- ${String(warning)}`));
|
|
52
52
|
}
|
|
53
|
+
// The run's page holds the text, the checks and the person's approval; give it on.
|
|
54
|
+
if (data?.runUrl) lines.push("", `Run page (text, checks, approval): ${data.runUrl}`);
|
|
53
55
|
|
|
54
56
|
return lines.join("\n");
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
const LABELS = { both: "both checks agree", one: "one check", rule: "rule
|
|
59
|
+
const LABELS = { both: "both checks agree", one: "one check", rule: "rule" };
|
|
58
60
|
|
|
59
61
|
/** A task=proofread response for an agent: the corrected text, what was fixed,
|
|
60
62
|
* the numbered suggestions to offer, and what to do once the person decides. */
|
|
@@ -81,6 +83,7 @@ export function formatProofreadResponse(data) {
|
|
|
81
83
|
} else {
|
|
82
84
|
lines.push("No suggestions.");
|
|
83
85
|
}
|
|
86
|
+
if (data?.runUrl) lines.push("", `Run page (text, checks, approval): ${data.runUrl}`);
|
|
84
87
|
const unresolved = Array.isArray(data?.unresolved) ? data.unresolved : [];
|
|
85
88
|
if (unresolved.length) {
|
|
86
89
|
lines.push("", "Likely errors left as they were (point them out to the person):");
|
package/package.json
CHANGED
package/report.mjs
CHANGED
|
@@ -88,7 +88,7 @@ export function reportMarkdown(input, data, options = {}) {
|
|
|
88
88
|
`| Málsnið | ${options.register || "neutral"} |`,
|
|
89
89
|
`| Innfæddni | ${q.nativenessScore ?? "n/a"}/100 |`,
|
|
90
90
|
`| Ritill | ${tier.writer || "standard"} / ${tier.scoring || "?"} |`,
|
|
91
|
-
`| Auðkenni | ${data?.id || "?"} |`,
|
|
91
|
+
`| Auðkenni | ${data?.runUrl ? `[${data.id}](${data.runUrl})` : data?.id || "?"} |`,
|
|
92
92
|
"",
|
|
93
93
|
"## Fyrir", "", fence(input), "",
|
|
94
94
|
"## Eftir", "", fence(data?.output), "",
|
|
@@ -187,7 +187,7 @@ export function costLines(data, before, after) {
|
|
|
187
187
|
else if (daily != null && daily > 0) parts.push(`ókeypis ${formatInt(daily)} einingar á dag`);
|
|
188
188
|
if (parts.length) lines.push(`- Staða: ${parts.join(" · ")}`);
|
|
189
189
|
else lines.push("- Staða: náðist ekki að sækja (`npx altalandi whoami`)");
|
|
190
|
-
if (balance != null && balance < 50) lines.push("- Bæta við inneign: https://altalandi.is/
|
|
190
|
+
if (balance != null && balance < 50) lines.push("- Bæta við inneign: https://altalandi.is/stjornbord/inneign");
|
|
191
191
|
return lines;
|
|
192
192
|
}
|
|
193
193
|
|
|
@@ -279,7 +279,9 @@ export function reviewMarkdown(input, data, { before = null, after = null, maxCh
|
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
out.push("**Kostnaður**", ...costLines(data, before, after));
|
|
282
|
-
|
|
282
|
+
// The run's own page: the text sent, the result, the checks and the review.
|
|
283
|
+
if (data?.runUrl) out.push(`- Verkið: ${data.runUrl}`);
|
|
284
|
+
else if (data?.id) out.push(`- Auðkenni: \`${data.id}\``);
|
|
283
285
|
return out.join("\n") + "\n";
|
|
284
286
|
}
|
|
285
287
|
|
package/server.mjs
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// The key comes from ALTALANDI_API_KEY or, failing that, from the login that
|
|
6
6
|
// `npx altalandi login` stored on this computer, so no config file ever holds
|
|
7
7
|
// a secret:
|
|
8
|
-
// claude mcp add altalandi -s user -- npx -y altalandi-mcp
|
|
9
|
-
// codex mcp add altalandi -- npx -y altalandi-mcp
|
|
8
|
+
// claude mcp add altalandi -s user -- npx -y altalandi-mcp@latest
|
|
9
|
+
// codex mcp add altalandi -- npx -y altalandi-mcp@latest
|
|
10
10
|
//
|
|
11
11
|
// Hosts that render MCP Apps (Claude Desktop, claude.ai) also get the
|
|
12
12
|
// Altalandi card (card.mjs); terminals read the text, which is unchanged.
|
|
@@ -22,7 +22,7 @@ import { resolveKey } from "./key.mjs";
|
|
|
22
22
|
import { reportEnabled, reviewMarkdown, writeReport } from "./report.mjs";
|
|
23
23
|
|
|
24
24
|
const BASE = (process.env.ALTALANDI_BASE_URL || "https://altalandi.is/api").replace(/\/$/, "");
|
|
25
|
-
const server = new McpServer({ name: "altalandi", version: "0.7.
|
|
25
|
+
const server = new McpServer({ name: "altalandi", version: "0.7.9" });
|
|
26
26
|
|
|
27
27
|
const CARD_NOTE = " When the host shows Altalandi's card, the card already shows this: do not repeat it, add at most a short sentence.";
|
|
28
28
|
|
|
@@ -127,7 +127,7 @@ server.registerTool(
|
|
|
127
127
|
"proofread_icelandic",
|
|
128
128
|
{
|
|
129
129
|
title: "Proofread Icelandic",
|
|
130
|
-
description: "Proofread Icelandic text the person wrote, keeping their wording. Proven errors (punctuation, word boundaries, a misspelt word one slip from the right one) are fixed in the returned text. Other corrections come back as numbered suggestions with a reason and, when known, how sure each is ('both checks agree', 'one check', 'rule
|
|
130
|
+
description: "Proofread Icelandic text the person wrote, keeping their wording. Proven errors (punctuation, word boundaries, a misspelt word one slip from the right one) are fixed in the returned text. Other corrections come back as numbered suggestions with a reason and, when known, how sure each is ('both checks agree', 'one check', 'rule'). Show the corrected text and every suggestion, ask which to apply, apply only those the person accepts, then call record_suggestion_decisions with the resultId and a decision for every suggestion. suggestions='apply' applies the kinds measured right at least minPrecision of the time and offers the rest; 'off' returns only the safe fixes. When the host shows Altalandi's card, the person chooses in the card and the card records the decisions: then do not list the suggestions again, just say briefly what was found.",
|
|
131
131
|
inputSchema: {
|
|
132
132
|
text: z.string().describe("The Icelandic text to proofread."),
|
|
133
133
|
suggestions: z.enum(["offer", "apply", "off"]).optional()
|
|
@@ -195,7 +195,7 @@ server.tool(
|
|
|
195
195
|
: me.freeDailyUnits ? `${me.freeDailyUnits} free units a day` : "no free daily units";
|
|
196
196
|
return { content: [{ type: "text", text:
|
|
197
197
|
`**Altalandi:** ${me.username ?? "connected"} · inneign ${isk(me.balanceIsk)} · ${free}.\n` +
|
|
198
|
-
"Add credit: https://altalandi.is/
|
|
198
|
+
"Add credit: https://altalandi.is/stjornbord/inneign" }] };
|
|
199
199
|
},
|
|
200
200
|
);
|
|
201
201
|
|