Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3e94295
Fix MV3 service worker crash by shimming ajv/ajv-formats
insane66613 Jan 20, 2026
f2770e4
Update chrome-extension/src/shims/ajv.ts
insane66613 Jan 23, 2026
13c6962
Update package.json
insane66613 Jan 23, 2026
988a0d3
Update chrome-extension/src/shims/ajv-codegen.ts
insane66613 Jan 23, 2026
e0e6658
Enable manual triggering of build-zip workflow
insane66613 Mar 7, 2026
fc018c8
Initial plan
Copilot Mar 7, 2026
0b51943
chore: bump version to v0.6.1
Copilot Mar 7, 2026
9152de1
Merge pull request #1 from insane66613/copilot/update-version-to-v061
insane66613 Mar 7, 2026
1d3cfa4
Add GitHub Actions workflow for releasing extension
insane66613 Mar 7, 2026
f806c7b
Update pnpm version to 9.15.0 in release workflow
insane66613 Mar 7, 2026
44d5e24
Update pnpm version to 9.15.1
insane66613 Mar 7, 2026
3d0cef2
Initial plan
Copilot Mar 7, 2026
05d1e85
Merge pull request #2 from insane66613/copilot/fix-node-version-mismatch
insane66613 Mar 7, 2026
d9c5842
Initial plan
Copilot Mar 7, 2026
994c299
Update release workflow to use Node.js 22.12.0
Copilot Mar 7, 2026
765c8bc
Merge pull request #3 from insane66613/copilot/update-node-version-re…
insane66613 Mar 7, 2026
c81ba75
Update upload-artifact and action-gh-release paths in release.yml
insane66613 Mar 7, 2026
55b6a52
Update release workflow for version tagging and pnpm
insane66613 Mar 7, 2026
a51be3c
Initial plan
Copilot Mar 7, 2026
143178e
Add icon-16.png and remove from .gitignore
Copilot Mar 7, 2026
4a1cc25
Initial plan
Copilot Mar 7, 2026
1a95de7
Fix: unload policy violation, SSE error logging/categorisation, Resiz…
Copilot Mar 7, 2026
d27fb4b
Merge pull request #5 from insane66613/copilot/fix-resizeobserver-issues
insane66613 Mar 7, 2026
3ba6962
Merge pull request #4 from insane66613/copilot/fix-icon-loading-error
insane66613 Mar 7, 2026
e812b9a
Merge tag 'v0.6.2' into fix/mv3-ajv-shims
insane66613 Mar 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-zip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches: [ main, dev ]
pull_request:
workflow_dispatch:

jobs:
build:
Expand Down
55 changes: 55 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: Release Extension

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to release (e.g. v0.6.1)"
required: true
type: string

jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9.15.1

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22.12.0"
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Build extension
run: pnpm build

- name: Create ZIP package
run: pnpm zip

- name: Upload Release Artifact
uses: actions/upload-artifact@v4
with:
name: extension-zip
path: ./dist-zip/*.zip

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.event.inputs.tag || github.ref_name }}
files: ./dist-zip/*.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ MCP_COMMUNICATION_FIXES.md
SESSION_10_COMPLETE.md
TEST_MCP_TOGGLE.md
websocket doc.md
chrome-extension/public/icon-16.png
chrome-extension/public/icon-34 copy.png
chrome-extension/public/icon-34.png
chrome-extension/src/mcpclient/architecture-option-3-plugin-based.md
Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,25 @@ pnpm build
pnpm zip
```

### MV3 CSP (`unsafe-eval`) note

Chrome Manifest V3 forbids `eval` / `new Function()` in the extension service worker. Some transitive dependencies (commonly Ajv via the MCP SDK) can trigger this and crash the background script.

- The background build aliases any `ajv` import (including subpaths) to a CSP-safe stub in `chrome-extension/src/shims/ajv.ts` via `chrome-extension/vite.config.mts`.
- To guard against regressions in bundled output, run:

```bash
pnpm scan:unsafe-eval
```

If `pnpm`/Corepack activation is blocked by permissions on your machine, you can still validate the bundle using npm:

```powershell
npm install --legacy-peer-deps
npm run build
npm run scan:unsafe-eval
Comment thread
insane66613 marked this conversation as resolved.
```

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.
Expand Down
3 changes: 1 addition & 2 deletions chrome-extension/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ const manifest = {
icons: {
128: 'icon-128.png',
34: 'icon-34.png',
16: 'icon-16.png',
},
content_scripts: [
// {
Expand Down Expand Up @@ -168,7 +167,7 @@ const manifest = {
// devtools_page: 'devtools/index.html',
web_accessible_resources: [
{
resources: ['*.js', '*.css', 'content/*.css', '*.svg', 'icon-128.png', 'icon-34.png', 'icon-16.png'],
resources: ['*.js', '*.css', 'content/*.css', '*.svg', 'icon-128.png', 'icon-34.png'],
matches: ['*://*/*'],
},
],
Expand Down
2 changes: 1 addition & 1 deletion chrome-extension/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "chrome-extension",
"version": "0.6.0",
"version": "0.6.1",
"description": "chrome extension - core settings",
"type": "module",
"private": true,
Expand Down
Binary file added chrome-extension/public/icon-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions chrome-extension/src/background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ function categorizeToolError(error: Error): { isConnectionError: boolean; isTool
/connection failed/i,
/transport error/i,
/fetch failed/i,
/failed to fetch/i,
/sse error/i,
];

