Skip to main content
Glama
stampchain-io

Stampchain MCP Server

Official

search_tokens

Find SRC-20 tokens by ticker, deployer, holders, or mint percentage to identify Bitcoin Stamps assets matching specific criteria.

Instructions

Search for SRC-20 tokens with various filtering criteria

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryNoSearch query for token ticker
deployerNoFilter by deployer address
min_holdersNoMinimum number of holders
min_percent_mintedNoMinimum percent minted
sort_byNoSort field (Note: only deploy_timestamp is supported by the API)deploy_timestamp
sort_orderNoSort orderDESC
pageNoPage number
page_sizeNoItems per page

Implementation Reference

  • Core handler function that executes the search_tokens tool: validates input, builds API query params, calls stampchain API searchTokens, processes and formats results as text table and metadata JSON.
    public async execute(params: SearchTokensParams, context?: ToolContext): Promise<ToolResponse> {
      try {
        context?.logger?.info('Executing search_tokens tool', { params });
    
        // Validate parameters
        const validatedParams = this.validateParams(params);
    
        // Build query parameters
        const queryParams = {
          query: validatedParams.query,
          deployer: validatedParams.deployer,
          sort_by: validatedParams.sort_by,
          sort_order: validatedParams.sort_order,
          page: validatedParams.page,
          page_size: validatedParams.page_size,
        };
    
        // Remove undefined values
        Object.keys(queryParams).forEach((key) => {
          if (queryParams[key as keyof typeof queryParams] === undefined) {
            delete queryParams[key as keyof typeof queryParams];
          }
        });
    
        // Search tokens
        const searchResponse = await this.apiClient.searchTokens(queryParams);
    
        if (!searchResponse || searchResponse.length === 0) {
          return textResponse('No tokens found matching the search criteria');
        }
    
        // Note: searchTokens returns TokenResponse[] directly
        const tokens = searchResponse;
    
        // Note: Client-side filtering for unsupported API fields
        let filteredTokens = tokens;
        if (
          validatedParams.min_holders !== undefined ||
          validatedParams.min_percent_minted !== undefined
        ) {
          // These fields are not available in the current API response
          // Filtering will be skipped and a note will be added to the output
          filteredTokens = tokens;
        }
    
        // Create summary
        const lines = [`Found ${filteredTokens.length} tokens`];
        lines.push('---');
    
        // Create table view
        const tokenTable = createTable(filteredTokens, [
          { key: 'tick', label: 'Ticker' },
          { key: 'max', label: 'Max Supply' },
          { key: 'lim', label: 'Mint Limit' },
          { key: 'deci', label: 'Decimals' },
          {
            key: 'creator',
            label: 'Creator',
            format: (v: unknown) => (typeof v === 'string' ? v.substring(0, 8) + '...' : String(v)),
          },
        ]);
    
        lines.push(tokenTable);
    
        // Add detailed view for top tokens
        lines.push('\n\nDetailed View (Top 5):');
        lines.push('---');
    
        filteredTokens.slice(0, 5).forEach((token, index) => {
          lines.push(`\n${index + 1}. ${token.tick}`);
          lines.push(`   Protocol: ${token.p} | Operation: ${token.op}`);
          lines.push(`   Max Supply: ${token.max}`);
          if (token.lim) {
            lines.push(`   Mint Limit: ${token.lim}`);
          }
          if (token.deci !== undefined) {
            lines.push(`   Decimals: ${token.deci}`);
          }
          lines.push(`   Creator: ${token.creator}`);
          lines.push(`   Block Index: ${token.block_index}`);
          lines.push(`   Block Time: ${new Date(token.block_time).toLocaleString()}`);
          if (token.creator_name) {
            lines.push(`   Creator Name: ${token.creator_name}`);
          }
          if (token.destination_name) {
            lines.push(`   Destination: ${token.destination_name}`);
          }
        });
    
        // Include metadata
        const metadata = {
          results_count: filteredTokens.length,
          query_params: queryParams,
          note: 'Pagination and holder/minting statistics are not available in the current API response',
          additional_filters: {
            min_holders: validatedParams.min_holders,
            min_percent_minted: validatedParams.min_percent_minted,
          },
        };
    
        // Add note about unsupported filters
        if (
          validatedParams.min_holders !== undefined ||
          validatedParams.min_percent_minted !== undefined
        ) {
          lines.push(
            '\n⚠️  Note: Holder and minting percentage filters are not supported by the current API and have been ignored.'
          );
        }
    
        return multiResponse(
          { type: 'text', text: lines.join('\n') },
          { type: 'text', text: `\n\nSearch Metadata:\n${JSON.stringify(metadata, null, 2)}` }
        );
      } catch (error) {
        context?.logger?.error('Error executing search_tokens tool', { error });
    
        if (error instanceof ValidationError) {
          throw error;
        }
    
        throw new ToolExecutionError('Failed to search tokens', this.name, error);
      }
    }
  • Zod schema defining the input parameters and validation for the search_tokens tool.
    export const SearchTokensParamsSchema = z.object({
      query: z.string().optional().describe('Search query for token ticker'),
      deployer: z.string().optional().describe('Filter by deployer address'),
      min_holders: z.number().int().nonnegative().optional().describe('Minimum number of holders'),
      min_percent_minted: z.number().min(0).max(100).optional().describe('Minimum percent minted'),
      sort_by: z
        .enum(['deploy_timestamp', 'holders', 'percent_minted'])
        .optional()
        .default('deploy_timestamp')
        .describe('Sort field'),
      sort_order: z.enum(['ASC', 'DESC']).optional().default('DESC').describe('Sort order'),
      page: z.number().int().positive().optional().default(1).describe('Page number'),
      page_size: z.number().int().positive().max(100).optional().default(20).describe('Items per page'),
    });
  • Registration of SearchTokensTool class and factory function to instantiate search_tokens tool with API client.
    export const tokenTools = {
      get_token_info: GetTokenInfoTool,
      search_tokens: SearchTokensTool,
    };
    
    /**
     * Factory function to create all token tool instances
     */
    export function createTokenTools(apiClient?: StampchainClient) {
      return {
        get_token_info: new GetTokenInfoTool(apiClient),
        search_tokens: new SearchTokensTool(apiClient),
      };
    }
  • Central factory function that creates all tools including search_tokens via createTokenTools and aggregates them.
    export function createAllTools(apiClient?: StampchainClient): Record<string, ITool> {
      const client = apiClient || new StampchainClient();
    
      const stamps = createStampTools(client);
      const collections = createCollectionTools(client);
      const tokens = createTokenTools(client);
      const analysis = createStampAnalysisTools(client);
    
      return {
        ...stamps,
        ...collections,
        ...tokens,
        ...analysis,
      };
    }
  • Underlying API client method called by the tool handler to perform the actual token search via Stampchain API.
     * Search SRC-20 tokens with query parameters
     */
    async searchTokens(params: TokenQueryParams = {}): Promise<TokenResponse[]> {
      const response = await this.client.get<TokenListResponse>('/src20', { params });
      return response.data.data;
    }
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure but offers minimal information. It doesn't mention whether this is a read-only operation, potential rate limits, authentication needs, or what the response format looks like (e.g., paginated results). The phrase 'Search for' implies a query operation, but lacks details on performance, errors, or side effects.

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. Every part of the sentence ('Search for SRC-20 tokens with various filtering criteria') contributes directly to understanding the tool's function, making it appropriately sized and well-structured.

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 (8 parameters, no output schema, no annotations), the description is inadequate. It doesn't explain what constitutes an SRC-20 token, typical search scenarios, or the structure of returned results. For a search tool with multiple filtering options and pagination, more context about expected outputs and behavioral constraints is needed for effective agent 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%, providing detailed documentation for all 8 parameters. The description adds no additional parameter semantics beyond mentioning 'various filtering criteria', which is already covered by the schema. This meets the baseline of 3 since the schema does the heavy lifting, but the description doesn't enhance understanding of parameter interactions or typical usage patterns.

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

Purpose4/5

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

The description clearly states the action ('Search for') and resource ('SRC-20 tokens'), making the purpose immediately understandable. However, it doesn't distinguish this tool from sibling tools like 'search_collections' or 'search_stamps' beyond specifying the resource type, missing an opportunity for explicit differentiation.

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?

The description provides no guidance on when to use this tool versus alternatives like 'search_collections', 'search_stamps', or 'get_token_info'. It mentions 'various filtering criteria' but doesn't specify typical use cases, prerequisites, or exclusions, leaving the agent with minimal contextual direction.

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