Skip to main content
Glama

list_services

Read-onlyIdempotent

Get a list of approved API services and their per-call USDC pricing. Use this before call_service to discover serviceId values.

Instructions

List approved API services available on the LemonCake marketplace. No authentication required.

Use this BEFORE call_service to discover serviceId values and per-call USDC pricing.

Each item: { id, name, provider, type ('API' | 'MCP'), pricePerCall, endpoint }. Errors: HTTP-level errors are returned as Error: API <status>: <body>.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
limitNoMaximum number of services to return (default 50, max 100).

Implementation Reference

  • Handler for the list_services tool: calls API endpoint /api/services?reviewStatus=APPROVED&limit=..., filters by verified, and maps results to {id, name, provider, type, pricePerCall, usage}.
    // ─── list_services ───────────────────────────────────────────────────
    case "list_services": {
      const limit = (args.limit as number | undefined) ?? 50;
      const services = await apiGet(`/api/services?reviewStatus=APPROVED&limit=${limit}`) as any[];
      return json(services
        .filter((s: any) => s.verified)
        .map((s: any) => ({
          id:           s.id,
          name:         s.name,
          provider:     s.providerName,
          type:         s.type,
          pricePerCall: `${s.pricePerCallUsdc} USDC`,
          usage:        usageHintFor(s.name),
        }))
      );
    }
  • Schema (inputSchema) for list_services: accepts an optional limit parameter (integer, 1-100, default 50).
    // ─── list_services(認証不要) ───────────────────────────────────────
    {
      name: "list_services",
      description: [
        "List approved API services available on the LemonCake marketplace. No authentication required.",
        "",
        "Use this BEFORE call_service to discover serviceId values and per-call USDC pricing.",
        "",
        "Each item: { id, name, provider, type ('API' | 'MCP'), pricePerCall, endpoint }.",
        "Errors: HTTP-level errors are returned as `Error: API <status>: <body>`.",
      ].join("\n"),
      annotations: {
        title:           "List marketplace services",
        readOnlyHint:    true,
        destructiveHint: false,
        idempotentHint:  true,
        openWorldHint:   true,
      },
      inputSchema: {
        type: "object",
        additionalProperties: false,
        properties: {
          limit: {
            type:        "integer",
            minimum:     1,
            maximum:     100,
            default:     50,
            description: "Maximum number of services to return (default 50, max 100).",
          },
        },
      },
    },
  • Tool registration in ListToolsRequestSchema handler: defines list_services as one of the available tools alongside setup, call_service, check_balance, check_tax, get_service_stats.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
    
        // ─── setup(認証不要) ────────────────────────────────────────────────
        {
          name: "setup",
          description: [
            "Show the LemonCake MCP first-run setup guide. No authentication required.",
            "Call this tool FIRST to learn what credentials are missing and how to obtain them.",
            "",
            "Returns the current credential status (Pay Token / Buyer JWT) and step-by-step",
            "instructions to obtain anything that is missing, including a sample MCP client",
            "config snippet ready to paste.",
            "",
            "Returns: { version, apiUrl, credentials, availableTools, setupSteps, register, dashboard, docs }",
            "Errors: none — this tool always succeeds.",
          ].join("\n"),
          annotations: {
            title:           "Setup guide",
            readOnlyHint:    true,
            destructiveHint: false,
            idempotentHint:  true,
            openWorldHint:   false,
          },
          inputSchema: { type: "object", properties: {}, additionalProperties: false },
        },
    
        // ─── list_services(認証不要) ───────────────────────────────────────
        {
          name: "list_services",
          description: [
            "List approved API services available on the LemonCake marketplace. No authentication required.",
            "",
            "Use this BEFORE call_service to discover serviceId values and per-call USDC pricing.",
            "",
            "Each item: { id, name, provider, type ('API' | 'MCP'), pricePerCall, endpoint }.",
            "Errors: HTTP-level errors are returned as `Error: API <status>: <body>`.",
          ].join("\n"),
          annotations: {
            title:           "List marketplace services",
            readOnlyHint:    true,
            destructiveHint: false,
            idempotentHint:  true,
            openWorldHint:   true,
          },
          inputSchema: {
            type: "object",
            additionalProperties: false,
            properties: {
              limit: {
                type:        "integer",
                minimum:     1,
                maximum:     100,
                default:     50,
                description: "Maximum number of services to return (default 50, max 100).",
              },
            },
          },
        },
    
        // ─── call_service(PAY_TOKEN 必須) ──────────────────────────────────
        {
          name: "call_service",
          description: [
            "Invoke an upstream API service through LemonCake's pay-per-call proxy.",
            "Each successful call automatically charges USDC against your configured Pay Token.",
            "",
            "PRECONDITIONS:",
            "  • LEMON_CAKE_PAY_TOKEN env var must be set. If missing, the tool returns a",
            "    structured CREDENTIAL_MISSING error (not a thrown exception) with how-to-fix steps.",
            "  • serviceId must come from list_services.",
            "",
            "BEHAVIOR:",
            "  • Returns the upstream response body verbatim (JSON or text), plus the X-Charge-Id",
            "    and X-Amount-Usdc headers reported by the proxy.",
            "  • HTTP 402 Payment Required is returned as a normal result (NOT thrown) so the",
            "    agent can autonomously stop spending when the Pay Token's limitUsdc is exhausted.",
            "  • Pass the same idempotencyKey to retry safely without double-charging.",
            "  • This tool spends real money and contacts an external service — it is",
            "    non-idempotent by default and has external side effects.",
            "",
            "Returns: { status, chargeId, amountUsdc, response }",
          ].join("\n"),
          annotations: {
            title:           "Call a paid service (charges USDC)",
            readOnlyHint:    false,
            destructiveHint: false,
            idempotentHint:  false,
            openWorldHint:   true,
          },
          inputSchema: {
            type: "object",
            required: ["serviceId"],
            additionalProperties: false,
            properties: {
              serviceId: {
                type:        "string",
                description: "ID of the service to call (obtain from list_services).",
                minLength:   1,
              },
              path: {
                type:        "string",
                description: "Sub-path on the service (e.g. \"/search\", \"/v1/completions\"). Defaults to \"/\".",
                default:     "/",
                pattern:     "^/.*",
              },
              method: {
                type:        "string",
                enum:        ["GET", "POST", "PUT", "PATCH", "DELETE"],
                description: "HTTP method to use against the service. Defaults to GET.",
                default:     "GET",
              },
              body: {
                type:                 "object",
                description:          "JSON request body (only used for POST/PUT/PATCH).",
                additionalProperties: true,
              },
              idempotencyKey: {
                type:        "string",
                description: "Optional idempotency key (UUID recommended). Identical keys within the proxy's retention window return the cached result without re-charging.",
                format:      "uuid",
              },
            },
          },
        },
    
        // ─── check_balance(BUYER_JWT 必須) ─────────────────────────────────
        {
          name: "check_balance",
          description: [
            "Check the current USDC balance, KYC tier, and account info of the configured buyer.",
            "",
            "PRECONDITIONS:",
            "  • LEMON_CAKE_BUYER_JWT env var must be set. If missing, returns a structured",
            "    CREDENTIAL_MISSING error with how-to-fix steps (no exception thrown).",
            "",
            "Use BEFORE call_service to confirm sufficient funds, especially before a long batch.",
            "",
            "Returns: { balanceUsdc, kycTier, email, name }",
          ].join("\n"),
          annotations: {
            title:           "Check USDC balance",
            readOnlyHint:    true,
            destructiveHint: false,
            idempotentHint:  true,
            openWorldHint:   true,
          },
          inputSchema: { type: "object", properties: {}, additionalProperties: false },
        },
    
        // ─── check_tax(認証不要) ───────────────────────────────────────────
        {
          name: "check_tax",
          description: [
            "Run a Japanese tax compliance check on a single transaction. No authentication required.",
            "",
            "Performs three checks in one call:",
            "  1. Validates the qualified-invoice registration number (T-number) against the NTA registry.",
            "  2. Determines whether source-withholding (源泉徴収) applies based on the service description.",
            "  3. If withholding applies, computes the withholding amount and net payable.",
            "",
            "Intended for Japanese corporations that pay AI / API services and need to file",
            "withholding correctly under the qualified-invoice (インボイス制度) regime.",
            "",
            "Returns: { invoice: { valid, name, ... }, withholding: { required, rate, amount, net } }",
            "Errors: invalid registrationNumber returns invoice.valid = false (not an exception).",
          ].join("\n"),
          annotations: {
            title:           "Japanese tax compliance check",
            readOnlyHint:    true,
            destructiveHint: false,
            idempotentHint:  true,
            openWorldHint:   true,
          },
          inputSchema: {
            type: "object",
            required: ["registrationNumber", "serviceDescription", "grossAmountJpy"],
            additionalProperties: false,
            properties: {
              registrationNumber: {
                type:        "string",
                description: "Qualified-invoice registration number issued by the Japanese NTA (e.g. \"T1234567890123\").",
                pattern:     "^T?\\d{13}$",
              },
              serviceDescription: {
                type:        "string",
                description: "Plain-text description of what was purchased. Used to classify whether source-withholding applies.",
                minLength:   1,
              },
              grossAmountJpy: {
                type:        "number",
                description: "Gross transaction amount in JPY, tax inclusive.",
                exclusiveMinimum: 0,
              },
            },
          },
        },
    
        // ─── get_service_stats(認証不要) ───────────────────────────────────
        {
          name: "get_service_stats",
          description: [
            "Return public usage statistics for every approved service on the marketplace. No authentication required.",
            "",
            "Use this AFTER list_services and BEFORE call_service to pick a service based on",
            "real-world traction (call counts, USDC revenue, last-used timestamp).",
            "",
            "Returns: an array of { serviceId, callCount, totalRevenueUsdc, lastCalledAt }.",
          ].join("\n"),
          annotations: {
            title:           "Marketplace usage stats",
            readOnlyHint:    true,
            destructiveHint: false,
            idempotentHint:  true,
            openWorldHint:   true,
          },
          inputSchema: { type: "object", properties: {}, additionalProperties: false },
        },
    
      ],
    }));
  • Helper function apiGet used by list_services to call the LemonCake backend API.
    async function apiGet(path: string, auth?: string) {
      const res = await fetch(`${API_URL}${path}`, {
        headers: {
          "Content-Type":       "application/json",
          "User-Agent":         USER_AGENT,
          "X-LemonCake-Client": USER_AGENT,
          ...(auth ? { Authorization: `Bearer ${auth}` } : {}),
        },
      });
      const body = await res.json();
      if (!res.ok) throw new Error(`API ${res.status}: ${JSON.stringify(body)}`);
      return body;
    }
  • Helper function usageHintFor used by list_services to attach usage hints to each service based on name matching.
    function usageHintFor(name: string): { path: string; method: string; body?: unknown; description: string } | undefined {
      const n = name.toLowerCase();
      if (n.includes("serper"))           return { path: "/search",       method: "POST", body: { q: "<query>", num: 5 },               description: "Google検索 (organic, news, images, knowledge graph)" };
      if (n.includes("hunter"))           return { path: "/domain-search?domain=<domain>&limit=5", method: "GET",                       description: "ドメインから企業の連絡先メール一覧 (役職・信頼度付き)" };
      if (n.includes("jina"))             return { path: "/?url=<url>",   method: "GET",                                                 description: "Webページを LLM-ready Markdown に変換" };
      if (n.includes("firecrawl"))        return { path: "/scrape",       method: "POST", body: { url: "<url>" },                       description: "JS実行ありのWebスクレイピング → Markdown" };
      if (n.includes("ipinfo"))           return { path: "/<ip>",         method: "GET",                                                 description: "IP geolocation・ISP・ASN・risk score" };
      if (n.includes("exchange") || n.includes("為替")) return { path: "/latest.json", method: "GET",                                    description: "USD基準の170+通貨レート (1日1回更新)" };
      if (n.includes("slack"))            return { path: "/chat.postMessage", method: "POST", body: { channel: "<id>", text: "<msg>" }, description: "Slackメッセージ送信、人間の判断仰ぎに最適" };
      if (n.includes("gbiz") || n.includes("法人")) return { path: "/hojin/<corporate_number>", method: "GET",                            description: "経産省 gBizINFO 法人情報 (法人番号13桁)" };
      if (n.includes("インボイス") || n.includes("invoice")) return { path: "/check?id=T<13桁>", method: "GET",                            description: "国税庁 適格請求書発行事業者番号の照合" };
      if (n.includes("e-gov") || n.includes("法令")) return { path: "/keyword?keyword=<語>", method: "GET",                                description: "日本の法律・政令・省令を全文検索" };
      if (n.includes("vat") || n.includes("abstract")) return { path: "/validate?vat_number=<vat>", method: "GET",                        description: "EU VAT番号有効性検証" };
      if (n.includes("coze") || n.includes("test")) return { path: "/anything", method: "POST", body: { test: "any" },                   description: "Echoサーバ (リクエストをそのまま200で返す、デバッグ用)" };
      return undefined;
    }
Behavior5/5

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

Annotations already declare readOnlyHint, destructiveHint, and idempotentHint. The description adds that no authentication is required and specifies the error format for HTTP-level errors, providing valuable behavioral context beyond what annotations offer.

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 very concise: five sentences each adding unique value. It immediately states purpose, then usage, then output structure, then error handling. No fluff or redundancy.

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

Completeness4/5

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

Given the tool's simplicity (one optional parameter, no nested objects, no output schema), the description covers the return structure and error handling adequately. It might lack pagination details beyond the limit parameter, but that is covered in the schema.

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% for the single parameter 'limit', which is fully described with min, max, default, and description. The description does not add further meaning beyond what the schema already provides, making this a baseline score.

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 lists approved API services on the LemonCake marketplace, using a specific verb and resource. It distinguishes from siblings like call_service by mentioning its role in discovering serviceId values.

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?

Explicitly states 'Use this BEFORE call_service to discover serviceId values and per-call USDC pricing.' This provides clear context for when to use it, though it does not mention scenarios when not to use it.

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/evidai/lemon-cake'

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