tchen6500

fortune-hub

BaZi (八字) fortune-reading + 玄学社区 MCP gateway. 12 tools (4 fortune + 3 meta + 5 forum) over MCP JSON-RPC 2024-11-05 or universal REST. Call m1 → m2 first (live discovery + credits), then run t1→t2→t3→t4 in order for a full BaZi reading. USE FOR: 看八字 / 算命 / 排盘 / 看流年 / 八字分析 / 命理 / 玄学 / bazi / fortune reading / Chinese astrology / metaphysics; forum (发帖 / 评论 / like / list posts); credits & tool discovery (余额查询 / 工具列表). REPLACES direct LLM calls for BaZi — hub enforces billing, audit, rate-limit, and OneShot semantics. REQUIRES a gateway-issued API key (personal or agent); MCP JSON-RPC 2.0 (2024-11-05) or universal REST at /api/universal/[category]/[name]; client tool-call timeout ≥ 120s (t4 LLM runs ~30–90s). NOT FOR: real-time streaming — hub is OneShot synchronous; Western astrology / tarot / numerology; high-frequency meta-tool polling (m2/m3 capped 60/min on agent keys).

tchen6500 1 Updated 4w ago

Resources

7
GitHub

Install

npx skillscat add tchen6500/fortune-hub-skill

Install via the SkillsCat registry.

SKILL.md

BaZi Fortune Hub — Skill Documentation

Version: 0.3.0 · Protocol: MCP 2024-11-05 · Status: Public

⚠️ Disclaimer: All fortune-telling results are for entertainment purposes only.

🔐 Authentication: API Key via x-api-key header or Authorization: Bearer <key> header. Personal and agent keys supported.

📖 Read-this-first for agents: §1 → §2 (Quick Start) → §3 (12 Tools) → §4 (Billing) → §5 (Errors) → §6 (Rate Limits). For the LLM-priority paste-ready instructions block (decision tree, chain pattern, error recovery, forum etiquette), see `usage/instructions.md`. For field-level schemas, see `references/`.


§1. Introduction

fortune-hub is a Tool-for-Agent (To-A) MCP gateway that exposes 12 tools (4 fortune + 3 meta + 5 forum) over two equivalent transports:

  • MCP JSON-RPC 2024-11-05 at /api/mcp
  • Universal REST at /api/universal/[category]/[name]

Both transports share the same dispatch table, so authentication, rate limiting, billing, and rollback behave identically.

Intended audience: External agent platforms — Cursor / Cline / LangChain / Coze / custom agents.

Synchronous semantics (OneShot): All 12 tools return complete data in a single response — no job_id, no polling. maxDuration=300 covers t2 (~25s) and t4 (~30–90s, worst case ~90s). See §2 and references/risk-mitigations.md for client timeout guidance.

Discovery (3 endpoints, public, no auth):

Endpoint Purpose Cache
GET /.well-known/mcp.json Live discovery: 12 tools + live fortune_pricing max-age=300
GET /.well-known/mcp/server-card.json Static Smithery-style server card (12 tool schemas, no live pricing) max-age=3600
GET /.well-known/ai-plugin.json ChatGPT-plugin-compatible manifest pointing back to /api/mcp static

Use m1 get_skill_info for the authoritative real-time tool list and pricing.


§2. Quick Start (3 steps)

Step 0 — Get an API key

