safari-devtools-mcp 1.7.0 → 1.8.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/README.md CHANGED
@@ -208,7 +208,7 @@ The server exposes guided debugging workflows as MCP prompts. Clients that suppo
208
208
  | `safari-specific-debugging` | Debug WebKit quirks — CSS prefixes, JS feature gaps, ITP/CORS issues |
209
209
  | `performance-debugging` | Performance analysis — Navigation Timing, Core Web Vitals, resource waterfall |
210
210
 
211
- ## Tools (41)
211
+ ## Tools (45)
212
212
 
213
213
  ### Debugging
214
214
 
@@ -286,6 +286,15 @@ The server exposes guided debugging workflows as MCP prompts. Clients that suppo
286
286
  | `press_key` | Press a key or combination (e.g., `Meta+A`, `Enter`) |
287
287
  | `upload_file` | Upload a file through a file input |
288
288
 
289
+ ### iOS Safari validation
290
+
291
+ | Tool | Description |
292
+ | ----------------------------- | -------------------------------------------------------------------------------------------------------------- |
293
+ | `inspect_viewport_meta` | Parse the viewport meta tag and validate against iOS best practices (width, zoom, viewport-fit) |
294
+ | `get_safe_area_insets` | Read CSS safe-area-inset values and check whether the page handles notched devices correctly |
295
+ | `check_ios_web_app_readiness` | Audit the page for Add to Home Screen / PWA readiness (apple-touch-icon, manifest, splash screens, status bar) |
296
+ | `check_webkit_compatibility` | Check page CSS against the live Safari session via CSS.supports() |
297
+
289
298
  ## Architecture
290
299
 
