list_services
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
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Maximum number of services to return (default 50, max 100). |
Implementation Reference
- mcp-server/src/index.ts:460-475 (handler)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), })) ); } - mcp-server/src/index.ts:186-217 (schema)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).", }, }, }, }, - mcp-server/src/index.ts:159-379 (registration)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 }, }, ], })); - mcp-server/src/index.ts:67-79 (helper)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; } - mcp-server/src/index.ts:107-122 (helper)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; }