Skip to main content
Glama

check_coupling_cohesion

Analyze code structure to measure coupling and cohesion, identify dependencies, and assess module quality for better maintainability.

Instructions

coupling|cohesion|coupling|cohesion|dependencies check|module structure - Check coupling and cohesion

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
codeYesCode to analyze
typeNoCode type
checkDependenciesNoAnalyze dependencies

Implementation Reference

  • Main handler function implementing coupling and cohesion analysis using AST parsing (ts-morph), regex for dependencies/fan-out, type-specific cohesion metrics (class/module/function/component), scoring, issues, and recommendations.
    export async function checkCouplingCohesion(args: { code: string; type?: string; checkDependencies?: boolean }): Promise<ToolResult> {
      const { code: couplingCode, type: couplingType = 'function', checkDependencies = false } = args;
      
      const couplingAnalysis = {
        action: 'check_coupling_cohesion',
        type: couplingType,
        checkDependencies,
        results: {} as any,
        overallScore: 0,
        issues: [] as string[],
        recommendations: [] as string[],
        status: 'pending' as string
      };
    
      // AST 기반 의존성/구조 분석
      try {
        const sourceFile = AST_PROJECT.createSourceFile('temp.ts', couplingCode, {
          overwrite: true,
          scriptKind: ScriptKind.TS
        });
        // Import/Require 분석
        const importDecls = sourceFile.getImportDeclarations();
        const requireCalls = sourceFile.getDescendantsOfKind(ts.SyntaxKind.CallExpression).filter(call => call.getExpression().getText() === 'require');
        // 클래스/함수/모듈 구조 분석
        const classDecls = sourceFile.getClasses();
        const funcDecls = sourceFile.getFunctions();
        const exportDecls = sourceFile.getExportDeclarations();
        couplingAnalysis.results.ast = {
          importCount: importDecls.length,
          requireCount: requireCalls.length,
          classCount: classDecls.length,
          functionCount: funcDecls.length,
          exportCount: exportDecls.length,
          importModules: importDecls.map(d => d.getModuleSpecifierValue()),
          exportedNames: exportDecls.map(d => d.getNamedExports().map(e => e.getName()))
        };
      } catch (e) {
        couplingAnalysis.results.ast = {
          error: 'AST analysis failed: ' + (e instanceof Error ? e.message : String(e))
        };
      }
      
      // Dependency analysis (Coupling)
      const imports = (couplingCode.match(/import\s+.*?\s+from\s+['"](.*?)['"]/g) || []).length;
      const requires = (couplingCode.match(/require\s*\(\s*['"](.*?)['"]\s*\)/g) || []).length;
      const totalDependencies = imports + requires;
      
      // External dependencies
      const externalDeps = (couplingCode.match(/import\s+.*?\s+from\s+['"](?!\.)(.*?)['"]/g) || []).length;
      const internalDeps = totalDependencies - externalDeps;
      
      // Function calls (fan-out)
      const functionCalls = (couplingCode.match(/\w+\s*\(/g) || []).length;
      const uniqueFunctionCalls = new Set((couplingCode.match(/\w+\s*\(/g) || []).map(call => call.replace(/\s*\(/, ''))).size;
      
      couplingAnalysis.results.coupling = {
        totalDependencies: totalDependencies,
        externalDependencies: externalDeps,
        internalDependencies: internalDeps,
        functionCalls: functionCalls,
        uniqueFunctionCalls: uniqueFunctionCalls,
        threshold: CODE_QUALITY_METRICS.COUPLING.maxDependencies,
        status: totalDependencies <= CODE_QUALITY_METRICS.COUPLING.maxDependencies ? 'pass' : 'fail',
        fanOut: uniqueFunctionCalls,
        fanOutStatus: uniqueFunctionCalls <= CODE_QUALITY_METRICS.COUPLING.maxFanOut ? 'pass' : 'fail'
      };
      
      // Cohesion analysis
      let cohesionScore = 0;
      let cohesionLevel = 'low';
      
      if (couplingType === 'class') {
        // Class cohesion: methods using same data
        const methods = (couplingCode.match(/\w+\s*\([^)]*\)\s*\{/g) || []).length;
        const properties = (couplingCode.match(/this\.\w+/g) || []).length;
        const uniqueProperties = new Set((couplingCode.match(/this\.(\w+)/g) || []).map(prop => prop.replace('this.', ''))).size;
        
        // LCOM (Lack of Cohesion in Methods) - simplified calculation
        // Use total property accesses to weight the cohesion score
        const propertyUsage = methods > 0 ? uniqueProperties / methods : 0;
        const accessDensity = methods > 0 ? properties / methods : 0;
        cohesionScore = propertyUsage * Math.min(1, accessDensity);
        cohesionLevel = cohesionScore > 0.7 ? 'high' : cohesionScore > 0.4 ? 'medium' : 'low';
        
        couplingAnalysis.results.cohesion = {
          type: 'class',
          methods: methods,
          properties: uniqueProperties,
          totalPropertyAccesses: properties,
          propertyUsageRatio: Math.round(propertyUsage * 100) / 100,
          score: cohesionScore,
          level: cohesionLevel,
          status: cohesionScore > 0.4 ? 'pass' : 'fail'
        };
      } else if (couplingType === 'module') {
        // Module cohesion: related functions and exports
        const functions = (couplingCode.match(/function\s+\w+|const\s+\w+\s*=\s*\(/g) || []).length;
        const exports = (couplingCode.match(/export\s+/g) || []).length;
        const variables = (couplingCode.match(/(?:const|let|var)\s+\w+/g) || []).length;
        
        // Functional cohesion: ratio of related functions
        const cohesionRatio = functions > 0 ? exports / functions : 0;
        cohesionScore = cohesionRatio;
        cohesionLevel = cohesionScore > 0.6 ? 'high' : cohesionScore > 0.3 ? 'medium' : 'low';
        
        couplingAnalysis.results.cohesion = {
          type: 'module',
          functions: functions,
          exports: exports,
          variables: variables,
          cohesionRatio: Math.round(cohesionRatio * 100) / 100,
          score: cohesionScore,
          level: cohesionLevel,
          status: cohesionScore > 0.3 ? 'pass' : 'fail'
        };
      } else if (couplingType === 'function') {
        // Function cohesion: single responsibility principle
        const statements = (couplingCode.match(/;/g) || []).length;
        const returns = (couplingCode.match(/\breturn\b/g) || []).length;
        const variables = (couplingCode.match(/(?:const|let|var)\s+\w+/g) || []).length;
        
        // Sequential cohesion: operations flow in sequence
        const lines = couplingCode.split('\n').filter(line => line.trim().length > 0).length;
        const complexityIndicators = (couplingCode.match(/\bif\b|\bfor\b|\bwhile\b|\bswitch\b/g) || []).length;
        
        cohesionScore = lines > 0 ? 1 - (complexityIndicators / lines) : 0;
        cohesionLevel = cohesionScore > 0.7 ? 'high' : cohesionScore > 0.4 ? 'medium' : 'low';
        
        couplingAnalysis.results.cohesion = {
          type: 'function',
          statements: statements,
          returns: returns,
          variables: variables,
          lines: lines,
          complexityIndicators: complexityIndicators,
          score: Math.round(cohesionScore * 100) / 100,
          level: cohesionLevel,
          status: cohesionScore > 0.4 ? 'pass' : 'fail'
        };
      } else if (couplingType === 'component') {
        // React component cohesion: props and state usage
        const props = (couplingCode.match(/props\.\w+/g) || []).length;
        const state = (couplingCode.match(/useState|useReducer/g) || []).length;
        const effects = (couplingCode.match(/useEffect/g) || []).length;
        const hooks = (couplingCode.match(/use\w+/g) || []).length;
        
        // Component cohesion: how well props, state, and effects are related
        const totalElements = props + state + effects;
        const cohesionRatio = totalElements > 0 ? hooks / totalElements : 0;
        cohesionScore = Math.min(1, cohesionRatio);
        cohesionLevel = cohesionScore > 0.6 ? 'high' : cohesionScore > 0.3 ? 'medium' : 'low';
        
        couplingAnalysis.results.cohesion = {
          type: 'component',
          props: props,
          state: state,
          effects: effects,
          hooks: hooks,
          cohesionRatio: Math.round(cohesionRatio * 100) / 100,
          score: Math.round(cohesionScore * 100) / 100,
          level: cohesionLevel,
          status: cohesionScore > 0.3 ? 'pass' : 'fail'
        };
      }
      
      // Overall assessment
      const issues = [];
      let overallScore = 100;
      
      if (couplingAnalysis.results.coupling.status === 'fail') {
        issues.push('High coupling detected - too many dependencies');
        overallScore -= 30;
      }
      
      if (couplingAnalysis.results.coupling.fanOutStatus === 'fail') {
        issues.push('High fan-out detected - too many function calls');
        overallScore -= 20;
      }
      
      if (couplingAnalysis.results.cohesion.status === 'fail') {
        issues.push('Low cohesion detected - poor single responsibility');
        overallScore -= 25;
      }
      
      couplingAnalysis.overallScore = Math.max(0, overallScore);
      couplingAnalysis.issues = issues;
      couplingAnalysis.recommendations = [];
      
      if (couplingAnalysis.results.coupling.status === 'fail') {
        couplingAnalysis.recommendations.push('Reduce dependencies by using dependency injection');
        couplingAnalysis.recommendations.push('Consider using interfaces to abstract dependencies');
      }
      
      if (couplingAnalysis.results.cohesion.status === 'fail') {
        couplingAnalysis.recommendations.push('Ensure single responsibility principle');
        couplingAnalysis.recommendations.push('Group related functionality together');
        couplingAnalysis.recommendations.push('Extract unrelated code into separate modules');
      }
      
      if (couplingAnalysis.recommendations.length === 0) {
        couplingAnalysis.recommendations.push('Coupling and cohesion are within acceptable ranges');
      }
      
      couplingAnalysis.status = 'success';
      
      return {
        content: [{ type: 'text', text: `Type: ${couplingType}\nScore: ${couplingAnalysis.overallScore}/100\n\nCoupling: ${couplingAnalysis.results.coupling.totalDependencies} deps (${couplingAnalysis.results.coupling.status}) | Fan-out: ${couplingAnalysis.results.coupling.fanOut} (${couplingAnalysis.results.coupling.fanOutStatus})\nCohesion: ${cohesionScore.toFixed(2)} (${cohesionLevel}, ${couplingAnalysis.results.cohesion.status})\n\nIssues (${couplingAnalysis.issues.length}):\n${couplingAnalysis.issues.map(i => `- ${i}`).join('\n')}\n\nRecommendations:\n${couplingAnalysis.recommendations.map(r => `- ${r}`).join('\n')}` }]
      };
    }
  • ToolDefinition schema defining input parameters and metadata for the check_coupling_cohesion tool.
    export const checkCouplingCohesionDefinition: ToolDefinition = {
      name: 'check_coupling_cohesion',
      description: 'coupling|cohesion|coupling|cohesion|dependencies check|module structure - Check coupling and cohesion',
      inputSchema: {
        type: 'object',
        properties: {
          code: { type: 'string', description: 'Code to analyze' },
          type: { type: 'string', description: 'Code type', enum: ['class', 'module', 'function', 'component'] },
          checkDependencies: { type: 'boolean', description: 'Analyze dependencies' }
        },
        required: ['code']
      },
      annotations: {
        title: 'Check Coupling & Cohesion',
        audience: ['user', 'assistant']
      }
    };
  • src/index.ts:666-667 (registration)
    Registration in the main tool dispatcher switch case within executeToolCall function.
    case 'check_coupling_cohesion':
      return await checkCouplingCohesion(args as any) as CallToolResult;
  • src/index.ts:141-141 (registration)
    Inclusion of the tool definition in the tools array used for ListToolsRequest.
    checkCouplingCohesionDefinition,
  • Configuration constants for quality metrics thresholds used in coupling and cohesion checks.
    const CODE_QUALITY_METRICS = {
      COMPLEXITY: {
        maxCyclomaticComplexity: 10,
        maxCognitiveComplexity: 15,
        maxFunctionLines: 20,
        maxNestingDepth: 3,
        maxParameters: 5
      },
      COUPLING: {
        maxDependencies: 7,
        maxFanOut: 5,
        preventCircularDeps: true
      },
      COHESION: {
        singleResponsibility: true,
        relatedFunctionsOnly: true
      },
      MAINTAINABILITY: {
        noMagicNumbers: true,
        consistentNaming: true,
        properErrorHandling: true,
        typesSafety: true
      },
      PERFORMANCE: {
        memoizeExpensiveCalc: true,
        lazyLoading: true,
        batchOperations: true
      }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only provide a title ('Check Coupling & Cohesion'), which doesn't cover behavioral traits like read-only or destructive hints. The description doesn't add any behavioral context beyond the name, such as what the analysis entails, output format, or any constraints. However, it doesn't contradict annotations, so it's scored as minimal but not misleading.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is repetitive and poorly structured, with redundant terms like 'coupling|cohesion' repeated and a fragmented format ('dependencies check|module structure - Check coupling and cohesion'). It's not front-loaded with clear information, wasting space without adding value, making it inefficient rather than concise.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (analyzing code for coupling and cohesion with 3 parameters), lack of output schema, and minimal annotations, the description is incomplete. It fails to explain what the tool returns, how results are interpreted, or any usage context, leaving significant gaps for an agent to understand and use the tool effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with clear descriptions for all three parameters (code, type, checkDependencies). The description adds no additional meaning about parameters beyond what the schema provides, such as explaining how 'code' should be formatted or what 'checkDependencies' entails. Baseline score of 3 is appropriate given high schema coverage.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description is tautological, essentially restating the tool name 'check_coupling_cohesion' with variations like 'coupling|cohesion|coupling|cohesion|dependencies check|module structure - Check coupling and cohesion'. It doesn't specify what the tool actually does beyond checking these metrics, failing to distinguish it from sibling tools like 'analyze_complexity' or 'validate_code_quality' in a meaningful way.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this tool versus alternatives. The description provides no context, prerequisites, or exclusions, leaving the agent with no information on appropriate scenarios for invoking this tool compared to other analysis tools in the sibling list.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/ssdeanx/ssd-ai'

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