Skip to main content
Glama

Convex MCP server

Official
by get-convex
http_action.ts7.08 kB
import { httpRouter } from "convex/server"; import { imported } from "./http_no_default"; import { api } from "./_generated/api"; import { httpAction, query } from "./_generated/server"; import { sleep } from "./helpers"; const basic = httpAction( async ({ runQuery, runMutation, runAction }, request: Request) => { const countBefore = await runQuery(api.basic.count); const createdObject = await runMutation(api.basic.insertObject, { foo: BigInt(42), }); const countAfter = await runQuery(api.basic.count); const actionResult = await runAction(api.basic.simpleAction, {}); // TODO: This should really be returning a Response object, but // for now just return some JSON so we can assert the result matches return new Response( JSON.stringify({ requestBody: await request.text(), countBefore, countAfter, actionResult, // Check that we correctly deserialize the return values // from `runMutation` etc. isBigInt: typeof createdObject!.foo === "bigint", }), ); }, ); // Regression test ensuring blob can be passed from fetch Response to action Response. const proxyFetchResponse = httpAction(async (_ctx, _request) => { return await fetch("http://localhost:4545/echo_server", { method: "POST", body: "Hello World", }); }); // Round trip Request -> fetch echo -> Response, // with different ways of passing the body through. const roundTripFetchBlob = httpAction(async (_ctx, request) => { const response = await fetch("http://localhost:4545/echo_server", { method: request.method, body: await request.blob(), }); return new Response(await response.blob()); }); const roundTripFetchText = httpAction(async (_ctx, request) => { const response = await fetch("http://localhost:4545/echo_server", { method: request.method, body: await request.text(), }); return new Response(await response.text()); }); const roundTripFetchArrayBuffer = httpAction(async (_ctx, request) => { const response = await fetch("http://localhost:4545/echo_server", { method: request.method, body: await request.arrayBuffer(), }); return new Response(await response.arrayBuffer()); }); const roundTripFetchJson = httpAction(async (_ctx, request) => { const response = await fetch("http://localhost:4545/echo_server", { method: request.method, body: JSON.stringify(await request.json()), }); return new Response(JSON.stringify(await response.json())); }); const echo = httpAction(async (_ctx, request) => { return new Response(await request.blob()); }); const schedule = httpAction(async ({ scheduler }, _request) => { await scheduler.runAfter(2000, api.basic.insertObject, { foo: "bar" }); return new Response(); }); const streamResponse = httpAction(async (_ctx, _request) => { const encoder = new TextEncoder(); const stream = new ReadableStream({ type: "bytes", start(controller) { controller.enqueue(encoder.encode("<html>")); setTimeout(() => { controller.enqueue(encoder.encode("</html>")); controller.close(); }, 20); }, }); return new Response(stream); }); const streamDanglingResponse = httpAction(async (_ctx, _request) => { const encoder = new TextEncoder(); const stream = new ReadableStream({ type: "bytes", start(controller) { controller.enqueue(encoder.encode("<html>")); }, }); return new Response(stream); }); const slowResponse = httpAction(async (_ctx, _request) => { await new Promise((resolve) => setTimeout(resolve, 900)); return new Response("slow"); }); const http = httpRouter(); http.route({ method: "POST", path: "/basic", handler: basic, }); http.route({ method: "GET", path: "/proxy_response", handler: proxyFetchResponse, }); http.route({ method: "POST", path: "/round_trip_fetch_blob", handler: roundTripFetchBlob, }); http.route({ method: "POST", path: "/round_trip_fetch_text", handler: roundTripFetchText, }); http.route({ method: "POST", path: "/round_trip_fetch_array_buffer", handler: roundTripFetchArrayBuffer, }); http.route({ method: "POST", path: "/round_trip_fetch_json", handler: roundTripFetchJson, }); http.route({ method: "POST", path: "/echo", handler: echo, }); http.route({ method: "GET", path: "/schedule", handler: schedule, }); http.route({ method: "GET", path: "/stream_response", handler: streamResponse, }); http.route({ method: "GET", path: "/stream_dangling_response", handler: streamDanglingResponse, }); http.route({ method: "GET", path: "/slow", handler: slowResponse, }); http.route({ method: "GET", path: "/errorInRun", handler: httpAction(async ({ runQuery }, _request: Request) => { await runQuery(api.http_action.erroringQuery); return new Response("success"); }), }); http.route({ method: "GET", path: "/errorInRunCatch", handler: httpAction(async ({ runQuery }, _request: Request) => { try { await runQuery(api.http_action.erroringQuery); } catch { // do nothing } return new Response("success"); }), }); http.route({ method: "GET", path: "/errorInEndpoint", handler: httpAction(async (_, _request: Request) => { throw new Error("Oh no!"); }), }); http.route({ method: "GET", path: "/imported", handler: imported, }); http.route({ method: "GET", path: "/convexCloudSystemVar", handler: httpAction(async (_, _request: Request) => { return new Response(process.env.CONVEX_CLOUD_URL); }), }); http.route({ method: "GET", path: "/convexSiteSystemVar", handler: httpAction(async (_, _request: Request) => { return new Response(process.env.CONVEX_SITE_URL); }), }); http.route({ method: "POST", path: "/largeResponse", handler: httpAction(async (_, request: Request) => { const size = +(await request.text()); const stringLength = size * 1024 * 1024; return new Response("a".repeat(stringLength)); }), }); http.route({ method: "POST", path: "/streaming", handler: httpAction(async (ctx, request) => { const { errorBeforeResponse, errorWhileStreaming } = await request.json(); if (errorBeforeResponse) { throw new Error("Hit error before response"); } const { readable, writable } = new TransformStream(); const writer = writable.getWriter(); const encoder = new TextEncoder(); const streamData = async () => { for (let i = 0; i < 5; i += 1) { await sleep(200); if (errorWhileStreaming && i === 3) { throw new Error("Hit error while streaming"); } await writer.write(encoder.encode(`Streaming message ${i + 1}`)); } await writer.close(); }; void streamData(); return new Response(readable, { status: 200, headers: { "Content-Type": "text/plain; charset=UTF-8", "Transfer-Encoding": "chunked", }, }); }), }); export const erroringQuery = query(() => { throw new Error("Oh no! Called erroring query"); }); export default http;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server