Skip to content
AI & Vectors

Fireworks AI

Call Fireworks AI models from Supabase Edge Functions, and store Fireworks embeddings in Postgres for similarity search with pgvector. The Fireworks API is compatible with the OpenAI API, so the OpenAI SDK works with a different base URL and API key.

Prerequisites#

Store your API key#

Edge Functions read the Fireworks API key from a secret named FIREWORKS_API_KEY.

  1. For local development, add the key to supabase/functions/.env:

    echo "FIREWORKS_API_KEY=<your-fireworks-api-key>" >> supabase/functions/.env
  2. For your hosted project, set the secret with the Supabase CLI:

    supabase secrets set FIREWORKS_API_KEY=<your-fireworks-api-key>

For more information about secrets in Edge Functions, see Environment variables.

Generate text#

Create an Edge Function that sends a prompt to a Fireworks chat model and returns the response.

  1. Create the function:

    supabase functions new fireworks-chat
  2. Replace the contents of supabase/functions/fireworks-chat/index.ts with the following code. It points the OpenAI SDK at the Fireworks base URL and calls the glm-5p3-flash model:

    import { withSupabase } from 'npm:@supabase/server@^1'
    import OpenAI from 'npm:openai@^6'
    const fireworks = new OpenAI({
    apiKey: Deno.env.get('FIREWORKS_API_KEY'),
    baseURL: 'https://api.fireworks.ai/inference/v1',
    })
    export default {
    fetch: withSupabase({ auth: 'user' }, async (req) => {
    const { prompt } = await req.json()
    const completion = await fireworks.chat.completions.create({
    model: 'accounts/fireworks/models/glm-5p3-flash',
    messages: [{ role: 'user', content: prompt }],
    })
    return Response.json({ text: completion.choices[0].message.content })
    }),
    }
  3. Deploy the function:

    supabase functions deploy fireworks-chat
  4. Call the function from your app with a signed-in user:

    const { data, error } = await supabase.functions.invoke('fireworks-chat', {
    body: { prompt: 'Explain Row Level Security in one sentence.' },
    })

To use a different model, replace the model value with any model ID from the Fireworks model library. For streaming responses, see Running AI models.

Search with embeddings#

Store text with its Fireworks embedding in a table, then find the rows closest in meaning to a search query. This section uses the fireworks/qwen3-embedding-8b model and requests 1,024-dimension vectors with the dimensions parameter. The table column has to match the dimension you request.

Create the table and search function#

Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.

create extension if not exists vector with schema extensions;
create table documents (
id bigint primary key generated always as identity,
content text not null,
embedding extensions.vector(1024) not null
);
alter table documents enable row level security;
create policy "Authenticated users can read documents"
on documents for select
to authenticated
using (true);
create index on documents using hnsw (embedding extensions.vector_cosine_ops);
create or replace function match_documents(
query_embedding extensions.vector(1024),
match_count int default 5
)
returns table (id bigint, content text, similarity float)
language sql
stable
set search_path = ''
as $$
select
documents.id,
documents.content,
1 - (documents.embedding operator(extensions.<=>) query_embedding) as similarity
from public.documents
order by documents.embedding operator(extensions.<=>) query_embedding
limit match_count;
$$;

The function orders rows by cosine distance. Qwen3 embeddings aren't normalized, and cosine distance normalizes the vectors for you.

The function runs with the caller's permissions, so the select policy decides which rows a user can search.

Store embeddings#

Create an Edge Function that embeds text and inserts it into the documents table. Only callers that send your project's secret key can reach it, so call it from a backend job or another Edge Function.

  1. Create the function:

    supabase functions new fireworks-embed
  2. In supabase/config.toml, turn off the platform JWT check for the function:

    [functions.fireworks-embed]
    verify_jwt = false
  3. Replace the contents of supabase/functions/fireworks-embed/index.ts with the following code:

    import { withSupabase } from 'npm:@supabase/server@^1'
    import OpenAI from 'npm:openai@^6'
    const fireworks = new OpenAI({
    apiKey: Deno.env.get('FIREWORKS_API_KEY'),
    baseURL: 'https://api.fireworks.ai/inference/v1',
    })
    export default {
    fetch: withSupabase({ auth: 'secret' }, async (req, ctx) => {
    const { content } = await req.json()
    const response = await fireworks.embeddings.create({
    model: 'fireworks/qwen3-embedding-8b',
    input: content,
    dimensions: 1024,
    })
    const { error } = await ctx.supabaseAdmin
    .from('documents')
    .insert({ content, embedding: JSON.stringify(response.data[0].embedding) })
    if (error) {
    return Response.json({ error: error.message }, { status: 500 })
    }
    return Response.json({ ok: true })
    }),
    }
  4. Deploy the function:

    supabase functions deploy fireworks-embed

To call the function with the secret key from a database trigger or a scheduled job, see Call an Edge Function.

Search documents#

