@atlaskit/ads-mcp 0.18.1 → 0.19.0

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.
Files changed (93) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +2 -2
  3. package/dist/cjs/helpers/fuse-multi-term.js +130 -0
  4. package/dist/cjs/helpers/index.js +15 -1
  5. package/dist/cjs/index.js +28 -14
  6. package/dist/cjs/instructions.js +1 -1
  7. package/dist/cjs/tools/analyze-a11y/index.js +12 -12
  8. package/dist/cjs/tools/get-a11y-guidelines/index.js +3 -3
  9. package/dist/cjs/tools/{get-components → get-all-components}/index.js +6 -6
  10. package/dist/cjs/tools/get-all-icons/index.js +1 -1
  11. package/dist/cjs/tools/get-all-tokens/index.js +1 -1
  12. package/dist/cjs/tools/get-guidelines/index.js +4 -4
  13. package/dist/cjs/tools/get-lint-rules/index.js +5 -5
  14. package/dist/cjs/tools/i18n-conversion/index.js +3 -3
  15. package/dist/cjs/tools/migration-guides/index.js +3 -3
  16. package/dist/cjs/tools/plan/index.js +13 -17
  17. package/dist/cjs/tools/search-components/index.js +44 -65
  18. package/dist/cjs/tools/search-icons/index.js +42 -68
  19. package/dist/cjs/tools/search-tokens/index.js +56 -43
  20. package/dist/cjs/tools/suggest-a11y-fixes/index.js +7 -7
  21. package/dist/es2019/helpers/fuse-multi-term.js +98 -0
  22. package/dist/es2019/helpers/index.js +1 -0
  23. package/dist/es2019/index.js +81 -83
  24. package/dist/es2019/instructions.js +2 -1
  25. package/dist/es2019/tools/analyze-a11y/index.js +28 -12
  26. package/dist/es2019/tools/get-a11y-guidelines/index.js +10 -3
  27. package/dist/es2019/tools/{get-components → get-all-components}/index.js +9 -4
  28. package/dist/es2019/tools/get-all-icons/index.js +6 -1
  29. package/dist/es2019/tools/get-all-tokens/index.js +6 -1
  30. package/dist/es2019/tools/get-guidelines/index.js +20 -7
  31. package/dist/es2019/tools/get-lint-rules/index.js +12 -8
  32. package/dist/es2019/tools/i18n-conversion/index.js +10 -13
  33. package/dist/es2019/tools/migration-guides/index.js +10 -4
  34. package/dist/es2019/tools/plan/index.js +25 -25
  35. package/dist/es2019/tools/search-components/index.js +42 -56
  36. package/dist/es2019/tools/search-icons/index.js +37 -62
  37. package/dist/es2019/tools/search-tokens/index.js +54 -45
  38. package/dist/es2019/tools/suggest-a11y-fixes/index.js +16 -7
  39. package/dist/esm/helpers/fuse-multi-term.js +122 -0
  40. package/dist/esm/helpers/index.js +1 -0
  41. package/dist/esm/index.js +28 -14
  42. package/dist/esm/instructions.js +1 -1
  43. package/dist/esm/tools/analyze-a11y/index.js +12 -12
  44. package/dist/esm/tools/get-a11y-guidelines/index.js +3 -3
  45. package/dist/esm/tools/{get-components → get-all-components}/index.js +5 -5
  46. package/dist/esm/tools/get-all-icons/index.js +1 -1
  47. package/dist/esm/tools/get-all-tokens/index.js +1 -1
  48. package/dist/esm/tools/get-guidelines/index.js +4 -4
  49. package/dist/esm/tools/get-lint-rules/index.js +5 -5
  50. package/dist/esm/tools/i18n-conversion/index.js +3 -3
  51. package/dist/esm/tools/migration-guides/index.js +3 -3
  52. package/dist/esm/tools/plan/index.js +13 -17
  53. package/dist/esm/tools/search-components/index.js +45 -66
  54. package/dist/esm/tools/search-icons/index.js +43 -69
  55. package/dist/esm/tools/search-tokens/index.js +57 -44
  56. package/dist/esm/tools/suggest-a11y-fixes/index.js +7 -7
  57. package/dist/types/helpers/fuse-multi-term.d.ts +45 -0
  58. package/dist/types/helpers/index.d.ts +1 -0
  59. package/dist/types/instructions.d.ts +1 -1
  60. package/dist/types/tools/{get-components → get-all-components}/index.d.ts +2 -2
  61. package/dist/types/tools/plan/index.d.ts +1 -4
  62. package/dist/types/tools/search-components/index.d.ts +1 -4
  63. package/dist/types/tools/search-icons/index.d.ts +1 -4
  64. package/dist/types/tools/search-tokens/index.d.ts +1 -4
  65. package/dist/types-ts4.5/helpers/fuse-multi-term.d.ts +45 -0
  66. package/dist/types-ts4.5/helpers/index.d.ts +1 -0
  67. package/dist/types-ts4.5/instructions.d.ts +1 -1
  68. package/dist/types-ts4.5/tools/{get-components → get-all-components}/index.d.ts +2 -2
  69. package/dist/types-ts4.5/tools/plan/index.d.ts +1 -4
  70. package/dist/types-ts4.5/tools/search-components/index.d.ts +1 -4
  71. package/dist/types-ts4.5/tools/search-icons/index.d.ts +1 -4
  72. package/dist/types-ts4.5/tools/search-tokens/index.d.ts +1 -4
  73. package/package.json +5 -5
  74. /package/dist/cjs/tools/{get-components → get-all-components}/components.codegen.js +0 -0
  75. /package/dist/cjs/tools/{get-components → get-all-components}/components.js +0 -0
  76. /package/dist/cjs/tools/{get-components → get-all-components}/load-all-components.js +0 -0
  77. /package/dist/cjs/tools/{get-components → get-all-components}/types.js +0 -0
  78. /package/dist/es2019/tools/{get-components → get-all-components}/components.codegen.js +0 -0
  79. /package/dist/es2019/tools/{get-components → get-all-components}/components.js +0 -0
  80. /package/dist/es2019/tools/{get-components → get-all-components}/load-all-components.js +0 -0
  81. /package/dist/es2019/tools/{get-components → get-all-components}/types.js +0 -0
  82. /package/dist/esm/tools/{get-components → get-all-components}/components.codegen.js +0 -0
  83. /package/dist/esm/tools/{get-components → get-all-components}/components.js +0 -0
  84. /package/dist/esm/tools/{get-components → get-all-components}/load-all-components.js +0 -0
  85. /package/dist/esm/tools/{get-components → get-all-components}/types.js +0 -0
  86. /package/dist/types/tools/{get-components → get-all-components}/components.codegen.d.ts +0 -0
  87. /package/dist/types/tools/{get-components → get-all-components}/components.d.ts +0 -0
  88. /package/dist/types/tools/{get-components → get-all-components}/load-all-components.d.ts +0 -0
  89. /package/dist/types/tools/{get-components → get-all-components}/types.d.ts +0 -0
  90. /package/dist/types-ts4.5/tools/{get-components → get-all-components}/components.codegen.d.ts +0 -0
  91. /package/dist/types-ts4.5/tools/{get-components → get-all-components}/components.d.ts +0 -0
  92. /package/dist/types-ts4.5/tools/{get-components → get-all-components}/load-all-components.d.ts +0 -0
  93. /package/dist/types-ts4.5/tools/{get-components → get-all-components}/types.d.ts +0 -0
