From cd0fdba61c01e18a6ae2dbf6775cb8dda133804d Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Thu, 23 Jul 2026 08:14:17 +0000 Subject: [PATCH] feat(web): add WebMCP support to c0mpute.com MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register JavaScript tools on document.modelContext so AI agents visiting the site can query the network and plugin catalogue directly, per the WebMCP draft spec (https://webmachinelearning.github.io/webmcp/). Tools: c0mpute_list_plugins, c0mpute_network_status, c0mpute_latest_release, c0mpute_install_command — each backed by an existing public endpoint. Adds a /api/plugins JSON route since the plugin catalogue was build-time only. Feature-detected and degrades to a no-op in agents/browsers that don't implement WebMCP. Co-Authored-By: Claude Opus 4.8 --- apps/web/src/app/api/plugins/route.ts | 33 ++++ apps/web/src/app/layout.tsx | 2 + apps/web/src/components/webmcp-provider.tsx | 166 ++++++++++++++++++++ apps/web/src/types/webmcp.d.ts | 54 +++++++ 4 files changed, 255 insertions(+) create mode 100644 apps/web/src/app/api/plugins/route.ts create mode 100644 apps/web/src/components/webmcp-provider.tsx create mode 100644 apps/web/src/types/webmcp.d.ts diff --git a/apps/web/src/app/api/plugins/route.ts b/apps/web/src/app/api/plugins/route.ts new file mode 100644 index 0000000..f96c0e9 --- /dev/null +++ b/apps/web/src/app/api/plugins/route.ts @@ -0,0 +1,33 @@ +/** + * JSON plugin listing at https://c0mpute.com/api/plugins. + * + * The marketplace page (`/plugins`) renders from `loadAllPlugins()` at build + * time and has no runtime data source. WebMCP tools run client-side, though, so + * they need a fetchable endpoint to enumerate the plugin catalogue for an agent. + * This route exposes the same manifest data as compact JSON. + */ + +import { NextResponse } from "next/server"; +import { loadAllPlugins, tagline, installCommand } from "@/lib/plugins"; + +export function GET() { + const plugins = loadAllPlugins().map((p) => ({ + id: p.id, + name: p.name, + version: p.version, + kind: p.kind, + tagline: tagline(p), + description: p.description, + keywords: p.keywords ?? [], + homepage: p.homepage, + source: p.source, + install_command: installCommand(p), + install_url: `https://c0mpute.com/plugins/${p.id}/install.sh`, + web: `https://c0mpute.com/plugins/${p.id}`, + })); + + return NextResponse.json( + { count: plugins.length, plugins }, + { headers: { "cache-control": "public, max-age=300, s-maxage=300" } }, + ); +} diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 310c647..ebfa2a4 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -2,6 +2,7 @@ import type { Metadata, Viewport } from "next"; import Link from "next/link"; import "./globals.css"; import Script from "next/script"; +import { WebMcpProvider } from "@/components/webmcp-provider"; const SITE = "https://c0mpute.com"; const DESCRIPTION = @@ -43,6 +44,7 @@ export default function RootLayout({ return ( +