mcp server
bazi-flex-mcp
A Bazi (八字 / Four Pillars) MCP server that works even when the birth hour is unknown. Built on shunshi-bazi-core, with true-solar-time correction.
Set up
Runs on your computer. Pick your agent and operating system.
Before you start
- Node.js 20 or newer
{
"mcpServers": {
"bazi-flex-mcp": {
"command": "npx",
"args": [
"-y",
"bazi-flex-mcp"
]
}
}
}same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12
Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs
README
synced Oct 27, 2026bazi-flex-mcp
A Bazi (八字 / Four Pillars) MCP server that works even when the birth hour is unknown.
Most real-world users don't know the exact hour they were born. Existing Bazi MCP servers require a complete birth timestamp (year / month / day / hour) and refuse to produce anything useful otherwise. bazi-flex-mcp is aiming to be the first open-source Bazi MCP server that handles the partial-time case directly, so an AI assistant can still reason about the chart when the user only knows the date.
The calculation lives in this repository (packages/core, on top of tyme4ts), with true-solar-time correction.
Status
Early WIP. Not published to npm yet. Both the full-time and partial-time tools are now available; auth on the HTTP transport is the next milestone.
Features
- Full-time Bazi charting —
getBaziChart - Partial-time mode —
getBaziChartPartial, chart computation when the birth hour is unknown - stdio transport — for Claude Desktop and other local MCP clients
- Streamable HTTP transport — for remote hosts (Cloudflare Workers, mobile Claude)
- OAuth / auth on HTTP transport
Quick start
Claude Desktop (stdio, local)
Clone the repo and point Claude Desktop's MCP config at it:
{
"mcpServers": {
"bazi-flex": {
"command": "bun",
"args": ["run", "/absolute/path/to/bazi-flex-mcp/src/transports/stdio.ts"]
}
}
}
Restart Claude Desktop. You should see getBaziChart in the tool list.
HTTP (remote)
bun install
bun dev
# → serves on http://localhost:3000
# /health — health check
# /mcp — MCP Streamable HTTP endpoint
Inspector
bunx @modelcontextprotocol/inspector bun run src/transports/stdio.ts
The getBaziChart tool
Computes a full Bazi chart from a complete birth time.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
year | int | yes | Gregorian |
month | int 1-12 | yes | |
day | int 1-31 | yes | |
hour | int 0-23 | yes | |
minute | int 0-59 | no | default 0 |
gender | 0 | 1 | yes | 0 = female, 1 = male |
city | string | no | enables true-solar-time correction |
longitude | number (°E) | no | |
latitude | number (°N) | no | |
referenceDate | YYYY-MM-DD | no | decides which decade-cycle is marked 当前; defaults to today |
liunianStart | int (year) | no | first Gregorian year for 八字.流年; defaults to referenceDate - 3 |
liunianEnd | int (year) | no | last Gregorian year for 八字.流年; defaults to referenceDate + 3 |
Output shape (post-processing on top of the calculation core)
The chart comes from @bazi-flex/core in this repository. Its output is kept intact and augmented with a few fields to make the chart more LLM-friendly:
八字.柱位详细.日柱.主星isnull(the day-master carries no ten-god against itself). Identify the day-pillar via日柱.isDayMaster === true;日柱.labelis"日主"for display. The raw engine puts"元男"/"元女"here — this server replaces that with a cleaner signal.八字.十神统计— aggregate counts keyed by ten-god, each{ 透, 藏, 共 }.透comes from year/month/hour pillars'主星;藏comes from all four pillars'副星(earth-branch hidden stems). The day-master itself is excluded.八字.大运[].日主关系isnullwhen there is no day-master-to-decade-stem relation (instead of"").八字.大运[].当前is recomputed againstmeta.referenceDateUsedso historical / hypothetical scenarios (e.g. "if I look at this chart at age 30") are supported.meta.referenceDateUsed— echoes the effective reference date.meta.scoringMethod— documents how八字.五行分值is computed (see below).八字.柱间关系— pair-wise (and triple-wise for 三刑) expansion of upstream's flat刑冲合会. Each entry carriespillars(which pillars are involved) and干支so complex charts with duplicate stems/branches (e.g. two 乙 across month/day) surface all candidate pairs instead of hiding the ambiguity. Parses upstream's short-form strings — does not re-implement relation rules.八字.流年/八字.流年范围— year-by-year 流年 table spanning[liunianStart, liunianEnd](defaults[referenceDate - 3, +3]= 7 years; widen via inputs if you need a longer horizon). Each entry:{ 年份, 干支, 天干, 地支, 主星, 藏干, 藏干十神, 当前 }. 流年 year boundaries follow the 立春-based 干支年 convention (first ~5 weeks of a Gregorian year before 立春 technically belong to the previous 干支年). Relations against 四柱/大运 are not pre-computed — combine流年[].干支with柱间关系logic or大运[].干支yourself.八字.决策辅助— three derived metrics so consumers do not recompute them:日主得令(day-master element vs month-command element,得令boolean),日主根气(day-master-element presence across all four earth-branch hidden-stems using 本/中/余 weights 1.0/0.5/0.3),透藏平衡(比劫 vs 异类 transparent/hidden counts). These are raw inputs — no 旺衰/格局/用神 judgement is made. Feed them into your own reasoning rules.- All time strings (
输入.公历,真太阳时.钟表时间,真太阳时.真太阳时,八字.公历) are normalised to ISO 8601 with second precision (YYYY-MM-DDTHH:MM:SS). The raw engine uses a mix of minute-precision ("YYYY-MM-DD HH:MM") and Chinese-formatted second-precision ("YYYY年M月D日 HH:MM:SS"); this server unifies them so consumers do not have to reconcile formats or lose seconds.真太阳时.修正秒数is added as an integer companion to修正分钟.
Where the enrichment layer still works around the core
- Pair-wise 柱间关系 — the core emits
刑冲合会as a flat short-form list without pillar labels, and the enrichment layer recovers the pairs by matching characters back against the four pillars. Now that the core is in this repository it can emit the pairs directly, which also removes the ambiguity when two pillars share a stem. - 流年 table — the core has no 流年 API, so the table is built from the canonical
(year - 4) mod 60year-ganzhi rule plus a local 藏干 lookup, both of which duplicate tablestyme4tsalready holds. - 决策辅助 — pure aggregation of already-computed fields, nothing to move.
空亡 (empty death) — structured surface
Upstream packed 空亡 into a single per-pillar string (柱.空亡) plus a tag inside 柱.神煞. Because the two surfaces used different semantics (reference data vs. void judgement based on a union of 日柱旬 and 年柱旬), consumer LLMs conflated them. This server restructures 空亡 into three explicit fields:
-
八字.旬空— top-level index of the void branches for the two traditional reference 旬s:"旬空": { "日柱旬空": ["午", "未"], "年柱旬空": ["申", "酉"] } -
柱位详细.{柱}.所在旬空亡: string[]— the two branches void in that pillar's own 旬. Reference data only; does not mean the pillar itself is falling into 空亡. -
柱位详细.{柱}.落空亡: { 日柱旬: boolean, 年柱旬: boolean }— does this pillar's earth branch actually fall into day-xun void / year-xun void. This is the authoritative "is this pillar in 空亡" signal. -
大运[].所在旬空亡/大运[].落空亡— the same two structured fields are applied to every decade-cycle entry, so 大运 and 柱位详细 share a single 空亡 surface. The upstream大运[].空亡: stringfield is removed in favour of these.
Conventions:
- Modern practice (以日起空亡): use
落空亡.日柱旬alone. - Traditional schools using year-xun: use
落空亡.年柱旬. - Upstream's
神煞array still contains a"空亡"string for back-compat; it equals the boolean-OR of the two落空亡flags.
Upstream's original 柱.空亡 and 大运[].空亡 single-string fields have been removed from the wrapper output — the structured fields above supersede them.
Wuxing score method
八字.五行分值 uses the calculation core's weighting:
| Source | Weight |
|---|---|
| Each heavenly stem (4 pillars) | 1.0 |
| Earth-branch 本气 (primary) | 1.0 |
| Earth-branch 中气 (secondary) | 0.5 |
| Earth-branch 余气 (residual) | 0.3 |
No month-command bonus and no transparent-stem bonus are applied. The raw numbers are a measure of relative presence, not classical 旺衰 strength — a higher score does not automatically mean the day-master is strong. Consumers that need 旺衰 judgement should factor in month-command, 得地/失地, 得势/失势 themselves. meta.scoringMethod repeats this information in machine-readable form.
The getBaziChartPartial tool
For callers who know only the birth date (year/month/day) but not the hour. Returns a 3-pillar chart (年/月/日) with all hour-dependent fields stripped, nulled, or recomputed.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
year | int | yes | Gregorian |
month | int 1-12 | yes | |
day | int 1-31 | yes | |
gender | 0 | 1 | yes | 0 = female, 1 = male |
referenceDate | YYYY-MM-DD | no | decides which decade-cycle is marked 当前; defaults to today |
liunianStart | int (year) | no | first Gregorian year for 八字.流年; defaults to referenceDate - 3 |
liunianEnd | int (year) | no | last Gregorian year for 八字.流年; defaults to referenceDate + 3 |
hour, minute, city, longitude, latitude are not accepted — they are only meaningful when the birth hour is known. If you have an hour, use getBaziChart instead.
How it works
The chart is built natively from three pillars. No placeholder hour is invented, so nothing in the response is derived from a time that was never supplied.
八字.三柱replaces八字.四柱and holds exactly three 干支.时柱is absent from八字.柱位详细(not nulled: the key does not exist).命宫,身宫,胎元,胎息arenull(these formulas require the birth hour).真太阳时is omitted from the response; the correction only ever moves the hour pillar.输入.公历and八字.公历are date-onlyYYYY-MM-DD;输入.时辰isnull;八字.农历carries no 时辰 suffix.八字.十神统计,八字.五行分值,八字.刑冲合会,八字.柱间关系and八字.决策辅助.日主根气all cover 年/月/日 only.占比in八字.五行分值is normalised over the 3-pillar total.八字.大运is kept: the decade-cycle direction (顺/逆) depends only on year-pillar polarity + gender, both unaffected by hour. However, 起运 and 起运日期 are measured from the birth moment to the neighbouring solar term, and the reference moment here is the midpoint of the day's month-command window, so they may carry ±1 day to ±1-2 month error, and the resulting 起始年份/结束年份 may shift by ±1 year. Treat decade boundaries as approximate windows. Seemeta.disclaimer.大运起运精度.八字.流年is unchanged: derivation depends only on the day-master stem and the 干支年.八字.旬空and per-pillar所在旬空亡/落空亡are unchanged in semantics; the day pillar depends on the hour only across the23:00子时 switch, which no date-only input can reach.
节气歧义
年柱 and 月柱 use day-granular attribution: the whole calendar day on which a 節 falls belongs to the new month, and 立春 day to the new 干支年. That is a convention, not a fact, and on the ~1.6% of dates that carry a 節 the true answer depends on an hour nobody supplied.
Rather than hide the choice, 八字.节气歧义 reports both candidates:
"节气歧义": {
"节气": "立春",
"时刻": "2000-02-04T20:40:24",
"影响": ["年柱", "月柱"],
"此刻之前": { "年柱": "己卯", "月柱": "丁丑" },
"此刻之后": { "年柱": "庚辰", "月柱": "戊寅" }
}
此刻之后 is what the rest of the response uses. On ordinary days the field is null. If the caller can narrow the birth time to one side of 时刻, prefer the matching branch and say so.
meta.disclaimer
The response includes a meta.disclaimer object enumerating exactly which fields are absent, null, or 3-pillar-only and why. Surface it to the end user when explaining the chart's confidence level: it is the single source of truth on what is and isn't trustworthy in a partial-time chart.
Development
bun install
bun run typecheck
bun dev # HTTP on :3000 (Bun)
bun run dev:worker # HTTP on :8787 via local workerd (Wrangler)
bun run dev:stdio # stdio
Deployment (Cloudflare Workers)
The HTTP transport is built on Hono + Web Standards, so it runs on Cloudflare Workers without code changes. This repo ships a wrangler.jsonc with two deploy modes.
Deploy your own instance
bunx wrangler login # first time only
bun run deploy
Your worker will be served from https://bazi-flex-mcp.<your-subdomain>.workers.dev, with MCP at /mcp.
Custom domain
To bind the worker to a domain managed on Cloudflare, override the production env in wrangler.jsonc:
"env": {
"production": {
"name": "your-worker-name",
"routes": [
{ "pattern": "bazi.example.com", "custom_domain": true }
]
}
}
Then:
bun run deploy:prod
Wrangler will create the DNS record and provision the certificate automatically.
Auto-deploy on push
.github/workflows/deploy.yml runs typecheck + tests on every push to main, then deploys with deploy:prod. Configure these repo secrets:
CLOUDFLARE_API_TOKEN— create at https://dash.cloudflare.com/profile/api-tokens withWorkers Scripts: Edit,Workers Routes: Edit,DNS: Editon your zone, plusAccount Settings: ReadCLOUDFLARE_ACCOUNT_ID— from the Cloudflare dashboard sidebar
Tech stack
- Runtime: Bun
- MCP:
@modelcontextprotocol/sdk - HTTP: Hono +
@hono/mcp - Validation: Zod 4
- Bazi engine:
packages/corein this repository, ontyme4ts
Credits
shunshi-bazi-core— the 神煞, 刑冲合会, city and true-solar-time modules inpackages/core/src/libare vendored from it under MIT. Seepackages/core/README.mdfor provenance.cantian-ai/bazi-mcp— prior art in the Bazi MCP space
License
MIT