Skip to content

MCP Security

This guide covers security considerations and best practices for the MCP server.

Create client keys with minimal required scopes:

Terminal window
# Read-only access for AI assistants
fluxbase clientkeys create --name "AI Reader" \
--scopes "read:tables,read:storage"
# Limited write access
fluxbase clientkeys create --name "AI Writer" \
--scopes "read:tables,write:tables"

Service keys bypass Row Level Security and have full access. Use only for:

  • Administrative operations
  • Trusted backend services
  • Development/debugging

Never expose service keys to client applications or AI assistants in untrusted environments.

Each MCP tool requires specific scopes:

# Example: Restrict to read-only operations
mcp:
allowed_tools:
- query_table
- list_objects
- download_object

All database operations respect PostgreSQL RLS policies:

-- Users can only see their own data
CREATE POLICY user_isolation ON public.orders
FOR SELECT
USING (user_id = current_setting('request.jwt.claims')::json->>'sub');

MCP queries execute within the user’s security context.

Restrict available tools in production:

mcp:
allowed_tools:
- query_table # Allow reads
- search_vectors # Allow vector search
# - insert_record # Block writes
# - delete_record # Block deletes

Restrict available resources:

mcp:
allowed_resources:
- "fluxbase://schema/tables"
- "fluxbase://functions"
# Exclude sensitive resources

The MCP server prevents SQL injection through:

  1. Identifier Validation - Table and column names validated against ^[a-zA-Z_][a-zA-Z0-9_]*$
  2. Parameterized Queries - All values use prepared statements
  3. Schema Cache - Table/column existence verified before query execution
  4. Quoting - Identifiers properly quoted and escaped

update_record and delete_record require a filter parameter:

// This will fail - no filter provided
{
"name": "delete_record",
"arguments": {"table": "users"}
}
// This works - filter specified
{
"name": "delete_record",
"arguments": {
"table": "users",
"filter": {"id": "eq.123"}
}
}
  • Maximum 1000 rows per query
  • Maximum 10MB file download
  • Maximum 100 vector search results

Configure per-client rate limits:

mcp:
rate_limit_per_min: 100 # 100 requests per minute per client

MCP operations are logged with:

  • Client key ID/name
  • User ID and role
  • Tool/resource accessed
  • Timestamp

Enable debug logging for detailed traces:

logging:
level: debug

Create dedicated client keys with minimal scopes:

Terminal window
# For a chatbot that only needs to search knowledge base
fluxbase clientkeys create --name "Support Bot" \
--scopes "read:vectors"

Don’t share client keys between applications:

Terminal window
fluxbase clientkeys create --name "Mobile App - Production"
fluxbase clientkeys create --name "Web App - Production"
fluxbase clientkeys create --name "AI Assistant - Production"

Rotate client keys periodically:

Terminal window
# Create new key
fluxbase clientkeys create --name "AI Assistant - 2024-Q2"
# Update application configuration
# Then revoke old key
fluxbase clientkeys delete "old-key-id"

Review MCP access patterns:

SELECT
client_key_name,
COUNT(*) as requests,
DATE_TRUNC('hour', created_at) as hour
FROM auth.audit_log
WHERE path LIKE '/mcp%'
GROUP BY 1, 3
ORDER BY 3 DESC;

Disable unnecessary features in production:

mcp:
enabled: true
allowed_tools:
- query_table
- search_vectors
allowed_resources:
- "fluxbase://schema/tables"
AttackPrevention
SQL InjectionParameterized queries, identifier validation
Unauthorized AccessScope-based access control
Data LeakageRow Level Security
Bulk DeletionMandatory filters
Resource ExhaustionQuery limits, rate limiting
  1. Don’t use service keys for AI assistants - Use scoped client keys instead
  2. Don’t disable RLS - Always use RLS in production
  3. Don’t allow all tools - Whitelist only required tools
  4. Don’t expose internal schemas - Non-admin users can’t see system tables
  5. Don’t skip rate limiting - Configure appropriate limits

Set up alerts for:

  • Unusual query patterns
  • Failed authentication attempts
  • Rate limit violations
  • Access to sensitive tables
-- Example: Alert on failed auth
SELECT COUNT(*)
FROM auth.audit_log
WHERE path LIKE '/mcp%'
AND status_code = 401
AND created_at > NOW() - INTERVAL '1 hour';