@atlaskit/ads-mcp 0.8.6 → 0.9.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/ads-mcp
2
2
 
3
+ ## 0.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`286a209277502`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/286a209277502) -
8
+ update inputSchema type in getToolRegistry
9
+
10
+ ## 0.9.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [`55344b6c664f0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/55344b6c664f0) -
15
+ Combine "Get All Tokens" and "Search Tokens" tools and convert their output format to Markdown.
16
+
17
+ ### Patch Changes
18
+
19
+ - [`ec7caf4e1130d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ec7caf4e1130d) -
20
+ Remove mentions of `@atlaskit/announcer` in accessibility guidance.
21
+
3
22
  ## 0.8.6
4
23
 
5
24
  ### Patch Changes
package/dist/cjs/index.js CHANGED
@@ -4,13 +4,14 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.toolRegistry = void 0;
7
+ exports.getToolRegistry = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
9
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11
11
  var _index = require("@modelcontextprotocol/sdk/server/index.js");
12
12
  var _stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
13
13
  var _types = require("@modelcontextprotocol/sdk/types.js");
14
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
15
  var _analytics = require("./helpers/analytics");
15
16
  var _validation = require("./helpers/validation");
16
17
  var _instructions = require("./instructions");
@@ -19,6 +20,7 @@ var _getA11yGuidelines = require("./tools/get-a11y-guidelines");
19
20
  var _getAllIcons = require("./tools/get-all-icons");
20
21
  var _getAllTokens = require("./tools/get-all-tokens");
21
22
  var _getComponents = require("./tools/get-components");
23
+ var _getTokens = require("./tools/get-tokens");
22
24
  var _plan = require("./tools/plan");
23
25
  var _suggestA11yFixes = require("./tools/suggest-a11y-fixes");
24
26
  /* eslint-disable no-console, import/extensions */
@@ -57,45 +59,60 @@ var generateLogger = function generateLogger(level) {
57
59
  }
58
60
  };
59
61
  };
60
- var toolRegistry = exports.toolRegistry = (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, _analyzeA11y.listAnalyzeA11yTool.name, {
61
- handler: _analyzeA11y.analyzeA11yTool,
62
- inputSchema: _analyzeA11y.analyzeA11yInputSchema,
63
- tool: _analyzeA11y.listAnalyzeA11yTool
64
- }), _analyzeA11y.listAnalyzeLocalhostA11yTool.name, {
65
- handler: _analyzeA11y.analyzeLocalhostA11yTool,
66
- inputSchema: _analyzeA11y.analyzeA11yLocalhostInputSchema,
67
- tool: _analyzeA11y.listAnalyzeLocalhostA11yTool
68
- }), _getA11yGuidelines.listGetA11yGuidelinesTool.name, {
69
- handler: _getA11yGuidelines.getA11yGuidelinesTool,
70
- inputSchema: _getA11yGuidelines.getA11yGuidelinesInputSchema,
71
- tool: _getA11yGuidelines.listGetA11yGuidelinesTool
72
- }), _getAllIcons.listGetAllIconsTool.name, {
73
- handler: _getAllIcons.getAllIconsTool,
74
- inputSchema: null,
75
- tool: _getAllIcons.listGetAllIconsTool
76
- }), _getAllTokens.listGetAllTokensTool.name, {
77
- handler: _getAllTokens.getAllTokensTool,
78
- inputSchema: null,
79
- tool: _getAllTokens.listGetAllTokensTool
80
- }), _getComponents.listGetComponentsTool.name, {
81
- handler: _getComponents.getComponentsTool,
82
- inputSchema: null,
83
- tool: _getComponents.listGetComponentsTool
84
- }), _plan.listPlanTool.name, {
85
- handler: _plan.planTool,
86
- inputSchema: _plan.planInputSchema,
87
- tool: _plan.listPlanTool
88
- }), _suggestA11yFixes.listSuggestA11yFixesTool.name, {
89
- handler: _suggestA11yFixes.suggestA11yFixesTool,
90
- inputSchema: _suggestA11yFixes.suggestA11yFixesInputSchema,
91
- tool: _suggestA11yFixes.listSuggestA11yFixesTool
92
- });
62
+ var getToolRegistry = exports.getToolRegistry = function getToolRegistry() {
63
+ var baseTools = (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, _analyzeA11y.listAnalyzeA11yTool.name, {
64
+ handler: _analyzeA11y.analyzeA11yTool,
65
+ inputSchema: _analyzeA11y.analyzeA11yInputSchema,
66
+ tool: _analyzeA11y.listAnalyzeA11yTool
67
+ }), _analyzeA11y.listAnalyzeLocalhostA11yTool.name, {
68
+ handler: _analyzeA11y.analyzeLocalhostA11yTool,
69
+ inputSchema: _analyzeA11y.analyzeA11yLocalhostInputSchema,
70
+ tool: _analyzeA11y.listAnalyzeLocalhostA11yTool
71
+ }), _getA11yGuidelines.listGetA11yGuidelinesTool.name, {
72
+ handler: _getA11yGuidelines.getA11yGuidelinesTool,
73
+ inputSchema: _getA11yGuidelines.getA11yGuidelinesInputSchema,
74
+ tool: _getA11yGuidelines.listGetA11yGuidelinesTool
75
+ }), _getAllIcons.listGetAllIconsTool.name, {
76
+ handler: _getAllIcons.getAllIconsTool,
77
+ inputSchema: null,
78
+ tool: _getAllIcons.listGetAllIconsTool
79
+ }), _getComponents.listGetComponentsTool.name, {
80
+ handler: _getComponents.getComponentsTool,
81
+ inputSchema: null,
82
+ tool: _getComponents.listGetComponentsTool
83
+ }), _plan.listPlanTool.name, {
84
+ handler: _plan.planTool,
85
+ inputSchema: _plan.planInputSchema,
86
+ tool: _plan.listPlanTool
87
+ }), _suggestA11yFixes.listSuggestA11yFixesTool.name, {
88
+ handler: _suggestA11yFixes.suggestA11yFixesTool,
89
+ inputSchema: _suggestA11yFixes.suggestA11yFixesInputSchema,
90
+ tool: _suggestA11yFixes.listSuggestA11yFixesTool
91
+ });
92
+
93
+ // Conditionally add token tools based on feature flag
94
+ if ((0, _platformFeatureFlags.fg)('design_system_mcp_structured_content')) {
95
+ baseTools[_getTokens.listGetTokensTool.name] = {
96
+ handler: _getTokens.getTokensTool,
97
+ inputSchema: _getTokens.getTokensInputSchema,
98
+ tool: _getTokens.listGetTokensTool
99
+ };
100
+ } else {
101
+ baseTools[_getAllTokens.listGetAllTokensTool.name] = {
102
+ handler: _getAllTokens.getAllTokensTool,
103
+ inputSchema: null,
104
+ tool: _getAllTokens.listGetAllTokensTool
105
+ };
106
+ }
107
+ return baseTools;
108
+ };
93
109
  server.setRequestHandler(_types.ListToolsRequestSchema, /*#__PURE__*/function () {
94
110
  var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(request, extra) {
95
- var tools;
111
+ var toolRegistry, tools;
96
112
  return _regenerator.default.wrap(function _callee$(_context) {
97
113
  while (1) switch (_context.prev = _context.next) {
98
114
  case 0:
115
+ toolRegistry = getToolRegistry();
99
116
  tools = Object.values(toolRegistry).map(function (toolConfig) {
100
117
  return toolConfig.tool;
101
118
  }); // Track list tools request
@@ -112,7 +129,7 @@ server.setRequestHandler(_types.ListToolsRequestSchema, /*#__PURE__*/function ()
112
129
  return _context.abrupt("return", {
113
130
  tools: tools
114
131
  });
115
- case 3:
132
+ case 4:
116
133
  case "end":
117
134
  return _context.stop();
118
135
  }
@@ -126,10 +143,11 @@ server.setRequestHandler(_types.ListToolsRequestSchema, /*#__PURE__*/function ()
126
143
  // Handle tool execution
127
144
  server.setRequestHandler(_types.CallToolRequestSchema, /*#__PURE__*/function () {
128
145
  var _ref2 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(request, extra) {
129
- var toolName, toolConfig, actionSubject, toolArguments, inputValidation, result;
146
+ var toolRegistry, toolName, toolConfig, actionSubject, toolArguments, inputValidation, result, toolRegistryForError;
130
147
  return _regenerator.default.wrap(function _callee2$(_context2) {
131
148
  while (1) switch (_context2.prev = _context2.next) {
132
149
  case 0:
150
+ toolRegistry = getToolRegistry();
133
151
  toolName = request.params.name;
134
152
  toolConfig = toolRegistry[toolName];
135
153
  actionSubject = "ads.mcp.callTool"; // Track call tool request
@@ -144,17 +162,17 @@ server.setRequestHandler(_types.CallToolRequestSchema, /*#__PURE__*/function ()
144
162
  }
145
163
  });
146
164
  if (!toolConfig) {
147
- _context2.next = 23;
165
+ _context2.next = 24;
148
166
  break;
149
167
  }
150
- _context2.prev = 5;
168
+ _context2.prev = 6;
151
169
  if (!toolConfig.inputSchema) {
152
- _context2.next = 12;
170
+ _context2.next = 13;
153
171
  break;
154
172
  }
155
173
  inputValidation = (0, _validation.validateToolArguments)(toolConfig.inputSchema, request.params.arguments);
156
174
  if (inputValidation.success) {
157
- _context2.next = 11;
175
+ _context2.next = 12;
158
176
  break;
159
177
  }
160
178
  (0, _analytics.sendOperationalEvent)({
@@ -170,12 +188,12 @@ server.setRequestHandler(_types.CallToolRequestSchema, /*#__PURE__*/function ()
170
188
  }
171
189
  });
172
190
  return _context2.abrupt("return", inputValidation.error);
173
- case 11:
174
- toolArguments = inputValidation.data;
175
191
  case 12:
176
- _context2.next = 14;
192
+ toolArguments = inputValidation.data;
193
+ case 13:
194
+ _context2.next = 15;
177
195
  return toolConfig.handler(toolArguments);
178
- case 14:
196
+ case 15:
179
197
  result = _context2.sent;
180
198
  // Track successful tool execution
181
199
  (0, _analytics.sendOperationalEvent)({
@@ -189,9 +207,9 @@ server.setRequestHandler(_types.CallToolRequestSchema, /*#__PURE__*/function ()
189
207
  }
190
208
  });
191
209
  return _context2.abrupt("return", result);
192
- case 19:
193
- _context2.prev = 19;
194
- _context2.t0 = _context2["catch"](5);
210
+ case 20:
211
+ _context2.prev = 20;
212
+ _context2.t0 = _context2["catch"](6);
195
213
  // Track tool execution error
196
214
  (0, _analytics.sendOperationalEvent)({
197
215
  action: 'failed',
@@ -210,7 +228,7 @@ server.setRequestHandler(_types.CallToolRequestSchema, /*#__PURE__*/function ()
210
228
  - when used alone, without the throw new McpError, it causes "Client error for command...", which will loop back to this catch
211
229
  */
212
230
  throw new _types.McpError(-32000, "Failed to execute '".concat(toolName, "' tool: ").concat(_context2.t0 instanceof Error ? _context2.t0.message : 'Unknown error'));
213
- case 23:
231
+ case 24:
214
232
  // Track tool not found error
215
233
  (0, _analytics.sendOperationalEvent)({
216
234
  action: 'notFound',
@@ -222,13 +240,14 @@ server.setRequestHandler(_types.CallToolRequestSchema, /*#__PURE__*/function ()
222
240
  extra: extra
223
241
  }
224
242
  });
225
- console.error("Tool '".concat(request.params.name, "' not found, only the following tools are available: ").concat(Object.keys(toolRegistry).join(', ')));
243
+ toolRegistryForError = getToolRegistry();
244
+ console.error("Tool '".concat(request.params.name, "' not found, only the following tools are available: ").concat(Object.keys(toolRegistryForError).join(', ')));
226
245
  return _context2.abrupt("return");
227
- case 26:
246
+ case 28:
228
247
  case "end":
229
248
  return _context2.stop();
230
249
  }
231
- }, _callee2, null, [[5, 19]]);
250
+ }, _callee2, null, [[6, 20]]);
232
251
  }));
233
252
  return function (_x3, _x4) {
234
253
  return _ref2.apply(this, arguments);
@@ -269,4 +288,6 @@ function _runServer() {
269
288
  runServer().catch(function (error) {
270
289
  var errorMessage = error instanceof Error ? error.message : 'Unknown error';
271
290
  console.error("Invalid input to ads-mcp: ".concat(errorMessage));
272
- });
291
+ });
292
+
293
+ // Export types for use by other packages
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.tokenToMarkdown = tokenToMarkdown;
7
+ function tokenToMarkdown(token) {
8
+ return "# ".concat(token.name, "\n\n").concat(token.description, "\n\nExample Value: `").concat(token.exampleValue, "`\n");
9
+ }
10
+
11
+ // When it comes time to generate HTML content, we can add `tokenToHtml` here.
@@ -0,0 +1 @@
1
+ "use strict";
@@ -87,10 +87,10 @@ var accessibilityGuidelines = exports.accessibilityGuidelines = {
87
87
  screenReaders: {
88
88
  title: 'Screen Reader Support',
89
89
  description: 'Guidelines for screen reader accessibility',
90
- guidelines: ['Use Announcer component for dynamic content', 'Provide skip links for keyboard users', 'Use proper heading hierarchy', 'Use semantic HTML elements', 'Provide live regions for status updates', 'Use VisuallyHidden for screen reader text'],
90
+ guidelines: ['Use aria-live regions for dynamic content announcements', 'Provide skip links for keyboard users', 'Use proper heading hierarchy', 'Use semantic HTML elements', 'Provide live regions for status updates', 'Use VisuallyHidden for screen reader text'],
91
91
  codeExamples: [{
92
92
  title: 'Screen Reader Announcements',
93
- code: "import { Announcer } from '@atlaskit/announcer';\n\n<Announcer\n message={`${count} items selected`}\n liveMode=\"polite\"\n shouldAnnounce={true}\n/>"
93
+ code: "<div aria-live=\"polite\" role=\"status\">\n {count} items selected\n</div>"
94
94
  }, {
95
95
  title: 'Skip Links',
96
96
  code: "<a href=\"#main-content\" className=\"skip-link\">\n Skip to main content\n</a>"
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.listGetTokensTool = exports.getTokensTool = exports.getTokensInputSchema = void 0;
8
+ var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
10
+ var _fuse = _interopRequireDefault(require("fuse.js"));
11
+ var _zod = require("zod");
12
+ var _zodToJsonSchema = require("zod-to-json-schema");
13
+ var _tokenMetadata = require("@atlaskit/tokens/token-metadata");
14
+ var _helpers = require("../../helpers");
15
+ var _token = require("../../structured-content/formatters/token");
16
+ // Transform Token[] from token-metadata to TokenSchema[] format
17
+ function transformTokensToSchemas() {
18
+ return _tokenMetadata.tokens.map(function (token) {
19
+ var _token$exampleValue;
20
+ return {
21
+ contentType: 'token',
22
+ name: token.name,
23
+ path: token.path,
24
+ description: token.description,
25
+ exampleValue: String((_token$exampleValue = token.exampleValue) !== null && _token$exampleValue !== void 0 ? _token$exampleValue : '')
26
+ };
27
+ });
28
+ }
29
+ var getTokensInputSchema = exports.getTokensInputSchema = _zod.z.object({
30
+ terms: _zod.z.array(_zod.z.string()).default([]).describe('An array of search terms to find tokens by name or description, eg. `["spacing", "inverted text", "background primary"]`. If empty or not provided, returns all tokens.').optional(),
31
+ limit: _zod.z.number().default(1).describe('Maximum number of results per search term in the array (default: 1)').optional(),
32
+ 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()
33
+ });
34
+ var listGetTokensTool = exports.listGetTokensTool = {
35
+ name: 'ads_get_tokens',
36
+ description: "Get Atlassian Design System tokens with optional search functionality.\n\n- If search parameters are provided, searches for tokens matching the criteria.\n- If no search parameters are provided, returns all tokens.\n\nExample token usage:\n```tsx\nimport { token } from '@atlaskit/tokens';\nconst styles = css({ color: token('color.text'), padding: token('space.100') });\n```",
37
+ annotations: {
38
+ title: 'Get ADS tokens',
39
+ readOnlyHint: true,
40
+ destructiveHint: false,
41
+ idempotentHint: true,
42
+ openWorldHint: true
43
+ },
44
+ inputSchema: (0, _zodToJsonSchema.zodToJsonSchema)(getTokensInputSchema)
45
+ };
46
+ var getTokensTool = exports.getTokensTool = /*#__PURE__*/function () {
47
+ var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
48
+ var _params$terms, terms, _params$limit, limit, _params$exactName, exactName, searchTerms, tokenDocs, allTokensMarkdown, exactNameMatches, _formattedTokens, fuse, results, uniqueResults, matchedTokens, formattedTokens;
49
+ return _regenerator.default.wrap(function _callee$(_context) {
50
+ while (1) switch (_context.prev = _context.next) {
51
+ case 0:
52
+ _params$terms = params.terms, terms = _params$terms === void 0 ? [] : _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;
53
+ searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
54
+ tokenDocs = transformTokensToSchemas(); // If no search terms provided, return all tokens formatted as Markdown
55
+ if (!(searchTerms.length === 0)) {
56
+ _context.next = 6;
57
+ break;
58
+ }
59
+ allTokensMarkdown = tokenDocs.map(_token.tokenToMarkdown).join('\n\n');
60
+ return _context.abrupt("return", {
61
+ content: [{
62
+ type: 'text',
63
+ text: allTokensMarkdown
64
+ }]
65
+ });
66
+ case 6:
67
+ if (!exactName) {
68
+ _context.next = 11;
69
+ break;
70
+ }
71
+ // for each search term, search for the exact match
72
+ exactNameMatches = searchTerms.map(function (term) {
73
+ return tokenDocs.find(function (token) {
74
+ return token.name.toLowerCase() === term.toLowerCase();
75
+ });
76
+ }).filter(Boolean);
77
+ if (!(exactNameMatches.length > 0)) {
78
+ _context.next = 11;
79
+ break;
80
+ }
81
+ _formattedTokens = exactNameMatches.map(_token.tokenToMarkdown).join('\n\n');
82
+ return _context.abrupt("return", {
83
+ content: [{
84
+ type: 'text',
85
+ text: _formattedTokens
86
+ }]
87
+ });
88
+ case 11:
89
+ // use Fuse.js to fuzzy-search for the tokens
90
+ fuse = new _fuse.default(tokenDocs, {
91
+ keys: [{
92
+ name: 'name',
93
+ weight: 3
94
+ }, {
95
+ name: 'description',
96
+ weight: 2
97
+ }, {
98
+ name: 'exampleValue',
99
+ weight: 1
100
+ }],
101
+ threshold: 0.4
102
+ });
103
+ results = searchTerms.map(function (term) {
104
+ return fuse.search(term).slice(0, limit);
105
+ }).flat(); // Remove duplicates based on token name
106
+ uniqueResults = results.filter(function (result, index, arr) {
107
+ return arr.findIndex(function (r) {
108
+ return r.item.name === result.item.name;
109
+ }) === index;
110
+ });
111
+ matchedTokens = uniqueResults.map(function (result) {
112
+ return result.item;
113
+ });
114
+ formattedTokens = matchedTokens.map(_token.tokenToMarkdown).join('\n\n');
115
+ return _context.abrupt("return", {
116
+ content: [{
117
+ type: 'text',
118
+ text: formattedTokens
119
+ }]
120
+ });
121
+ case 17:
122
+ case "end":
123
+ return _context.stop();
124
+ }
125
+ }, _callee);
126
+ }));
127
+ return function getTokensTool(_x) {
128
+ return _ref.apply(this, arguments);
129
+ };
130
+ }();
@@ -2,6 +2,7 @@
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
4
  import { CallToolRequestSchema, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js';
5
+ import { fg } from '@atlaskit/platform-feature-flags';
5
6
  import { sendOperationalEvent } from './helpers/analytics';
6
7
  import { validateToolArguments } from './helpers/validation';
7
8
  import { instructions } from './instructions';
@@ -10,6 +11,7 @@ import { getA11yGuidelinesInputSchema, getA11yGuidelinesTool, listGetA11yGuideli
10
11
  import { getAllIconsTool, listGetAllIconsTool } from './tools/get-all-icons';
11
12
  import { getAllTokensTool, listGetAllTokensTool } from './tools/get-all-tokens';
12
13
  import { getComponentsTool, listGetComponentsTool } from './tools/get-components';
14
+ import { getTokensInputSchema, getTokensTool, listGetTokensTool } from './tools/get-tokens';
13
15
  import { listPlanTool, planInputSchema, planTool } from './tools/plan';
14
16
  import { listSuggestA11yFixesTool, suggestA11yFixesInputSchema, suggestA11yFixesTool } from './tools/suggest-a11y-fixes';
15
17
 
@@ -41,66 +43,80 @@ const generateLogger = level => (...args) => {
41
43
  console.error(`[ads-mcp.custom-logging][${level}]`, ...args);
42
44
  }
43
45
  };
44
- export const toolRegistry = {
45
- [listAnalyzeA11yTool.name]: {
46
- handler: analyzeA11yTool,
47
- inputSchema: analyzeA11yInputSchema,
48
- tool: listAnalyzeA11yTool
49
- },
50
- [listAnalyzeLocalhostA11yTool.name]: {
51
- handler: analyzeLocalhostA11yTool,
52
- inputSchema: analyzeA11yLocalhostInputSchema,
53
- tool: listAnalyzeLocalhostA11yTool
54
- },
55
- [listGetA11yGuidelinesTool.name]: {
56
- handler: getA11yGuidelinesTool,
57
- inputSchema: getA11yGuidelinesInputSchema,
58
- tool: listGetA11yGuidelinesTool
59
- },
60
- [listGetAllIconsTool.name]: {
61
- handler: getAllIconsTool,
62
- inputSchema: null,
63
- tool: listGetAllIconsTool
64
- },
65
- [listGetAllTokensTool.name]: {
66
- handler: getAllTokensTool,
67
- inputSchema: null,
68
- tool: listGetAllTokensTool
69
- },
70
- [listGetComponentsTool.name]: {
71
- handler: getComponentsTool,
72
- inputSchema: null,
73
- tool: listGetComponentsTool
74
- },
75
- [listPlanTool.name]: {
76
- handler: planTool,
77
- inputSchema: planInputSchema,
78
- tool: listPlanTool
79
- },
80
- // NOTE: These should not actually be called as they're not in the `list_tools` endpoint.
81
- // But there might be a reason to keep them around for backwards-compatibility.
82
- // [listSearchComponentsTool.name]: {
83
- // handler: searchComponentsTool,
84
- // inputSchema: searchComponentsInputSchema,
85
- // tool: listSearchComponentsTool,
86
- // },
87
- // [listSearchIconsTool.name]: {
88
- // handler: searchIconsTool,
89
- // inputSchema: searchIconsInputSchema,
90
- // tool: listSearchIconsTool,
91
- // },
92
- // [listSearchTokensTool.name]: {
93
- // handler: searchTokensTool,
94
- // inputSchema: searchTokensInputSchema,
95
- // tool: listSearchTokensTool,
96
- // },
97
- [listSuggestA11yFixesTool.name]: {
98
- handler: suggestA11yFixesTool,
99
- inputSchema: suggestA11yFixesInputSchema,
100
- tool: listSuggestA11yFixesTool
46
+ export const getToolRegistry = () => {
47
+ const baseTools = {
48
+ [listAnalyzeA11yTool.name]: {
49
+ handler: analyzeA11yTool,
50
+ inputSchema: analyzeA11yInputSchema,
51
+ tool: listAnalyzeA11yTool
52
+ },
53
+ [listAnalyzeLocalhostA11yTool.name]: {
54
+ handler: analyzeLocalhostA11yTool,
55
+ inputSchema: analyzeA11yLocalhostInputSchema,
56
+ tool: listAnalyzeLocalhostA11yTool
57
+ },
58
+ [listGetA11yGuidelinesTool.name]: {
59
+ handler: getA11yGuidelinesTool,
60
+ inputSchema: getA11yGuidelinesInputSchema,
61
+ tool: listGetA11yGuidelinesTool
62
+ },
63
+ [listGetAllIconsTool.name]: {
64
+ handler: getAllIconsTool,
65
+ inputSchema: null,
66
+ tool: listGetAllIconsTool
67
+ },
68
+ [listGetComponentsTool.name]: {
69
+ handler: getComponentsTool,
70
+ inputSchema: null,
71
+ tool: listGetComponentsTool
72
+ },
73
+ [listPlanTool.name]: {
74
+ handler: planTool,
75
+ inputSchema: planInputSchema,
76
+ tool: listPlanTool
77
+ },
78
+ // NOTE: These should not actually be called as they're not in the `list_tools` endpoint.
79
+ // But there might be a reason to keep them around for backwards-compatibility.
80
+ // [listSearchComponentsTool.name]: {
81
+ // handler: searchComponentsTool,
82
+ // inputSchema: searchComponentsInputSchema,
83
+ // tool: listSearchComponentsTool,
84
+ // },
85
+ // [listSearchIconsTool.name]: {
86
+ // handler: searchIconsTool,
87
+ // inputSchema: searchIconsInputSchema,
88
+ // tool: listSearchIconsTool,
89
+ // },
90
+ // [listSearchTokensTool.name]: {
91
+ // handler: searchTokensTool,
92
+ // inputSchema: searchTokensInputSchema,
93
+ // tool: listSearchTokensTool,
94
+ // },
95
+ [listSuggestA11yFixesTool.name]: {
96
+ handler: suggestA11yFixesTool,
97
+ inputSchema: suggestA11yFixesInputSchema,
98
+ tool: listSuggestA11yFixesTool
99
+ }
100
+ };
101
+
102
+ // Conditionally add token tools based on feature flag
103
+ if (fg('design_system_mcp_structured_content')) {
104
+ baseTools[listGetTokensTool.name] = {
105
+ handler: getTokensTool,
106
+ inputSchema: getTokensInputSchema,
107
+ tool: listGetTokensTool
108
+ };
109
+ } else {
110
+ baseTools[listGetAllTokensTool.name] = {
111
+ handler: getAllTokensTool,
112
+ inputSchema: null,
113
+ tool: listGetAllTokensTool
114
+ };
101
115
  }
116
+ return baseTools;
102
117
  };
103
118
  server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
119
+ const toolRegistry = getToolRegistry();
104
120
  const tools = Object.values(toolRegistry).map(toolConfig => toolConfig.tool);
105
121
 
106
122
  // Track list tools request
@@ -121,6 +137,7 @@ server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
121
137
 
122
138
  // Handle tool execution
123
139
  server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
140
+ const toolRegistry = getToolRegistry();
124
141
  const toolName = request.params.name;
125
142
  const toolConfig = toolRegistry[toolName];
126
143
  const actionSubject = `ads.mcp.callTool`;
@@ -204,7 +221,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
204
221
  extra
205
222
  }
206
223
  });
207
- console.error(`Tool '${request.params.name}' not found, only the following tools are available: ${Object.keys(toolRegistry).join(', ')}`);
224
+ const toolRegistryForError = getToolRegistry();
225
+ console.error(`Tool '${request.params.name}' not found, only the following tools are available: ${Object.keys(toolRegistryForError).join(', ')}`);
208
226
  return;
209
227
  });
210
228
  async function runServer() {
@@ -226,4 +244,6 @@ async function runServer() {
226
244
  runServer().catch(error => {
227
245
  const errorMessage = error instanceof Error ? error.message : 'Unknown error';
228
246
  console.error(`Invalid input to ads-mcp: ${errorMessage}`);
229
- });
247
+ });
248
+
249
+ // Export types for use by other packages
@@ -0,0 +1,10 @@
1
+ export function tokenToMarkdown(token) {
2
+ return `# ${token.name}
3
+
4
+ ${token.description}
5
+
6
+ Example Value: \`${token.exampleValue}\`
7
+ `;
8
+ }
9
+
10
+ // When it comes time to generate HTML content, we can add `tokenToHtml` here.
File without changes
@@ -165,16 +165,12 @@ useEffect(() => {
165
165
  screenReaders: {
166
166
  title: 'Screen Reader Support',
167
167
  description: 'Guidelines for screen reader accessibility',
168
- guidelines: ['Use Announcer component for dynamic content', 'Provide skip links for keyboard users', 'Use proper heading hierarchy', 'Use semantic HTML elements', 'Provide live regions for status updates', 'Use VisuallyHidden for screen reader text'],
168
+ guidelines: ['Use aria-live regions for dynamic content announcements', 'Provide skip links for keyboard users', 'Use proper heading hierarchy', 'Use semantic HTML elements', 'Provide live regions for status updates', 'Use VisuallyHidden for screen reader text'],
169
169
  codeExamples: [{
170
170
  title: 'Screen Reader Announcements',
171
- code: `import { Announcer } from '@atlaskit/announcer';
172
-
173
- <Announcer
174
- message={\`\${count} items selected\`}
175
- liveMode="polite"
176
- shouldAnnounce={true}
177
- />`
171
+ code: `<div aria-live="polite" role="status">
172
+ {count} items selected
173
+ </div>`
178
174
  }, {
179
175
  title: 'Skip Links',
180
176
  code: `<a href="#main-content" className="skip-link">
@@ -0,0 +1,113 @@
1
+ import Fuse from 'fuse.js';
2
+ import { z } from 'zod';
3
+ import { zodToJsonSchema } from 'zod-to-json-schema';
4
+ import { tokens } from '@atlaskit/tokens/token-metadata';
5
+ import { cleanQuery } from '../../helpers';
6
+ import { tokenToMarkdown } from '../../structured-content/formatters/token';
7
+ // Transform Token[] from token-metadata to TokenSchema[] format
8
+ function transformTokensToSchemas() {
9
+ return tokens.map(token => {
10
+ var _token$exampleValue;
11
+ return {
12
+ contentType: 'token',
13
+ name: token.name,
14
+ path: token.path,
15
+ description: token.description,
16
+ exampleValue: String((_token$exampleValue = token.exampleValue) !== null && _token$exampleValue !== void 0 ? _token$exampleValue : '')
17
+ };
18
+ });
19
+ }
20
+ export const getTokensInputSchema = z.object({
21
+ terms: z.array(z.string()).default([]).describe('An array of search terms to find tokens by name or description, eg. `["spacing", "inverted text", "background primary"]`. If empty or not provided, returns all tokens.').optional(),
22
+ limit: z.number().default(1).describe('Maximum number of results per search term in the array (default: 1)').optional(),
23
+ exactName: 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()
24
+ });
25
+ export const listGetTokensTool = {
26
+ name: 'ads_get_tokens',
27
+ description: `Get Atlassian Design System tokens with optional search functionality.
28
+
29
+ - If search parameters are provided, searches for tokens matching the criteria.
30
+ - If no search parameters are provided, returns all tokens.
31
+
32
+ Example token usage:
33
+ \`\`\`tsx
34
+ import { token } from '@atlaskit/tokens';
35
+ const styles = css({ color: token('color.text'), padding: token('space.100') });
36
+ \`\`\``,
37
+ annotations: {
38
+ title: 'Get ADS tokens',
39
+ readOnlyHint: true,
40
+ destructiveHint: false,
41
+ idempotentHint: true,
42
+ openWorldHint: true
43
+ },
44
+ inputSchema: zodToJsonSchema(getTokensInputSchema)
45
+ };
46
+ export const getTokensTool = async params => {
47
+ const {
48
+ terms = [],
49
+ limit = 1,
50
+ exactName = false
51
+ } = params;
52
+ const searchTerms = terms.filter(Boolean).map(cleanQuery);
53
+ const tokenDocs = transformTokensToSchemas();
54
+
55
+ // If no search terms provided, return all tokens formatted as Markdown
56
+ if (searchTerms.length === 0) {
57
+ const allTokensMarkdown = tokenDocs.map(tokenToMarkdown).join('\n\n');
58
+ return {
59
+ content: [{
60
+ type: 'text',
61
+ text: allTokensMarkdown
62
+ }]
63
+ };
64
+ }
65
+
66
+ // Search logic (same as search-tokens)
67
+ if (exactName) {
68
+ // for each search term, search for the exact match
69
+ const exactNameMatches = searchTerms.map(term => {
70
+ return tokenDocs.find(token => token.name.toLowerCase() === term.toLowerCase());
71
+ }).filter(Boolean);
72
+ if (exactNameMatches.length > 0) {
73
+ const formattedTokens = exactNameMatches.map(tokenToMarkdown).join('\n\n');
74
+ return {
75
+ content: [{
76
+ type: 'text',
77
+ text: formattedTokens
78
+ }]
79
+ };
80
+ }
81
+ }
82
+
83
+ // use Fuse.js to fuzzy-search for the tokens
84
+ const fuse = new Fuse(tokenDocs, {
85
+ keys: [{
86
+ name: 'name',
87
+ weight: 3
88
+ }, {
89
+ name: 'description',
90
+ weight: 2
91
+ }, {
92
+ name: 'exampleValue',
93
+ weight: 1
94
+ }],
95
+ threshold: 0.4
96
+ });
97
+ const results = searchTerms.map(term => {
98
+ return fuse.search(term).slice(0, limit);
99
+ }).flat();
100
+
101
+ // Remove duplicates based on token name
102
+ const uniqueResults = results.filter((result, index, arr) => {
103
+ return arr.findIndex(r => r.item.name === result.item.name) === index;
104
+ });
105
+ const matchedTokens = uniqueResults.map(result => result.item);
106
+ const formattedTokens = matchedTokens.map(tokenToMarkdown).join('\n\n');
107
+ return {
108
+ content: [{
109
+ type: 'text',
110
+ text: formattedTokens
111
+ }]
112
+ };
113
+ };
package/dist/esm/index.js CHANGED
@@ -5,6 +5,7 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
5
5
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
7
  import { CallToolRequestSchema, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js';
8
+ import { fg } from '@atlaskit/platform-feature-flags';
8
9
  import { sendOperationalEvent } from './helpers/analytics';
9
10
  import { validateToolArguments } from './helpers/validation';
10
11
  import { instructions } from './instructions';
@@ -13,6 +14,7 @@ import { getA11yGuidelinesInputSchema, getA11yGuidelinesTool, listGetA11yGuideli
13
14
  import { getAllIconsTool, listGetAllIconsTool } from './tools/get-all-icons';
14
15
  import { getAllTokensTool, listGetAllTokensTool } from './tools/get-all-tokens';
15
16
  import { getComponentsTool, listGetComponentsTool } from './tools/get-components';
17
+ import { getTokensInputSchema, getTokensTool, listGetTokensTool } from './tools/get-tokens';
16
18
  import { listPlanTool, planInputSchema, planTool } from './tools/plan';
17
19
  import { listSuggestA11yFixesTool, suggestA11yFixesInputSchema, suggestA11yFixesTool } from './tools/suggest-a11y-fixes';
18
20
 
@@ -50,45 +52,60 @@ var generateLogger = function generateLogger(level) {
50
52
  }
51
53
  };
52
54
  };
53
- export var toolRegistry = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, listAnalyzeA11yTool.name, {
54
- handler: analyzeA11yTool,
55
- inputSchema: analyzeA11yInputSchema,
56
- tool: listAnalyzeA11yTool
57
- }), listAnalyzeLocalhostA11yTool.name, {
58
- handler: analyzeLocalhostA11yTool,
59
- inputSchema: analyzeA11yLocalhostInputSchema,
60
- tool: listAnalyzeLocalhostA11yTool
61
- }), listGetA11yGuidelinesTool.name, {
62
- handler: getA11yGuidelinesTool,
63
- inputSchema: getA11yGuidelinesInputSchema,
64
- tool: listGetA11yGuidelinesTool
65
- }), listGetAllIconsTool.name, {
66
- handler: getAllIconsTool,
67
- inputSchema: null,
68
- tool: listGetAllIconsTool
69
- }), listGetAllTokensTool.name, {
70
- handler: getAllTokensTool,
71
- inputSchema: null,
72
- tool: listGetAllTokensTool
73
- }), listGetComponentsTool.name, {
74
- handler: getComponentsTool,
75
- inputSchema: null,
76
- tool: listGetComponentsTool
77
- }), listPlanTool.name, {
78
- handler: planTool,
79
- inputSchema: planInputSchema,
80
- tool: listPlanTool
81
- }), listSuggestA11yFixesTool.name, {
82
- handler: suggestA11yFixesTool,
83
- inputSchema: suggestA11yFixesInputSchema,
84
- tool: listSuggestA11yFixesTool
85
- });
55
+ export var getToolRegistry = function getToolRegistry() {
56
+ var baseTools = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, listAnalyzeA11yTool.name, {
57
+ handler: analyzeA11yTool,
58
+ inputSchema: analyzeA11yInputSchema,
59
+ tool: listAnalyzeA11yTool
60
+ }), listAnalyzeLocalhostA11yTool.name, {
61
+ handler: analyzeLocalhostA11yTool,
62
+ inputSchema: analyzeA11yLocalhostInputSchema,
63
+ tool: listAnalyzeLocalhostA11yTool
64
+ }), listGetA11yGuidelinesTool.name, {
65
+ handler: getA11yGuidelinesTool,
66
+ inputSchema: getA11yGuidelinesInputSchema,
67
+ tool: listGetA11yGuidelinesTool
68
+ }), listGetAllIconsTool.name, {
69
+ handler: getAllIconsTool,
70
+ inputSchema: null,
71
+ tool: listGetAllIconsTool
72
+ }), listGetComponentsTool.name, {
73
+ handler: getComponentsTool,
74
+ inputSchema: null,
75
+ tool: listGetComponentsTool
76
+ }), listPlanTool.name, {
77
+ handler: planTool,
78
+ inputSchema: planInputSchema,
79
+ tool: listPlanTool
80
+ }), listSuggestA11yFixesTool.name, {
81
+ handler: suggestA11yFixesTool,
82
+ inputSchema: suggestA11yFixesInputSchema,
83
+ tool: listSuggestA11yFixesTool
84
+ });
85
+
86
+ // Conditionally add token tools based on feature flag
87
+ if (fg('design_system_mcp_structured_content')) {
88
+ baseTools[listGetTokensTool.name] = {
89
+ handler: getTokensTool,
90
+ inputSchema: getTokensInputSchema,
91
+ tool: listGetTokensTool
92
+ };
93
+ } else {
94
+ baseTools[listGetAllTokensTool.name] = {
95
+ handler: getAllTokensTool,
96
+ inputSchema: null,
97
+ tool: listGetAllTokensTool
98
+ };
99
+ }
100
+ return baseTools;
101
+ };
86
102
  server.setRequestHandler(ListToolsRequestSchema, /*#__PURE__*/function () {
87
103
  var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(request, extra) {
88
- var tools;
104
+ var toolRegistry, tools;
89
105
  return _regeneratorRuntime.wrap(function _callee$(_context) {
90
106
  while (1) switch (_context.prev = _context.next) {
91
107
  case 0:
108
+ toolRegistry = getToolRegistry();
92
109
  tools = Object.values(toolRegistry).map(function (toolConfig) {
93
110
  return toolConfig.tool;
94
111
  }); // Track list tools request
@@ -105,7 +122,7 @@ server.setRequestHandler(ListToolsRequestSchema, /*#__PURE__*/function () {
105
122
  return _context.abrupt("return", {
106
123
  tools: tools
107
124
  });
108
- case 3:
125
+ case 4:
109
126
  case "end":
110
127
  return _context.stop();
111
128
  }
@@ -119,10 +136,11 @@ server.setRequestHandler(ListToolsRequestSchema, /*#__PURE__*/function () {
119
136
  // Handle tool execution
120
137
  server.setRequestHandler(CallToolRequestSchema, /*#__PURE__*/function () {
121
138
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(request, extra) {
122
- var toolName, toolConfig, actionSubject, toolArguments, inputValidation, result;
139
+ var toolRegistry, toolName, toolConfig, actionSubject, toolArguments, inputValidation, result, toolRegistryForError;
123
140
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
124
141
  while (1) switch (_context2.prev = _context2.next) {
125
142
  case 0:
143
+ toolRegistry = getToolRegistry();
126
144
  toolName = request.params.name;
127
145
  toolConfig = toolRegistry[toolName];
128
146
  actionSubject = "ads.mcp.callTool"; // Track call tool request
@@ -137,17 +155,17 @@ server.setRequestHandler(CallToolRequestSchema, /*#__PURE__*/function () {
137
155
  }
138
156
  });
139
157
  if (!toolConfig) {
140
- _context2.next = 23;
158
+ _context2.next = 24;
141
159
  break;
142
160
  }
143
- _context2.prev = 5;
161
+ _context2.prev = 6;
144
162
  if (!toolConfig.inputSchema) {
145
- _context2.next = 12;
163
+ _context2.next = 13;
146
164
  break;
147
165
  }
148
166
  inputValidation = validateToolArguments(toolConfig.inputSchema, request.params.arguments);
149
167
  if (inputValidation.success) {
150
- _context2.next = 11;
168
+ _context2.next = 12;
151
169
  break;
152
170
  }
153
171
  sendOperationalEvent({
@@ -163,12 +181,12 @@ server.setRequestHandler(CallToolRequestSchema, /*#__PURE__*/function () {
163
181
  }
164
182
  });
165
183
  return _context2.abrupt("return", inputValidation.error);
166
- case 11:
167
- toolArguments = inputValidation.data;
168
184
  case 12:
169
- _context2.next = 14;
185
+ toolArguments = inputValidation.data;
186
+ case 13:
187
+ _context2.next = 15;
170
188
  return toolConfig.handler(toolArguments);
171
- case 14:
189
+ case 15:
172
190
  result = _context2.sent;
173
191
  // Track successful tool execution
174
192
  sendOperationalEvent({
@@ -182,9 +200,9 @@ server.setRequestHandler(CallToolRequestSchema, /*#__PURE__*/function () {
182
200
  }
183
201
  });
184
202
  return _context2.abrupt("return", result);
185
- case 19:
186
- _context2.prev = 19;
187
- _context2.t0 = _context2["catch"](5);
203
+ case 20:
204
+ _context2.prev = 20;
205
+ _context2.t0 = _context2["catch"](6);
188
206
  // Track tool execution error
189
207
  sendOperationalEvent({
190
208
  action: 'failed',
@@ -203,7 +221,7 @@ server.setRequestHandler(CallToolRequestSchema, /*#__PURE__*/function () {
203
221
  - when used alone, without the throw new McpError, it causes "Client error for command...", which will loop back to this catch
204
222
  */
205
223
  throw new McpError(-32000, "Failed to execute '".concat(toolName, "' tool: ").concat(_context2.t0 instanceof Error ? _context2.t0.message : 'Unknown error'));
206
- case 23:
224
+ case 24:
207
225
  // Track tool not found error
208
226
  sendOperationalEvent({
209
227
  action: 'notFound',
@@ -215,13 +233,14 @@ server.setRequestHandler(CallToolRequestSchema, /*#__PURE__*/function () {
215
233
  extra: extra
216
234
  }
217
235
  });
218
- console.error("Tool '".concat(request.params.name, "' not found, only the following tools are available: ").concat(Object.keys(toolRegistry).join(', ')));
236
+ toolRegistryForError = getToolRegistry();
237
+ console.error("Tool '".concat(request.params.name, "' not found, only the following tools are available: ").concat(Object.keys(toolRegistryForError).join(', ')));
219
238
  return _context2.abrupt("return");
220
- case 26:
239
+ case 28:
221
240
  case "end":
222
241
  return _context2.stop();
223
242
  }
224
- }, _callee2, null, [[5, 19]]);
243
+ }, _callee2, null, [[6, 20]]);
225
244
  }));
226
245
  return function (_x3, _x4) {
227
246
  return _ref2.apply(this, arguments);
@@ -262,4 +281,6 @@ function _runServer() {
262
281
  runServer().catch(function (error) {
263
282
  var errorMessage = error instanceof Error ? error.message : 'Unknown error';
264
283
  console.error("Invalid input to ads-mcp: ".concat(errorMessage));
265
- });
284
+ });
285
+
286
+ // Export types for use by other packages
@@ -0,0 +1,5 @@
1
+ export function tokenToMarkdown(token) {
2
+ return "# ".concat(token.name, "\n\n").concat(token.description, "\n\nExample Value: `").concat(token.exampleValue, "`\n");
3
+ }
4
+
5
+ // When it comes time to generate HTML content, we can add `tokenToHtml` here.
File without changes
@@ -81,10 +81,10 @@ export var accessibilityGuidelines = {
81
81
  screenReaders: {
82
82
  title: 'Screen Reader Support',
83
83
  description: 'Guidelines for screen reader accessibility',
84
- guidelines: ['Use Announcer component for dynamic content', 'Provide skip links for keyboard users', 'Use proper heading hierarchy', 'Use semantic HTML elements', 'Provide live regions for status updates', 'Use VisuallyHidden for screen reader text'],
84
+ guidelines: ['Use aria-live regions for dynamic content announcements', 'Provide skip links for keyboard users', 'Use proper heading hierarchy', 'Use semantic HTML elements', 'Provide live regions for status updates', 'Use VisuallyHidden for screen reader text'],
85
85
  codeExamples: [{
86
86
  title: 'Screen Reader Announcements',
87
- code: "import { Announcer } from '@atlaskit/announcer';\n\n<Announcer\n message={`${count} items selected`}\n liveMode=\"polite\"\n shouldAnnounce={true}\n/>"
87
+ code: "<div aria-live=\"polite\" role=\"status\">\n {count} items selected\n</div>"
88
88
  }, {
89
89
  title: 'Skip Links',
90
90
  code: "<a href=\"#main-content\" className=\"skip-link\">\n Skip to main content\n</a>"
@@ -0,0 +1,123 @@
1
+ import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
2
+ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
+ import Fuse from 'fuse.js';
4
+ import { z } from 'zod';
5
+ import { zodToJsonSchema } from 'zod-to-json-schema';
6
+ import { tokens } from '@atlaskit/tokens/token-metadata';
7
+ import { cleanQuery } from '../../helpers';
8
+ import { tokenToMarkdown } from '../../structured-content/formatters/token';
9
+ // Transform Token[] from token-metadata to TokenSchema[] format
10
+ function transformTokensToSchemas() {
11
+ return tokens.map(function (token) {
12
+ var _token$exampleValue;
13
+ return {
14
+ contentType: 'token',
15
+ name: token.name,
16
+ path: token.path,
17
+ description: token.description,
18
+ exampleValue: String((_token$exampleValue = token.exampleValue) !== null && _token$exampleValue !== void 0 ? _token$exampleValue : '')
19
+ };
20
+ });
21
+ }
22
+ export var getTokensInputSchema = z.object({
23
+ terms: z.array(z.string()).default([]).describe('An array of search terms to find tokens by name or description, eg. `["spacing", "inverted text", "background primary"]`. If empty or not provided, returns all tokens.').optional(),
24
+ limit: z.number().default(1).describe('Maximum number of results per search term in the array (default: 1)').optional(),
25
+ exactName: 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()
26
+ });
27
+ export var listGetTokensTool = {
28
+ name: 'ads_get_tokens',
29
+ description: "Get Atlassian Design System tokens with optional search functionality.\n\n- If search parameters are provided, searches for tokens matching the criteria.\n- If no search parameters are provided, returns all tokens.\n\nExample token usage:\n```tsx\nimport { token } from '@atlaskit/tokens';\nconst styles = css({ color: token('color.text'), padding: token('space.100') });\n```",
30
+ annotations: {
31
+ title: 'Get ADS tokens',
32
+ readOnlyHint: true,
33
+ destructiveHint: false,
34
+ idempotentHint: true,
35
+ openWorldHint: true
36
+ },
37
+ inputSchema: zodToJsonSchema(getTokensInputSchema)
38
+ };
39
+ export var getTokensTool = /*#__PURE__*/function () {
40
+ var _ref = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(params) {
41
+ var _params$terms, terms, _params$limit, limit, _params$exactName, exactName, searchTerms, tokenDocs, allTokensMarkdown, exactNameMatches, _formattedTokens, fuse, results, uniqueResults, matchedTokens, formattedTokens;
42
+ return _regeneratorRuntime.wrap(function _callee$(_context) {
43
+ while (1) switch (_context.prev = _context.next) {
44
+ case 0:
45
+ _params$terms = params.terms, terms = _params$terms === void 0 ? [] : _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;
46
+ searchTerms = terms.filter(Boolean).map(cleanQuery);
47
+ tokenDocs = transformTokensToSchemas(); // If no search terms provided, return all tokens formatted as Markdown
48
+ if (!(searchTerms.length === 0)) {
49
+ _context.next = 6;
50
+ break;
51
+ }
52
+ allTokensMarkdown = tokenDocs.map(tokenToMarkdown).join('\n\n');
53
+ return _context.abrupt("return", {
54
+ content: [{
55
+ type: 'text',
56
+ text: allTokensMarkdown
57
+ }]
58
+ });
59
+ case 6:
60
+ if (!exactName) {
61
+ _context.next = 11;
62
+ break;
63
+ }
64
+ // for each search term, search for the exact match
65
+ exactNameMatches = searchTerms.map(function (term) {
66
+ return tokenDocs.find(function (token) {
67
+ return token.name.toLowerCase() === term.toLowerCase();
68
+ });
69
+ }).filter(Boolean);
70
+ if (!(exactNameMatches.length > 0)) {
71
+ _context.next = 11;
72
+ break;
73
+ }
74
+ _formattedTokens = exactNameMatches.map(tokenToMarkdown).join('\n\n');
75
+ return _context.abrupt("return", {
76
+ content: [{
77
+ type: 'text',
78
+ text: _formattedTokens
79
+ }]
80
+ });
81
+ case 11:
82
+ // use Fuse.js to fuzzy-search for the tokens
83
+ fuse = new Fuse(tokenDocs, {
84
+ keys: [{
85
+ name: 'name',
86
+ weight: 3
87
+ }, {
88
+ name: 'description',
89
+ weight: 2
90
+ }, {
91
+ name: 'exampleValue',
92
+ weight: 1
93
+ }],
94
+ threshold: 0.4
95
+ });
96
+ results = searchTerms.map(function (term) {
97
+ return fuse.search(term).slice(0, limit);
98
+ }).flat(); // Remove duplicates based on token name
99
+ uniqueResults = results.filter(function (result, index, arr) {
100
+ return arr.findIndex(function (r) {
101
+ return r.item.name === result.item.name;
102
+ }) === index;
103
+ });
104
+ matchedTokens = uniqueResults.map(function (result) {
105
+ return result.item;
106
+ });
107
+ formattedTokens = matchedTokens.map(tokenToMarkdown).join('\n\n');
108
+ return _context.abrupt("return", {
109
+ content: [{
110
+ type: 'text',
111
+ text: formattedTokens
112
+ }]
113
+ });
114
+ case 17:
115
+ case "end":
116
+ return _context.stop();
117
+ }
118
+ }, _callee);
119
+ }));
120
+ return function getTokensTool(_x) {
121
+ return _ref.apply(this, arguments);
122
+ };
123
+ }();
@@ -1,7 +1,8 @@
1
1
  import { type Tool } from '@modelcontextprotocol/sdk/types.js';
2
2
  import type { z } from 'zod';
3
- export declare const toolRegistry: Record<string, {
3
+ export declare const getToolRegistry: () => Record<string, {
4
4
  handler: (params: any) => Promise<any>;
5
- inputSchema: z.ZodSchema | null;
5
+ inputSchema: z.AnyZodObject | null;
6
6
  tool: Tool;
7
7
  }>;
8
+ export type { TokenSchema, ContentSchema } from './structured-content/types';
@@ -0,0 +1,2 @@
1
+ import { type TokenSchema } from '../types';
2
+ export declare function tokenToMarkdown(token: TokenSchema): string;
@@ -0,0 +1,20 @@
1
+ export type TokenSchema = {
2
+ contentType: 'token';
3
+ /**
4
+ * The name of the token, e.g. `color.text`
5
+ */
6
+ name: string;
7
+ /**
8
+ * The constituent parts of the tokens' name, e.g. `['color', 'text', '[default]' ]
9
+ */
10
+ path: string[];
11
+ /**
12
+ * A brief explanation of where and how to use the token
13
+ */
14
+ description: string;
15
+ /**
16
+ * CSS value to use as an example of what this token might look like
17
+ */
18
+ exampleValue: string;
19
+ };
20
+ export type ContentSchema = TokenSchema;
@@ -0,0 +1,33 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types';
2
+ import { z } from 'zod';
3
+ export declare const getTokensInputSchema: z.ZodObject<{
4
+ terms: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
5
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
6
+ exactName: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ terms?: string[] | undefined;
9
+ limit?: number | undefined;
10
+ exactName?: boolean | undefined;
11
+ }, {
12
+ terms?: string[] | undefined;
13
+ limit?: number | undefined;
14
+ exactName?: boolean | undefined;
15
+ }>;
16
+ export declare const listGetTokensTool: {
17
+ name: string;
18
+ description: string;
19
+ annotations: {
20
+ title: string;
21
+ readOnlyHint: boolean;
22
+ destructiveHint: boolean;
23
+ idempotentHint: boolean;
24
+ openWorldHint: boolean;
25
+ };
26
+ inputSchema: import("zod-to-json-schema").JsonSchema7Type & {
27
+ $schema?: string | undefined;
28
+ definitions?: {
29
+ [key: string]: import("zod-to-json-schema").JsonSchema7Type;
30
+ } | undefined;
31
+ };
32
+ };
33
+ export declare const getTokensTool: (params: z.infer<typeof getTokensInputSchema>) => Promise<CallToolResult>;
@@ -1,7 +1,8 @@
1
1
  import { type Tool } from '@modelcontextprotocol/sdk/types.js';
2
2
  import type { z } from 'zod';
3
- export declare const toolRegistry: Record<string, {
3
+ export declare const getToolRegistry: () => Record<string, {
4
4
  handler: (params: any) => Promise<any>;
5
- inputSchema: z.ZodSchema | null;
5
+ inputSchema: z.AnyZodObject | null;
6
6
  tool: Tool;
7
7
  }>;
8
+ export type { TokenSchema, ContentSchema } from './structured-content/types';
@@ -0,0 +1,2 @@
1
+ import { type TokenSchema } from '../types';
2
+ export declare function tokenToMarkdown(token: TokenSchema): string;
@@ -0,0 +1,20 @@
1
+ export type TokenSchema = {
2
+ contentType: 'token';
3
+ /**
4
+ * The name of the token, e.g. `color.text`
5
+ */
6
+ name: string;
7
+ /**
8
+ * The constituent parts of the tokens' name, e.g. `['color', 'text', '[default]' ]
9
+ */
10
+ path: string[];
11
+ /**
12
+ * A brief explanation of where and how to use the token
13
+ */
14
+ description: string;
15
+ /**
16
+ * CSS value to use as an example of what this token might look like
17
+ */
18
+ exampleValue: string;
19
+ };
20
+ export type ContentSchema = TokenSchema;
@@ -0,0 +1,33 @@
1
+ import type { CallToolResult } from '@modelcontextprotocol/sdk/types';
2
+ import { z } from 'zod';
3
+ export declare const getTokensInputSchema: z.ZodObject<{
4
+ terms: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString, "many">>>;
5
+ limit: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
6
+ exactName: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ terms?: string[] | undefined;
9
+ limit?: number | undefined;
10
+ exactName?: boolean | undefined;
11
+ }, {
12
+ terms?: string[] | undefined;
13
+ limit?: number | undefined;
14
+ exactName?: boolean | undefined;
15
+ }>;
16
+ export declare const listGetTokensTool: {
17
+ name: string;
18
+ description: string;
19
+ annotations: {
20
+ title: string;
21
+ readOnlyHint: boolean;
22
+ destructiveHint: boolean;
23
+ idempotentHint: boolean;
24
+ openWorldHint: boolean;
25
+ };
26
+ inputSchema: import("zod-to-json-schema").JsonSchema7Type & {
27
+ $schema?: string | undefined;
28
+ definitions?: {
29
+ [key: string]: import("zod-to-json-schema").JsonSchema7Type;
30
+ } | undefined;
31
+ };
32
+ };
33
+ export declare const getTokensTool: (params: z.infer<typeof getTokensInputSchema>) => Promise<CallToolResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/ads-mcp",
3
- "version": "0.8.6",
3
+ "version": "0.9.1",
4
4
  "description": "The official Atlassian Design System MCP server to develop apps and user interfaces matching the Atlassian style.",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -24,9 +24,15 @@
24
24
  }
25
25
  },
26
26
  "atlaskit:src": "src/index.tsx",
27
+ "platform-feature-flags": {
28
+ "design_system_mcp_structured_content": {
29
+ "type": "boolean"
30
+ }
31
+ },
27
32
  "dependencies": {
28
33
  "@atlaskit/icon": "^29.0.0",
29
- "@atlaskit/tokens": "^8.2.0",
34
+ "@atlaskit/platform-feature-flags": "^1.1.0",
35
+ "@atlaskit/tokens": "^8.4.0",
30
36
  "@axe-core/playwright": "^4.8.0",
31
37
  "@axe-core/puppeteer": "^4.7.3",
32
38
  "@babel/runtime": "^7.0.0",