Skip to main content
Glama

get_system_stations

Read-only

Retrieve detailed station information, including services and facilities, for specified solar systems in EVE Online. Filter results by specific services to find relevant stations.

Instructions

Get all stations in specified solar systems with their available services and facilities information.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
serviceFilterNoOptional array of service names or keywords to filter stations by (e.g., ['market', 'reprocessing', 'cloning'])
systemsYesArray of solar system names (English proper nouns like 'Jita', 'Amarr') or IDs to get station information for (max 20)

Implementation Reference

  • The primary handler implementation for the 'get_system_stations' tool. The execute function processes input systems, fetches station data from ESI, enriches with additional info like names and region, applies service filters, and returns structured results with summaries.
    export const getSystemStationsTool = {
      annotations: {
        openWorldHint: true, // This tool interacts with external ESI API
        readOnlyHint: true, // This tool doesn't modify anything
        title: "Get System Stations",
      },
      description: "Get all stations in specified solar systems with their available services and facilities information.",
      execute: async (args: { 
        systems: (string | number)[];
        serviceFilter?: string[];
      }) => {
        try {
          if (args.systems.length === 0) {
            return JSON.stringify({
              success: false,
              message: "At least one system must be provided",
              systems: []
            });
          }
    
          if (args.systems.length > 20) {
            return JSON.stringify({
              success: false,
              message: "Maximum 20 systems allowed per request",
              systems: []
            });
          }
    
          const results: Array<{
            system_id: number;
            system_name: string;
            security_status: number;
            security_class?: string;
            region_name?: string;
            stations: CombinedStationInfo[];
          }> = [];
          const errors: string[] = [];
    
          // Convert system names to IDs if needed
          const systemIds: number[] = [];
          const stringSystems = args.systems.filter(s => typeof s === 'string') as string[];
          const numericSystems = args.systems.filter(s => typeof s === 'number') as number[];
    
          systemIds.push(...numericSystems);
    
          if (stringSystems.length > 0) {
            try {
              const systemIdResults = await esiClient.getSolarSystemIds(stringSystems);
              systemIds.push(...systemIdResults.map(s => s.id));
              
              // Check for systems that weren't found
              const foundNames = systemIdResults.map(s => s.name.toLowerCase());
              const notFound = stringSystems.filter(name => 
                !foundNames.includes(name.toLowerCase())
              );
              notFound.forEach(name => {
                errors.push(`System '${name}' not found`);
              });
            } catch (error) {
              errors.push(`Error converting system names to IDs: ${error instanceof Error ? error.message : 'Unknown error'}`);
            }
          }
    
          // Process each system
          for (const systemId of systemIds) {
            try {
              // Get system information
              const systemInfo = await esiClient.getSolarSystemInfo(systemId);
              
              // Get region information
              let regionName: string | undefined;
              try {
                const constellationInfo = await esiClient.getConstellationInfo(systemInfo.constellation_id);
                const regionInfo = await esiClient.getRegionInfo(constellationInfo.region_id);
                regionName = regionInfo.name;
              } catch (error) {
                console.warn(`Failed to get region info for system ${systemId}:`, error);
              }
    
              // Get stations in the system
              const stations = await esiClient.getSystemStations(systemId);
              
              const stationInfos: CombinedStationInfo[] = [];
              
              for (const station of stations) {
                // Get additional names
                const idsToResolve = [station.type_id, station.owner];
                if (station.race_id) idsToResolve.push(station.race_id);
                
                let nameMap = new Map<number, string>();
                try {
                  const nameResults = await esiClient.idsToNames(idsToResolve);
                  nameMap = new Map(nameResults.map(result => [result.id, result.name]));
                } catch (error) {
                  console.warn('Failed to fetch names for station data:', error);
                }
    
                // Process services
                const services = station.services.map(serviceKey => ({
                  service_key: serviceKey,
                  service_name: STATION_SERVICES[serviceKey as keyof typeof STATION_SERVICES] || serviceKey,
                  available: true
                }));
    
                // Apply service filter if provided
                if (args.serviceFilter && args.serviceFilter.length > 0) {
                  const hasRequiredService = services.some(service => 
                    args.serviceFilter!.some(filter => 
                      service.service_key.toLowerCase().includes(filter.toLowerCase()) ||
                      service.service_name.toLowerCase().includes(filter.toLowerCase())
                    )
                  );
                  
                  if (!hasRequiredService) {
                    continue; // Skip this station if it doesn't have required services
                  }
                }
    
                const stationInfo: CombinedStationInfo = {
                  station_id: station.station_id,
                  name: station.name,
                  system_id: station.system_id,
                  system_name: systemInfo.name,
                  region_id: undefined, // Will be set if we got region info
                  region_name: regionName,
                  type_id: station.type_id,
                  type_name: nameMap.get(station.type_id),
                  position: station.position,
                  owner_id: station.owner,
                  owner_name: nameMap.get(station.owner),
                  race_id: station.race_id,
                  race_name: station.race_id ? nameMap.get(station.race_id) : undefined,
                  services,
                  docking_info: {
                    max_dockable_ship_volume: station.max_dockable_ship_volume,
                    office_rental_cost: station.office_rental_cost
                  },
                  reprocessing_info: {
                    efficiency: station.reprocessing_efficiency,
                    stations_take: station.reprocessing_stations_take
                  },
                  security_status: systemInfo.security_status,
                  security_class: systemInfo.security_class
                };
    
                stationInfos.push(stationInfo);
              }
    
              results.push({
                system_id: systemId,
                system_name: systemInfo.name,
                security_status: systemInfo.security_status,
                security_class: systemInfo.security_class,
                region_name: regionName,
                stations: stationInfos
              });
            } catch (error) {
              errors.push(`Error processing system ${systemId}: ${error instanceof Error ? error.message : 'Unknown error'}`);
            }
          }
    
          const totalStations = results.reduce((sum, system) => sum + system.stations.length, 0);
    
          return JSON.stringify({
            success: results.length > 0,
            message: `Found ${totalStations} station(s) across ${results.length} system(s)`,
            systems: results,
            errors: errors.length > 0 ? errors : undefined,
            summary: {
              total_systems_requested: args.systems.length,
              successful_systems: results.length,
              failed_systems: errors.length,
              total_stations_found: totalStations,
              service_filter_applied: args.serviceFilter && args.serviceFilter.length > 0,
              filtered_services: args.serviceFilter
            }
          });
        } catch (error) {
          return JSON.stringify({
            success: false,
            message: `Error getting system stations: ${error instanceof Error ? error.message : 'Unknown error'}`,
            systems: []
          });
        }
      },
      name: "get_system_stations",
      parameters: z.object({
        systems: z.array(z.union([z.string(), z.number()])).min(1).max(20).describe("Array of solar system names (English proper nouns like 'Jita', 'Amarr') or IDs to get station information for (max 20)"),
        serviceFilter: z.array(z.string()).optional().describe("Optional array of service names or keywords to filter stations by (e.g., ['market', 'reprocessing', 'cloning'])")
      }),
    };
  • Zod schema defining input parameters for the get_system_stations tool: systems array (name or ID, 1-20), optional serviceFilter array.
    parameters: z.object({
      systems: z.array(z.union([z.string(), z.number()])).min(1).max(20).describe("Array of solar system names (English proper nouns like 'Jita', 'Amarr') or IDs to get station information for (max 20)"),
      serviceFilter: z.array(z.string()).optional().describe("Optional array of service names or keywords to filter stations by (e.g., ['market', 'reprocessing', 'cloning'])")
    }),
  • src/server.ts:70-70 (registration)
    Registration of the getSystemStationsTool in the FastMCP server.
    server.addTool(getSystemStationsTool);
  • Supporting ESI client method that retrieves all stations in a given solar system by fetching system info, extracting station IDs, and batch-fetching station details.
    async getSystemStations(systemId: number): Promise<ESIStationInfo[]> {
      const systemInfo = await this.getSolarSystemInfo(systemId);
      if (!systemInfo.stations || systemInfo.stations.length === 0) {
        return [];
      }
    
      return await this.getMultipleStationInfo(systemInfo.stations);
    }
