Skip to main content
Glama

Convex MCP server

Official
by get-convex
example.tsx6.08 kB
/* eslint-disable jsx-a11y/anchor-is-valid */ import { QueryClient, QueryClientProvider, useMutation, useQuery, useSuspenseQuery, } from "@tanstack/react-query"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; import { Authenticated, AuthLoading, ConvexProvider, ConvexReactClient, Unauthenticated, } from "convex/react"; import ReactDOM from "react-dom/client"; import { ConvexQueryClient, convexAction, convexQuery, useConvexMutation, } from "./index.js"; import "./index.css"; import { FormEvent, useState } from "react"; import { api } from "../convex/_generated/api.js"; import { ConvexAuthProvider, useAuthActions } from "@convex-dev/auth/react"; // Build a global convexClient wherever you would normally create a TanStack Query client. const convexClient = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL); const convexQueryClient = new ConvexQueryClient(convexClient); const queryClient = new QueryClient({ defaultOptions: { queries: { // The queryKeyHashFn needs to be set globally: it cannot be specified // in `setQueryData()`, so the client couldn't update the query results. queryKeyHashFn: convexQueryClient.hashFn(), // The queryFn is convenient to set globally to avoid needing to import // the client everywhere. queryFn: convexQueryClient.queryFn(), }, }, }); convexQueryClient.connect(queryClient); function Main() { return ( <ConvexAuthProvider client={convexClient}> <QueryClientProvider client={queryClient}> <AuthLoading> <div>Loading...</div> </AuthLoading> <Unauthenticated> <SignIn /> </Unauthenticated> <Authenticated> <App /> </Authenticated> <ReactQueryDevtools initialIsOpen /> </QueryClientProvider> </ConvexAuthProvider> ); } function SignIn() { const { signIn } = useAuthActions(); const [step, setStep] = useState<"signUp" | "signIn">("signIn"); return ( <div className="signin-container"> <form className="signin-form" onSubmit={(event) => { event.preventDefault(); const formData = new FormData(event.currentTarget); void signIn("password", formData); }} > <input name="email" placeholder="Email" type="text" className="signin-input" /> <input name="password" placeholder="Password" type="password" className="signin-input" /> <input name="flow" type="hidden" value={step} /> <button type="submit"> {step === "signIn" ? "Sign in" : "Sign up"} </button> <button type="button" className="signin-secondary" onClick={() => { setStep(step === "signIn" ? "signUp" : "signIn"); }} > {step === "signIn" ? "Sign up instead" : "Sign in instead"} </button> </form> </div> ); } function Weather() { const { data, isPending, error } = useQuery( // This query doesn't update reactively, it refetches like a normal queryFn. convexAction(api.weather.getSFWeather, {}), ); if (isPending || error) return <span>?</span>; const fetchedAt = new Date(data.fetchedAt); return ( <div className="weather"> It is {data.fahrenheit}° F in San Francisco (fetched at{" "} {fetchedAt.toLocaleString("en-US", { hour: "numeric", minute: "2-digit", second: "2-digit", })} ). </div> ); } function MessageCount() { const [shown, setShown] = useState(true); // This is a conditional query const { data, isPending, error } = useQuery( convexQuery(api.messages.count, shown ? {} : "skip"), ); return ( <div className="message-count"> {isPending ? "? messages" : error ? "error counting messages" : `${data} messages`} <span onClick={() => setShown(!shown)}> {shown ? " (click to disable message count)" : " (click to enable message count)"} </span> </div> ); } function App() { const { signOut } = useAuthActions(); const { data, error, isPending } = useQuery({ // This query updates reactively. ...convexQuery(api.messages.list, {}), initialData: [], }); const { data: user, error: userError, isPending: userIsPending, } = useQuery({ ...convexQuery(api.user.getCurrent, {}), initialData: null, }); const [newMessageText, setNewMessageText] = useState(""); const { mutate, isPending: sending } = useMutation({ mutationFn: useConvexMutation(api.messages.send), }); async function handleSendMessage(event: FormEvent) { event.preventDefault(); if (!sending && newMessageText) { mutate( { body: newMessageText, author: user?._id }, { onSuccess: () => setNewMessageText(""), }, ); } } if (error) { return <div>Error: {error.toString()}</div>; } if (isPending) { return <div>loading...</div>; } return ( <main> <button type="button" onClick={() => void signOut()}> Sign out </button> <h1>Convex Chat</h1> <Weather /> <MessageCount /> <p className="badge"> <span>{user?.email}</span> </p> <ul> {data.map((message) => ( <li key={message._id}> <span>{message.authorEmail}:</span> <span>{message.body}</span> <span>{new Date(message._creationTime).toLocaleTimeString()}</span> </li> ))} </ul> <form onSubmit={handleSendMessage}> <input value={newMessageText} onChange={(event) => setNewMessageText(event.target.value)} placeholder="Write a message…" /> <input type="submit" value="Send" /> </form> </main> ); } const rootElement = document.getElementById("root")!; ReactDOM.createRoot(rootElement).render(<Main />);

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