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 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 (67 tests, <500ms)
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
- | Page Info | `word_get_page_info` requires WordApiDesktop 1.2+ (Word for Windows/Mac desktop). Not available on Word for the web. | |
303
- | 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. | |
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 import_types7 = require("@modelcontextprotocol/sdk/types.js");
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. Note: Word search codes (^p = paragraph mark, ^t = tab) are interpreted. To search for literal "^", use "^^".',
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 (^p = paragraph mark, ^t = tab). For single-paragraph edits, prefer word_replace_paragraph_text.",
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.0",
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(import_types7.ListToolsRequestSchema, async () => ({
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(import_types7.CallToolRequestSchema, async (request) => {
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(import_types7.ListResourcesRequestSchema, async () => ({
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(import_types7.ReadResourceRequestSchema, async (request) => {
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
  }