Claude Code Plugins

Community-maintained marketplace

Feedback

Chrome Extension APIs reference covering runtime, storage, tabs, scripting, action, alarms, notifications, contextMenus, sidePanel, offscreen, and identity APIs. Use when implementing Chrome extension functionality.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name extension-apis
description Chrome Extension APIs reference covering runtime, storage, tabs, scripting, action, alarms, notifications, contextMenus, sidePanel, offscreen, and identity APIs. Use when implementing Chrome extension functionality.

Chrome Extension APIs Reference

chrome.runtime

Lifecycle Events

// Extension installed or updated
chrome.runtime.onInstalled.addListener((details) => {
  if (details.reason === 'install') {
    // First install
  } else if (details.reason === 'update') {
    // Updated from details.previousVersion
  }
});

// Browser started with extension enabled
chrome.runtime.onStartup.addListener(() => {
  // Browser startup
});

// Extension suspended (MV3)
chrome.runtime.onSuspend.addListener(() => {
  // Clean up before suspension
});

Messaging

// Send message to background
const response = await chrome.runtime.sendMessage({
  type: 'ACTION',
  data: payload
});

// Listen for messages
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  console.log('From:', sender.tab?.id || 'extension');

  // Async response - MUST return true
  handleAsync(message).then(sendResponse);
  return true;
});

// Connect for long-lived connection
const port = chrome.runtime.connect({ name: 'channel' });
port.postMessage({ type: 'HELLO' });
port.onMessage.addListener((message) => {
  console.log('Received:', message);
});
port.onDisconnect.addListener(() => {
  console.log('Port disconnected');
});

Extension Info

// Get extension info
const manifest = chrome.runtime.getManifest();
const id = chrome.runtime.id;
const url = chrome.runtime.getURL('page.html');

// Reload extension
chrome.runtime.reload();

// Open options page
chrome.runtime.openOptionsPage();

chrome.storage

Storage Areas

// Local storage (10MB limit, persistent)
await chrome.storage.local.set({ key: 'value' });
const { key } = await chrome.storage.local.get('key');
const all = await chrome.storage.local.get(null);
await chrome.storage.local.remove('key');
await chrome.storage.local.clear();

// Sync storage (100KB limit, synced across devices)
await chrome.storage.sync.set({ settings: { theme: 'dark' } });
const { settings } = await chrome.storage.sync.get('settings');

// Session storage (cleared on browser restart)
await chrome.storage.session.set({ tempData: data });
const { tempData } = await chrome.storage.session.get('tempData');

// Check usage
const bytes = await chrome.storage.local.getBytesInUse(null);
const syncBytes = await chrome.storage.sync.getBytesInUse(null);

Storage Limits

Area Total Limit Per Item
local 10 MB Unlimited
sync 102,400 bytes 8,192 bytes
session 10 MB Unlimited

Change Listener

chrome.storage.onChanged.addListener((changes, areaName) => {
  for (const [key, { oldValue, newValue }] of Object.entries(changes)) {
    console.log(`${areaName}.${key}: ${oldValue} → ${newValue}`);
  }
});

chrome.tabs

Query Tabs

// Get current tab
const [tab] = await chrome.tabs.query({
  active: true,
  currentWindow: true
});

// Get all tabs
const tabs = await chrome.tabs.query({});

// Query with filters
const tabs = await chrome.tabs.query({
  url: '*://*.example.com/*',
  status: 'complete',
  pinned: false
});

Tab Operations

// Create new tab
const tab = await chrome.tabs.create({
  url: 'https://example.com',
  active: true,
  index: 0
});

// Update tab
await chrome.tabs.update(tabId, {
  url: 'https://new-url.com',
  active: true,
  pinned: true
});

// Remove tab
await chrome.tabs.remove(tabId);
await chrome.tabs.remove([tabId1, tabId2]);

// Reload tab
await chrome.tabs.reload(tabId);

// Duplicate tab
const newTab = await chrome.tabs.duplicate(tabId);

// Move tab
await chrome.tabs.move(tabId, { index: 0 });

// Get tab
const tab = await chrome.tabs.get(tabId);

Tab Messaging

// Send message to content script in tab
try {
  const response = await chrome.tabs.sendMessage(tabId, {
    type: 'GET_DATA'
  });
} catch (error) {
  // Content script not loaded
}

// Send to specific frame
await chrome.tabs.sendMessage(tabId, message, { frameId: 0 });

Tab Events

