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'],
Behavior2/5

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

With no annotations, the description carries the full burden of behavioral disclosure. It mentions creating a graph but lacks details on performance implications (e.g., speed for deep graphs), error handling, or output specifics, leaving gaps in understanding how the tool behaves.

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

Conciseness5/5

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

The description is a single, efficient sentence that front-loads the core purpose without unnecessary words, making it easy to grasp quickly and earning its place.

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 (dependency mapping with 5 parameters) and lack of annotations and output schema, the description is insufficient. It doesn't explain return values, error cases, or behavioral nuances, leaving critical gaps for effective use.

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%, so the schema fully documents all 5 parameters. The description adds no additional meaning beyond implying dependency traversal, which aligns with the schema but doesn't enhance parameter understanding beyond what's already provided.

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

Purpose5/5

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

The description clearly states the specific action ('create a hierarchical dependency graph') and resource ('recursive stamp'), distinguishing it from siblings like 'get_stamp' or 'analyze_stamp_code' by focusing on dependency mapping rather than retrieval or analysis.

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

Usage Guidelines2/5

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

No guidance is provided on when to use this tool versus alternatives. It does not mention prerequisites, exclusions, or compare with sibling tools like 'get_stamp' or 'analyze_stamp_patterns', leaving usage context unclear.

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

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