Skip to main content
Glama

mcp-github-project-manager

ResourceRelationshipManager.ts6.23 kB
import { Resource, ResourceType, RelationshipType, Relationship, ResourceStatus } from "../../domain/resource-types"; import { ResourceCache } from "../cache/ResourceCache"; export class ResourceRelationshipManager { private static RELATIONSHIP_NAMESPACE = "relationships"; private static RELATIONSHIP_TAG = "relationship"; constructor(private cache: ResourceCache) {} /** * Create a relationship between two resources */ async createRelationship( sourceId: string, sourceType: ResourceType, targetId: string, targetType: ResourceType, relationshipType: RelationshipType, metadata?: Record<string, unknown> ): Promise<Relationship> { // Create a unique ID for the relationship const id = `rel_${sourceId}_${targetId}_${relationshipType}`; // Create Date objects instead of strings const now = new Date(); const relationship: Relationship = { id, type: ResourceType.RELATIONSHIP, // Using the RELATIONSHIP ResourceType version: 1, status: ResourceStatus.ACTIVE, createdAt: now.toISOString(), // Convert Date to string updatedAt: now.toISOString(), // Convert Date to string sourceId, sourceType, targetId, targetType, relationshipType }; // Store in cache with appropriate tags await this.cache.set(ResourceType.RELATIONSHIP, id, relationship, { tags: [ ResourceRelationshipManager.RELATIONSHIP_TAG, `source:${sourceId}`, `target:${targetId}`, `type:${relationshipType}`, ], namespaces: [ResourceRelationshipManager.RELATIONSHIP_NAMESPACE], }); return relationship; } /** * Delete a relationship */ async deleteRelationship(relationshipId: string): Promise<void> { await this.cache.delete(relationshipId); } /** * Get all relationships where a resource is the source */ async getOutgoingRelationships( resourceId: string, type?: RelationshipType ): Promise<Relationship[]> { const relationships = await this.cache.getByTag(`source:${resourceId}`) as Relationship[]; if (!type) { return relationships; } // Make sure to compare relationshipType, not the Resource type return relationships.filter(rel => rel.relationshipType === type); } /** * Get all relationships where a resource is the target */ async getIncomingRelationships( resourceId: string, type?: RelationshipType ): Promise<Relationship[]> { const relationships = await this.cache.getByTag(`target:${resourceId}`) as Relationship[]; if (!type) { return relationships; } // Make sure to compare relationshipType, not the Resource type return relationships.filter(rel => rel.relationshipType === type); } /** * Get all relationships of a specific type */ async getRelationshipsByType(type: RelationshipType): Promise<Relationship[]> { const relationships = await this.cache.getByTag(`type:${type}`); // Properly cast the result to Relationship[] instead of just using 'as' return relationships.filter(rel => rel.type === ResourceType.RELATIONSHIP && (rel as Relationship).relationshipType === type ) as Relationship[]; } /** * Get all related resources of a specific type */ async getRelatedResources<T extends Resource>( resourceId: string, targetType: ResourceType, relationshipType?: RelationshipType ): Promise<T[]> { // Get outgoing relationships that match the criteria const relationships = await this.getOutgoingRelationships(resourceId, relationshipType); // Filter by target type const matchingRelationships = relationships.filter( rel => rel.targetType === targetType ); // Get the actual resources const resources: T[] = []; for (const rel of matchingRelationships) { const resource = await this.cache.get<T>(rel.targetType, rel.targetId); if (resource) { resources.push(resource); } } return resources; } /** * Find resources that depend on the given resource */ async getDependentResources<T extends Resource>( resourceId: string ): Promise<T[]> { // Get incoming dependency relationships const relationships = await this.getIncomingRelationships( resourceId, RelationshipType.DEPENDENCY ); // Get the actual resources const resources: T[] = []; for (const rel of relationships) { const resource = await this.cache.get<T>(rel.sourceType, rel.sourceId); if (resource) { resources.push(resource); } } return resources; } /** * Find parent resources */ async getParentResources<T extends Resource>( resourceId: string ): Promise<T[]> { // Get incoming parent-child relationships const relationships = await this.getIncomingRelationships( resourceId, RelationshipType.PARENT_CHILD ); // Get the actual resources const resources: T[] = []; for (const rel of relationships) { const resource = await this.cache.get<T>(rel.sourceType, rel.sourceId); if (resource) { resources.push(resource); } } return resources; } /** * Find child resources */ async getChildResources<T extends Resource>( resourceId: string ): Promise<T[]> { // Get outgoing parent-child relationships const relationships = await this.getOutgoingRelationships( resourceId, RelationshipType.PARENT_CHILD ); // Get the actual resources const resources: T[] = []; for (const rel of relationships) { const resource = await this.cache.get<T>(rel.targetType, rel.targetId); if (resource) { resources.push(resource); } } return resources; } /** * Delete all relationships for a resource */ async deleteAllRelationships(resourceId: string): Promise<void> { const outgoing = await this.getOutgoingRelationships(resourceId); const incoming = await this.getIncomingRelationships(resourceId); const allRelationships = [...outgoing, ...incoming]; for (const rel of allRelationships) { await this.cache.delete(rel.id); } } }

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/kunwarVivek/mcp-github-project-manager'

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