mcp-word-bridge 4.1.0 → 4.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/server.js +120 -28
- package/dist/server.js.map +3 -3
- package/dist/taskpane-app.js +229 -39
- package/dist/taskpane-app.js.map +3 -3
- package/manifest.xml +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -266,7 +266,7 @@ npm install
|
|
|
266
266
|
npm run build # build server + taskpane
|
|
267
267
|
npm run dev # watch mode
|
|
268
268
|
npm run typecheck # TypeScript type checking
|
|
269
|
-
npm test # unit tests (
|
|
269
|
+
npm test # unit tests (272 tests, <600ms)
|
|
270
270
|
npm run test:live # integration tests (requires Word)
|
|
271
271
|
```
|
|
272
272
|
|
|
@@ -299,8 +299,9 @@ Platform constraints in the Word JavaScript API that cannot be resolved in this
|
|
|
299
299
|
| Tracked Changes | `getTrackedChanges()` throws if the document contains "moved" tracked changes (drag-and-drop reorders). These produce paired "moved from"/"moved to" entries that the API cannot handle. | [office-js#5535](https://github.com/OfficeDev/office-js/issues/5535) |
|
|
300
300
|
| Highlight Colors | `highlightColor` only supports 17 named colors (Yellow, Green, Cyan, Magenta, Blue, Red, DarkBlue, DarkCyan, DarkGreen, DarkMagenta, DarkRed, DarkYellow, Gray25, Gray50, Black, White, NoHighlight). The Word API documentation claims `#RRGGBB` is accepted, but Desktop silently maps hex values to the nearest named color. This tool rejects hex to prevent silent mismatch — use `color` for arbitrary RGB. | [office-js#4638](https://github.com/OfficeDev/office-js/issues/4638) |
|
|
301
301
|
| Paragraph Style | TOC field entries and certain table paragraphs return `style: ""` (empty string) instead of the actual style name. Paragraphs are flagged with `isTocEntry: true` when detected as TOC entries. Use this flag rather than the style field for identification. | [office-js#5934](https://github.com/OfficeDev/office-js/issues/5934) |
|
|
302
|
-
|
|
|
303
|
-
|
|
|
302
|
+
| Mixed Formatting | `word_get_paragraph_by_index` returns `null` for font properties (`bold`, `italic`, `size`, etc.) when the paragraph contains mixed formatting (e.g. partially bold text). This is the Word API's way of indicating "no single value" — treat `null` as "mixed". | [Word.Font docs](https://learn.microsoft.com/en-us/javascript/api/word/word.font?view=word-js-preview) |
|
|
303
|
+
| Page Info | `word_get_page_info` requires WordApiDesktop 1.2+ (Word for Windows/Mac desktop). Not available on Word for the web. | [WordApiDesktop 1.2](https://learn.microsoft.com/en-us/javascript/api/requirement-sets/word/word-api-desktop-1-2-requirement-set) |
|
|
304
|
+
| Undo | The Word JavaScript API does not expose a programmatic `undo()` method. Changes made by the add-in appear in Word's undo stack (Ctrl+Z works for the user), but there is no way to trigger undo from code. | [Stack Overflow](https://stackoverflow.com/questions/58440921/accessing-the-undo-stack-from-word-javascript-api) |
|
|
304
305
|
|
|
305
306
|
## License
|
|
306
307
|
|
package/dist/server.js
CHANGED
|
@@ -18722,19 +18722,28 @@ var Bridge = class {
|
|
|
18722
18722
|
|
|
18723
18723
|
// src/server/mcp.ts
|
|
18724
18724
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
18725
|
-
var
|
|
18725
|
+
var import_types9 = require("@modelcontextprotocol/sdk/types.js");
|
|
18726
|
+
|
|
18727
|
+
// src/server/types.ts
|
|
18728
|
+
var ToolError = class extends Error {
|
|
18729
|
+
constructor(message) {
|
|
18730
|
+
super(message);
|
|
18731
|
+
this.name = "ToolError";
|
|
18732
|
+
}
|
|
18733
|
+
};
|
|
18726
18734
|
|
|
18727
18735
|
// src/server/tools/helpers.ts
|
|
18728
18736
|
function jsonResult(data) {
|
|
18729
18737
|
return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
|
|
18730
18738
|
}
|
|
18731
|
-
function forwardTool(name, description, schema, action) {
|
|
18739
|
+
function forwardTool(name, description, schema, action, validate) {
|
|
18732
18740
|
return {
|
|
18733
18741
|
name,
|
|
18734
18742
|
description,
|
|
18735
18743
|
schema,
|
|
18736
18744
|
bridgeAction: action,
|
|
18737
18745
|
async handler(args, bridge2) {
|
|
18746
|
+
if (validate) validate(args);
|
|
18738
18747
|
const result = await bridge2.send(action, args);
|
|
18739
18748
|
return jsonResult(result);
|
|
18740
18749
|
}
|
|
@@ -18780,7 +18789,7 @@ var save = forwardTool(
|
|
|
18780
18789
|
);
|
|
18781
18790
|
var clear = forwardTool(
|
|
18782
18791
|
"word_clear",
|
|
18783
|
-
"[Document] Clear all document body content. Does not clear headers/footers or custom properties.",
|
|
18792
|
+
"[Document] Clear all document body content. Does not clear headers/footers or custom properties. In multi-section documents, section breaks are removed and the last section's layout (margins, orientation) is preserved.",
|
|
18784
18793
|
{ properties: {} },
|
|
18785
18794
|
"clearDocument"
|
|
18786
18795
|
);
|
|
@@ -18823,6 +18832,9 @@ var getDocumentOutline = {
|
|
|
18823
18832
|
},
|
|
18824
18833
|
async handler(args, bridge2) {
|
|
18825
18834
|
const maxLevel = args.maxLevel ?? 3;
|
|
18835
|
+
if (typeof args.maxLevel === "number" && (args.maxLevel < 1 || args.maxLevel > 9 || !Number.isInteger(args.maxLevel))) {
|
|
18836
|
+
throw new ToolError("maxLevel must be an integer between 1 and 9.");
|
|
18837
|
+
}
|
|
18826
18838
|
const result = await bridge2.send("getParagraphs", {});
|
|
18827
18839
|
const headings = [];
|
|
18828
18840
|
for (const para of result.paragraphs) {
|
|
@@ -18854,14 +18866,6 @@ var documentTools = [
|
|
|
18854
18866
|
getDocumentOutline
|
|
18855
18867
|
];
|
|
18856
18868
|
|
|
18857
|
-
// src/server/types.ts
|
|
18858
|
-
var ToolError = class extends Error {
|
|
18859
|
-
constructor(message) {
|
|
18860
|
-
super(message);
|
|
18861
|
-
this.name = "ToolError";
|
|
18862
|
-
}
|
|
18863
|
-
};
|
|
18864
|
-
|
|
18865
18869
|
// src/server/validation.ts
|
|
18866
18870
|
function checkNonEmpty(value, name) {
|
|
18867
18871
|
if (!value || typeof value !== "string" || value.trim() === "") {
|
|
@@ -18926,6 +18930,11 @@ function checkPropertyKeyLength(key) {
|
|
|
18926
18930
|
);
|
|
18927
18931
|
}
|
|
18928
18932
|
}
|
|
18933
|
+
function checkHexColor(color, name) {
|
|
18934
|
+
if (!/^#[0-9A-Fa-f]{6}$/.test(color)) {
|
|
18935
|
+
throw new ToolError(`${name} must be a valid hex color (e.g. #FF0000).`);
|
|
18936
|
+
}
|
|
18937
|
+
}
|
|
18929
18938
|
|
|
18930
18939
|
// src/server/tools/paragraphs.ts
|
|
18931
18940
|
var getParagraphs = forwardTool(
|
|
@@ -18941,7 +18950,7 @@ var getParagraphs = forwardTool(
|
|
|
18941
18950
|
);
|
|
18942
18951
|
var getParagraphByIndex = forwardTool(
|
|
18943
18952
|
"word_get_paragraph_by_index",
|
|
18944
|
-
"[Paragraphs] Get full details of a single paragraph including font, spacing, indentation, and outline level.",
|
|
18953
|
+
"[Paragraphs] Get full details of a single paragraph including font, spacing, indentation, and outline level. Font properties return null when the paragraph has mixed formatting (e.g. partially bold).",
|
|
18945
18954
|
{
|
|
18946
18955
|
properties: {
|
|
18947
18956
|
index: { type: "number", description: "Paragraph index (0-based)" }
|
|
@@ -19033,6 +19042,12 @@ var setParagraphSpacing = {
|
|
|
19033
19042
|
async handler(args, bridge2) {
|
|
19034
19043
|
const index = args.index;
|
|
19035
19044
|
checkNonNegative(index, "index");
|
|
19045
|
+
const hasProperty = args.lineSpacing !== void 0 || args.spaceBefore !== void 0 || args.spaceAfter !== void 0 || args.firstLineIndent !== void 0 || args.leftIndent !== void 0 || args.rightIndent !== void 0;
|
|
19046
|
+
if (!hasProperty) {
|
|
19047
|
+
throw new ToolError(
|
|
19048
|
+
"At least one spacing or indent property must be provided (lineSpacing, spaceBefore, spaceAfter, firstLineIndent, leftIndent, rightIndent)."
|
|
19049
|
+
);
|
|
19050
|
+
}
|
|
19036
19051
|
const spacingFields = [
|
|
19037
19052
|
["lineSpacing", args.lineSpacing],
|
|
19038
19053
|
["spaceBefore", args.spaceBefore],
|
|
@@ -19092,6 +19107,12 @@ var moveParagraph = {
|
|
|
19092
19107
|
throw new ToolError(`Paragraph ${para.index} is inside a table cell. Use table-specific tools to modify table content.`);
|
|
19093
19108
|
}
|
|
19094
19109
|
}
|
|
19110
|
+
const destPara = paraCount.paragraphs.find((p) => p.index === toIndex);
|
|
19111
|
+
if (destPara?.inTable) {
|
|
19112
|
+
throw new ToolError(
|
|
19113
|
+
`Destination paragraph ${toIndex} is inside a table cell. Moving content here would corrupt table structure. Use table-specific tools or target a paragraph outside the table.`
|
|
19114
|
+
);
|
|
19115
|
+
}
|
|
19095
19116
|
const lastIdx = total - 1;
|
|
19096
19117
|
if (fromIndex + count - 1 === lastIdx) {
|
|
19097
19118
|
const lastPara = paraCount.paragraphs.find((p) => p.index === lastIdx);
|
|
@@ -19157,6 +19178,12 @@ var copyParagraph = {
|
|
|
19157
19178
|
throw new ToolError(`Paragraph ${para.index} is inside a table cell. Use table-specific tools to modify table content.`);
|
|
19158
19179
|
}
|
|
19159
19180
|
}
|
|
19181
|
+
const destPara = paraCount.paragraphs.find((p) => p.index === toIndex);
|
|
19182
|
+
if (destPara?.inTable) {
|
|
19183
|
+
throw new ToolError(
|
|
19184
|
+
`Destination paragraph ${toIndex} is inside a table cell. Copying content here would corrupt table structure. Use table-specific tools or target a paragraph outside the table.`
|
|
19185
|
+
);
|
|
19186
|
+
}
|
|
19160
19187
|
const ooxmlResult = await bridge2.send("getParaOoxml", { index: fromIndex, count });
|
|
19161
19188
|
const effectiveToIndex = toIndex >= fromIndex && toIndex < fromIndex + count ? fromIndex + count - 1 : toIndex;
|
|
19162
19189
|
const effectiveLocation = toIndex >= fromIndex && toIndex < fromIndex + count ? "After" : location;
|
|
@@ -19180,12 +19207,17 @@ var paragraphTools = [
|
|
|
19180
19207
|
// src/server/tools/search.ts
|
|
19181
19208
|
var search = forwardTool(
|
|
19182
19209
|
"word_search",
|
|
19183
|
-
'[Search] Find text in the document. Returns match count and up to 30 matches. Query must be \u2264255 chars.
|
|
19210
|
+
'[Search] Find text in the document. Returns match count and up to 30 matches. Query must be \u2264255 chars. Supports Word search codes (^p = paragraph mark, ^t = tab, etc.) and wildcard mode (?, *, [], {n,m}). To search for literal "^", use "^^".',
|
|
19184
19211
|
{
|
|
19185
19212
|
properties: {
|
|
19186
19213
|
query: { type: "string" },
|
|
19187
19214
|
matchCase: { type: "boolean", description: "Case-sensitive search. Default: false" },
|
|
19188
|
-
matchWholeWord: { type: "boolean" }
|
|
19215
|
+
matchWholeWord: { type: "boolean", description: "Match whole words only. Default: false" },
|
|
19216
|
+
matchWildcards: { type: "boolean", description: "Enable wildcard/regex search (?, *, [], {n,m}, @). Default: false" },
|
|
19217
|
+
matchPrefix: { type: "boolean", description: "Match words that begin with the search string. Default: false" },
|
|
19218
|
+
matchSuffix: { type: "boolean", description: "Match words that end with the search string. Default: false" },
|
|
19219
|
+
ignorePunct: { type: "boolean", description: "Ignore punctuation between words when matching. Default: false" },
|
|
19220
|
+
ignoreSpace: { type: "boolean", description: "Ignore whitespace between words when matching. Default: false" }
|
|
19189
19221
|
},
|
|
19190
19222
|
required: ["query"]
|
|
19191
19223
|
},
|
|
@@ -19193,13 +19225,18 @@ var search = forwardTool(
|
|
|
19193
19225
|
);
|
|
19194
19226
|
var searchAndReplace = {
|
|
19195
19227
|
name: "word_search_and_replace",
|
|
19196
|
-
description: "[Search] Find and replace ALL occurrences. Supports Word search codes
|
|
19228
|
+
description: "[Search] Find and replace ALL occurrences. Supports Word search codes in both find and replace: ^p (paragraph mark), ^l (line break), ^m (page break), ^n (column break), ^t (tab), ^s (non-breaking space), ^~ (non-breaking hyphen), ^- (optional hyphen), ^+ (em dash), ^= (en dash), ^^ (literal caret). Supports wildcard search in find string. For single-paragraph edits, prefer word_replace_paragraph_text.",
|
|
19197
19229
|
schema: {
|
|
19198
19230
|
properties: {
|
|
19199
19231
|
find: { type: "string" },
|
|
19200
19232
|
replace: { type: "string" },
|
|
19201
19233
|
matchCase: { type: "boolean", description: "Default: false" },
|
|
19202
|
-
matchWholeWord: { type: "boolean" },
|
|
19234
|
+
matchWholeWord: { type: "boolean", description: "Match whole words only. Default: false" },
|
|
19235
|
+
matchWildcards: { type: "boolean", description: "Enable wildcard/regex search in find string (?, *, [], {n,m}, @). Default: false" },
|
|
19236
|
+
matchPrefix: { type: "boolean", description: "Match words that begin with the find string. Default: false" },
|
|
19237
|
+
matchSuffix: { type: "boolean", description: "Match words that end with the find string. Default: false" },
|
|
19238
|
+
ignorePunct: { type: "boolean", description: "Ignore punctuation between words when matching. Default: false" },
|
|
19239
|
+
ignoreSpace: { type: "boolean", description: "Ignore whitespace between words when matching. Default: false" },
|
|
19203
19240
|
preserveBookmarks: { type: "boolean", description: "Re-create bookmarks on replacement text after replace. Default: false" }
|
|
19204
19241
|
},
|
|
19205
19242
|
required: ["find", "replace"]
|
|
@@ -19215,14 +19252,19 @@ var searchAndReplace = {
|
|
|
19215
19252
|
};
|
|
19216
19253
|
var insertTextAtMatch = forwardTool(
|
|
19217
19254
|
"word_insert_text_at_match",
|
|
19218
|
-
'[Search] Insert text before or after a search match. Provide "after" OR "before" as the anchor text. Use occurrence for Nth match.',
|
|
19255
|
+
'[Search] Insert text before or after a search match. Supports Word search codes in inserted text: ^p (paragraph mark), ^l (line break), ^t (tab), ^s (non-breaking space), ^m (page break), ^n (column break), ^~ (non-breaking hyphen), ^- (optional hyphen), ^+ (em dash), ^= (en dash), ^^ (literal caret). Provide "after" OR "before" as the anchor text. Use occurrence for Nth match. Note: inserting ^p after hyperlinked text may create a paragraph that inherits the hyperlink character style.',
|
|
19219
19256
|
{
|
|
19220
19257
|
properties: {
|
|
19221
19258
|
text: { type: "string", description: "Text to insert" },
|
|
19222
19259
|
after: { type: "string", description: "Search for this text and insert AFTER it" },
|
|
19223
19260
|
before: { type: "string", description: "Search for this text and insert BEFORE it" },
|
|
19224
19261
|
occurrence: { type: "number", description: "0=first, 1=second, etc. Default: 0" },
|
|
19225
|
-
matchCase: { type: "boolean", description: "Default: false" }
|
|
19262
|
+
matchCase: { type: "boolean", description: "Default: false" },
|
|
19263
|
+
matchWildcards: { type: "boolean", description: "Enable wildcard/regex search for anchor text. Default: false" },
|
|
19264
|
+
matchPrefix: { type: "boolean", description: "Match words that begin with the anchor text. Default: false" },
|
|
19265
|
+
matchSuffix: { type: "boolean", description: "Match words that end with the anchor text. Default: false" },
|
|
19266
|
+
ignorePunct: { type: "boolean", description: "Ignore punctuation between words when matching. Default: false" },
|
|
19267
|
+
ignoreSpace: { type: "boolean", description: "Ignore whitespace between words when matching. Default: false" }
|
|
19226
19268
|
},
|
|
19227
19269
|
required: ["text"]
|
|
19228
19270
|
},
|
|
@@ -19270,6 +19312,34 @@ var searchTools = [
|
|
|
19270
19312
|
];
|
|
19271
19313
|
|
|
19272
19314
|
// src/server/tools/formatting.ts
|
|
19315
|
+
var HIGHLIGHT_COLORS = ["Yellow", "Green", "Cyan", "Magenta", "Blue", "Red", "DarkBlue", "DarkCyan", "DarkGreen", "DarkMagenta", "DarkRed", "DarkYellow", "Gray25", "Gray50", "Black", "White", "NoHighlight"];
|
|
19316
|
+
function validateFormatText(args) {
|
|
19317
|
+
checkNonEmpty(args.text, "text");
|
|
19318
|
+
const hasFormatting = args.bold !== void 0 || args.italic !== void 0 || args.underline !== void 0 || args.strikeThrough !== void 0 || args.color !== void 0 || args.highlightColor !== void 0 || args.size !== void 0 || args.name !== void 0;
|
|
19319
|
+
if (!hasFormatting) {
|
|
19320
|
+
throw new ToolError(
|
|
19321
|
+
"At least one formatting property must be specified (bold, italic, underline, strikeThrough, color, highlightColor, size, or name)."
|
|
19322
|
+
);
|
|
19323
|
+
}
|
|
19324
|
+
if (args.size !== void 0) {
|
|
19325
|
+
const size = args.size;
|
|
19326
|
+
if (size <= 0) throw new ToolError("size must be positive (minimum 1 point).");
|
|
19327
|
+
if (size > 1638) throw new ToolError("size must not exceed 1638 points (Word maximum).");
|
|
19328
|
+
if (!Number.isFinite(size)) throw new ToolError("size must be a finite number.");
|
|
19329
|
+
}
|
|
19330
|
+
if (args.color !== void 0) {
|
|
19331
|
+
checkHexColor(args.color, "color");
|
|
19332
|
+
}
|
|
19333
|
+
if (args.highlightColor !== void 0) {
|
|
19334
|
+
const hc = args.highlightColor;
|
|
19335
|
+
const isNamed = HIGHLIGHT_COLORS.some((c) => c.toLowerCase() === hc.toLowerCase());
|
|
19336
|
+
if (!isNamed) {
|
|
19337
|
+
throw new ToolError(
|
|
19338
|
+
`Invalid highlightColor: "${hc}". Valid values: ${HIGHLIGHT_COLORS.join(", ")}.`
|
|
19339
|
+
);
|
|
19340
|
+
}
|
|
19341
|
+
}
|
|
19342
|
+
}
|
|
19273
19343
|
var formatText = forwardTool(
|
|
19274
19344
|
"word_format_text",
|
|
19275
19345
|
"[Formatting] Apply formatting (bold, italic, color, size, font) to a text match. Color must be hex (#FF0000). Size: 1-1638pt.",
|
|
@@ -19289,7 +19359,8 @@ var formatText = forwardTool(
|
|
|
19289
19359
|
},
|
|
19290
19360
|
required: ["text"]
|
|
19291
19361
|
},
|
|
19292
|
-
"formatRange"
|
|
19362
|
+
"formatRange",
|
|
19363
|
+
validateFormatText
|
|
19293
19364
|
);
|
|
19294
19365
|
var clearFormatting = forwardTool(
|
|
19295
19366
|
"word_clear_formatting",
|
|
@@ -19468,7 +19539,7 @@ var tableTools = [
|
|
|
19468
19539
|
// src/server/tools/lists.ts
|
|
19469
19540
|
var insertList = forwardTool(
|
|
19470
19541
|
"word_insert_list",
|
|
19471
|
-
"[Lists] Insert a bulleted or numbered list from an array of item strings.",
|
|
19542
|
+
"[Lists] Insert a bulleted or numbered list from an array of item strings. Text is inserted literally (Word search codes like ^p or ^t are NOT interpreted).",
|
|
19472
19543
|
{
|
|
19473
19544
|
properties: {
|
|
19474
19545
|
items: { type: "array", items: { type: "string" }, description: "List item strings" },
|
|
@@ -19521,7 +19592,11 @@ var addComment = forwardTool(
|
|
|
19521
19592
|
},
|
|
19522
19593
|
required: ["anchorText", "comment"]
|
|
19523
19594
|
},
|
|
19524
|
-
"addComment"
|
|
19595
|
+
"addComment",
|
|
19596
|
+
(args) => {
|
|
19597
|
+
checkNonEmpty(args.anchorText, "anchorText");
|
|
19598
|
+
checkNonEmpty(args.comment, "comment");
|
|
19599
|
+
}
|
|
19525
19600
|
);
|
|
19526
19601
|
var getComments = forwardTool(
|
|
19527
19602
|
"word_get_comments",
|
|
@@ -19550,7 +19625,10 @@ var replyToComment = forwardTool(
|
|
|
19550
19625
|
},
|
|
19551
19626
|
required: ["commentId", "text"]
|
|
19552
19627
|
},
|
|
19553
|
-
"replyToComment"
|
|
19628
|
+
"replyToComment",
|
|
19629
|
+
(args) => {
|
|
19630
|
+
checkNonEmpty(args.text, "text");
|
|
19631
|
+
}
|
|
19554
19632
|
);
|
|
19555
19633
|
var resolveComment = forwardTool(
|
|
19556
19634
|
"word_resolve_comment",
|
|
@@ -19813,6 +19891,9 @@ var setContentControlText = {
|
|
|
19813
19891
|
if (tag) {
|
|
19814
19892
|
const ccResult = await bridge2.send("getContentControls", {});
|
|
19815
19893
|
const matches = ccResult.controls.filter((c) => c.tag === tag);
|
|
19894
|
+
if (matches.length === 0) {
|
|
19895
|
+
throw new ToolError(`Content control with tag "${tag}" not found. Use word_get_content_controls to list available controls.`);
|
|
19896
|
+
}
|
|
19816
19897
|
if (matches.length > 1) {
|
|
19817
19898
|
throw new ToolError(
|
|
19818
19899
|
`Multiple content controls (${matches.length}) share tag "${tag}". Use "id" instead to target a specific control. Matching IDs: ${matches.map((m) => m.id).join(", ")}.`
|
|
@@ -20181,7 +20262,7 @@ var equationTools = [
|
|
|
20181
20262
|
function createBatchTool(registry, actionMap) {
|
|
20182
20263
|
return {
|
|
20183
20264
|
name: "word_batch",
|
|
20184
|
-
description: "[Batch] Execute multiple operations in a single call. Operations execute sequentially \u2014 if one fails, subsequent are skipped.",
|
|
20265
|
+
description: "[Batch] Execute multiple operations in a single call. Operations execute sequentially \u2014 if one fails, subsequent are skipped. Note: paragraph indices are NOT auto-adjusted between operations. Multiple inserts at the same index will produce reversed order (last inserted appears first).",
|
|
20185
20266
|
schema: {
|
|
20186
20267
|
properties: {
|
|
20187
20268
|
operations: {
|
|
@@ -20377,6 +20458,8 @@ Controls a live Word document. All operations execute immediately.
|
|
|
20377
20458
|
\`\`\`
|
|
20378
20459
|
Runs sequentially. Stops on first error. Maximum 50 per batch. Prefer batching over individual calls.
|
|
20379
20460
|
|
|
20461
|
+
**Index caveat:** Paragraph indices are NOT auto-adjusted between operations. Multiple inserts at the same index produce reversed order (last inserted appears first). To insert A, B, C in order at position 5, either increment the index or insert in reverse.
|
|
20462
|
+
|
|
20380
20463
|
## Search
|
|
20381
20464
|
|
|
20382
20465
|
- Case-insensitive by default. Pass \`matchCase: true\` for exact case.
|
|
@@ -20437,12 +20520,21 @@ After inserting a Table of Contents, heading text appears twice (in TOC and body
|
|
|
20437
20520
|
6. Use \`word_copy_paragraph\` to duplicate (preserves everything)
|
|
20438
20521
|
7. Save explicitly after significant changes
|
|
20439
20522
|
8. Resolve comments rather than deleting (preserves audit trail)
|
|
20523
|
+
|
|
20524
|
+
## Alignment Values
|
|
20525
|
+
|
|
20526
|
+
Input accepts case-insensitive: "left", "center", "right", "justified".
|
|
20527
|
+
Output always uses canonical form: "Left", "Center", "Right", "Justified".
|
|
20528
|
+
|
|
20529
|
+
## Mixed Formatting
|
|
20530
|
+
|
|
20531
|
+
\`word_get_paragraph_by_index\` returns \`null\` for font properties when the paragraph has mixed formatting (e.g. partially bold). Treat \`null\` as "mixed" \u2014 use \`word_get_font_info\` with a specific text match for precise values.
|
|
20440
20532
|
`;
|
|
20441
20533
|
|
|
20442
20534
|
// package.json
|
|
20443
20535
|
var package_default = {
|
|
20444
20536
|
name: "mcp-word-bridge",
|
|
20445
|
-
version: "4.1.
|
|
20537
|
+
version: "4.1.2",
|
|
20446
20538
|
description: "MCP server for live Word document editing via Office Add-in",
|
|
20447
20539
|
main: "dist/server.js",
|
|
20448
20540
|
bin: {
|
|
@@ -20507,7 +20599,7 @@ function createMcpServer(bridge2) {
|
|
|
20507
20599
|
);
|
|
20508
20600
|
const { tools, handlers } = buildToolRegistry();
|
|
20509
20601
|
const toolMutex = createMutex();
|
|
20510
|
-
server.setRequestHandler(
|
|
20602
|
+
server.setRequestHandler(import_types9.ListToolsRequestSchema, async () => ({
|
|
20511
20603
|
tools: tools.map((t) => ({
|
|
20512
20604
|
name: t.name,
|
|
20513
20605
|
description: t.description,
|
|
@@ -20518,7 +20610,7 @@ function createMcpServer(bridge2) {
|
|
|
20518
20610
|
}
|
|
20519
20611
|
}))
|
|
20520
20612
|
}));
|
|
20521
|
-
server.setRequestHandler(
|
|
20613
|
+
server.setRequestHandler(import_types9.CallToolRequestSchema, async (request) => {
|
|
20522
20614
|
return toolMutex.run(async () => {
|
|
20523
20615
|
const { name, arguments: args } = request.params;
|
|
20524
20616
|
const handler = handlers.get(name);
|
|
@@ -20534,7 +20626,7 @@ function createMcpServer(bridge2) {
|
|
|
20534
20626
|
}
|
|
20535
20627
|
});
|
|
20536
20628
|
});
|
|
20537
|
-
server.setRequestHandler(
|
|
20629
|
+
server.setRequestHandler(import_types9.ListResourcesRequestSchema, async () => ({
|
|
20538
20630
|
resources: [{
|
|
20539
20631
|
uri: "word-bridge://usage-guide",
|
|
20540
20632
|
name: "Word Bridge Usage Guide",
|
|
@@ -20542,7 +20634,7 @@ function createMcpServer(bridge2) {
|
|
|
20542
20634
|
mimeType: "text/markdown"
|
|
20543
20635
|
}]
|
|
20544
20636
|
}));
|
|
20545
|
-
server.setRequestHandler(
|
|
20637
|
+
server.setRequestHandler(import_types9.ReadResourceRequestSchema, async (request) => {
|
|
20546
20638
|
if (request.params.uri === "word-bridge://usage-guide") {
|
|
20547
20639
|
return { contents: [{ uri: request.params.uri, mimeType: "text/markdown", text: usageGuide }] };
|
|
20548
20640
|
}
|