Integration Guide - Advanced Usage Patterns
Comprehensive guide for integrating Fluorite MCP with development workflows, CI/CD pipelines, and custom applications.
🎯 Quick Links: For detailed real-world scenarios and examples, see Use Cases & Examples Guide
📖 Table of Contents
- Claude Code CLI Integration
- Development Workflow Integration
- CI/CD Pipeline Integration
- IDE and Editor Integration
- Custom Application Integration
- Team Collaboration Patterns
- Enterprise Deployment
- Monitoring and Observability
Claude Code CLI Integration
Basic Setup and Configuration
Standard Installation
# Install Fluorite MCP globally
npm install -g fluorite-mcp
# Add to Claude Code CLI with correct server binary
claude mcp add fluorite-mcp -- fluorite-mcp
# Verify installation
claude mcp list
claude mcp status fluorite
Advanced Configuration
# Custom catalog directory
export FLUORITE_CATALOG_DIR="/path/to/custom/specs"
# Performance optimization
export NODE_OPTIONS="--max-old-space-size=4096"
export FLUORITE_CACHE_TTL="3600"
# Development mode with verbose logging
export FLUORITE_LOG_LEVEL="debug"
export FLUORITE_LOG_FILE="/path/to/fluorite.log"
Natural Language Integration Patterns
Library-Specific Requests
# Automatic specification access
"Create a form component using react-hook-form with Zod validation"
→ Accesses: spec://react-hook-form, spec://zod
"Build a Next.js API route with Prisma and authentication"
→ Accesses: spec://nextjs, spec://prisma, spec://nextauth
"Set up Playwright testing with accessibility checks"
→ Accesses: spec://playwright, spec://playwright-axe-accessibility
Framework-Aware Development
# Next.js development
"Analyze this Next.js component for hydration issues"
→ Uses: static-analysis with nextjs framework rules
"Create a Next.js middleware for authentication"
→ Uses: spike templates + static analysis validation
# React development
"Optimize this React component for performance"
→ Uses: react-specific analysis rules + performance recommendations
Workflow Optimization
Pre-Development Setup
# Project initialization with validation
claude-code "Analyze my project structure and suggest improvements"
# Framework-specific setup
claude-code "Set up TypeScript configuration for Next.js with strict mode"
Active Development
# Real-time validation during development
claude-code "Enable real-time validation for this component"
# Quick code validation
claude-code "Check this hook implementation for React best practices"
Pre-Commit Integration
# Static analysis before commits
claude-code "Run comprehensive static analysis on changed files"
# Spike validation
claude-code "Validate that this spike implementation follows best practices"
Development Workflow Integration
Git Workflow Integration
Pre-Commit Hooks
#!/bin/bash
# .git/hooks/pre-commit
echo "Running Fluorite MCP analysis..."
# Get list of staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx|js|jsx|vue)$')
if [ ! -z "$STAGED_FILES" ]; then
# Run static analysis on staged files
claude-code "Analyze these staged files for issues: $STAGED_FILES"
# Check exit code
if [ $? -ne 0 ]; then
echo "❌ Static analysis found issues. Please fix before committing."
exit 1
fi
fi
echo "✅ Static analysis passed"
Pull Request Validation
# .github/workflows/pr-validation.yml
name: PR Validation with Fluorite MCP
on:
pull_request:
branches: [main, develop]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install Fluorite MCP
run: npm install -g fluorite-mcp
- name: Run Static Analysis
run: |
fluorite-mcp --static-analysis \
--project-path . \
--framework nextjs \
--predict-errors \
--max-issues 50
- name: Validate Spike Templates
run: |
fluorite-mcp --validate-spikes \
--check-integrity \
--verify-dependencies
Code Review Enhancement
Automated Code Review
// scripts/code-review.ts
import { MCPClient } from '@modelcontextprotocol/sdk';
class FluoriteCodeReview {
private client: MCPClient;
constructor() {
this.client = new MCPClient({
command: 'fluorite-mcp',
args: []
});
}
async reviewPullRequest(files: string[]) {
const results = [];
for (const file of files) {
// Static analysis for each file
const analysis = await this.client.callTool('static-analysis', {
targetFiles: [file],
framework: this.detectFramework(file),
predictErrors: true,
autoFix: true
});
results.push({
file,
analysis: analysis.content[0].text,
severity: this.calculateSeverity(analysis)
});
}
return this.generateReviewSummary(results);
}
private detectFramework(file: string): string {
if (file.includes('app/') || file.includes('pages/')) return 'nextjs';
if (file.includes('.vue')) return 'vue';
if (file.includes('.tsx') || file.includes('.jsx')) return 'react';
return 'typescript';
}
private calculateSeverity(analysis: any): 'low' | 'medium' | 'high' {
const text = analysis.content[0].text;
if (text.includes('❌') || text.includes('error')) return 'high';
if (text.includes('⚠️') || text.includes('warning')) return 'medium';
return 'low';
}
private generateReviewSummary(results: any[]): string {
const highSeverity = results.filter(r => r.severity === 'high').length;
const mediumSeverity = results.filter(r => r.severity === 'medium').length;
return `
## 🤖 Automated Code Review Summary
📊 **Analysis Results:**
- Files analyzed: ${results.length}
- High severity issues: ${highSeverity}
- Medium severity issues: ${mediumSeverity}
${results.map(r => `
### 📄 ${r.file}
**Severity:** ${r.severity.toUpperCase()}
${r.analysis}
`).join('\n')}
---
*Generated by Fluorite MCP v${process.env.npm_package_version}*
`.trim();
}
}
// Usage in GitHub Actions or local scripts
const reviewer = new FluoriteCodeReview();
const changedFiles = process.argv.slice(2);
reviewer.reviewPullRequest(changedFiles)
.then(summary => console.log(summary))
.catch(console.error);
CI/CD Pipeline Integration
GitHub Actions Integration
Complete CI/CD Pipeline
# .github/workflows/fluorite-enhanced-ci.yml
name: Enhanced CI with Fluorite MCP
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
env:
NODE_VERSION: '20'
FLUORITE_LOG_LEVEL: 'info'
FLUORITE_CACHE_TTL: '3600'
jobs:
setup:
runs-on: ubuntu-latest
outputs:
changed-files: ${{ steps.changed-files.outputs.files }}
framework: ${{ steps.detect-framework.outputs.framework }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get changed files
id: changed-files
run: |
if [ "${{ github.event_name }}" == "push" ]; then
FILES=$(git diff --name-only ${{ github.event.before }} ${{ github.event.after }})
else
FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
fi
echo "files<<EOF" >> $GITHUB_OUTPUT
echo "$FILES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Detect Framework
id: detect-framework
run: |
if [ -f "next.config.js" ] || [ -f "next.config.ts" ]; then
echo "framework=nextjs" >> $GITHUB_OUTPUT
elif [ -f "vue.config.js" ] || [ -f "vite.config.ts" ]; then
echo "framework=vue" >> $GITHUB_OUTPUT
elif [ -f "package.json" ] && grep -q "react" package.json; then
echo "framework=react" >> $GITHUB_OUTPUT
else
echo "framework=typescript" >> $GITHUB_OUTPUT
fi
static-analysis:
runs-on: ubuntu-latest
needs: setup
if: needs.setup.outputs.changed-files != ''
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Fluorite MCP
run: npm install -g fluorite-mcp
- name: Run Comprehensive Static Analysis
run: |
fluorite-mcp --static-analysis \
--project-path . \
--framework ${{ needs.setup.outputs.framework }} \
--predict-errors true \
--analyze-dependencies true \
--auto-fix true \
--max-issues 100 \
--strict-mode true
- name: Validate Changed Files
if: needs.setup.outputs.changed-files != ''
run: |
echo "${{ needs.setup.outputs.changed-files }}" | while read file; do
if [[ "$file" =~ \.(ts|tsx|js|jsx|vue)$ ]]; then
echo "Validating $file..."
fluorite-mcp --quick-validate \
--file "$file" \
--language "typescript" \
--framework ${{ needs.setup.outputs.framework }}
fi
done
spike-validation:
runs-on: ubuntu-latest
if: contains(github.event.head_commit.message, '[spike]') || contains(github.event.pull_request.title, '[spike]')
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Fluorite MCP
run: npm install -g fluorite-mcp
- name: Discover Applied Spikes
run: |
fluorite-mcp --discover-spikes \
--query "detect applied templates" \
--validate-integrity
- name: Validate Spike Implementation
run: |
# Extract spike IDs from commit message or PR description
SPIKE_IDS=$(echo "${{ github.event.head_commit.message }}" | grep -oP 'spike:\K[^\s]+' || echo "")
if [ ! -z "$SPIKE_IDS" ]; then
for spike_id in $SPIKE_IDS; do
echo "Validating spike: $spike_id"
fluorite-mcp --validate-spike \
--id "$spike_id" \
--check-integrity \
--verify-best-practices
done
fi
performance-analysis:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Fluorite MCP
run: npm install -g fluorite-mcp
- name: Performance Testing
run: |
fluorite-mcp --performance-test
fluorite-mcp --server-metrics
- name: Bundle Analysis
if: needs.setup.outputs.framework == 'nextjs'
run: |
npm run build
fluorite-mcp --static-analysis \
--project-path . \
--framework nextjs \
--focus performance \
--analyze-bundle-size
security-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install Fluorite MCP
run: npm install -g fluorite-mcp
- name: Security Analysis
run: |
fluorite-mcp --static-analysis \
--project-path . \
--framework ${{ needs.setup.outputs.framework }} \
--focus security \
--strict-mode true \
--enabled-rules security-xss-prevention,security-injection,security-auth-patterns
- name: Dependency Security Check
run: |
fluorite-mcp --static-analysis \
--analyze-dependencies true \
--security-scan true
generate-report:
runs-on: ubuntu-latest
needs: [static-analysis, spike-validation, performance-analysis, security-scan]
if: always()
steps:
- name: Generate Analysis Report
run: |
echo "# 📊 Fluorite MCP Analysis Report" > analysis-report.md
echo "" >> analysis-report.md
echo "## Summary" >> analysis-report.md
echo "- Framework: ${{ needs.setup.outputs.framework }}" >> analysis-report.md
echo "- Files analyzed: $(echo '${{ needs.setup.outputs.changed-files }}' | wc -l)" >> analysis-report.md
echo "- Static Analysis: ${{ needs.static-analysis.result }}" >> analysis-report.md
echo "- Spike Validation: ${{ needs.spike-validation.result }}" >> analysis-report.md
echo "- Performance: ${{ needs.performance-analysis.result }}" >> analysis-report.md
echo "- Security: ${{ needs.security-scan.result }}" >> analysis-report.md
- name: Upload Report
uses: actions/upload-artifact@v4
with:
name: fluorite-analysis-report
path: analysis-report.md
Jenkins Integration
Declarative Pipeline
// Jenkinsfile
pipeline {
agent any
environment {
NODE_VERSION = '20'
FLUORITE_LOG_LEVEL = 'info'
FLUORITE_CACHE_DIR = "${WORKSPACE}/.fluorite-cache"
}
stages {
stage('Setup') {
steps {
sh 'node --version'
sh 'npm --version'
sh 'npm install -g fluorite-mcp'
sh 'fluorite-mcp --version'
}
}
stage('Health Check') {
steps {
script {
def healthCheck = sh(
script: 'fluorite-mcp --self-test',
returnStatus: true
)
if (healthCheck != 0) {
error('Fluorite MCP health check failed')
}
}
}
}
stage('Static Analysis') {
parallel {
stage('Framework Analysis') {
steps {
sh '''
fluorite-mcp --static-analysis \
--project-path . \
--framework nextjs \
--predict-errors true \
--strict-mode true
'''
}
}
stage('Security Scan') {
steps {
sh '''
fluorite-mcp --static-analysis \
--focus security \
--enabled-rules security-xss-prevention,security-injection \
--max-issues 50
'''
}
}
stage('Performance Analysis') {
steps {
sh '''
fluorite-mcp --static-analysis \
--focus performance \
--analyze-dependencies true
'''
}
}
}
}
stage('Spike Validation') {
when {
anyOf {
changeset "spikes/**"
changeset "**/*.spike.json"
}
}
steps {
sh '''
fluorite-mcp --discover-spikes \
--validate-integrity \
--check-dependencies
'''
}
}
stage('Performance Testing') {
steps {
sh 'fluorite-mcp --performance-test'
sh 'fluorite-mcp --server-metrics'
}
}
stage('Generate Reports') {
steps {
sh '''
fluorite-mcp --catalog-stats > catalog-report.txt
fluorite-mcp --server-metrics > metrics-report.json
'''
archiveArtifacts artifacts: '*-report.*', fingerprint: true
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'catalog-report.txt',
reportName: 'Fluorite MCP Report'
])
}
}
}
post {
always {
sh 'fluorite-mcp --server-metrics'
}
failure {
emailext(
subject: "Fluorite MCP Analysis Failed: ${env.JOB_NAME} - ${env.BUILD_NUMBER}",
body: "The Fluorite MCP analysis failed. Please check the build logs.",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
success {
slackSend(
channel: '#dev-notifications',
color: 'good',
message: "✅ Fluorite MCP analysis passed for ${env.JOB_NAME} - ${env.BUILD_NUMBER}"
)
}
}
}
IDE and Editor Integration
Visual Studio Code Integration
Extension Development
// vscode-fluorite-extension/src/extension.ts
import * as vscode from 'vscode';
import { MCPClient } from '@modelcontextprotocol/sdk';
export function activate(context: vscode.ExtensionContext) {
const client = new MCPClient({
command: 'fluorite-mcp',
args: []
});
// Real-time validation on save
const onSave = vscode.workspace.onDidSaveTextDocument(async (document) => {
if (document.languageId === 'typescript' || document.languageId === 'javascript') {
await validateDocument(document);
}
});
// Command for manual analysis
const analyzeCommand = vscode.commands.registerCommand(
'fluorite.analyzeFile',
async () => {
const editor = vscode.window.activeTextEditor;
if (editor) {
await validateDocument(editor.document);
}
}
);
// Spike template insertion
const insertSpikeCommand = vscode.commands.registerCommand(
'fluorite.insertSpike',
async () => {
const quickPick = vscode.window.createQuickPick();
quickPick.placeholder = 'Search for spike templates...';
quickPick.onDidChangeValue(async (value) => {
if (value.length > 2) {
const spikes = await client.callTool('discover-spikes', {
query: value,
limit: 10
});
quickPick.items = parseSpikes(spikes);
}
});
quickPick.onDidAccept(async () => {
const selected = quickPick.selectedItems[0];
if (selected) {
await applySpikeTemplate(selected.label);
}
quickPick.dispose();
});
quickPick.show();
}
);
context.subscriptions.push(onSave, analyzeCommand, insertSpikeCommand);
async function validateDocument(document: vscode.TextDocument) {
const diagnostics: vscode.Diagnostic[] = [];
try {
const result = await client.callTool('quick-validate', {
code: document.getText(),
language: document.languageId as any,
fileName: document.fileName,
framework: detectFramework(document.fileName)
});
const issues = parseValidationResult(result);
for (const issue of issues) {
const diagnostic = new vscode.Diagnostic(
new vscode.Range(issue.line - 1, 0, issue.line - 1, 100),
issue.message,
issue.severity === 'error'
? vscode.DiagnosticSeverity.Error
: vscode.DiagnosticSeverity.Warning
);
diagnostics.push(diagnostic);
}
} catch (error) {
console.error('Fluorite MCP validation failed:', error);
}
const collection = vscode.languages.createDiagnosticCollection('fluorite');
collection.set(document.uri, diagnostics);
}
function detectFramework(fileName: string): string {
if (fileName.includes('app/') || fileName.includes('pages/')) return 'nextjs';
if (fileName.endsWith('.vue')) return 'vue';
if (fileName.includes('.tsx') || fileName.includes('.jsx')) return 'react';
return 'typescript';
}
function parseValidationResult(result: any): Array<{line: number, message: string, severity: string}> {
// Parse the validation result text and extract issues
const text = result.content[0].text;
const issues: Array<{line: number, message: string, severity: string}> = [];
const lines = text.split('\n');
for (const line of lines) {
const errorMatch = line.match(/❌.*Line (\d+):\s*(.+)/);
const warningMatch = line.match(/⚠️.*Line (\d+):\s*(.+)/);
if (errorMatch) {
issues.push({
line: parseInt(errorMatch[1]),
message: errorMatch[2],
severity: 'error'
});
} else if (warningMatch) {
issues.push({
line: parseInt(warningMatch[1]),
message: warningMatch[2],
severity: 'warning'
});
}
}
return issues;
}
function parseSpikes(result: any): vscode.QuickPickItem[] {
const text = result.content[0].text;
const items: vscode.QuickPickItem[] = [];
// Parse spike templates from result text
const lines = text.split('\n');
for (const line of lines) {
const match = line.match(/\d+\.\s*🔐\s*([^\s]+)/);
if (match) {
items.push({
label: match[1],
description: line.split('•')[1]?.trim() || ''
});
}
}
return items;
}
async function applySpikeTemplate(templateId: string) {
// Apply spike template to current workspace
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (workspacePath) {
try {
await client.callTool('apply-spike', {
id: templateId,
params: {
app_name: vscode.workspace.name || 'my-app'
}
});
vscode.window.showInformationMessage(
`Spike template '${templateId}' applied successfully!`
);
} catch (error) {
vscode.window.showErrorMessage(
`Failed to apply spike template: ${error}`
);
}
}
}
}
Settings Configuration
// .vscode/settings.json
{
"fluorite.enableRealTimeValidation": true,
"fluorite.framework": "nextjs",
"fluorite.analysisLevel": "strict",
"fluorite.autoFixEnabled": true,
"fluorite.spikeTemplatesPath": "./spikes",
"fluorite.customRules": [
"team-naming-convention",
"custom-accessibility-rules"
],
"fluorite.excludePatterns": [
"**/*.test.ts",
"**/*.spec.ts",
"**/node_modules/**"
]
}
Neovim Integration
Lua Plugin Configuration
-- ~/.config/nvim/lua/fluorite.lua
local M = {}
local uv = vim.loop
local api = vim.api
-- MCP client implementation for Neovim
M.client = {
process = nil,
start = function(self)
if self.process then
return
end
self.process = uv.spawn('fluorite-mcp', {
stdio = {nil, nil, nil}
}, function(code, signal)
print("Fluorite MCP server exited with code " .. code)
self.process = nil
end)
end,
stop = function(self)
if self.process then
self.process:kill()
self.process = nil
end
end,
call_tool = function(self, tool, params, callback)
-- Implementation of MCP tool calling
local request = {
id = math.random(1000000),
method = "tools/call",
params = {
name = tool,
arguments = params
}
}
-- Send request and handle response
-- (Implementation details omitted for brevity)
end
}
-- Real-time validation on buffer write
M.setup = function(opts)
opts = opts or {}
-- Start MCP server
M.client:start()
-- Set up autocommands
local group = api.nvim_create_augroup("FluoriteMCP", { clear = true })
-- Validate on save
api.nvim_create_autocmd("BufWritePost", {
group = group,
pattern = {"*.ts", "*.tsx", "*.js", "*.jsx", "*.vue"},
callback = function()
M.validate_buffer()
end,
})
-- Clean up on exit
api.nvim_create_autocmd("VimLeavePre", {
group = group,
callback = function()
M.client:stop()
end,
})
end
M.validate_buffer = function()
local buf = api.nvim_get_current_buf()
local filename = api.nvim_buf_get_name(buf)
local content = table.concat(api.nvim_buf_get_lines(buf, 0, -1, false), "\n")
M.client:call_tool("quick-validate", {
code = content,
language = M.detect_language(filename),
fileName = filename,
framework = M.detect_framework(filename)
}, function(result)
M.display_diagnostics(result)
end)
end
M.detect_language = function(filename)
if filename:match("%.tsx$") then return "tsx"
elseif filename:match("%.ts$") then return "typescript"
elseif filename:match("%.jsx$") then return "jsx"
elseif filename:match("%.js$") then return "javascript"
elseif filename:match("%.vue$") then return "vue"
else return "typescript" end
end
M.detect_framework = function(filename)
if filename:match("app/") or filename:match("pages/") then return "nextjs"
elseif filename:match("%.vue$") then return "vue"
elseif filename:match("%.tsx$") or filename:match("%.jsx$") then return "react"
else return "typescript" end
end
M.display_diagnostics = function(result)
-- Parse validation result and show diagnostics
local diagnostics = {}
local content = result.content[1].text
for line in content:gmatch("[^\r\n]+") do
local line_num, message = line:match("Line (%d+):%s*(.+)")
if line_num and message then
table.insert(diagnostics, {
lnum = tonumber(line_num) - 1,
col = 0,
text = message,
type = line:match("❌") and "E" or "W"
})
end
end
vim.diagnostic.set(
vim.api.nvim_create_namespace("fluorite"),
0,
diagnostics
)
end
-- Commands
M.commands = {
analyze_file = function()
M.validate_buffer()
end,
discover_spikes = function()
vim.ui.input({ prompt = "Search spike templates: " }, function(query)
if query then
M.client:call_tool("discover-spikes", {
query = query,
limit = 10
}, function(result)
M.show_spike_picker(result)
end)
end
end)
end,
apply_spike = function()
M.commands.discover_spikes()
end
}
M.show_spike_picker = function(result)
local content = result.content[1].text
local spikes = {}
for line in content:gmatch("[^\r\n]+") do
local id = line:match("🔐%s*([^%s]+)")
if id then
table.insert(spikes, id)
end
end
vim.ui.select(spikes, {
prompt = "Select spike template:",
}, function(choice)
if choice then
M.client:call_tool("apply-spike", {
id = choice,
params = {
app_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":t")
}
}, function(result)
print("Spike applied: " .. choice)
end)
end
end)
end
return M
Custom Application Integration
Programmatic Usage
Node.js Application Integration
// fluorite-integration.ts
import { MCPClient } from '@modelcontextprotocol/sdk';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
export class FluoriteIntegration {
private client: MCPClient;
private transport: StdioClientTransport;
constructor() {
this.transport = new StdioClientTransport({
command: 'fluorite-mcp',
args: []
});
this.client = new MCPClient(
{
name: "my-app",
version: "1.0.0"
},
{
capabilities: {
tools: {},
resources: {}
}
}
);
}
async initialize(): Promise<void> {
try {
await this.client.connect(this.transport);
console.log('Fluorite MCP client initialized successfully');
} catch (error) {
console.error('Failed to initialize Fluorite MCP client:', error);
throw error;
}
}
async analyzeProject(projectPath: string, framework?: string): Promise<AnalysisResult> {
try {
const result = await this.client.callTool('static-analysis', {
projectPath,
framework,
predictErrors: true,
analyzeDependencies: true,
autoFix: true
});
return this.parseAnalysisResult(result);
} catch (error) {
console.error('Project analysis failed:', error);
throw error;
}
}
async validateCode(code: string, language: string, framework?: string): Promise<ValidationResult> {
try {
const result = await this.client.callTool('quick-validate', {
code,
language,
framework
});
return this.parseValidationResult(result);
} catch (error) {
console.error('Code validation failed:', error);
throw error;
}
}
async discoverSpikes(query: string): Promise<SpikeTemplate[]> {
try {
const result = await this.client.callTool('discover-spikes', {
query,
limit: 20
});
return this.parseSpikeTemplates(result);
} catch (error) {
console.error('Spike discovery failed:', error);
throw error;
}
}
async autoSelectSpike(task: string, constraints?: Record<string, string>): Promise<SpikeRecommendation> {
try {
const result = await this.client.callTool('auto-spike', {
task,
constraints
});
return this.parseSpikeRecommendation(result);
} catch (error) {
console.error('Auto spike selection failed:', error);
throw error;
}
}
async applySpike(id: string, params?: Record<string, string>, strategy?: ConflictResolutionStrategy): Promise<ApplicationResult> {
try {
const result = await this.client.callTool('apply-spike', {
id,
params,
strategy
});
return this.parseApplicationResult(result);
} catch (error) {
console.error('Spike application failed:', error);
throw error;
}
}
async getHealthStatus(): Promise<HealthStatus> {
try {
const [selfTest, performance, metrics] = await Promise.all([
this.client.callTool('self-test', {}),
this.client.callTool('performance-test', {}),
this.client.callTool('server-metrics', {})
]);
return {
healthy: selfTest.content[0].text.includes('✅'),
performance: performance.content[0].text.includes('✅'),
metrics: JSON.parse(metrics.content[0].text.split('📊 Server Metrics:\n')[1])
};
} catch (error) {
console.error('Health check failed:', error);
return { healthy: false, performance: false, metrics: null };
}
}
async cleanup(): Promise<void> {
try {
await this.client.close();
console.log('Fluorite MCP client closed successfully');
} catch (error) {
console.error('Failed to close Fluorite MCP client:', error);
}
}
private parseAnalysisResult(result: any): AnalysisResult {
const text = result.content[0].text;
const errors = (text.match(/❌/g) || []).length;
const warnings = (text.match(/⚠️/g) || []).length;
const predictions = (text.match(/🔮/g) || []).length;
return {
summary: {
errors,
warnings,
predictions,
filesAnalyzed: this.extractNumber(text, /Files analyzed: (\d+)/)
},
issues: this.extractIssues(text),
predictions: this.extractPredictions(text),
recommendations: this.extractRecommendations(text)
};
}
private parseValidationResult(result: any): ValidationResult {
const text = result.content[0].text;
const isValid = !text.includes('❌');
return {
valid: isValid,
issues: this.extractIssues(text),
suggestions: this.extractSuggestions(text)
};
}
private parseSpikeTemplates(result: any): SpikeTemplate[] {
const text = result.content[0].text;
const templates: SpikeTemplate[] = [];
const lines = text.split('\n');
for (const line of lines) {
const match = line.match(/\d+\.\s*🔐\s*([^\s]+)/);
if (match) {
const parts = line.split('•');
templates.push({
id: match[1],
name: parts[1]?.trim() || '',
description: parts[2]?.trim() || '',
stack: parts[3]?.trim().split(', ') || [],
difficulty: this.extractDifficulty(line)
});
}
}
return templates;
}
private parseSpikeRecommendation(result: any): SpikeRecommendation {
const text = result.content[0].text;
return {
selectedTemplate: this.extractValue(text, /Best Match Selected: ([^\n]+)/),
confidence: this.extractNumber(text, /Match confidence: (\d+)%/) / 100,
reasoning: this.extractSection(text, '📊 Selection Reasoning:', '🏗️'),
nextSteps: this.extractSection(text, '📋 Suggested Next Steps:', '🔗'),
estimatedTime: this.extractValue(text, /Estimated setup time: ([^\n]+)/)
};
}
private parseApplicationResult(result: any): ApplicationResult {
const text = result.content[0].text;
const success = text.includes('✅ Spike Applied Successfully');
return {
success,
filesCreated: this.extractFileList(text),
configuration: this.extractConfiguration(text),
nextSteps: this.extractSection(text, '📋 Next Steps:', '💡')
};
}
// Helper methods
private extractNumber(text: string, pattern: RegExp): number {
const match = text.match(pattern);
return match ? parseInt(match[1]) : 0;
}
private extractValue(text: string, pattern: RegExp): string {
const match = text.match(pattern);
return match ? match[1].trim() : '';
}
private extractSection(text: string, start: string, end: string): string {
const startIndex = text.indexOf(start);
if (startIndex === -1) return '';
const endIndex = text.indexOf(end, startIndex);
const sectionText = endIndex === -1
? text.substring(startIndex + start.length)
: text.substring(startIndex + start.length, endIndex);
return sectionText.trim();
}
private extractIssues(text: string): Issue[] {
const issues: Issue[] = [];
const lines = text.split('\n');
for (const line of lines) {
const errorMatch = line.match(/❌\s*(.+)/);
const warningMatch = line.match(/⚠️\s*(.+)/);
if (errorMatch) {
issues.push({
severity: 'error',
message: errorMatch[1].trim(),
type: 'static-analysis'
});
} else if (warningMatch) {
issues.push({
severity: 'warning',
message: warningMatch[1].trim(),
type: 'static-analysis'
});
}
}
return issues;
}
private extractPredictions(text: string): Prediction[] {
const predictions: Prediction[] = [];
const lines = text.split('\n');
for (const line of lines) {
const match = line.match(/🔮\s*(.+)/);
if (match) {
const prediction = match[1].trim();
const probability = this.extractNumber(prediction, /(\d+)%/);
predictions.push({
type: this.extractValue(prediction, /^([^(]+)/),
probability: probability / 100,
description: prediction
});
}
}
return predictions;
}
private extractRecommendations(text: string): string[] {
const recommendations: string[] = [];
const lines = text.split('\n');
for (const line of lines) {
if (line.includes('💡') || line.includes('✅')) {
recommendations.push(line.replace(/[💡✅]\s*/, '').trim());
}
}
return recommendations;
}
private extractSuggestions(text: string): string[] {
const suggestions: string[] = [];
const lines = text.split('\n');
for (const line of lines) {
if (line.includes('Suggestion') || line.includes('Fix:')) {
suggestions.push(line.trim());
}
}
return suggestions;
}
private extractDifficulty(line: string): 'beginner' | 'intermediate' | 'advanced' {
if (line.includes('Beginner')) return 'beginner';
if (line.includes('Advanced')) return 'advanced';
return 'intermediate';
}
private extractFileList(text: string): string[] {
const files: string[] = [];
const lines = text.split('\n');
for (const line of lines) {
const match = line.match(/[├└].*✅\s*([^\s]+)/);
if (match) {
files.push(match[1]);
}
}
return files;
}
private extractConfiguration(text: string): Record<string, string> {
const config: Record<string, string> = {};
const configSection = this.extractSection(text, '🔧 Configuration Applied:', '📋');
const lines = configSection.split('\n');
for (const line of lines) {
const match = line.match(/•\s*([^:]+):\s*(.+)/);
if (match) {
config[match[1].trim()] = match[2].trim();
}
}
return config;
}
}
// Type definitions
export interface AnalysisResult {
summary: {
errors: number;
warnings: number;
predictions: number;
filesAnalyzed: number;
};
issues: Issue[];
predictions: Prediction[];
recommendations: string[];
}
export interface ValidationResult {
valid: boolean;
issues: Issue[];
suggestions: string[];
}
export interface SpikeTemplate {
id: string;
name: string;
description: string;
stack: string[];
difficulty: 'beginner' | 'intermediate' | 'advanced';
}
export interface SpikeRecommendation {
selectedTemplate: string;
confidence: number;
reasoning: string;
nextSteps: string;
estimatedTime: string;
}
export interface ApplicationResult {
success: boolean;
filesCreated: string[];
configuration: Record<string, string>;
nextSteps: string;
}
export interface Issue {
severity: 'error' | 'warning' | 'info';
message: string;
type: string;
file?: string;
line?: number;
}
export interface Prediction {
type: string;
probability: number;
description: string;
}
export interface HealthStatus {
healthy: boolean;
performance: boolean;
metrics: any;
}
export type ConflictResolutionStrategy = 'overwrite' | 'three_way_merge' | 'abort';
// Usage example
async function example() {
const fluorite = new FluoriteIntegration();
try {
await fluorite.initialize();
// Analyze a project
const analysis = await fluorite.analyzeProject('./my-project', 'nextjs');
console.log('Analysis results:', analysis);
// Discover and apply a spike
const recommendation = await fluorite.autoSelectSpike(
'Create a Next.js app with authentication'
);
console.log('Recommended template:', recommendation.selectedTemplate);
const application = await fluorite.applySpike(
recommendation.selectedTemplate,
{ app_name: 'my-app' }
);
console.log('Application results:', application);
} finally {
await fluorite.cleanup();
}
}
Web Application Integration
Express.js Middleware
// express-fluorite-middleware.ts
import express from 'express';
import { FluoriteIntegration } from './fluorite-integration';
export interface FluoriteMiddlewareOptions {
enableAnalysis?: boolean;
enableValidation?: boolean;
enableSpikes?: boolean;
projectPath?: string;
framework?: string;
}
export function createFluoriteMiddleware(options: FluoriteMiddlewareOptions = {}) {
const fluorite = new FluoriteIntegration();
let initialized = false;
const initializeOnce = async () => {
if (!initialized) {
await fluorite.initialize();
initialized = true;
}
};
const router = express.Router();
// Health check endpoint
router.get('/health', async (req, res) => {
try {
await initializeOnce();
const health = await fluorite.getHealthStatus();
res.json(health);
} catch (error) {
res.status(500).json({ error: 'Health check failed', details: error.message });
}
});
// Static analysis endpoint
if (options.enableAnalysis) {
router.post('/analyze', async (req, res) => {
try {
await initializeOnce();
const { projectPath, framework } = req.body;
const result = await fluorite.analyzeProject(
projectPath || options.projectPath || process.cwd(),
framework || options.framework
);
res.json(result);
} catch (error) {
res.status(500).json({ error: 'Analysis failed', details: error.message });
}
});
}
// Code validation endpoint
if (options.enableValidation) {
router.post('/validate', async (req, res) => {
try {
await initializeOnce();
const { code, language, framework } = req.body;
if (!code || !language) {
return res.status(400).json({
error: 'Missing required fields: code, language'
});
}
const result = await fluorite.validateCode(
code,
language,
framework || options.framework
);
res.json(result);
} catch (error) {
res.status(500).json({ error: 'Validation failed', details: error.message });
}
});
}
// Spike operations endpoints
if (options.enableSpikes) {
router.get('/spikes/discover', async (req, res) => {
try {
await initializeOnce();
const { query } = req.query;
const templates = await fluorite.discoverSpikes(query as string || '');
res.json(templates);
} catch (error) {
res.status(500).json({ error: 'Spike discovery failed', details: error.message });
}
});
router.post('/spikes/auto-select', async (req, res) => {
try {
await initializeOnce();
const { task, constraints } = req.body;
if (!task) {
return res.status(400).json({ error: 'Missing required field: task' });
}
const recommendation = await fluorite.autoSelectSpike(task, constraints);
res.json(recommendation);
} catch (error) {
res.status(500).json({ error: 'Auto selection failed', details: error.message });
}
});
router.post('/spikes/apply', async (req, res) => {
try {
await initializeOnce();
const { id, params, strategy } = req.body;
if (!id) {
return res.status(400).json({ error: 'Missing required field: id' });
}
const result = await fluorite.applySpike(id, params, strategy);
res.json(result);
} catch (error) {
res.status(500).json({ error: 'Spike application failed', details: error.message });
}
});
}
// Error handling middleware
router.use((error: any, req: express.Request, res: express.Response, next: express.NextFunction) => {
console.error('Fluorite middleware error:', error);
res.status(500).json({
error: 'Internal server error',
details: process.env.NODE_ENV === 'development' ? error.message : undefined
});
});
return router;
}
// Usage example
const app = express();
app.use(express.json());
app.use('/api/fluorite', createFluoriteMiddleware({
enableAnalysis: true,
enableValidation: true,
enableSpikes: true,
projectPath: process.cwd(),
framework: 'nextjs'
}));
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
console.log('Fluorite endpoints:');
console.log(' GET /api/fluorite/health');
console.log(' POST /api/fluorite/analyze');
console.log(' POST /api/fluorite/validate');
console.log(' GET /api/fluorite/spikes/discover');
console.log(' POST /api/fluorite/spikes/auto-select');
console.log(' POST /api/fluorite/spikes/apply');
});
Team Collaboration Patterns
Shared Configuration
Team Specifications Repository
# .fluorite/team-config.yml
team:
name: "MyTeam Development"
standards_version: "1.2.0"
custom_specifications:
- name: "team-component-library"
path: "./specs/component-library.yaml"
version: "2.1.0"
- name: "team-api-standards"
path: "./specs/api-standards.yaml"
version: "1.5.0"
validation_rules:
enabled:
- team-naming-convention
- team-file-structure
- team-component-patterns
- team-api-standards
disabled:
- generic-naming-rules
spike_templates:
custom_repository: "https://github.com/myteam/fluorite-templates"
auto_update: true
frameworks:
primary: "nextjs"
supported: ["nextjs", "react", "vue"]
quality_gates:
error_threshold: 0
warning_threshold: 5
performance_threshold: "95%"
integrations:
slack:
webhook_url: "${SLACK_WEBHOOK_URL}"
notify_on: ["analysis_complete", "spike_applied", "errors_found"]
jira:
project_key: "DEV"
auto_create_tickets: true
severity_mapping:
error: "High"
warning: "Medium"
info: "Low"
Team Specification Example
# specs/team-component-library.yaml
name: Team Component Library Standards
version: 2.1.0
description: Internal component library specifications and patterns
category: team-standards
maintainer: frontend-team@company.com
standards:
naming_convention:
pattern: "^[A-Z][a-zA-Z0-9]*Component$"
examples:
- ButtonComponent
- DataTableComponent
- UserProfileComponent
file_structure:
component_directory: "src/components"
test_directory: "src/components/__tests__"
story_directory: "src/components/__stories__"
required_files:
- index.ts
- ComponentName.tsx
- ComponentName.test.tsx
- ComponentName.stories.tsx
- ComponentName.types.ts
props_standards:
typescript_required: true
prop_validation: "zod"
default_props: "object_syntax"
accessibility:
aria_required: true
semantic_html: true
keyboard_navigation: true
testing:
unit_tests: "required"
integration_tests: "recommended"
visual_regression: "required"
accessibility_tests: "required"
examples:
basic_component: |
import React from 'react';
import { z } from 'zod';
const ButtonComponentPropsSchema = z.object({
variant: z.enum(['primary', 'secondary', 'danger']).default('primary'),
size: z.enum(['sm', 'md', 'lg']).default('md'),
disabled: z.boolean().default(false),
children: z.string(),
onClick: z.function().optional()
});
export type ButtonComponentProps = z.infer<typeof ButtonComponentPropsSchema>;
export const ButtonComponent: React.FC<ButtonComponentProps> = ({
variant = 'primary',
size = 'md',
disabled = false,
children,
onClick
}) => {
return (
<button
className={`btn btn-${variant} btn-${size}`}
disabled={disabled}
onClick={onClick}
aria-label={children}
>
{children}
</button>
);
};
test_example: |
import { render, screen, fireEvent } from '@testing-library/react';
import { ButtonComponent } from './ButtonComponent';
describe('ButtonComponent', () => {
it('renders with correct text', () => {
render(<ButtonComponent>Click me</ButtonComponent>);
expect(screen.getByRole('button')).toHaveTextContent('Click me');
});
it('calls onClick when clicked', () => {
const handleClick = jest.fn();
render(<ButtonComponent onClick={handleClick}>Click me</ButtonComponent>);
fireEvent.click(screen.getByRole('button'));
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
best_practices:
- Use TypeScript for all component definitions
- Implement proper prop validation with Zod
- Include comprehensive test coverage
- Follow team naming conventions
- Ensure accessibility compliance
- Document component usage in Storybook
Collaborative Workflows
Code Review Integration
#!/bin/bash
# scripts/team-review.sh
set -e
echo "🔍 Running team code review with Fluorite MCP..."
# Get changed files in PR
CHANGED_FILES=$(git diff --name-only origin/main...HEAD)
echo "Files to review: $CHANGED_FILES"
# Run team-specific analysis
echo "📊 Running team standards analysis..."
fluorite-mcp --static-analysis \
--project-path . \
--framework nextjs \
--custom-rules team-naming-convention,team-component-patterns \
--team-config .fluorite/team-config.yml \
--max-issues 0
# Generate review report
echo "📝 Generating review report..."
REPORT_FILE="review-report-$(date +%Y%m%d-%H%M%S).md"
cat > "$REPORT_FILE" << EOF
# 🔍 Team Code Review Report
## Overview
- **Date**: $(date)
- **Reviewer**: Fluorite MCP Team Standards
- **Files Analyzed**: $(echo "$CHANGED_FILES" | wc -l)
- **Standards Version**: $(grep standards_version .fluorite/team-config.yml | cut -d: -f2 | tr -d ' "')
## Files Reviewed
$(echo "$CHANGED_FILES" | sed 's/^/- /')
## Analysis Results
$(fluorite-mcp --static-analysis --output-format markdown)
## Team Standards Compliance
$(fluorite-mcp --validate-team-standards --output-format markdown)
## Recommendations
$(fluorite-mcp --generate-recommendations --team-context)
---
*Generated by Fluorite MCP Team Integration*
EOF
echo "✅ Review report generated: $REPORT_FILE"
# Post to Slack if configured
if [ ! -z "$SLACK_WEBHOOK_URL" ]; then
echo "📢 Posting to Slack..."
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Code review completed for '"$GITHUB_HEAD_REF"'. See attached report."}' \
"$SLACK_WEBHOOK_URL"
fi
echo "🎉 Team review completed!"
Spike Template Sharing
#!/bin/bash
# scripts/sync-team-templates.sh
TEAM_REPO="https://github.com/myteam/fluorite-templates"
LOCAL_DIR="$HOME/.fluorite/team-templates"
echo "🔄 Syncing team spike templates..."
# Clone or update team templates
if [ -d "$LOCAL_DIR" ]; then
echo "📥 Updating existing templates..."
cd "$LOCAL_DIR"
git pull origin main
else
echo "📦 Cloning team templates..."
git clone "$TEAM_REPO" "$LOCAL_DIR"
fi
# Register templates with Fluorite MCP
echo "📋 Registering templates..."
cd "$LOCAL_DIR"
for template in *.json; do
if [ -f "$template" ]; then
echo " - Registering $template"
fluorite-mcp --register-spike --file "$template" --team-context
fi
done
echo "✅ Team templates synced successfully!"
echo "📊 Available templates:"
fluorite-mcp --list-spikes --team-only
Enterprise Deployment
Production Configuration
Docker Deployment
# Dockerfile.fluorite-mcp
FROM node:20-alpine
# Install system dependencies
RUN apk add --no-cache git curl
# Create app directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Install Fluorite MCP globally
RUN npm install -g fluorite-mcp@latest
# Create non-root user
RUN addgroup -g 1001 -S fluorite && \
adduser -S fluorite -u 1001
# Create directories
RUN mkdir -p /app/catalog /app/cache /app/logs && \
chown -R fluorite:fluorite /app
# Copy application code
COPY . .
# Set environment variables
ENV NODE_ENV=production
ENV FLUORITE_CATALOG_DIR=/app/catalog
ENV FLUORITE_CACHE_DIR=/app/cache
ENV FLUORITE_LOG_DIR=/app/logs
ENV FLUORITE_LOG_LEVEL=info
# Switch to non-root user
USER fluorite
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD fluorite-mcp --self-test || exit 1
# Expose port (if running HTTP interface)
EXPOSE 3000
# Start the MCP server
CMD ["fluorite-mcp"]
Docker Compose Configuration
# docker-compose.yml
version: '3.8'
services:
fluorite-mcp:
build:
context: .
dockerfile: Dockerfile.fluorite-mcp
container_name: fluorite-mcp
restart: unless-stopped
environment:
- NODE_ENV=production
- FLUORITE_CATALOG_DIR=/app/catalog
- FLUORITE_CACHE_DIR=/app/cache
- FLUORITE_LOG_LEVEL=info
- FLUORITE_MAX_MEMORY=4096
volumes:
- ./catalog:/app/catalog:ro
- fluorite-cache:/app/cache
- fluorite-logs:/app/logs
networks:
- fluorite-network
healthcheck:
test: ["CMD", "fluorite-mcp", "--self-test"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
fluorite-web:
build:
context: .
dockerfile: Dockerfile.web
container_name: fluorite-web-interface
restart: unless-stopped
ports:
- "3000:3000"
depends_on:
fluorite-mcp:
condition: service_healthy
environment:
- FLUORITE_MCP_HOST=fluorite-mcp
- FLUORITE_MCP_PORT=8080
networks:
- fluorite-network
fluorite-proxy:
image: nginx:alpine
container_name: fluorite-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/ssl:ro
depends_on:
- fluorite-web
networks:
- fluorite-network
volumes:
fluorite-cache:
driver: local
fluorite-logs:
driver: local
networks:
fluorite-network:
driver: bridge
Kubernetes Deployment
# k8s/fluorite-mcp-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: fluorite-mcp
namespace: development-tools
labels:
app: fluorite-mcp
version: v1.0.0
spec:
replicas: 3
selector:
matchLabels:
app: fluorite-mcp
template:
metadata:
labels:
app: fluorite-mcp
spec:
containers:
- name: fluorite-mcp
image: myregistry/fluorite-mcp:1.0.0
ports:
- containerPort: 8080
name: mcp-port
- containerPort: 3000
name: http-port
env:
- name: NODE_ENV
value: "production"
- name: FLUORITE_CATALOG_DIR
value: "/app/catalog"
- name: FLUORITE_CACHE_DIR
value: "/app/cache"
- name: FLUORITE_LOG_LEVEL
value: "info"
- name: FLUORITE_MAX_MEMORY
value: "4096"
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "4Gi"
cpu: "2000m"
volumeMounts:
- name: catalog-volume
mountPath: /app/catalog
readOnly: true
- name: cache-volume
mountPath: /app/cache
- name: logs-volume
mountPath: /app/logs
livenessProbe:
exec:
command:
- fluorite-mcp
- --self-test
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3
readinessProbe:
exec:
command:
- fluorite-mcp
- --performance-test
initialDelaySeconds: 15
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
volumes:
- name: catalog-volume
configMap:
name: fluorite-catalog
- name: cache-volume
emptyDir:
sizeLimit: "1Gi"
- name: logs-volume
emptyDir:
sizeLimit: "500Mi"
securityContext:
runAsNonRoot: true
runAsUser: 1001
fsGroup: 1001
---
apiVersion: v1
kind: Service
metadata:
name: fluorite-mcp-service
namespace: development-tools
labels:
app: fluorite-mcp
spec:
selector:
app: fluorite-mcp
ports:
- port: 8080
targetPort: 8080
protocol: TCP
name: mcp
- port: 3000
targetPort: 3000
protocol: TCP
name: http
type: ClusterIP
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluorite-catalog
namespace: development-tools
data:
# Catalog specifications as ConfigMap data
# (Specifications would be loaded here)
Load Balancing and Scaling
HAProxy Configuration
# haproxy.cfg
global
daemon
maxconn 4096
log stdout local0
defaults
mode http
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
option httplog
frontend fluorite_frontend
bind *:80
bind *:443 ssl crt /etc/ssl/fluorite.pem
redirect scheme https if !{ ssl_fc }
default_backend fluorite_backend
backend fluorite_backend
balance roundrobin
option httpchk GET /health
http-check expect status 200
server fluorite1 fluorite-mcp-1:3000 check
server fluorite2 fluorite-mcp-2:3000 check
server fluorite3 fluorite-mcp-3:3000 check
listen stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats admin if TRUE
Monitoring and Observability
Prometheus Metrics
Metrics Collection
// monitoring/prometheus-integration.ts
import prometheus from 'prom-client';
import { FluoriteIntegration } from '../fluorite-integration';
export class FluoritePrometheusMetrics {
private registry: prometheus.Registry;
private fluorite: FluoriteIntegration;
// Metrics definitions
private requestDuration = new prometheus.Histogram({
name: 'fluorite_request_duration_seconds',
help: 'Duration of Fluorite MCP requests in seconds',
labelNames: ['tool', 'status'],
buckets: [0.1, 0.5, 1, 2, 5, 10]
});
private requestCount = new prometheus.Counter({
name: 'fluorite_requests_total',
help: 'Total number of Fluorite MCP requests',
labelNames: ['tool', 'status']
});
private activeConnections = new prometheus.Gauge({
name: 'fluorite_active_connections',
help: 'Number of active MCP connections'
});
private cacheHitRate = new prometheus.Gauge({
name: 'fluorite_cache_hit_rate',
help: 'Cache hit rate for Fluorite operations'
});
private analysisErrors = new prometheus.Counter({
name: 'fluorite_analysis_errors_total',
help: 'Total number of analysis errors',
labelNames: ['error_type', 'framework']
});
private spikeApplications = new prometheus.Counter({
name: 'fluorite_spike_applications_total',
help: 'Total number of spike template applications',
labelNames: ['template_id', 'status']
});
constructor() {
this.registry = new prometheus.Registry();
this.fluorite = new FluoriteIntegration();
// Register metrics
this.registry.registerMetric(this.requestDuration);
this.registry.registerMetric(this.requestCount);
this.registry.registerMetric(this.activeConnections);
this.registry.registerMetric(this.cacheHitRate);
this.registry.registerMetric(this.analysisErrors);
this.registry.registerMetric(this.spikeApplications);
// Collect default metrics
prometheus.collectDefaultMetrics({ register: this.registry });
// Start collecting Fluorite-specific metrics
this.startMetricsCollection();
}
private async startMetricsCollection() {
await this.fluorite.initialize();
// Collect metrics every 30 seconds
setInterval(async () => {
try {
const health = await this.fluorite.getHealthStatus();
if (health.metrics) {
// Update cache hit rate
this.cacheHitRate.set(health.metrics.cache?.hitRate || 0);
// Update active connections (if available)
this.activeConnections.set(health.metrics.connections || 0);
}
} catch (error) {
console.error('Failed to collect Fluorite metrics:', error);
}
}, 30000);
}
public instrumentRequest(tool: string) {
const end = this.requestDuration.startTimer({ tool });
return {
success: () => {
end({ status: 'success' });
this.requestCount.inc({ tool, status: 'success' });
},
error: (errorType?: string) => {
end({ status: 'error' });
this.requestCount.inc({ tool, status: 'error' });
if (tool === 'static-analysis' && errorType) {
this.analysisErrors.inc({ error_type: errorType, framework: 'unknown' });
}
}
};
}
public recordSpikeApplication(templateId: string, success: boolean) {
this.spikeApplications.inc({
template_id: templateId,
status: success ? 'success' : 'failure'
});
}
public getMetrics(): string {
return this.registry.metrics();
}
public getRegistry(): prometheus.Registry {
return this.registry;
}
}
// Express endpoint for metrics
export function createMetricsEndpoint() {
const metrics = new FluoritePrometheusMetrics();
return async (req: any, res: any) => {
try {
res.set('Content-Type', prometheus.register.contentType);
res.end(await metrics.getMetrics());
} catch (error) {
res.status(500).end('Error collecting metrics');
}
};
}
Grafana Dashboard
{
"dashboard": {
"id": null,
"title": "Fluorite MCP Monitoring",
"tags": ["fluorite", "mcp", "development"],
"timezone": "browser",
"panels": [
{
"id": 1,
"title": "Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(fluorite_requests_total[5m])",
"legendFormat": "{{tool}} - {{status}}"
}
],
"yAxes": [
{
"label": "Requests/sec"
}
]
},
{
"id": 2,
"title": "Request Duration",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(fluorite_request_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
},
{
"expr": "histogram_quantile(0.50, rate(fluorite_request_duration_seconds_bucket[5m]))",
"legendFormat": "50th percentile"
}
]
},
{
"id": 3,
"title": "Cache Hit Rate",
"type": "singlestat",
"targets": [
{
"expr": "fluorite_cache_hit_rate",
"format": "percent"
}
]
},
{
"id": 4,
"title": "Active Connections",
"type": "singlestat",
"targets": [
{
"expr": "fluorite_active_connections"
}
]
},
{
"id": 5,
"title": "Analysis Errors",
"type": "graph",
"targets": [
{
"expr": "rate(fluorite_analysis_errors_total[5m])",
"legendFormat": "{{error_type}} - {{framework}}"
}
]
},
{
"id": 6,
"title": "Spike Applications",
"type": "graph",
"targets": [
{
"expr": "rate(fluorite_spike_applications_total[5m])",
"legendFormat": "{{template_id}} - {{status}}"
}
]
}
],
"time": {
"from": "now-1h",
"to": "now"
},
"refresh": "30s"
}
}
Logging and Alerting
Structured Logging
// monitoring/structured-logging.ts
import winston from 'winston';
import { FluoriteIntegration } from '../fluorite-integration';
export class FluoriteLogger {
private logger: winston.Logger;
constructor() {
this.logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: 'fluorite-mcp',
version: process.env.npm_package_version
},
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
new winston.transports.Console({
format: winston.format.simple()
})
]
});
// Add request correlation IDs
this.logger.add(new winston.transports.File({
filename: 'audit.log',
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json()
)
}));
}
public logRequest(tool: string, params: any, correlationId: string) {
this.logger.info('MCP request started', {
tool,
params: this.sanitizeParams(params),
correlationId,
timestamp: new Date().toISOString()
});
}
public logResponse(tool: string, success: boolean, duration: number, correlationId: string) {
this.logger.info('MCP request completed', {
tool,
success,
duration,
correlationId,
timestamp: new Date().toISOString()
});
}
public logError(tool: string, error: Error, correlationId: string) {
this.logger.error('MCP request failed', {
tool,
error: error.message,
stack: error.stack,
correlationId,
timestamp: new Date().toISOString()
});
}
public logAnalysisResult(result: any, framework: string, correlationId: string) {
this.logger.info('Analysis completed', {
framework,
errorsFound: result.summary?.errors || 0,
warningsFound: result.summary?.warnings || 0,
filesAnalyzed: result.summary?.filesAnalyzed || 0,
correlationId,
timestamp: new Date().toISOString()
});
}
public logSpikeApplication(templateId: string, success: boolean, correlationId: string) {
this.logger.info('Spike template applied', {
templateId,
success,
correlationId,
timestamp: new Date().toISOString()
});
}
private sanitizeParams(params: any): any {
const sanitized = { ...params };
// Remove sensitive data
if (sanitized.code && sanitized.code.length > 1000) {
sanitized.code = sanitized.code.substring(0, 1000) + '... (truncated)';
}
return sanitized;
}
}
// Usage with Fluorite Integration
export class MonitoredFluoriteIntegration extends FluoriteIntegration {
private logger: FluoriteLogger;
private metrics: FluoritePrometheusMetrics;
constructor() {
super();
this.logger = new FluoriteLogger();
this.metrics = new FluoritePrometheusMetrics();
}
public async analyzeProject(projectPath: string, framework?: string): Promise<any> {
const correlationId = this.generateCorrelationId();
const timer = this.metrics.instrumentRequest('static-analysis');
this.logger.logRequest('static-analysis', { projectPath, framework }, correlationId);
try {
const start = Date.now();
const result = await super.analyzeProject(projectPath, framework);
const duration = Date.now() - start;
this.logger.logResponse('static-analysis', true, duration, correlationId);
this.logger.logAnalysisResult(result, framework || 'unknown', correlationId);
timer.success();
return result;
} catch (error) {
this.logger.logError('static-analysis', error as Error, correlationId);
timer.error(error.constructor.name);
throw error;
}
}
public async applySpike(id: string, params?: Record<string, string>, strategy?: any): Promise<any> {
const correlationId = this.generateCorrelationId();
const timer = this.metrics.instrumentRequest('apply-spike');
this.logger.logRequest('apply-spike', { id, params, strategy }, correlationId);
try {
const start = Date.now();
const result = await super.applySpike(id, params, strategy);
const duration = Date.now() - start;
this.logger.logResponse('apply-spike', result.success, duration, correlationId);
this.logger.logSpikeApplication(id, result.success, correlationId);
this.metrics.recordSpikeApplication(id, result.success);
timer.success();
return result;
} catch (error) {
this.logger.logError('apply-spike', error as Error, correlationId);
this.metrics.recordSpikeApplication(id, false);
timer.error(error.constructor.name);
throw error;
}
}
private generateCorrelationId(): string {
return `fluorite-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
}
}
Alerting Rules
# alerting/fluorite-alerts.yml
groups:
- name: fluorite-mcp
rules:
- alert: FluoriteMCPHighErrorRate
expr: rate(fluorite_requests_total{status="error"}[5m]) > 0.1
for: 2m
labels:
severity: warning
service: fluorite-mcp
annotations:
summary: "High error rate in Fluorite MCP"
description: "Error rate is {{ $value }} errors per second"
- alert: FluoriteMCPSlowRequests
expr: histogram_quantile(0.95, rate(fluorite_request_duration_seconds_bucket[5m])) > 10
for: 5m
labels:
severity: warning
service: fluorite-mcp
annotations:
summary: "Slow requests in Fluorite MCP"
description: "95th percentile latency is {{ $value }} seconds"
- alert: FluoriteMCPDown
expr: up{job="fluorite-mcp"} == 0
for: 1m
labels:
severity: critical
service: fluorite-mcp
annotations:
summary: "Fluorite MCP is down"
description: "Fluorite MCP has been down for more than 1 minute"
- alert: FluoriteMCPLowCacheHitRate
expr: fluorite_cache_hit_rate < 0.5
for: 10m
labels:
severity: info
service: fluorite-mcp
annotations:
summary: "Low cache hit rate in Fluorite MCP"
description: "Cache hit rate is {{ $value }}, consider reviewing cache configuration"
- alert: FluoriteMCPHighMemoryUsage
expr: (process_resident_memory_bytes{job="fluorite-mcp"} / 1024 / 1024 / 1024) > 4
for: 5m
labels:
severity: warning
service: fluorite-mcp
annotations:
summary: "High memory usage in Fluorite MCP"
description: "Memory usage is {{ $value }}GB"
Quick Integration Examples
5-Minute Team Setup
Scenario: Get a small development team up and running with Fluorite MCP in 5 minutes.
#!/bin/bash
# quick-team-setup.sh
echo "🚀 5-Minute Fluorite MCP Team Setup"
echo "=================================="
# 1. Install for team lead
npm install -g fluorite-mcp
claude mcp add fluorite-mcp -- fluorite-mcp
# 2. Verify installation
echo "✅ Testing installation..."
fluorite-mcp --self-test
# 3. Create team configuration
mkdir -p .fluorite
cat > .fluorite/team-config.yml << EOF
team:
name: "Development Team"
size: "small"
frameworks:
primary: ["nextjs", "react"]
quality_gates:
error_threshold: 3
warning_threshold: 10
EOF
# 4. Test with sample project
echo "🧪 Testing with sample component..."
echo 'import React from "react"; export const Button = () => <button>Click me</button>;' > sample.tsx
# Use Claude Code CLI to analyze
echo "Sample analysis result:"
claude-code "Analyze this React component for best practices and suggest improvements" < sample.tsx
echo "✅ Setup complete! Share these commands with your team:"
echo " npm install -g fluorite-mcp"
echo " claude mcp add fluorite-mcp -- fluorite-mcp"
Instant Project Analysis
Scenario: Quickly analyze any project for issues and improvements.
# instant-analysis.sh
#!/bin/bash
PROJECT_PATH=${1:-.}
FRAMEWORK=${2:-auto-detect}
echo "🔍 Instant Project Analysis"
echo "=========================="
echo "Path: $PROJECT_PATH"
echo "Framework: $FRAMEWORK"
echo ""
# Quick validation
fluorite-mcp --static-analysis \
--project-path "$PROJECT_PATH" \
--framework "$FRAMEWORK" \
--max-issues 20 \
--output-format summary
echo ""
echo "📊 Performance test:"
time fluorite-mcp --performance-test
echo ""
echo "💡 To see full analysis, run:"
echo " fluorite-mcp --static-analysis --project-path \"$PROJECT_PATH\" --framework \"$FRAMEWORK\""
Spike Template Discovery
Scenario: Find and apply the perfect template for your project quickly.
// quick-spike-finder.js
const { FluoriteIntegration } = require('./lib/fluorite-integration');
async function quickSpikeFinder(userQuery) {
const fluorite = new FluoriteIntegration();
await fluorite.initialize();
console.log(`🔍 Finding templates for: "${userQuery}"`);
// Discover templates
const templates = await fluorite.discoverSpikes(userQuery);
if (templates.length === 0) {
console.log('❌ No templates found. Try a broader search term.');
return;
}
console.log(`✅ Found ${templates.length} templates:`);
templates.slice(0, 5).forEach((template, index) => {
console.log(`${index + 1}. 🔐 ${template.id}`);
console.log(` 📝 ${template.description}`);
console.log(` 🏗️ Stack: ${template.stack.join(', ')}`);
console.log('');
});
// Auto-select best match
const recommendation = await fluorite.autoSelectSpike(userQuery);
if (recommendation.selectedTemplate) {
console.log(`🎯 Best match: ${recommendation.selectedTemplate}`);
console.log(`📊 Confidence: ${Math.round(recommendation.confidence * 100)}%`);
console.log(`💭 Reasoning: ${recommendation.reasoning}`);
console.log('\n🚀 To apply this template, run:');
console.log(`fluorite-mcp --apply-spike ${recommendation.selectedTemplate}`);
}
}
// Usage examples
if (require.main === module) {
const query = process.argv[2] || 'React form with validation';
quickSpikeFinder(query).catch(console.error);
}
// Run with: node quick-spike-finder.js "Next.js API with authentication"
Common Integration Patterns
Pattern 1: Pre-commit Hook
#!/bin/sh
# .git/hooks/pre-commit
echo "🔍 Running Fluorite MCP pre-commit checks..."
# Get staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(ts|tsx|js|jsx|vue|py)$')
if [ -z "$STAGED_FILES" ]; then
echo "✅ No relevant files to check"
exit 0
fi
# Quick validation
fluorite-mcp --quick-validate \
--files "$STAGED_FILES" \
--framework auto-detect \
--fast-mode
if [ $? -ne 0 ]; then
echo "❌ Pre-commit validation failed. Please fix issues before committing."
exit 1
fi
echo "✅ Pre-commit validation passed"
Pattern 2: VS Code Task
// .vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"label": "Fluorite: Analyze Current File",
"type": "shell",
"command": "fluorite-mcp",
"args": [
"--quick-validate",
"--file", "${file}",
"--framework", "auto-detect"
],
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "shared"
},
"problemMatcher": []
},
{
"label": "Fluorite: Project Analysis",
"type": "shell",
"command": "fluorite-mcp",
"args": [
"--static-analysis",
"--project-path", "${workspaceFolder}",
"--framework", "auto-detect"
],
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": true,
"panel": "new"
}
},
{
"label": "Fluorite: Apply Spike Template",
"type": "shell",
"command": "fluorite-mcp",
"args": [
"--discover-spikes",
"--query", "${input:spikeQuery}"
],
"group": "build"
}
],
"inputs": [
{
"id": "spikeQuery",
"description": "What kind of template are you looking for?",
"default": "React component",
"type": "promptString"
}
]
}
Pattern 3: Package.json Scripts
{
"scripts": {
"fluorite:analyze": "fluorite-mcp --static-analysis --project-path . --framework auto-detect",
"fluorite:validate": "fluorite-mcp --quick-validate --framework auto-detect",
"fluorite:performance": "fluorite-mcp --performance-test",
"fluorite:health": "fluorite-mcp --self-test",
"fluorite:spikes": "fluorite-mcp --discover-spikes",
"precommit": "fluorite-mcp --quick-validate --staged-files",
"prebuild": "npm run fluorite:validate",
"test:quality": "npm run fluorite:analyze && npm run test"
}
}
Framework-Specific Integration Examples
Next.js Integration
// next.config.js enhancement
/** @type {import('next').NextConfig} */
const nextConfig = {
// ... existing config
webpack: (config, { dev, isServer }) => {
if (dev && !isServer) {
// Add Fluorite MCP development integration
config.plugins.push(
new (class FluoriteMCPPlugin {
apply(compiler) {
compiler.hooks.afterCompile.tap('FluoriteMCP', (compilation) => {
// Run quick validation on changed files
const changedFiles = Array.from(compilation.fileDependencies)
.filter(file => file.includes('/src/') && file.match(/\.(ts|tsx|js|jsx)$/));
if (changedFiles.length > 0) {
// Background validation (non-blocking)
setImmediate(() => {
import('./tools/fluorite-validator').then(({ validateFiles }) => {
validateFiles(changedFiles).catch(console.error);
});
});
}
});
}
})()
);
}
return config;
}
};
module.exports = nextConfig;
React Development
// tools/react-dev-integration.ts
import { FluoriteIntegration } from './fluorite-integration';
export class ReactDevIntegration {
private fluorite: FluoriteIntegration;
constructor() {
this.fluorite = new FluoriteIntegration();
}
async setupDevelopmentMode() {
await this.fluorite.initialize();
// Watch for component changes
if (process.env.NODE_ENV === 'development') {
this.watchComponentChanges();
}
}
private watchComponentChanges() {
const chokidar = require('chokidar');
chokidar.watch('src/**/*.{tsx,jsx}').on('change', async (filePath) => {
console.log(`🔍 Validating ${filePath}...`);
try {
const validation = await this.fluorite.validateCode(
require('fs').readFileSync(filePath, 'utf8'),
'tsx',
'react'
);
if (!validation.valid) {
console.warn(`⚠️ Issues found in ${filePath}:`);
validation.issues.forEach(issue => {
console.warn(` - ${issue.message}`);
});
}
} catch (error) {
console.error(`❌ Validation failed for ${filePath}:`, error.message);
}
});
}
}
// Auto-initialize in development
if (process.env.NODE_ENV === 'development') {
const integration = new ReactDevIntegration();
integration.setupDevelopmentMode();
}
FastAPI Integration
# tools/fastapi_fluorite_integration.py
import asyncio
import subprocess
import json
from typing import Dict, List
from fastapi import FastAPI, BackgroundTasks
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class FluoriteFileHandler(FileSystemEventHandler):
def __init__(self, app: FastAPI):
self.app = app
def on_modified(self, event):
if event.is_directory:
return
if event.src_path.endswith('.py'):
# Run validation in background
asyncio.create_task(self.validate_file(event.src_path))
async def validate_file(self, file_path: str):
try:
# Run Fluorite MCP validation
result = subprocess.run([
'fluorite-mcp', '--quick-validate',
'--file', file_path,
'--language', 'python',
'--framework', 'fastapi'
], capture_output=True, text=True)
if result.returncode != 0:
print(f"⚠️ Validation issues in {file_path}:")
print(result.stdout)
except Exception as e:
print(f"❌ Validation failed for {file_path}: {e}")
def setup_fluorite_integration(app: FastAPI):
"""Setup Fluorite MCP integration for FastAPI development"""
if not app.debug:
return
# Watch for file changes
event_handler = FluoriteFileHandler(app)
observer = Observer()
observer.schedule(event_handler, path='.', recursive=True)
observer.start()
@app.on_event("startup")
async def startup_validation():
print("🔍 Running startup validation with Fluorite MCP...")
try:
result = subprocess.run([
'fluorite-mcp', '--static-analysis',
'--project-path', '.',
'--framework', 'fastapi',
'--max-issues', '10'
], capture_output=True, text=True)
if result.returncode == 0:
print("✅ Startup validation passed")
else:
print("⚠️ Validation issues found:")
print(result.stdout)
except Exception as e:
print(f"❌ Startup validation failed: {e}")
@app.on_event("shutdown")
def shutdown_cleanup():
observer.stop()
observer.join()
# Usage in main.py
from fastapi import FastAPI
from tools.fastapi_fluorite_integration import setup_fluorite_integration
app = FastAPI()
# Setup Fluorite integration in development
if __name__ == "__main__":
import os
if os.getenv("ENV") == "development":
setup_fluorite_integration(app)
Troubleshooting Common Integration Issues
Issue 1: Claude Code CLI Connection Problems
Symptoms: "Server not found" or connection timeout errors
Diagnosis:
# Check MCP server registration
claude mcp list | grep fluorite
# Check server status
claude mcp status fluorite
# Test server directly
fluorite-mcp --self-test
# Check logs
claude mcp logs fluorite --tail 50
Solutions:
# Remove and re-add server
claude mcp remove fluorite
claude mcp add fluorite-mcp -- fluorite-mcp
# Check for port conflicts
lsof -i :3000
# Verify Node.js version
node --version # Should be >= 18.0
# Check permissions
ls -la $(which fluorite-mcp)
Issue 2: Performance Problems
Symptoms: Slow analysis, high memory usage, timeouts
Diagnosis:
# Check system resources
top -p $(pgrep fluorite-mcp)
# Monitor memory usage
watch -n 5 'ps aux | grep fluorite-mcp'
# Check analysis performance
time fluorite-mcp --performance-test
Solutions:
# Increase memory allocation
export NODE_OPTIONS="--max-old-space-size=4096"
# Enable caching
export FLUORITE_ENABLE_CACHE=true
export FLUORITE_CACHE_TTL=3600
# Optimize for your framework
export FLUORITE_PRIMARY_FRAMEWORK=nextjs
# Exclude large directories
export FLUORITE_IGNORE_PATTERNS="node_modules/**,dist/**,build/**"
Issue 3: False Positives in Analysis
Symptoms: Incorrect error reports, warnings for valid code
Solutions:
# Update to latest version
npm update -g fluorite-mcp
# Configure custom rules
cat > .fluorite.json << EOF
{
"rules": {
"react-hooks-dependencies": "warning",
"nextjs-client-server-boundary": "error"
},
"ignorePatterns": [
"**/*.test.ts",
"**/legacy/**"
],
"framework": "nextjs",
"strictMode": false
}
EOF
# Disable specific rules temporarily
export FLUORITE_DISABLED_RULES="rule-id-1,rule-id-2"
This comprehensive integration guide provides detailed patterns for integrating Fluorite MCP across development workflows, CI/CD pipelines, team collaboration, and enterprise deployment scenarios. Each section includes practical examples and production-ready configurations that can be adapted to specific organizational needs.
Integration Guide v0.20.1 - Last updated: January 2025