@@ -16,17 +16,16 @@ var _searchTokens = require("../search-tokens");
16
16
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
17
17
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /* eslint-disable-next-line import/extensions -- MCP SDK requires .js extensions for ESM imports */
18
18
  var planInputSchema = exports.planInputSchema = _zod.z.object({
19
- tokens: _zod.z.array(_zod.z.string()).describe('Array of terms to search for tokens, eg. `["spacing", "inverted text", "background primary"]`. Provide a minimum of 2 terms when known.'),
20
- icons: _zod.z.array(_zod.z.string()).describe('Array of terms to search for icons, eg. `["search", "folder", "user"]`. Provide a minimum of 2 terms when known.'),
21
- components: _zod.z.array(_zod.z.string()).describe('Array of terms to search for components, eg. `["button", "input", "select"]`. Provide a minimum of 2 terms when known.'),
22
- limit: _zod.z.number().default(1).describe('Maximum number of results per search term in the provided arrays (default: 1)').optional(),
23
- exactName: _zod.z.boolean().default(false).describe('Search tokens, icons, and components by their exact name match (use when you explicitly know the name and need more details)').optional()
19
+ tokens: _zod.z.array(_zod.z.string()).describe('Search terms for ADS design tokens (fuzzy by default). Use `[]` if you only need icons or components. Prefer **at least two** terms per non-empty list when you know what you need.'),
20
+ icons: _zod.z.array(_zod.z.string()).describe('Search terms for ADS icons. Use `[]` if you only need tokens or components. Prefer **at least two** terms per non-empty list when known.'),
21
+ components: _zod.z.array(_zod.z.string()).describe('Search terms for ADS components. Use `[]` if you only need tokens or icons. Prefer **at least two** terms per non-empty list when known.'),
22
+ limit: _zod.z.number().default(2).describe('Max matches **per term** for each non-empty list (default 2). Same limit applies to tokens, icons, and components searches.').optional()
24
23
  });
