@atlaskit/ads-mcp 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +63 -2
- package/dist/cjs/helpers/analytics.js +98 -0
- package/dist/cjs/index.js +142 -28
- package/dist/cjs/tools/analyze-a11y/index.js +38 -38
- package/dist/cjs/tools/get-a11y-guidelines/index.js +4 -4
- package/dist/cjs/tools/get-all-icons/index.js +1 -1
- package/dist/cjs/tools/get-all-tokens/index.js +1 -1
- package/dist/cjs/tools/get-components/components.js +2 -2
- package/dist/cjs/tools/get-components/index.js +1 -1
- package/dist/cjs/tools/plan/index.js +6 -6
- package/dist/cjs/tools/search-components/index.js +9 -9
- package/dist/cjs/tools/search-icons/index.js +9 -9
- package/dist/cjs/tools/search-tokens/index.js +5 -5
- package/dist/cjs/tools/suggest-a11y-fixes/index.js +4 -4
- package/dist/es2019/helpers/analytics.js +88 -0
- package/dist/es2019/index.js +118 -15
- package/dist/es2019/tools/get-components/components.js +2 -2
- package/dist/esm/helpers/analytics.js +90 -0
- package/dist/esm/index.js +143 -29
- package/dist/esm/tools/analyze-a11y/index.js +38 -38
- package/dist/esm/tools/get-a11y-guidelines/index.js +4 -4
- package/dist/esm/tools/get-all-icons/index.js +1 -1
- package/dist/esm/tools/get-all-tokens/index.js +1 -1
- package/dist/esm/tools/get-components/components.js +2 -2
- package/dist/esm/tools/get-components/index.js +1 -1
- package/dist/esm/tools/plan/index.js +6 -6
- package/dist/esm/tools/search-components/index.js +9 -9
- package/dist/esm/tools/search-icons/index.js +9 -9
- package/dist/esm/tools/search-tokens/index.js +5 -5
- package/dist/esm/tools/suggest-a11y-fixes/index.js +4 -4
- package/dist/types/helpers/analytics.d.ts +28 -0
- package/dist/types/tools/get-components/components.d.ts +1 -1
- package/dist/types-ts4.5/helpers/analytics.d.ts +28 -0
- package/dist/types-ts4.5/tools/get-components/components.d.ts +1 -1
- package/package.json +4 -1
|
@@ -9,7 +9,7 @@ exports.components = void 0;
|
|
|
9
9
|
*
|
|
10
10
|
* Generates TypeScript components data for AI tooling from offerings.json files
|
|
11
11
|
*
|
|
12
|
-
* @codegen <<SignedSource::
|
|
12
|
+
* @codegen <<SignedSource::1792e8b918fbdc85769f6ddf38970c58>>
|
|
13
13
|
* @codegenCommand yarn workspace @af/ads-ai-tooling codegen:prototyping
|
|
14
14
|
*/
|
|
15
15
|
|
|
@@ -1695,7 +1695,7 @@ var components = exports.components = [{
|
|
|
1695
1695
|
type: '"off" | "on"'
|
|
1696
1696
|
}, {
|
|
1697
1697
|
name: 'children',
|
|
1698
|
-
description: 'The contents rendered inside of the form. This is a function where the props will be passed from the form. The function props you can access are `dirty`, `submitting` and `disabled`.\
|
|
1698
|
+
description: 'The contents rendered inside of the form. This is a function where the props will be passed from the form. The function props you can access are `dirty`, `submitting` and `disabled`.\nYou can read more about these props in [react-final form documentation](https://final-form.org/docs/final-form/types/FormState).\n\nIf you are only spreading `formProps` onto the HTML `<form>` element and not using any of the other props (like `submitting`, etc.), `children` can be plain JSX. All of the children will be wrapped within an HTML `<form>` element that includes all necessary props, including those provided on the form component.',
|
|
1699
1699
|
type: '(() => void) | React.ReactNode | ((args: FormChildrenArgs<FormValues>) => React.ReactNode)'
|
|
1700
1700
|
}, {
|
|
1701
1701
|
name: 'formProps',
|
|
@@ -24,7 +24,7 @@ var listGetComponentsTool = exports.listGetComponentsTool = {
|
|
|
24
24
|
};
|
|
25
25
|
var getComponentsTool = exports.getComponentsTool = /*#__PURE__*/function () {
|
|
26
26
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
|
|
27
|
-
return _regenerator.default.wrap(function
|
|
27
|
+
return _regenerator.default.wrap(function (_context) {
|
|
28
28
|
while (1) switch (_context.prev = _context.next) {
|
|
29
29
|
case 0:
|
|
30
30
|
return _context.abrupt("return", {
|
|
@@ -37,12 +37,12 @@ var listPlanTool = exports.listPlanTool = {
|
|
|
37
37
|
var planTool = exports.planTool = /*#__PURE__*/function () {
|
|
38
38
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
|
|
39
39
|
var tokens_search, icons_search, components_search, _params$limit, limit, _params$exactName, exactName, results, searchPromises, getResultCount, consolidatedResult;
|
|
40
|
-
return _regenerator.default.wrap(function
|
|
40
|
+
return _regenerator.default.wrap(function (_context) {
|
|
41
41
|
while (1) switch (_context.prev = _context.next) {
|
|
42
42
|
case 0:
|
|
43
43
|
tokens_search = params.tokens, icons_search = params.icons, components_search = params.components, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName; // Validate that at least one search type is provided
|
|
44
44
|
if (!(!(tokens_search !== null && tokens_search !== void 0 && tokens_search.length) && !(icons_search !== null && icons_search !== void 0 && icons_search.length) && !(components_search !== null && components_search !== void 0 && components_search.length))) {
|
|
45
|
-
_context.next =
|
|
45
|
+
_context.next = 1;
|
|
46
46
|
break;
|
|
47
47
|
}
|
|
48
48
|
return _context.abrupt("return", {
|
|
@@ -52,7 +52,7 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
|
|
|
52
52
|
text: 'Error: At least one search type (tokens_search, icons_search, or components_search) must be provided with search terms'
|
|
53
53
|
}]
|
|
54
54
|
});
|
|
55
|
-
case
|
|
55
|
+
case 1:
|
|
56
56
|
results = {}; // Execute searches in parallel
|
|
57
57
|
searchPromises = [];
|
|
58
58
|
if (tokens_search !== null && tokens_search !== void 0 && tokens_search.length) {
|
|
@@ -84,9 +84,9 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
// Wait for all searches to complete
|
|
87
|
-
_context.next =
|
|
87
|
+
_context.next = 2;
|
|
88
88
|
return Promise.all(searchPromises);
|
|
89
|
-
case
|
|
89
|
+
case 2:
|
|
90
90
|
// Helper function to safely count results
|
|
91
91
|
getResultCount = function getResultCount(result) {
|
|
92
92
|
var _result$content;
|
|
@@ -124,7 +124,7 @@ var planTool = exports.planTool = /*#__PURE__*/function () {
|
|
|
124
124
|
text: JSON.stringify(consolidatedResult, null, 2)
|
|
125
125
|
}]
|
|
126
126
|
});
|
|
127
|
-
case
|
|
127
|
+
case 3:
|
|
128
128
|
case "end":
|
|
129
129
|
return _context.stop();
|
|
130
130
|
}
|
|
@@ -42,13 +42,13 @@ var cleanComponentResult = function cleanComponentResult(result) {
|
|
|
42
42
|
var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function () {
|
|
43
43
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
|
|
44
44
|
var terms, _params$limit, limit, _params$exactName, exactName, searchTerms, exactNameMatches, fuse, results, uniqueResults;
|
|
45
|
-
return _regenerator.default.wrap(function
|
|
45
|
+
return _regenerator.default.wrap(function (_context) {
|
|
46
46
|
while (1) switch (_context.prev = _context.next) {
|
|
47
47
|
case 0:
|
|
48
48
|
terms = params.terms, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName;
|
|
49
49
|
searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
|
|
50
50
|
if (searchTerms.length) {
|
|
51
|
-
_context.next =
|
|
51
|
+
_context.next = 1;
|
|
52
52
|
break;
|
|
53
53
|
}
|
|
54
54
|
return _context.abrupt("return", {
|
|
@@ -58,9 +58,9 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
|
|
|
58
58
|
text: "Error: Required parameter 'terms' is missing or empty"
|
|
59
59
|
}]
|
|
60
60
|
});
|
|
61
|
-
case
|
|
61
|
+
case 1:
|
|
62
62
|
if (!exactName) {
|
|
63
|
-
_context.next =
|
|
63
|
+
_context.next = 2;
|
|
64
64
|
break;
|
|
65
65
|
}
|
|
66
66
|
// for each search term, search for the exact match
|
|
@@ -70,7 +70,7 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
|
|
|
70
70
|
});
|
|
71
71
|
}).filter(Boolean);
|
|
72
72
|
if (!(exactNameMatches.length > 0)) {
|
|
73
|
-
_context.next =
|
|
73
|
+
_context.next = 2;
|
|
74
74
|
break;
|
|
75
75
|
}
|
|
76
76
|
return _context.abrupt("return", {
|
|
@@ -79,7 +79,7 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
|
|
|
79
79
|
text: JSON.stringify(exactNameMatches.map(cleanComponentResult))
|
|
80
80
|
}]
|
|
81
81
|
});
|
|
82
|
-
case
|
|
82
|
+
case 2:
|
|
83
83
|
// use Fuse.js to fuzzy-search through the components
|
|
84
84
|
fuse = new _fuse.default(_components.components, {
|
|
85
85
|
keys: [{
|
|
@@ -113,7 +113,7 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
|
|
|
113
113
|
return fuse.search(term).slice(0, limit);
|
|
114
114
|
}).flat();
|
|
115
115
|
if (results.length) {
|
|
116
|
-
_context.next =
|
|
116
|
+
_context.next = 3;
|
|
117
117
|
break;
|
|
118
118
|
}
|
|
119
119
|
return _context.abrupt("return", {
|
|
@@ -125,7 +125,7 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
|
|
|
125
125
|
}).join(', '))
|
|
126
126
|
}]
|
|
127
127
|
});
|
|
128
|
-
case
|
|
128
|
+
case 3:
|
|
129
129
|
// Remove duplicates based on component name
|
|
130
130
|
uniqueResults = results.filter(function (result, index, arr) {
|
|
131
131
|
return arr.findIndex(function (r) {
|
|
@@ -140,7 +140,7 @@ var searchComponentsTool = exports.searchComponentsTool = /*#__PURE__*/function
|
|
|
140
140
|
}).map(cleanComponentResult))
|
|
141
141
|
}]
|
|
142
142
|
});
|
|
143
|
-
case
|
|
143
|
+
case 4:
|
|
144
144
|
case "end":
|
|
145
145
|
return _context.stop();
|
|
146
146
|
}
|
|
@@ -50,13 +50,13 @@ var listSearchIconsTool = exports.listSearchIconsTool = {
|
|
|
50
50
|
var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
51
51
|
var _ref3 = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
|
|
52
52
|
var terms, _params$limit, limit, _params$exactName, exactName, searchTerms, exactNameMatches, fuse, results, uniqueResults, matchedIcons;
|
|
53
|
-
return _regenerator.default.wrap(function
|
|
53
|
+
return _regenerator.default.wrap(function (_context) {
|
|
54
54
|
while (1) switch (_context.prev = _context.next) {
|
|
55
55
|
case 0:
|
|
56
56
|
terms = params.terms, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName;
|
|
57
57
|
searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
|
|
58
58
|
if (searchTerms.length) {
|
|
59
|
-
_context.next =
|
|
59
|
+
_context.next = 1;
|
|
60
60
|
break;
|
|
61
61
|
}
|
|
62
62
|
return _context.abrupt("return", {
|
|
@@ -66,9 +66,9 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
|
66
66
|
text: "Error: Required parameter 'terms' is missing or empty"
|
|
67
67
|
}]
|
|
68
68
|
});
|
|
69
|
-
case
|
|
69
|
+
case 1:
|
|
70
70
|
if (!exactName) {
|
|
71
|
-
_context.next =
|
|
71
|
+
_context.next = 2;
|
|
72
72
|
break;
|
|
73
73
|
}
|
|
74
74
|
// for each search term, search for the exact match
|
|
@@ -78,7 +78,7 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
|
78
78
|
});
|
|
79
79
|
}).filter(Boolean);
|
|
80
80
|
if (!(exactNameMatches.length > 0)) {
|
|
81
|
-
_context.next =
|
|
81
|
+
_context.next = 2;
|
|
82
82
|
break;
|
|
83
83
|
}
|
|
84
84
|
return _context.abrupt("return", {
|
|
@@ -87,7 +87,7 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
|
87
87
|
text: JSON.stringify(exactNameMatches)
|
|
88
88
|
}]
|
|
89
89
|
});
|
|
90
|
-
case
|
|
90
|
+
case 2:
|
|
91
91
|
// use Fuse.js to fuzzy-search through the icons
|
|
92
92
|
fuse = new _fuse.default(icons, {
|
|
93
93
|
keys: [{
|
|
@@ -124,7 +124,7 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
|
124
124
|
return fuse.search(term).slice(0, limit);
|
|
125
125
|
}).flat();
|
|
126
126
|
if (results.length) {
|
|
127
|
-
_context.next =
|
|
127
|
+
_context.next = 3;
|
|
128
128
|
break;
|
|
129
129
|
}
|
|
130
130
|
return _context.abrupt("return", {
|
|
@@ -136,7 +136,7 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
|
136
136
|
}).join(', '))
|
|
137
137
|
}]
|
|
138
138
|
});
|
|
139
|
-
case
|
|
139
|
+
case 3:
|
|
140
140
|
// Remove duplicates based on componentName
|
|
141
141
|
uniqueResults = results.filter(function (result, index, arr) {
|
|
142
142
|
return arr.findIndex(function (r) {
|
|
@@ -156,7 +156,7 @@ var searchIconsTool = exports.searchIconsTool = /*#__PURE__*/function () {
|
|
|
156
156
|
text: JSON.stringify(matchedIcons)
|
|
157
157
|
}]
|
|
158
158
|
});
|
|
159
|
-
case
|
|
159
|
+
case 4:
|
|
160
160
|
case "end":
|
|
161
161
|
return _context.stop();
|
|
162
162
|
}
|
|
@@ -32,13 +32,13 @@ var listSearchTokensTool = exports.listSearchTokensTool = {
|
|
|
32
32
|
var searchTokensTool = exports.searchTokensTool = /*#__PURE__*/function () {
|
|
33
33
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
|
|
34
34
|
var terms, _params$limit, limit, _params$exactName, exactName, searchTerms, exactNameMatches, fuse, results, uniqueResults, matchedTokens;
|
|
35
|
-
return _regenerator.default.wrap(function
|
|
35
|
+
return _regenerator.default.wrap(function (_context) {
|
|
36
36
|
while (1) switch (_context.prev = _context.next) {
|
|
37
37
|
case 0:
|
|
38
38
|
terms = params.terms, _params$limit = params.limit, limit = _params$limit === void 0 ? 1 : _params$limit, _params$exactName = params.exactName, exactName = _params$exactName === void 0 ? false : _params$exactName;
|
|
39
39
|
searchTerms = terms.filter(Boolean).map(_helpers.cleanQuery);
|
|
40
40
|
if (!exactName) {
|
|
41
|
-
_context.next =
|
|
41
|
+
_context.next = 1;
|
|
42
42
|
break;
|
|
43
43
|
}
|
|
44
44
|
// for each search term, search for the exact match
|
|
@@ -48,7 +48,7 @@ var searchTokensTool = exports.searchTokensTool = /*#__PURE__*/function () {
|
|
|
48
48
|
});
|
|
49
49
|
}).filter(Boolean);
|
|
50
50
|
if (!(exactNameMatches.length > 0)) {
|
|
51
|
-
_context.next =
|
|
51
|
+
_context.next = 1;
|
|
52
52
|
break;
|
|
53
53
|
}
|
|
54
54
|
return _context.abrupt("return", {
|
|
@@ -62,7 +62,7 @@ var searchTokensTool = exports.searchTokensTool = /*#__PURE__*/function () {
|
|
|
62
62
|
}))
|
|
63
63
|
}]
|
|
64
64
|
});
|
|
65
|
-
case
|
|
65
|
+
case 1:
|
|
66
66
|
// use Fuse.js to fuzzy-search for the tokens
|
|
67
67
|
fuse = new _fuse.default(_tokenMetadata.tokens, {
|
|
68
68
|
keys: [{
|
|
@@ -97,7 +97,7 @@ var searchTokensTool = exports.searchTokensTool = /*#__PURE__*/function () {
|
|
|
97
97
|
text: JSON.stringify(matchedTokens)
|
|
98
98
|
}]
|
|
99
99
|
});
|
|
100
|
-
case
|
|
100
|
+
case 2:
|
|
101
101
|
case "end":
|
|
102
102
|
return _context.stop();
|
|
103
103
|
}
|
|
@@ -118,13 +118,13 @@ function findBestMatchingFix(violation) {
|
|
|
118
118
|
var suggestA11yFixesTool = exports.suggestA11yFixesTool = /*#__PURE__*/function () {
|
|
119
119
|
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
|
|
120
120
|
var violation, component, context, match, _match, key, fix;
|
|
121
|
-
return _regenerator.default.wrap(function
|
|
121
|
+
return _regenerator.default.wrap(function (_context) {
|
|
122
122
|
while (1) switch (_context.prev = _context.next) {
|
|
123
123
|
case 0:
|
|
124
124
|
violation = params.violation, component = params.component, context = params.context; // Use improved matching logic
|
|
125
125
|
match = findBestMatchingFix(violation);
|
|
126
126
|
if (!match) {
|
|
127
|
-
_context.next =
|
|
127
|
+
_context.next = 1;
|
|
128
128
|
break;
|
|
129
129
|
}
|
|
130
130
|
_match = (0, _slicedToArray2.default)(match, 2), key = _match[0], fix = _match[1];
|
|
@@ -142,7 +142,7 @@ var suggestA11yFixesTool = exports.suggestA11yFixesTool = /*#__PURE__*/function
|
|
|
142
142
|
}), null, 2)
|
|
143
143
|
}]
|
|
144
144
|
});
|
|
145
|
-
case
|
|
145
|
+
case 1:
|
|
146
146
|
return _context.abrupt("return", {
|
|
147
147
|
content: [{
|
|
148
148
|
type: 'text',
|
|
@@ -173,7 +173,7 @@ var suggestA11yFixesTool = exports.suggestA11yFixesTool = /*#__PURE__*/function
|
|
|
173
173
|
}, null, 2)
|
|
174
174
|
}]
|
|
175
175
|
});
|
|
176
|
-
case
|
|
176
|
+
case 2:
|
|
177
177
|
case "end":
|
|
178
178
|
return _context.stop();
|
|
179
179
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { userInfo } from 'node:os';
|
|
3
|
+
// eslint-disable-next-line import/no-extraneous-dependencies -- this uses require because not all node versions this package supports use the same import assertions/attributes
|
|
4
|
+
const pkgJson = require('@atlaskit/ads-mcp/package.json');
|
|
5
|
+
const version = pkgJson.version || '0.0.0-unknown';
|
|
6
|
+
|
|
7
|
+
// Get staff ID using the same logic as @repo-feature-flags-statsig
|
|
8
|
+
export const staffId = process.env.STAFF_ID || process.env.USER || process.env.ATLAS_USER || userInfo().username;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* This is a user-passed value via environment to define what agent we may be running in.
|
|
12
|
+
* This could be anything, do not rely on it!
|
|
13
|
+
* @default `'unknown'`
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const agent = process.env.ADSMCP_AGENT || 'unknown';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The path to the MCP config file that is being used to run the MCP server
|
|
20
|
+
* e.g. 'mcp.json', 'jira/.cursor/mcp.json', 'platform/.vscode/mcp.json' or 'unknown'
|
|
21
|
+
* This could be anything, do not rely on it!
|
|
22
|
+
* @default `'unknown'`
|
|
23
|
+
*/
|
|
24
|
+
export const configPath = process.env.ADSMCP_CONFIG_PATH || 'unknown';
|
|
25
|
+
|
|
26
|
+
// Check if user has opted out of analytics
|
|
27
|
+
const isAnalyticsOptedOut = String(process.env.ADSMCP_ANALYTICS_OPT_OUT) === 'true' || String(process.env.ADSMCP_ANALYTICS_OPT_OUT) === '1';
|
|
28
|
+
|
|
29
|
+
// Initialize analytics client with error handling
|
|
30
|
+
// If analytics client fails to initialize or user has opted out, we continue without analytics
|
|
31
|
+
let analyticsClient = null;
|
|
32
|
+
if (!isAnalyticsOptedOut) {
|
|
33
|
+
try {
|
|
34
|
+
// Dynamic import to catch initialization errors
|
|
35
|
+
const {
|
|
36
|
+
analyticsClient: createAnalyticsClient
|
|
37
|
+
} = require('@atlassiansox/analytics-node-client');
|
|
38
|
+
analyticsClient = createAnalyticsClient({
|
|
39
|
+
env: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
|
|
40
|
+
product: 'atlaskit',
|
|
41
|
+
subproduct: 'ads-mcp',
|
|
42
|
+
flushInterval: 5000
|
|
43
|
+
});
|
|
44
|
+
} catch (error) {
|
|
45
|
+
// Analytics client not available or failed to initialize
|
|
46
|
+
// Log the error but continue without analytics
|
|
47
|
+
console.error('Could not initialize analytics client. This is normal as it is only intended to measure authenticated Atlassians');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Send an operational event to analytics
|
|
52
|
+
* Wraps the analytics client and handles errors gracefully
|
|
53
|
+
* If analytics client is not available, this function is a no-op
|
|
54
|
+
*/
|
|
55
|
+
export function sendOperationalEvent({
|
|
56
|
+
action,
|
|
57
|
+
actionSubject,
|
|
58
|
+
actionSubjectId = '',
|
|
59
|
+
attributes = {}
|
|
60
|
+
}) {
|
|
61
|
+
// If analytics client is not available, skip analytics
|
|
62
|
+
if (!analyticsClient) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
analyticsClient.sendOperationalEvent({
|
|
67
|
+
anonymousId: 'unknown',
|
|
68
|
+
operationalEvent: {
|
|
69
|
+
action,
|
|
70
|
+
actionSubject,
|
|
71
|
+
actionSubjectId,
|
|
72
|
+
source: '@atlaskit/ads-mcp',
|
|
73
|
+
tags: ['ads-mcp'],
|
|
74
|
+
attributes: {
|
|
75
|
+
version,
|
|
76
|
+
staffId,
|
|
77
|
+
agent,
|
|
78
|
+
configPath,
|
|
79
|
+
...attributes
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// Analytics errors should not prevent normal operation
|
|
85
|
+
// Silently fail to avoid disrupting the main functionality
|
|
86
|
+
console.error('Error sending operational event to analytics');
|
|
87
|
+
}
|
|
88
|
+
}
|
package/dist/es2019/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/* eslint-disable import/extensions */
|
|
1
|
+
/* eslint-disable no-console, import/extensions */
|
|
2
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { sendOperationalEvent } from './helpers/analytics';
|
|
5
6
|
import { instructions } from './instructions';
|
|
6
7
|
import { analyzeA11yTool, analyzeLocalhostA11yTool, listAnalyzeA11yTool, listAnalyzeLocalhostA11yTool } from './tools/analyze-a11y';
|
|
7
8
|
import { getA11yGuidelinesTool, listGetA11yGuidelinesTool } from './tools/get-a11y-guidelines';
|
|
@@ -22,19 +23,48 @@ const server = new Server({
|
|
|
22
23
|
}, {
|
|
23
24
|
instructions,
|
|
24
25
|
capabilities: {
|
|
26
|
+
// logging: {}, // NOTE: We do not have logging enabled as it's not implemented consistently in MCP specs
|
|
25
27
|
// Tools are defined in the handlers below.
|
|
26
28
|
tools: {}
|
|
27
29
|
}
|
|
28
30
|
});
|
|
29
|
-
|
|
31
|
+
const generateLogger = level => (...args) => {
|
|
32
|
+
// NOTE: We do not have logging enabled as it's not implemented consistently in MCP specs
|
|
33
|
+
// server.sendLoggingMessage({
|
|
34
|
+
// level,
|
|
35
|
+
// data: args,
|
|
36
|
+
// });
|
|
37
|
+
|
|
38
|
+
// Log to console if ADSMCP_DEBUG is set to true
|
|
39
|
+
// using console.error since the only one that works for logging is `stderr`
|
|
40
|
+
// using console.log / other console.fn that use `stdout` will cause an error
|
|
41
|
+
// ref: https://www.mcpevals.io/blog/debugging-mcp-servers-tips-and-best-practices
|
|
42
|
+
if (String(process.env.ADSMCP_DEBUG) === 'true') {
|
|
43
|
+
console.error(`[ads-mcp.custom-logging][${level}]`, ...args);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
|
|
47
|
+
const tools = [listAnalyzeA11yTool, listAnalyzeLocalhostA11yTool, listGetA11yGuidelinesTool, listGetAllIconsTool, listGetAllTokensTool, listGetComponentsTool, listPlanTool,
|
|
48
|
+
// NOTE: These are disabled as `ads_plan` should cover everything more performantly.
|
|
49
|
+
// When these are enabled, they result in token usage to describe them, even if never used.
|
|
50
|
+
// listSearchComponentsTool,
|
|
51
|
+
// listSearchIconsTool,
|
|
52
|
+
// listSearchTokensTool,
|
|
53
|
+
listSuggestA11yFixesTool];
|
|
54
|
+
|
|
55
|
+
// Track list tools request
|
|
56
|
+
sendOperationalEvent({
|
|
57
|
+
action: 'listed',
|
|
58
|
+
actionSubject: 'ads.mcp.listTools',
|
|
59
|
+
attributes: {
|
|
60
|
+
toolsCount: tools.length,
|
|
61
|
+
// Number of available tools
|
|
62
|
+
request,
|
|
63
|
+
extra
|
|
64
|
+
}
|
|
65
|
+
});
|
|
30
66
|
return {
|
|
31
|
-
tools
|
|
32
|
-
// NOTE: These are disabled as `ads_plan` should cover everything more performantly.
|
|
33
|
-
// When these are enabled, they result in token usage to describe them, even if never used.
|
|
34
|
-
// listSearchComponentsTool,
|
|
35
|
-
// listSearchIconsTool,
|
|
36
|
-
// listSearchTokensTool,
|
|
37
|
-
listSuggestA11yFixesTool]
|
|
67
|
+
tools
|
|
38
68
|
};
|
|
39
69
|
});
|
|
40
70
|
const callTools = {
|
|
@@ -54,17 +84,90 @@ const callTools = {
|
|
|
54
84
|
};
|
|
55
85
|
|
|
56
86
|
// Handle tool execution
|
|
57
|
-
server.setRequestHandler(CallToolRequestSchema, async request => {
|
|
58
|
-
const
|
|
87
|
+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
88
|
+
const toolName = request.params.name;
|
|
89
|
+
const tool = callTools[toolName];
|
|
90
|
+
const actionSubject = `ads.mcp.callTool`;
|
|
91
|
+
|
|
92
|
+
// Track call tool request
|
|
93
|
+
sendOperationalEvent({
|
|
94
|
+
action: 'called',
|
|
95
|
+
actionSubject,
|
|
96
|
+
actionSubjectId: toolName,
|
|
97
|
+
attributes: {
|
|
98
|
+
toolName,
|
|
99
|
+
request,
|
|
100
|
+
extra
|
|
101
|
+
}
|
|
102
|
+
});
|
|
59
103
|
if (tool) {
|
|
60
|
-
|
|
104
|
+
try {
|
|
105
|
+
const result = await tool(request.params.arguments);
|
|
106
|
+
// Track successful tool execution
|
|
107
|
+
sendOperationalEvent({
|
|
108
|
+
action: 'succeeded',
|
|
109
|
+
actionSubject,
|
|
110
|
+
actionSubjectId: toolName,
|
|
111
|
+
attributes: {
|
|
112
|
+
toolName,
|
|
113
|
+
request,
|
|
114
|
+
extra
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return result;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Track tool execution error
|
|
120
|
+
sendOperationalEvent({
|
|
121
|
+
action: 'failed',
|
|
122
|
+
actionSubject,
|
|
123
|
+
actionSubjectId: toolName,
|
|
124
|
+
attributes: {
|
|
125
|
+
toolName,
|
|
126
|
+
request,
|
|
127
|
+
extra,
|
|
128
|
+
errorMessage: error instanceof Error ? error.message : 'Unknown error'
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
/* Throwing an MCP error will cause the MCP server to return an error response to the client.
|
|
133
|
+
We don't use console.error here:
|
|
134
|
+
- when used alone, without the throw new McpError, it causes "Client error for command...", which will loop back to this catch
|
|
135
|
+
*/
|
|
136
|
+
throw new McpError(-32000, `Failed to execute '${toolName}' tool: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
137
|
+
}
|
|
61
138
|
}
|
|
62
|
-
|
|
139
|
+
|
|
140
|
+
// Track tool not found error
|
|
141
|
+
sendOperationalEvent({
|
|
142
|
+
action: 'notFound',
|
|
143
|
+
actionSubject,
|
|
144
|
+
actionSubjectId: toolName,
|
|
145
|
+
attributes: {
|
|
146
|
+
toolName,
|
|
147
|
+
request,
|
|
148
|
+
extra
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
console.error(`Tool '${request.params.name}' not found, only the following tools are available: ${Object.keys(callTools).join(', ')}`);
|
|
152
|
+
return;
|
|
63
153
|
});
|
|
64
154
|
async function runServer() {
|
|
155
|
+
/**
|
|
156
|
+
* We force all logging to go through the MCP server to avoid breaking the MCP.
|
|
157
|
+
*/
|
|
158
|
+
console.log = generateLogger('info');
|
|
159
|
+
console.debug = generateLogger('debug');
|
|
160
|
+
console.warn = generateLogger('warning');
|
|
161
|
+
|
|
162
|
+
// Track server initialization
|
|
163
|
+
sendOperationalEvent({
|
|
164
|
+
action: 'initialized',
|
|
165
|
+
actionSubject: 'ads.mcp.initialize'
|
|
166
|
+
});
|
|
65
167
|
const transport = new StdioServerTransport();
|
|
66
168
|
await server.connect(transport);
|
|
67
169
|
}
|
|
68
170
|
runServer().catch(error => {
|
|
69
|
-
|
|
171
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
172
|
+
console.error(`Invalid input to ads-mcp: ${errorMessage}`);
|
|
70
173
|
});
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Generates TypeScript components data for AI tooling from offerings.json files
|
|
5
5
|
*
|
|
6
|
-
* @codegen <<SignedSource::
|
|
6
|
+
* @codegen <<SignedSource::1792e8b918fbdc85769f6ddf38970c58>>
|
|
7
7
|
* @codegenCommand yarn workspace @af/ads-ai-tooling codegen:prototyping
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -1689,7 +1689,7 @@ export const components = [{
|
|
|
1689
1689
|
type: '"off" | "on"'
|
|
1690
1690
|
}, {
|
|
1691
1691
|
name: 'children',
|
|
1692
|
-
description: 'The contents rendered inside of the form. This is a function where the props will be passed from the form. The function props you can access are `dirty`, `submitting` and `disabled`.\
|
|
1692
|
+
description: 'The contents rendered inside of the form. This is a function where the props will be passed from the form. The function props you can access are `dirty`, `submitting` and `disabled`.\nYou can read more about these props in [react-final form documentation](https://final-form.org/docs/final-form/types/FormState).\n\nIf you are only spreading `formProps` onto the HTML `<form>` element and not using any of the other props (like `submitting`, etc.), `children` can be plain JSX. All of the children will be wrapped within an HTML `<form>` element that includes all necessary props, including those provided on the form component.',
|
|
1693
1693
|
type: '(() => void) | React.ReactNode | ((args: FormChildrenArgs<FormValues>) => React.ReactNode)'
|
|
1694
1694
|
}, {
|
|
1695
1695
|
name: 'formProps',
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
/* eslint-disable no-console */
|
|
5
|
+
import { userInfo } from 'node:os';
|
|
6
|
+
// eslint-disable-next-line import/no-extraneous-dependencies -- this uses require because not all node versions this package supports use the same import assertions/attributes
|
|
7
|
+
var pkgJson = require('@atlaskit/ads-mcp/package.json');
|
|
8
|
+
var version = pkgJson.version || '0.0.0-unknown';
|
|
9
|
+
|
|
10
|
+
// Get staff ID using the same logic as @repo-feature-flags-statsig
|
|
11
|
+
export var staffId = process.env.STAFF_ID || process.env.USER || process.env.ATLAS_USER || userInfo().username;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This is a user-passed value via environment to define what agent we may be running in.
|
|
15
|
+
* This could be anything, do not rely on it!
|
|
16
|
+
* @default `'unknown'`
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export var agent = process.env.ADSMCP_AGENT || 'unknown';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The path to the MCP config file that is being used to run the MCP server
|
|
23
|
+
* e.g. 'mcp.json', 'jira/.cursor/mcp.json', 'platform/.vscode/mcp.json' or 'unknown'
|
|
24
|
+
* This could be anything, do not rely on it!
|
|
25
|
+
* @default `'unknown'`
|
|
26
|
+
*/
|
|
27
|
+
export var configPath = process.env.ADSMCP_CONFIG_PATH || 'unknown';
|
|
28
|
+
|
|
29
|
+
// Check if user has opted out of analytics
|
|
30
|
+
var isAnalyticsOptedOut = String(process.env.ADSMCP_ANALYTICS_OPT_OUT) === 'true' || String(process.env.ADSMCP_ANALYTICS_OPT_OUT) === '1';
|
|
31
|
+
|
|
32
|
+
// Initialize analytics client with error handling
|
|
33
|
+
// If analytics client fails to initialize or user has opted out, we continue without analytics
|
|
34
|
+
var analyticsClient = null;
|
|
35
|
+
if (!isAnalyticsOptedOut) {
|
|
36
|
+
try {
|
|
37
|
+
// Dynamic import to catch initialization errors
|
|
38
|
+
var _require = require('@atlassiansox/analytics-node-client'),
|
|
39
|
+
createAnalyticsClient = _require.analyticsClient;
|
|
40
|
+
analyticsClient = createAnalyticsClient({
|
|
41
|
+
env: process.env.NODE_ENV === 'development' ? 'dev' : 'prod',
|
|
42
|
+
product: 'atlaskit',
|
|
43
|
+
subproduct: 'ads-mcp',
|
|
44
|
+
flushInterval: 5000
|
|
45
|
+
});
|
|
46
|
+
} catch (error) {
|
|
47
|
+
// Analytics client not available or failed to initialize
|
|
48
|
+
// Log the error but continue without analytics
|
|
49
|
+
console.error('Could not initialize analytics client. This is normal as it is only intended to measure authenticated Atlassians');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Send an operational event to analytics
|
|
54
|
+
* Wraps the analytics client and handles errors gracefully
|
|
55
|
+
* If analytics client is not available, this function is a no-op
|
|
56
|
+
*/
|
|
57
|
+
export function sendOperationalEvent(_ref) {
|
|
58
|
+
var action = _ref.action,
|
|
59
|
+
actionSubject = _ref.actionSubject,
|
|
60
|
+
_ref$actionSubjectId = _ref.actionSubjectId,
|
|
61
|
+
actionSubjectId = _ref$actionSubjectId === void 0 ? '' : _ref$actionSubjectId,
|
|
62
|
+
_ref$attributes = _ref.attributes,
|
|
63
|
+
attributes = _ref$attributes === void 0 ? {} : _ref$attributes;
|
|
64
|
+
// If analytics client is not available, skip analytics
|
|
65
|
+
if (!analyticsClient) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
analyticsClient.sendOperationalEvent({
|
|
70
|
+
anonymousId: 'unknown',
|
|
71
|
+
operationalEvent: {
|
|
72
|
+
action: action,
|
|
73
|
+
actionSubject: actionSubject,
|
|
74
|
+
actionSubjectId: actionSubjectId,
|
|
75
|
+
source: '@atlaskit/ads-mcp',
|
|
76
|
+
tags: ['ads-mcp'],
|
|
77
|
+
attributes: _objectSpread({
|
|
78
|
+
version: version,
|
|
79
|
+
staffId: staffId,
|
|
80
|
+
agent: agent,
|
|
81
|
+
configPath: configPath
|
|
82
|
+
}, attributes)
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
} catch (error) {
|
|
86
|
+
// Analytics errors should not prevent normal operation
|
|
87
|
+
// Silently fail to avoid disrupting the main functionality
|
|
88
|
+
console.error('Error sending operational event to analytics');
|
|
89
|
+
}
|
|
90
|
+
}
|