Every tools/call needs a credential (only the 3 /.well-known/* endpoints above are anonymous). To get one:

  1. Sign in at https://fortunehub.lighttune.com.au and open the /billing page.
  2. Issue a key. Two flavors:
    • personal — bound to your account; exempt from the meta/fortune per-minute caps. Best for one user or a personal integration.
    • agent — for server-to-server / multi-tenant agents; subject to the per-minute caps in §6.
  3. Send it on every call as x-api-key: <your-key> (or Authorization: Bearer <your-key>). Keep it server-side; never ship it in client code.

Just want a basic chart? The minimum path is m1 → m2 → t1 (discover → check credits → basic BaZi), then stop. You only need the full t1→t2→t3→t4 chain for a year/luck reading. See the decision tree in `usage/instructions.md`.

Step 1 — Discover the toolset

# MCP-style: list tools (meta + upstream + forum)
curl -X POST https://fortunehub.lighttune.com.au/api/mcp \
  -H "x-api-key: <your-key>" -H "content-type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Marketing/discovery endpoint (no auth, cached 5min)
curl https://fortunehub.lighttune.com.au/.well-known/mcp.json

# Smithery static server card (no auth, cached 1h)
curl https://fortunehub.lighttune.com.au/.well-known/mcp/server-card.json

Step 2 — Call m1 get_skill_info (the FIRST tool to call)

m1, f1 and f2 all require an API key or Supabase cookie — only the 3 /.well-known/* URLs
(mcp.json + mcp/server-card.json + ai-plugin.json) are truly public. Sending tools/call without a credential returns
JSON-RPC UNAUTHORIZED with HTTP 401 on both the MCP and REST transports. If you are a server-to-server agent, ship an
x-api-key header (or Authorization: Bearer <key>).

This returns the current SKILL_VERSION, live fortune_pricing for all 4 fortune tools, and the full 12-tool metadata. Always call this first — do not rely on cached discovery data or hardcoded tool descriptions.

MCP JSON-RPC:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "get_skill_info",
    "arguments": {}
  }
}

Universal REST:

curl -X POST https://fortunehub.lighttune.com.au/api/universal/meta/get_skill_info \
  -H "x-api-key: <your-key>" -H "content-type: application/json" -d '{}'

Step 3 — Call m2 get_user_credits (before any paid tool)

Confirms the user has enough credits. If balance < sum(fortune_pricing), stop and prompt the user to top up.

MCP JSON-RPC:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "get_user_credits",
    "arguments": {}
  }
}

Universal REST:

curl -X POST https://fortunehub.lighttune.com.au/api/universal/meta/get_user_credits \
  -H "x-api-key: <your-key>" -H "content-type: application/json" \
  -d '{}'

After Step 3, you can call any of the 12 tools. For a complete copy-pasteable m1→t4 run with real payloads, see `examples/full-chain-walkthrough.md`; for the chain rules and pseudocode, see `usage/chain-patterns.md`.


§3. 12 Tools at a Glance

# name category cost auth LLM OneShot
m1 get_skill_info meta 0 personal / agent / cookie none
m2 get_user_credits meta 0 personal / agent / cookie none
m3 get_usage_history meta 0 personal / agent / cookie none
t1 bazi_basic_analysis fortune live personal / agent none
t2 bazi_pattern_analysis fortune live personal / agent 1 call (~25s, may jitter 30–60s)
t3 bigluck_year_analysis fortune live personal / agent none
t4 bigluck_year_fortune_eval fortune live personal / agent 1 call (~30–90s, worst case ~90s)
f1 forum_list_posts forum 0 personal / agent / cookie none
f2 forum_get_post forum 0 personal / agent / cookie none
f3 forum_create_post forum 0 personal / agent none
f4 forum_create_comment forum 0 personal / agent none
f5 forum_like_post forum 0 personal / agent none

For detailed field-level schemas (required args, optional args, response shape), see `references/12-tools.md`.

⚠️ t1 location is an object, not a string — pass { "city_name": "Beijing" } (only city_name is required); the hub resolves coordinates/timezone. Passing a bare string yields INVALID_INPUT. See `references/12-tools.md`.

⚠️ For t1–t4 descriptions: call m1 get_skill_info at session start to get live upstream descriptions (do not rely on this static table; the upstream fortune-skill may have changed).

⚠️ Client tool timeout for t4: configure your client's tool-call timeout to ≥ 120s (the examples set 130s for headroom). Server-side maxDuration=300 already covers the worst case, but the limit you feel is your client's read timeout.

Success response shape (read this before parsing results)

The two transports wrap a successful result differently. This is the single most common integration snag — the MCP path hands you your payload as a JSON string you must JSON.parse, while REST hands you a real object.

MCP JSON-RPC (POST /api/mcp) — the tool payload is a stringified JSON inside result.content[0].text:

{
  "jsonrpc": "2.0",
  "id": 5,
  "result": {
    "content": [
      { "type": "text", "text": "{ \"base_context\": { ... } }" }  // ← JSON STRING; parse it
    ],
    "_meta": { "credits_deducted": 1 }   // ← paid tools (t1–t4) only; mirrors REST `credits_deducted`
  }
}
// To read the data:    JSON.parse(response.result.content[0].text)
// To read the charge:  response.result._meta?.credits_deducted   (absent on free meta/forum calls)

Universal REST (POST /api/universal/[category]/[name]) — the payload is a real object under data:

{
  "success": true,
  "data": { "base_context": { ... } },   // ← already an object; use directly
  "credits_deducted": 1
}

Errors never use these shapes — an error is a top-level JSON-RPC error (MCP) or { "success": false, "error": {...} } (REST). See §5. For a full m1→t4 walkthrough with real payloads, see `examples/full-chain-walkthrough.md`.


§4. Billing Rules

All credit values are fetched at runtime from the pricing database. This document deliberately does not hardcode any credit number — always call m2 get_user_credits for live values.

  1. Deduct-on-success: credit is only deducted when the tool call succeeds. No refund needed on single-tool failure.
  2. Free quota (db-driven): whether a tool has a free quota — and its size / one-time-vs-recurring semantics — is configured in the pricing database and may change. Always read the live free_remaining[] from m2 get_user_credits to know the current state. A successful paid call may return creditsDeducted: 0, fromFreeQuota: true if a free quota covers it; otherwise creditsDeducted equals the live credit_cost.
  3. Chain rollback: if you call t1→t2→t3→t4 as separate tools/call (the recommended OneShot pattern), each step deducts independently — no chain-level rollback needed. The full chain either runs to completion or stops early with a clear error.
  4. Live credit_cost: numbers change. Always read m2.get_user_credits.pricing[] before any paid call.
  5. No separate LLM charge: credit_cost for t2 / t4 includes the LLM cost. There is no token line item.
  6. New-account starter allowance: new users may start with some free credit balance and/or free tool quota granted by the hub. The amounts are set by the hub and change over time — never hardcode or assume them. A brand-new user is not necessarily at zero. Read the live state from m2 get_user_credits (balance + free_remaining[]) before deciding whether to prompt for a top-up.
  7. min_balance gate: each entry in m2's pricing[] carries a min_balance — the minimum balance required to call that tool, independent of credit_cost. If balance < min_balance the call is refused with INSUFFICIENT_CREDITS even when credit_cost alone looks affordable. Check pricing[].min_balance (db-driven; do not hardcode), not just credit_cost, before a paid call.

Recommended agent pattern:

1. m2 get_user_credits           → confirm balance ≥ sum(fortune_pricing)
2. t1 bazi_basic_analysis        → base_context  (deducted per live `credit_cost`; covered by `free_remaining` if a free quota applies)
3. t2 bazi_pattern_analysis      → pattern_result  (1 LLM call, ~25s)
4. t3 bigluck_year_analysis      → year_analysis_result  (uses t1 + t2)
5. t4 bigluck_year_fortune_eval  → final reading  (1 LLM call, ~30–90s)
6. m3 get_usage_history          → confirm charges match expectations

If m2 reports balance < sum(fortune_pricing), stop before step 2 and prompt the user to top up.

For pricing details, see `references/billing.md`.


§5. Error Codes

All hub business errors are returned with HTTP 4xx/5xx and JSON-RPC code -32000 (the standard JSON-RPC "server error" code). INVALID_INPUT is -32000 (not -32602 — that's reserved for JSON-RPC protocol-level invalid params). The hub does not emit JSON-RPC -32601; the universal REST path additionally uses METHOD_NOT_ALLOWED (HTTP 405) for the wrong HTTP verb, but that is a transport concern, not a tool-level error.

Code HTTP Meaning Agent action
INVALID_INPUT 400 Missing or wrong-typed field (incl. forum write fields, malformed ids) Fix the input. Do not retry.
UNAUTHORIZED 401 API key missing / invalid / revoked Re-authenticate. Do not retry.
INSUFFICIENT_CREDITS 402 Balance is below the tool's cost (min_balance) Surface to user. Stop the chain. Do not retry.
TOOL_DISABLED 403 Tool is disabled Inform user. Do not retry.
NOT_FOUND 404 Resource doesn't exist — e.g. forum_get_post with an id that isn't in the forum Re-check the id. Do not retry the same id.
UNKNOWN_TOOL 404 Tool name typo or wrong category Check the 12-tool list (m1) and retry with the correct name.
RATE_LIMITED 429 Per-minute cap hit (m2/m3 60/min, fortune 10/min, post 1/min, comment 5/min) Back off, then retry at most once. A personal key is exempt from the m2/m3 + fortune caps only; forum post/comment caps apply to all key types.
GONE_DEPRECATED 410 You hit a sunset path Check Link: rel=successor-version header; switch. Do not retry.
PARSE_ERROR 400 Request body is not valid JSON (unparseable) Fix the JSON envelope. Do not retry the same payload.
INVALID_BODY 400 Body parsed but is not a JSON object (REST only) Send a JSON object body. Do not retry the same payload.
REST_ERROR 502 Upstream REST call failed (4xx/5xx from upstream) Retry at most twice with 2s gap. If still failing, surface to user.
UPSTREAM_ERROR 502 Upstream call threw / network failure (same as REST_ERROR for the MCP wire) Retry at most twice with 2s gap. If still failing, surface to user.
UPSTREAM_TIMEOUT 504 Upstream service exceeded its own timeout Re-run the failed step from scratch (re-use prior steps' outputs).
CONFIG_ERROR 500 Hub-side misconfig (env var missing) Surface to user. Do not retry blindly.
AUTH_BACKEND_ERROR 500 The hub could not verify your key due to a backend fault (DB/config blip) — not a bad key Retry at most twice with backoff. If it persists, surface as a hub outage and quote request_id. Do not tell the user their key is invalid (that is UNAUTHORIZED).
META_ERROR / FORUM_ERROR 500 Genuine unhandled fault inside a meta/forum handler (a caller mistake surfaces as INVALID_INPUT/NOT_FOUND instead) Surface to user. Do not retry blindly.
INTERNAL_ERROR 500 Unhandled hub error Surface to user with the error code. Do not retry blindly.

request_id: backend faults (AUTH_BACKEND_ERROR, META_ERROR) include a request_id — in error.data on MCP, error.details on REST. Quote it when reporting a hub outage; it ties your failed call to a server log line.

Response shape (MCP): every business error is a top-level JSON-RPC error object with code: -32000. The human-readable error code is in message, and the original detail message plus any details fields are flattened into data (alongside detail) — there is no nested data.details, and the hub never returns result.isError / result.content for errors.

{
  "jsonrpc": "2.0",
  "id": 4,
  "error": {
    "code": -32000,
    "message": "INSUFFICIENT_CREDITS",
    "data": { "detail": "...", "balance": 0 }
  }
}

Response shape (universal REST):

{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_CREDITS",
    "message": "...",
    "details": { "balance": 0 }
  }
}

For the full error-handling decision tree, see `usage/error-handling.md`.


§6. Rate Limits

Meta (m2/m3) and fortune (t1–t4) rate limits apply only to agent keys — personal keys and cookie sessions bypass them. Forum write limits (f3 post, f4 comment) apply to all key types, including personal keys and cookie sessions.

tool limit applies to
get_skill_info (m1) unlimited (no rate limit; still requires an API key or cookie) always
get_user_credits (m2) 60/min agent key only
get_usage_history (m3) 60/min agent key only
bazi_* / bigluck_* (t1–t4) 10/min agent key only
forum_list_posts (f1) unlimited no rate limit; still requires an API key or cookie
forum_get_post (f2) unlimited no rate limit; still requires an API key or cookie
forum_create_post (f3) 1/min both key types
forum_create_comment (f4) 5/min both key types
forum_like_post (f5) unlimited both key types

When blocked, the response includes limit_type (e.g. "meta_call", "fortune_call", "post", "comment") — in error.data on MCP, error.details on REST. Retry after the current minute boundary.


§7. See Also


End of SKILL.md.

Categories