Skip to main content
Glama
stampchain-io

Stampchain MCP Server

Official

get_stamp_dependencies

Map hierarchical dependencies for recursive stamps to visualize relationships and referenced stamps in various output formats.

Instructions

Create a hierarchical dependency graph for a recursive stamp, showing all referenced stamps and their relationships

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
stamp_idYesThe ID or CPID of the stamp to map dependencies for
max_depthNoMaximum depth to traverse dependencies
include_metadataNoInclude full stamp metadata for each dependency
formatNoOutput format for the dependency structuretree
resolve_allNoAttempt to resolve all dependencies (may be slow for deep graphs)

Implementation Reference

  • GetStampDependenciesTool class: the main handler implementing the tool's execute() method. Resolves stamp ID/CPID, parses dependencies recursively up to max_depth, builds tree/graph structure, handles circular references, and supports multiple output formats (tree, graph, mermaid, 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, '_');
      }
    }
  • Input validation schema GetStampDependenciesParamsSchema defining parameters like stamp_id, max_depth, format etc. Used by the tool's inputSchema and Zod 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)'),
    });
  • Registration in stampAnalysisTools export object, making the tool class available for instantiation.
    export const stampAnalysisTools = {
      analyze_stamp_code: AnalyzeStampCodeTool,
      get_stamp_dependencies: GetStampDependenciesTool,
      analyze_stamp_patterns: AnalyzeStampPatternsTool,
    };
  • Tool name 'get_stamp_dependencies' registered in getAvailableToolNames() function listing all available tools.
      'analyze_stamp_code',
      'get_stamp_dependencies',
      'analyze_stamp_patterns',
    ];
  • Tool included in toolMetadata.analysis.tools array for discovery and categorization.
    tools: ['analyze_stamp_code', 'get_stamp_dependencies', 'analyze_stamp_patterns'],

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/stampchain-io/stampchain-mcp'

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