Vectrum
Sign In
46 lines · 2.2 KB · TypeScript
import { corsJson, preflight, withCors } from "@/lib/cors";
import { handleMcpPost } from "@/lib/mcp/server";
import { authenticateAccessToken } from "@/lib/oauth";
import { serverOrigin } from "@/lib/request-origin";
import { tokenAgentKey } from "@/lib/actor";
import { authenticateToken } from "@/lib/tokens";

/**
 * The MCP endpoint for agents. Credentials are `Authorization: Bearer …` with an OAuth access token
 * (see lib/oauth.ts), or an API token for agents that can't do OAuth. Without valid credentials the
 * 401 points to the OAuth metadata, which is how a client finds out where to sign the user in.
 */
async function authenticate(request: Request) {
  const secret = /^Bearer\s+(\S+)$/i.exec(request.headers.get("authorization") ?? "")?.[1];
  if (!secret) return null;
  const grant = await authenticateAccessToken(secret);
  if (grant) return grant;
  const token = await authenticateToken(secret);
  // An API token used here is an agent too, named after the token.
  return token ? { user: token.user, scope: token.token, agent: { key: tokenAgentKey(token.token.id), name: token.token.name } } : "invalid";
}

export async function POST(request: Request) {
  const origin = await serverOrigin();
  const auth = await authenticate(request);
  if (!auth || auth === "invalid") {
    const challenge = [
      ...(auth === "invalid" ? ['error="invalid_token"', 'error_description="The access token is invalid or expired"'] : []),
      `resource_metadata="${origin}/.well-known/oauth-protected-resource/mcp"`,
      'scope="read write"',
    ];
    return corsJson({ error: auth ? "invalid_token" : "unauthorized", error_description: "Sign in with OAuth, or send an API token as a bearer token." }, 401, {
      "WWW-Authenticate": `Bearer ${challenge.join(", ")}`,
    });
  }
  return withCors(await handleMcpPost(request, { ...auth, origin }));
}

// Every answer comes back in the POST's response, so there is no event stream to open, and no
// session to end.
const notStreaming = () =>
  withCors(new Response("This MCP server answers every request directly. Send JSON-RPC messages with POST.\n", { status: 405, headers: { Allow: "POST" } }));

export const GET = notStreaming;
export const DELETE = notStreaming;
export const OPTIONS = preflight;