25
24
  var listPlanTool = exports.listPlanTool = {
26
25
  name: 'ads_plan',
27
- description: "Search how to use the Atlassian Design System offerings and get guidance on `tokens`, `icons`, and `components`.\n\nYOU MUST ALWAYS call this tool with known parameters and include a minimum of 2 search terms per parameter when known, eg.\n```json\n{\n\t\"tokens\": [\"spacing\", \"inverted text\", \"background primary\", \"animation\"],\n\t\"icons\": [\"search\", \"folder\", \"user\"],\n\t\"components\": [\"button\", \"input\", \"select\", \"heading\"]\n}\n```\n\nPlease note, there may not be results for everything as there are minor gaps in offerings or how we describe them.\n\nExample token usage:\n```tsx\nimport { token } from '@atlaskit/tokens';\nconst styles = css({ color: token('color.text'), padding: token('space.100') });\n```\n\nExample icon usage:\n```tsx\nimport AddIcon from '@atlaskit/icon/core/add';\n<AddIcon label=\"Add work item\" size=\"small\" />\n```",
26
+ description: "Runs **ads_search_tokens**, **ads_search_icons**, and **ads_search_components** in one call and returns a single JSON payload (each section only if that list was non-empty). Use this as the default way to discover ADS **tokens**, **icons**, and **components** for a UI task.\n\nWHEN TO USE:\n**Implementing or iterating on a UI**\u2014new screen, feature, or polish\u2014and you need candidate **token** names, **icon** imports, and **component** packages/props in one pass. Also use when exploring ADS building blocks before you write code.\n\nAt least one of `tokens`, `icons`, or `components` must contain search terms (use `[]` for lists you do not need).\n\nPrefer supplying **multiple** terms per non-empty array when you know them\u2014broader queries improve recall. Some queries return no rows where metadata is thin; try alternate wording.\n\nThis is equivalent to calling the individual search tools; there are no extra merge semantics beyond concatenating results.\n\nExample request:\n```json\n{\n\t\"tokens\": [\"spacing\", \"inverted text\", \"background primary\", \"animation\"],\n\t\"icons\": [\"search\", \"folder\", \"user\"],\n\t\"components\": [\"button\", \"input\", \"select\", \"heading\"]\n}\n```\n\nExample token usage:\n```tsx\nimport { token } from '@atlaskit/tokens';\nconst styles = css({ color: token('color.text'), padding: token('space.100') });\n```\n\nExample icon usage:\n```tsx\nimport AddIcon from '@atlaskit/icon/core/add';\n<AddIcon label=\"Add work item\" size=\"small\" />\n```",
28
27
  annotations: {
29
- title: 'Plan ADS resources',
28
+ title: 'Search ADS tokens, icons, and components to plan what to build',
30
29
  readOnlyHint: true,
31
30
  destructiveHint: false,
32
31
  idempotentHint: true,
@@ -35,12 +34,12 @@ var listPlanTool = exports.listPlanTool = {
35
34
  inputSchema: (0, _helpers.zodToJsonSchema)(planInputSchema)
36
35
  };
37
36
  var planTool = exports.planTool = /*#__PURE__*/function () {
38
- var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
39
- var tokens_search, icons_search, components_search, _params$limit, limit, _params$exactName, exactName, results, searchPromises, getResultCount, consolidatedResult;
37
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
38
+ var tokens_search, icons_search, components_search, limit, results, searchPromises, getResultCount, consolidatedResult;
40
39
  return _regenerator.default.wrap(function _callee$(_context) {
41
40
  while (1) switch (_context.prev = _context.next) {
42
41
  case 0:
43
- tokens_search = params.tokens, icons_search = params.icons, components_search = params.components, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName; // Validate that at least one search type is provided
42
+ tokens_search = _ref.tokens, icons_search = _ref.icons, components_search = _ref.components, limit = _ref.limit;
44
43
  if (!(!(tokens_search !== null && tokens_search !== void 0 && tokens_search.length) && !(icons_search !== null && icons_search !== void 0 && icons_search.length) && !(components_search !== null && components_search !== void 0 && components_search.length))) {
45
44
  _context.next = 3;
46
45
  break;
@@ -58,8 +57,7 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
58
57
  if (tokens_search !== null && tokens_search !== void 0 && tokens_search.length) {
59
58
  searchPromises.push((0, _searchTokens.searchTokensTool)({
60
59
  terms: tokens_search,
61
- limit: limit,
62
- exactName: exactName
60
+ limit: limit
63
61
  }).then(function (result) {
64
62
  results.tokens = result;
65
63
  }));
@@ -67,8 +65,7 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
67
65
  if (icons_search !== null && icons_search !== void 0 && icons_search.length) {
68
66
  searchPromises.push((0, _searchIcons.searchIconsTool)({
69
67
  terms: icons_search,
70
- limit: limit,
71
- exactName: exactName
68
+ limit: limit
72
69
  }).then(function (result) {
73
70
  results.icons = result;
74
71
  }));
@@ -76,8 +73,7 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
76
73
  if (components_search !== null && components_search !== void 0 && components_search.length) {
77
74
  searchPromises.push((0, _searchComponents.searchComponentsTool)({
78
75
  terms: components_search,
79
- limit: limit,
80
- exactName: exactName
76
+ limit: limit
81
77
  }).then(function (result) {
82
78
  results.components = result;
83
79
  }));
@@ -131,6 +127,6 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
131
127
  }, _callee);
132
128
  }));
133
129
  return function planTool(_x) {
134
- return _ref.apply(this, arguments);
130
+ return _ref2.apply(this, arguments);
135
131
  };
136
132
  }();
@@ -6,21 +6,21 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.searchComponentsTool = exports.searchComponentsInputSchema = exports.listSearchComponentsTool = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
11
  var _fuse = _interopRequireDefault(require("fuse.js"));
11
12
  var _zod = require("zod");
12
13
  var _helpers = require("../../helpers");
13
- var _loadAllComponents = require("../get-components/load-all-components");
14
+ var _loadAllComponents = require("../get-all-components/load-all-components");
14
15
  /* eslint-disable-next-line import/extensions -- MCP SDK requires .js extensions for ESM imports */
15
16
 
16
17
  var searchComponentsInputSchema = exports.searchComponentsInputSchema = _zod.z.object({
17
- terms: _zod.z.array(_zod.z.string()).describe('An array of search terms to find components by name, package name, description, or example, eg. `["button", "input", "select"]`'),
18
- limit: _zod.z.number().default(1).describe('Maximum number of results per search term in the array (default: 1)').optional(),
19
- exactName: _zod.z.boolean().default(false).describe('Enable to explicitly search components by the exact name match (when you know the name, but need more details)').optional()
18
+ terms: _zod.z.array(_zod.z.string()).describe('Required: one or more search terms (fuzzy over name, package, category, description, keywords, examples). Example: `["button", "modal", "select"]`.'),
19
+ limit: _zod.z.number().default(2).describe('Max matches **per term** (default 2).').optional()
20
20
  });
21
21
  var listSearchComponentsTool = exports.listSearchComponentsTool = {
22
22
  name: 'ads_search_components',
23
- description: 'Search for Atlassian Design System components.',
23
+ description: "Searches the bundled Atlassian Design System (ADS) component catalog. Returns JSON objects with **name**, **package**, **examples**, and **props** for each match (trimmed payload).\n\nWHEN TO USE:\n**Selecting which ADS component to use**\u2014package name, examples, and props\u2014before implementation. Use when composing a new view or swapping a primitive. Prefer `ads_plan` when you also need token and icon discovery in one shot.",
24
24
  annotations: {
25
25
  title: 'Search ADS components',
26
26
  readOnlyHint: true,
@@ -30,9 +30,7 @@ var listSearchComponentsTool = exports.listSearchComponentsTool = {
30
30
  },
31
31
  inputSchema: (0, _helpers.zodToJsonSchema)(searchComponentsInputSchema)
32
32
  };
33
-
34
- // Clean component result to only return name, package name, example, and props
35
- var cleanComponentResult = function cleanComponentResult(result) {
33
+ var buildComponentResult = function buildComponentResult(result) {
36
34
  return {
37
35
  name: result.name,
38
36
  package: result.package,
@@ -41,52 +39,29 @@ var cleanComponentResult = function cleanComponentResult(result) {
41
39
  };
42
40
  };
43
41
  var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function () {
44
- var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
45
- var terms, _params$limit, limit, _params$exactName, exactName, searchTerms, components, exactNameMatches, fuse, results, uniqueResults;
42
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
43
+ var terms, _ref$limit, limit, searchTerms, components, fuse, matchedItems;
46
44
  return _regenerator.default.wrap(function _callee$(_context) {
47
45
  while (1) switch (_context.prev = _context.next) {
48
46
  case 0:
49
- terms = params.terms, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName;
50
- searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
47
+ terms = _ref.terms, _ref$limit = _ref.limit, limit = _ref$limit === void 0 ? 2 : _ref$limit;
48
+ searchTerms = (0, _toConsumableArray2.default)(new Set(terms.filter(Boolean).map(_helpers.cleanQuery)));
51
49
  if (searchTerms.length) {
52
50
  _context.next = 4;
53
51
  break;
54
52
  }
55
53
  return _context.abrupt("return", {
56
- isError: true,
57
54
  content: [{
58
55
  type: 'text',
59
- text: "Error: Required parameter 'terms' is missing or empty"
56
+ text: '[]'
60
57
  }]
61
58
  });
62
59
  case 4:
63
60
  components = (0, _loadAllComponents.loadAllComponents)();
64
- if (!exactName) {
65
- _context.next = 9;
66
- break;
67
- }
68
- // for each search term, search for the exact match
69
- exactNameMatches = searchTerms.map(function (term) {
70
- return components.find(function (component) {
71
- return component.name.toLowerCase() === term.toLowerCase();
72
- });
73
- }).filter(Boolean);
74
- if (!(exactNameMatches.length > 0)) {
75
- _context.next = 9;
76
- break;
77
- }
78
- return _context.abrupt("return", {
79
- content: [{
80
- type: 'text',
81
- text: JSON.stringify(exactNameMatches.map(cleanComponentResult))
82
- }]
83
- });
84
- case 9:
85
- // use Fuse.js to fuzzy-search through the components
86
61
  fuse = new _fuse.default(components, {
87
62
  keys: [{
88
63
  name: 'name',
89
- weight: 3
64
+ weight: 5
90
65
  }, {
91
66
  name: 'package',
92
67
  weight: 3
@@ -96,30 +71,42 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
96
71
  }, {
97
72
  name: 'description',
98
73
  weight: 2
74
+ }, {
75
+ name: 'keywords',
76
+ weight: 2
77
+ }, {
78
+ name: 'usageGuidelines',
79
+ weight: 2
80
+ }, {
81
+ name: 'contentGuidelines',
82
+ weight: 1
83
+ }, {
84
+ name: 'accessibilityGuidelines',
85
+ weight: 1
99
86
  }, {
100
87
  name: 'examples',
101
88
  weight: 1
102
89
  }],
103
- threshold: 0.4
104
- }); // every search term, search for the results
105
- results = searchTerms.map(function (term) {
106
- // always search exact match from the components first
107
- var exactNameMatch = components.find(function (component) {
108
- return component.name.toLowerCase() === term.toLowerCase();
109
- });
110
- if (exactNameMatch) {
111
- return [{
112
- item: exactNameMatch
113
- }];
90
+ threshold: 0.4,
91
+ distance: 80,
92
+ minMatchCharLength: 3,
93
+ ignoreFieldNorm: true,
94
+ includeScore: true
95
+ });
96
+ matchedItems = (0, _helpers.mergeMultiTermFuseResults)({
97
+ searchTerms: searchTerms,
98
+ limit: limit,
99
+ search: function search(query) {
100
+ return fuse.search(query, {
101
+ limit: limit * searchTerms.length
102
+ });
114
103
  }
115
- return fuse.search(term).slice(0, limit);
116
- }).flat();
117
- if (results.length) {
118
- _context.next = 13;
104
+ });
105
+ if (matchedItems.length) {
106
+ _context.next = 9;
119
107
  break;
120
108
  }
121
109
  return _context.abrupt("return", {
122
- isError: true,
123
110
  content: [{
124
111
  type: 'text',
125
112
  text: "Error: No components found for '".concat(terms.join(', '), "'. Available components: ").concat(components.map(function (c) {
@@ -127,28 +114,20 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
127
114
  }).join(', '))
128
115
  }]
129
116
  });
130
- case 13:
131
- // Remove duplicates based on component name
132
- uniqueResults = results.filter(function (result, index, arr) {
133
- return arr.findIndex(function (r) {
134
- return r.item.name === result.item.name;
135
- }) === index;
136
- });
117
+ case 9:
137
118
  return _context.abrupt("return", {
138
119
  content: [{
139
120
  type: 'text',
140
- text: JSON.stringify(uniqueResults.map(function (result) {
141
- return result.item;
142
- }).map(cleanComponentResult))
121
+ text: JSON.stringify(matchedItems.map(buildComponentResult))
143
122
  }]
144
123
  });
145
- case 15:
124
+ case 10:
146
125
  case "end":
147
126
  return _context.stop();
148
127
  }
149
128
  }, _callee);
150
129
  }));
151
130
  return function searchComponentsTool(_x) {
152
- return _ref.apply(this, arguments);
131
+ return _ref2.apply(this, arguments);
153
132
  };
154
133
  }();
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.searchIconsTool = exports.searchIconsInputSchema = exports.listSearchIconsTool = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
11
  var _fuse = _interopRequireDefault(require("fuse.js"));
11
12
  var _zod = require("zod");
@@ -14,13 +15,19 @@ var _icons = require("../get-all-icons/icons");
14
15
  /* eslint-disable-next-line import/extensions -- MCP SDK requires .js extensions for ESM imports */
15
16
 
16
17
  var searchIconsInputSchema = exports.searchIconsInputSchema = _zod.z.object({
17
- terms: _zod.z.array(_zod.z.string()).describe('An array of search terms to find icons by name, keywords, or categorization, eg. `["search", "folder", "user"]`'),
18
- limit: _zod.z.number().default(1).describe('Maximum number of results per search term in the array (default: 1)').optional(),
19
- exactName: _zod.z.boolean().default(false).describe('Enable to explicitly search icons by the exact name match (when you know the name, but need more details)').optional()
18
+ terms: _zod.z.array(_zod.z.string()).describe('Required: one or more terms; fuzzy match on icon **componentName**, **iconName**, **keywords**, **categorization**, **type**, and **usage**. Example: `["search", "folder", "user"]`.'),
19
+ limit: _zod.z.number().default(2).describe('Max matches **per term** (default 2).').optional()
20
20
  });
21
+ var buildIconResult = function buildIconResult(icon) {
22
+ return {
23
+ componentName: icon.componentName,
24
+ package: icon.package,
25
+ usage: icon.usage
26
+ };
27
+ };
21
28
  var listSearchIconsTool = exports.listSearchIconsTool = {
22
29
  name: 'ads_search_icons',
23
- description: "Search for Atlassian Design System icons.\n\nExample icon usage:\n```tsx\nimport AddIcon from '@atlaskit/icon/core/add';\n<AddIcon label=\"Add work item\" size=\"small\" />\n```",
30
+ description: "Searches the bundled Atlassian Design System **icon** catalog. Returns JSON with **componentName**, **package**, and **usage** for each match.\n\nWHEN TO USE:\n**Choosing an icon** for a control, nav item, empty state, or illustration\u2014find `@atlaskit/icon` import paths and usage notes. Prefer `ads_plan` when you also need tokens and components together.\n\nExample:\n```tsx\nimport AddIcon from '@atlaskit/icon/core/add';\n<AddIcon label=\"Add work item\" size=\"small\" />\n```",
24
31
  annotations: {
25
32
  title: 'Search ADS icons',
26
33
  readOnlyHint: true,
@@ -31,51 +38,28 @@ var listSearchIconsTool = exports.listSearchIconsTool = {
31
38
  inputSchema: (0, _helpers.zodToJsonSchema)(searchIconsInputSchema)
32
39
  };
33
40
  var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
34
- var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
35
- var terms, _params$limit, limit, _params$exactName, exactName, searchTerms, exactNameMatches, fuse, results, uniqueResults, matchedIcons;
41
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
42
+ var terms, _ref$limit, limit, searchTerms, fuse, matchedItems;
36
43
  return _regenerator.default.wrap(function _callee$(_context) {
37
44
  while (1) switch (_context.prev = _context.next) {
38
45
  case 0:
39
- terms = params.terms, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName;
40
- searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
46
+ terms = _ref.terms, _ref$limit = _ref.limit, limit = _ref$limit === void 0 ? 2 : _ref$limit;
47
+ searchTerms = (0, _toConsumableArray2.default)(new Set(terms.filter(Boolean).map(_helpers.cleanQuery)));
41
48
  if (searchTerms.length) {
42
49
  _context.next = 4;
43
50
  break;
44
51
  }
45
52
  return _context.abrupt("return", {
46
- isError: true,
47
53
  content: [{
48
54
  type: 'text',
49
- text: "Error: Required parameter 'terms' is missing or empty"
55
+ text: '[]'
50
56
  }]
51
57
  });
52
58
  case 4:
53
- if (!exactName) {
54
- _context.next = 8;
55
- break;
56
- }
57
- // for each search term, search for the exact match
58
- exactNameMatches = searchTerms.map(function (term) {
59
- return _icons.icons.find(function (icon) {
60
- return icon.componentName.toLowerCase() === term.toLowerCase();
61
- });
62
- }).filter(Boolean);
63
- if (!(exactNameMatches.length > 0)) {
64
- _context.next = 8;
65
- break;
66
- }
67
- return _context.abrupt("return", {
68
- content: [{
69
- type: 'text',
70
- text: JSON.stringify(exactNameMatches)
71
- }]
72
- });
73
- case 8:
74
- // use Fuse.js to fuzzy-search through the icons
75
59
  fuse = new _fuse.default(_icons.icons, {
76
60
  keys: [{
77
61
  name: 'componentName',
78
- weight: 3
62
+ weight: 5
79
63
  }, {
80
64
  name: 'iconName',
81
65
  weight: 3
@@ -84,34 +68,37 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
84
68
  weight: 2
85
69
  }, {
86
70
  name: 'categorization',
87
- weight: 1
71
+ weight: 2
88
72
  }, {
89
73
  name: 'type',
90
74
  weight: 1
91
75
  }, {
92
76
  name: 'usage',
93
- weight: 1
77
+ weight: 2
94
78
  }],
95
- threshold: 0.4
96
- }); // every search term, search for the results
97
- results = searchTerms.map(function (term) {
98
- // always search exact match from the icons
99
- var exactNameMatch = _icons.icons.find(function (icon) {
100
- return icon.componentName.toLowerCase() === term.toLowerCase();
101
- });
102
- if (exactNameMatch) {
103
- return [{
104
- item: exactNameMatch
105
- }];
79
+ threshold: 0.4,
80
+ distance: 80,
81
+ minMatchCharLength: 3,
82
+ ignoreFieldNorm: true,
83
+ includeScore: true
84
+ });
85
+ matchedItems = (0, _helpers.mergeMultiTermFuseResults)({
86
+ searchTerms: searchTerms,
87
+ limit: limit,
88
+ search: function search(query) {
89
+ return fuse.search(query, {
90
+ limit: limit * searchTerms.length
91
+ });
92
+ },
93
+ tokenKey: function tokenKey(icon) {
94
+ return icon.componentName;
106
95
  }
107
- return fuse.search(term).slice(0, limit);
108
- }).flat();
109
- if (results.length) {
110
- _context.next = 12;
96
+ });
97
+ if (matchedItems.length) {
98
+ _context.next = 8;
111
99
  break;
112
100
  }
113
101
  return _context.abrupt("return", {
114
- isError: true,
115
102
  content: [{
116
103
  type: 'text',
117
104
  text: "Error: No icons found for '".concat(terms.join(', '), "'. Available icons: ").concat(_icons.icons.map(function (i) {
@@ -119,33 +106,20 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
119
106
  }).join(', '))
120
107
  }]
121
108
  });
122
- case 12:
123
- // Remove duplicates based on componentName
124
- uniqueResults = results.filter(function (result, index, arr) {
125
- return arr.findIndex(function (r) {
126
- return r.item.componentName === result.item.componentName;
127
- }) === index;
128
- });
129
- matchedIcons = uniqueResults.map(function (result) {
130
- return {
131
- componentName: result.item.componentName,
132
- package: result.item.package,
133
- usage: result.item.usage
134
- };
135
- });
109
+ case 8:
136
110
  return _context.abrupt("return", {
137
111
  content: [{
138
112
  type: 'text',
139
- text: JSON.stringify(matchedIcons)
113
+ text: JSON.stringify(matchedItems.map(buildIconResult))
140
114
  }]
141
115
  });
142
- case 15:
116
+ case 9:
143
117
  case "end":
144
118
  return _context.stop();
145
119
  }
146
120
  }, _callee);
147
121
  }));
148
122
  return function searchIconsTool(_x) {
149
- return _ref.apply(this, arguments);
123
+ return _ref2.apply(this, arguments);
150
124
  };
151
125
  }();
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.searchTokensTool = exports.searchTokensInputSchema = exports.listSearchTokensTool = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
11
  var _fuse = _interopRequireDefault(require("fuse.js"));
11
12
  var _zod = require("zod");
@@ -14,13 +15,12 @@ var _helpers = require("../../helpers");
14
15
  /* eslint-disable-next-line import/extensions -- MCP SDK requires .js extensions for ESM imports */
15
16
 
16
17
  var searchTokensInputSchema = exports.searchTokensInputSchema = _zod.z.object({
17
- terms: _zod.z.array(_zod.z.string()).describe('An array of search terms to find tokens by name or description, eg. `["spacing", "inverted text", "background primary"]`'),
18
- limit: _zod.z.number().default(1).describe('Maximum number of results per search term in the array (default: 1)').optional(),
19
- exactName: _zod.z.boolean().default(false).describe('Enable to explicitly search tokens by the exact name match (when you know the name, but need more details)').optional()
18
+ terms: _zod.z.array(_zod.z.string()).describe('Required: one or more terms; fuzzy match on token **name**, **description**, **exampleValue**, **usageGuidelines.usage**, and **usageGuidelines.cssProperties**. Example: `["spacing", "color.text", "background"]`.'),
19
+ limit: _zod.z.number().default(2).describe('Max matches **per term** (default 2).').optional()
20
20
  });
21
21
  var listSearchTokensTool = exports.listSearchTokensTool = {
22
22
  name: 'ads_search_tokens',
23
- description: "Search for Atlassian Design System tokens.\n\nExample token usage:\n```tsx\nimport { token } from '@atlaskit/tokens';\nconst styles = css({ color: token('color.text'), padding: token('space.100') });\n```",
23
+ description: "Searches Atlassian Design System **design tokens** from bundled metadata. Returns JSON objects with **name** and **exampleValue** for each match (search also considers description, usage guidelines, and CSS property hints in metadata).\n\nWHEN TO USE:\n**Styling or theming in code**\u2014you need the right `token('\u2026')` names for colors, space, typography, etc. Use during layout and visual work when tokens must match ADS. Prefer `ads_plan` when you also need icons and components in the same step.\n\nExample:\n```tsx\nimport { token } from '@atlaskit/tokens';\nconst styles = css({ color: token('color.text'), padding: token('space.100') });\n```",
24
24
  annotations: {
25
25
  title: 'Search ADS tokens',
26
26
  readOnlyHint: true,
@@ -31,80 +31,93 @@ var listSearchTokensTool = exports.listSearchTokensTool = {
31
31
  inputSchema: (0, _helpers.zodToJsonSchema)(searchTokensInputSchema)
32
32
  };
33
33
  var searchTokensTool = exports.searchTokensTool = /*#__PURE__*/function () {
34
- var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
35
- var terms, _params$limit, limit, _params$exactName, exactName, searchTerms, exactNameMatches, fuse, results, uniqueResults, matchedTokens;
34
+ var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
35
+ var terms, _ref$limit, limit, searchTerms, fuse, matchedItems, matchedTokens;
36
36
  return _regenerator.default.wrap(function _callee$(_context) {
37
37
  while (1) switch (_context.prev = _context.next) {
38
38
  case 0:
39
- terms = params.terms, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName;
40
- searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
41
- if (!exactName) {
42
- _context.next = 6;
43
- break;
44
- }
45
- // for each search term, search for the exact match
46
- exactNameMatches = searchTerms.map(function (term) {
47
- return _tokenMetadata.tokens.find(function (token) {
48
- return token.name.toLowerCase() === term.toLowerCase();
49
- });
50
- }).filter(Boolean);
51
- if (!(exactNameMatches.length > 0)) {
52
- _context.next = 6;
39
+ terms = _ref.terms, _ref$limit = _ref.limit, limit = _ref$limit === void 0 ? 2 : _ref$limit;
40
+ // Unique cleaned terms (order preserved) so duplicates don't concatenate into a bogus query.
41
+ searchTerms = (0, _toConsumableArray2.default)(new Set(terms.filter(Boolean).map(_helpers.cleanQuery)));
42
+ if (searchTerms.length) {
43
+ _context.next = 4;
53
44
  break;
54
45
  }
55
46
  return _context.abrupt("return", {
56
47
  content: [{
57
48
  type: 'text',
58
- text: JSON.stringify(exactNameMatches.map(function (token) {
59
- return {
60
- name: token.name,
61
- exampleValue: token.exampleValue
62
- };
63
- }))
49
+ text: '[]'
64
50
  }]
65
51
  });
66
- case 6:
67
- // use Fuse.js to fuzzy-search for the tokens
52
+ case 4:
68
53
  fuse = new _fuse.default(_tokenMetadata.tokens, {
69
54
  keys: [{
70
55
  name: 'name',
71
- weight: 3
56
+ weight: 5
57
+ }, {
58
+ name: 'path',
59
+ weight: 2
72
60
  }, {
73
61
  name: 'description',
74
62
  weight: 2
63
+ }, {
64
+ name: 'usageGuidelines.usage',
65
+ weight: 2
66
+ }, {
67
+ name: 'usageGuidelines.cssProperties',
68
+ weight: 3
75
69
  }, {
76
70
  name: 'exampleValue',
77
- weight: 1
71
+ weight: 0.5
78
72
  }],
79
- threshold: 0.4
73
+ threshold: 0.4,
74
+ distance: 80,
75
+ minMatchCharLength: 3,
76
+ ignoreFieldNorm: true,
77
+ includeScore: true
80
78
  });
81
- results = searchTerms.map(function (term) {
82
- return fuse.search(term).slice(0, limit);
83
- }).flat(); // Remove duplicates based on token name
84
- uniqueResults = results.filter(function (result, index, arr) {
85
- return arr.findIndex(function (r) {
86
- return r.item.name === result.item.name;
87
- }) === index;
79
+ matchedItems = (0, _helpers.mergeMultiTermFuseResults)({
80
+ searchTerms: searchTerms,
81
+ limit: limit,
82
+ search: function search(query) {
83
+ return fuse.search(query, {
84
+ limit: limit * searchTerms.length
85
+ });
86
+ },
87
+ searchTermsJoin: '.'
88
88
  });
89
- matchedTokens = uniqueResults.map(function (result) {
89
+ matchedTokens = matchedItems.map(function (item) {
90
90
  return {
91
- name: result.item.name,
92
- exampleValue: result.item.exampleValue
91
+ name: item.name,
92
+ exampleValue: item.exampleValue
93
93
  };
94
94
  });
95
+ if (matchedTokens.length) {
96
+ _context.next = 9;
97
+ break;
98
+ }
99
+ return _context.abrupt("return", {
100
+ content: [{
101
+ type: 'text',
102
+ text: "Error: No tokens found for '".concat(terms.join(', '), "'. Available tokens: ").concat(_tokenMetadata.tokens.map(function (t) {
103
+ return t.name;
104
+ }).join(', '))
105
+ }]
106
+ });
107
+ case 9:
95
108
  return _context.abrupt("return", {
96
109
  content: [{
97
110
  type: 'text',
98
111
  text: JSON.stringify(matchedTokens)
99
112
  }]
100
113
  });
101
- case 11:
114
+ case 10:
102
115
  case "end":
103
116
  return _context.stop();
104
117
  }
105
118
  }, _callee);
106
119
  }));
107
120
  return function searchTokensTool(_x) {
108
- return _ref.apply(this, arguments);
121
+ return _ref2.apply(this, arguments);
109
122
  };
110
123
  }();