Skip to main content
Glama
README.md12.2 kB
# Custom Framework Examples This directory contains examples of how to use the FairSquare custom framework schema with the Code Graph Context MCP server. ## What is FairSquare? FairSquare is a custom TypeScript framework (inspired by NestJS) that uses specific patterns for dependency injection, routing, and repository access. This example demonstrates how to create custom framework schemas to detect your own framework patterns. ## Files - **`fairsquare-framework-schema.ts`** - Located in `src/core/config/` - The actual schema definition - **`test-fairsquare.ts`** - Example script to parse a FairSquare codebase - **`test-credit-module.ts`** - Example parsing a specific FairSquare module - **`test_query.cypher`** - Sample Cypher query for Neo4j ## How to Use the FairSquare Schema The FairSquare schema is already integrated into the parser factory. You can use it in three ways: ### 1. Via MCP Tool (Automatic) ```typescript // The parse_typescript_project tool supports auto-detection await mcp.call('parse_typescript_project', { projectPath: '/path/to/fairsquare/project', tsconfigPath: '/path/to/tsconfig.json', projectType: 'fairsquare' // or 'auto' for auto-detection }); ``` ### 2. Programmatically with Auto-Detection ```typescript import { ParserFactory } from './src/core/parsers/parser-factory.js'; const parser = await ParserFactory.createParserWithAutoDetection( '/path/to/project', '/path/to/tsconfig.json' ); const result = await parser.parseWorkspace(); ``` ### 3. Explicitly Specify FairSquare ```typescript import { ParserFactory, ProjectType } from './src/core/parsers/parser-factory.js'; const parser = ParserFactory.createParser({ workspacePath: '/path/to/project', tsConfigPath: '/path/to/tsconfig.json', projectType: ProjectType.FAIRSQUARE }); const result = await parser.parseWorkspace(); ``` ## Running the Examples ### Prerequisites 1. Build the project: ```bash npm run build ``` 2. Update the paths in the test files to point to your FairSquare codebase ### Run Full Codebase Parse ```bash node examples/custom-frameworks/test-fairsquare.ts ``` ### Run Single Module Parse ```bash node examples/custom-frameworks/test-credit-module.ts ``` ## Creating Your Own Custom Framework Schema The FairSquare schema (`src/core/config/fairsquare-framework-schema.ts`) serves as a template for creating your own framework schemas. Here's the actual structure: ### 1. Define Semantic Types and Labels ```typescript // Define semantic node types for your framework entities export enum YourFrameworkSemanticNodeType { CUSTOM_CONTROLLER = 'Controller', CUSTOM_SERVICE = 'Service', CUSTOM_REPOSITORY = 'Repository', } // Define semantic edge types for relationships export enum YourFrameworkSemanticEdgeType { INJECTS = 'INJECTS', USES_REPOSITORY = 'USES_REPOSITORY', } // Define Neo4j labels for categorization export enum YourFrameworkLabel { YOUR_FRAMEWORK = 'YourFramework', BUSINESS_LOGIC = 'BusinessLogic', DATA_ACCESS = 'DataAccess', } ``` ### 2. Create Context Extractors Context extractors analyze AST nodes and extract metadata into the `context` property: ```typescript /** * Extract dependencies from decorator arguments * @Injectable([Dep1, Dep2]) → { dependencies: ['Dep1', 'Dep2'] } */ const extractDependencies = ( parsedNode: ParsedNode, allNodes?: Map<string, ParsedNode>, sharedContext?: ParsingContext, ): Record<string, any> => { const node = parsedNode.sourceNode; if (!node || !Node.isClassDeclaration(node)) return {}; const decorators = node.getDecorators(); for (const decorator of decorators) { if (decorator.getName() === 'Injectable') { const args = decorator.getArguments(); if (args.length > 0 && Node.isArrayLiteralExpression(args[0])) { const elements = args[0].getElements(); const dependencies = elements.map((el) => el.getText()); return { dependencies, dependencyCount: dependencies.length, }; } } } return {}; }; ``` ### 3. Define Node Enhancements Node enhancements detect and enrich specific node types: ```typescript export const YOUR_FRAMEWORK_SCHEMA: FrameworkSchema = { name: 'Your Custom Framework', version: '1.0.0', description: 'Custom framework patterns', enhances: [CoreNodeType.CLASS_DECLARATION], metadata: { targetLanguages: ['typescript'], dependencies: ['@your-framework/core'], }, // Global context extractors run on ALL nodes contextExtractors: [ { nodeType: CoreNodeType.CLASS_DECLARATION, extractor: extractDependencies, priority: 10, }, ], enhancements: { // Define a Controller enhancement customController: { name: 'Custom Controller', targetCoreType: CoreNodeType.CLASS_DECLARATION, semanticType: YourFrameworkSemanticNodeType.CUSTOM_CONTROLLER as any, priority: 100, // Detection patterns determine if a node matches this enhancement detectionPatterns: [ { type: 'classname', pattern: /Controller$/, // Matches classes ending with "Controller" confidence: 0.7, priority: 5, }, { type: 'function', pattern: (parsedNode: ParsedNode) => { const node = parsedNode.sourceNode; if (!node || !Node.isClassDeclaration(node)) return false; // Check if extends base Controller class const baseClass = node.getExtends(); return baseClass?.getText() === 'Controller'; }, confidence: 1.0, priority: 10, }, ], // Context extractors specific to this enhancement contextExtractors: [ { nodeType: CoreNodeType.CLASS_DECLARATION, extractor: extractDependencies, priority: 10, }, ], // Additional relationships this node type can have additionalRelationships: [ YourFrameworkSemanticEdgeType.INJECTS as any, ], // Neo4j configuration neo4j: { additionalLabels: [ YourFrameworkLabel.YOUR_FRAMEWORK, YourFrameworkLabel.BUSINESS_LOGIC ], primaryLabel: YourFrameworkSemanticNodeType.CUSTOM_CONTROLLER, }, }, // Define a Service enhancement customService: { name: 'Custom Service', targetCoreType: CoreNodeType.CLASS_DECLARATION, semanticType: YourFrameworkSemanticNodeType.CUSTOM_SERVICE as any, priority: 90, detectionPatterns: [ { type: 'classname', pattern: /Service$/, confidence: 0.8, priority: 5, }, { type: 'decorator', pattern: 'Injectable', confidence: 0.9, priority: 8, }, ], contextExtractors: [], additionalRelationships: [YourFrameworkSemanticEdgeType.USES_REPOSITORY as any], neo4j: { additionalLabels: [YourFrameworkLabel.YOUR_FRAMEWORK], primaryLabel: YourFrameworkSemanticNodeType.CUSTOM_SERVICE, }, }, }, // Edge enhancements detect relationships between nodes edgeEnhancements: { // Detect dependency injection relationships injectsDependency: { name: 'Injects Dependency', semanticType: YourFrameworkSemanticEdgeType.INJECTS as any, // Detection pattern runs for every pair of nodes detectionPattern: ( parsedSourceNode: ParsedNode, parsedTargetNode: ParsedNode, allParsedNodes: Map<string, ParsedNode>, sharedContext: ParsingContext ) => { // Only create edges between class declarations if ( parsedSourceNode.coreType !== CoreNodeType.CLASS_DECLARATION || parsedTargetNode.coreType !== CoreNodeType.CLASS_DECLARATION ) { return false; } // Check if source has target in its dependencies array const sourceContext = parsedSourceNode.properties.context; const targetName = parsedTargetNode.properties.name; if (!sourceContext?.dependencies) return false; return sourceContext.dependencies.some((dep: string) => { const cleanDep = dep.replace(/['"]/g, '').trim(); return cleanDep === targetName; }); }, // Extract additional context for this relationship contextExtractor: ( parsedSourceNode: ParsedNode, parsedTargetNode: ParsedNode, allParsedNodes: Map<string, ParsedNode>, sharedContext: ParsingContext ) => ({ injectionType: 'constructor', framework: 'your-framework', targetDependency: parsedTargetNode.properties.name, }), neo4j: { relationshipType: 'INJECTS', direction: 'OUTGOING', }, }, // Service uses Repository relationship usesRepository: { name: 'Uses Repository', semanticType: YourFrameworkSemanticEdgeType.USES_REPOSITORY as any, detectionPattern: ( parsedSourceNode: ParsedNode, parsedTargetNode: ParsedNode, allParsedNodes: Map<string, ParsedNode>, sharedContext: ParsingContext ) => { // Only Service → Repository edges const isSourceService = parsedSourceNode.semanticType === YourFrameworkSemanticNodeType.CUSTOM_SERVICE; const isTargetRepository = parsedTargetNode.semanticType === YourFrameworkSemanticNodeType.CUSTOM_REPOSITORY; if (!isSourceService || !isTargetRepository) return false; // Check if Service injects this Repository const sourceDeps = parsedSourceNode.properties.context?.dependencies ?? []; const targetName = parsedTargetNode.properties.name; return sourceDeps.some((dep: string) => { const cleanDep = dep.replace(/['"]/g, '').trim(); return cleanDep === targetName; }); }, contextExtractor: (source, target, allNodes, sharedContext) => ({ repositoryName: target.properties.name, serviceName: source.properties.name, }), neo4j: { relationshipType: 'USES_REPOSITORY', direction: 'OUTGOING', }, }, }, }; ``` ### 4. Detection Pattern Types The framework supports multiple detection pattern types: ```typescript // Pattern by class name (regex) { type: 'classname', pattern: /Controller$/, confidence: 0.7, priority: 5, } // Pattern by decorator name (string) { type: 'decorator', pattern: 'Injectable', confidence: 0.9, priority: 8, } // Pattern by filename (regex) { type: 'filename', pattern: /vendor-client/, confidence: 0.9, priority: 8, } // Pattern by custom function (most flexible) { type: 'function', pattern: (parsedNode: ParsedNode) => { const node = parsedNode.sourceNode; if (!node || !Node.isClassDeclaration(node)) return false; // Custom logic - check extends, decorators, file path, etc. const baseClass = node.getExtends(); return baseClass?.getText() === 'Controller'; }, confidence: 1.0, priority: 10, } ``` ## Key Patterns in FairSquare Schema ### Dependency Injection via Decorators ```typescript @Injectable([CreditCheckService, PermissionManager]) export class CreditCheckController extends Controller { // ... } ``` **Detected as**: `INJECTS` relationships from Controller → Services ### Route Definitions ```typescript export const CreditCheckRoutes: ModuleRoute[] = [ { method: 'POST', path: '/v1/credit/check', controller: CreditCheckController, handler: 'post', } ]; ``` **Detected as**: `ROUTES_TO_HANDLER` relationships from Route → Handler Method ### Repository Pattern ```typescript export class UserRepository extends Repository<User> { // ... } ``` **Detected as**: `Repository` semantic type with data access patterns ## Next Steps 1. **Copy the FairSquare schema** as a starting point 2. **Modify detection rules** for your framework's patterns 3. **Add it to parser-factory.ts** (or use as custom schema) 4. **Test with your codebase** For more details, see the main [README.md](../../README.md#creating-custom-framework-schemas).

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/drewdrewH/code-graph-context'

If you have feedback or need assistance with the MCP directory API, please join our Discord server