get_stamp_dependencies
Generate a hierarchical dependency graph for a recursive stamp, mapping all referenced stamps and their relationships. Specify depth, include metadata, and choose output format like tree, graph, mermaid, or JSON.
Instructions
Create a hierarchical dependency graph for a recursive stamp, showing all referenced stamps and their relationships
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| format | No | Output format for the dependency structure | tree |
| include_metadata | No | Include full stamp metadata for each dependency | |
| max_depth | No | Maximum depth to traverse dependencies | |
| resolve_all | No | Attempt to resolve all dependencies (may be slow for deep graphs) | |
| stamp_id | Yes | The ID or CPID of the stamp to map dependencies for |
Implementation Reference
- src/tools/stamp-analysis.ts:472-942 (handler)GetStampDependenciesTool class: the main handler implementing the tool's execute() method, stamp resolution, recursive dependency tree building with cycle detection, and multi-format output rendering (tree, Mermaid diagram, graph, JSON).export class GetStampDependenciesTool extends BaseTool< z.input<typeof GetStampDependenciesParamsSchema>, GetStampDependenciesParams > { public readonly name = 'get_stamp_dependencies'; public readonly description = 'Create a hierarchical dependency graph for a recursive stamp, showing all referenced stamps and their relationships'; public readonly inputSchema: MCPTool['inputSchema'] = { type: 'object', properties: { stamp_id: { type: ['number', 'string'], description: 'The ID or CPID of the stamp to map dependencies for', }, max_depth: { type: 'number', description: 'Maximum depth to traverse dependencies', default: 5, minimum: 1, maximum: 10, }, include_metadata: { type: 'boolean', description: 'Include full stamp metadata for each dependency', default: true, }, format: { type: 'string', enum: ['tree', 'graph', 'mermaid', 'json'], description: 'Output format for the dependency structure', default: 'tree', }, resolve_all: { type: 'boolean', description: 'Attempt to resolve all dependencies (may be slow for deep graphs)', default: true, }, }, required: ['stamp_id'], }; public readonly schema = GetStampDependenciesParamsSchema; public readonly metadata = { version: '1.0.0', tags: ['stamps', 'dependencies', 'visualization', 'mapping'], requiresNetwork: true, apiDependencies: ['stampchain'], }; private apiClient: StampchainClient; private parser: RecursiveStampParser; constructor(apiClient?: StampchainClient) { super(); this.apiClient = apiClient || new StampchainClient(); this.parser = new RecursiveStampParser(); } public async execute( params: GetStampDependenciesParams, context?: ToolContext ): Promise<ToolResponse> { try { context?.logger?.info('Executing get_stamp_dependencies tool', { params }); // Validate parameters const validatedParams = this.validateParams(params); // Resolve the root stamp const rootStamp = await this.resolveStamp(validatedParams.stamp_id, context); // Build the dependency tree const dependencyTree = await this.buildDependencyTree(rootStamp, validatedParams, context); // Format the output based on requested format const formattedOutput = this.formatDependencyTree( dependencyTree, validatedParams.format, context ); // Return both formatted output and raw data return multiResponse( { type: 'text', text: formattedOutput }, { type: 'text', text: `\nRaw Dependency Data:\n${JSON.stringify(dependencyTree, null, 2)}` } ); } catch (error) { context?.logger?.error('Error executing get_stamp_dependencies tool', { error }); if (error instanceof ValidationError) { throw error; } if (error instanceof ToolExecutionError) { throw error; } if (error instanceof Error) { throw new ToolExecutionError(error.message, this.name, error); } throw new ToolExecutionError('Failed to map stamp dependencies', this.name, error); } } /** * Resolve stamp by ID or CPID */ private async resolveStamp(stampId: string | number, context?: ToolContext): Promise<Stamp> { try { if (typeof stampId === 'number') { return await this.apiClient.getStamp(stampId); } const identifier = isStampIdentifier(stampId); if (identifier.type === 'id') { return await this.apiClient.getStamp(identifier.value as number); } else { // Search for CPID and get full stamp data context?.logger?.debug('Searching for stamp by CPID', { cpid: identifier.value }); const searchResults = await this.apiClient.searchStamps({ cpid: identifier.value as string, }); if (searchResults.length === 0) { throw new ToolExecutionError(`No stamp found with CPID: ${identifier.value}`, this.name); } const stampId = searchResults[0].stamp; if (!stampId) { throw new ToolExecutionError(`Invalid stamp ID for CPID: ${identifier.value}`, this.name); } return await this.apiClient.getStamp(stampId); } } catch (error) { if (error instanceof ToolExecutionError) { throw error; } throw new ToolExecutionError( `Failed to resolve stamp: ${error instanceof Error ? error.message : String(error)}`, this.name, error ); } } /** * Build a complete dependency tree for a stamp */ private async buildDependencyTree( rootStamp: Stamp, params: GetStampDependenciesParams, context?: ToolContext, visited: Set<string> = new Set(), currentDepth: number = 0 ): Promise<any> { const rootCpid = rootStamp.cpid; // Prevent infinite recursion if (visited.has(rootCpid) || currentDepth >= params.max_depth) { return { root: this.createTreeNode(rootStamp, [], currentDepth, params.include_metadata), nodes: new Map(), edges: [], totalNodes: 1, maxDepth: currentDepth, circularReferences: visited.has(rootCpid) ? [rootCpid] : [], }; } visited.add(rootCpid); context?.logger?.debug('Building dependency tree', { cpid: rootCpid, depth: currentDepth, visited: visited.size, }); // Parse dependencies from stamp content let dependencies: StampDependency[] = []; if (rootStamp.stamp_base64) { try { const rawContent = Buffer.from(rootStamp.stamp_base64, 'base64').toString('utf8'); const parsedJs = this.parser.parseJavaScript(rawContent); dependencies = parsedJs.dependencies; } catch (error) { context?.logger?.warn('Failed to parse stamp content', { cpid: rootCpid, error }); } } // Resolve child dependencies if requested const childNodes: any[] = []; const allNodes = new Map<string, any>(); const allEdges: Array<{ from: string; to: string; type: string }> = []; const circularRefs: string[] = []; let totalNodes = 1; let maxDepth = currentDepth; if (params.resolve_all && dependencies.length > 0) { for (const dep of dependencies) { // Skip HTML data dependencies (not actual stamps) if (dep.type === 'html' || !dep.cpid || !dep.cpid.match(/^A[0-9]+$/)) { continue; } try { // Check for circular reference if (visited.has(dep.cpid)) { circularRefs.push(dep.cpid); context?.logger?.warn('Circular dependency detected', { from: rootCpid, to: dep.cpid, }); continue; } // Resolve the dependency const searchResults = await this.apiClient.searchStamps({ cpid: dep.cpid }); if (searchResults.length > 0) { const depStamp = searchResults[0]; const depStampId = depStamp.stamp; if (depStampId) { const fullDepStamp = await this.apiClient.getStamp(depStampId); // Recursively build child tree const childTree = await this.buildDependencyTree( fullDepStamp, params, context, new Set(visited), currentDepth + 1 ); childNodes.push(childTree.root); // Merge child tree data childTree.nodes.forEach((node: any, cpid: string) => allNodes.set(cpid, node)); allEdges.push(...childTree.edges); circularRefs.push(...childTree.circularReferences); totalNodes += childTree.totalNodes; maxDepth = Math.max(maxDepth, childTree.maxDepth); // Add edge from root to child allEdges.push({ from: rootCpid, to: dep.cpid, type: dep.loadMethod, }); } } } catch (error) { context?.logger?.warn('Failed to resolve dependency', { cpid: dep.cpid, error: error instanceof Error ? error.message : String(error), }); } } } // Create the root node const rootNode = this.createTreeNode( rootStamp, childNodes, currentDepth, params.include_metadata ); allNodes.set(rootCpid, rootNode); return { root: rootNode, nodes: allNodes, edges: allEdges, totalNodes, maxDepth, circularReferences: [...new Set(circularRefs)], }; } /** * Create a tree node from stamp data */ private createTreeNode( stamp: Stamp, children: any[], depth: number, includeMetadata: boolean ): any { const node: any = { cpid: stamp.cpid, stampId: stamp.stamp || 0, depth, children, metadata: includeMetadata ? { creator: stamp.creator, mimetype: stamp.stamp_mimetype, blockIndex: stamp.block_index, txHash: stamp.tx_hash, supply: stamp.supply || 0, locked: Boolean(stamp.locked), } : undefined, }; return node; } /** * Format dependency tree based on requested output format */ private formatDependencyTree(tree: any, format: string, context?: ToolContext): string { switch (format) { case 'tree': return this.formatAsTree(tree); case 'mermaid': return this.formatAsMermaid(tree); case 'graph': return this.formatAsGraph(tree); case 'json': return JSON.stringify(tree, null, 2); default: return this.formatAsTree(tree); } } /** * Format dependency tree as a tree structure */ private formatAsTree(tree: any): string { const lines: string[] = []; lines.push('π³ Stamp Dependency Tree'); lines.push('========================'); lines.push(''); // Summary lines.push(`π Tree Summary:`); lines.push(` Total Nodes: ${tree.totalNodes}`); lines.push(` Max Depth: ${tree.maxDepth}`); lines.push(` Edges: ${tree.edges.length}`); if (tree.circularReferences.length > 0) { lines.push(` β οΈ Circular References: ${tree.circularReferences.length}`); } lines.push(''); // Tree structure lines.push('ποΈ Dependency Structure:'); this.renderTreeNode(tree.root, lines, '', true); // Circular references warning if (tree.circularReferences.length > 0) { lines.push(''); lines.push('β οΈ Circular References Detected:'); tree.circularReferences.forEach((cpid: string) => { lines.push(` - ${cpid}`); }); } return lines.join('\n'); } /** * Recursively render tree nodes */ private renderTreeNode(node: any, lines: string[], prefix: string, isLast: boolean): void { const connector = isLast ? 'βββ ' : 'βββ '; const stampInfo = node.metadata ? ` (ID: ${node.stampId}, Creator: ${node.metadata.creator?.substring(0, 20)}...)` : ` (ID: ${node.stampId})`; lines.push(`${prefix}${connector}π ${node.cpid}${stampInfo}`); if (node.metadata) { const childPrefix = prefix + (isLast ? ' ' : 'β '); lines.push(`${childPrefix}ββ Type: ${node.metadata.mimetype}`); lines.push(`${childPrefix}ββ Block: ${node.metadata.blockIndex}`); lines.push(`${childPrefix}ββ Supply: ${node.metadata.supply}`); } // Render children const newPrefix = prefix + (isLast ? ' ' : 'β '); node.children.forEach((child: any, index: number) => { const childIsLast = index === node.children.length - 1; this.renderTreeNode(child, lines, newPrefix, childIsLast); }); } /** * Format dependency tree as Mermaid diagram */ private formatAsMermaid(tree: any): string { const lines: string[] = []; lines.push('```mermaid'); lines.push('graph TD'); lines.push(''); // Add nodes tree.nodes.forEach((node: any, cpid: string) => { const shortCpid = cpid.substring(0, 8) + '...'; const nodeLabel = `${shortCpid}<br/>ID: ${node.stampId}`; lines.push(` ${this.sanitizeMermaidId(cpid)}["${nodeLabel}"]`); }); lines.push(''); // Add edges tree.edges.forEach((edge: any) => { const fromId = this.sanitizeMermaidId(edge.from); const toId = this.sanitizeMermaidId(edge.to); lines.push(` ${fromId} -->|${edge.type}| ${toId}`); }); // Add circular reference warnings if (tree.circularReferences.length > 0) { lines.push(''); lines.push(' %% Circular references detected:'); tree.circularReferences.forEach((cpid: string) => { lines.push(` %% - ${cpid}`); }); } lines.push('```'); lines.push(''); lines.push( `**Graph Statistics:** ${tree.totalNodes} nodes, ${tree.edges.length} edges, max depth ${tree.maxDepth}` ); return lines.join('\n'); } /** * Format dependency tree as adjacency list */ private formatAsGraph(tree: any): string { const lines: string[] = []; lines.push('π Dependency Graph (Adjacency List)'); lines.push('===================================='); lines.push(''); tree.nodes.forEach((node: any, cpid: string) => { lines.push(`π ${cpid} (ID: ${node.stampId}, Depth: ${node.depth})`); // Find outgoing edges const outgoingEdges = tree.edges.filter((edge: any) => edge.from === cpid); if (outgoingEdges.length > 0) { outgoingEdges.forEach((edge: any) => { lines.push(` ββ ${edge.type} β ${edge.to}`); }); } else { lines.push(' ββ (no dependencies)'); } lines.push(''); }); return lines.join('\n'); } /** * Sanitize CPID for Mermaid node IDs */ private sanitizeMermaidId(cpid: string): string { return cpid.replace(/[^a-zA-Z0-9]/g, '_'); } }
- Zod schema (GetStampDependenciesParamsSchema) and inferred TypeScript type for input parameters validation.* Schema for get_stamp_dependencies parameters */ export const GetStampDependenciesParamsSchema = z.object({ stamp_id: z .union([z.number(), z.string()]) .describe('The ID or CPID of the stamp to map dependencies for'), max_depth: z .number() .min(1) .max(10) .default(5) .describe('Maximum depth to traverse dependencies'), include_metadata: z .boolean() .default(true) .describe('Include full stamp metadata for each dependency'), format: z .enum(['tree', 'graph', 'mermaid', 'json']) .default('tree') .describe('Output format for the dependency structure'), resolve_all: z .boolean() .default(true) .describe('Attempt to resolve all dependencies (may be slow for deep graphs)'), }); // Type exports export type StampDependency = z.infer<typeof StampDependencySchema>; export type DetectedPattern = z.infer<typeof DetectedPatternSchema>; export type CodeStructure = z.infer<typeof CodeStructureSchema>; export type SecurityAnalysis = z.infer<typeof SecurityAnalysisSchema>; export type PerformanceAnalysis = z.infer<typeof PerformanceAnalysisSchema>; export type StampAnalysisResult = z.infer<typeof StampAnalysisResultSchema>; export type DependencyGraphNode = z.infer<typeof DependencyGraphNodeSchema>; export type DependencyGraphEdge = z.infer<typeof DependencyGraphEdgeSchema>; export type DependencyGraph = z.infer<typeof DependencyGraphSchema>; export type LibraryUsage = z.infer<typeof LibraryUsageSchema>; export type LibraryUsageReport = z.infer<typeof LibraryUsageReportSchema>; export type AnalyzeStampCodeParams = z.infer<typeof AnalyzeStampCodeParamsSchema>; export type BuildDependencyGraphParams = z.infer<typeof BuildDependencyGraphParamsSchema>; export type AnalyzeLibraryUsageParams = z.infer<typeof AnalyzeLibraryUsageParamsSchema>; export type GetStampDependenciesParams = z.infer<typeof GetStampDependenciesParamsSchema>;
- src/tools/stamp-analysis.ts:1763-1778 (registration)Tool registration: Exports the GetStampDependenciesTool class and creates instances via factory function createStampAnalysisTools, imported and aggregated in src/tools/index.ts.export const stampAnalysisTools = { analyze_stamp_code: AnalyzeStampCodeTool, get_stamp_dependencies: GetStampDependenciesTool, analyze_stamp_patterns: AnalyzeStampPatternsTool, }; /** * Factory function to create stamp analysis tool instances */ export function createStampAnalysisTools(apiClient?: StampchainClient) { return { analyze_stamp_code: new AnalyzeStampCodeTool(apiClient), get_stamp_dependencies: new GetStampDependenciesTool(apiClient), analyze_stamp_patterns: new AnalyzeStampPatternsTool(apiClient), }; }
- src/tools/index.ts:47-70 (registration)Lists 'get_stamp_dependencies' in getAvailableToolNames() for tool discovery.export function getAvailableToolNames(): string[] { return [ // Stamp tools 'get_stamp', 'search_stamps', 'get_recent_stamps', 'get_recent_sales', 'get_market_data', 'get_stamp_market_data', // Collection tools 'get_collection', 'search_collections', // Token tools 'get_token_info', 'search_tokens', // Analysis tools 'analyze_stamp_code', 'get_stamp_dependencies', 'analyze_stamp_patterns', ]; }
- src/tools/index.ts:98-101 (registration)Includes 'get_stamp_dependencies' in toolMetadata for categorization and discovery.analysis: { category: 'Recursive Stamp Analysis', description: 'Tools for analyzing recursive stamp code structure and dependencies', tools: ['analyze_stamp_code', 'get_stamp_dependencies', 'analyze_stamp_patterns'],