#199
feat: new mcp events api with both sep and chatgpt compat
Stacked on #196.
Adds mcp-handler/events, aiming for SEP-3415's "as simple as implementing tools":
// lib/events.ts
import {
connect,
createMcpEvents,
defineEvent,
redisStore,
vercelQueue,
} from "mcp-handler/events";
import { send } from "@vercel/queue";
import { createClient } from "redis";
import { z } from "zod";
import { canReadProject } from "./linear";
const redis = await createClient({ url: process.env.REDIS_URL }).connect();
export const issueCreated = defineEvent({
name: "issue.created",
input: z.strictObject({ projectId: z.string() }),
payload: z.object({ id: z.string(), projectId: z.string() }),
// Checked at subscribe time and before every delivery, with the
// subscriber's Linear token from Vercel Connect.
authorize: connect("linear/acme", (token, { projectId }) =>
canReadProject(token, projectId),
),
});
export const events = createMcpEvents({
events: [issueCreated],
principal: (auth) => String(auth.extra?.sub),
store: redisStore({ client: redis }),
queue: vercelQueue({ topic: "mcp-events", send }),
});// app/mcp/route.ts
import { createMcpHandler, withMcpAuth } from "mcp-handler";
import { events } from "@/lib/events";
import { registerTools, verifyToken } from "@/lib/mcp";
const handler = createMcpHandler(registerTools, { events });
export const POST = withMcpAuth(handler, verifyToken, { required: true });// app/api/issues/route.ts (wherever changes happen)
import { events, issueCreated } from "@/lib/events";
export async function POST(request: Request) {
const issue = await request.json();
await events.emit(issueCreated, issue, { id: issue.id });
return new Response(null, { status: 202 });
}// app/api/mcp-events/route.ts (queue consumer)
import { handleCallback } from "@vercel/queue";
import { vercelQueueConsumer } from "mcp-handler/events";
import { events } from "@/lib/events";
export const POST = vercelQueueConsumer(events, handleCallback);// vercel.json
{
"functions": {
"app/api/mcp-events/route.ts": {
"experimentalTriggers": [{ "type": "queue/v2beta", "topic": "mcp-events" }]
}
}
}The engine stores subscriptions, verifies callbacks, signs and retries deliveries, and drops subscriptions once authorize fails.
Webhook delivery only. Speaks SEP-3415, and falls back to ChatGPT's dialect for clients that don't declare the extension. #196's helpers are now internal.
Activity
14This pull request is a draft
Merging is unavailable until it is marked ready for review.
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
Security
View full report