Etincel
About
Find the AI tells in your prose. Deterministic, local, runs in CI. MCP server + CLI + Action.
Details
- Transport
- SSE
- License
- MIT
Explore
- leverage
- streamline
- comprehensive
- uses: AIStoryHub/etincel@main
- An MCP server (src/server.ts) exposing twenty tools
Setting up with Highlight
This MCP is not yet compatible with Highlight’s one-click setup. However, you can still use it with Highlight by following these steps:
- Download and install Highlight from highlightai.com/download
- Navigate to the plugins tab and select "Add Custom Plugin"
-
Configure the plugin with the settings below
Plugin Name
EtincelCommand (node, npx, python, etc.)Please refer to the README for specific instructions on how to obtain API keys or other required environment variables.
- Enable "Start Automatically" if you want the plugin to start when Highlight launches
From the repository
A team's rules don't have to live only in each person's local~/.etincel/. Drop a.etincelrc(or.etincelrc.json/etincel.config.json) at the repo root and it's picked up automatically by the CLI and by the local (stdio) server, reviewable in code review and versioned instead of invisible and gone when someone leaves:
{ "bannedWords": ["Acme Cloud Platform"], "allowedWords": ["leverage"], "register": "docs", "threshold": "orange", "instructions": "Always include a one-line CTA at the end.", "style": { "name": "House Voice", "dials": { "formality": 6, "warmth": 4, "directness": 7, "sentenceLength": 40, "sentenceRhythmVariance": 50, "paragraphVariance": 30, "contractionUse": 20, "emDashUse": 0, "fragmentTolerance": 10, "questionUse": 5, "entropy": 60 } } }
- bannedWords/allowedWordsmerge alongside whatever's in your account/style dictionary;register/thresholdact as repo-wide defaults that an explicit--register/--thresholdflag still overrides.
- instructionsis free text, folded intoget_style_guide'sinstructionsforeverystyle, not just the team one, ahead of your own account-level global instructions: the team-wide equivalent ofset_style_instructionswith nostyleId, but committed to the repo instead of living in one person's account.
- styledefines a shared "house voice" from dials, addressable everywhere asstyleId: "team"(get_style_guide, and once forked into a real trained voice withfork_style, everywhere else too) so a team has one already-tuned starting voice from day one instead of everyone hand-training or hand-tuning their own from scratch.list_stylesincludes it automatically when a.etincelrcin the current repo defines one.
The hosted server doesn't use any of this (it has no local repo to look in).
.etincelrcis the versioned, code-reviewable layer above; the layer beneath it isETINCEL_HOME, an environment variable that points the local (stdio) server and CLI at a directory to use instead of the default~/.etincel/. Point every teammate'sETINCEL_HOMEat the same shared, synced, or mounted directory (a repo-external path everyone's machine can read, e.g. something synced by your usual file-sharing setup) and trained voices, the default style, and account-level instructions/dictionaries are shared too, not just the.etincelrc-committed subset:
{ "mcpServers": { "etincel-nonfiction": { "command": "node", "args": ["/path/to/etincel/dist/server.js"], "env": { "ETINCEL_HOME": "/path/to/shared/etincel-home" } } } }
Nothing else to export or import: pointingETINCEL_HOMEat the same directoryisthe sync, the same way it already is for a single person's~/.etincel/.
- An MCP server(src/server.ts) exposing twenty tools:
- list_styles: premade tone presets, any voices you've trained, and (if a repo-local.etincelrcdefines one) a shared team style
- get_style_guide: the drafting instructions for one style
- train_style: learn a voice from your own writing samples (sentence rhythm, contraction rate, em-dash habits, paragraph variance, recurring phrasing: measured, not guessed)
- create_style_from_dials: build a style from explicit formality/warmth/directness and mechanical dials instead of samples
- update_style: rename a trained voice or adjust its dials in place
- fork_style: copy a preset's dials and guide into a new trained voice you can retrain or hand-tune, or fork another installer's style once they've published it publicly on the hosted gallery (addressed ashandle/slug, e.g.jpleblanc/blunt-memo, the same address shown on its public page atetincel.ai/v/handle/slug); a public-style fork also carries over its mechanical dials and any dictionary/instructions the source installer set specifically for that style (never their private, account-wide ones), and makes one network call toetincel.aito fetch it, while a preset fork never leaves this install
- delete_style: permanently remove a trained voice
- set_default_style: remember which style to use without repeating yourself
- check_voice_match: compare a draft's measured rhythm against a trained voice's baseline. A rhythm/mechanics check, not an authorship or AI-detection check, and low-confidence on short input
- check_self_repetition: compare a draft against a voice's own recent training samples for habits, not AI tells: the same opener, or a phrase, recurring across several past pieces ("you've opened this way in 4 of your last 6 pieces"). Local install only for now
- audit_text: a deterministic, rules-based scan for AI tells, returning a tier, specific findings with severity, and a strengths signal (specificity, concrete-vs-abstract ratio, sentence-rhythm variation) so fixes don't flatten the prose. Takes an optionalregister(email/blog/memo/essay/social/docs/general/personal, defaultgeneral) to calibrate strictness against the kind of text it is:docssuppresses Markdown-structure false positives (headings, bolded terms) and recalibrates rhythm/vocabulary detection against long-form reference prose instead of punchier short-form copy.personalis scaffolded (accepted, suppresses nothing extra yet) but not yet calibrated: no term suppressions, rhythm weight, or labeled corpus of its own until one is measured. Also takes an optionalsourceFacts(details elicited from the user, never generated): checks how many actually made it into the draft, flaggingelicited-material-unusedbelow a quota of two used and at least one in a sentence that isn't proving a qualification
- second_read: a single model call that reads a draft and reports what a careful human editor would notice, unscored and untiered, never a rewrite. Hosted only: this install always fails with a clear explanation, since a model call needs an account, a pinned model, and a billing surface this install doesn't have.audit_textremains fully available, no account needed
- add_banned_word/remove_banned_word: maintain your own banned-vocabulary list, checked byaudit_textalongside the built-in corpus
- add_custom_word/remove_custom_word: maintain a "never flag this" list: an org's own acronyms or house terms, the corporate-dictionary case
- list_dictionary: see a scope's banned/custom words, and (for a style) what actually applies once merged with the global list
- set_style_instructions/clear_style_instructions/get_style_instructions: save, remove, or read free-text drafting rules for a scope (required elements, forbidden topics, format constraints), merged intoget_style_guidethe same way dictionaries merge intoaudit_text
Trained voices, dictionaries, and your default style live locally in~/.etincel/: nothing is sent anywhere.audit_textis plain deterministic code (string analysis + a curated corpus of AI-writing tells), not a model call. The one exception is forking apublicstyle viafork_style, which fetches (never sends) that style's guide frometincel.ai's public gallery; forking a preset, or anything else in this list, still touches the network not at all.
Beyond the built-in AI-tell corpus, you can maintain your own banned and "always allowed" word lists: just tell Claude (or any MCP client) things like "add[word]to my banned words list" or "add[word]to my custom words list, it's one of ours." Each list lives at ascope:global(applies everywhere, the default when no style is named) or a specific style id, whose list is merged on top ofglobalwhen you audit against that style.list_dictionaryshows what's saved for a scope, plus the effective merged list for a style. Editing the global list is already the way to keep a word in sync across every style: it's merged in automatically, live, every timeaudit_textorlist_dictionaryruns.
npm install npm test # run the engine/tools test suite (node:test via tsx) npm run dev # run the MCP server over stdio via tsx, for local testing npm run build # compile to dist/
The mcpscore badge audits the hosted remote server (etincel.ai/api/mcp), not the local/stdio engine in this repo; the two expose the same tools but run as separate deployments.
- The audit corpus insrc/data/is a curated subset, not exhaustive. Seesrc/data/SOURCES.mdfor provenance and what isn't ported yet. The full corpus is ataistoryhub.co/corpus.
- email(AUC 0.540) andgeneral(0.748) are uncalibrated.socialhas no labeled corpus at all and isn't tracked.
- check_voice_matchis a rhythm-and-mechanics check, not authorship detection, and it's low-confidence on short input.
- check_self_repetitionis local-install only for now.
- AI tells are a moving target: OpenAI patched ChatGPT's em-dash habit in November 2025. That's why efficacy is measured rather than claimed: when a tell stops being a tell, the AUC moves and the regression check catches it.
The 1Password MCP server creates a bridge that allows MCP clients such as Codex and Kiro to manage your 1Password Environments with secure authorization prompts.
This is the 1st, easiest, and cheapest PPT, slides, presentation AI generation MCP Server in the world.
Persistent memory for any AI assistant. Zero token cost until recall. Stores memories in local SQLite, ranks by 6-factor scoring, returns results 79% smaller than JSON. Works with Claude, ChatGPT, Grok, Cursor, Windsurf, and any MCP client.
A MCP server that enables AI assistants to interact with Anki, the spaced repetition flashcard application.
Enables LLM clients to interact with macOS applications through AppleScript. Built using the @beyondbetter/bb-mcp-server library, this server provides safe, controlled execution of predefined scripts with optional support for arbitrary script execution.
An MCP server for WordPress plugin audits
Turn your AI assistant into a digital marketing hub that creates, organizes, and analyzes links and QR Codes on demand.
Connect AI clients to Cal.com scheduling through the Model Context Protocol using the hosted server at mcp.cal.com or a local instance.
Sync Calendars, Scheduling Links, AI Executive Scheduling Assistant, Unified Calendar
list_styles
List every available style: premade emotional-tone presets, any voices the user has trained from their own samples, and (if a .etincelrc in the current repo defines one) the shared team style, id 'team'. Call this before drafting or revising non-fiction prose if the caller hasn't been told which style to use, or if the user asks what styles exist.
get_style_guide
Fetch the full drafting guide for one style (a preset id like 'direct-warm', the id of a trained voice, or 'team' for the shared style a .etincelrc in the current repo defines, if any). Returns prose instructions to follow while drafting or revising: sentence rhythm, tone dials, and (for trained voices) the writer's own measured habits. If a .etincelrc in the current repo sets team-wide instructions, those are folded into every style's instructions, not just 'team''s. Read this before drafting; it is context for you, the drafting model, not a tool that writes prose itself.
train_style
Analyze one or more of the user's own writing samples (emails, posts, essays, memos: real finished text they wrote or approved) and persist a trained voice profile under that name. Measures sentence length and variance, paragraph rhythm, contraction rate, em-dash and semicolon habits, fragment use, structural entropy (sentence-opener variety and punctuation-mark variety), and recurring phrasing. Call again with the same name and new samples to add more training data to that voice; the new samples blend into its existing measurements rather than replacing them. If the voice may have been renamed since it was created, pass its id (from list_styles) instead so the right voice is targeted regardless of its current name. This never fabricates a voice from a description; it only learns from real text the user supplies.
create_style_from_dials
Build a style profile from explicit dial values instead of writing samples: for when the user wants to hand-tune a voice (or doesn't have samples handy). 3 persona dials (formality, warmth, directness, 0-10) plus 8 mechanical dials (0-100, including entropy: how much AI-typical structural regularity to break) that map onto the same measurements train_style extracts from real text, so a dial-built style and a trained voice are the same shape. Call again with the same name to overwrite.
update_style
Rename a trained voice or adjust its dials in place, keeping its id (and default-style pointer) stable. Persona dials (formality, warmth, directness) always apply; the mechanical dials only take effect if the voice has no writing samples (was built from dials, not trained). A sample-trained voice keeps its measured mechanical stats regardless of what's passed here.
fork_style
Copy a style into a new trained voice under the given name, seeded with its persona dials and drafting guide. Two kinds of source: a premade preset (e.g. 'pr-review', 'linkedin-post'), or another installer's style published publicly on the hosted gallery, addressed as "handle/slug" (e.g. "jpleblanc/blunt-memo", the same address shown on its public page at etincel.ai/v/handle/slug). A public-style fork also carries over its 8 mechanical dials (rhythm, sentence length, em-dash use, etc.) and any banned words, custom words, or drafting instructions the source installer set specifically for that style, never their private account-wide dictionary or instructions; a preset fork only ever has persona dials and entropy to carry, since a preset has no measured mechanics. A public-style fork makes one network call to etincel.ai to fetch it; a preset fork never leaves this install. The fork is then a normal trained voice: retrain it with train_style from real samples, or hand-tune it with update_style, without touching the original.
delete_style
Permanently delete a trained voice profile by id. Does not affect premade presets.
set_default_style
Set which style (preset or trained voice) should be used by default for this user going forward, so it doesn't need to be re-specified every time.
check_voice_match
Compare a piece of drafted text's measured sentence/paragraph rhythm and mechanics against a trained or custom voice's baseline (sentence length, rhythm variance, paragraph variance, contraction rate, em-dash use, fragment use, question use, structural entropy). Use this after drafting in a voice to check whether the draft's rhythm actually landed close to it, instead of just eyeballing it. Returns a verdict ('on rhythm' / 'some drift' / 'off rhythm'), a match score, specific dials that drifted with a plain-language note for each, and a caveat you should relay alongside the verdict: this is a rhythm/mechanics measurement, not an authorship or AI-detection check, so text merely shaped like the voice (by anyone, or any tool) can come back 'on rhythm', and a genuine off-voice draft by the target writer can still come back drifted. Confidence comes back "low" on short input, since a handful of sentences isn't enough to read rhythm reliably; treat a low-confidence verdict as provisional. Only works against trained or custom voices (from train_style, create_style_from_dials, or fork_style), not bare presets, which have no measured baseline; fork_style a preset first if you want to check a draft against one.
check_self_repetition
Compare a piece of drafted text against a trained voice's own recent training samples for two kinds of self-repetition: opening the same way ("you've opened this way in 4 of your last 6 pieces"), and reusing a characteristic phrase across several of them. This is about the writer's own recurring habits, not AI-writing tells; use audit_text for those. Only meaningful for a voice trained from real samples (train_style) with at least 3 recorded samples; dial-tuned or preset-forked voices, or ones with too little history yet, come back with an empty findings list rather than an error. Only the local install tracks sample history today, so a hosted/remote connection may always report zero history. A signal to weigh, same trust-mode spirit as audit_text: never rewrite the draft on the strength of this alone.
audit_text
Deterministically scan a piece of non-fiction text for common AI-writing tells: banned hype vocabulary, chatbot fingerprints, and structural patterns (uniform paragraph length, stacked transitions, em-dash overuse, rule-of-three compulsion, and more), plus this installer's own banned/custom word lists from add_banned_word/add_custom_word (the 'global' list, merged with a style's own list if styleId is given), plus a repo-local .etincelrc/.etincelrc.json/etincel.config.json if one exists at or above the current directory (dictionary as code, reviewable and versioned). Returns a tier (green/yellow/orange/red), a numeric score, specific findings with severity and location, and a strengths signal (specificity density, concrete-vs-abstract ratio, sentence-rhythm variation). Read strengths too, not just findings: it's the counter-signal against fixing every flagged word into flat, sterile prose. Never a silent rewrite. Use this to show the user what's flagged and why, so they stay in control of any change; only rewrite what they ask you to rewrite.
second_read
A single model call that reads a draft and reports what a careful human editor would notice: unscored, untiered, and never a rewrite. Distinct from audit_text, which is deterministic and reproducible; this is neither, so weigh it as one more opinion, not a verdict, and never gate a decision on it alone. Requires the hosted server (https://etincel.ai/api/mcp): the local (stdio) install has no account to bill a model call against, so this always fails there with a clear explanation. audit_text remains fully available locally, no account needed.
add_banned_word
Add a term to this installer's own banned-words list, checked by audit_text alongside the built-in AI-tell corpus. Without styleId, this adds to the global list, which applies to every style. With styleId, it only applies when auditing against that specific style, merged on top of the global list. Use this when the user says something like 'add [word] to my banned words list' or 'never let me use [word] again'.
remove_banned_word
Remove a term from this installer's banned-words list (global, or a specific style's list).
add_custom_word
Add a term to this installer's own allowed-words list, so audit_text never flags it even if it matches the built-in corpus or a banned word: the 'corporate dictionary' case, e.g. an org's own acronyms or house terms. Without styleId, this adds to the global list. With styleId, it only applies to that specific style, merged on top of the global list. Use this when the user says something like 'add [word] to my custom words list' or 'stop flagging [word], it's one of ours'.
remove_custom_word
Remove a term from this installer's allowed-words list (global, or a specific style's list).
list_dictionary
Show the banned-words and custom (allowed) words lists for a scope: the global list (default), or a specific style's list. For a style, also returns the effective merged list (that style's words plus the global ones): what audit_text actually applies when that style is selected.
set_style_instructions
Save free-text drafting rules layered on top of a style's voice: required elements ('always include a CTA'), audience notes, forbidden topics, format constraints, anything that isn't about sentence rhythm or tone. Overwrites whatever was saved for this scope. Without styleId, this sets the global instructions, which apply to every style. With styleId, it only applies to that specific style, merged after the global instructions (get_style_guide returns the merged result automatically). Use this when the user says something like 'for this style, always end with a CTA' or 'remember: never mention pricing in emails'.
clear_style_instructions
Remove the saved instructions for a scope (global, or a specific style), leaving it empty.
get_style_instructions
Show the saved instructions for a scope: the global instructions (default), or a specific style's own. For a style, also returns the effective merged text (global plus that style's own) that get_style_guide already folds in automatically.
Claude Desktop / Cursor
Paste into your MCP client config file to install this server.
{
"mcpServers": {
"etincel": {
"etincel-nonfiction": {
"command": "npx",
"args": [
"etincel",
"serve"
]
}
}
}
}
McpServers
{
"etincel-nonfiction": {
"command": "npx",
"args": [
"etincel",
"serve"
]
}
}
___ ___ ___ ___ ___ ___ /\ \ /\ \ ___ /\__\ /\ \ /\ \ /\__\ /::\ \ \:\ \ /\ \ /::| | /::\ \ /::\ \ /:/ / /:/\:\ \ \:\ \ \:\ \ /:|:| | /:/\:\ \ /:/\:\ \ /:/ / /::\~\:\ \ /::\ \ /::\__\ /:/|:| |__ /:/ \:\ \ /::\~\:\ \ /:/ / /:/\:\ \:\__\ /:/\:\__\ __/:/\/__/ /:/ |:| /\__\ /:/__/ \:\__\ /:/\:\ \:\__\ /:/__/ \:\~\:\ \/__/ /:/ \/__/ /\/:/ / \/__|:|/:/ / \:\ \ \/__/ \:\~\:\ \/__/ \:\ \ \:\ \:\__\ /:/ / \::/__/ |:/:/ / \:\ \ \:\ \:\__\ \:\ \ \:\ \/__/ \/__/ \:\__\ |::/ / \:\ \ \:\ \/__/ \:\ \ \:\__\ \/__/ /:/ / \:\__\ \:\__\ \:\__\ \/__/ \/__/ \/__/ \/__/ \/__/
Find the AI tells in your prose. Deterministically, locally, in CI.
No install, no account, no model call, no network.
$ npx etincel lint drafts/announcement.md --register blog ✗ drafts/announcement.md RED 100/100 (8 findings, 169 words, register: blog) Heavy AI styling. Multiple strong tells stacking up, worth a structural rewrite, not a word-swap pass. Whole-piece rhythm medium uniform-paragraph-length 6 paragraphs, most running about the same length with little variation between them. Vary paragraph length more. medium low-burstiness 13 sentences averaging 13 words, with little variation in length from one sentence to the next. Mix short sentences with long; allow fragments. medium mechanical-register-drift Fragment rate and structural variety (sentence openers, punctuation mix) sit off where blog prose typically lands. Allow more sentence fragments and vary openers/punctuation more. Vocabulary and phrasing high comprehensive L3:C30 → thorough, complete high cutting-edge L3:C109 → newest, latest high leverage L3:C99 → use high seamless L3:C150 → smooth, easy high streamline L3:C70 → simplify, speed up strengths specificity 11.4/1k · concrete:abstract 0.40 · burstiness 0.32 1 file audited, 1 at or above orange.
Étincel is two things. Adeterministic auditthat finds the patterns making prose read as machine-written, as a CLI, a GitHub Action, and an MCP tool. And avoice layerthat trains Claude, Cursor, or any MCP client to draft in a style measured from your own writing, so the problem is smaller before you ever read the draft.
AI-drafted prose has a recognizable shape: uniform paragraphs, hedged authority, em dashes where a comma would do, closings that resolve too neatly. Étincel encodes the rules against that shape and, just as important, shows you what it found and why instead of quietly overwriting your voice. There is no model call anywhere in the audit path, and you decide what changes. You stay the author.
We measure it rather than assert it. Pooled AUC per register, measured byassayagainst labeled corpora with a fixed bootstrap seed, so any PR that drops these numbers fails CI:
Email is close to a coin flip. It's deliberately uncalibrated, and we track it anyway so an accidental regression doesn't go unnoticed.generalis uncalibrated too. The calibrated registers aredocs,blog,memo, andessay; use those if the number matters to you. Seeefficacy-baselines.jsonandsrc/data/SOURCES.mdfor method and provenance.
vs.Vale+vale-ai-tellsVale is an excellent markup-aware linter and vale-ai-tells is a serious 78-rule package. They match tokens against patterns. Étincel measures shape: sentence-rhythm variance, paragraph-length uniformity, and repetition against your own previous drafts, the kind of statistical layer vale-ai-tells' own README says needs analysis beyond what token matching can do. Étincel also feeds a trained voice to the model before you draft, which no linter does. Running both is reasonable; they overlap less than you'd expect.
vs. skill files (stop-slop, avoid-ai-writing, no-slop)Those are instructions to a model, and they help. But a prompt can't verify it worked, can't produce an exit code, and can't gate a merge. Use oneandrun this in CI.
vs. humanizers (Undetectable.ai, QuillBot, StealthWriter)Those rewrite your text to evade detectors. Turnitin now has a dedicated AI-paraphrasing detection feature, expanded in 2025 to target humanizer output specifically. Étincel does the opposite job: it never modifies your text, and it would be a poor tool for fooling anyone.
vs. detectors (GPTZero, Pangram, Originality.ai, Copyleaks)Those answer "was a machine involved?" with a probability, over a whole document, as a verdict on authorship, in the cloud. Étincel answers "which specific patterns make this read as machine-written?" with locations and severities, locally, and never renders a verdict on who wrote something.
/plugin marketplace add AIStoryHub/etincel /plugin install etincel-nonfiction
Or from a local clone:/plugin marketplace add /path/to/etincel.
{ "mcpServers": { "etincel-nonfiction": { "command": "npx", "args": ["etincel", "serve"] } } }
From a local clone instead:npm install && npm run build, then pointargsat["/path/to/etincel/dist/server.js"]with"command": "node".
A hosted version is also available atetincel.ai, exposing the same tools over Streamable HTTP (https://etincel.ai/api/mcp) with per-account auth instead of stdio. Point any MCP client at it directly:
{ "mcpServers": { "etincel-nonfiction": { "url": "https://etincel.ai/api/mcp" } } }
The hosted server isn't part of this repo; this repo is the local/stdio engine, CLI, and skill that the hosted version is built on top of.
Once installed, just ask for what you'd normally ask for, like "draft an email to the team about the delay," "write a blog post about X," or "clean up this memo," inside Claude Code or Claude Desktop. The skill picks up automatically for non-fiction prose of meaningful length. To train your own voice:
Train a style called "me" from these three emails I wrote: [paste samples]
Then either name it per-request ("write this in my voice") or set it as default:
Twelve premade presets ship out of the box: six emotional tones (Direct & Warm, Executive Brief, Reflective Essayist, Founder Memo, Plainspoken Analyst, Wry & Candid) plus six use-case presets (PR Review, Code Comment, Slack Message, LinkedIn Post, Website Copy, Blog Post). Each carries formality/warmth/directness dials plus a sentence-rhythm and voice description that gets fed to the model as drafting context, not a template that fills in blanks. The server reads these fromsrc/data/presets.json. Fork any preset into a trained voice withfork_styleto make it your own.
audit_textis a pure function under the hood, so it also ships as a CLI, for linting prose outside a chat client (READMEs, docs, PR descriptions in CI):
npx etincel lint 'docs//.md' npx etincel lint README.md --register docs --threshold yellow
Exits non-zero if any matched file's tier is at or above--threshold(defaultorange)..md/.mdxfiles default to thedocsregister automatically (suppresses the Markdown-structure false positives, since a real heading isn't a chatbot tell); pass--registerto override. Add--jsonfor a machine-readable report. Runnpx etincel lint --helpfor the full option list.
A GitHub Action wraps the same CLI (seeaction.yml, and.github/workflows/lint.ymlin this repo for a working example):
- uses: AIStoryHub/etincel@main with: patterns: "docs//.md README.md" threshold: orange
Repo-local config: dictionary, instructions, and a shared team style
A team's rules don't have to live only in each person's local~/.etincel/. Drop a.etincelrc(or.etincelrc.json/etincel.config.json) at the repo root and it's picked up automatically by the CLI and by the local (stdio) server, reviewable in code review and versioned instead of invisible and gone when someone leaves:
{ "bannedWords": ["Acme Cloud Platform"], "allowedWords": ["leverage"], "register": "docs", "threshold": "orange", "instructions": "Always include a one-line CTA at the end.", "style": { "name": "House Voice", "dials": { "formality": 6, "warmth": 4, "directness": 7, "sentenceLength": 40, "sentenceRhythmVariance": 50, "paragraphVariance": 30, "contractionUse": 20, "emDashUse": 0, "fragmentTolerance": 10, "questionUse": 5, "entropy": 60 } } }
- bannedWords/allowedWordsmerge alongside whatever's in your account/style dictionary;register/thresholdact as repo-wide defaults that an explicit--register/--thresholdflag still overrides.
- instructionsis free text, folded intoget_style_guide'sinstructionsforeverystyle, not just the team one, ahead of your own account-level global instructions: the team-wide equivalent ofset_style_instructionswith nostyleId, but committed to the repo instead of living in one person's account.
- styledefines a shared "house voice" from dials, addressable everywhere asstyleId: "team"(get_style_guide, and once forked into a real trained voice withfork_style, everywhere else too) so a team has one already-tuned starting voice from day one instead of everyone hand-training or hand-tuning their own from scratch.list_stylesincludes it automatically when a.etincelrcin the current repo defines one.
The hosted server doesn't use any of this (it has no local repo to look in).
Sharing config across a team without committing it
.etincelrcis the versioned, code-reviewable layer above; the layer beneath it isETINCEL_HOME, an environment variable that points the local (stdio) server and CLI at a directory to use instead of the default~/.etincel/. Point every teammate'sETINCEL_HOMEat the same shared, synced, or mounted directory (a repo-external path everyone's machine can read, e.g. something synced by your usual file-sharing setup) and trained voices, the default style, and account-level instructions/dictionaries are shared too, not just the.etincelrc-committed subset:
{ "mcpServers": { "etincel-nonfiction": { "command": "node", "args": ["/path/to/etincel/dist/server.js"], "env": { "ETINCEL_HOME": "/path/to/shared/etincel-home" } } } }
Nothing else to export or import: pointingETINCEL_HOMEat the same directoryisthe sync, the same way it already is for a single person's~/.etincel/.
- An MCP server(src/server.ts) exposing twenty tools:
- list_styles: premade tone presets, any voices you've trained, and (if a repo-local.etincelrcdefines one) a shared team style
- get_style_guide: the drafting instructions for one style
- train_style: learn a voice from your own writing samples (sentence rhythm, contraction rate, em-dash habits, paragraph variance, recurring phrasing: measured, not guessed)
- create_style_from_dials: build a style from explicit formality/warmth/directness and mechanical dials instead of samples
- update_style: rename a trained voice or adjust its dials in place
- fork_style: copy a preset's dials and guide into a new trained voice you can retrain or hand-tune, or fork another installer's style once they've published it publicly on the hosted gallery (addressed ashandle/slug, e.g.jpleblanc/blunt-memo, the same address shown on its public page atetincel.ai/v/handle/slug); a public-style fork also carries over its mechanical dials and any dictionary/instructions the source installer set specifically for that style (never their private, account-wide ones), and makes one network call toetincel.aito fetch it, while a preset fork never leaves this install
- delete_style: permanently remove a trained voice
- set_default_style: remember which style to use without repeating yourself
- check_voice_match: compare a draft's measured rhythm against a trained voice's baseline. A rhythm/mechanics check, not an authorship or AI-detection check, and low-confidence on short input
- check_self_repetition: compare a draft against a voice's own recent training samples for habits, not AI tells: the same opener, or a phrase, recurring across several past pieces ("you've opened this way in 4 of your last 6 pieces"). Local install only for now
- audit_text: a deterministic, rules-based scan for AI tells, returning a tier, specific findings with severity, and a strengths signal (specificity, concrete-vs-abstract ratio, sentence-rhythm variation) so fixes don't flatten the prose. Takes an optionalregister(email/blog/memo/essay/social/docs/general/personal, defaultgeneral) to calibrate strictness against the kind of text it is:docssuppresses Markdown-structure false positives (headings, bolded terms) and recalibrates rhythm/vocabulary detection against long-form reference prose instead of punchier short-form copy.personalis scaffolded (accepted, suppresses nothing extra yet) but not yet calibrated: no term suppressions, rhythm weight, or labeled corpus of its own until one is measured. Also takes an optionalsourceFacts(details elicited from the user, never generated): checks how many actually made it into the draft, flaggingelicited-material-unusedbelow a quota of two used and at least one in a sentence that isn't proving a qualification
- second_read: a single model call that reads a draft and reports what a careful human editor would notice, unscored and untiered, never a rewrite. Hosted only: this install always fails with a clear explanation, since a model call needs an account, a pinned model, and a billing surface this install doesn't have.audit_textremains fully available, no account needed
- add_banned_word/remove_banned_word: maintain your own banned-vocabulary list, checked byaudit_textalongside the built-in corpus
- add_custom_word/remove_custom_word: maintain a "never flag this" list: an org's own acronyms or house terms, the corporate-dictionary case
- list_dictionary: see a scope's banned/custom words, and (for a style) what actually applies once merged with the global list
- set_style_instructions/clear_style_instructions/get_style_instructions: save, remove, or read free-text drafting rules for a scope (required elements, forbidden topics, format constraints), merged intoget_style_guidethe same way dictionaries merge intoaudit_text
Trained voices, dictionaries, and your default style live locally in~/.etincel/: nothing is sent anywhere.audit_textis plain deterministic code (string analysis + a curated corpus of AI-writing tells), not a model call. The one exception is forking apublicstyle viafork_style, which fetches (never sends) that style's guide frometincel.ai's public gallery; forking a preset, or anything else in this list, still touches the network not at all.
Beyond the built-in AI-tell corpus, you can maintain your own banned and "always allowed" word lists: just tell Claude (or any MCP client) things like "add[word]to my banned words list" or "add[word]to my custom words list, it's one of ours." Each list lives at ascope:global(applies everywhere, the default when no style is named) or a specific style id, whose list is merged on top ofglobalwhen you audit against that style.list_dictionaryshows what's saved for a scope, plus the effective merged list for a style. Editing the global list is already the way to keep a word in sync across every style: it's merged in automatically, live, every timeaudit_textorlist_dictionaryruns.
npm install npm test # run the engine/tools test suite (node:test via tsx) npm run dev # run the MCP server over stdio via tsx, for local testing npm run build # compile to dist/
The mcpscore badge audits the hosted remote server (etincel.ai/api/mcp), not the local/stdio engine in this repo; the two expose the same tools but run as separate deployments.
- The audit corpus insrc/data/is a curated subset, not exhaustive. Seesrc/data/SOURCES.mdfor provenance and what isn't ported yet. The full corpus is ataistoryhub.co/corpus.
- email(AUC 0.540) andgeneral(0.748) are uncalibrated.socialhas no labeled corpus at all and isn't tracked.
- check_voice_matchis a rhythm-and-mechanics check, not authorship detection, and it's low-confidence on short input.
- check_self_repetitionis local-install only for now.
- AI tells are a moving target: OpenAI patched ChatGPT's em-dash habit in November 2025. That's why efficacy is measured rather than claimed: when a tell stops being a tell, the AUC moves and the regression check catches it.
The 1Password MCP server creates a bridge that allows MCP clients such as Codex and Kiro to manage your 1Password Environments with secure authorization prompts.
This is the 1st, easiest, and cheapest PPT, slides, presentation AI generation MCP Server in the world.
Persistent memory for any AI assistant. Zero token cost until recall. Stores memories in local SQLite, ranks by 6-factor scoring, returns results 79% smaller than JSON. Works with Claude, ChatGPT, Grok, Cursor, Windsurf, and any MCP client.
A MCP server that enables AI assistants to interact with Anki, the spaced repetition flashcard application.
Enables LLM clients to interact with macOS applications through AppleScript. Built using the @beyondbetter/bb-mcp-server library, this server provides safe, controlled execution of predefined scripts with optional support for arbitrary script execution.
An MCP server for WordPress plugin audits
Turn your AI assistant into a digital marketing hub that creates, organizes, and analyzes links and QR Codes on demand.
Connect AI clients to Cal.com scheduling through the Model Context Protocol using the hosted server at mcp.cal.com or a local instance.
Sync Calendars, Scheduling Links, AI Executive Scheduling Assistant, Unified Calendar
Sign in to leave a review
Use Google, GitHub, or an email account so ratings stay tied to real people.
No reviews posted yet.



