@openephemeris/mcp-server 4.14.3 → 4.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,27 @@ Version numbering follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## [4.15.1] — 2026-09-10
11
+
12
+ ### Fixed
13
+ - **`ephemeris_planet_position` now says what `planet_id=10` actually returns.** The body is named
14
+ "North Node (Mean)", but the API serves the osculating **true** node for it (DE440, true ecliptic of
15
+ date) — the node Human Design uses and the one `/human-design/chart` derives gates from. It wobbles
16
+ ±1.7° about the mean and can go direct, so a `longitude_speed` that varies or turns positive is
17
+ correct, not a defect. Id 11 is the analytical true node; neither id is a classical mean node. The
18
+ tool description states this so no caller selects id 10 believing it a mean node. Names and ids are
19
+ unchanged.
20
+
21
+ ## [4.15.0] — 2026-09-03
22
+
23
+ ### Added
24
+ - **`electional_angle_crossings` gains `natal_datetime`.** Pass a zoned natal instant and the search
25
+ finds when the **secondary-progressed** positions of Sun, Moon, Mercury, Venus, Mars and the lunar
26
+ nodes reach an angle at the place — the same body set and day-for-a-year formula the hybrid CCG
27
+ transit map draws with — while slower bodies stay transits. Progressed crossings carry
28
+ `progressed: true` and `search_summary.progression` names the mode, the natal instant and the
29
+ bodies progressed. Omit the parameter for the plain transit search, which is unchanged.
30
+
10
31
  ## [4.14.3] — 2026-09-02
11
32
 
12
33
  ### Fixed
package/README.md CHANGED
@@ -198,6 +198,14 @@ Cursor deeplink payload:
198
198
  The server is hosted at `https://mcp.openephemeris.com/mcp` with full Streamable HTTP support (MCP 2025-11-25 spec). Remote-only clients can connect directly — no bridge/proxy required:
199
199
 
200
200
  - **Claude Web**: Add `https://mcp.openephemeris.com/mcp` as a custom connector URL — leave OAuth Client ID and Secret **blank**. The server uses OAuth 2.1 + PKCE (Dynamic Client Registration), so Claude handles authentication via a browser popup automatically.
