
mern-patterns
MERN stack patterns including React with Vite, Express middleware, MongoDB schemas, API Gateway architecture, session management, error handling, and testing strategies. Activate for MERN development, microservices architecture, and full-stack JavaScript applications.
- Installs
- 68
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/thelobbi/claude --skill mern-patterns- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads mern-patterns automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
MERN Stack Patterns Skill
Comprehensive MERN stack development patterns for the keycloak-alpha multi-tenant platform with 8 microservices.
When to Use This Skill
Activate this skill when:
- Building React + Vite frontend applications
- Implementing Express microservices
- Designing MongoDB schemas and data models
- Setting up API Gateway architecture
- Implementing session and cookie management
- Adding error handling and validation
- Writing tests for MERN stack applications
keycloak-alpha Project Structure
keycloak-alpha/
├── apps/
│ ├── web-app/ # Main React + Vite SPA
│ │ ├── src/
│ │ │ ├── components/
│ │ │ ├── pages/
│ │ │ ├── hooks/
│ │ │ ├── contexts/
│ │ │ ├── config/
│ │ │ ├── utils/
│ │ │ └── main.jsx
│ │ ├── vite.config.js
│ │ └── package.json
│ └── admin-portal/ # Admin dashboard (React + Vite)
│
├── services/
│ ├── api-gateway/ # Express API Gateway
│ ├── user-service/ # User management
│ ├── org-service/ # Organization management
│ ├── tenant-service/ # Multi-tenant provisioning
│ ├── notification-service/ # Email/SMS notifications
│ ├── billing-service/ # Stripe integration
│ ├── analytics-service/ # Usage analytics
│ └── keycloak-service/ # Keycloak integration
│
├── routes/
│ ├── api/
│ │ ├── users.js
│ │ ├── organizations.js
│ │ └── tenants.js
│ └── index.js
│
├── shared/
│ ├── types/
│ ├── utils/
│ └── constants/
│
└── package.json
React + Vite Frontend Patterns
Vite Configuration
// apps/web-app/vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@hooks': path.resolve(__dirname, './src/hooks'),
'@contexts': path.resolve(__dirname, './src/contexts'),
'@utils': path.resolve(__dirname, './src/utils'),
'@config': path.resolve(__dirname, './src/config'),
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:4000',
changeOrigin: true,
}
}
},
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
'vendor': ['react', 'react-dom', 'react-router-dom'],
'keycloak': ['keycloak-js'],
'ui': ['@chakra-ui/react', '@emotion/react']
}
}
}
},
optimizeDeps: {
include: ['react', 'react-dom', 'keycloak-js']
}
});
Component Organization
// apps/web-app/src/components/features/UserProfile/index.jsx
import { useState, useEffect } from 'react';
import { useAuth } from '@hooks/useAuth';
import { useToast } from '@chakra-ui/react';
import { updateUserProfile } from '@/api/users';
export function UserProfile() {
const { user } = useAuth();
const toast = useToast();
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
fetchProfile();
}, []);
async function fetchProfile() {
setLoading(true);
try {
const data = await getUserProfile(user.sub);
setProfile(data);
} catch (error) {
toast({
title: 'Error loading profile',
description: error.message,
status: 'error',
duration: 5000,
});
} finally {
setLoading(false);
}
}
async function handleSubmit(formData) {
try {
await updateUserProfile(user.sub, formData);
toast({
title: 'Profile updated',
status: 'success',
duration: 3000,
});
} catch (error) {
toast({
title: 'Update failed',
description: error.message,
status: 'error',
duration: 5000,
});
}
}
if (loading) return <Spinner />;
if (!profile) return <Alert>Profile not found</Alert>;
return <ProfileForm profile={profile} onSubmit={handleSubmit} />;
}
Custom Hooks Pattern
// apps/web-app/src/hooks/useAuth.js
import { createContext, useContext, useState, useEffect } from 'react';
import Keycloak from 'keycloak-js';
import { keycloakConfig } from '@config/keycloak.config';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const [keycloak, setKeycloak] = useState(null);
const [authenticated, setAuthenticated] = useState(false);
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const kc = new Keycloak(keycloakConfig);
kc.init({
onLoad: 'check-sso',
checkLoginIframe: true,
pkceMethod: 'S256'
}).then(authenticated => {
setKeycloak(kc);
setAuthenticated(authenticated);
if (authenticated) {
setUser({
sub: kc.tokenParsed.sub,
email: kc.tokenParsed.email,
name: kc.tokenParsed.name,
orgId: kc.tokenParsed.org_id,
roles: kc.tokenParsed.realm_access?.roles || []
});
}
setLoading(false);
});
// Token refresh
kc.onTokenExpired = () => {
kc.updateToken(30).catch(() => {
kc.logout();
});
};
}, []);
const login = () => keycloak.login();
const logout = () => keycloak.logout();
const getToken = () => keycloak.token;
return (
<AuthContext.Provider value={{
authenticated,
user,
loading,
login,
logout,
getToken
}}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
};
API Client Pattern
// apps/web-app/src/utils/apiClient.js
import axios from 'axios';
import { useAuth } from '@hooks/useAuth';
const apiClient = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:4000/api',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});
// Request interceptor to add auth token
apiClient.interceptors.request.use(
async (config) => {
const token = await getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor for error handling
apiClient.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.response?.status === 401) {
// Redirect to login
window.location.href = '/login';
}
const errorMessage = error.response?.data?.message || error.message;
return Promise.reject(new Error(errorMessage));
}
);
export default apiClient;
// Typed API methods
export const api = {
users: {
getProfile: (userId) => apiClient.get(`/users/${userId}`),
updateProfile: (userId, data) => apiClient.put(`/users/${userId}`, data),
listUsers: (orgId) => apiClient.get(`/users?org_id=${orgId}`)
},
organizations: {
get: (orgId) => apiClient.get(`/organizations/${orgId}`),
create: (data) => apiClient.post('/organizations', data),
update: (orgId, data) => apiClient.put(`/organizations/${orgId}`, data)
}
};
Express Microservice Patterns
Service Structure
// services/user-service/src/index.js
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import morgan from 'morgan';
import { connectDB } from './config/database.js';
import { errorHandler } from './middleware/errorHandler.js';
import { authMiddleware } from './middleware/auth.js';
import userRoutes from './routes/users.js';
const app = express();
// Security middleware
app.use(helmet());
app.use(cors({
origin: process.env.CORS_ORIGIN?.split(',') || 'http://localhost:3000',
credentials: true
}));
// Logging
app.use(morgan('combined'));
// Body parsing
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Connect to MongoDB
await connectDB();
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'healthy', service: 'user-service' });
});
// API routes (protected)
app.use('/api/users', authMiddleware, userRoutes);
// Error handling
app.use(errorHandler);
const PORT = process.env.PORT || 5001;
app.listen(PORT, () => {
console.log(`User service running on port ${PORT}`);
});
Route Organization
// services/user-service/src/routes/users.js
import express from 'express';
import { body, param, query } from 'express-validator';
import { validate } from '../middleware/validate.js';
import { requireRole } from '../middleware/rbac.js';
import * as userController from '../controllers/user.controller.js';
const router = express.Router();
// GET /api/users - List users (org admin only)
router.get('/',
requireRole(['org_admin']),
query('org_id').optional().isString(),
query('page').optional().isInt({ min: 1 }),
query('limit').optional().isInt({ min: 1, max: 100 }),
validate,
userController.listUsers
);
// GET /api/users/:id - Get user by ID
router.get('/:id',
param('id').isUUID(),
validate,
userController.getUser
);
// POST /api/users - Create user (org admin only)
router.post('/',
requireRole(['org_admin']),
body('email').isEmail().normalizeEmail(),
body('firstName').trim().isLength({ min: 1, max: 50 }),
body('lastName').trim().isLength({ min: 1, max: 50 }),
body('orgId').isString(),
validate,
userController.createUser
);
// PUT /api/users/:id - Update user
router.put('/:id',
param('id').isUUID(),
body('firstName').optional().trim().isLength({ min: 1, max: 50 }),
body('lastName').optional().trim().isLength({ min: 1, max: 50 }),
validate,
userController.updateUser
);
// DELETE /api/users/:id - Delete user (org admin only)
router.delete('/:id',
requireRole(['org_admin']),
param('id').isUUID(),
validate,
userController.deleteUser
);
export default router;
Controller Pattern
// services/user-service/src/controllers/user.controller.js
import { UserModel } from '../models/User.js';
import { KeycloakService } from '../services/keycloak.service.js';
import { AppError } from '../utils/AppError.js';
export async function listUsers(req, res, next) {
try {
const { org_id, page = 1, limit = 20 } = req.query;
// Ensure user can only list users from their org (unless super admin)
const orgIdFilter = req.user.roles.includes('super_admin')
? org_id
: req.user.org_id;
if (!orgIdFilter) {
throw new AppError('Organization ID required', 400);
}
const users = await UserModel.find({ org_id: orgIdFilter })
.select('-password')
.limit(limit)
.skip((page - 1) * limit)
.sort({ createdAt: -1 });
const total = await UserModel.countDocuments({ org_id: orgIdFilter });
res.json({
users,
pagination: {
page: parseInt(page),
limit: parseInt(limit),
total,
pages: Math.ceil(total / limit)
}
});
} catch (error) {
next(error);
}
}
export async function getUser(req, res, next) {
try {
const { id } = req.params;
const user = await UserModel.findById(id).select('-password');
if (!user) {
throw new AppError('User not found', 404);
}
// Ensure user can only access users from their org
if (user.org_id !== req.user.org_id && !req.user.roles.includes('super_admin')) {
throw new AppError('Access denied', 403);
}
res.json(user);
} catch (error) {
next(error);
}
}
export async function createUser(req, res, next) {
try {
const { email, firstName, lastName, orgId } = req.body;
// Verify org_id matches user's org (unless super admin)
if (orgId !== req.user.org_id && !req.user.roles.includes('super_admin')) {
throw new AppError('Cannot create user for different organization', 403);
}
// Create user in Keycloak
const keycloakService = new KeycloakService();
const keycloakUserId = await keycloakService.createUser({
email,
firstName,
lastName,
orgId
});
// Create user in MongoDB
const user = new UserModel({
keycloakId: keycloakUserId,
email,
firstName,
lastName,
org_id: orgId,
createdBy: req.user.sub
});
await user.save();
res.status(201).json({
id: user._id,
keycloakId: keycloakUserId,
email: user.email
});
} catch (error) {
next(error);
}
}
MongoDB Schema Patterns
User Model
// services/user-service/src/models/User.js
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
keycloakId: {
type: String,
required: true,
unique: true,
index: true
},
email: {
type: String,
required: true,
lowercase: true,
trim: true,
index: true
},
firstName: {
type: String,
required: true,
trim: true
},
lastName: {
type: String,
required: true,
trim: true
},
org_id: {
type: String,
required: true,
index: true
},
roles: [{
type: String,
enum: ['org_admin', 'org_user', 'super_admin']
}],
metadata: {
type: Map,
of: String
},
createdBy: String,
updatedBy: String
}, {
timestamps: true,
toJSON: {
transform: (doc, ret) => {
ret.id = ret._id;
delete ret._id;
delete ret.__v;
return ret;
}
}
});
// Compound index for org queries
userSchema.index({ org_id: 1, email: 1 }, { unique: true });
// Virtual for full name
userSchema.virtual('fullName').get(function() {
return `${this.firstName} ${this.lastName}`;
});
// Pre-save hook
userSchema.pre('save', function(next) {
if (this.isModified('email')) {
this.email = this.email.toLowerCase();
}
next();
});
export const UserModel = mongoose.model('User', userSchema);
Organization Model
// services/org-service/src/models/Organization.js
import mongoose from 'mongoose';
const organizationSchema = new mongoose.Schema({
org_id: {
type: String,
required: true,
unique: true,
index: true
},
name: {
type: String,
required: true,
trim: true
},
domain: {
type: String,
required: true,
unique: true,
lowercase: true
},
settings: {
theme: {
type: String,
default: 'lobbi-base'
},
features: {
type: Map,
of: Boolean,
default: new Map()
},
branding: {
logoUrl: String,
primaryColor: String,
secondaryColor: String
}
},
subscription: {
plan: {
type: String,
enum: ['free', 'starter', 'professional', 'enterprise'],
default: 'free'
},
status: {
type: String,
enum: ['active', 'inactive', 'suspended'],
default: 'active'
},
billingCycle: {
type: String,
enum: ['monthly', 'annual']
},
stripeCustomerId: String,
stripeSubscriptionId: String
},
adminUsers: [{
userId: String,
email: String,
addedAt: Date
}],
status: {
type: String,
enum: ['active', 'inactive', 'suspended'],
default: 'active'
}
}, {
timestamps: true
});
export const OrganizationModel = mongoose.model('Organization', organizationSchema);
API Gateway Architecture
Gateway Setup
// services/api-gateway/src/index.js
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import { authMiddleware } from './middleware/auth.js';
import { rateLimiter } from './middleware/rateLimit.js';
import { cacheMiddleware } from './middleware/cache.js';
const app = express();
// Rate limiting
app.use(rateLimiter);
// Authentication
app.use(authMiddleware);
// Service routing
const services = {
users: process.env.USER_SERVICE_URL || 'http://localhost:5001',
orgs: process.env.ORG_SERVICE_URL || 'http://localhost:5002',
tenants: process.env.TENANT_SERVICE_URL || 'http://localhost:5003',
notifications: process.env.NOTIFICATION_SERVICE_URL || 'http://localhost:5004',
billing: process.env.BILLING_SERVICE_URL || 'http://localhost:5005',
analytics: process.env.ANALYTICS_SERVICE_URL || 'http://localhost:5006'
};
// Proxy to microservices
Object.entries(services).forEach(([name, target]) => {
app.use(`/api/${name}`, createProxyMiddleware({
target,
changeOrigin: true,
pathRewrite: { [`^/api/${name}`]: '' },
onProxyReq: (proxyReq, req) => {
// Forward user context
if (req.user) {
proxyReq.setHeader('X-User-Id', req.user.sub);
proxyReq.setHeader('X-Org-Id', req.user.org_id);
proxyReq.setHeader('X-User-Roles', JSON.stringify(req.user.roles));
}
}
}));
});
app.listen(4000, () => {
console.log('API Gateway running on port 4000');
});
Session and Cookie Management
Session Configuration
// services/api-gateway/src/config/session.js
import session from 'express-session';
import MongoStore from 'connect-mongo';
export const sessionConfig = session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URL,
ttl: 24 * 60 * 60, // 1 day
touchAfter: 24 * 3600 // lazy session update
}),
cookie: {
secure: process.env.NODE_ENV === 'production',
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24, // 24 hours
sameSite: 'lax',
domain: process.env.COOKIE_DOMAIN
},
name: 'lobbi.sid'
});
Error Handling Patterns
Custom Error Classes
// shared/utils/AppError.js
export class AppError extends Error {
constructor(message, statusCode = 500, isOperational = true) {
super(message);
this.statusCode = statusCode;
this.isOperational = isOperational;
Error.captureStackTrace(this, this.constructor);
}
}
export class ValidationError extends AppError {
constructor(message, errors = []) {
super(message, 400);
this.errors = errors;
}
}
export class NotFoundError extends AppError {
constructor(resource) {
super(`${resource} not found`, 404);
}
}
export class UnauthorizedError extends AppError {
constructor(message = 'Unauthorized') {
super(message, 401);
}
}
export class ForbiddenError extends AppError {
constructor(message = 'Forbidden') {
super(message, 403);
}
}
Error Handler Middleware
// services/user-service/src/middleware/errorHandler.js
import { AppError } from '../utils/AppError.js';
export function errorHandler(err, req, res, next) {
let { statusCode, message, isOperational } = err;
// Default to 500 server error
if (!statusCode) {
statusCode = 500;
isOperational = false;
}
// Log error
console.error('Error:', {
message,
statusCode,
isOperational,
stack: err.stack,
url: req.url,
method: req.method,
user: req.user?.sub
});
// Send error response
res.status(statusCode).json({
error: {
message: isOperational ? message : 'Internal server error',
statusCode,
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
}
});
}
// Async error wrapper
export function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
Testing Strategies
Unit Tests with Jest
// services/user-service/tests/controllers/user.controller.test.js
import { listUsers, createUser } from '../../src/controllers/user.controller.js';
import { UserModel } from '../../src/models/User.js';
import { KeycloakService } from '../../src/services/keycloak.service.js';
jest.mock('../../src/models/User.js');
jest.mock('../../src/services/keycloak.service.js');
describe('UserController', () => {
describe('listUsers', () => {
it('should return paginated users for org', async () => {
const mockUsers = [
{ _id: '1', email: 'user1@test.com', org_id: 'org_1' },
{ _id: '2', email: 'user2@test.com', org_id: 'org_1' }
];
UserModel.find.mockReturnValue({
select: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
sort: jest.fn().mockResolvedValue(mockUsers)
});
UserModel.countDocuments.mockResolvedValue(2);
const req = {
query: { org_id: 'org_1', page: 1, limit: 20 },
user: { org_id: 'org_1', roles: ['org_admin'] }
};
const res = {
json: jest.fn()
};
const next = jest.fn();
await listUsers(req, res, next);
expect(res.json).toHaveBeenCalledWith({
users: mockUsers,
pagination: expect.objectContaining({
page: 1,
total: 2
})
});
});
});
});
Integration Tests
// services/user-service/tests/integration/users.test.js
import request from 'supertest';
import { app } from '../../src/index.js';
import { connectDB, closeDB, clearDB } from '../setup.js';
beforeAll(async () => await connectDB());
afterEach(async () => await clearDB());
afterAll(async () => await closeDB());
describe('User API Integration', () => {
it('POST /api/users - should create user', async () => {
const userData = {
email: 'test@example.com',
firstName: 'Test',
lastName: 'User',
orgId: 'org_test'
};
const response = await request(app)
.post('/api/users')
.set('Authorization', `Bearer ${mockAdminToken}`)
.send(userData)
.expect(201);
expect(response.body).toMatchObject({
email: userData.email
});
});
});
Best Practices
- Use environment variables for all configuration
- Implement proper error handling with custom error classes
- Validate all inputs using express-validator or Joi
- Use async/await consistently, avoid callback hell
- Implement proper logging with structured logs
- Use MongoDB indexes for frequently queried fields
- Implement rate limiting to prevent abuse
- Use CORS properly with specific origins
- Implement request/response compression with gzip
- Use TypeScript for better type safety (optional)
- Implement health checks for all services
- Use connection pooling for database connections
- Implement graceful shutdown for services
- Use dependency injection for better testability
- Implement proper security headers with Helmet
File Locations in keycloak-alpha
| Path | Purpose |
|---|---|
apps/web-app/ | React + Vite main application |
services/api-gateway/ | API Gateway with routing |
services/user-service/ | User management microservice |
services/org-service/ | Organization management |
routes/api/ | Shared route definitions |
shared/utils/ | Shared utilities and helpers |
FAQ
What does the mern-patterns skill do?
MERN stack patterns including React with Vite, Express middleware, MongoDB schemas, API Gateway architecture, session management, error handling, and testing strategies. Activate for MERN development, microservices architecture, and full-stack JavaScript applications.
How do I install mern-patterns?
Run npx skills add https://github.com/thelobbi/claude --skill mern-patterns in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/mern-patterns/ (personal) or .claude/skills/mern-patterns/ (project).
Which AI agents can use mern-patterns?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is mern-patterns safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains mern-patterns?
It is published by thelobbi in thelobbi/claude. It has 68 recorded installs.
More from thelobbi/claude
vision-multimodal
thelobbi/claude
Vision and multimodal capabilities for Claude including image analysis, PDF processing, and document understanding. Activate for image input, base64 encoding, multiple images, and visual analysis.
design-system
thelobbi/claude
Open the skill to read its SKILL.md instructions.
authentication
thelobbi/claude
Authentication and authorization including JWT, OAuth2, OIDC, sessions, RBAC, and security analysis. Activate for login, auth flows, security audits, threat modeling, access control, and identity management.
Popular backend & apis skills
find-skills
vercel-labs/skills
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.
entra-app-registration
microsoft/azure-skills
Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.
google-agents-cli-adk-code
google/agents-cli
This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).
embedded-captions
heygen-com/hyperframes
Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.
entra-agent-id
microsoft/azure-skills
Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).
neon
neondatabase/agent-skills
Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.