Create an Edge Function that embeds a search query with the same model and dimension, then calls match_documents.

  1. Create the function:

    supabase functions new fireworks-search
  2. Replace the contents of supabase/functions/fireworks-search/index.ts with the following code:

    import { withSupabase } from 'npm:@supabase/server@^1'
    import OpenAI from 'npm:openai@^6'
    const fireworks = new OpenAI({
    apiKey: Deno.env.get('FIREWORKS_API_KEY'),
    baseURL: 'https://api.fireworks.ai/inference/v1',
    })
    export default {
    fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
    const { query } = await req.json()
    const response = await fireworks.embeddings.create({
    model: 'fireworks/qwen3-embedding-8b',
    input: query,
    dimensions: 1024,
    })
    const { data, error } = await ctx.supabase.rpc('match_documents', {
    query_embedding: JSON.stringify(response.data[0].embedding),
    match_count: 5,
    })
    if (error) {
    return Response.json({ error: error.message }, { status: 500 })
    }
    return Response.json({ query, results: data })
    }),
    }
  3. Deploy the function:

    supabase functions deploy fireworks-search
  4. Call the function from your app with a signed-in user:

    const { data, error } = await supabase.functions.invoke('fireworks-search', {
    body: { query: 'How do I secure a table?' },
    })

The response lists the five closest documents, each with a similarity score between -1 and 1. Higher scores mean closer matches.

Expose documents to MCP clients#

Serve the documents table as a remote Model Context Protocol (MCP) server, so clients such as Claude, Cursor, or VS Code can search it. The server embeds each query with the same Fireworks model as Search with embeddings, and every tool call runs as the signed-in user, so the select policy on documents decides what a client reads.

The function composes the authenticated pipeline from Deploy MCP servers. withOAuthProtectedResource() answers MCP client discovery, and withSupabase({ auth: 'user' }) verifies the user's token and scopes the Supabase client to them. Configure the OAuth 2.1 server, dynamic client registration, and a consent screen before you start. For those steps, see Add authentication.

  1. Create the function:

    supabase functions new mcp
  2. In supabase/config.toml, turn off the platform JWT check for the function. The function verifies tokens itself, and the gateway would otherwise reject the unauthenticated discovery request:

    [functions.mcp]
    verify_jwt = false
  3. Replace the contents of supabase/functions/mcp/index.ts with the following code. It reads the FIREWORKS_API_KEY secret from Store your API key and registers one tool to search the table and one to read a single row:

    import { createMcpHandler, McpServer } from 'npm:@modelcontextprotocol/server@^2.0.0'
    import { pipeline } from 'npm:@supabase/middleware@1'
    import { withOAuthProtectedResource, withSupabase } from 'npm:@supabase/server@1'
    import OpenAI from 'npm:openai@^6'
    import { z } from 'npm:zod@^4.3.6'
    const fireworks = new OpenAI({
    apiKey: Deno.env.get('FIREWORKS_API_KEY'),
    baseURL: 'https://api.fireworks.ai/inference/v1',
    })
    Deno.serve(
    pipeline(
    [withOAuthProtectedResource(), withSupabase({ auth: 'user' })],
    async (req, { supabase }) => {
    // A fresh server per request: Edge Functions are stateless
    const handler = createMcpHandler(() => {
    const server = new McpServer({ name: 'documents', version: '0.1.0' })
    server.registerTool(
    'search_document',
    {
    description: 'Search documents by meaning and return the closest matches',
    inputSchema: z.object({
    query: z.string().min(1),
    limit: z.number().int().min(1).max(20).default(5),
    }),
    annotations: { readOnlyHint: true },
    },
    async ({ query, limit }) => {
    const response = await fireworks.embeddings.create({
    model: 'fireworks/qwen3-embedding-8b',
    input: query,
    dimensions: 1024,
    })
    const { data, error } = await supabase.rpc('match_documents', {
    query_embedding: JSON.stringify(response.data[0].embedding),
    match_count: limit,
    })
    if (error) throw new Error(error.message)
    return { content: [{ type: 'text', text: JSON.stringify(data) }] }
    }
    )
    server.registerTool(
    'fetch_document',
    {
    description: 'Fetch the full content of one document by ID',
    inputSchema: z.object({ id: z.number().int() }),
    annotations: { readOnlyHint: true },
    },
    async ({ id }) => {
    const { data, error } = await supabase
    .from('documents')
    .select('id, content')
    .eq('id', id)
    .single()
    if (error) throw new Error(error.message)
    return { content: [{ type: 'text', text: JSON.stringify(data) }] }
    }
    )
    return server
    })
    return handler.fetch(req)
    }
    )
    )
  4. Deploy the function:

    supabase functions deploy mcp
  5. Add the server to an MCP client. For Claude Code:

    claude mcp add --transport http documents \
    https://<your-project-ref>.supabase.co/functions/v1/mcp

Every client takes the same URL and runs the OAuth flow on first connection. For the other clients and where to enter the URL, see Connect from MCP clients.

Resources#