201
+ - **ChatGPT**: OpenEphemeris is not in the ChatGPT app directory — you add it yourself.
202
+ Turn on **Settings → Plugins → Advanced → Developer mode**, then use the **+ (Create app)**
203
+ button on [chatgpt.com/plugins](https://chatgpt.com/plugins). Server URL:
204
+ `https://mcp.openephemeris.com/mcp` (append `?profile=core` for the curated 39-tool
205
+ surface, which still includes every interactive chart). Leave Authentication on **OAuth**;
206
+ the same PKCE + Dynamic Client Registration flow applies, so there is no client ID or
207
+ secret to enter. Charts render inline, exactly as they do in Claude. *Developer mode was
208
+ available on a Free plan when this was last checked (2026-09-04); availability may vary.*
201
209
  - **Via Smithery**: Use the [Smithery listing](https://smithery.ai/servers/open-ephemeris/openephemeris) for managed connections with any client
202
210
  - **Legacy SSE**: retired in 3.20.0 — use Streamable HTTP at `/mcp`
203
211
 
@@ -235,7 +243,16 @@ Nine of the tools don't answer with JSON. They open a chart in the conversation
235
243
 
236
244
  This matters more than it sounds. A natal chart returned as JSON is a list of numbers you have to already understand to read. The same chart rendered as a wheel is something you can point at. Click a planet and you get that placement explained; click a house and you get what's in it. The chart stays on screen while you keep talking, and it doesn't cost another credit to keep looking at it.
237
245
 
238
- These need a host that supports MCP Apps — Claude Desktop is the main one today. In a client without app support the same tools still work; you get the underlying data instead of the picture, so nothing breaks, you just don't get the wheel.
246
+ These need a host that supports MCP Apps. **Claude and ChatGPT both do**, and they render
247
+ the same widget — there is no separate ChatGPT build. MCP Apps ([SEP-1865][sep1865]) was
248
+ co-authored by Anthropic and OpenAI and became the first official MCP extension in January
249
+ 2026, so one `ui://` resource serves both. In ChatGPT you add the server yourself as a
250
+ custom app (see [Setup](#setup)); OpenEphemeris is not in the ChatGPT app directory.
251
+
252
+ In a client without app support the same tools still work; you get the underlying data
253
+ instead of the picture, so nothing breaks, you just don't get the wheel.
254
+
255
+ [sep1865]: https://modelcontextprotocol.io/seps/1865-mcp-apps-interactive-user-interfaces-for-mcp
239
256
 
240
257
  | Tool | What opens | What you can click | Credits |
241
258
  |---|---|---|---|
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
  import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
8
8
  import { initTools, toolRegistry, formatToolResponse, formatToolError, modelVisibleTools, parseToolSurface, SERVER_VERSION } from "./tools/index.js";
9
+ import { buildUiMeta } from "./tools/ui-meta.js";
9
10
  import { SERVER_INSTRUCTIONS } from "./instructions.js";
10
11
  import { captureEvent, captureFirstToolCallOnce, distinctIdFor } from "./analytics.js";
11
12
  import { listPrompts, getPromptContent } from "./prompts.js";
@@ -124,18 +125,8 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
124
125
  idempotentHint: tool.annotations?.idempotentHint ?? true,
125
126
  openWorldHint: tool.annotations?.openWorldHint ?? false,
126
127
  },
127
- // Expose MCP Apps UI linkage so Claude Desktop can prefetch the resource
128
- ...(tool._meta?.ui?.resourceUri
129
- ? {
130
- _meta: {
131
- "ui/resourceUri": tool._meta.ui.resourceUri,
132
- ui: {
133
- resourceUri: tool._meta.ui.resourceUri,
134
- visibility: tool._meta.ui.visibility ?? ["model", "app"],
135
- },
136
- },
137
- }
138
- : {}),
128
+ // MCP Apps UI linkage (+ ChatGPT aliases) so hosts can prefetch the resource.
129
+ ...buildUiMeta(tool),
139
130
  }));
140
131
  if (!toolsListedReported) {
141
132
  toolsListedReported = true;
@@ -24,6 +24,7 @@ import { InMemoryEventStore } from "./event-store.js";
24
24
  import { captureEvent, captureFirstToolCallOnce, distinctIdFor } from "./analytics.js";
25
25
  import { CallToolRequestSchema, ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, isInitializeRequest, } from "@modelcontextprotocol/sdk/types.js";
26
26
  import { initTools, toolRegistry, formatToolResponse, formatToolError, modelVisibleTools, parseToolSurface, describeSurface } from "./tools/index.js";
27
+ import { buildUiMeta } from "./tools/ui-meta.js";
27
28
  import { SERVER_INSTRUCTIONS } from "./instructions.js";
28
29
  import { BackendClient, runWithClient } from "./backend/client.js";
29
30
  import { CHART_WHEEL_RESOURCE_URI, CHART_WHEEL_MIME_TYPE, getChartWheelBundle, } from "./tools/apps/chart-wheel-app.js";
@@ -304,18 +305,8 @@ function createMcpServer(analyticsId = "anonymous", surface = "core", activity =
304
305
  description: tool.description,
305
306
  inputSchema: tool.inputSchema,
306
307
  annotations: buildAnnotations(tool),
307
- // Expose MCP Apps UI linkage so Claude Desktop can prefetch the resource
308
- ...(tool._meta?.ui?.resourceUri
309
- ? {
310
- _meta: {
311
- "ui/resourceUri": tool._meta.ui.resourceUri,
312
- ui: {
313
- resourceUri: tool._meta.ui.resourceUri,
314
- visibility: tool._meta.ui.visibility ?? ["model", "app"],
315
- },
316
- },
317
- }
318
- : {}),
308
+ // MCP Apps UI linkage (+ ChatGPT aliases) so hosts can prefetch the resource.
309
+ ...buildUiMeta(tool),
319
310
  }));
320
311
  // Once per session: the host has fetched the surface. Everything after this
321
312
  // point is the model's choice, so this is the denominator for activation.