291
300
  ```
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAClE,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAqC/C,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,aAAkB,GAAG;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;CACtB,CA2CA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAC,SAAS,EAAC,MAAM,yCAAyC,CAAC;AAClE,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAyC/C,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,aAAkB,GAAG;IAClE,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;CACtB,CA2CA"}
@@ -22,6 +22,8 @@ import { tools as cookieTools } from './tools/cookies.js';
22
22
  import { tools as storageTools } from './tools/storage.js';
23
23
  import { tools as inputTools } from './tools/input.js';
24
24
  import { tools as emulationTools, deviceTools } from './tools/emulation.js';
25
+ import { tools as iosValidationTools } from './tools/ios-validation.js';
26
+ import { tools as webkitCompatTools } from './tools/webkit-compat.js';
25
27
  const allTools = [
26
28
  ...consoleTools,
27
29
  ...networkTools,
@@ -37,6 +39,8 @@ const allTools = [
37
39
  ...inputTools,
38
40
  ...emulationTools,
39
41
  ...deviceTools,
42
+ ...iosValidationTools,
43
+ ...webkitCompatTools,
40
44
  ];
41
45
  export function createSafariMcpServer(options = {}) {
42
46
  const { slim = false } = options;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * iOS Safari validation tools.
3
+ *
4
+ * Inspect viewport meta tags, safe-area insets, and PWA readiness —
5
+ * the three things every iOS Safari developer has to fight with.
6
+ */
7
+ export declare const tools: import("./types.js").ToolDef<import("zod").ZodRawShape>[];
8
+ //# sourceMappingURL=ios-validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-validation.d.ts","sourceRoot":"","sources":["../../../src/tools/ios-validation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAyEH,eAAO,MAAM,KAAK,2DA6QjB,CAAC"}
@@ -0,0 +1,282 @@
1
+ /**
2
+ * iOS Safari validation tools.
3
+ *
4
+ * Inspect viewport meta tags, safe-area insets, and PWA readiness —
5
+ * the three things every iOS Safari developer has to fight with.
6
+ */
7
+ import { defineTool } from './types.js';
8
+ function analyzeViewport(attrs) {
9
+ const issues = [];
10
+ if (Object.keys(attrs).length === 0) {
11
+ return [
12
+ {
13
+ severity: 'error',
14
+ message: 'No viewport meta tag found. iOS Safari will render at 980px width and scale down.',
15
+ },
16
+ ];
17
+ }
18
+ if (!attrs.width) {
19
+ issues.push({
20
+ severity: 'error',
21
+ message: 'Missing "width=device-width". Without this, Safari uses a default 980px layout width.',
22
+ });
23
+ }
24
+ else if (attrs.width !== 'device-width') {
25
+ issues.push({
26
+ severity: 'warning',
27
+ message: `width="${attrs.width}" — consider using "device-width" for responsive layouts.`,
28
+ });
29
+ }
30
+ if (!attrs['initial-scale']) {
31
+ issues.push({
32
+ severity: 'warning',
33
+ message: 'Missing "initial-scale=1". Some iOS Safari versions may not zoom correctly without it.',
34
+ });
35
+ }
36
+ if (attrs['user-scalable'] === 'no' || attrs['maximum-scale'] === '1') {
37
+ issues.push({
38
+ severity: 'error',
39
+ message: 'Zoom is disabled (user-scalable=no or maximum-scale=1). This is an accessibility violation (WCAG 1.4.4) and Safari 10+ ignores it anyway.',
40
+ });
41
+ }
42
+ if (attrs['viewport-fit'] !== 'cover') {
43
+ issues.push({
44
+ severity: 'warning',
45
+ message: 'Missing "viewport-fit=cover". Required for safe-area-inset-* CSS env() values to work on notched devices (iPhone X+).',
46
+ });
47
+ }
48
+ if (attrs['minimum-scale'] && parseFloat(attrs['minimum-scale']) < 1) {
49
+ issues.push({
50
+ severity: 'info',
51
+ message: `minimum-scale=${attrs['minimum-scale']}. Values below 1 allow zoom-out on iOS, which can cause layout issues.`,
52
+ });
53
+ }
54
+ return issues;
55
+ }
56
+ // ---- Tools ----
57
+ export const tools = [
58
+ defineTool({
59
+ name: 'inspect_viewport_meta',
60
+ description: 'Parse the viewport meta tag and validate it against iOS Safari ' +
61
+ 'best practices. Reports issues like missing width=device-width, ' +
62
+ 'disabled zoom (accessibility violation), missing viewport-fit=cover ' +
63
+ 'for safe areas, and other common misconfiguration.',
64
+ slimDescription: 'Validate iOS viewport meta tag.',
65
+ schema: {},
66
+ handler: async (_params, driver) => {
67
+ const result = await driver.runScript(`(() => {
68
+ const meta = document.querySelector('meta[name="viewport"]');
69
+ if (!meta) return { content: null, attrs: {} };
70
+ const content = meta.getAttribute('content') || '';
71
+ const attrs = {};
72
+ for (const part of content.split(',')) {
73
+ const [key, val] = part.split('=').map(s => s.trim());
74
+ if (key) attrs[key] = val || '';
75
+ }
76
+ return { content, attrs };
77
+ })()`);
78
+ const issues = analyzeViewport(result.attrs);
79
+ const lines = [];
80
+ if (result.content) {
81
+ lines.push(`Viewport meta: ${result.content}`);
82
+ }
83
+ else {
84
+ lines.push('⚠ No <meta name="viewport"> tag found.');
85
+ }
86
+ lines.push('');
87
+ if (issues.length === 0) {
88
+ lines.push('✅ No issues found. Viewport is correctly configured for iOS Safari.');
89
+ }
90
+ else {
91
+ const errors = issues.filter(i => i.severity === 'error');
92
+ const warnings = issues.filter(i => i.severity === 'warning');
93
+ const infos = issues.filter(i => i.severity === 'info');
94
+ if (errors.length > 0) {
95
+ lines.push('Errors:');
96
+ for (const i of errors)
97
+ lines.push(` ❌ ${i.message}`);
98
+ }
99
+ if (warnings.length > 0) {
100
+ lines.push('Warnings:');
101
+ for (const i of warnings)
102
+ lines.push(` ⚠ ${i.message}`);
103
+ }
104
+ if (infos.length > 0) {
105
+ lines.push('Info:');
106
+ for (const i of infos)
107
+ lines.push(` ℹ ${i.message}`);
108
+ }
109
+ }
110
+ return {
111
+ content: [{ type: 'text', text: lines.join('\n') }],
112
+ };
113
+ },
114
+ }),
115
+ defineTool({
116
+ name: 'get_safe_area_insets',
117
+ description: 'Read the current CSS safe-area-inset values (top, right, bottom, left) ' +
118
+ 'as seen by the page. These are non-zero on notched iPhones when ' +
119
+ 'viewport-fit=cover is set. Also checks whether the page uses ' +
120
+ 'env(safe-area-inset-*) in its stylesheets.',
121
+ slimDescription: 'Get iOS safe area inset values.',
122
+ schema: {},
123
+ handler: async (_params, driver) => {
124
+ const result = await driver.runScript(`(() => {
125
+ // Read computed env() values via a probe element
126
+ const probe = document.createElement('div');
127
+ probe.style.cssText =
128
+ 'position:fixed;top:env(safe-area-inset-top,0px);' +
129
+ 'right:env(safe-area-inset-right,0px);' +
130
+ 'bottom:env(safe-area-inset-bottom,0px);' +
131
+ 'left:env(safe-area-inset-left,0px);' +
132
+ 'pointer-events:none;visibility:hidden;';
133
+ document.body.appendChild(probe);
134
+ const cs = getComputedStyle(probe);
135
+ const insets = {
136
+ top: cs.top, right: cs.right,
137
+ bottom: cs.bottom, left: cs.left,
138
+ };
139
+ probe.remove();
140
+
141
+ // Check viewport-fit=cover
142
+ const meta = document.querySelector('meta[name="viewport"]');
143
+ const viewportFitCover = meta
144
+ ? (meta.getAttribute('content') || '').includes('viewport-fit=cover')
145
+ : false;
146
+
147
+ // Scan stylesheets for env(safe-area-inset usage
148
+ let usedInCSS = false;
149
+ try {
150
+ for (const sheet of document.styleSheets) {
151
+ try {
152
+ const text = [...sheet.cssRules].map(r => r.cssText).join(' ');
153
+ if (text.includes('safe-area-inset')) { usedInCSS = true; break; }
154
+ } catch {}
155
+ }
156
+ } catch {}
157
+
158
+ return { insets, viewportFitCover, usedInCSS };
159
+ })()`);
160
+ const lines = ['Safe area insets:'];
161
+ lines.push(` top: ${result.insets.top}`);
162
+ lines.push(` right: ${result.insets.right}`);
163
+ lines.push(` bottom: ${result.insets.bottom}`);
164
+ lines.push(` left: ${result.insets.left}`);
165
+ lines.push('');
166
+ if (!result.viewportFitCover) {
167
+ lines.push('⚠ viewport-fit=cover is NOT set. Safe area insets will always be 0 ' +
168
+ 'even on notched devices. Add viewport-fit=cover to your viewport meta tag.');
169
+ }
170
+ else {
171
+ lines.push('✅ viewport-fit=cover is set.');
172
+ }
173
+ lines.push(result.usedInCSS
174
+ ? '✅ env(safe-area-inset-*) found in stylesheets.'
175
+ : '⚠ env(safe-area-inset-*) not found in any stylesheet. Content may be obscured by the notch/home indicator.');
176
+ return {
177
+ content: [{ type: 'text', text: lines.join('\n') }],
178
+ };
179
+ },
180
+ }),
181
+ defineTool({
182
+ name: 'check_ios_web_app_readiness',
183
+ description: 'Audit the page for iOS Safari "Add to Home Screen" / PWA readiness. ' +
184
+ 'Checks apple-touch-icon, apple-mobile-web-app-capable, status bar style, ' +
185
+ 'theme-color, manifest link, and splash screen configuration.',
186
+ slimDescription: 'Audit iOS PWA/home screen readiness.',
187
+ schema: {},
188
+ handler: async (_params, driver) => {
189
+ const result = await driver.runScript(`(() => {
190
+ const getMeta = (name) => {
191
+ const el = document.querySelector('meta[name="' + name + '"]');
192
+ return el ? el.getAttribute('content') : null;
193
+ };
194
+ const getLink = (rel) => {
195
+ const el = document.querySelector('link[rel="' + rel + '"]');
196
+ return el ? el.getAttribute('href') : null;
197
+ };
198
+
199
+ const touchIcons = [...document.querySelectorAll('link[rel="apple-touch-icon"]')].map(el => ({
200
+ sizes: el.getAttribute('sizes') || 'unspecified',
201
+ href: el.getAttribute('href') || '',
202
+ }));
203
+
204
+ const splashScreens = document.querySelectorAll('link[rel="apple-touch-startup-image"]').length;
205
+
206
+ return {
207
+ capable: getMeta('apple-mobile-web-app-capable'),
208
+ statusBarStyle: getMeta('apple-mobile-web-app-status-bar-style'),
209
+ themeColor: getMeta('theme-color'),
210
+ manifestHref: getLink('manifest'),
211
+ touchIcons,
212
+ title: document.title,
213
+ appleSplashScreens: splashScreens,
214
+ };
215
+ })()`);
216
+ const checks = [];
217
+ // apple-mobile-web-app-capable
218
+ checks.push({
219
+ pass: result.capable === 'yes',
220
+ label: 'apple-mobile-web-app-capable',
221
+ detail: result.capable === 'yes'
222
+ ? 'Set to "yes" — app will run in standalone mode.'
223
+ : 'Not set or not "yes". The app will open in Safari, not standalone.',
224
+ });
225
+ // apple-touch-icon
226
+ const has180 = result.touchIcons.some(i => i.sizes === '180x180');
227
+ checks.push({
228
+ pass: result.touchIcons.length > 0,
229
+ label: 'apple-touch-icon',
230
+ detail: result.touchIcons.length > 0
231
+ ? `${result.touchIcons.length} icon(s): ${result.touchIcons.map(i => i.sizes).join(', ')}` +
232
+ (has180 ? '' : '. ⚠ Missing 180x180 (recommended for iPhone).')
233
+ : 'No apple-touch-icon found. iOS will use a screenshot as the icon.',
234
+ });
235
+ // theme-color
236
+ checks.push({
237
+ pass: result.themeColor !== null,
238
+ label: 'theme-color',
239
+ detail: result.themeColor !== null
240
+ ? `Set to "${result.themeColor}".`
241
+ : 'Not set. Safari 15+ uses theme-color for the tab bar tint.',
242
+ });
243
+ // status bar style
244
+ checks.push({
245
+ pass: result.statusBarStyle !== null,
246
+ label: 'apple-mobile-web-app-status-bar-style',
247
+ detail: result.statusBarStyle !== null
248
+ ? `Set to "${result.statusBarStyle}".`
249
+ : 'Not set. Defaults to "default" (black text on white). Consider "black-translucent" for full-screen feel.',
250
+ });
251
+ // manifest
252
+ checks.push({
253
+ pass: result.manifestHref !== null,
254
+ label: 'Web App Manifest',
255
+ detail: result.manifestHref !== null
256
+ ? `Found: ${result.manifestHref}`
257
+ : 'No <link rel="manifest"> found. Required for PWA install prompts.',
258
+ });
259
+ // splash screens
260
+ checks.push({
261
+ pass: result.appleSplashScreens > 0,
262
+ label: 'apple-touch-startup-image',
263
+ detail: result.appleSplashScreens > 0
264
+ ? `${result.appleSplashScreens} splash screen(s) defined.`
265
+ : 'No splash screens. Users will see a white screen while the app loads.',
266
+ });
267
+ const passed = checks.filter(c => c.pass).length;
268
+ const lines = [
269
+ `iOS Web App Readiness: ${passed}/${checks.length} checks passed`,
270
+ '',
271
+ ];
272
+ for (const c of checks) {
273
+ lines.push(`${c.pass ? '✅' : '❌'} ${c.label}`);
274
+ lines.push(` ${c.detail}`);
275
+ }
276
+ return {
277
+ content: [{ type: 'text', text: lines.join('\n') }],
278
+ };
279
+ },
280
+ }),
281
+ ];
282
+ //# sourceMappingURL=ios-validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ios-validation.js","sourceRoot":"","sources":["../../../src/tools/ios-validation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * WebKit CSS compatibility checker.
3
+ *
4
+ * Runs inside a live Safari WebDriver session. Extracts CSS from the page
5
+ * using structured DOM APIs (rule.style iteration — no regex, no false
6
+ * positives on custom properties), then runs CSS.supports() in the actual
7
+ * browser to report what is genuinely broken right now.
8
+ */
9
+ export declare const tools: import("./types.js").ToolDef<import("zod").ZodRawShape>[];
10
+ //# sourceMappingURL=webkit-compat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webkit-compat.d.ts","sourceRoot":"","sources":["../../../src/tools/webkit-compat.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH,eAAO,MAAM,KAAK,2DAkKjB,CAAC"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * WebKit CSS compatibility checker.
3
+ *
4
+ * Runs inside a live Safari WebDriver session. Extracts CSS from the page
5
+ * using structured DOM APIs (rule.style iteration — no regex, no false
6
+ * positives on custom properties), then runs CSS.supports() in the actual
7
+ * browser to report what is genuinely broken right now.
8
+ */
9
+ import { defineTool } from './types.js';
10
+ /**
11
+ * Behavioral quirks that CSS.supports() cannot detect — the property IS
12
+ * "supported", it just renders incorrectly. Keep this list small, precise,
13
+ * and each entry must be a confirmed Safari rendering bug.
14
+ */
15
+ const BEHAVIORAL_QUIRKS = [
16
+ {
17
+ property: 'position',
18
+ matchValue: 'sticky',
19
+ message: 'position:sticky silently fails inside an overflow:hidden or overflow:auto ' +
20
+ 'ancestor in Safari. Use overflow:clip on the ancestor instead.',
21
+ },
22
+ ];
23
+ export const tools = [
24
+ defineTool({
25
+ name: 'check_webkit_compatibility',
26
+ description: 'Check CSS on the current page against the live Safari session. ' +
27
+ 'Extracts every CSS property via structured DOM APIs, runs CSS.supports() ' +
28
+ 'in the actual browser, and reports what is broken right now — unsupported ' +
29
+ 'properties, missing -webkit- prefixes, and known Safari rendering quirks.',
30
+ slimDescription: 'Check CSS against live Safari via CSS.supports().',
31
+ schema: {},
32
+ handler: async (_params, driver) => {
33
+ // -----------------------------------------------------------------
34
+ // Step 1: Extract structured CSS from the page + run CSS.supports()
35
+ // in one round-trip. Uses rule.style iteration for canonical property
36
+ // names — custom properties (--*) are excluded automatically.
37
+ // -----------------------------------------------------------------
38
+ const results = await driver.runScript(`(() => {
39
+ // Collect unique property:value pairs from all accessible stylesheets
40
+ const seen = new Map();
41
+
42
+ function collectFromStyle(style) {
43
+ for (const prop of style) {
44
+ if (prop.startsWith('--')) continue;
45
+ if (seen.has(prop)) continue;
46
+ seen.set(prop, style.getPropertyValue(prop).trim());
47
+ }
48
+ }
49
+
50
+ function processRules(rules) {
51
+ for (const rule of rules) {
52
+ // Skip @font-face — its descriptors (src, font-display, unicode-range)
53
+ // are not CSS properties and CSS.supports() always returns false for them
54
+ if (rule.constructor?.name === 'CSSFontFaceRule') continue;
55
+ if (rule.style) collectFromStyle(rule.style);
56
+ if (rule.cssRules) {
57
+ try { processRules(rule.cssRules); } catch {}
58
+ }
59
+ }
60
+ }
61
+
62
+ for (const sheet of document.styleSheets) {
63
+ try { processRules(sheet.cssRules); } catch {}
64
+ }
65
+
66
+ // Inline styles
67
+ for (const el of document.querySelectorAll('[style]')) {
68
+ collectFromStyle(el.style);
69
+ }
70
+
71
+ const totalProperties = seen.size;
72
+ const unsupported = [];
73
+ const needsPrefix = [];
74
+
75
+ for (const [prop, value] of seen) {
76
+ let supported = false;
77
+ try { supported = CSS.supports(prop, value); } catch {}
78
+
79
+ if (!supported) {
80
+ // Try again with a generic value — the extracted value might be
81
+ // a computed/resolved form CSS.supports doesn't accept
82
+ try { supported = CSS.supports(prop, 'initial'); } catch {}
83
+ }
84
+
85
+ if (supported) continue;
86
+
87
+ // Not supported unprefixed — check if -webkit- variant works
88
+ const webkitProp = '-webkit-' + prop;
89
+ let webkitSupported = false;
90
+ try { webkitSupported = CSS.supports(webkitProp, value); } catch {}
91
+ if (!webkitSupported) {
92
+ try { webkitSupported = CSS.supports(webkitProp, 'initial'); } catch {}
93
+ }
94
+
95
+ if (webkitSupported) {
96
+ needsPrefix.push({ property: prop, value });
97
+ } else {
98
+ unsupported.push({ property: prop, value });
99
+ }
100
+ }
101
+
102
+ // Collect a subset of computed properties for behavioral quirk checks
103
+ const body = document.body;
104
+ const computedProperties = {};
105
+ if (body) {
106
+ const quirkProps = ${JSON.stringify(BEHAVIORAL_QUIRKS.map(q => q.property))};
107
+ for (const prop of quirkProps) {
108
+ if (seen.has(prop)) {
109
+ computedProperties[prop] = seen.get(prop);
110
+ }
111
+ }
112
+ }
113
+
114
+ return { totalProperties, unsupported, needsPrefix, computedProperties };
115
+ })()`);
116
+ // -----------------------------------------------------------------
117
+ // Step 2: Check behavioral quirks
118
+ // -----------------------------------------------------------------
119
+ const quirks = [];
120
+ for (const quirk of BEHAVIORAL_QUIRKS) {
121
+ const value = results.computedProperties[quirk.property];
122
+ if (value === undefined)
123
+ continue;
124
+ if (quirk.matchValue && !value.includes(quirk.matchValue))
125
+ continue;
126
+ quirks.push(quirk.message);
127
+ }
128
+ // -----------------------------------------------------------------
129
+ // Step 3: Format output
130
+ // -----------------------------------------------------------------
131
+ const total = results.unsupported.length + results.needsPrefix.length + quirks.length;
132
+ const lines = [];
133
+ if (total === 0) {
134
+ lines.push(`✅ No issues found. ${results.totalProperties} CSS properties checked via CSS.supports() in this Safari session.`);
135
+ }
136
+ else {
137
+ lines.push(`Found ${total} issue(s) — ${results.totalProperties} properties checked via CSS.supports() in this Safari session.`);
138
+ lines.push('');
139
+ if (results.unsupported.length > 0) {
140
+ lines.push(`❌ Unsupported in this Safari (${results.unsupported.length}):`);
141
+ for (const { property, value } of results.unsupported) {
142
+ const preview = value.length > 60 ? value.slice(0, 57) + '...' : value;
143
+ lines.push(` ${property}: ${preview}`);
144
+ }
145
+ lines.push('');
146
+ }
147
+ if (results.needsPrefix.length > 0) {
148
+ lines.push(`⚠ Needs -webkit- prefix (${results.needsPrefix.length}):`);
149
+ for (const { property } of results.needsPrefix) {
150
+ lines.push(` ${property} → use -webkit-${property} alongside it`);
151
+ }
152
+ lines.push('');
153
+ }
154
+ if (quirks.length > 0) {
155
+ lines.push(`ℹ Known Safari rendering quirks (${quirks.length}):`);
156
+ for (const msg of quirks) {
157
+ lines.push(` ${msg}`);
158
+ }
159
+ }
160
+ }
161
+ return { content: [{ type: 'text', text: lines.join('\n') }] };
162
+ },
163
+ }),
164
+ ];
165
+ //# sourceMappingURL=webkit-compat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webkit-compat.js","sourceRoot":"","sources":["../../../src/tools/webkit-compat.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "1.7.0";
1
+ export declare const VERSION = "1.8.1";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,4 +1,4 @@
1
1
  // x-release-please-start-version
2
- export const VERSION = '1.7.0';
2
+ export const VERSION = '1.8.1';
3
3
  // x-release-please-end
4
4
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "safari-devtools-mcp",
3
3
  "mcpName": "io.github.HayoDev/safari-devtools-mcp",
4
- "version": "1.7.0",
4
+ "version": "1.8.1",
5
5
  "description": "Safari DevTools MCP — real browser debugging with network interception, DOM inspection, cookie/storage management, and CSS analysis for AI agents on macOS",
6
6
  "type": "module",
7
7
  "main": "build/src/index.js",
@@ -7,18 +7,23 @@ or when debugging WebKit-specific behavior.
7
7
 
8
8
  ### CSS issues
9
9
 
10
- Safari lags behind on some CSS features and requires `-webkit-` prefixes
11
- for others. Debug with:
10
+ Start by running `check_webkit_compatibility` it checks every CSS property
11
+ on the page against the live Safari session via `CSS.supports()` and reports
12
+ what is actually broken (unsupported properties, missing `-webkit-` prefixes):
13
+
14
+ ```
15
+ check_webkit_compatibility
16
+ ```
17
+
18
+ For specific elements, inspect computed styles directly:
12
19
 
13
20
  ```
