|
| 1 | +--- |
| 2 | +id: 'examples-mcp-server-mcp-lite' |
| 3 | +title: 'Building an MCP Server with mcp-lite' |
| 4 | +description: 'Build and deploy a Model Context Protocol (MCP) server on Supabase Edge Functions using mcp-lite.' |
| 5 | +--- |
| 6 | + |
| 7 | +The [Model Context Protocol](https://modelcontextprotocol.io/introduction) (MCP) enables Large Language Models (LLMs) to interact with external tools and data sources. With `mcp-lite`, you can build lightweight MCP servers that run on Supabase Edge Functions, giving your AI assistants the ability to execute custom tools at the edge. |
| 8 | + |
| 9 | +This guide shows you how to scaffold, develop, and deploy an MCP server using mcp-lite on Supabase Edge Functions. |
| 10 | + |
| 11 | +## What is mcp-lite? |
| 12 | + |
| 13 | +[mcp-lite](https://github.com/fiberplane/mcp-lite) is a lightweight, zero-dependency TypeScript framework for building MCP servers. It works everywhere the Fetch API is available, including Node, Bun, Cloudflare Workers, Deno, and Supabase Edge Functions. |
| 14 | + |
| 15 | +## Why Supabase Edge Functions + mcp-lite? |
| 16 | + |
| 17 | +This combination offers several advantages: |
| 18 | + |
| 19 | +- **Zero cold starts**: Edge Functions stay warm for fast responses |
| 20 | +- **Global distribution**: Deploy once and run everywhere |
| 21 | +- **Direct database access**: Connect directly to your Supabase Postgres |
| 22 | +- **Minimal footprint**: mcp-lite has zero runtime dependencies |
| 23 | +- **Full type safety**: TypeScript support in Deno |
| 24 | +- **Simple deployment**: One command to production |
| 25 | + |
| 26 | +## Prerequisites |
| 27 | + |
| 28 | +You need: |
| 29 | + |
| 30 | +- [Docker](https://docs.docker.com/get-docker/) (to run Supabase locally) |
| 31 | +- [Deno](https://deno.land/) (Supabase Edge Functions runtime) |
| 32 | +- [Supabase CLI](/docs/guides/cli/getting-started) |
| 33 | + |
| 34 | +## Create a new MCP server |
| 35 | + |
| 36 | +Starting with `[email protected]`, you can scaffold a complete MCP server that runs on Supabase Edge Functions: |
| 37 | + |
| 38 | +```bash |
| 39 | +npm create mcp-lite@latest |
| 40 | +``` |
| 41 | + |
| 42 | +When prompted, select **Supabase Edge Functions (MCP server)** from the template options. |
| 43 | + |
| 44 | +The template creates a focused structure for Edge Functions development: |
| 45 | + |
| 46 | +``` |
| 47 | +my-mcp-server/ |
| 48 | +├── supabase/ |
| 49 | +│ ├── config.toml # Minimal Supabase config (Edge Functions only) |
| 50 | +│ └── functions/ |
| 51 | +│ └── mcp-server/ |
| 52 | +│ ├── index.ts # MCP server implementation |
| 53 | +│ └── deno.json # Deno imports and configuration |
| 54 | +├── package.json |
| 55 | +└── tsconfig.json |
| 56 | +``` |
| 57 | + |
| 58 | +## Understanding the project structure |
| 59 | + |
| 60 | +### Minimal config.toml |
| 61 | + |
| 62 | +The template includes a minimal `config.toml` that runs only Edge Functions - no database, storage, or Studio UI. This keeps your local setup lightweight: |
| 63 | + |
| 64 | +```toml |
| 65 | +# Minimal config for running only Edge Functions (no DB, storage, or studio) |
| 66 | +project_id = "starter-mcp-supabase" |
| 67 | + |
| 68 | +[api] |
| 69 | +enabled = true |
| 70 | +port = 54321 |
| 71 | + |
| 72 | +[edge_runtime] |
| 73 | +enabled = true |
| 74 | +policy = "per_worker" |
| 75 | +deno_version = 2 |
| 76 | +``` |
| 77 | + |
| 78 | +You can always add more services as needed. |
| 79 | + |
| 80 | +### Two Hono apps pattern |
| 81 | + |
| 82 | +The template uses a specific pattern required by Supabase Edge Functions: |
| 83 | + |
| 84 | +```ts |
| 85 | +// Root handler - matches the function name |
| 86 | +const app = new Hono() |
| 87 | + |
| 88 | +// MCP protocol handler |
| 89 | +const mcpApp = new Hono() |
| 90 | + |
| 91 | +mcpApp.get('/', (c) => { |
| 92 | + return c.json({ |
| 93 | + message: 'MCP Server on Supabase Edge Functions', |
| 94 | + endpoints: { |
| 95 | + mcp: '/mcp', |
| 96 | + health: '/health', |
| 97 | + }, |
| 98 | + }) |
| 99 | +}) |
| 100 | + |
| 101 | +mcpApp.all('/mcp', async (c) => { |
| 102 | + const response = await httpHandler(c.req.raw) |
| 103 | + return response |
| 104 | +}) |
| 105 | + |
| 106 | +// Mount at /mcp-server (the function name) |
| 107 | +app.route('/mcp-server', mcpApp) |
| 108 | +``` |
| 109 | + |
| 110 | +This is required because Supabase routes all requests to `/<function-name>/*`. The outer `app` handles the function-level routing, while `mcpApp` handles your actual MCP endpoints. |
| 111 | + |
| 112 | +### Deno import maps |
| 113 | + |
| 114 | +The template uses Deno's import maps in `deno.json` to manage dependencies: |
| 115 | + |
| 116 | +```json |
| 117 | +{ |
| 118 | + "compilerOptions": { |
| 119 | + "lib": ["deno.window", "deno.ns"], |
| 120 | + "strict": true |
| 121 | + }, |
| 122 | + "imports": { |
| 123 | + "hono": "npm:hono@^4.6.14", |
| 124 | + "mcp-lite": "npm:[email protected]", |
| 125 | + "zod": "npm:zod@^4.1.12" |
| 126 | + } |
| 127 | +} |
| 128 | +``` |
| 129 | + |
| 130 | +This gives you npm package access while staying in the Deno ecosystem. |
| 131 | + |
| 132 | +## Local development |
| 133 | + |
| 134 | +### Start Supabase |
| 135 | + |
| 136 | +Navigate to your project directory and start Supabase services: |
| 137 | + |
| 138 | +```bash |
| 139 | +supabase start |
| 140 | +``` |
| 141 | + |
| 142 | +### Serve your function |
| 143 | + |
| 144 | +In a separate terminal, serve your MCP function locally: |
| 145 | + |
| 146 | +```bash |
| 147 | +supabase functions serve --no-verify-jwt mcp-server |
| 148 | +``` |
| 149 | + |
| 150 | +Or use the npm script (which runs the same command): |
| 151 | + |
| 152 | +```bash |
| 153 | +npm run dev |
| 154 | +``` |
| 155 | + |
| 156 | +Your MCP server is available at: |
| 157 | + |
| 158 | +``` |
| 159 | +http://localhost:54321/functions/v1/mcp-server/mcp |
| 160 | +``` |
| 161 | + |
| 162 | +### Testing your server |
| 163 | + |
| 164 | +Test the MCP server by adding it to your Claude Code, Claude Desktop, Cursor, or your preferred MCP client. |
| 165 | + |
| 166 | +Using Claude Code: |
| 167 | + |
| 168 | +```bash |
| 169 | +claude mcp add my-mcp-server -t http http://localhost:54321/functions/v1/mcp-server/mcp |
| 170 | +``` |
| 171 | + |
| 172 | +You can also test it using the MCP inspector: |
| 173 | + |
| 174 | +```bash |
| 175 | +npx @modelcontextprotocol/inspector |
| 176 | +``` |
| 177 | + |
| 178 | +Then add the MCP endpoint URL in the inspector UI. |
| 179 | + |
| 180 | +## How it works |
| 181 | + |
| 182 | +The MCP server setup is straightforward: |
| 183 | + |
| 184 | +```ts |
| 185 | +import { McpServer, StreamableHttpTransport } from 'mcp-lite' |
| 186 | +import { z } from 'zod' |
| 187 | + |
| 188 | +// Create MCP server instance |
| 189 | +const mcp = new McpServer({ |
| 190 | + name: 'starter-mcp-supabase-server', |
| 191 | + version: '1.0.0', |
| 192 | + schemaAdapter: (schema) => z.toJSONSchema(schema as z.ZodType), |
| 193 | +}) |
| 194 | + |
| 195 | +// Define a tool |
| 196 | +mcp.tool('sum', { |
| 197 | + description: 'Adds two numbers together', |
| 198 | + inputSchema: z.object({ |
| 199 | + a: z.number(), |
| 200 | + b: z.number(), |
| 201 | + }), |
| 202 | + handler: (args: { a: number; b: number }) => ({ |
| 203 | + content: [{ type: 'text', text: String(args.a + args.b) }], |
| 204 | + }), |
| 205 | +}) |
| 206 | + |
| 207 | +// Bind to HTTP transport |
| 208 | +const transport = new StreamableHttpTransport() |
| 209 | +const httpHandler = transport.bind(mcp) |
| 210 | +``` |
| 211 | + |
| 212 | +## Adding more tools |
| 213 | + |
| 214 | +Extend your MCP server by adding tools directly to the `mcp` instance. Here's an example of adding a database search tool: |
| 215 | + |
| 216 | +```ts |
| 217 | +mcp.tool('searchDatabase', { |
| 218 | + description: 'Search your Supabase database', |
| 219 | + inputSchema: z.object({ |
| 220 | + table: z.string(), |
| 221 | + query: z.string(), |
| 222 | + }), |
| 223 | + handler: async (args) => { |
| 224 | + // Access Supabase client here |
| 225 | + // const { data } = await supabase.from(args.table).select('*') |
| 226 | + return { |
| 227 | + content: [{ type: 'text', text: `Searching ${args.table}...` }], |
| 228 | + } |
| 229 | + }, |
| 230 | +}) |
| 231 | +``` |
| 232 | + |
| 233 | +You can add tools that: |
| 234 | + |
| 235 | +- Query your Supabase database |
| 236 | +- Access Supabase Storage for file operations |
| 237 | +- Call external APIs |
| 238 | +- Process data with custom logic |
| 239 | +- Integrate with other Supabase features |
| 240 | + |
| 241 | +## Deploy to production |
| 242 | + |
| 243 | +When ready, deploy to Supabase's global edge network: |
| 244 | + |
| 245 | +```bash |
| 246 | +supabase functions deploy --no-verify-jwt mcp-server |
| 247 | +``` |
| 248 | + |
| 249 | +Or use the npm script: |
| 250 | + |
| 251 | +```bash |
| 252 | +npm run deploy |
| 253 | +``` |
| 254 | + |
| 255 | +Your MCP server will be live at: |
| 256 | + |
| 257 | +``` |
| 258 | +https://your-project-ref.supabase.co/functions/v1/mcp-server/mcp |
| 259 | +``` |
| 260 | + |
| 261 | +## Authentication considerations |
| 262 | + |
| 263 | +<Admonition type="caution"> |
| 264 | + |
| 265 | +The template uses `--no-verify-jwt` for quick development. This means authentication is not enforced by Supabase's JWT layer. |
| 266 | + |
| 267 | +For production, you should implement authentication at the MCP server level following the [MCP Authorization specification](https://modelcontextprotocol.io/specification/draft/basic/authorization). This gives you control over who can access your MCP tools. |
| 268 | + |
| 269 | +</Admonition> |
| 270 | + |
| 271 | +### Security best practices |
| 272 | + |
| 273 | +When deploying MCP servers: |
| 274 | + |
| 275 | +- **Don't expose sensitive data**: Use the server in development environments with non-production data |
| 276 | +- **Implement authentication**: Add proper authentication for production deployments |
| 277 | +- **Validate inputs**: Always validate and sanitize tool inputs |
| 278 | +- **Limit tool scope**: Only expose tools that are necessary for your use case |
| 279 | +- **Monitor usage**: Track tool calls and monitor for unusual activity |
| 280 | + |
| 281 | +For more security guidance, see the [MCP security guide](/guides/getting-started/mcp#security-risks). |
| 282 | + |
| 283 | +## What's next |
| 284 | + |
| 285 | +With your MCP server running on Supabase Edge Functions, you can: |
| 286 | + |
| 287 | +- Connect it to your Supabase database for data-driven tools |
| 288 | +- Use Supabase Auth to secure your endpoints |
| 289 | +- Access Supabase Storage for file operations |
| 290 | +- Deploy to multiple regions automatically |
| 291 | +- Scale to handle production traffic |
| 292 | +- Integrate with AI assistants like Claude, Cursor, or custom MCP clients |
| 293 | + |
| 294 | +## Resources |
| 295 | + |
| 296 | +- [mcp-lite on GitHub](https://github.com/fiberplane/mcp-lite) |
| 297 | +- [Model Context Protocol Spec](https://modelcontextprotocol.io/) |
| 298 | +- [Supabase Edge Functions Docs](/guides/functions) |
| 299 | +- [Deno Runtime Documentation](https://deno.land/) |
| 300 | +- [Fiberplane tutorial](https://blog.fiberplane.com/blog/mcp-lite-supabase-edge-functions/) |
0 commit comments