Vespper DOCX MCP
Server Details
the DOCX MCP to allow agents to edit microsoft word documents
- Status
- Healthy
- Uptime
- 86.6% over 25 days
- Last Tested
- Transport
- Streamable HTTP · MCP 2025-11-25
- URL
- ChatGPT
- –
- Claude
- –
TDQS
Scored across 3 tools
The three tools map cleanly onto distinct operations: read_document (paginated full read), search_document (regex location), and edit_document (batch mutations). The descriptions even include explicit 'WHICH TOOL FIRST' guidance, so an agent can unambiguously choose between them. No two tools overlap in purpose.
All three names follow a strict verb_noun snake_case pattern: read_document, search_document, edit_document. The shared '_document' noun makes the set highly predictable and easy to reason about.
Three tools form a tight, well-scoped triad (locate -> read -> edit) that covers the whole workflow without redundancy. Each tool is powerful rather than trivial, so the small count is justified rather than thin.
The surface robustly covers the document lifecycle: read (paginated), search (regex), and comprehensive edit (tracked changes, comments, lists, tables, headers/footers). Minor gaps exist around creating a brand-new document or exporting/converting it, but core editing coverage is thorough and no dead ends block typical tasks.
Available Tools
3 toolsedit_documentAInspect
Edit a Microsoft Word (.docx) document: apply a LIST of edits in ONE call.
The client controls whether edits appear as tracked changes or directly in place. The author
comes from developer-controlled _meta, not a tool argument; you cannot choose or change it.
'edits' is an array of { old, new }. For each edit:
'old': an HTML snippet copied VERBATIM from the document's current HTML (as returned by read_document) that matches EXACTLY ONE spot (prefer a whole block: /// heading). Copy the inner markup as-is -- keep any , , nested -- and only shorten class attributes (see LAZY CLASSES).
'new': the full replacement HTML for that block -- its FINAL text, with NO / of your own; the tool handles the configured review representation. Use "" to delete it.
DELETE OR REJECT AN ENTIRE TABLE ROW: use the complete ... as 'old' and "" as 'new'. Never empty the individual cells while preserving the ; that leaves a blank row in the document instead of removing the row.
THE DOCUMENT BODY AND EXPOSED HEADERS / FOOTERS ARE EDITABLE. A referenced running header/footer is emitted once as /; edit its blocks normally. Shared parts are not duplicated, so one edit updates every section/type listed in data-docx-refs.
COMMENTS ARE EDITABLE LIKE CONTENT. To comment, wrap the text in a bare and add a bare
Do NOT anchor on the following non-editable content:
FOOTNOTES / ENDNOTES: these auxiliary parts are read-only.
GLOBAL STYLES: the header at the TOP of the HTML, and adding or renaming a named style. To restyle, reuse an existing style class or apply a local Tailwind class on the block; editing the block is rejected.
MAKE EVERY 'old' UNIQUE. Each edit's 'old' must independently match exactly one location. NEVER reuse an ambiguous 'old' for different targets or rely on edit order to choose among repeated matches. To target duplicated blocks, expand EACH 'old' to a unique neighborhood by including an adjacent heading or block -- a CONSECUTIVE span copied verbatim. Preserve unchanged context in 'new'. A consecutive span can also replace adjacent blocks; NON-adjacent blocks require separate edits.
INSERTION ANCHORS ARE ALSO OOXML EXAMPLES. edit_document localizes only old
to x_old; the reconciler sees that OOXML with old and new, not the rest
of the document. Choose the smallest consecutive old containing the insertion
boundary and a structure the new content should resemble: a same-role list
item, a same-shape row, or one complete prior repeated block. Preserve the
example unchanged in new. Do not substitute an unrelated heading, blank
paragraph, or merged row merely because it is unique, including on retries.
Context is evidence, not permission to edit beyond the user's requested scope.
RETRY SAFETY: a failed edit does not make a weaker anchor valid. Keep the same document part, same-role examples, insertion boundary, and unchanged anchor text. Reduce only the new content when splitting a failed insertion. Do not switch to an unrelated header/footer, blank/title block, or tag-only fragment, and never alter preserved text merely to satisfy a validator. If the structurally correct anchor still fails, stop instead of broadening the edit.
LAZY CLASSES (default): write class="..." (literally three dots) for EVERY class attribute in BOTH 'old' and 'new' -- it means "keep this class". Don't paste the real class lists. Spell a class out only when changing the class IS the edit (e.g. bold), to break a "multiple matches" tie, or on a brand-new INSERTED block (one with no counterpart in 'old') -- it has no existing element to inherit from, so it must SPELL OUT its class, never "...".
class="..." is ALL-OR-NOTHING -- it stands for the ENTIRE class value, so it must be the whole attribute, written EXACTLY as class="..." with nothing else inside or after the quotes. NEVER keep a fragment of the real class beside it: class="..." bg-[#F6F4EF]" is INVALID (the whole class, bg-[#F6F4EF] included, lives INSIDE one class attribute, so this fails to locate). To keep a distinguishing class (e.g. a background), spell the WHOLE class out for that element instead of lazying it.
STYLE-ONLY CHANGES: when changing only a block's named document style, preserve its tag and
every unrelated class. Do NOT change the HTML tag merely because the target style commonly
renders with another tag, or replace a full class list with only the new style token -- the
other classes carry direct spacing, alignment, font, and layout that the request did not ask
you to remove. In 'old', use class="..." normally. In 'new', copy that block's COMPLETE
class value from read_document, remove only its previous named style token if it has one,
add the requested named style token, and leave every Tailwind utility unchanged. Keep
unrelated descendants lazy. Example, to change BodyText to IntenseQuote on
<p class="BodyText text-center mb-[6pt]">Summary</p>:
old: Summary
new: Summary
LISTS: list content is marker-free semantic HTML. Never type bullets, numbers, or
letters into paragraph text. Every item is <li><p>…</p></li>; nest a child
<ol>/<ul> inside its parent <li>. Ordered lists use type="1|a|A|i|I" and
start="…" when a restart matters.
When search_document returns one <li>, it is sufficient for changing or deleting only that
item's content. Before an insertion, reorder, nesting/unnesting, restart, split, merge, or
ordered/bullet conversion, call read_document for that page and inspect the parent <ol>/<ul>,
its type/start, and the affected siblings before building the structural edit.
Choose the edit shape by intent:
A direct
<p>or one<li>is only for changing or deleting that one item's content while keeping its list position, level, marker type, and numbering.An insertion, reorder, nesting/unnesting, restart, split, merge, or ordered/bullet conversion is structural. Put the smallest complete affected
<ol>/<ul>subtree in BOTHoldandnew; do not send loose sibling<li>elements for a new structural edit.A newly inserted block cannot inherit
class="...". Copy the complete formatting of the appropriate neighbouring item, spelling its classes out.
INSERTING INTO A CONTINUING NUMBERED LIST: separate <ol> wrappers do not
automatically continue merely because their classes and types match. If old
includes both the preceding N-1 item and following N item from one sequence,
preserve the following start="N" so that context proves natural continuation.
If only the separately wrapped following start="N" item is included, set it
to start="N+1" in new. Keep its content and formatting unchanged, and do
not seek out or renumber unrelated later fragments.
Examples (classes omitted only to keep these examples short): Insert between items: old: AlphaGamma new: AlphaBetaGamma Delete one item without changing the list structure: old: Obsolete new: "" Nest an item: old: ParentChild new: ParentChild Restart at five: old: First new: First
PREFER BATCHING: send all of a request's edits in one call when it makes sense -- they reconcile IN PARALLEL, so a 10-edit call takes about as long as one edit while 10 separate calls are ~10x slower (max 20 edits per call -- if a task needs more, send several batches of <=20). NO OVERLAPS: every edit must target a DIFFERENT block; two edits that touch the SAME block (or the same consecutive span) overlap and BOTH are dropped -- combine those changes into a SINGLE old/new pair.
KEEP EACH EDIT SMALL. Each edit is applied by a model with a bounded output, so ONE edit carrying a huge 'new' (say beyond ~10,000 characters -- several pages of new text) risks truncation or a timeout and fails the whole edit. When writing a LOT of new content, break it into MULTIPLE edits of a few blocks each: give each chunk its own { old, new } anchored on a DIFFERENT neighboring block (e.g. chunk 1 anchors on the block above and appends the first blocks; chunk 2 anchors on chunk 1's LAST block and appends the next ones -- but note within one batch edits must not overlap, so either anchor each chunk on a distinct existing block, or send the chunks as consecutive calls). Prefer many small edits over one giant edit; batching keeps them fast.
EDITS SUCCEED OR FAIL INDIVIDUALLY. A batch can apply in part: the result's 'count' is how many landed and 'failed_edits' lists the rest as { index, reason } ('index' is 1-based over the 'edits' you sent). The applied ones are ALREADY saved in the document, so NEVER resend the whole batch after a partial result -- that re-applies them and duplicates the text. Resend ONLY the listed index(es), fixed, or tell the user what couldn't be changed. When NOTHING applies you get an error instead and the document is untouched; then fix the listed edits and resend the whole batch.
Some successful edits need no new document mutation because the requested result is already
represented by an existing tracked change. Those return count: 0 plus a notices entry.
Do not retry an edit named by a notice.
SUCCESSFUL EDITS ARE COMPLETE: applied edits are already present in the returned document. Depending on client configuration, they appear as tracked changes or directly in place. If tracked changes are visible, deleted text can remain struck through until accepted; do not repeat a successful edit.
RESOLVE OR MODIFY AN EXISTING TRACKED CHANGE. read_document renders changes already in the
doc as (insertions) and (deletions), each with a data-author. To act on one,
copy its whole block verbatim into 'old' (keep the / and their data-author). Then
write 'new' as that block's final state, under two rules:
RESOLVE ONLY THE CHANGE YOU WERE ASKED ABOUT. A block often carries several pending changes. Every one you were not asked about must appear in 'new' exactly as it stands in 'old' -- wrapper, data-author and text untouched. Pending is the default state: clearing a change nobody mentioned is a WRONG edit even when the resulting words look right. "Mentioned" is semantic, not author-only: a revision is in scope when the request identifies its author, its text, its role in a combined change, or explicitly includes it in the requested final value. Apply the requested action to every such revision, even when several authors' wrappers contribute pieces of one accepted or rejected result.
The only / allowed in 'new' are those ALREADY in 'old'; keep their data-author untouched. Never create one -- the tool handles any new revision markup itself. FINAL ACCEPTED TEXT: read remaining text and omit text. Text requested to "read exactly" must match exactly, including punctuation and spacing, and omit proposal labels or brackets absent from the supplied phrase.
ACCEPT an insertion -> keep its text, drop the wrapper. ACCEPT a deletion -> drop the and its text.
REJECT an insertion -> drop the and its text. REJECT a deletion -> keep its text, drop the wrapper.
MODIFY a change -- anyone's, your own included -> keep its / wrapper AND its data-author exactly as they are, and edit only the TEXT inside. Never unwrap it, re-author it, or hand-write a nested / in it. Examples (author configured as "Dana"): Reject Jane's insertion: old: The quick brown fox new: The fox Accept Jane's insertion; Sam's was not mentioned, so it stays exactly as it was: old: A big red fast car new: A big red fast car Accept a combined version update whose requested final value explicitly includes both pieces: old: Version 2.1 new: Version 2.1 Modify Jane's insertion, dropping "brown " -- edit her text in place; Dana's deletion is recorded inside Jane's insertion automatically: old: The quick brown fox new: The quick fox
BEST PRACTICES:
MATCH THE NEIGHBOURS. Editing or adding a table cell, a list item or a heading? Look at its siblings first -- the other cells in that column, the other bullets in that list, the other headings at that level -- and give yours the SAME formatting, spelled out. Content that is right but plain where its column is bold counts as wrong.
FORM ROLE PRECEDENCE. Infer labels and entry cells from repeated row structure before considering style names or cell widths. A class such as
Answersdoes not make non-empty text a placeholder, and a narrow blank cell can still be its entry cell. When a non-empty cell is paired with an adjacent blank cell, preserve the non-empty cell and fill the blank unless explicitly told otherwise. An -only cell is blank; label padding is not an entry slot.PRESERVE BLANK WORD FIELDS. "Click or tap here to enter text" marks an empty content control. Copy its complete placeholder element, including class and text, only when cloning a same-role peer in the same repeated form structure whose HTML visibly contains that placeholder. When filling an existing field, replace it with the value. Never infer a content control merely from "field" or "line", add one to an ordinary blank cell, or substitute filler.
HIGHLIGHTING COMES FROM WORD'S FIXED PALETTE, not from arbitrary colours: black, blue, cyan, darkBlue, darkCyan, darkGray, darkGreen, darkMagenta, darkRed, darkYellow, green, lightGray, magenta, red, white, yellow. Some are named differently in Word's own menu -- PINK is magenta (#FF00FF), turquoise is cyan (#00FFFF), violet is darkMagenta (#800080), bright green is green (#00FF00), gray 25%/50% are lightGray/darkGray. Write the palette colour's hex (e.g. bg-[#FF00FF] for pink); a colour off the palette gets snapped to the nearest one it can paint, which for a pastel is usually a grey.
| Name | Required | Description | Default |
|---|---|---|---|
| edits | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral load and does so: tracked-changes vs in-place is client-controlled, author comes from _meta and is unchangeable, edits succeed/fail individually with count/failed_edits/notices, applied edits are already saved, and there are documented limits (max 20 edits, ~10k-char 'new', parallel reconciliation). Mutating semantics and partial-failure behavior are fully disclosed.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is front-loaded with the core purpose and organized under clear headers, but it is enormous (well over a thousand words) with repeated restatements of retry safety and anchor uniqueness. Most sentences earn their place, yet the volume and redundancy go past what a tool description should carry.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values needn't be explained, yet the description still clarifies result semantics (count, failed_edits with 1-based index, count 0 + notices) that the schema alone would not make obvious. For a tool of this complexity, nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is reported at 0%, so the description must compensate, and it does far beyond the schema's one-line field notes: uniqueness of 'old', verbatim copying, the class="..." lazy convention, empty-string deletion, whole-<tr> row deletion, and per-edit overlap rejection. This is semantics the schema cannot express.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Opens with a specific verb+resource+scope: apply a LIST of edits in ONE call to a Word .docx. It explicitly frames itself against the siblings by tying 'old' to HTML previously returned by read_document, so an agent can tell edit_document apart from read_document/search_document without further inspection.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives explicit routing rules: call read_document before structural list edits, search_document's single <li> suffices for content-only changes, batch edits in one call (<=20), never resend a partially-applied batch, and don't retry edits named in notices. It also states when NOT to broaden an anchor on retry. When/when-not/alternatives are all present.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
read_documentAInspect
Read a .docx and return its HTML representation, paginated. Every edit anchor must be HTML copied verbatim from this tool or from search_document, so see the exact markup of a spot before you edit it.
WHICH TOOL FIRST — USE YOUR JUDGEMENT, DON'T DEFAULT TO READING EVERYTHING:
You already know WHAT text to change (a name, a date, a clause, a row, a placeholder)? Start with search_document. It returns each matching spot as a COMPLETE block — verbatim markup you can paste straight into edit_document as
old— plus the page it is on, so you usually never need to read that page at all. On a long document this is far faster and cheaper than paging through it.You need the SHAPE of the document (what sections exist, how a table is laid out, where a new paragraph belongs, or the request is vague like "tighten this up")? Read pages here. Start with the default span and page forward only as far as the task needs.
Short document, or you need all of it? One read is fine. Both tools return the SAME HTML, so an anchor from either one is equally valid.
PAGINATION: the response covers a contiguous RANGE of logical pages. By default the range
starts at page 1 and returns a small span (also capped by a per-call token budget) — so a
short document comes back whole. The LAST LINE of what you get back always states the range,
e.g. "Pages 3-7 of 42 returned; 35 pages remain." — read it, and you always know where you
are and how much you have not seen. (The same numbers travel as start_page, end_page,
total_pages, total_tokens and has_more.) Pass start_page to start at a later page and
end_page to widen or cap the range — a wide end_page is still bounded by the per-call token
budget, so on a large document has_more stays true and you keep calling with
start_page=end_page+1 until it is false.
Anchors copied from any page match the whole document, so you can edit straight from a page.
Don't re-read a page you have already been given, and don't page through a document hunting
for a string — search for it.
Tracked changes already in the document render as (insertions) and (deletions)
elements, each with a data-author. Use them to see which changes exist and who made them
before you accept, reject, or modify one (see edit_document).
Comments render as around the commented text, and each comment thread appears once as an
| Name | Required | Description | Default |
|---|---|---|---|
| end_page | No | 1-based last page to return; omit for a span from start_page. | |
| start_page | No | 1-based first page to return. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so thoroughly: it discloses the pagination contract, the token budget cap on span size, the terminal status line format, the has_more loop, and — critically — the domain-specific rendering of tracked changes (<ins>/<del> with data-author) and comments (<mark>/<aside> with shared data-comment, data-resolved, and thread structure). This is far more than a safe-read statement; it is genuine behavioral context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long but deliberately structured with headers (WHICH TOOL FIRST, PAGINATION) and front-loaded with the core purpose before the routing guidance. A few passages restate the same pagination facts twice (the status line and the field-name list), which is mild redundancy rather than padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read tool with two optional params and an output schema, the description covers everything an agent needs: entry conditions, span control, termination logic, anchor validity, and the markup conventions for tracked changes and comments. Nothing material is left to inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description goes beyond the schema by explaining that end_page merely widens a range that is still bounded by the per-call token budget, that has_more stays true on large documents, and that the correct loop is start_page=end_page+1. That is operational semantics the schema's one-line property descriptions do not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Read a .docx and return its HTML representation, paginated') and explicitly positions itself against both siblings (search_document for known targets, edit_document for applying anchors). An agent can tell instantly what this tool produces and how it differs from alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The 'WHICH TOOL FIRST' block gives explicit decision rules: use search_document when you already know the text, read pages here when you need document shape or the request is vague, and a single read suffices for short documents. It also adds negative guidance ('don't re-read a page you have already been given, don't page through hunting for a string'), which is exactly the routing information an agent needs.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_documentAInspect
Regex-search a .docx and get back the matching blocks, ready to edit — WITHOUT pulling the whole document into the conversation.
Use this to locate text in a document that is too large to read_document whole, or to jump
straight to the passages you care about before editing. The document is converted to the SAME
HTML representation read_document returns, and pattern is matched against BOTH that HTML and
the text a reader sees in it — so a phrase Word split across runs, or spelled with a
non-breaking space, still matches when typed plainly, AND a pattern naming markup (a class, an
, a tag) still works. If the pattern only matches with a relaxation (case, or a repeated
space), that is applied and note says so.
pattern is a Python regular expression, e.g.:
"Effective Date" -> literal text
"termination|terminate" -> either word
"(?i)confidential" -> case-insensitive
"$[0-9,]+(?:.[0-9]{2})?" -> dollar amounts
.matches newlines (DOTALL), so a pattern can span lines.
Each match is a COMPLETE BLOCK — verbatim HTML for the whole ///heading the match
sits in, tags and all, with nothing cut off. For a content-only edit of that
block, COPY match_text STRAIGHT INTO edit_document AS old; it needs no
trimming. For an insertion or structural edit, first treat match_text as a
locator. If it is not the nearest same-role structure the new content should
resemble, read the reported page and expand old to include that structural
example plus the insertion boundary. A match also carries:
matched— what your pattern actually matched inside the block, so you can see why it hit.page— the 1-based page it falls on, the SAME numbering read_document uses, so you can read_document(start_page=...) if you want the surrounding context.unique— false when that block's text appears elsewhere in the document too, so pasting it asoldwould be ambiguous. Add a neighbouring block from the same page to pin it down (see edit_document's MAKE 'old' UNIQUE).
When a match sits inside a semantic list item, match_text is the complete nearest
<li>...</li> rather than its inner paragraph or the surrounding <ol>/<ul>. That item is
enough for a one-item content edit. Before a structural list edit though, use read_document on the
reported page to inspect the parent list's type/start, sibling items, and nesting.
total_matches is how many spots match in the WHOLE document; the blocks returned in one call
fit a bounded budget, so has_more tells you some are still waiting and you get them with
start_at (1-based index of the first match you want, e.g. start_at=9 after eight came back).
note states all of this in one line — read it. Nothing is silently dropped, so you never have
to narrow a pattern just to see the rest of its matches.
Two spots that read identically (a running footer, a repeated placeholder) are two matches with
the same match_text, both marked unique: false — not a mistake, and not a reason to search
again. They differ by page, which is how you pick the one you want.
Only the document's CONTENT is searched, never the stylesheet in its , so a pattern naming a CSS class finds the blocks carrying that class and not the rule that defines it.
Comment markup is searchable too. <aside finds comment threads, while <article finds
individual comments and replies; each result returns its complete <aside> thread so it remains
a valid edit anchor. Consequently, the total for <article\b is the document's comment count,
whereas <aside\b is the thread count.
| Name | Required | Description | Default |
|---|---|---|---|
| pattern | Yes | Regex to run over the document's HTML projection. | |
| start_at | No | 1-based index of the first match to return. |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so richly: matching runs against both the HTML and the reader-visible text, relaxed matches are flagged in `note`, DOTALL is on, matches are complete blocks not fragments, pagination via total_matches/has_more/start_at drops nothing silently, duplicate identical matches are expected and distinguished by page, stylesheets in <head> are never searched, and comment markup is searchable. Also discloses the non-obvious case that `<article` counts comments while `<aside` counts threads.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Long, but front-loaded with the purpose and scope in the first sentence, then organized by concern (pattern behavior, match anatomy, pagination, duplicates, markup). Every section carries real information, though the density of secondary details (comment markup, stylesheet exclusion) makes it heavier than strictly necessary.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists and the description still explains the match fields an agent must act on (matched, page, unique, match_text, note, total_matches, has_more) in terms of what to do with them. For a two-parameter search tool with no annotations, nothing an agent needs to call or interpret it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description genuinely adds meaning beyond the schema: `pattern` is a Python regex, dot matches newlines, it is matched against both HTML and rendered text, and relaxation rules apply. It also illustrates `start_at` semantics with a concrete worked example ('start_at=9 after eight came back'). It falls short of 5 only because the per-parameter syntax details (four example patterns) shade into usage rather than pure parameter semantics.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb (regex-search) plus resource (.docx) and a concrete deliverable (matching blocks ready to edit) with an explicit scope boundary ('WITHOUT pulling the whole document into the conversation'). It also distinguishes itself from both siblings, read_document and edit_document, which is stated directly rather than implied.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Names the exact condition for use ('a document that is too large to read_document whole', 'jump straight to the passages you care about before editing') and routes the agent onward to read_document(start_page=...) for context and edit_document for the actual edit. When-not is implicit but the alternative paths are spelled out.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
- First observed
edit_document - First observed
read_document - First observed
search_document
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceRemote MCP server that lets AI assistants read tracked competitive intelligence — competitor news, reviews, website changes, social posts and ads — and act on it by managing projects, competitors, battle cards, reports and canvases. Every answer carries a source and a date, and write actions only publish when explicitly shared.MIT
- AlicenseCqualityCmaintenanceCompetitor Monitor AI - MCP server providing AI-powered tools and automation by MEOK AI Labs112 npm29 PyPIMIT
- AlicenseAqualityDmaintenanceEnables brand visibility monitoring across major AI platforms like ChatGPT, Claude, Gemini, and Perplexity. It allows users to track visibility scores, analyze competitor data, and receive actionable insights to improve AI-generated brand recommendations.1623 npm1MIT

industrylens-mcpofficial
AlicenseNot gradedqualityBmaintenanceBrowse IndustryLens's published competitive-intelligence reports and head-to-head competitor comparisons from any AI agent — real, source-backed data.MIT
Glama MCP Gateway
Add one secure layer between your agents and this server.