@@ -118,7 +118,7 @@ registerTool({
118
118
  "Click any pillar for an instant interpretation of what it reveals about that life domain " +
119
119
  "(Year = ancestry/early life, Month = parents/career, Day = self/spouse, Hour = children/later life).\n\n" +
120
120
  "CREDIT COST: 3 credits per call (1 base + 2 visual render).\n\n" +
121
- "Use this for a rich, interactive BaZi experience in MCP Apps-capable hosts (Claude Desktop). " +
121
+ "Use this for a rich, interactive BaZi experience in MCP Apps-capable hosts (Claude and ChatGPT). " +
122
122
  "Falls back to a text summary in other hosts, so it is also the right call without a " +
123
123
  "renderer. Deeper derivations (Ten Gods, element balance, luck pillars) and a plain " +
124
124
  "non-visual pillars lookup have dedicated tools on the full surface (`?profile=full`).",
@@ -316,7 +316,7 @@ registerTool({
316
316
  "and Incarnation Cross. " +
317
317
  "The chart is calculated using NASA JPL DE440 ephemerides for both Personality and Design positions. " +
318
318
  "Use this instead of human_design_chart for a richer, interactive HD " +
319
- "experience in MCP Apps-capable hosts (Claude Desktop). " +
319
+ "experience in MCP Apps-capable hosts (Claude and ChatGPT). " +
320
320
  "Falls back to a text summary in other hosts." +
321
321
  HD_DISCLAIMER,
322
322
  inputSchema: {
@@ -1312,7 +1312,7 @@ registerTool({
1312
1312
  "CREDIT COST: 3 credits per call.\n\n" +
1313
1313
  "Highlights the channels a transit temporarily COMPLETES with the natal chart and any centers it " +
1314
1314
  "newly defines — the core of a Human Design transit reading. " +
1315
- "Returns an interactive overlay bodygraph in MCP Apps-capable hosts (Claude Desktop), with a text " +
1315
+ "Returns an interactive overlay bodygraph in MCP Apps-capable hosts (Claude and ChatGPT), with a text " +
1316
1316
  "summary fallback elsewhere. Premium (Pro tier). Calculated with NASA JPL DE440 ephemerides." +
1317
1317
  HD_DISCLAIMER,
1318
1318
  inputSchema: {
@@ -127,7 +127,7 @@ registerTool({
127
127
  "CREDIT COST: 1 credit per call.\n\n" +
128
128
  "Supports house system switching (Placidus, Whole Sign, Equal, Koch). " +
129
129
  "The chart is computed using NASA JPL DE440 ephemerides for sub-arcsecond precision. " +
130
- "Renders interactively in MCP Apps-capable hosts (Claude Desktop) and falls back to " +
130
+ "Renders interactively in MCP Apps-capable hosts (Claude and ChatGPT) and falls back to " +
131
131
  "static SVG in other hosts.",
132
132
  inputSchema: {
133
133
  type: "object",
@@ -204,7 +204,7 @@ registerTool({
204
204
  " • Upcoming New Moon and Full Moon dates\n\n" +
205
205
  "CREDIT COST: 3 credits per call (phase + void-of-course + aspects, 1 each).\n\n" +
206
206
  "Use this instead of ephemeris_moon_phase for a rich, interactive lunar phase experience in " +
207
- "MCP Apps-capable hosts (Claude Desktop). Falls back to a text summary in other hosts.",
207
+ "MCP Apps-capable hosts (Claude and ChatGPT). Falls back to a text summary in other hosts.",
208
208
  inputSchema: {
209
209
  type: "object",
210
210
  properties: {
@@ -106,7 +106,7 @@ registerTool({
106
106
  "SEARCH RANGE LIMITS: Explorer/PayG → 1 year; Pro → 5 years; Startup → 10 years.\n\n" +
107
107
  "CREDIT COST: 6 credits per call (natal chart + predictive transit search).\n\n" +
108
108
  "Use this for a rich, interactive transit-forecast experience in MCP Apps-capable hosts " +
109
- "(Claude Desktop). Falls back to a text summary in other hosts.",
109
+ "(Claude and ChatGPT). Falls back to a text summary in other hosts.",
110
110
  inputSchema: {
111
111
  type: "object",
112
112
  properties: {
@@ -102,7 +102,7 @@ registerTool({
102
102
  "Returns an embedded visual explorer that lets you click any rashi cell for its themes and " +
103
103
  "any planets placed there. Shows sidereal (Lahiri by default) planet placements, nakshatra with " +
104
104
  "pada, navamsa, and the Lagna (Ascendant) rashi. Uses NASA JPL DE440 ephemerides. " +
105
- "Use this for a rich, interactive Jyotish experience in MCP Apps-capable hosts (Claude Desktop). " +
105
+ "Use this for a rich, interactive Jyotish experience in MCP Apps-capable hosts (Claude and ChatGPT). " +
106
106
  "Falls back to a text summary in other hosts.",
107
107
  inputSchema: {
108
108
  type: "object",
@@ -10,7 +10,7 @@ const HOUSE_SYSTEM_MAP = {
10
10
  };
11
11
  registerTool({
12
12
  name: "ephemeris_bi_wheel",
13
- description: "Generate a Bi-Wheel (Synastry/Transit) image (SVG) comparing two charts. Draws Subject A's planets on the inside wheel and Subject B's on the outside wheel. Returns a native SVG that Claude displays inline in the conversation.\n\n" +
13
+ description: "Generate a Bi-Wheel (Synastry/Transit) image (SVG) comparing two charts. Draws Subject A's planets on the inside wheel and Subject B's on the outside wheel. Returns a native SVG the host displays inline in the conversation.\n\n" +
14
14
  "For a user-facing interactive bi-wheel, use explore_bi_wheel instead.\n\n" +
15
15
  "CREDIT COST: 2 credits per call.\n\n" +
16
16
  "EXAMPLE: Compare someone born April 15, 1990 (A) to someone born June 10, 1992 (B):\n" +
@@ -10,7 +10,7 @@ const HOUSE_SYSTEM_MAP = {
10
10
  };
11
11
  registerTool({
12
12
  name: "ephemeris_chart_wheel",
13
- description: "Generate a classic astrological Chart Wheel image (SVG) for a person/event. This draws a standard circular chart wheel with planets, aspects, and house cusps. The tool returns a native SVG image that Claude displays inline in the conversation — no external tools needed.\n\n" +
13
+ description: "Generate a classic astrological Chart Wheel image (SVG) for a person/event. This draws a standard circular chart wheel with planets, aspects, and house cusps. The tool returns a native SVG image the host displays inline in the conversation — no external tools needed.\n\n" +
14
14
  "For a user-facing interactive chart wheel, use explore_natal_chart instead.\n\n" +
15
15
  "CREDIT COST: 2 credits per call.\n\n" +
16
16
  "EXAMPLE: born 15 April 1990 at 2:30 PM local time in Chicago:\n" +
@@ -1,7 +1,7 @@
1
1
  import { registerTool, validateRequired } from "../index.js";
2
2
  import { getActiveClient } from "../../backend/client.js";
3
3
  import { OUTPUT_SCHEMA_JSON } from "../output-schemas.js";
4
- import { WINDOW_DATE_DESC } from "../datetime.js";
4
+ import { DATETIME_DESC, WINDOW_DATE_DESC, localToUtcIso, timezoneProperty } from "../datetime.js";
5
5
  // All electional endpoints are GET endpoints with query params.
6
6
  // GET /electional/find-window — already existed, keeping it
7
7
  registerTool({
@@ -279,6 +279,10 @@ registerTool({
279
279
  "Crossing times use the same in-mundo geometry as the astrocartography line engine, so casting a " +
280
280
  "map at a returned moment puts that line through the query point. Circumpolar cases (a body that " +
281
281
  "never rises or never sets at that latitude) simply return no event.\n\n" +
282
+ "PROGRESSED (hybrid) lines: pass natal_datetime to search the SECONDARY-PROGRESSED positions of " +
283
+ "Sun, Moon, Mercury, Venus, Mars and the lunar nodes — the same body set and formula the CCG hybrid " +
284
+ "transit map (/acg/ccg mode=ccg_hybrid) draws with; every other body stays a transit. Progressed " +
285
+ "crossings carry progressed: true and search_summary.progression names what was progressed.\n\n" +
282
286
  "CREDIT COST: 5 credits per call.\n\n" +
283
287
  "EXAMPLE: When does Mercury cross the Descendant over Warsaw on 2026-07-30?\n" +
284
288
  " latitude=52.2297, longitude=21.0122, bodies='Mercury', angles='DC',\n" +
@@ -320,6 +324,14 @@ registerTool({
320
324
  type: "number",
321
325
  description: "Maximum number of crossing events to return (default 5000).",
322
326
  },
327
+ natal_datetime: {
328
+ type: "string",
329
+ description: "Optional natal (birth) instant. When given, Sun, Moon, Mercury, Venus, Mars and the lunar " +
330
+ "nodes are secondary-progressed from it (day-for-a-year) and the search finds when their " +
331
+ "PROGRESSED position reaches the angle — the line a hybrid CCG map paints. Slow bodies " +
332
+ "(Jupiter outward) are unaffected. Omit for the plain transit search. " + DATETIME_DESC,
333
+ },
334
+ natal_timezone: timezoneProperty("the natal birth location", "Europe/London"),
323
335
  format: {
324
336
  type: "string",
325
337
  enum: ["json", "llm"],
@@ -349,6 +361,11 @@ registerTool({
349
361
  query.refraction_correction = args.refraction_correction;
350
362
  if (args.max_results !== undefined)
351
363
  query.max_results = args.max_results;
364
+ // A naive birth time plus its zone is a legitimate call (the datetime
365
+ // contract); the API's GET takes one zoned instant, so resolve here.
366
+ if (args.natal_datetime) {
367
+ query.natal_datetime = localToUtcIso("natal_datetime", args.natal_datetime, args.natal_timezone, "natal_timezone");
368
+ }
352
369
  if (args.format)
353
370
  query.format = args.format;
354
371
  return await getActiveClient().request("GET", "/electional/angle-crossings", {
@@ -12,6 +12,9 @@ registerTool({
12
12
  "12=Lilith (Mean), 15=Chiron, 17=Ceres, 18=Pallas, 19=Juno, 20=Vesta.\n\n" +
13
13
  "SOUTH NODE: there is no South Node id. The South Node is always exactly " +
14
14
  "opposite the North Node — request id 10 or 11 and add 180° (mod 360).\n\n" +
15
+ "NODES: despite its name, id 10 returns the osculating TRUE node (DE440, ecliptic of " +
16
+ "date) — the Human Design node. It wobbles ±1.7° about the mean and can go direct; " +
17
+ "that is correct. Id 11 is the analytical true node. Neither is a classical mean node.\n\n" +
15
18
  "CREDIT COST: 1 credit per call.\n\n" +
16
19
  "EXAMPLE: Where is Mars on 2026-03-20 at noon UTC?\n" +
17
20
  " planet_id=4, datetime='2026-03-20T12:00:00Z'",
@@ -9,7 +9,7 @@ registerTool({
9
9
  "Renders the full bodygraph circuit board showing all 9 centers, 36 channels, and 64 gates " +
10
10
  "with Personality (conscious) and Design (unconscious) activations color-coded. " +
11
11
  "Defined centers are filled with their HD doctrine color; open/undefined centers remain muted. " +
12
- "Returns a native SVG that Claude displays inline. Use format='png' to opt into raster output (requires server-side rasterizer).\n\n" +
12
+ "Returns a native SVG the host displays inline. Use format='png' to opt into raster output (requires server-side rasterizer).\n\n" +
13
13
  "For a user-facing interactive bodygraph explorer, use explore_human_design instead.\n\n" +
14
14
  "CREDIT COST: 2 credits per call.\n\n" +
15
15
  "EXAMPLE (local birth time + zone): born 15 April 1990 at 14:30 in Chicago:\n" +
@@ -42,7 +42,7 @@ registerTool({
42
42
  format: {
43
43
  type: "string",
44
44
  enum: ["svg", "png"],
45
- description: "Output format. Defaults to 'svg' (pure Go renderer — no external deps, displays natively in Claude). Use 'png' for raster output (requires server-side rasterizer).",
45
+ description: "Output format. Defaults to 'svg' (pure Go renderer — no external deps, displays inline in any host that renders images). Use 'png' for raster output (requires server-side rasterizer).",
46
46
  },
47
47
  },
48
48
  required: ["datetime"],
@@ -0,0 +1,39 @@
1
+ /**
2
+ * src/tools/ui-meta.ts — the `_meta` block a tool carries when it renders an MCP App.
3
+ *
4
+ * One builder, used by both transports (stdio `src/index.ts`, remote
5
+ * `src/server-sse.ts`), because the two had drifted into copy-paste twins of the
6
+ * same object literal and only one of them would ever get updated.
7
+ *
8
+ * Three wire forms, all describing the same `ui://` resource:
9
+ *
10
+ * 1. `_meta.ui.{resourceUri,visibility}` — the canonical MCP Apps form
11
+ * (SEP-1865, first official MCP extension 2026-01-26, folded into the
12
+ * 2026-07-28 spec). This is what the spec says hosts read.
13
+ * 2. `_meta["ui/resourceUri"]` — the deprecated flat alias. The spec says it
14
+ * "will be removed before general availability"; kept only for pre-GA
15
+ * hosts still on the older shape. Drop it once those are gone.
16
+ * 3. `_meta["openai/*"]` — ChatGPT's compatibility aliases. OpenAI co-authored
17
+ * SEP-1865 and its own reference documents `openai/outputTemplate` as an
18
+ * "optional/compatibility alias for `_meta.ui.resourceUri`", so a fully
19
+ * converged ChatGPT host needs nothing here. Emitting it anyway is a few
20
+ * bytes and removes the question of which side converged first.
21
+ *
22
+ * `openai/widgetAccessible` marks a tool the widget itself may invoke. That is
23
+ * exactly what `visibility` already encodes: a tool visible to "app" is one the
24
+ * iframe calls (the `*_recalculate` and `*_on_*_click` handlers), so the flag is
25
+ * derived rather than hand-maintained on 92 tools.
26
+ */
27
+ export interface UiMetaSource {
28
+ _meta?: {
29
+ ui?: {
30
+ resourceUri?: string;
31
+ visibility?: Array<"app" | "model">;
32
+ };
33
+ };
34
+ }
35
+ /**
36
+ * Build the `_meta` wrapper for a tool, or `{}` when the tool renders no UI.
37
+ * Spread the result into the tools/list entry.
38
+ */
39
+ export declare function buildUiMeta(tool: UiMetaSource): Record<string, unknown>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * src/tools/ui-meta.ts — the `_meta` block a tool carries when it renders an MCP App.
3
+ *
4
+ * One builder, used by both transports (stdio `src/index.ts`, remote
5
+ * `src/server-sse.ts`), because the two had drifted into copy-paste twins of the
6
+ * same object literal and only one of them would ever get updated.
7
+ *
8
+ * Three wire forms, all describing the same `ui://` resource:
9
+ *
10
+ * 1. `_meta.ui.{resourceUri,visibility}` — the canonical MCP Apps form
11
+ * (SEP-1865, first official MCP extension 2026-01-26, folded into the
12
+ * 2026-07-28 spec). This is what the spec says hosts read.
13
+ * 2. `_meta["ui/resourceUri"]` — the deprecated flat alias. The spec says it
14
+ * "will be removed before general availability"; kept only for pre-GA
15
+ * hosts still on the older shape. Drop it once those are gone.
16
+ * 3. `_meta["openai/*"]` — ChatGPT's compatibility aliases. OpenAI co-authored
17
+ * SEP-1865 and its own reference documents `openai/outputTemplate` as an
18
+ * "optional/compatibility alias for `_meta.ui.resourceUri`", so a fully
19
+ * converged ChatGPT host needs nothing here. Emitting it anyway is a few
20
+ * bytes and removes the question of which side converged first.
21
+ *
22
+ * `openai/widgetAccessible` marks a tool the widget itself may invoke. That is
23
+ * exactly what `visibility` already encodes: a tool visible to "app" is one the
24
+ * iframe calls (the `*_recalculate` and `*_on_*_click` handlers), so the flag is
25
+ * derived rather than hand-maintained on 92 tools.
26
+ */
27
+ const DEFAULT_VISIBILITY = ["model", "app"];
28
+ /**
29
+ * Build the `_meta` wrapper for a tool, or `{}` when the tool renders no UI.
30
+ * Spread the result into the tools/list entry.
31
+ */
32
+ export function buildUiMeta(tool) {
33
+ const resourceUri = tool._meta?.ui?.resourceUri;
34
+ if (!resourceUri)
35
+ return {};
36
+ const visibility = tool._meta?.ui?.visibility ?? DEFAULT_VISIBILITY;
37
+ return {
38
+ _meta: {
39
+ // Deprecated flat form — pre-GA hosts only.
40
+ "ui/resourceUri": resourceUri,
41
+ // Canonical MCP Apps form.
42
+ ui: { resourceUri, visibility },
43
+ // ChatGPT compatibility aliases.
44
+ "openai/outputTemplate": resourceUri,
45
+ "openai/widgetAccessible": visibility.includes("app"),
46
+ },
47
+ };
48
+ }
package/dist/ui/bazi.html CHANGED
@@ -6802,7 +6802,7 @@ function E_(e4, t2, n2 = []) {
6802
6802
  let e5 = document.getElementById("fs-hint-banner");
6803
6803
  e5 && e5.remove();
6804
6804
  let t3 = document.createElement("div");
6805
- t3.id = "fs-hint-banner", Object.assign(t3.style, { position: "fixed", top: "12px", left: "50%", transform: "translateX(-50%)", background: "oklch(14% 0.012 265 / 0.90)", backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)", border: "1px solid oklch(72% 0.12 80 / 0.3)", borderRadius: "20px", padding: "6px 16px", fontSize: "11px", fontWeight: "600", color: "oklch(80% 0.08 80)", letterSpacing: "0.04em", zIndex: "9999", pointerEvents: "none", opacity: "0", transition: "opacity 300ms ease", whiteSpace: "nowrap" }), t3.textContent = "Esc \u2014 exit fullscreen & return to Claude chat", document.body.appendChild(t3), requestAnimationFrame(() => {
6805
+ t3.id = "fs-hint-banner", Object.assign(t3.style, { position: "fixed", top: "12px", left: "50%", transform: "translateX(-50%)", background: "oklch(14% 0.012 265 / 0.90)", backdropFilter: "blur(12px)", WebkitBackdropFilter: "blur(12px)", border: "1px solid oklch(72% 0.12 80 / 0.3)", borderRadius: "20px", padding: "6px 16px", fontSize: "11px", fontWeight: "600", color: "oklch(80% 0.08 80)", letterSpacing: "0.04em", zIndex: "9999", pointerEvents: "none", opacity: "0", transition: "opacity 300ms ease", whiteSpace: "nowrap" }), t3.textContent = "Esc \u2014 exit fullscreen & return to chat", document.body.appendChild(t3), requestAnimationFrame(() => {
6806
6806
  t3.style.opacity = "1";
6807
6807
  }), setTimeout(() => {
6808
6808
  t3.style.opacity = "0", setTimeout(() => t3.remove(), 350);
@@ -6856,6 +6856,11 @@ async function V_(e4) {
6856
6856
  }
6857
6857
  }
6858
6858
  async function H_(e4) {
6859
+ let t2 = F_ == null ? void 0 : F_._birth_params;
6860
+ if (t2) {
6861
+ let n2 = String(t2.hour).padStart(2, "0");
6862
+ e4 = "BaZi chart: ".concat(t2.year, "-").concat(String(t2.month).padStart(2, "0"), "-").concat(String(t2.day).padStart(2, "0"), " ").concat(n2, ":00\n\n").concat(e4);
6863
+ }
6859
6864
  try {
6860
6865
  (await P_.sendMessage({ role: "user", content: [{ type: "text", text: e4 }] })).isError && console.error("[BaZi] Host rejected sendMessage");
6861
6866
  } catch (e5) {