From 8ea30790d7200b726056b6e16dec57c01823a6ca Mon Sep 17 00:00:00 2001 From: Adrien Clerbois Date: Tue, 18 Aug 2026 20:14:21 +0200 Subject: [PATCH 1/2] fix(eng): count plugin MCP servers in both catalogs The README generator ignored MCP servers entirely, and the website generator only counted them when plugin.json declared extensions[com.github.awesome-copilot] .mcpServers. Plugins that ship a plugin-root config file without that manifest entry had their MCP server counted nowhere, and the two catalogs disagreed for plugins that did declare it. Resolve an explicit manifest reference first, then fall back to a plugin-root config file, preferring the spec-named mcp.json over the legacy .mcp.json so counts stay correct while plugins migrate between the two conventions. Corrects awesome-copilot (4 to 5 items) and context-matic (2 to 3 items). No plugin files are modified. --- docs/README.plugins.md | 4 ++-- eng/generate-website-data.mjs | 27 ++++++++++++++++--------- eng/update-readme.mjs | 37 ++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/docs/README.plugins.md b/docs/README.plugins.md index 0d89c85ffc..2b53a0c1b6 100644 --- a/docs/README.plugins.md +++ b/docs/README.plugins.md @@ -35,7 +35,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-plugins) for guidelines on how t | [arch](../plugins/arch/README.md) | Architecture and modernization toolkit: produce a cited architecture document for a locally-cloned repo, and generate a phased modernization plan that auto-runs Documentation mode when needed. | 1 items | architecture, modernization, documentation, migration, onboarding | | [arize-ax](../plugins/arize-ax/README.md) | Arize AX platform skills for LLM observability, evaluation, and optimization. Includes trace export, instrumentation, datasets, experiments, evaluators, AI provider integrations, annotations, prompt optimization, and deep linking to the Arize UI. | 9 items | arize, llm, observability, tracing, evaluation, instrumentation, datasets, experiments, prompt-optimization | | [automate-this](../plugins/automate-this/README.md) | Record your screen doing a manual process, drop the video on your Desktop, and let Copilot CLI analyze it frame-by-frame to build working automation scripts. Supports narrated recordings with audio transcription. | 1 items | automation, screen-recording, workflow, video-analysis, process-automation, scripting, productivity, copilot-cli | -| [awesome-copilot](../plugins/awesome-copilot/README.md) | Meta prompts that help you discover and generate curated GitHub Copilot agents, instructions, prompts, and skills. | 4 items | github-copilot, discovery, meta, prompt-engineering, agents | +| [awesome-copilot](../plugins/awesome-copilot/README.md) | Meta prompts that help you discover and generate curated GitHub Copilot agents, instructions, prompts, and skills. | 5 items | github-copilot, discovery, meta, prompt-engineering, agents | | [aws-cloud-development](../plugins/aws-cloud-development/README.md) | Comprehensive AWS cloud development tools including Infrastructure as Code, serverless functions, architecture patterns, and cost optimization for building scalable cloud applications. | 8 items | aws, cloud, infrastructure, cloudformation, terraform, serverless, architecture, devops, cdk | | [azure-cloud-development](../plugins/azure-cloud-development/README.md) | Comprehensive Azure cloud development tools including Infrastructure as Code, serverless functions, architecture patterns, and cost optimization for building scalable cloud applications. | 11 items | azure, cloud, infrastructure, bicep, terraform, serverless, architecture, devops | | [backlog-swipe-triage](../plugins/backlog-swipe-triage/README.md) | Quickly swipe through backlog issues to triage decisions like assign, needs-info, defer, close, or ignore. | 1 items | agent-assignment, backlog-triage, github-issues, issue-prioritization, swipe-interface, workflow-automation | @@ -46,7 +46,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-plugins) for guidelines on how t | [cms-development](../plugins/cms-development/README.md) | Skills for CMS development across themes, plugins, admin tooling, media workflows, markdown rendering, and static export pipelines. | 3 items | cms, content-management-system, wordpress, shopify, drupal, theme, plugin, media, static-site | | [color-orb](../plugins/color-orb/README.md) | A visual orb that users can ask the agent to recolor while showing a live activity log in the canvas. | 1 items | agent-actions, color-picker, interactive-demo, realtime-updates, sse-events, visual-feedback | | [context-engineering](../plugins/context-engineering/README.md) | Tools and techniques for maximizing GitHub Copilot effectiveness through better context management. Includes guidelines for structuring code, an agent for planning multi-file changes, and prompts for context-aware development. | 4 items | context, productivity, refactoring, best-practices, architecture | -| [context-matic](../plugins/context-matic/README.md) | Coding agents hallucinate APIs. ContextMatic gives them curated, versioned API and SDK docs. Ask your agent to "integrate the payments API" and it guesses — falling back on outdated training data and generic patterns that don't match your actual SDK. ContextMatic solves this by giving the agent deterministic, version-aware, SDK-native context at the exact moment it's needed. | 2 items | api-context, api-integration, mcp, sdk, apimatic, third-party-apis, sdks | +| [context-matic](../plugins/context-matic/README.md) | Coding agents hallucinate APIs. ContextMatic gives them curated, versioned API and SDK docs. Ask your agent to "integrate the payments API" and it guesses — falling back on outdated training data and generic patterns that don't match your actual SDK. ContextMatic solves this by giving the agent deterministic, version-aware, SDK-native context at the exact moment it's needed. | 3 items | api-context, api-integration, mcp, sdk, apimatic, third-party-apis, sdks | | [convert-to-md](../plugins/convert-to-md/README.md) | A collection of Copilot skills that convert common document formats into Markdown so their contents can be accurately analyzed, summarized, searched, or extracted from. Just tell Copilot what you need — the right skill is invoked automatically and the conversion happens behind the scenes. | 3 items | skills, configuration, copilot, convert-word-to-md, convert-excel-to-md, convert-pdf-to-md | | [copilot-sdk](../plugins/copilot-sdk/README.md) | Build applications with the GitHub Copilot SDK across multiple programming languages. Includes comprehensive instructions for C#, Go, Node.js/TypeScript, and Python to help you create AI-powered applications. | 1 items | copilot-sdk, sdk, csharp, go, nodejs, typescript, python, ai, github-copilot | | [csharp-dotnet-development](../plugins/csharp-dotnet-development/README.md) | Essential prompts, instructions, and chat modes for C# and .NET development including testing, documentation, and best practices. | 9 items | csharp, dotnet, aspnet, testing | diff --git a/eng/generate-website-data.mjs b/eng/generate-website-data.mjs index 57f058ec37..e07a930cd8 100755 --- a/eng/generate-website-data.mjs +++ b/eng/generate-website-data.mjs @@ -591,25 +591,34 @@ function generatePluginsData(gitDates, resourceIndex = {}) { ]; }); - // Parse mcpServers: supports a path to a .mcp.json file or an inline object + // Parse mcpServers: an explicit manifest reference (path or inline object) + // takes precedence, otherwise fall back to the spec-named mcp.json at the + // plugin root, preferring the spec-named mcp.json over the legacy .mcp.json. const mcpItems = []; - if (composition.mcpServers) { + { let mcpServersObj = null; let mcpConfigPath = relPath; - if (typeof composition.mcpServers === "string") { - const manifestMcpPath = composition.mcpServers.replace(/^\.\//, ""); - mcpConfigPath = manifestMcpPath ? `${relPath}/${manifestMcpPath}` : relPath; - const mcpJsonPath = path.join(pluginDir, manifestMcpPath); - if (fs.existsSync(mcpJsonPath)) { + if (typeof composition.mcpServers === "object" && composition.mcpServers !== null) { + mcpServersObj = composition.mcpServers; + } else { + const candidates = + typeof composition.mcpServers === "string" + ? [composition.mcpServers.replace(/^\.\//, "")] + : ["mcp.json", ".mcp.json"]; + for (const candidate of candidates) { + const mcpJsonPath = path.join(pluginDir, candidate); + if (!fs.existsSync(mcpJsonPath)) { + continue; + } try { const mcpJson = JSON.parse(fs.readFileSync(mcpJsonPath, "utf-8")); mcpServersObj = mcpJson.mcpServers || mcpJson; + mcpConfigPath = `${relPath}/${candidate}`; + break; } catch { // ignore parse errors } } - } else if (typeof composition.mcpServers === "object") { - mcpServersObj = composition.mcpServers; } if (mcpServersObj) { for (const serverName of Object.keys(mcpServersObj)) { diff --git a/eng/update-readme.mjs b/eng/update-readme.mjs index 821364c583..d49b0e003c 100644 --- a/eng/update-readme.mjs +++ b/eng/update-readme.mjs @@ -779,6 +779,40 @@ function generatePluginsSection(pluginsDir) { return ""; } + // Count configured MCP servers, mirroring generate-website-data.mjs so the + // README and website catalogs report the same item count. + // Resolves an explicit manifest reference (path or inline object) first, then + // falls back to a plugin-root config file, preferring the spec-named mcp.json + // over the legacy .mcp.json, so counts stay correct under both conventions. + const countMcpServers = (composition, pluginDir) => { + const configured = composition.mcpServers; + + let mcpServersObj = null; + if (typeof configured === "object" && configured !== null) { + mcpServersObj = configured; + } else { + const candidates = + typeof configured === "string" + ? [configured.replace(/^\.\//, "")] + : ["mcp.json", ".mcp.json"]; + for (const candidate of candidates) { + const mcpJsonPath = path.join(pluginDir, candidate); + if (!fs.existsSync(mcpJsonPath)) { + continue; + } + try { + const mcpJson = JSON.parse(fs.readFileSync(mcpJsonPath, "utf-8")); + mcpServersObj = mcpJson.mcpServers || mcpJson; + break; + } catch { + // ignore parse errors + } + } + } + + return mcpServersObj ? Object.keys(mcpServersObj).length : 0; + }; + // Create table header let pluginsContent = "| Name | Description | Items | Tags |\n| ---- | ----------- | ----- | ---- |\n"; @@ -802,7 +836,8 @@ function generatePluginsSection(pluginsDir) { (composition.agents || []).length + (composition.skills || []).length + extensionReferences + - implicitExtension; + implicitExtension + + countMcpServers(composition, entry.pluginDir); const keywords = plugin.keywords ? plugin.keywords.join(", ") : ""; const link = `../plugins/${dir}/README.md`; From 412695ac08fea86a94cb6015e34d75e2a7fa037f Mon Sep 17 00:00:00 2001 From: Adrien Clerbois <50712277+AClerbois@users.noreply.github.com> Date: Tue, 18 Aug 2026 20:48:56 +0200 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- eng/generate-website-data.mjs | 9 ++++++--- eng/update-readme.mjs | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/eng/generate-website-data.mjs b/eng/generate-website-data.mjs index e07a930cd8..6aef057ce1 100755 --- a/eng/generate-website-data.mjs +++ b/eng/generate-website-data.mjs @@ -601,10 +601,13 @@ function generatePluginsData(gitDates, resourceIndex = {}) { if (typeof composition.mcpServers === "object" && composition.mcpServers !== null) { mcpServersObj = composition.mcpServers; } else { - const candidates = - typeof composition.mcpServers === "string" + const candidates = [ + ...(typeof composition.mcpServers === "string" ? [composition.mcpServers.replace(/^\.\//, "")] - : ["mcp.json", ".mcp.json"]; + : []), + "mcp.json", + ".mcp.json", + ]; for (const candidate of candidates) { const mcpJsonPath = path.join(pluginDir, candidate); if (!fs.existsSync(mcpJsonPath)) { diff --git a/eng/update-readme.mjs b/eng/update-readme.mjs index d49b0e003c..ade9e9f94d 100644 --- a/eng/update-readme.mjs +++ b/eng/update-readme.mjs @@ -791,10 +791,13 @@ function generatePluginsSection(pluginsDir) { if (typeof configured === "object" && configured !== null) { mcpServersObj = configured; } else { - const candidates = - typeof configured === "string" + const candidates = [ + ...(typeof configured === "string" ? [configured.replace(/^\.\//, "")] - : ["mcp.json", ".mcp.json"]; + : []), + "mcp.json", + ".mcp.json", + ]; for (const candidate of candidates) { const mcpJsonPath = path.join(pluginDir, candidate); if (!fs.existsSync(mcpJsonPath)) {