Compare commits

...

4 Commits

Author SHA1 Message Date
Tobin South
73856575d6 Strengthen marketplace validator and remove orphaned test file
- validate-marketplace.ts: check duplicate names and required fields
  (name, description, source) per entry, not just valid JSON
- remove .github/workflows/test-marketplace-check.js: tested a
  checkMarketplaceViolations function that doesn't exist in the PR,
  and was in workflows/ instead of scripts/
2026-03-13 21:34:43 +00:00
Noah Zweben MacBook
7d7f29cf27 Add CI workflow to validate marketplace.json on PRs
Add a GitHub Actions workflow that validates marketplace.json is
well-formed JSON with a plugins array whenever PRs modify it. Includes:
- validate-marketplace.ts: Bun script that parses and validates the JSON
- validate-marketplace.yml: GH Actions workflow triggered on PR changes
- test-marketplace-check.js: Unit tests for the validation logic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 21:33:43 +00:00
Tobin South
b36fd4b753 Add pagerduty to marketplace (#566) 2026-03-11 16:37:54 -07:00
Noah Zweben
bd041495bd update(plugin-json): point to the correct Semgrep plugin directory (#584)
The Semgrep plugin currently does not work correctly when used through
Claude because it is located within a subdirectory of the Semgrep
Marketplace repository. This issue was reported in:
https://github.com/anthropics/claude-plugins-official/issues/450

Previously, this could not be fixed due to a limitation in Claude Code's
handling of plugins located in subdirectories. Support for this was added
with the git-subdir feature, released in v2.1.69:
https://github.com/anthropics/claude-code/issues/30593

A fix for the Semgrep plugin was proposed once this version became the
latest release. Now that v2.1.69+ is available as latest, this PR
implements that fix.

https://claude.ai/code/cse_01RtW9KS12VZNFfWmWY6z9Pu

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-10 15:59:33 -07:00
3 changed files with 108 additions and 2 deletions

View File

@@ -704,11 +704,23 @@
"description": "Semgrep catches security vulnerabilities in real-time and guides Claude to write secure code from the start.",
"category": "security",
"source": {
"source": "url",
"url": "https://github.com/semgrep/mcp-marketplace.git"
"source": "git-subdir",
"url": "https://github.com/semgrep/mcp-marketplace.git",
"path": "plugin"
},
"homepage": "https://github.com/semgrep/mcp-marketplace.git"
},
{
"name": "pagerduty",
"description": "Enhance code quality and security through PagerDuty risk scoring and incident correlation. Score pre-commit diffs against historical incident data and surface deployment risk before you ship.",
"category": "monitoring",
"source": {
"source": "url",
"url": "https://github.com/PagerDuty/claude-code-plugins.git",
"sha": "b16c23e0d790deceaa7a6182616d0e36673f2eae"
},
"homepage": "https://github.com/PagerDuty/claude-code-plugins"
},
{
"name": "postman",
"description": "Full API lifecycle management for Claude Code. Sync collections, generate client code, discover APIs, run tests, create mocks, publish docs, and audit security. Powered by the Postman MCP Server.",

77
.github/scripts/validate-marketplace.ts vendored Normal file
View File

@@ -0,0 +1,77 @@
#!/usr/bin/env bun
/**
* Validates marketplace.json: well-formed JSON, plugins array present,
* each entry has required fields, and no duplicate plugin names.
*
* Usage:
* bun validate-marketplace.ts <path-to-marketplace.json>
*/
import { readFile } from "fs/promises";
async function main() {
const filePath = process.argv[2];
if (!filePath) {
console.error("Usage: validate-marketplace.ts <path-to-marketplace.json>");
process.exit(2);
}
const content = await readFile(filePath, "utf-8");
let parsed: unknown;
try {
parsed = JSON.parse(content);
} catch (err) {
console.error(
`ERROR: ${filePath} is not valid JSON: ${err instanceof Error ? err.message : err}`
);
process.exit(1);
}
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
console.error(`ERROR: ${filePath} must be a JSON object`);
process.exit(1);
}
const marketplace = parsed as Record<string, unknown>;
if (!Array.isArray(marketplace.plugins)) {
console.error(`ERROR: ${filePath} missing "plugins" array`);
process.exit(1);
}
const errors: string[] = [];
const seen = new Set<string>();
const required = ["name", "description", "source"] as const;
marketplace.plugins.forEach((p, i) => {
if (!p || typeof p !== "object") {
errors.push(`plugins[${i}]: must be an object`);
return;
}
const entry = p as Record<string, unknown>;
for (const field of required) {
if (!entry[field]) {
errors.push(`plugins[${i}] (${entry.name ?? "?"}): missing required field "${field}"`);
}
}
if (typeof entry.name === "string") {
if (seen.has(entry.name)) {
errors.push(`plugins[${i}]: duplicate plugin name "${entry.name}"`);
}
seen.add(entry.name);
}
});
if (errors.length) {
console.error(`ERROR: ${filePath} has ${errors.length} validation error(s):`);
for (const e of errors) console.error(` - ${e}`);
process.exit(1);
}
console.log(`OK: ${marketplace.plugins.length} plugins, no duplicates, all required fields present`);
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(2);
});

View File

@@ -0,0 +1,17 @@
name: Validate Marketplace JSON
on:
pull_request:
paths:
- '.claude-plugin/marketplace.json'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Validate marketplace.json
run: bun .github/scripts/validate-marketplace.ts .claude-plugin/marketplace.json