// Check tool errors first (highest priority)
Expand Down
2 changes: 1 addition & 1 deletion chrome-extension/src/mcpclient/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function setupGlobalClientEventListeners(client: McpClient): void {
});

client.on('client:error', (event) => {
logger.error('[Global Client] Client error:', event);
logger.error('[Global Client] Client error:', event.error);
});
}

Expand Down
74 changes: 74 additions & 0 deletions chrome-extension/src/shims/ajv-codegen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// CSP-safe stub for Ajv internal codegen helpers.
//
// Some packages (notably ajv-formats) import `ajv/dist/compile/codegen` and expect
// helpers like `operators`, `str`, `_`, `or`, and `KeywordCxt` to exist.
//
// In MV3 service workers we must avoid Ajv's real codegen (it can use `new Function()`).
// This module provides just enough surface area to prevent crashes during module init.

export const operators = {
LT: '<',
LTE: '<=',
GT: '>',
GTE: '>=',
EQ: '===',
NEQ: '!==',
} as const;

function interpolate(strings: TemplateStringsArray, exprs: unknown[]): string {
let out = '';
for (let i = 0; i < strings.length; i++) {
out += strings[i] ?? '';
if (i < exprs.length) out += String(exprs[i]);
}
return out;
}

// Ajv's codegen exports `_` and `str` as tagged-template helpers. We return plain strings.
export function _(strings: TemplateStringsArray, ...exprs: unknown[]): string {
return interpolate(strings, exprs);
}

export function str(strings: TemplateStringsArray, ...exprs: unknown[]): string {
return interpolate(strings, exprs);
}

export function or(...parts: unknown[]): string {
return parts.map((p) => String(p)).join(' || ');
}

export function and(...parts: unknown[]): string {
return parts.map((p) => String(p)).join(' && ');
}

export function getProperty(prop: unknown): string {
if (typeof prop === 'string' && /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(prop)) return `.${prop}`;
const safe = String(prop).replace(/[\\'\n\r\u2028\u2029]/g, (ch) => {
switch (ch) {
case '\\':
return '\\\\';
case "'":
return "\\'";
case '\n':
return '\\n';
case '\r':
return '\\r';
case '\u2028':
return '\\u2028';
case '\u2029':
return '\\u2029';
default:
return ch;
}
});
return `['${safe}']`;
}

// Minimal KeywordCxt shape used by ajv-formats at runtime.
export class KeywordCxt {
public $data = false;
public schemaCode: string = '""';
public schema: unknown = undefined;

constructor(_it: unknown, _def: unknown, _keyword: string) {}
}
19 changes: 19 additions & 0 deletions chrome-extension/src/shims/ajv-formats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// CSP-safe stub for `ajv-formats`.
//
// Some dependencies import `ajv-formats` (often via `ajv-formats/dist/formats`).
// The real package expects full Ajv internals/codegen to exist and can crash in MV3.
// This shim ensures those imports are no-ops.

export type AjvLike = {
addFormat?: (name: string, format: unknown) => unknown;
addKeyword?: (keyword: string, definition?: unknown) => unknown;
};

export default function addFormats(ajv: AjvLike, _opts?: unknown): AjvLike {
return ajv;
}

// Common ajv-formats named exports (keep harmless).
export const formats: Record<string, unknown> = {};
export const fullFormats: Record<string, unknown> = {};
export const fastFormats: Record<string, unknown> = {};
60 changes: 60 additions & 0 deletions chrome-extension/src/shims/ajv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// src/shims/ajv.ts