chrome.tabs.onCreated.addListener((tab) => {});
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete') {
    // Page loaded
  }
});
chrome.tabs.onRemoved.addListener((tabId, removeInfo) => {});
chrome.tabs.onActivated.addListener(({ tabId, windowId }) => {});

chrome.scripting

Execute Script

// Execute function in tab
const results = await chrome.scripting.executeScript({
  target: { tabId },
  func: () => document.title
});
console.log(results[0].result);

// Execute with arguments
await chrome.scripting.executeScript({
  target: { tabId },
  func: (arg1, arg2) => {
    console.log(arg1, arg2);
  },
  args: ['hello', 'world']
});

// Execute file
await chrome.scripting.executeScript({
  target: { tabId },
  files: ['content.js']
});

// Execute in specific frames
await chrome.scripting.executeScript({
  target: { tabId, frameIds: [0] },
  files: ['content.js']
});

// Execute in all frames
await chrome.scripting.executeScript({
  target: { tabId, allFrames: true },
  files: ['content.js']
});

// Execute in main world (access page variables)
await chrome.scripting.executeScript({
  target: { tabId },
  world: 'MAIN',
  func: () => window.somePageVariable
});

Insert CSS

// Insert CSS string
await chrome.scripting.insertCSS({
  target: { tabId },
  css: 'body { background: red !important; }'
});

// Insert CSS file
await chrome.scripting.insertCSS({
  target: { tabId },
  files: ['styles.css']
});

// Remove CSS
await chrome.scripting.removeCSS({
  target: { tabId },
  css: 'body { background: red !important; }'
});

Register Content Scripts

// Register dynamic content script
await chrome.scripting.registerContentScripts([{
  id: 'my-script',
  matches: ['*://*.example.com/*'],
  js: ['content.js'],
  runAt: 'document_idle'
}]);

// Update registered script
await chrome.scripting.updateContentScripts([{
  id: 'my-script',
  matches: ['*://*.new-domain.com/*']
}]);

// Unregister
await chrome.scripting.unregisterContentScripts({
  ids: ['my-script']
});

// Get registered scripts
const scripts = await chrome.scripting.getRegisteredContentScripts();

chrome.action

// Set popup
chrome.action.setPopup({ popup: 'popup.html' });
chrome.action.setPopup({ tabId, popup: 'tab-popup.html' });

// Set icon
chrome.action.setIcon({
  path: 'icons/active.png'
});
chrome.action.setIcon({
  tabId,
  path: { 16: 'icon16.png', 32: 'icon32.png' }
});

// Set badge
chrome.action.setBadgeText({ text: '5' });
chrome.action.setBadgeText({ tabId, text: '!' });
chrome.action.setBadgeBackgroundColor({ color: '#FF0000' });
chrome.action.setBadgeTextColor({ color: '#FFFFFF' });

// Set title (tooltip)
chrome.action.setTitle({ title: 'Custom tooltip' });

// Enable/disable
chrome.action.enable(tabId);
chrome.action.disable(tabId);

// Click handler (when no popup)
chrome.action.onClicked.addListener((tab) => {
  // Handle click
});

// Open popup programmatically (Chrome 127+)
await chrome.action.openPopup();

chrome.alarms

// Create alarm
chrome.alarms.create('myAlarm', {
  delayInMinutes: 1,           // First trigger
  periodInMinutes: 5           // Repeat interval
});

chrome.alarms.create('oneTime', {
  when: Date.now() + 60000     // Specific time (ms)
});

// Listen for alarm
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'myAlarm') {
    // Handle alarm
  }
});

// Get alarms
const alarm = await chrome.alarms.get('myAlarm');
const allAlarms = await chrome.alarms.getAll();

// Clear alarms
await chrome.alarms.clear('myAlarm');
await chrome.alarms.clearAll();

chrome.notifications

// Create notification
const notificationId = await chrome.notifications.create({
  type: 'basic',
  iconUrl: 'icon128.png',
  title: 'Notification Title',
  message: 'Notification message',
  priority: 2,
  buttons: [
    { title: 'Button 1' },
    { title: 'Button 2' }
  ]
});

// Progress notification
await chrome.notifications.create({
  type: 'progress',
  iconUrl: 'icon128.png',
  title: 'Downloading',
  message: 'Please wait...',
  progress: 50
});

// Update notification
await chrome.notifications.update(notificationId, {
  progress: 100,
  message: 'Complete!'
});

// Clear notification
await chrome.notifications.clear(notificationId);

