simple-answers 1.0.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/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +14 -0
- package/LICENSE +21 -0
- package/README.md +141 -0
- package/bin/cli.js +7 -0
- package/commands/simple-off.md +11 -0
- package/commands/simple.md +23 -0
- package/hooks/hooks.json +14 -0
- package/hooks/simple-style.sh +33 -0
- package/package.json +41 -0
- package/skills/plain-english/SKILL.md +39 -0
- package/src/readability.js +222 -0
- package/src/server.js +217 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simple-answers",
|
|
3
|
+
"owner": {
|
|
4
|
+
"name": "tehseenullah786"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "simple-answers",
|
|
9
|
+
"source": "./",
|
|
10
|
+
"description": "Makes Claude answer in short, simple English."
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"description": "Plugins that make Claude answer in short, simple English."
|
|
14
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simple-answers",
|
|
3
|
+
"description": "Makes Claude answer in short, simple English. Answer first, no jargon, no long walls of text.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "tehseenullah786"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"plain-english",
|
|
10
|
+
"short-answers",
|
|
11
|
+
"eli5",
|
|
12
|
+
"readability"
|
|
13
|
+
]
|
|
14
|
+
}
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tehseenullah786
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Simple Answers
|
|
2
|
+
|
|
3
|
+
Make AI answers **short and easy to read**.
|
|
4
|
+
|
|
5
|
+
Answer first. Small sentences. No jargon. No wall of text.
|
|
6
|
+
|
|
7
|
+
Works in Claude Code, Claude Desktop, Cursor, and any app that supports MCP.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install (MCP — works everywhere)
|
|
12
|
+
|
|
13
|
+
**Claude Code**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
claude mcp add simple-answers -- npx -y simple-answers
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Claude Desktop / Cursor** — add this to the MCP config file:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"mcpServers": {
|
|
24
|
+
"simple-answers": {
|
|
25
|
+
"command": "npx",
|
|
26
|
+
"args": ["-y", "simple-answers"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Nothing to download first. `npx` fetches it for you.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## What you get
|
|
37
|
+
|
|
38
|
+
### Tools (the AI calls these)
|
|
39
|
+
|
|
40
|
+
| Tool | What it does |
|
|
41
|
+
|---|---|
|
|
42
|
+
| `check_readability` | Scores text. Finds long sentences, hard words, filler. Real maths, no AI. |
|
|
43
|
+
| `simplify` | Gives the AI the exact rules + the score, so it rewrites your text simply. |
|
|
44
|
+
| `style_rules` | Turns on plain-English mode for the rest of the chat. |
|
|
45
|
+
|
|
46
|
+
### Prompts (you pick these)
|
|
47
|
+
|
|
48
|
+
In Claude Code they appear as slash commands:
|
|
49
|
+
|
|
50
|
+
| Prompt | What it does |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `/simple` | Simple mode ON — and say the last answer again in easy words |
|
|
53
|
+
| `/simple-off` | Simple mode OFF — normal replies again |
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Example
|
|
58
|
+
|
|
59
|
+
Ask it to check this sentence:
|
|
60
|
+
|
|
61
|
+
> Notwithstanding the erroneous implementation, we will utilize the existing
|
|
62
|
+
> paradigm in order to facilitate a seamless migration.
|
|
63
|
+
|
|
64
|
+
It answers:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Reading ease: -5 (very hard). Target: 60 or more.
|
|
68
|
+
Length: 26 words, 1 sentences, 26 words per sentence. Target: 18 or fewer.
|
|
69
|
+
|
|
70
|
+
Replace these words:
|
|
71
|
+
notwithstanding -> even so
|
|
72
|
+
erroneous -> wrong
|
|
73
|
+
utilize -> use
|
|
74
|
+
paradigm -> model
|
|
75
|
+
facilitate -> help
|
|
76
|
+
seamless -> smooth
|
|
77
|
+
|
|
78
|
+
Filler to delete: in order to
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Good for README files, error messages, docs and UI text — not only chat.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Also: a Claude Code plugin
|
|
86
|
+
|
|
87
|
+
The same repo is a Claude Code plugin. The plugin adds one extra thing the MCP
|
|
88
|
+
server cannot do: a **hook** that puts the plain-English rules into *every*
|
|
89
|
+
message automatically, with no tool call.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
/plugin marketplace add tehseenullah786/simple-answers
|
|
93
|
+
/plugin install simple-answers@simple-answers
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Short command names
|
|
97
|
+
|
|
98
|
+
Plugin commands are typed with the plugin name in front:
|
|
99
|
+
`/simple-answers:simple`, `/simple-answers:simple-off`.
|
|
100
|
+
|
|
101
|
+
To type just `/simple` and `/simple-off`, run this once:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
./install-shortcuts.sh
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It copies the commands into `~/.claude/commands/`. Restart Claude Code after.
|
|
108
|
+
|
|
109
|
+
Turn it off and on — just type:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
/simple-off normal replies again
|
|
113
|
+
/simple simple replies again
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
MCP or plugin — which one?
|
|
117
|
+
|
|
118
|
+
| You want | Use |
|
|
119
|
+
|---|---|
|
|
120
|
+
| Simple answers in Claude Desktop, Cursor, other apps | MCP |
|
|
121
|
+
| Simple answers in Claude Code, automatic every time | plugin |
|
|
122
|
+
| Both | install both — they work together |
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Change the rules
|
|
127
|
+
|
|
128
|
+
- MCP rules: `src/server.js`, the `STYLE_RULES` text at the top.
|
|
129
|
+
- Word replacements: `src/readability.js`, the `JARGON` list.
|
|
130
|
+
- Plugin hook: `hooks/simple-style.sh`.
|
|
131
|
+
|
|
132
|
+
## Develop
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install
|
|
136
|
+
npm test # talks to the server over stdio and prints the output
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## License
|
|
140
|
+
|
|
141
|
+
MIT
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Simple answers OFF — normal, full replies again
|
|
3
|
+
allowed-tools: Bash(touch:*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
!`touch "$HOME/.claude/simple-answers-off" && echo "simple mode off"`
|
|
7
|
+
|
|
8
|
+
Say exactly this to the user and nothing else:
|
|
9
|
+
|
|
10
|
+
**Simple mode is OFF.** Normal replies start from your next message.
|
|
11
|
+
Turn it back on with `/simple`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Simple answers ON — short, easy English from now on
|
|
3
|
+
allowed-tools: Bash(rm:*)
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
!`rm -f "$HOME/.claude/simple-answers-off" && echo "simple mode on"`
|
|
7
|
+
|
|
8
|
+
Simple mode is now ON. Every answer from now on is short and easy.
|
|
9
|
+
|
|
10
|
+
Now say your **previous answer** again, simply:
|
|
11
|
+
|
|
12
|
+
- 80 words maximum.
|
|
13
|
+
- Sentences of 12 words or fewer.
|
|
14
|
+
- No technical words. If one is unavoidable, explain it in brackets.
|
|
15
|
+
- Start with the most important point.
|
|
16
|
+
- 4 bullets maximum.
|
|
17
|
+
- Keep every command and file path exactly as it was.
|
|
18
|
+
|
|
19
|
+
Do not do new work. Only rewrite what you already said.
|
|
20
|
+
|
|
21
|
+
If there is no previous answer, just say one line: "Simple mode is on. Ask me anything."
|
|
22
|
+
|
|
23
|
+
$ARGUMENTS
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# simple-answers: adds plain-English writing rules to every prompt.
|
|
3
|
+
# Turn it off: touch ~/.claude/simple-answers-off
|
|
4
|
+
# Turn it on : rm -f ~/.claude/simple-answers-off
|
|
5
|
+
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
STATE_FILE="${HOME:-/nonexistent}/.claude/simple-answers-off"
|
|
9
|
+
[ -f "$STATE_FILE" ] && exit 0
|
|
10
|
+
|
|
11
|
+
cat <<'RULES'
|
|
12
|
+
<simple-answers-style>
|
|
13
|
+
How to write your reply to the user (writing style only — never change the
|
|
14
|
+
correctness or completeness of the work itself):
|
|
15
|
+
|
|
16
|
+
1. Put the answer in the FIRST sentence. No warm-up, no repeating the question.
|
|
17
|
+
2. Short sentences. Aim for 15 words or fewer. One idea per sentence.
|
|
18
|
+
3. Easy words. No jargon. If a technical word is required, put a 3-5 word
|
|
19
|
+
meaning right after it in brackets.
|
|
20
|
+
4. Keep the whole reply under ~120 words, unless the user asked for detail,
|
|
21
|
+
asked for a plan, or the answer is a list of steps.
|
|
22
|
+
5. Use bullets or numbered steps for anything with more than two parts.
|
|
23
|
+
6. Say what you did and what the user should do next. Skip the reasoning
|
|
24
|
+
story, skip the options you did not take, skip the summary of the summary.
|
|
25
|
+
7. Bold the one thing that matters most, if anything does.
|
|
26
|
+
8. Code blocks, file paths and commands stay exact and complete. Simplify the
|
|
27
|
+
words around the code, never the code.
|
|
28
|
+
9. If the honest answer is "I don't know" or "it failed", say that plainly.
|
|
29
|
+
|
|
30
|
+
The user is not a native English speaker. Clear beats clever.
|
|
31
|
+
</simple-answers-style>
|
|
32
|
+
RULES
|
|
33
|
+
exit 0
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "simple-answers",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server that makes AI answers short and easy to read. Scores text for readability and rewrites it in plain English.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"simple-answers": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "src/server.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"src",
|
|
13
|
+
"commands",
|
|
14
|
+
"hooks",
|
|
15
|
+
"skills",
|
|
16
|
+
".claude-plugin",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"modelcontextprotocol",
|
|
26
|
+
"claude",
|
|
27
|
+
"plain-english",
|
|
28
|
+
"readability",
|
|
29
|
+
"eli5",
|
|
30
|
+
"simplify"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"author": "tehseenullah786",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.30.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"start": "node bin/cli.js",
|
|
39
|
+
"test": "node test/smoke.js && node test/full-test.mjs"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: plain-english
|
|
3
|
+
description: Explain code, errors, logs or documents in simple English for a non-native speaker. Use when the user says they do not understand, asks "what does this mean", "explain simply", "in easy words", "I am confused", or asks for an explanation of an error message, a file, or a technical document.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Plain English
|
|
7
|
+
|
|
8
|
+
Explain the thing so a smart person with basic English understands it.
|
|
9
|
+
|
|
10
|
+
## Shape of the answer
|
|
11
|
+
|
|
12
|
+
1. **One sentence: what it is.**
|
|
13
|
+
2. **One sentence: why it matters to the user right now.**
|
|
14
|
+
3. Then, only if needed, 3-5 bullets of detail.
|
|
15
|
+
4. Last line: what to do next.
|
|
16
|
+
|
|
17
|
+
## Rules
|
|
18
|
+
|
|
19
|
+
- 12-15 words per sentence. One idea per sentence.
|
|
20
|
+
- Use everyday words: "run" not "execute", "fix" not "remediate",
|
|
21
|
+
"sends" not "dispatches", "wrong" not "erroneous".
|
|
22
|
+
- Every technical word gets a meaning in brackets the first time:
|
|
23
|
+
"a signal (a message the app sends when something happens)".
|
|
24
|
+
- Use a small real-world comparison when a concept is abstract.
|
|
25
|
+
- Never paste a long block of code or logs back to the user. Quote the one
|
|
26
|
+
line that matters and say what it means.
|
|
27
|
+
- Do not apologise, do not flatter, do not add "I hope this helps".
|
|
28
|
+
|
|
29
|
+
## For an error message
|
|
30
|
+
|
|
31
|
+
Answer in this order:
|
|
32
|
+
- What broke, in plain words.
|
|
33
|
+
- The one line in the error that proves it (file and line number).
|
|
34
|
+
- The fix, as a command or a small edit.
|
|
35
|
+
|
|
36
|
+
## What not to simplify
|
|
37
|
+
|
|
38
|
+
Commands, file paths, variable names, and code stay exact. Making a command
|
|
39
|
+
"simpler" makes it wrong.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic readability analysis. No AI, no network.
|
|
3
|
+
* Everything here is plain arithmetic over the text.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Words that look hard by syllable count but are everyday English. */
|
|
7
|
+
const COMMON_LONG_WORDS = new Set([
|
|
8
|
+
"another", "anything", "everything", "everyone", "everybody", "already",
|
|
9
|
+
"understand", "understanding", "different", "important", "example",
|
|
10
|
+
"remember", "together", "usually", "probably", "actually", "family",
|
|
11
|
+
"company", "area", "video", "audio", "idea", "every", "never", "over",
|
|
12
|
+
"into", "open", "easy", "easier", "easiest", "only", "also", "even",
|
|
13
|
+
"many", "money", "people", "using", "user", "users", "other", "others",
|
|
14
|
+
"before", "after", "about", "because", "however", "final", "finally",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
/** Jargon -> plain replacement. Kept short on purpose: only common offenders. */
|
|
18
|
+
const JARGON = {
|
|
19
|
+
utilize: "use",
|
|
20
|
+
utilise: "use",
|
|
21
|
+
leverage: "use",
|
|
22
|
+
facilitate: "help",
|
|
23
|
+
implement: "build",
|
|
24
|
+
instantiate: "create",
|
|
25
|
+
initialize: "set up",
|
|
26
|
+
initialise: "set up",
|
|
27
|
+
terminate: "stop",
|
|
28
|
+
execute: "run",
|
|
29
|
+
invoke: "call",
|
|
30
|
+
propagate: "pass on",
|
|
31
|
+
aggregate: "collect",
|
|
32
|
+
disseminate: "share",
|
|
33
|
+
remediate: "fix",
|
|
34
|
+
mitigate: "reduce",
|
|
35
|
+
ascertain: "find out",
|
|
36
|
+
endeavor: "try",
|
|
37
|
+
endeavour: "try",
|
|
38
|
+
commence: "start",
|
|
39
|
+
cease: "stop",
|
|
40
|
+
prior: "before",
|
|
41
|
+
subsequent: "next",
|
|
42
|
+
subsequently: "then",
|
|
43
|
+
additionally: "also",
|
|
44
|
+
furthermore: "also",
|
|
45
|
+
moreover: "also",
|
|
46
|
+
nevertheless: "but",
|
|
47
|
+
notwithstanding: "even so",
|
|
48
|
+
approximately: "about",
|
|
49
|
+
sufficient: "enough",
|
|
50
|
+
numerous: "many",
|
|
51
|
+
obtain: "get",
|
|
52
|
+
provide: "give",
|
|
53
|
+
require: "need",
|
|
54
|
+
attempt: "try",
|
|
55
|
+
demonstrate: "show",
|
|
56
|
+
indicate: "show",
|
|
57
|
+
modify: "change",
|
|
58
|
+
optimize: "improve",
|
|
59
|
+
optimise: "improve",
|
|
60
|
+
erroneous: "wrong",
|
|
61
|
+
anomaly: "problem",
|
|
62
|
+
paradigm: "model",
|
|
63
|
+
robust: "strong",
|
|
64
|
+
granular: "detailed",
|
|
65
|
+
holistic: "complete",
|
|
66
|
+
seamless: "smooth",
|
|
67
|
+
canonical: "standard",
|
|
68
|
+
idempotent: "safe to repeat",
|
|
69
|
+
deterministic: "always the same",
|
|
70
|
+
asynchronous: "not waiting",
|
|
71
|
+
orchestrate: "coordinate",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const HEDGES = [
|
|
75
|
+
"it is worth noting", "it should be noted", "as you may know",
|
|
76
|
+
"i hope this helps", "please note that", "in order to",
|
|
77
|
+
"at the end of the day", "needless to say", "it is important to note",
|
|
78
|
+
"that being said", "with that said", "as previously mentioned",
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
/** Strip fenced code, inline code and URLs — those must never be simplified. */
|
|
82
|
+
export function stripCode(text) {
|
|
83
|
+
return text
|
|
84
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
85
|
+
.replace(/`[^`\n]*`/g, " ")
|
|
86
|
+
.replace(/https?:\/\/\S+/g, " ")
|
|
87
|
+
.replace(/^\s*[|+].*$/gm, " ");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function countSyllables(word) {
|
|
91
|
+
const w = word.toLowerCase().replace(/[^a-z]/g, "");
|
|
92
|
+
if (!w) return 0;
|
|
93
|
+
if (w.length <= 3) return 1;
|
|
94
|
+
const trimmed = w
|
|
95
|
+
.replace(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, "")
|
|
96
|
+
.replace(/^y/, "");
|
|
97
|
+
const groups = trimmed.match(/[aeiouy]{1,2}/g);
|
|
98
|
+
return groups ? groups.length : 1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function splitSentences(text) {
|
|
102
|
+
return text
|
|
103
|
+
.split(/(?<=[.!?])\s+|\n{2,}|\n(?=[-*\d])/)
|
|
104
|
+
.map((s) => s.trim())
|
|
105
|
+
.filter((s) => s.replace(/[^a-zA-Z]/g, "").length > 1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function splitWords(text) {
|
|
109
|
+
return text.match(/[A-Za-z][A-Za-z'’-]*/g) || [];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Flesch Reading Ease: 90-100 very easy, 60-70 plain, below 30 very hard. */
|
|
113
|
+
export function fleschReadingEase(words, sentences, syllables) {
|
|
114
|
+
if (!words || !sentences) return 100;
|
|
115
|
+
return 206.835 - 1.015 * (words / sentences) - 84.6 * (syllables / words);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function easeLabel(score) {
|
|
119
|
+
if (score >= 80) return "very easy";
|
|
120
|
+
if (score >= 60) return "easy — good for a non-native reader";
|
|
121
|
+
if (score >= 50) return "a bit hard";
|
|
122
|
+
if (score >= 30) return "hard";
|
|
123
|
+
return "very hard";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function analyze(rawText, { maxSentenceWords = 18 } = {}) {
|
|
127
|
+
const text = stripCode(String(rawText || ""));
|
|
128
|
+
const sentences = splitSentences(text);
|
|
129
|
+
const words = splitWords(text);
|
|
130
|
+
const syllables = words.reduce((n, w) => n + countSyllables(w), 0);
|
|
131
|
+
|
|
132
|
+
const ease = fleschReadingEase(words.length, sentences.length, syllables);
|
|
133
|
+
const avgSentence = sentences.length ? words.length / sentences.length : 0;
|
|
134
|
+
|
|
135
|
+
const longSentences = sentences
|
|
136
|
+
.map((s, i) => ({ index: i + 1, words: splitWords(s).length, text: s }))
|
|
137
|
+
.filter((s) => s.words > maxSentenceWords)
|
|
138
|
+
.sort((a, b) => b.words - a.words)
|
|
139
|
+
.slice(0, 8);
|
|
140
|
+
|
|
141
|
+
const hardWords = [];
|
|
142
|
+
const seenHard = new Set();
|
|
143
|
+
for (const w of words) {
|
|
144
|
+
const lower = w.toLowerCase();
|
|
145
|
+
if (seenHard.has(lower)) continue;
|
|
146
|
+
if (COMMON_LONG_WORDS.has(lower)) continue;
|
|
147
|
+
if (lower.length < 8) continue;
|
|
148
|
+
if (countSyllables(lower) >= 4) {
|
|
149
|
+
seenHard.add(lower);
|
|
150
|
+
hardWords.push(lower);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const jargon = [];
|
|
155
|
+
const seenJargon = new Set();
|
|
156
|
+
for (const w of words) {
|
|
157
|
+
const lower = w.toLowerCase();
|
|
158
|
+
const base = lower.replace(/(s|es|ed|ing|ly)$/, "");
|
|
159
|
+
const hit = JARGON[lower] ?? JARGON[base];
|
|
160
|
+
if (hit && !seenJargon.has(lower)) {
|
|
161
|
+
seenJargon.add(lower);
|
|
162
|
+
jargon.push({ word: lower, use: hit });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const lowerText = text.toLowerCase();
|
|
167
|
+
const hedges = HEDGES.filter((h) => lowerText.includes(h));
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
words: words.length,
|
|
171
|
+
sentences: sentences.length,
|
|
172
|
+
avgSentenceWords: Number(avgSentence.toFixed(1)),
|
|
173
|
+
readingEase: Number(ease.toFixed(1)),
|
|
174
|
+
readingEaseLabel: easeLabel(ease),
|
|
175
|
+
longSentences,
|
|
176
|
+
hardWords: hardWords.slice(0, 15),
|
|
177
|
+
jargon: jargon.slice(0, 15),
|
|
178
|
+
hedges,
|
|
179
|
+
passes:
|
|
180
|
+
ease >= 60 &&
|
|
181
|
+
avgSentence <= maxSentenceWords &&
|
|
182
|
+
jargon.length === 0 &&
|
|
183
|
+
hedges.length === 0,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function formatReport(r, { maxWords } = {}) {
|
|
188
|
+
const lines = [];
|
|
189
|
+
lines.push(
|
|
190
|
+
`Reading ease: ${r.readingEase} (${r.readingEaseLabel}). Target: 60 or more.`
|
|
191
|
+
);
|
|
192
|
+
lines.push(
|
|
193
|
+
`Length: ${r.words} words, ${r.sentences} sentences, ${r.avgSentenceWords} words per sentence. Target: 18 or fewer.`
|
|
194
|
+
);
|
|
195
|
+
if (maxWords && r.words > maxWords) {
|
|
196
|
+
lines.push(`Too long: cut to ${maxWords} words or fewer.`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (r.longSentences.length) {
|
|
200
|
+
lines.push("", "Sentences to split (longest first):");
|
|
201
|
+
for (const s of r.longSentences) {
|
|
202
|
+
lines.push(` [${s.words} words] ${s.text.slice(0, 160)}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (r.jargon.length) {
|
|
206
|
+
lines.push("", "Replace these words:");
|
|
207
|
+
for (const j of r.jargon) lines.push(` ${j.word} -> ${j.use}`);
|
|
208
|
+
}
|
|
209
|
+
if (r.hardWords.length) {
|
|
210
|
+
lines.push(
|
|
211
|
+
"",
|
|
212
|
+
`Hard words (explain in brackets or replace): ${r.hardWords.join(", ")}`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (r.hedges.length) {
|
|
216
|
+
lines.push("", `Filler to delete: ${r.hedges.join("; ")}`);
|
|
217
|
+
}
|
|
218
|
+
if (r.passes) {
|
|
219
|
+
lines.push("", "This text already reads well. Do not rewrite it.");
|
|
220
|
+
}
|
|
221
|
+
return lines.join("\n");
|
|
222
|
+
}
|
package/src/server.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { analyze, formatReport } from "./readability.js";
|
|
5
|
+
|
|
6
|
+
const STYLE_RULES = `Write the reply like this:
|
|
7
|
+
1. Answer in the first sentence. No warm-up, no repeating the question.
|
|
8
|
+
2. Short sentences, 15 words or fewer. One idea per sentence.
|
|
9
|
+
3. Everyday words only. A needed technical word gets a 3-5 word meaning in brackets.
|
|
10
|
+
4. Bullets or numbered steps for anything with more than two parts.
|
|
11
|
+
5. Say what to do next. Cut the reasoning story and the summary of the summary.
|
|
12
|
+
6. Commands, file paths and code stay exact. Simplify the words, never the code.
|
|
13
|
+
7. If something failed or is unknown, say so plainly.`;
|
|
14
|
+
|
|
15
|
+
export function createServer() {
|
|
16
|
+
const server = new McpServer({
|
|
17
|
+
name: "simple-answers",
|
|
18
|
+
version: "1.0.0",
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
server.registerTool(
|
|
22
|
+
"check_readability",
|
|
23
|
+
{
|
|
24
|
+
title: "Check readability",
|
|
25
|
+
description:
|
|
26
|
+
"Score text for plain-English readability. Returns a Flesch reading-ease score, sentences that are too long, jargon with plain replacements, and filler phrases. Pure arithmetic — no AI, no network. Use it before sending a long explanation, or to check a README, docs page, error message or UI copy.",
|
|
27
|
+
inputSchema: {
|
|
28
|
+
text: z.string().describe("The text to score."),
|
|
29
|
+
maxSentenceWords: z
|
|
30
|
+
.number()
|
|
31
|
+
.int()
|
|
32
|
+
.min(5)
|
|
33
|
+
.max(60)
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Longest allowed sentence, in words. Default 18."),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
async ({ text, maxSentenceWords }) => {
|
|
39
|
+
const result = analyze(text, { maxSentenceWords });
|
|
40
|
+
return {
|
|
41
|
+
content: [{ type: "text", text: formatReport(result) }],
|
|
42
|
+
structuredContent: result,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
server.registerTool(
|
|
48
|
+
"simplify",
|
|
49
|
+
{
|
|
50
|
+
title: "Simplify text",
|
|
51
|
+
description:
|
|
52
|
+
"Get the exact instructions and readability numbers needed to rewrite a piece of text in simple English. Returns the problems found plus the rules to follow; you then write the simpler version yourself. Use when the user says they do not understand, asks for easy words, or asks for a shorter answer.",
|
|
53
|
+
inputSchema: {
|
|
54
|
+
text: z.string().describe("The text to simplify."),
|
|
55
|
+
maxWords: z
|
|
56
|
+
.number()
|
|
57
|
+
.int()
|
|
58
|
+
.min(10)
|
|
59
|
+
.max(2000)
|
|
60
|
+
.optional()
|
|
61
|
+
.describe("Word budget for the rewrite. Default 120."),
|
|
62
|
+
audience: z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe(
|
|
66
|
+
"Who is reading, e.g. 'non-native English speaker', 'beginner', 'manager'."
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
async ({ text, maxWords = 120, audience }) => {
|
|
71
|
+
const result = analyze(text);
|
|
72
|
+
const parts = [
|
|
73
|
+
`Rewrite the text below in simple English. Reader: ${
|
|
74
|
+
audience || "a non-native English speaker"
|
|
75
|
+
}. Budget: ${maxWords} words or fewer.`,
|
|
76
|
+
"",
|
|
77
|
+
STYLE_RULES,
|
|
78
|
+
"",
|
|
79
|
+
"What the text scores now:",
|
|
80
|
+
formatReport(result, { maxWords }),
|
|
81
|
+
"",
|
|
82
|
+
"Reply with the rewritten text only. No preface, no notes about the rewrite.",
|
|
83
|
+
];
|
|
84
|
+
return {
|
|
85
|
+
content: [{ type: "text", text: parts.join("\n") }],
|
|
86
|
+
structuredContent: result,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
server.registerTool(
|
|
92
|
+
"style_rules",
|
|
93
|
+
{
|
|
94
|
+
title: "Plain-English style rules",
|
|
95
|
+
description:
|
|
96
|
+
"Return the plain-English writing rules to follow for the rest of the conversation. Call once when the user asks for simple, short, or easy answers.",
|
|
97
|
+
inputSchema: {},
|
|
98
|
+
},
|
|
99
|
+
async () => ({
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: `${STYLE_RULES}\n\nKeep following these rules until the user asks for more detail.`,
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
server.registerPrompt(
|
|
110
|
+
"simple",
|
|
111
|
+
{
|
|
112
|
+
title: "Say it simply",
|
|
113
|
+
description: "Say the last answer again in very simple English.",
|
|
114
|
+
},
|
|
115
|
+
() => ({
|
|
116
|
+
messages: [
|
|
117
|
+
{
|
|
118
|
+
role: "user",
|
|
119
|
+
content: {
|
|
120
|
+
type: "text",
|
|
121
|
+
text: `Say your previous answer again, simply.
|
|
122
|
+
|
|
123
|
+
- 80 words maximum.
|
|
124
|
+
- Sentences of 12 words or fewer.
|
|
125
|
+
- No technical words. If one is unavoidable, explain it in brackets.
|
|
126
|
+
- Start with the most important point.
|
|
127
|
+
- 4 bullets maximum.
|
|
128
|
+
- Keep every command and file path exactly as it was.
|
|
129
|
+
|
|
130
|
+
Do not do new work. Only rewrite what you already said.`,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
server.registerPrompt(
|
|
138
|
+
"tldr",
|
|
139
|
+
{
|
|
140
|
+
title: "One line",
|
|
141
|
+
description: "The last answer in one sentence.",
|
|
142
|
+
},
|
|
143
|
+
() => ({
|
|
144
|
+
messages: [
|
|
145
|
+
{
|
|
146
|
+
role: "user",
|
|
147
|
+
content: {
|
|
148
|
+
type: "text",
|
|
149
|
+
text: "Reply with one single sentence, 25 words maximum, that captures your previous answer. No bullets, no heading, no code block, nothing else.",
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
})
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
server.registerPrompt(
|
|
157
|
+
"steps",
|
|
158
|
+
{
|
|
159
|
+
title: "Give me steps",
|
|
160
|
+
description: "Turn the last answer into a numbered checklist.",
|
|
161
|
+
},
|
|
162
|
+
() => ({
|
|
163
|
+
messages: [
|
|
164
|
+
{
|
|
165
|
+
role: "user",
|
|
166
|
+
content: {
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `Turn your previous answer into a numbered checklist I can follow.
|
|
169
|
+
|
|
170
|
+
- One action per step, starting with a verb.
|
|
171
|
+
- 12 words or fewer per step, plus the command on its own line if needed.
|
|
172
|
+
- 7 steps maximum. Merge small steps.
|
|
173
|
+
- No text before or after the list.`,
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
})
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
server.registerPrompt(
|
|
181
|
+
"explain",
|
|
182
|
+
{
|
|
183
|
+
title: "Explain simply",
|
|
184
|
+
description: "Explain something in plain English.",
|
|
185
|
+
argsSchema: {
|
|
186
|
+
topic: z.string().describe("What to explain."),
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
({ topic }) => ({
|
|
190
|
+
messages: [
|
|
191
|
+
{
|
|
192
|
+
role: "user",
|
|
193
|
+
content: {
|
|
194
|
+
type: "text",
|
|
195
|
+
text: `Explain this in plain English: ${topic}
|
|
196
|
+
|
|
197
|
+
Shape of your answer:
|
|
198
|
+
1. One sentence: what it is.
|
|
199
|
+
2. One sentence: why it matters to me.
|
|
200
|
+
3. 3-5 short bullets of detail, only if needed.
|
|
201
|
+
4. Last line: what I should do next.
|
|
202
|
+
|
|
203
|
+
Sentences of 15 words or fewer. Everyday words. Every technical word gets a short meaning in brackets. Do not paste long code or logs back at me — quote the one line that matters.`,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
})
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
return server;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export async function main() {
|
|
214
|
+
const server = createServer();
|
|
215
|
+
const transport = new StdioServerTransport();
|
|
216
|
+
await server.connect(transport);
|
|
217
|
+
}
|