Skip to main content
Glama
nicktcode

Swissgroceries MCP

find_stock

Check which stores of a specified chain have a product in stock. Filter by location or query a single store. Useful for finding nearby availability.

Instructions

Check which stores of a given chain have a specific product in stock. Optionally filter by proximity to GPS coordinates or query a single store by ID. Not all chains support per-store stock queries; unsupported chains return a clear error. Use for "is this product available near Zurich HB?", "which Coop has item X in stock?".

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chainYesThe chain to query for stock. Only chains with perStoreStock capability are supported.
productIdYesChain-specific product ID to check stock for. Obtain via search_products or get_product.
nearNoOptional location to filter nearby stores. Pass coordinates, a Swiss ZIP, or a free-text address. If omitted, all stores may be queried.
storeIdNoQuery a single specific store by its chain-specific store ID. Takes precedence over `near`.

Implementation Reference

  • Main handler that resolves geolocation (if provided) and delegates to the chain adapter's findStoresWithStock method.
    export async function findStockHandler(
      registry: AdapterRegistry,
      input: FindStockInput,
    ): Promise<StockResult[]> {
      const adapter = registry.get(input.chain);
      if (!adapter) {
        throw new ToolError(
          'adapter_not_registered',
          `No adapter registered for chain "${input.chain}"`,
          'Ensure the chain is enabled in the server configuration. For Denner, set the DENNER_JWT env var.',
        );
      }
      if (!adapter.findStoresWithStock) {
        throw new ToolError(
          'capability_unsupported',
          `Adapter "${input.chain}" does not support per-store stock queries`,
          'Use search_products to find which chains carry this product, then try a chain that supports stock queries.',
        );
      }
    
      // Resolve {zip} / {address} / {lat,lng} to coordinates via the same
      // geocode() helper find_stores uses. Skip when no `near` was passed.
      let coords: { lat: number; lng: number } | undefined;
      if (input.near) {
        const geo = await geocode(input.near as any);
        if (!geo.ok) {
          const err = geo.error;
          if (err.code === 'unknown_zip') {
            throw new ToolError(
              'unknown_zip',
              `ZIP "${(err as any).zip}" is not in the lookup table`,
              'Pass { lat, lng } directly or check that the ZIP is a valid Swiss PLZ (e.g. "8001").',
            );
          }
          if (err.code === 'address_not_found') {
            throw new ToolError(
              'address_not_found',
              `Address "${(err as any).query}" could not be geocoded`,
              'Try a more specific address or pass a Swiss ZIP code or { lat, lng } coordinates.',
            );
          }
          if (err.code === 'unavailable') {
            throw new ToolError(
              'unavailable',
              (err as any).reason,
              'The Nominatim geocoding service is temporarily unavailable. Try passing a ZIP or { lat, lng } instead.',
            );
          }
          throw new ToolError(
            (err as any).code,
            'reason' in err ? (err as any).reason : (err as any).code,
            'Pass a Swiss ZIP code or { lat, lng } coordinates instead of a free-text address.',
          );
        }
        coords = { lat: geo.data.lat, lng: geo.data.lng };
      }
    
      const r = await adapter.findStoresWithStock(input.productId, coords);
      if (!r.ok) {
        throw new ToolError(
          r.error.code,
          `Stock query failed for ${input.chain}/${input.productId}: ${r.error.code}`,
          'Try again later or use search_products as a fallback.',
        );
      }
      return r.data;
    }
  • Zod schema defining the tool's input: chain (enum of 8 chains), productId, optional near (lat/lng, zip, or address), and optional storeId.
    export const findStockSchema = z.object({
      chain: z.enum(['migros', 'coop', 'aldi', 'denner', 'lidl', 'farmy', 'volgshop', 'ottos'])
        .describe('The chain to query for stock. Only chains with perStoreStock capability are supported.'),
      productId: z.string().min(1)
        .describe('Chain-specific product ID to check stock for. Obtain via search_products or get_product.'),
      near: z.union([
        z.object({
          lat: z.number().describe('Latitude in decimal degrees (WGS 84), e.g. 47.3769'),
          lng: z.number().describe('Longitude in decimal degrees (WGS 84), e.g. 8.5417'),
        }).describe('Coordinates of the search center'),
        z.object({
          zip: z.string().describe('Swiss postal code (PLZ / NPA), e.g. "8001"'),
        }).describe('Swiss postal code (PLZ), e.g. "8001"'),
        z.object({
          address: z.string().describe('Free-text address, e.g. "Bahnhofstrasse 1, Zürich" — geocoded via OpenStreetMap Nominatim'),
        }).describe('Free-text address — geocoded via Nominatim; prefer zip or lat/lng for speed'),
      ]).optional()
        .describe('Optional location to filter nearby stores. Pass coordinates, a Swiss ZIP, or a free-text address. If omitted, all stores may be queried.'),
      storeId: z.string()
        .optional()
        .describe('Query a single specific store by its chain-specific store ID. Takes precedence over `near`.'),
    }).describe('Check which stores of a given chain have a specific product in stock. Useful for "is this item available near me?" queries. Not all chains support per-store stock; check capabilities first.');
  • src/index.ts:94-104 (registration)
    Registration of find_stock in the TOOLS array, mapping name to schema and handler.
    {
      name: 'find_stock',
      description: [
        'Check which stores of a given chain have a specific product in stock.',
        'Optionally filter by proximity to GPS coordinates or query a single store by ID.',
        'Not all chains support per-store stock queries; unsupported chains return a clear error.',
        'Use for "is this product available near Zurich HB?", "which Coop has item X in stock?".',
      ].join(' '),
      schema: findStockSchema,
      handler: findStockHandler,
    },
  • Optional interface method on StoreAdapter that the handler delegates to for per-store stock queries.
    findStoresWithStock?(productId: string, near?: GeoPoint): Promise<AdapterResult<StockResult[]>>;
  • Return type of findStockHandler, containing store info, stock status, and optional quantity.
    export interface StockResult {
      store: NormalizedStore;
      inStock: boolean;
      quantity?: number;
    }
Behavior4/5

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

With no annotations, the description discloses key behaviors: optional proximity filtering, single store query, and the limitation that not all chains support per-store stock queries, with a clear error on unsupported chains. It is transparent about constraints.

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 three concise sentences. First sentence states purpose, second addresses limitations, third gives examples. No redundant information; well-structured and efficient.

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?

The description covers purpose, parameters, limitations, and usage. However, without an output schema, it does not describe the response format (e.g., array of stores with stock status). Moderate completeness given the tool's complexity.

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 coverage is 100% with detailed parameter descriptions. The description adds minimal extra context (e.g., obtain productId via search_products). Baseline 3 is appropriate; the description doesn't significantly enhance semantics beyond the schema.

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 tool checks which stores of a chain have a specific product in stock, with optional proximity filtering. It distinguishes from siblings like find_stores (which likely lacks stock info) and search_products. Examples provided.

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

Usage Guidelines4/5

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

The description gives concrete usage examples ('is this product available near Zurich HB?') and notes that unsupported chains return a clear error. It implicitly guides when to use this tool, but lacks explicit when-not-to-use or alternative tool references.

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/nicktcode/swissgroceries-mcp'

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