Skip to main content
Glama
dependencies-schemas.ts9.48 kB
import { z } from 'zod'; // Dependency types export const DependencyTypeSchema = z.enum([ 'blocking', // Task A blocks Task B (A must finish before B can start) 'waiting_on', // Task A is waiting on Task B (A cannot start until B finishes) 'linked' // Tasks are linked but not blocking ]); // Dependency status export const DependencyStatusSchema = z.enum([ 'active', 'resolved', 'broken', 'ignored' ]); // Create dependency schema export const CreateDependencySchema = z.object({ task_id: z.string().min(1).describe('The ID of the task that depends on another'), depends_on: z.string().min(1).describe('The ID of the task that this task depends on'), type: DependencyTypeSchema.default('blocking').describe('The type of dependency relationship'), link_id: z.string().optional().describe('Optional link ID for grouping related dependencies') }); // Update dependency schema export const UpdateDependencySchema = z.object({ dependency_id: z.string().min(1).describe('The ID of the dependency to update'), type: DependencyTypeSchema.optional().describe('New dependency type'), status: DependencyStatusSchema.optional().describe('New dependency status') }); // Get dependencies filter schema export const GetDependenciesFilterSchema = z.object({ task_id: z.string().min(1).describe('The ID of the task to get dependencies for'), type: DependencyTypeSchema.optional().describe('Filter by dependency type'), status: DependencyStatusSchema.optional().describe('Filter by dependency status'), include_resolved: z.boolean().default(false).describe('Whether to include resolved dependencies') }); // Dependency graph options schema export const DependencyGraphOptionsSchema = z.object({ task_id: z.string().min(1).describe('The root task ID for the dependency graph'), depth: z.number().min(1).max(10).default(3).describe('Maximum depth to traverse in the graph'), direction: z.enum(['upstream', 'downstream', 'both']).default('both').describe('Direction to traverse dependencies'), include_resolved: z.boolean().default(false).describe('Whether to include resolved dependencies'), include_broken: z.boolean().default(true).describe('Whether to include broken dependencies') }); // Dependency conflict check schema export const DependencyConflictCheckSchema = z.object({ task_id: z.string().min(1).describe('The task ID to check for conflicts'), proposed_dependencies: z.array(z.object({ depends_on: z.string(), type: DependencyTypeSchema })).optional().describe('Proposed new dependencies to check for conflicts') }); // Bulk dependency operations schema export const BulkDependencyOperationSchema = z.object({ operation: z.enum(['create', 'delete', 'update']).describe('The bulk operation to perform'), dependencies: z.array(z.union([ CreateDependencySchema, z.object({ dependency_id: z.string() }), // For delete operations UpdateDependencySchema ])).describe('Array of dependency operations to perform') }); // Type exports export type DependencyType = z.infer<typeof DependencyTypeSchema>; export type DependencyStatus = z.infer<typeof DependencyStatusSchema>; export type CreateDependencyRequest = z.infer<typeof CreateDependencySchema>; export type UpdateDependencyRequest = z.infer<typeof UpdateDependencySchema>; export type GetDependenciesFilter = z.infer<typeof GetDependenciesFilterSchema>; export type DependencyGraphOptions = z.infer<typeof DependencyGraphOptionsSchema>; export type DependencyConflictCheck = z.infer<typeof DependencyConflictCheckSchema>; export type BulkDependencyOperation = z.infer<typeof BulkDependencyOperationSchema>; // Dependency response interfaces export interface DependencyResponse { id: string; task_id: string; depends_on: string; type: DependencyType; status: DependencyStatus; link_id?: string; date_created: string; date_updated: string; created_by: { id: number; username: string; email: string; }; task_info: { id: string; name: string; status: { status: string; color: string; }; assignees: Array<{ id: number; username: string; }>; due_date?: string; url: string; }; depends_on_info: { id: string; name: string; status: { status: string; color: string; }; assignees: Array<{ id: number; username: string; }>; due_date?: string; url: string; }; } export interface DependencyListResponse { dependencies: DependencyResponse[]; total_count: number; } export interface DependencyGraphNode { task_id: string; task_name: string; task_status: string; task_url: string; level: number; dependencies: Array<{ id: string; type: DependencyType; status: DependencyStatus; target_task_id: string; }>; dependents: Array<{ id: string; type: DependencyType; status: DependencyStatus; source_task_id: string; }>; } export interface DependencyGraphResponse { root_task_id: string; nodes: DependencyGraphNode[]; edges: Array<{ id: string; source: string; target: string; type: DependencyType; status: DependencyStatus; }>; cycles: Array<{ cycle_id: string; task_ids: string[]; description: string; }>; critical_path?: Array<{ task_id: string; task_name: string; duration_days: number; }>; } export interface DependencyConflictResponse { has_conflicts: boolean; conflicts: Array<{ type: 'circular' | 'duplicate' | 'invalid_status'; description: string; affected_tasks: string[]; suggested_resolution: string; }>; warnings: Array<{ type: 'performance' | 'complexity' | 'timeline'; description: string; affected_tasks: string[]; }>; } // Utility functions export const getDependencyDirection = (type: DependencyType): 'forward' | 'backward' => { switch (type) { case 'blocking': return 'forward'; case 'waiting_on': return 'backward'; case 'linked': return 'forward'; default: return 'forward'; } }; export const getOppositeDependencyType = (type: DependencyType): DependencyType => { switch (type) { case 'blocking': return 'waiting_on'; case 'waiting_on': return 'blocking'; case 'linked': return 'linked'; default: return type; } }; export const validateDependencyChain = (dependencies: CreateDependencyRequest[]): { isValid: boolean; cycles: string[][]; errors: string[]; } => { const graph = new Map<string, Set<string>>(); const errors: string[] = []; // Build adjacency list dependencies.forEach(dep => { if (!graph.has(dep.task_id)) { graph.set(dep.task_id, new Set()); } if (!graph.has(dep.depends_on)) { graph.set(dep.depends_on, new Set()); } if (dep.type === 'blocking') { graph.get(dep.depends_on)?.add(dep.task_id); } else if (dep.type === 'waiting_on') { graph.get(dep.task_id)?.add(dep.depends_on); } }); // Detect cycles using DFS const cycles: string[][] = []; const visited = new Set<string>(); const recursionStack = new Set<string>(); const dfs = (node: string, path: string[]): void => { visited.add(node); recursionStack.add(node); path.push(node); const neighbors = graph.get(node) || new Set(); for (const neighbor of neighbors) { if (!visited.has(neighbor)) { dfs(neighbor, [...path]); } else if (recursionStack.has(neighbor)) { // Found a cycle const cycleStart = path.indexOf(neighbor); if (cycleStart !== -1) { cycles.push(path.slice(cycleStart)); } } } recursionStack.delete(node); }; // Check all nodes for cycles for (const node of graph.keys()) { if (!visited.has(node)) { dfs(node, []); } } if (cycles.length > 0) { errors.push(`Circular dependencies detected: ${cycles.map(cycle => cycle.join(' -> ')).join(', ')}`); } return { isValid: cycles.length === 0, cycles, errors }; }; export const calculateCriticalPath = (nodes: DependencyGraphNode[]): DependencyGraphNode[] => { // Simplified critical path calculation // In a real implementation, this would consider task durations and dates const nodeMap = new Map(nodes.map(node => [node.task_id, node])); const visited = new Set<string>(); const criticalPath: DependencyGraphNode[] = []; // Find the longest path through the dependency graph const findLongestPath = (nodeId: string, currentPath: DependencyGraphNode[]): DependencyGraphNode[] => { if (visited.has(nodeId)) return currentPath; visited.add(nodeId); const node = nodeMap.get(nodeId); if (!node) return currentPath; const pathWithNode = [...currentPath, node]; let longestPath = pathWithNode; // Explore all dependencies for (const dep of node.dependencies) { const childPath = findLongestPath(dep.target_task_id, pathWithNode); if (childPath.length > longestPath.length) { longestPath = childPath; } } return longestPath; }; // Start from nodes with no dependencies (root nodes) const rootNodes = nodes.filter(node => node.dependencies.length === 0); for (const rootNode of rootNodes) { const path = findLongestPath(rootNode.task_id, []); if (path.length > criticalPath.length) { criticalPath.splice(0, criticalPath.length, ...path); } } return criticalPath; };

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/Chykalophia/ClickUp-MCP-Server---Enhanced'

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