14
21
  get_computed_style uid="<element-uid>" properties=["display", "gap", "aspect-ratio", "container-type", "backdrop-filter"]
15
22
  ```
16
23
 
17
- **Known Safari CSS quirks:**
24
+ **Common Safari CSS gotchas:**
18
25
 
19
- - `gap` in flexbox: Supported since Safari 14.1, but older versions fail silently.
20
- - `aspect-ratio`: Works, but may not apply in some flex/grid contexts.
21
- - `-webkit-backdrop-filter`: Still requires prefix in some versions.
26
+ - `-webkit-backdrop-filter`: Still requires prefix in Safari <18.
22
27
  - `100vh` on iOS Safari includes the address bar — use `100dvh` instead.
23
28
  - `position: sticky` inside `overflow: auto` containers often breaks.
24
29
  - `:has()` selector: Safari was first to ship it, but edge cases may differ.
@@ -115,7 +120,13 @@ see what the a11y tree reports, then `take_screenshot` for visual comparison.
115
120
  evaluate_script function="() => { /* feature detection code */ }"
116
121
  ```
117
122
 
118
- 6. Check CSS rendering:
123
+ 6. Check CSS compatibility across the page:
124
+
125
+ ```
126
+ check_webkit_compatibility
127
+ ```
128
+
129
+ 7. Inspect a specific element's styles:
119
130
  ```
120
131
  get_computed_style uid="<element-uid>"
121
132
  ```