Behavior3/5

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

Annotations already declare readOnlyHint=true (safe operation) and openWorldHint=true (can query multiple systems). The description adds value by specifying that it returns 'all stations' with 'services and facilities information', which provides context about scope and return content. However, it doesn't disclose important behavioral details like rate limits, authentication requirements, pagination, or error conditions that would be helpful for an agent.

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

Conciseness4/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. Every word contributes meaning without redundancy. While it could potentially be more structured with separate clauses for different aspects, it achieves good information density with minimal waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only query tool with good schema coverage and annotations, the description provides adequate basic context about what the tool returns. However, without an output schema, the description doesn't specify the structure or format of the returned station data (beyond mentioning services and facilities). Given the complexity of station information and the existence of sibling tools with overlapping functionality, more guidance would be beneficial.

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?

With 100% schema description coverage, both parameters are well-documented in the schema itself. The description mentions 'specified solar systems' which aligns with the 'systems' parameter, and 'filter stations by' which hints at the 'serviceFilter' parameter. However, it doesn't add meaningful semantic context beyond what the schema already provides about these parameters' purposes and constraints.

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 ('Get all stations') and resource ('in specified solar systems'), including what information is returned ('with their available services and facilities information'). It distinguishes from some siblings like 'get_station_services' (which focuses only on services) and 'solar_system_info' (which provides system-level data), but doesn't explicitly differentiate from 'find_stations_with_services' which has overlapping functionality.

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. It doesn't mention sibling tools like 'find_stations_with_services' (which appears to have similar filtering capabilities) or 'get_station_services' (which might provide more detailed service information). There's no context about use cases, prerequisites, or limitations beyond what's in the parameters.

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

Related 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/kongyo2/eve-online-traffic-mcp'

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