chrome-devtools-mcp-for-extension 0.10.4 → 0.11.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.
@@ -280,29 +280,27 @@ export const askChatGPTWeb = defineTool({
280
280
  if (deepResearchEnabled) {
281
281
  response.appendResponseLine('✅ DeepResearchモード有効化完了');
282
282
  await new Promise((resolve) => setTimeout(resolve, 1000));
283
- // Verify mode was actually enabled (check for composer-pill button)
284
- const verified = await page.evaluate(() => {
285
- const DEEP_RESEARCH_PATTERN = /リサーチ|deep\s*research|ディープ\s*リサーチ|深度研究|深入研究/i;
286
- const promptTextarea = document.querySelector('#prompt-textarea');
287
- if (promptTextarea) {
288
- const form = promptTextarea.closest('form');
289
- if (form) {
290
- const pillButton = Array.from(form.querySelectorAll('button')).find((btn) => {
291
- const text = btn.textContent?.trim() || '';
292
- const ariaLabel = btn.getAttribute('aria-label') || '';
293
- return (btn.className.includes('composer-pill') &&
294
- DEEP_RESEARCH_PATTERN.test(text + ' ' + ariaLabel));
295
- });
296
- return !!pillButton;
297
- }
298
- }
299
- return false;
283
+ // Verify mode was actually enabled (check for new UI indicators)
284
+ const verification = await page.evaluate(() => {
285
+ // Check placeholder text
286
+ const textarea = document.querySelector('textarea');
287
+ const placeholder = textarea?.getAttribute('placeholder') || '';
288
+ // Check for delete button
289
+ const deleteButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('リサーチ:クリックして削除'));
290
+ // Check for sources button
291
+ const sourcesButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('情報源'));
292
+ return {
293
+ hasCorrectPlaceholder: placeholder.includes('詳細なレポート') || placeholder.includes('リサーチ'),
294
+ hasDeleteButton: !!deleteButton,
295
+ hasSourcesButton: !!sourcesButton,
296
+ placeholder: placeholder
297
+ };
300
298
  });
301
- if (verified) {
302
- response.appendResponseLine('✅ モード確認完了: DeepResearch有効(composer-pill検出)');
299
+ if (verification.hasCorrectPlaceholder || verification.hasDeleteButton) {
300
+ response.appendResponseLine('✅ モード確認完了: DeepResearch有効');
303
301
  }
304
302
  else {
305
- response.appendResponseLine('⚠️ DeepResearchモードの確認に失敗しました');
303
+ response.appendResponseLine(`⚠️ DeepResearchモードの確認に失敗しました(placeholder: ${verification.placeholder})`);
306
304
  }
307
305
  }
308
306
  else {
@@ -313,27 +311,21 @@ export const askChatGPTWeb = defineTool({
313
311
  // Step 4: Send question (with final mode verification)
314
312
  if (useDeepResearch) {
315
313
  const finalCheck = await page.evaluate(() => {
316
- const DEEP_RESEARCH_PATTERN = /リサーチ|deep\s*research|ディープ\s*リサーチ|深度研究|深入研究/i;
317
- const promptTextarea = document.querySelector('#prompt-textarea');
318
- if (promptTextarea) {
319
- const form = promptTextarea.closest('form');
320
- if (form) {
321
- const pillButton = Array.from(form.querySelectorAll('button')).find((btn) => {
322
- const text = btn.textContent?.trim() || '';
323
- const ariaLabel = btn.getAttribute('aria-label') || '';
324
- return (btn.className.includes('composer-pill') &&
325
- DEEP_RESEARCH_PATTERN.test(text + ' ' + ariaLabel));
326
- });
327
- return !!pillButton;
328
- }
329
- }
330
- return false;
314
+ // Check placeholder text
315
+ const textarea = document.querySelector('textarea');
316
+ const placeholder = textarea?.getAttribute('placeholder') || '';
317
+ // Check for delete button
318
+ const deleteButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('リサーチ:クリックして削除'));
319
+ return {
320
+ isEnabled: placeholder.includes('詳細なレポート') || placeholder.includes('リサーチ') || !!deleteButton,
321
+ placeholder: placeholder
322
+ };
331
323
  });
332
- if (!finalCheck) {
333
- response.appendResponseLine('❌ エラー: DeepResearchモードが無効です(composer-pill未検出)。送信を中止します。');
324
+ if (!finalCheck.isEnabled) {
325
+ response.appendResponseLine(`❌ エラー: DeepResearchモードが無効です。送信を中止します。(placeholder: ${finalCheck.placeholder})`);
334
326
  return;
335
327
  }
336
- response.appendResponseLine('✅ 送信前確認: DeepResearchモード有効(composer-pill検出)');
328
+ response.appendResponseLine('✅ 送信前確認: DeepResearchモード有効');
337
329
  }
338
330
  response.appendResponseLine('質問を送信中...');
339
331
  const questionSent = await page.evaluate((questionText) => {
@@ -266,44 +266,73 @@ async function detectDeepResearchMode(page) {
266
266
  });
267
267
  }
268
268
  /**
269
- * Enable DeepResearch mode by clicking + button and selecting option
269
+ * Enable DeepResearch mode using new UI structure (2025-10)
270
+ * - Click "ファイルの追加など" button to open menu
271
+ * - Select "Deep Research" menuitemradio
272
+ * - Verify by checking placeholder and delete button
270
273
  */
271
274
  async function enableDeepResearchMode(page, response) {
272
275
  try {
273
276
  response.appendResponseLine('DeepResearchモードを有効化中...');
274
- // Step 1: +ボタンをクリック(Puppeteer click
275
- const plusButton = await page.$('button[aria-label*="ファイルの追加"]');
276
- if (!plusButton) {
277
- return { success: false, error: '+ボタンが見つかりません' };
277
+ // Step 1: Find and click "ファイルの追加など" button (with haspopup="menu")
278
+ const menuButton = await page.evaluate(() => {
279
+ const buttons = Array.from(document.querySelectorAll('button[haspopup="menu"]'));
280
+ const targetButton = buttons.find(btn => {
281
+ const text = btn.textContent || '';
282
+ return text.includes('ファイルの追加') || text.includes('追加');
283
+ });
284
+ if (targetButton) {
285
+ targetButton.click();
286
+ return true;
287
+ }
288
+ return false;
289
+ });
290
+ if (!menuButton) {
291
+ return { success: false, error: '「ファイルの追加など」ボタンが見つかりません' };
278
292
  }
279
- await plusButton.click();
280
- response.appendResponseLine('✅ +ボタンをクリック');
293
+ response.appendResponseLine('✅ メニューボタンをクリック');
281
294
  await new Promise((resolve) => setTimeout(resolve, 500));
282
- // Step 2: Deep Research menuitemradio をクリック(Puppeteer click)
283
- const menuItems = await page.$$('[role="menuitemradio"]');
284
- let deepResearchItem = null;
285
- for (const item of menuItems) {
286
- const text = await item.evaluate((el) => el.textContent);
287
- if (text?.includes('Deep Research') || text?.includes('リサーチ')) {
288
- deepResearchItem = item;
289
- break;
295
+ // Step 2: Click "Deep Research" menuitemradio from opened menu
296
+ const deepResearchClicked = await page.evaluate(() => {
297
+ const menuItems = Array.from(document.querySelectorAll('[role="menuitemradio"]'));
298
+ const deepResearchItem = menuItems.find(item => {
299
+ const text = item.textContent || '';
300
+ return text.includes('Deep Research') || text.includes('リサーチ');
301
+ });
302
+ if (deepResearchItem) {
303
+ deepResearchItem.click();
304
+ return true;
290
305
  }
291
- }
292
- if (!deepResearchItem) {
306
+ return false;
307
+ });
308
+ if (!deepResearchClicked) {
293
309
  return { success: false, error: 'Deep Research menuitemradio が見つかりません' };
294
310
  }
295
- await deepResearchItem.click();
296
- response.appendResponseLine('✅ Deep Research menuitemradio をクリック');
311
+ response.appendResponseLine('✅ Deep Research を選択');
297
312
  await new Promise((resolve) => setTimeout(resolve, 1000));
298
- // Step 3: 検証(composer-pill確認)
299
- const verification = await detectDeepResearchMode(page);
300
- if (!verification.isEnabled) {
313
+ // Step 3: Verify DeepResearch mode is enabled
314
+ const verification = await page.evaluate(() => {
315
+ // Check placeholder text
316
+ const textarea = document.querySelector('textarea');
317
+ const placeholder = textarea?.getAttribute('placeholder') || '';
318
+ // Check for delete button
319
+ const deleteButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('リサーチ:クリックして削除'));
320
+ // Check for sources button
321
+ const sourcesButton = Array.from(document.querySelectorAll('button')).find(btn => btn.textContent?.includes('情報源'));
322
+ return {
323
+ hasCorrectPlaceholder: placeholder.includes('詳細なレポート') || placeholder.includes('リサーチ'),
324
+ hasDeleteButton: !!deleteButton,
325
+ hasSourcesButton: !!sourcesButton,
326
+ placeholder: placeholder
327
+ };
328
+ });
329
+ if (!verification.hasCorrectPlaceholder && !verification.hasDeleteButton) {
301
330
  return {
302
331
  success: false,
303
- error: 'DeepResearchモードの有効化に失敗しました(リサーチpillが検出されませんでした)',
332
+ error: `DeepResearchモードの有効化に失敗しました(プレースホルダー: ${verification.placeholder})`,
304
333
  };
305
334
  }
306
- response.appendResponseLine(`✅ モード確認完了: DeepResearch有効 (${verification.indicator})`);
335
+ response.appendResponseLine(`✅ モード確認完了: DeepResearch有効 (placeholder + delete button)`);
307
336
  return { success: true };
308
337
  }
309
338
  catch (error) {
@@ -414,13 +443,28 @@ async function handleConversationLoop(page, response, maxTurns = 5) {
414
443
  while (conversationTurns < maxTurns) {
415
444
  await new Promise((resolve) => setTimeout(resolve, 3000));
416
445
  const status = await page.evaluate(() => {
417
- // Check for research progress indicator
446
+ // Check for research progress indicator (NEW UI: loading sources button)
447
+ const allButtons = Array.from(document.querySelectorAll('button'));
448
+ // NEW: Look for "〜を読み込んでいます X 情報源" button
449
+ const loadingSourcesButton = allButtons.find(btn => {
450
+ const text = btn.textContent || '';
451
+ return text.includes('読み込んでいます') && text.includes('情報源');
452
+ });
453
+ // Also check for "リサーチを開始しています" button
454
+ const researchStartButton = allButtons.find(btn => btn.textContent?.includes('リサーチを開始しています'));
455
+ if (loadingSourcesButton || researchStartButton) {
456
+ return {
457
+ phase: 'researching',
458
+ indicator: loadingSourcesButton ? 'loading sources' : 'starting research'
459
+ };
460
+ }
461
+ // Legacy indicators (fallback)
418
462
  const progressIndicators = Array.from(document.querySelectorAll('div, span'));
419
463
  const isResearching = progressIndicators.some((el) => el.textContent?.includes('リサーチ中') ||
420
464
  el.textContent?.includes('Researching') ||
421
465
  el.textContent?.includes('情報を収集中'));
422
466
  if (isResearching) {
423
- return { phase: 'researching' };
467
+ return { phase: 'researching', indicator: 'text match' };
424
468
  }
425
469
  // Check if ChatGPT is asking a clarifying question
426
470
  const assistantMessages = document.querySelectorAll('[data-message-author-role="assistant"]');
@@ -430,8 +474,8 @@ async function handleConversationLoop(page, response, maxTurns = 5) {
430
474
  const latestMessage = assistantMessages[assistantMessages.length - 1];
431
475
  const messageText = latestMessage.textContent || '';
432
476
  // Check if it's still streaming
433
- const buttons = Array.from(document.querySelectorAll('button'));
434
- const isStreaming = buttons.some((btn) => {
477
+ const streamButtons = Array.from(document.querySelectorAll('button'));
478
+ const isStreaming = streamButtons.some((btn) => {
435
479
  const text = btn.textContent || '';
436
480
  const aria = btn.getAttribute('aria-label') || '';
437
481
  return (text.includes('ストリーミングの停止') ||
@@ -501,22 +545,39 @@ async function monitorResearch(page, response, startTime) {
501
545
  while (Date.now() - startTime < MAX_WAIT_TIME) {
502
546
  await new Promise((resolve) => setTimeout(resolve, 5000));
503
547
  const researchStatus = await page.evaluate(() => {
504
- // Check if research completed
505
- const assistantMessages = document.querySelectorAll('[data-message-author-role="assistant"]');
506
- if (assistantMessages.length === 0) {
507
- return { completed: false, stillResearching: true };
548
+ const buttons = Array.from(document.querySelectorAll('button'));
549
+ // Check if still loading sources (NEW UI indicator)
550
+ const loadingSourcesButton = buttons.find(btn => {
551
+ const text = btn.textContent || '';
552
+ return text.includes('読み込んでいます') && text.includes('情報源');
553
+ });
554
+ // Check for "リサーチを開始しています" button
555
+ const researchStartButton = buttons.find(btn => btn.textContent?.includes('リサーチを開始しています'));
556
+ if (loadingSourcesButton || researchStartButton) {
557
+ const sourcesText = loadingSourcesButton?.textContent || '';
558
+ const match = sourcesText.match(/(\d+)\s*情報源/);
559
+ const sourcesCount = match ? match[1] : '?';
560
+ return {
561
+ completed: false,
562
+ stillResearching: true,
563
+ progress: `情報源読み込み中 (${sourcesCount}件)`
564
+ };
508
565
  }
509
- const latestMessage = assistantMessages[assistantMessages.length - 1];
510
- // Check if still researching
566
+ // Legacy: Check if still researching
511
567
  const progressIndicators = Array.from(document.querySelectorAll('div, span'));
512
568
  const isResearching = progressIndicators.some((el) => el.textContent?.includes('リサーチ中') ||
513
569
  el.textContent?.includes('Researching') ||
514
570
  el.textContent?.includes('情報を収集中'));
515
571
  if (isResearching) {
516
- return { completed: false, stillResearching: true };
572
+ return { completed: false, stillResearching: true, progress: 'リサーチ中' };
517
573
  }
574
+ // Check if research completed
575
+ const assistantMessages = document.querySelectorAll('[data-message-author-role="assistant"]');
576
+ if (assistantMessages.length === 0) {
577
+ return { completed: false, stillResearching: true, progress: '待機中' };
578
+ }
579
+ const latestMessage = assistantMessages[assistantMessages.length - 1];
518
580
  // Check if streaming
519
- const buttons = Array.from(document.querySelectorAll('button'));
520
581
  const isStreaming = buttons.some((btn) => {
521
582
  const text = btn.textContent || '';
522
583
  const aria = btn.getAttribute('aria-label') || '';
@@ -544,7 +605,8 @@ async function monitorResearch(page, response, startTime) {
544
605
  progressCounter++;
545
606
  if (progressCounter % 6 === 0) {
546
607
  const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000);
547
- response.appendResponseLine(`⏱️ ${elapsedSeconds}秒経過 - リサーチ継続中...`);
608
+ const progressText = researchStatus.progress || 'リサーチ継続中';
609
+ response.appendResponseLine(`⏱️ ${elapsedSeconds}秒経過 - ${progressText}...`);
548
610
  }
549
611
  }
550
612
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-devtools-mcp-for-extension",
3
- "version": "0.10.4",
3
+ "version": "0.11.1",
4
4
  "description": "MCP server for Chrome extension development with Web Store automation. Fork of chrome-devtools-mcp with extension-specific tools.",
5
5
  "type": "module",
6
6
  "bin": "./build/src/index.js",