@flxbl-dev/mcp
v0.6.1-beta
Published
FLXBL Model Context Protocol Server - Bridge between LLM-assisted IDEs and FLXBL
Downloads
338
Maintainers
Readme
FLXBL MCP Server
Model Context Protocol (MCP) server for integrating FLXBL with LLM-assisted IDEs like Cursor, Windsurf, Kiro, and Antigravity.
Overview
The FLXBL MCP Server enables AI assistants in your IDE to:
- Understand your schema: Read your current FLXBL schema, entities, fields, and relationships
- Design optimized schemas: Get guidance on FLXBL best practices (graph relationships, field types, naming conventions)
- Publish schema versions: Create new schema versions directly from your IDE
- Monitor migrations: Track the status of schema migrations
- Generate client code: Create TypeScript interfaces, Zod schemas, or Python models from your schema
- Write queries: Get help with FLXBL's Query DSL and GraphQL
Prerequisites
- Node.js 18 or later
- A FLXBL account with an active tenant
- An API key from your FLXBL dashboard
Installation
Global Installation
npm install -g @flxbl-dev/mcpUsing npx (No Installation)
npx @flxbl-dev/mcpConfiguration
The MCP server requires two environment variables:
| Variable | Description | Example |
|----------|-------------|---------|
| FLXBL_INSTANCE_URL | URL of your FLXBL instance | https://api.flxbl.dev |
| FLXBL_API_KEY | Your FLXBL API key | flxbl_ab12cd34_... |
Getting an API Key
- Log in to your FLXBL dashboard
- Navigate to Settings → API Keys
- Click Generate New Key
- Copy the key (it won't be shown again)
IDE Setup
Cursor
Add to your Cursor MCP configuration (~/.cursor/mcp.json):
{
"mcpServers": {
"flxbl": {
"command": "npx",
"args": ["@flxbl-dev/mcp"],
"env": {
"FLXBL_INSTANCE_URL": "https://api.flxbl.dev",
"FLXBL_API_KEY": "your-api-key-here"
}
}
}
}Windsurf
Add to your Windsurf MCP settings:
{
"mcp": {
"servers": {
"flxbl": {
"command": "npx",
"args": ["@flxbl-dev/mcp"],
"env": {
"FLXBL_INSTANCE_URL": "https://api.flxbl.dev",
"FLXBL_API_KEY": "your-api-key-here"
}
}
}
}
}VS Code with Continue
Add to your Continue configuration:
{
"mcpServers": [
{
"name": "flxbl",
"command": "npx",
"args": ["@flxbl-dev/mcp"],
"env": {
"FLXBL_INSTANCE_URL": "https://api.flxbl.dev",
"FLXBL_API_KEY": "your-api-key-here"
}
}
]
}Available Capabilities
Resources (Context)
| Resource URI | Description |
|--------------|-------------|
| flxbl://about | Product information about FLXBL - graph-based BaaS architecture |
| flxbl://tenant | Your tenant context including tenantId (required for GraphQL API) |
| flxbl://schema/active | Current active schema with all entities, fields, and relationships |
| flxbl://context | Formatted schema summary for LLM context (Markdown) |
| flxbl://api/openapi/{schemaId} | OpenAPI 3.0 specification for REST API |
| flxbl://api/graphql | GraphQL SDL schema with usage instructions |
| flxbl://api/context | Unified API context combining OpenAPI, GraphQL SDL, and schema details |
| flxbl://templates | Available schema templates |
| flxbl://identity/config | End-user auth settings (requires identity entity in schema) |
Tools (Actions)
| Tool | Description |
|------|-------------|
| validate_schema | Validate a schema without publishing (dry run) |
| publish_schema_version | Create a new schema version with entities and relationships |
| check_migration_status | Check the status of a running migration by ID |
| generate_client_types | Generate TypeScript/Python types or language-agnostic API contract schema |
| list_schema_templates | Browse available pre-built schema templates |
| get_api_spec | Get OpenAPI 3.0 specification for the active schema (json or summary format) |
| get_graphql_schema | Get GraphQL SDL for the active schema |
generate_client_types Options
| Language | Format | Description |
|----------|--------|-------------|
| typescript | interface | TypeScript interfaces |
| typescript | zod | Zod schemas with full API client |
| typescript | class | TypeScript classes |
| python | pydantic | Pydantic models |
| python | dataclass | Python dataclasses |
| any | schema | Language-agnostic JSON API contract |
The schema format returns a comprehensive JSON structure that includes:
- Entity definitions with field types and constraints
- Complete REST endpoint documentation
- Query DSL operators and examples
- Relationship operation endpoints
- GraphQL endpoint information
- Type mapping for multiple languages (TypeScript, Python, Go, Rust)
Prompts (Guidance)
| Prompt | Description |
|--------|-------------|
| design_flxbl_schema | Guide for designing optimal FLXBL schemas |
| schema_design_wizard | Interactive wizard for step-by-step schema design |
| generate_query | Help writing FLXBL Query DSL or GraphQL |
| query_builder | Interactive wizard for building queries |
Key Concepts
Tenant Context for GraphQL
The GraphQL API requires your tenantId in the URL. Get it from the flxbl://tenant resource:
{
"tenantId": "your-tenant-uuid",
"endpoints": {
"graphql": "/api/v1/dynamic-gql/your-tenant-uuid"
}
}FLXBL Architecture
FLXBL is a graph-relational backend-as-a-service that combines:
- PostgreSQL for schema management and control plane
- Neo4j for data storage and graph traversal
When you define a schema, FLXBL automatically generates REST and GraphQL APIs. Use the flxbl://about resource for detailed architecture information.
Example Workflows
Designing a New Schema
- Open your IDE chat
- Ask: "Help me design a schema for a blog platform with posts, authors, and comments"
- The AI will use the
design_flxbl_schemaprompt to guide you through best practices - Review the suggested schema
- Use
publish_schema_versionto create it in FLXBL
Adding Features to Existing Schema
- The AI reads your current schema via
flxbl://schema/active - Ask: "Add a 'Reviews' feature to my Product entity"
- The AI suggests schema changes using relationships
- Publish the new version with
publish_schema_version - Monitor with
check_migration_status
Generating Client Code
- Ask: "Generate TypeScript interfaces for my schema"
- Use
generate_client_typeswithlanguage: typescript, format: interface - Copy the generated code to your project
Available formats:
# TypeScript interfaces
Generate TypeScript interfaces for my FLXBL schema
# Zod schemas with full API client
Generate Zod schemas for my FLXBL schema
# Python Pydantic models
Generate Pydantic models for my schema
# Python dataclasses
Generate Python dataclasses for my schema
# Language-agnostic API contract (for any language)
Generate API contract schema for my FLXBL schemaThe schema format is especially useful when you need to:
- Generate code in Go, Rust, Java, or other languages
- Create custom API clients
- Understand the full API contract including Query DSL and relationships
Using Generated Types with the Dynamic API
Once you've generated types using the MCP, you'll need to connect your application to the FLXBL Dynamic API.
API Base URL
The Dynamic REST API is available at:
{YOUR_FLXBL_URL}/api/v1/dynamicFor local development:
http://localhost:3001/api/v1/dynamicAuthentication
All requests to the Dynamic API must include your API key in the Authorization header:
Authorization: Bearer your_api_key_hereREST Endpoints
For each entity in your schema, FLXBL automatically generates these endpoints:
Entity CRUD Operations
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/v1/dynamic/{entity} | List all records |
| GET | /api/v1/dynamic/{entity}/:id | Get a record by ID |
| POST | /api/v1/dynamic/{entity} | Create a new record |
| PUT | /api/v1/dynamic/{entity}/:id | Full update (replace) |
| PATCH | /api/v1/dynamic/{entity}/:id | Partial update |
| DELETE | /api/v1/dynamic/{entity}/:id | Delete a record |
| POST | /api/v1/dynamic/{entity}/query | Query with filters (Query DSL) |
Relationship Operations
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/v1/dynamic/{entity}/:id/relationships/{rel} | Create a relationship |
| GET | /api/v1/dynamic/{entity}/:id/relationships/{rel}?direction= | Get relationships |
| PATCH | /api/v1/dynamic/{entity}/:id/relationships/{rel}/:targetId | Update relationship properties |
| DELETE | /api/v1/dynamic/{entity}/:id/relationships/{rel}/:targetId | Delete a relationship |
GraphQL Endpoint
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/v1/dynamic-gql/{tenantId} | GraphQL queries and mutations |
| GET | /api/v1/dynamic-gql/{tenantId}/schema.graphql | Download GraphQL schema |
Identity API (End-User Authentication)
If your schema has an identity entity (isIdentity: true), these endpoints are automatically enabled for your application's end-users:
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/v1/identity/{tenantId}/register | User registration |
| POST | /api/v1/identity/{tenantId}/login | User authentication |
| GET | /api/v1/identity/{tenantId}/me | Get authenticated user profile |
| POST | /api/v1/identity/{tenantId}/verify | Verify email |
| POST | /api/v1/identity/{tenantId}/forgot-password | Request password reset |
| POST | /api/v1/identity/{tenantId}/reset-password | Reset password |
| POST | /api/v1/identity/{tenantId}/refresh | Refresh access token |
| POST | /api/v1/identity/{tenantId}/logout | Revoke refresh token |
Important - Dual-Access Pattern: Identity entities are accessible via BOTH:
- Identity API (
/api/v1/identity/...) - For end-user self-service (registration, login) - Dynamic API (
/api/v1/dynamic/Customer) - For admin operations (list users, bulk updates)
Creating an Identity Entity:
{
"name": "Customer",
"isIdentity": true,
"identifierField": "email",
"fields": [
{ "name": "email", "type": "STRING", "required": true },
{ "name": "password", "type": "PASSWORD", "required": true },
{ "name": "role", "type": "STRING", "required": false }
]
}The PASSWORD field type is automatically hashed on write and redacted in all API responses.
Query DSL
FLXBL supports a powerful JSON-based Query DSL for complex graph queries:
{
"where": {
"age": { "$gte": 18 },
"status": { "$eq": "active" }
},
"orderBy": "createdAt",
"orderDirection": "DESC",
"limit": 10
}Available Operators:
- Comparison:
$eq,$ne,$gt,$gte,$lt,$lte,$in,$nin - Array:
$contains - Logical:
$and,$or
Graph Traversal for Filtering (related data NOT in response):
{
"where": { "name": { "$eq": "Alice" } },
"traverse": [{
"relationship": "WROTE",
"direction": "out",
"where": { "published": { "$eq": true } }
}]
}Graph Traversal for Projection (related data IS included in response):
{
"where": { "name": { "$eq": "Alice" } },
"traverse": [{
"relationship": "WROTE",
"direction": "out",
"include": true,
"limit": 10,
"orderBy": "createdAt",
"orderDirection": "DESC"
}]
}Response: [{ id, name, ..., WROTE: [{ id, title, published, ... }, ...] }]
Important: DELETE Requests
Do NOT include Content-Type: application/json header for DELETE requests without a body. This will cause a 400 error.
Example: TypeScript/React
Important: Entity names in URLs are case-sensitive. Use the exact entity name from your schema (e.g., List not lists).
const API_BASE = 'http://localhost:3001/api/v1/dynamic';
const API_KEY = 'your_api_key_here';
// Headers for requests WITH body
const postHeaders = {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
};
// Headers for requests WITHOUT body (GET, DELETE)
const getHeaders = {
'Authorization': `Bearer ${API_KEY}`,
};
// Fetch all items from an entity (note: entity name is case-sensitive!)
async function getLists(): Promise<List[]> {
const response = await fetch(`${API_BASE}/List`, {
headers: getHeaders, // No Content-Type for GET
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// Create a new item
async function createList(data: CreateListDto): Promise<List> {
const response = await fetch(`${API_BASE}/List`, {
method: 'POST',
headers: postHeaders,
body: JSON.stringify(data),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// Partial update (PATCH)
async function updateListField(id: string, field: string, value: unknown): Promise<List> {
const response = await fetch(`${API_BASE}/List/${id}`, {
method: 'PATCH',
headers: postHeaders,
body: JSON.stringify({ [field]: value }),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
// DELETE - Important: No Content-Type header!
async function deleteList(id: string): Promise<void> {
const response = await fetch(`${API_BASE}/List/${id}`, {
method: 'DELETE',
headers: getHeaders, // No Content-Type for DELETE without body!
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
}
// Query with filters
async function findActiveLists(): Promise<List[]> {
const response = await fetch(`${API_BASE}/List/query`, {
method: 'POST',
headers: postHeaders,
body: JSON.stringify({
where: { status: { $eq: 'active' } },
orderBy: 'createdAt',
orderDirection: 'DESC',
limit: 10
}),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}Example: Python
Important: Entity names in URLs are case-sensitive. Use the exact entity name from your schema.
import requests
API_BASE = 'http://localhost:3001/api/v1/dynamic'
API_KEY = 'your_api_key_here'
# Headers for requests WITH body
post_headers = {
'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'
}
# Headers for requests WITHOUT body (GET, DELETE)
get_headers = {
'Authorization': f'Bearer {API_KEY}'
}
# Fetch all items (note: entity name is case-sensitive!)
response = requests.get(f'{API_BASE}/List', headers=get_headers)
items = response.json()
# Create a new item
new_item = {'name': 'My List', 'description': 'A test list'}
response = requests.post(f'{API_BASE}/List', headers=post_headers, json=new_item)
created = response.json()
# Partial update (PATCH)
updates = {'name': 'Updated Name'}
response = requests.patch(f'{API_BASE}/List/{id}', headers=post_headers, json=updates)
# DELETE - Important: No Content-Type header!
response = requests.delete(f'{API_BASE}/List/{id}', headers=get_headers)
# Query with filters
query = {
'where': {'status': {'$eq': 'active'}},
'orderBy': 'createdAt',
'orderDirection': 'DESC',
'limit': 10
}
response = requests.post(f'{API_BASE}/List/query', headers=post_headers, json=query)
results = response.json()
# Create a relationship
rel_data = {'targetId': 'node_target_id', 'properties': {'role': 'owner'}}
response = requests.post(
f'{API_BASE}/List/{id}/relationships/BELONGS_TO',
headers=post_headers,
json=rel_data
)Zod Format Includes Full API Client
When you generate types with format: zod, the output includes a comprehensive type-safe API client:
import { createFlxblClient } from './schemas';
const client = createFlxblClient({
baseUrl: 'http://localhost:3001',
apiKey: 'your_api_key_here'
});
// Entity CRUD Operations (entity names are case-sensitive!)
const lists = await client.list('List', { limit: 10, orderBy: 'createdAt' });
const list = await client.get('List', 'node_123');
const newList = await client.create('List', { name: 'My List' });
const updated = await client.update('List', 'node_123', { name: 'Updated' });
const patched = await client.patch('List', 'node_123', { status: 'active' });
await client.delete('List', 'node_123');
// Query DSL with type safety
const results = await client.query('List', {
where: { status: { $eq: 'active' } },
orderBy: 'createdAt',
orderDirection: 'DESC',
limit: 10
});
// Graph traversal for FILTERING (find users who wrote published posts)
const filteredUsers = await client.query('User', {
where: { name: { $eq: 'Alice' } },
traverse: [{
relationship: 'WROTE',
direction: 'out',
where: { published: { $eq: true } }
}]
});
// Graph traversal for PROJECTION (find users AND include their posts)
const usersWithPosts = await client.query('User', {
where: { name: { $eq: 'Alice' } },
traverse: [{
relationship: 'WROTE',
direction: 'out',
include: true, // Include related posts in response
limit: 10,
orderBy: 'createdAt',
orderDirection: 'DESC'
}]
});
// Response: [{ id, name, WROTE: [{ id, title, published, ... }, ...] }]
// Relationship operations
await client.createRelationship('User', 'user_id', 'MEMBER_OF', {
targetId: 'group_id',
properties: { role: 'admin', joinedAt: new Date().toISOString() }
});
const relationships = await client.getRelationships('User', 'user_id', 'MEMBER_OF', 'out');
await client.updateRelationship('User', 'user_id', 'MEMBER_OF', 'group_id', {
role: 'owner'
});
await client.deleteRelationship('User', 'user_id', 'MEMBER_OF', 'group_id');Language-Agnostic Schema Format
For generating code in languages other than TypeScript or Python, use the schema format:
Generate API contract schema for my FLXBL schemaThis returns a comprehensive JSON structure that can be used to generate code in any language, including:
- Complete entity and field definitions
- All REST endpoint specifications
- Query DSL documentation with examples
- Type mappings for TypeScript, Python, Go, and Rust
Team Workflow
When working with a team, each team member should have their own API key for IDE integration. This ensures proper audit trails and allows granular permission control.
Setting Up API Keys for Team Members
Tenant Admin Creates API Keys:
- Log in to the FLXBL dashboard as an admin
- Navigate to Settings → API Keys
- Create a dedicated API key for each team member
Recommended Scope Configuration by Role:
| Role | Recommended Scopes | Description | |------|-------------------|-------------| | Developer |
entity:*:read,entity:*:create,entity:*:update| Full read access, create and update data | | Designer |entity:*:read,relationship:*:read| Read-only access to explore schema | | Admin |entity:*:*,relationship:*:*| Full access to all operations | | CI/CD | Specific entity scopes only | Minimal scopes for automation |Team Members Configure Their IDE:
- Each team member adds the MCP configuration to their IDE with their personal API key
- Never share API keys between team members
API Key Scopes and Schema Evolution
When you publish a new schema version that removes or renames entities/relationships, existing API keys may have "stale" scopes that reference the old schema:
- Detection: The API Keys page shows a warning badge next to keys with stale scopes
- Impact: Stale scopes don't grant access to anything—they're simply ignored
- Resolution: Update the API key scopes to match the new schema
Best Practice: Use wildcard scopes (e.g., entity:*:read) when possible. Wildcards automatically adapt to schema changes.
Permission Implications
The MCP server operates with the permissions of the API key used:
| Operation | Required Permission |
|-----------|-------------------|
| Read schema (flxbl://schema/active) | Any valid API key |
| Validate schema (validate_schema) | Any valid API key |
| Publish schema (publish_schema_version) | entity:*:create or admin role |
| Generate types (generate_client_types) | Any valid API key |
| Get API specs (get_api_spec, get_graphql_schema) | Any valid API key |
| Manage access keys (manage_access_keys) | Admin role only |
| Configure identity (configure_identity) | Admin role only |
Best Practices for MCP Key Scopes
- Principle of Least Privilege: Only grant the scopes team members need
- Use Descriptive Names: Name keys by team member and purpose (e.g., "alice-dev-macbook")
- Set Expiration: Use expiration dates for contractor or temporary access
- Regular Audits: Review API keys periodically and revoke unused ones
- Wildcard Scopes: Prefer wildcards for developers who need to work across the whole schema
Security
- API Key Isolation: Your API key only has access to your tenant's data
- No Database Access: The MCP server communicates only via FLXBL's public API
- Key Redaction: API keys are automatically redacted in logs
- Read-Only Resources: Resources only read data; mutations require explicit tool calls
Troubleshooting
"Configuration Error: Missing required environment variables"
Ensure both FLXBL_INSTANCE_URL and FLXBL_API_KEY are set in your MCP configuration.
"Invalid or expired API key"
Your API key may have expired or been revoked. Generate a new one from the FLXBL dashboard.
"Could not verify FLXBL connectivity"
Check that:
- Your
FLXBL_INSTANCE_URLis correct - Your network can reach the FLXBL API
- The FLXBL service is operational
MCP Server Not Starting
Check the logs in your IDE's MCP output panel. Common issues:
- Node.js version too old (requires 18+)
- npm/npx not in PATH
- Missing environment variables
Development
Building from Source
cd mcp
npm install
npm run buildRunning Locally
FLXBL_INSTANCE_URL=http://localhost:3000 \
FLXBL_API_KEY=your-dev-key \
npm startType Checking
npm run typecheckLicense
MIT
Support
- GitHub Issues: flxbl-monolith/issues
- Documentation: docs.flxbl.dev
