feat(mcp): serve an MCP Server Card at /mcp/server-card

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
MaheshtheDev 2026-10-26 20:40:42 +00:00
parent 225fbf0e0c
commit 12f819b1e9
2 changed files with 64 additions and 0 deletions

View file

@ -42,6 +42,28 @@ describe("MCP — transport & auth (raw HTTP)", () => {
expect(body.authorization_servers?.length).toBeGreaterThan(0)
})
it("serves an MCP Server Card at the reserved location", async () => {
const res = await fetch(`${MCP_URL}/server-card`, {
headers: { Accept: "application/mcp-server-card+json" },
})
expect(res.status).toBe(200)
expect(res.headers.get("content-type")).toBe(
"application/mcp-server-card+json",
)
expect(res.headers.get("access-control-allow-origin")).toBe("*")
const card = (await res.json()) as {
$schema?: string
name?: string
remotes?: { type?: string; url?: string }[]
}
expect(card.$schema).toBe(
"https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
)
expect(card.name).toMatch(/^[a-zA-Z0-9.-]+\/[a-zA-Z0-9._-]+$/)
expect(card.remotes?.[0]).toMatchObject({ type: "streamable-http" })
expect(card.remotes?.[0]?.url).toMatch(/\/mcp$/)
})
it("rejects a request with no token (401 + WWW-Authenticate)", async () => {
const res = await fetch(MCP_URL, {
method: "POST",

View file

@ -2,6 +2,7 @@ import type { AuthInfo } from "@modelcontextprotocol/server"
import { createMcpHandler } from "agents/mcp/server"
import { Hono, type Context } from "hono"
import { cors } from "hono/cors"
import { etag } from "hono/etag"
import {
isApiKey,
TransientAuthError,
@ -77,6 +78,47 @@ function resourceMetadata(c: Context<{ Bindings: Bindings }>) {
app.get("/.well-known/oauth-protected-resource", resourceMetadata)
app.get(PROTECTED_RESOURCE_METADATA_PATH, resourceMetadata)
app.get("/mcp/server-card", etag(), (c) => {
const card = {
$schema:
"https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
name: "ai.supermemory/memory",
version: "1.0.0",
title: "Supermemory",
description:
"Save, search, and recall a user's memories and documents across AI conversations.",
websiteUrl: "https://supermemory.ai/docs/supermemory-mcp/mcp",
repository: {
url: "https://github.com/supermemoryai/supermemory",
source: "github",
subfolder: "apps/mcp",
},
icons: [
{
src: "https://supermemory.ai/icon-512.png",
mimeType: "image/png",
sizes: ["512x512"],
},
{
src: "https://supermemory.ai/favicon.svg",
mimeType: "image/svg+xml",
sizes: ["any"],
},
],
remotes: [
{
type: "streamable-http",
url: c.env.MCP_RESOURCE || DEFAULT_MCP_RESOURCE,
},
],
}
return c.body(JSON.stringify(card, null, 2), 200, {
"Content-Type": "application/mcp-server-card+json",
"Cache-Control": "public, max-age=3600",
})
})
app.get("/.well-known/openai-apps-challenge", (c) => {
return c.text(c.env.OPENAI_APPS_CHALLENGE || "")
})