MCP server for automatic tone-of-voice analysis from published writing corpus
The server implements three well-intentioned tools with explicit Zod schemas and error handling, but falls short of production-grade quality. Tool names follow verb_noun conventions (collect_, analyze_, generate_), descriptions are substantive (137-236 characters), and all parameters have type definitions and descriptions. However, output schemas are undocumented (tools return generic JSON without shape definitions), error messages lack actionable recovery guidance, and parameter constraints are incomplete. The tool composition is sound (three separate concerns: corpus collection, analysis, skill generation), but the interfaces do not support agent chaining patterns well, no pagination for potentially large results, and unclear what fields are returned. Security is adequate for local use but lacks audit logging and permission gates. This is solidly in the 'Fair' range: functional definitions with noticeable gaps in schema documentation and error recovery.
Perform linguistic analysis on collected corpus. Runs the six analysers that feed the voice skill: phrases, voice markers, punctuation, vocabulary tiers, vulnerability patterns, and specificity patterns.
Crawl sitemap and collect clean writing corpus from published articles
Generate a Claude Skill bundle (SKILL.md plus real article samples) so Claude writes prose in the corpus author's voice by mimicking actual writing rather than following rule lists.
Output schemas are undocumented. All three tools return `{ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }` but the structure of `result` is never defined. LLMs cannot plan downstream tool calls or extract structured data without knowing the shape of the response.
Error messages lack actionable recovery guidance. All three tools catch errors and throw `McpError(ErrorCode.InternalError, ...)` with only the underlying error message (e.g., 'Corpus collection failed: ENOENT: no such file'). LLMs cannot determine whether to retry, ask the user, or investigate a user-provided path.
Parameter `corpus_dir` in `analyze_corpus` and `generate_voice_skill` accepts free-form strings with no validation or format guidance. No description explains the expected directory structure or error behavior if the path does not exist.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 64 | 2026-07-28+ | v2 |
| 2026-03-09 | F | 41 | - | v1 |
Parameter `max_articles` in `collect_corpus` is a number with default 100 but no minimum or maximum bounds documented. An LLM could pass 1000000 or a negative number, causing resource exhaustion or silent failures.
Parameter `sample_count` in `generate_voice_skill` uses `.int().positive()` schema constraint (correct), but the description does not mention the upper bound or consequence of requesting more samples than available in the corpus.
No pagination support for corpus analysis results. If `analyze_corpus` returns large linguistic data (phrase libraries, marker frequencies), it could exceed context windows. No limit or offset parameters offered.
Tool composition is sound but lacks chaining hints. There is no indication in the `collect_corpus` response documentation that its `output_name` must match the `corpus_name` parameter of downstream tools. An LLM might use a different name and cause silent failures.