// Event listeners
chrome.notifications.onClicked.addListener((notificationId) => {});
chrome.notifications.onButtonClicked.addListener((notificationId, buttonIndex) => {});
chrome.notifications.onClosed.addListener((notificationId, byUser) => {});

chrome.contextMenus

// Create context menu
chrome.contextMenus.create({
  id: 'myMenu',
  title: 'Do something with "%s"',
  contexts: ['selection', 'link', 'image'],
  documentUrlPatterns: ['*://*.example.com/*']
});

// Nested menu
chrome.contextMenus.create({
  id: 'parent',
  title: 'Parent Menu',
  contexts: ['all']
});
chrome.contextMenus.create({
  id: 'child',
  parentId: 'parent',
  title: 'Child Item',
  contexts: ['all']
});

// Handle click
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'myMenu') {
    console.log('Selected text:', info.selectionText);
    console.log('Link URL:', info.linkUrl);
    console.log('Image URL:', info.srcUrl);
  }
});

// Update menu
chrome.contextMenus.update('myMenu', { title: 'New Title' });

// Remove menu
chrome.contextMenus.remove('myMenu');
chrome.contextMenus.removeAll();

Context Types

all, page, frame, selection, link, editable, image, video, audio, launcher, browser_action, page_action, action


chrome.sidePanel

// Open side panel
await chrome.sidePanel.open({ tabId: tab.id });
await chrome.sidePanel.open({ windowId: window.id });

// Set panel options
await chrome.sidePanel.setOptions({
  tabId,
  path: 'sidepanel.html',
  enabled: true
});

// Get panel options
const options = await chrome.sidePanel.getOptions({ tabId });

// Set panel behavior
await chrome.sidePanel.setPanelBehavior({
  openPanelOnActionClick: true
});

chrome.offscreen

For tasks requiring DOM but not visible UI.

// Create offscreen document
await chrome.offscreen.createDocument({
  url: 'offscreen.html',
  reasons: ['DOM_SCRAPING', 'AUDIO_PLAYBACK'],
  justification: 'Parse HTML content'
});

// Check if exists
const existing = await chrome.offscreen.hasDocument();

// Close offscreen document
await chrome.offscreen.closeDocument();

Reasons

TESTING, AUDIO_PLAYBACK, IFRAME_SCRIPTING, DOM_SCRAPING, BLOBS, DOM_PARSER, USER_MEDIA, DISPLAY_MEDIA, WEB_RTC, CLIPBOARD, LOCAL_STORAGE, WORKERS, BATTERY_STATUS, MATCH_MEDIA, GEOLOCATION


chrome.identity

// OAuth2 authentication
const token = await chrome.identity.getAuthToken({
  interactive: true
});

// Launch web auth flow
const responseUrl = await chrome.identity.launchWebAuthFlow({
  url: 'https://auth.provider.com/oauth?...',
  interactive: true
});

// Remove cached token
await chrome.identity.removeCachedAuthToken({ token });

// Get profile info
const userInfo = await chrome.identity.getProfileUserInfo({
  accountStatus: 'ANY'
});

chrome.permissions

// Check permissions
const hasPermission = await chrome.permissions.contains({
  permissions: ['tabs'],
  origins: ['*://*.example.com/*']
});

// Request permissions (must be user gesture)
const granted = await chrome.permissions.request({
  permissions: ['history'],
  origins: ['*://*.new-site.com/*']
});

// Remove permissions
await chrome.permissions.remove({
  permissions: ['history']
});

// Get all permissions
const all = await chrome.permissions.getAll();

// Listen for changes
chrome.permissions.onAdded.addListener((permissions) => {});
chrome.permissions.onRemoved.addListener((permissions) => {});

chrome.declarativeNetRequest

// Update dynamic rules
await chrome.declarativeNetRequest.updateDynamicRules({
  removeRuleIds: [1],
  addRules: [{
    id: 2,
    priority: 1,
    action: { type: 'block' },
    condition: {
      urlFilter: '*://ads.example.com/*',
      resourceTypes: ['script', 'image']
    }
  }]
});

// Get rules
const rules = await chrome.declarativeNetRequest.getDynamicRules();
const sessionRules = await chrome.declarativeNetRequest.getSessionRules();

// Test rules
const result = await chrome.declarativeNetRequest.testMatchOutcome({
  url: 'https://ads.example.com/script.js',
  type: 'script'
});

// Enable/disable rulesets
await chrome.declarativeNetRequest.updateEnabledRulesets({
  enableRulesetIds: ['ruleset_1'],
  disableRulesetIds: ['ruleset_2']
});