@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 +19 -0
- package/dist/cjs/index.js +74 -53
- package/dist/cjs/structured-content/formatters/token.js +11 -0
- package/dist/cjs/structured-content/types.js +1 -0
- package/dist/cjs/tools/get-a11y-guidelines/guidelines.js +2 -2
- package/dist/cjs/tools/get-tokens/index.js +130 -0
- package/dist/es2019/index.js +79 -59
- package/dist/es2019/structured-content/formatters/token.js +10 -0
- package/dist/es2019/structured-content/types.js +0 -0
- package/dist/es2019/tools/get-a11y-guidelines/guidelines.js +4 -8
- package/dist/es2019/tools/get-tokens/index.js +113 -0
- package/dist/esm/index.js +73 -52
- package/dist/esm/structured-content/formatters/token.js +5 -0
- package/dist/esm/structured-content/types.js +0 -0
- package/dist/esm/tools/get-a11y-guidelines/guidelines.js +2 -2
- package/dist/esm/tools/get-tokens/index.js +123 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/structured-content/formatters/token.d.ts +2 -0
- package/dist/types/structured-content/types.d.ts +20 -0
- package/dist/types/tools/get-tokens/index.d.ts +33 -0
- package/dist/types-ts4.5/index.d.ts +3 -2
- package/dist/types-ts4.5/structured-content/formatters/token.d.ts +2 -0
- package/dist/types-ts4.5/structured-content/types.d.ts +20 -0
- package/dist/types-ts4.5/tools/get-tokens/index.d.ts +33 -0
- package/package.json +8 -2
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.
|
|
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
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
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 =
|
|
165
|
+
_context2.next = 24;
|
|
148
166
|
break;
|
|
149
167
|
}
|
|
150
|
-
_context2.prev =
|
|
168
|
+
_context2.prev = 6;
|
|
151
169
|
if (!toolConfig.inputSchema) {
|
|
152
|
-
_context2.next =
|
|
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 =
|
|
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
|
-
|
|
192
|
+
toolArguments = inputValidation.data;
|
|
193
|
+
case 13:
|
|
194
|
+
_context2.next = 15;
|
|
177
195
|
return toolConfig.handler(toolArguments);
|
|
178
|
-
case
|
|
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
|
|
193
|
-
_context2.prev =
|
|
194
|
-
_context2.t0 = _context2["catch"](
|
|
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
|
|
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
|
-
|
|
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
|
|
246
|
+
case 28:
|
|
228
247
|
case "end":
|
|
229
248
|
return _context2.stop();
|
|
230
249
|
}
|
|
231
|
-
}, _callee2, null, [[
|
|
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
|
|
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: "
|
|
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
|
+
}();
|
package/dist/es2019/index.js
CHANGED
|
@@ -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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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 =
|
|
158
|
+
_context2.next = 24;
|
|
141
159
|
break;
|
|
142
160
|
}
|
|
143
|
-
_context2.prev =
|
|
161
|
+
_context2.prev = 6;
|
|
144
162
|
if (!toolConfig.inputSchema) {
|
|
145
|
-
_context2.next =
|
|
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 =
|
|
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
|
-
|
|
185
|
+
toolArguments = inputValidation.data;
|
|
186
|
+
case 13:
|
|
187
|
+
_context2.next = 15;
|
|
170
188
|
return toolConfig.handler(toolArguments);
|
|
171
|
-
case
|
|
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
|
|
186
|
-
_context2.prev =
|
|
187
|
-
_context2.t0 = _context2["catch"](
|
|
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
|
|
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
|
-
|
|
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
|
|
239
|
+
case 28:
|
|
221
240
|
case "end":
|
|
222
241
|
return _context2.stop();
|
|
223
242
|
}
|
|
224
|
-
}, _callee2, null, [[
|
|
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
|
|
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
|
|
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: "
|
|
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
|
+
}();
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
|
3
|
+
export declare const getToolRegistry: () => Record<string, {
|
|
4
4
|
handler: (params: any) => Promise<any>;
|
|
5
|
-
inputSchema: z.
|
|
5
|
+
inputSchema: z.AnyZodObject | null;
|
|
6
6
|
tool: Tool;
|
|
7
7
|
}>;
|
|
8
|
+
export type { TokenSchema, ContentSchema } from './structured-content/types';
|
|
@@ -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
|
|
3
|
+
export declare const getToolRegistry: () => Record<string, {
|
|
4
4
|
handler: (params: any) => Promise<any>;
|
|
5
|
-
inputSchema: z.
|
|
5
|
+
inputSchema: z.AnyZodObject | null;
|
|
6
6
|
tool: Tool;
|
|
7
7
|
}>;
|
|
8
|
+
export type { TokenSchema, ContentSchema } from './structured-content/types';
|
|
@@ -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.
|
|
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/
|
|
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",
|