export class Name {
public str: string;

constructor(s: unknown) {
this.str = String(s);
}

toString() {
return this.str;
}
}

export default class Ajv {
constructor(options?: any) {}

// CRITICAL: Return 'this' to allow method chaining (e.g. ajv.addSchema().compile())
addSchema(schema: any, key?: string) {
return this;
}
addKeyword(keyword: string, definition?: any) {
return this;
}
addFormat(name: string, format: any) {
return this;
}

// The actual validator
compile(schema: any) {
Comment thread
insane66613 marked this conversation as resolved.
// In development, warn that this CSP-safe shim bypasses real validation.
if (
typeof process !== 'undefined' &&
process.env &&
process.env.NODE_ENV === 'development'
) {
// eslint-disable-next-line no-console
console.warn(
'[Ajv shim] Using CSP-compatible Ajv shim: all validations will always pass. ' +
'This is intended for the Chrome extension environment and may mask schema errors.',
);
}
// Return a function that always says "Valid" (true)
return (data: any) => true;
}

// Direct validation
validate(schema: any, data: any) {
return true;
}

// Properties
errors = null;
}

// Export dummy codegen helpers just in case something imports them.
export const _ = () => {};
export const str = () => {};
Comment thread
insane66613 marked this conversation as resolved.
export const nil = {};
export const CodeGen = {};
21 changes: 16 additions & 5 deletions chrome-extension/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,22 @@ export default defineConfig({
},
envPrefix: ['VITE_', 'CEB_'],
resolve: {
alias: {
'@root': rootDir,
'@src': srcDir,
'@assets': resolve(srcDir, 'assets'),
},
// NOTE: Vite only supports RegExp aliases via the array form.
alias: [
{ find: '@root', replacement: rootDir },
{ find: '@src', replacement: srcDir },
{ find: '@assets', replacement: resolve(srcDir, 'assets') },
// MV3 CSP: ajv-formats can import deep subpaths (e.g. `ajv-formats/dist/formats`).
// Catch all of them and route to a no-op shim.
{ find: /^ajv-formats(\/.*)?$/, replacement: resolve(srcDir, 'shims', 'ajv-formats.ts') },
// MV3 CSP: some dependencies (often via MCP SDK) may pull Ajv which uses `new Function()`.
// Alias all Ajv imports (including subpaths) to a CSP-safe stub to prevent service worker crashes.
// If upstream removes Ajv, this alias becomes a no-op.
// Ajv internal imports used by ajv-formats expect codegen exports like `operators`, `_`, `str`, etc.
{ find: 'ajv/dist/compile/codegen', replacement: resolve(srcDir, 'shims', 'ajv-codegen.ts') },
// Fallback: route any other Ajv entry/subpath to the main Ajv shim.
{ find: /^ajv(\/.*)?$/, replacement: resolve(srcDir, 'shims', 'ajv.ts') },
],
},
plugins: [
libAssetsPlugin({
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mcp-superassistant",
"version": "0.6.0",
"version": "0.6.1",
"description": "MCP SuperAssistant",
"license": "MIT",
"private": true,
Expand Down Expand Up @@ -33,10 +33,11 @@
"prettier": "turbo prettier --continue -- --cache --cache-location node_modules/.cache/.prettiercache",
"prepare": "husky",
"update-version": "bash bash-scripts/update_version.sh",
"copy_env": "bash bash-scripts/copy_env.sh",
"set-global-env": "bash bash-scripts/set_global_env.sh",
"copy_env": "node scripts/copy-env.mjs",
"set-global-env": "node scripts/set-global-env.mjs",
"postinstall": "pnpm build:eslint && pnpm copy_env",
"module-manager": "pnpm -F module-manager start"
"module-manager": "pnpm -F module-manager start",
"scan:unsafe-eval": "node scripts/scan-unsafe-eval.mjs dist"
},
"dependencies": {
"react": "19.1.0",
Expand Down
4 changes: 3 additions & 1 deletion pages/content/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ function setupSidebarRecovery(): void {
}, 1000); // Check every second

// Clean up when navigating away
window.addEventListener('unload', () => {
// Using 'pagehide' instead of 'unload' to comply with the Permissions-Policy that
// disallows 'unload' handlers (they prevent bfcache usage in modern browsers).
window.addEventListener('pagehide', () => {
clearInterval(recoveryInterval);
});

Expand Down
Loading