react-native-gtkx 0.1.0-alpha.4 → 0.2.0-alpha.2
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/README.md +22 -9
- package/dist/adw/index.d.ts +3 -0
- package/dist/adw/index.js +41 -0
- package/dist/adw/index.js.map +1 -0
- package/dist/adw/widgets.generated.d.ts +63 -0
- package/dist/adw/widgets.generated.js +125 -0
- package/dist/adw/widgets.generated.js.map +1 -0
- package/dist/animated/create-animated.d.ts +2 -0
- package/dist/animated/create-animated.js +2 -0
- package/dist/animated/create-animated.js.map +1 -1
- package/dist/animated/index.d.ts +2 -0
- package/dist/animated/index.js +1 -0
- package/dist/animated/index.js.map +1 -1
- package/dist/animated/value-xy.d.ts +41 -0
- package/dist/animated/value-xy.js +90 -0
- package/dist/animated/value-xy.js.map +1 -0
- package/dist/animated/value.d.ts +9 -0
- package/dist/animated/value.js +30 -2
- package/dist/animated/value.js.map +1 -1
- package/dist/common/content.d.ts +69 -0
- package/dist/common/content.js +84 -0
- package/dist/common/content.js.map +1 -0
- package/dist/common/icon.d.ts +23 -0
- package/dist/common/icon.js +14 -0
- package/dist/common/icon.js.map +1 -0
- package/dist/common/index.d.ts +5 -0
- package/dist/common/index.js +21 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/list.d.ts +82 -0
- package/dist/common/list.js +166 -0
- package/dist/common/list.js.map +1 -0
- package/dist/common/navigation-stack.d.ts +84 -0
- package/dist/common/navigation-stack.js +313 -0
- package/dist/common/navigation-stack.js.map +1 -0
- package/dist/common/widget.d.ts +62 -0
- package/dist/common/widget.js +209 -0
- package/dist/common/widget.js.map +1 -0
- package/dist/components/animated.d.ts +7 -4
- package/dist/components/animated.js +88 -67
- package/dist/components/animated.js.map +1 -1
- package/dist/components/app-registry.d.ts +36 -1
- package/dist/components/app-registry.js +8 -3
- package/dist/components/app-registry.js.map +1 -1
- package/dist/components/host-node.d.ts +7 -0
- package/dist/components/host-node.js +26 -1
- package/dist/components/host-node.js.map +1 -1
- package/dist/components/index.d.ts +6 -2
- package/dist/components/index.js +2 -1
- package/dist/components/index.js.map +1 -1
- package/dist/components/measure.d.ts +13 -0
- package/dist/components/measure.js +51 -0
- package/dist/components/measure.js.map +1 -0
- package/dist/components/modal.d.ts +1 -1
- package/dist/components/press-event.d.ts +28 -0
- package/dist/components/press-event.js +46 -0
- package/dist/components/press-event.js.map +1 -0
- package/dist/components/pressable.d.ts +2 -6
- package/dist/components/pressable.js +119 -20
- package/dist/components/pressable.js.map +1 -1
- package/dist/components/rect-store.d.ts +11 -1
- package/dist/components/rect-store.js +27 -3
- package/dist/components/rect-store.js.map +1 -1
- package/dist/components/scroll-view.d.ts +4 -0
- package/dist/components/scroll-view.js +8 -2
- package/dist/components/scroll-view.js.map +1 -1
- package/dist/components/svg/animated-support.d.ts +19 -0
- package/dist/components/svg/animated-support.js +64 -0
- package/dist/components/svg/animated-support.js.map +1 -0
- package/dist/components/svg/context.d.ts +8 -0
- package/dist/components/svg/context.js +15 -0
- package/dist/components/svg/context.js.map +1 -0
- package/dist/components/svg/gradients.d.ts +31 -0
- package/dist/components/svg/gradients.js +93 -0
- package/dist/components/svg/gradients.js.map +1 -0
- package/dist/components/svg/group.d.ts +8 -0
- package/dist/components/svg/group.js +30 -0
- package/dist/components/svg/group.js.map +1 -0
- package/dist/components/svg/index.d.ts +6 -0
- package/dist/components/svg/index.js +5 -0
- package/dist/components/svg/index.js.map +1 -0
- package/dist/components/svg/node.d.ts +7 -0
- package/dist/components/svg/node.js +7 -0
- package/dist/components/svg/node.js.map +1 -0
- package/dist/components/svg/paint.d.ts +24 -0
- package/dist/components/svg/paint.js +57 -0
- package/dist/components/svg/paint.js.map +1 -0
- package/dist/components/svg/shapes.d.ts +47 -0
- package/dist/components/svg/shapes.js +51 -0
- package/dist/components/svg/shapes.js.map +1 -0
- package/dist/components/svg/svg.d.ts +14 -0
- package/dist/components/svg/svg.js +48 -0
- package/dist/components/svg/svg.js.map +1 -0
- package/dist/components/svg/use-shape-node.d.ts +12 -0
- package/dist/components/svg/use-shape-node.js +52 -0
- package/dist/components/svg/use-shape-node.js.map +1 -0
- package/dist/components/text-input.js +1 -1
- package/dist/components/text-input.js.map +1 -1
- package/dist/components/text.js +36 -3
- package/dist/components/text.js.map +1 -1
- package/dist/components/use-layout-child.js +50 -4
- package/dist/components/use-layout-child.js.map +1 -1
- package/dist/components/view.d.ts +9 -4
- package/dist/components/view.js +8 -2
- package/dist/components/view.js.map +1 -1
- package/dist/contracts.d.ts +31 -4
- package/dist/contracts.js.map +1 -1
- package/dist/gtk/index.d.ts +7 -0
- package/dist/gtk/index.js +46 -0
- package/dist/gtk/index.js.map +1 -0
- package/dist/gtk/widgets.generated.d.ts +103 -0
- package/dist/gtk/widgets.generated.js +205 -0
- package/dist/gtk/widgets.generated.js.map +1 -0
- package/dist/gtk/window-actions.d.ts +77 -0
- package/dist/gtk/window-actions.js +193 -0
- package/dist/gtk/window-actions.js.map +1 -0
- package/dist/gtkx/bridge/geometry.d.ts +15 -0
- package/dist/gtkx/bridge/geometry.js +38 -0
- package/dist/gtkx/bridge/geometry.js.map +1 -0
- package/dist/gtkx/bridge/index.d.ts +11 -4
- package/dist/gtkx/bridge/index.js +47 -6
- package/dist/gtkx/bridge/index.js.map +1 -1
- package/dist/gtkx/bridge/layout-manager.d.ts +2 -1
- package/dist/gtkx/bridge/layout-manager.js +31 -9
- package/dist/gtkx/bridge/layout-manager.js.map +1 -1
- package/dist/gtkx/bridge/slot-portal.d.ts +12 -0
- package/dist/gtkx/bridge/slot-portal.js +17 -0
- package/dist/gtkx/bridge/slot-portal.js.map +1 -0
- package/dist/gtkx/bridge/svg-node.d.ts +79 -0
- package/dist/gtkx/bridge/svg-node.js +323 -0
- package/dist/gtkx/bridge/svg-node.js.map +1 -0
- package/dist/gtkx/bridge/use-signal.js +19 -9
- package/dist/gtkx/bridge/use-signal.js.map +1 -1
- package/dist/gtkx/bridge/widgets.generated.d.ts +2 -0
- package/dist/gtkx/bridge/widgets.generated.js +14 -0
- package/dist/gtkx/bridge/widgets.generated.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/bin.d.ts +2 -0
- package/dist/mcp/bin.js +17 -0
- package/dist/mcp/bin.js.map +1 -0
- package/dist/mcp/data/generated.d.ts +1304 -0
- package/dist/mcp/data/generated.js +1179 -0
- package/dist/mcp/data/generated.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/list.d.ts +4 -0
- package/dist/mcp/list.js +56 -0
- package/dist/mcp/list.js.map +1 -0
- package/dist/mcp/resolve.d.ts +55 -0
- package/dist/mcp/resolve.js +151 -0
- package/dist/mcp/resolve.js.map +1 -0
- package/dist/mcp/search.d.ts +13 -0
- package/dist/mcp/search.js +48 -0
- package/dist/mcp/search.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +135 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tool.d.ts +21 -0
- package/dist/mcp/tool.js +36 -0
- package/dist/mcp/tool.js.map +1 -0
- package/dist/metro/index.js +10 -0
- package/dist/metro/index.js.map +1 -1
- package/dist/navigation/index.d.ts +50 -13
- package/dist/navigation/index.js +183 -148
- package/dist/navigation/index.js.map +1 -1
- package/dist/navigation/option-warnings.js +9 -1
- package/dist/navigation/option-warnings.js.map +1 -1
- package/dist/navigation/sidebar.d.ts +207 -9
- package/dist/navigation/sidebar.js +294 -10
- package/dist/navigation/sidebar.js.map +1 -1
- package/dist/responder/system.d.ts +27 -0
- package/dist/responder/system.js +184 -0
- package/dist/responder/system.js.map +1 -0
- package/dist/responder/touch-history.d.ts +10 -0
- package/dist/responder/touch-history.js +78 -0
- package/dist/responder/touch-history.js.map +1 -0
- package/dist/responder/types.d.ts +63 -0
- package/dist/responder/types.js +29 -0
- package/dist/responder/types.js.map +1 -0
- package/dist/responder/use-responder.d.ts +12 -0
- package/dist/responder/use-responder.js +98 -0
- package/dist/responder/use-responder.js.map +1 -0
- package/dist/runner/host-dev.js +3 -1
- package/dist/runner/host-dev.js.map +1 -1
- package/dist/runner/host.js +5 -1
- package/dist/runner/host.js.map +1 -1
- package/dist/runner/index.d.ts +22 -2
- package/dist/runner/index.js +163 -13
- package/dist/runner/index.js.map +1 -1
- package/dist/sea/assemble.d.ts +29 -0
- package/dist/sea/assemble.js +129 -0
- package/dist/sea/assemble.js.map +1 -0
- package/dist/sea/bundle.d.ts +61 -0
- package/dist/sea/bundle.js +202 -0
- package/dist/sea/bundle.js.map +1 -0
- package/dist/sea/gtkx-config-module.d.ts +18 -0
- package/dist/sea/gtkx-config-module.js +67 -0
- package/dist/sea/gtkx-config-module.js.map +1 -0
- package/dist/sea/native-shim.d.ts +40 -0
- package/dist/sea/native-shim.js +121 -0
- package/dist/sea/native-shim.js.map +1 -0
- package/dist/sea/react-anchor-plugin.d.ts +2 -0
- package/dist/sea/react-anchor-plugin.js +30 -0
- package/dist/sea/react-anchor-plugin.js.map +1 -0
- package/dist/style/box-shadow.d.ts +7 -0
- package/dist/style/box-shadow.js +197 -0
- package/dist/style/box-shadow.js.map +1 -0
- package/dist/style/index.d.ts +3 -0
- package/dist/style/index.js +3 -0
- package/dist/style/index.js.map +1 -1
- package/dist/style/split-style.d.ts +5 -2
- package/dist/style/split-style.js +33 -5
- package/dist/style/split-style.js.map +1 -1
- package/dist/style/text-decoration.d.ts +15 -0
- package/dist/style/text-decoration.js +26 -0
- package/dist/style/text-decoration.js.map +1 -0
- package/dist/style/transform.d.ts +22 -0
- package/dist/style/transform.js +137 -0
- package/dist/style/transform.js.map +1 -0
- package/dist/style/visual-css.js +40 -3
- package/dist/style/visual-css.js.map +1 -1
- package/dist/svg/geometry.d.ts +19 -0
- package/dist/svg/geometry.js +80 -0
- package/dist/svg/geometry.js.map +1 -0
- package/dist/svg/gradient-geometry.d.ts +15 -0
- package/dist/svg/gradient-geometry.js +15 -0
- package/dist/svg/gradient-geometry.js.map +1 -0
- package/dist/svg/transform.d.ts +22 -0
- package/dist/svg/transform.js +74 -0
- package/dist/svg/transform.js.map +1 -0
- package/dist/svg/view-box.d.ts +34 -0
- package/dist/svg/view-box.js +108 -0
- package/dist/svg/view-box.js.map +1 -0
- package/dist/svg-compat/index.d.ts +1 -0
- package/dist/svg-compat/index.js +16 -0
- package/dist/svg-compat/index.js.map +1 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +14 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/vendor/react-native/pan-responder.d.ts +102 -0
- package/dist/vendor/react-native/pan-responder.js +196 -0
- package/dist/vendor/react-native/pan-responder.js.map +1 -0
- package/dist/vendor/react-native/touch-history-math.d.ts +18 -0
- package/dist/vendor/react-native/touch-history-math.js +102 -0
- package/dist/vendor/react-native/touch-history-math.js.map +1 -0
- package/dist/vite/index.d.ts +7 -3
- package/dist/vite/index.js +16 -4
- package/dist/vite/index.js.map +1 -1
- package/dist/vitest/index.d.ts +52 -0
- package/dist/vitest/index.js +58 -0
- package/dist/vitest/index.js.map +1 -0
- package/dist/vitest/setup.d.ts +4 -0
- package/dist/vitest/setup.js +22 -0
- package/dist/vitest/setup.js.map +1 -0
- package/package.json +52 -13
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { buildTools, createMcpServer, main, SERVER_NAME } from "./server.js";
|
|
2
|
+
export { formatEntity, resolveComponent, type Candidate, type Entity, type ResolveResult, } from "./resolve.js";
|
|
3
|
+
export { listSurface, SURFACE_AREAS, type SurfaceArea } from "./list.js";
|
|
4
|
+
export { searchDocs, type SearchResult } from "./search.js";
|
|
5
|
+
export type { Tool, ToolArgs } from "./tool.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// react-native-gtkx/mcp — programmatic access to the same server the
|
|
2
|
+
// react-native-gtkx-mcp bin runs, for embedding or testing (see
|
|
3
|
+
// tests/unit/mcp/). The bin (src/mcp/bin.ts) is the primary way this is
|
|
4
|
+
// consumed; this barrel exists for everything else.
|
|
5
|
+
export { buildTools, createMcpServer, main, SERVER_NAME } from "./server.js";
|
|
6
|
+
export { formatEntity, resolveComponent, } from "./resolve.js";
|
|
7
|
+
export { listSurface, SURFACE_AREAS } from "./list.js";
|
|
8
|
+
export { searchDocs } from "./search.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,gEAAgE;AAChE,wEAAwE;AACxE,oDAAoD;AACpD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC5E,OAAO,EACL,YAAY,EACZ,gBAAgB,GAIjB,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAoB,MAAM,WAAW,CAAA;AACxE,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAA","sourcesContent":["// react-native-gtkx/mcp — programmatic access to the same server the\n// react-native-gtkx-mcp bin runs, for embedding or testing (see\n// tests/unit/mcp/). The bin (src/mcp/bin.ts) is the primary way this is\n// consumed; this barrel exists for everything else.\nexport { buildTools, createMcpServer, main, SERVER_NAME } from \"./server.js\"\nexport {\n formatEntity,\n resolveComponent,\n type Candidate,\n type Entity,\n type ResolveResult,\n} from \"./resolve.js\"\nexport { listSurface, SURFACE_AREAS, type SurfaceArea } from \"./list.js\"\nexport { searchDocs, type SearchResult } from \"./search.js\"\nexport type { Tool, ToolArgs } from \"./tool.js\"\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
declare const SURFACE_AREAS: readonly ["portable-components", "portable-apis", "gtk-widgets", "adw-widgets", "common"];
|
|
2
|
+
type SurfaceArea = (typeof SURFACE_AREAS)[number];
|
|
3
|
+
declare const listSurface: (area?: SurfaceArea) => string;
|
|
4
|
+
export { listSurface, SURFACE_AREAS, type SurfaceArea };
|
package/dist/mcp/list.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// rn_gtkx_list_surface: browse the surface without knowing an exact name
|
|
2
|
+
// first — an overview with counts, or the full name list for one area.
|
|
3
|
+
import { ADW_WIDGETS, COMMON_PRIMITIVES, GTK_WIDGETS, PORTABLE_APIS, PORTABLE_COMPONENTS, } from "./data/generated.js";
|
|
4
|
+
const SURFACE_AREAS = [
|
|
5
|
+
"portable-components",
|
|
6
|
+
"portable-apis",
|
|
7
|
+
"gtk-widgets",
|
|
8
|
+
"adw-widgets",
|
|
9
|
+
"common",
|
|
10
|
+
];
|
|
11
|
+
const formatWidgetArea = (subpath, widgets) => {
|
|
12
|
+
const wrapped = widgets.filter((w) => w.wrapped);
|
|
13
|
+
const raw = widgets.filter((w) => !w.wrapped);
|
|
14
|
+
const wrappedLines = wrapped.map((w) => `- ${w.name}`).join("\n");
|
|
15
|
+
const rawLines = raw
|
|
16
|
+
.map((w) => `- ${w.name} (${w.reason ?? "raw"})`)
|
|
17
|
+
.join("\n");
|
|
18
|
+
return (`${subpath} — ${wrapped.length} wrapped (take \`style\`/\`onLayout\`), ${raw.length} raw (exported exactly as gtkx binds them):\n\n` +
|
|
19
|
+
`Wrapped:\n${wrappedLines}\n\nRaw:\n${rawLines}`);
|
|
20
|
+
};
|
|
21
|
+
const overview = () => {
|
|
22
|
+
const gtkWrapped = GTK_WIDGETS.filter((w) => w.wrapped).length;
|
|
23
|
+
const gtkRaw = GTK_WIDGETS.length - gtkWrapped;
|
|
24
|
+
const adwWrapped = ADW_WIDGETS.filter((w) => w.wrapped).length;
|
|
25
|
+
const adwRaw = ADW_WIDGETS.length - adwWrapped;
|
|
26
|
+
return [
|
|
27
|
+
"react-native-gtkx surface overview",
|
|
28
|
+
"",
|
|
29
|
+
`portable-components (${PORTABLE_COMPONENTS.length}): exported from "react-native" — same import path as upstream RN (View, Text, FlatList, ...). Each has RN-compatible differences documented per name.`,
|
|
30
|
+
`portable-apis (${PORTABLE_APIS.length}): modules exported from "react-native" (StyleSheet, Platform, AppRegistry, ...).`,
|
|
31
|
+
`gtk-widgets (${gtkWrapped} wrapped + ${gtkRaw} raw): react-native-gtkx/gtk — every GTK4 widget gtkx binds.`,
|
|
32
|
+
`adw-widgets (${adwWrapped} wrapped + ${adwRaw} raw): react-native-gtkx/adw — every libadwaita widget gtkx binds.`,
|
|
33
|
+
`common (${COMMON_PRIMITIVES.length}): react-native-gtkx/common — NavigationStack, NavigationStackPage, SlotContent, IntrinsicContent. (Widget, wrapReactNative and useWidgetLayout also live here but are prose-only in the docs — use rn_gtkx_search_docs for those.)`,
|
|
34
|
+
"",
|
|
35
|
+
"Call again with `area` to list names, or rn_gtkx_describe_component with a specific name for full details.",
|
|
36
|
+
].join("\n");
|
|
37
|
+
};
|
|
38
|
+
const listSurface = (area) => {
|
|
39
|
+
if (area === undefined) {
|
|
40
|
+
return overview();
|
|
41
|
+
}
|
|
42
|
+
switch (area) {
|
|
43
|
+
case "portable-components":
|
|
44
|
+
return PORTABLE_COMPONENTS.map((c) => `- ${c.name} (GTK: ${c.gtkImplementation})`).join("\n");
|
|
45
|
+
case "portable-apis":
|
|
46
|
+
return PORTABLE_APIS.map((a) => `- ${a.name}`).join("\n");
|
|
47
|
+
case "common":
|
|
48
|
+
return COMMON_PRIMITIVES.map((c) => `- ${c.name} — ${c.summary}`).join("\n");
|
|
49
|
+
case "gtk-widgets":
|
|
50
|
+
return formatWidgetArea("react-native-gtkx/gtk", GTK_WIDGETS);
|
|
51
|
+
case "adw-widgets":
|
|
52
|
+
return formatWidgetArea("react-native-gtkx/adw", ADW_WIDGETS);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
export { listSurface, SURFACE_AREAS };
|
|
56
|
+
//# sourceMappingURL=list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.js","sourceRoot":"","sources":["../../src/mcp/list.ts"],"names":[],"mappings":"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","sourcesContent":["// rn_gtkx_list_surface: browse the surface without knowing an exact name\n// first — an overview with counts, or the full name list for one area.\nimport {\n ADW_WIDGETS,\n COMMON_PRIMITIVES,\n GTK_WIDGETS,\n PORTABLE_APIS,\n PORTABLE_COMPONENTS,\n type WidgetRecord,\n} from \"./data/generated.js\"\n\nconst SURFACE_AREAS = [\n \"portable-components\",\n \"portable-apis\",\n \"gtk-widgets\",\n \"adw-widgets\",\n \"common\",\n] as const\n\ntype SurfaceArea = (typeof SURFACE_AREAS)[number]\n\nconst formatWidgetArea = (\n subpath: string,\n widgets: readonly WidgetRecord[],\n): string => {\n const wrapped = widgets.filter((w) => w.wrapped)\n const raw = widgets.filter((w) => !w.wrapped)\n const wrappedLines = wrapped.map((w) => `- ${w.name}`).join(\"\\n\")\n const rawLines = raw\n .map((w) => `- ${w.name} (${w.reason ?? \"raw\"})`)\n .join(\"\\n\")\n return (\n `${subpath} — ${wrapped.length} wrapped (take \\`style\\`/\\`onLayout\\`), ${raw.length} raw (exported exactly as gtkx binds them):\\n\\n` +\n `Wrapped:\\n${wrappedLines}\\n\\nRaw:\\n${rawLines}`\n )\n}\n\nconst overview = (): string => {\n const gtkWrapped = GTK_WIDGETS.filter((w) => w.wrapped).length\n const gtkRaw = GTK_WIDGETS.length - gtkWrapped\n const adwWrapped = ADW_WIDGETS.filter((w) => w.wrapped).length\n const adwRaw = ADW_WIDGETS.length - adwWrapped\n\n return [\n \"react-native-gtkx surface overview\",\n \"\",\n `portable-components (${PORTABLE_COMPONENTS.length}): exported from \"react-native\" — same import path as upstream RN (View, Text, FlatList, ...). Each has RN-compatible differences documented per name.`,\n `portable-apis (${PORTABLE_APIS.length}): modules exported from \"react-native\" (StyleSheet, Platform, AppRegistry, ...).`,\n `gtk-widgets (${gtkWrapped} wrapped + ${gtkRaw} raw): react-native-gtkx/gtk — every GTK4 widget gtkx binds.`,\n `adw-widgets (${adwWrapped} wrapped + ${adwRaw} raw): react-native-gtkx/adw — every libadwaita widget gtkx binds.`,\n `common (${COMMON_PRIMITIVES.length}): react-native-gtkx/common — NavigationStack, NavigationStackPage, SlotContent, IntrinsicContent. (Widget, wrapReactNative and useWidgetLayout also live here but are prose-only in the docs — use rn_gtkx_search_docs for those.)`,\n \"\",\n \"Call again with `area` to list names, or rn_gtkx_describe_component with a specific name for full details.\",\n ].join(\"\\n\")\n}\n\nconst listSurface = (area?: SurfaceArea): string => {\n if (area === undefined) {\n return overview()\n }\n switch (area) {\n case \"portable-components\":\n return PORTABLE_COMPONENTS.map(\n (c) => `- ${c.name} (GTK: ${c.gtkImplementation})`,\n ).join(\"\\n\")\n case \"portable-apis\":\n return PORTABLE_APIS.map((a) => `- ${a.name}`).join(\"\\n\")\n case \"common\":\n return COMMON_PRIMITIVES.map((c) => `- ${c.name} — ${c.summary}`).join(\n \"\\n\",\n )\n case \"gtk-widgets\":\n return formatWidgetArea(\"react-native-gtkx/gtk\", GTK_WIDGETS)\n case \"adw-widgets\":\n return formatWidgetArea(\"react-native-gtkx/adw\", ADW_WIDGETS)\n }\n}\n\nexport { listSurface, SURFACE_AREAS, type SurfaceArea }\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type CommonRecord, type PortableRecord, type WidgetRecord } from "./data/generated.js";
|
|
2
|
+
type Entity = {
|
|
3
|
+
kind: "portable-component";
|
|
4
|
+
record: PortableRecord;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "portable-api";
|
|
7
|
+
record: PortableRecord;
|
|
8
|
+
} | {
|
|
9
|
+
kind: "common";
|
|
10
|
+
record: CommonRecord;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "gtk-widget";
|
|
13
|
+
record: WidgetRecord;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "adw-widget";
|
|
16
|
+
record: WidgetRecord;
|
|
17
|
+
};
|
|
18
|
+
type Candidate = {
|
|
19
|
+
name: string;
|
|
20
|
+
subpath: string;
|
|
21
|
+
kind: Entity["kind"];
|
|
22
|
+
};
|
|
23
|
+
type ResolvedEntity = {
|
|
24
|
+
status: "resolved";
|
|
25
|
+
entity: Entity;
|
|
26
|
+
matchedBy: "exact" | "case-insensitive" | "prefix" | "substring";
|
|
27
|
+
};
|
|
28
|
+
type AmbiguousResult = {
|
|
29
|
+
status: "ambiguous";
|
|
30
|
+
query: string;
|
|
31
|
+
candidates: readonly Candidate[];
|
|
32
|
+
};
|
|
33
|
+
type NotFoundResult = {
|
|
34
|
+
status: "not-found";
|
|
35
|
+
query: string;
|
|
36
|
+
suggestions: readonly Candidate[];
|
|
37
|
+
};
|
|
38
|
+
type ResolveResult = ResolvedEntity | AmbiguousResult | NotFoundResult;
|
|
39
|
+
/**
|
|
40
|
+
* Resolves a free-form name to exactly one surface entity:
|
|
41
|
+
* 1. exact match, then case-insensitive exact match;
|
|
42
|
+
* 2. Gtk<name>/Adw<name> exact match — tried BEFORE substring search, so
|
|
43
|
+
* "Popover" resolves cleanly to GtkPopover instead of landing ambiguous
|
|
44
|
+
* against GtkPopoverBin/GtkPopoverMenu/GtkPopoverMenuBar too;
|
|
45
|
+
* 3. substring search (case-insensitive): one hit resolves, several are
|
|
46
|
+
* reported as ambiguous candidates (never guessed), zero fall through
|
|
47
|
+
* to a not-found response with the closest known names by edit
|
|
48
|
+
* distance.
|
|
49
|
+
*/
|
|
50
|
+
declare const resolveComponent: (query: string) => ResolveResult;
|
|
51
|
+
/** Formats a resolved entity into the plain object the tool returns as
|
|
52
|
+
* JSON text — one shape per kind, matching what docs/api.md and
|
|
53
|
+
* docs/platform-layer.md actually document for it. */
|
|
54
|
+
declare const formatEntity: (entity: Entity) => Record<string, unknown>;
|
|
55
|
+
export { formatEntity, resolveComponent, type Candidate, type Entity, type ResolveResult, };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Name resolution for rn_gtkx_describe_component: turns a free-form name
|
|
2
|
+
// into exactly one of the surface's entities, or says clearly why it
|
|
3
|
+
// could not (ambiguous vs. not-found) instead of guessing.
|
|
4
|
+
import { ADW_WIDGETS, COMMON_PRIMITIVES, GTK_WIDGETS, PORTABLE_APIS, PORTABLE_COMPONENTS, } from "./data/generated.js";
|
|
5
|
+
const ALL_ENTITIES = [
|
|
6
|
+
...PORTABLE_COMPONENTS.map((record) => ({
|
|
7
|
+
kind: "portable-component",
|
|
8
|
+
record,
|
|
9
|
+
})),
|
|
10
|
+
...PORTABLE_APIS.map((record) => ({ kind: "portable-api", record })),
|
|
11
|
+
...COMMON_PRIMITIVES.map((record) => ({ kind: "common", record })),
|
|
12
|
+
...GTK_WIDGETS.map((record) => ({ kind: "gtk-widget", record })),
|
|
13
|
+
...ADW_WIDGETS.map((record) => ({ kind: "adw-widget", record })),
|
|
14
|
+
];
|
|
15
|
+
const candidateOf = (entity) => ({
|
|
16
|
+
name: entity.record.name,
|
|
17
|
+
subpath: entity.record.subpath,
|
|
18
|
+
kind: entity.kind,
|
|
19
|
+
});
|
|
20
|
+
// Unweighted Levenshtein distance — 200-odd short names, no need for
|
|
21
|
+
// anything fancier to rank "closest known name" suggestions.
|
|
22
|
+
const levenshtein = (a, b) => {
|
|
23
|
+
const rows = a.length + 1;
|
|
24
|
+
const cols = b.length + 1;
|
|
25
|
+
const dp = Array.from({ length: rows }, () => new Array(cols).fill(0));
|
|
26
|
+
for (let i = 0; i < rows; i++) {
|
|
27
|
+
dp[i][0] = i;
|
|
28
|
+
}
|
|
29
|
+
for (let j = 0; j < cols; j++) {
|
|
30
|
+
dp[0][j] = j;
|
|
31
|
+
}
|
|
32
|
+
for (let i = 1; i < rows; i++) {
|
|
33
|
+
for (let j = 1; j < cols; j++) {
|
|
34
|
+
const cost = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
35
|
+
dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return dp[rows - 1][cols - 1];
|
|
39
|
+
};
|
|
40
|
+
const MAX_CANDIDATES = 20;
|
|
41
|
+
const MAX_SUGGESTIONS = 5;
|
|
42
|
+
const PREFIXES = ["Gtk", "Adw"];
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a free-form name to exactly one surface entity:
|
|
45
|
+
* 1. exact match, then case-insensitive exact match;
|
|
46
|
+
* 2. Gtk<name>/Adw<name> exact match — tried BEFORE substring search, so
|
|
47
|
+
* "Popover" resolves cleanly to GtkPopover instead of landing ambiguous
|
|
48
|
+
* against GtkPopoverBin/GtkPopoverMenu/GtkPopoverMenuBar too;
|
|
49
|
+
* 3. substring search (case-insensitive): one hit resolves, several are
|
|
50
|
+
* reported as ambiguous candidates (never guessed), zero fall through
|
|
51
|
+
* to a not-found response with the closest known names by edit
|
|
52
|
+
* distance.
|
|
53
|
+
*/
|
|
54
|
+
const resolveComponent = (query) => {
|
|
55
|
+
const trimmed = query.trim();
|
|
56
|
+
const lower = trimmed.toLowerCase();
|
|
57
|
+
if (lower.length === 0) {
|
|
58
|
+
return { status: "not-found", query: trimmed, suggestions: [] };
|
|
59
|
+
}
|
|
60
|
+
const exact = ALL_ENTITIES.find((e) => e.record.name === trimmed);
|
|
61
|
+
if (exact) {
|
|
62
|
+
return { status: "resolved", entity: exact, matchedBy: "exact" };
|
|
63
|
+
}
|
|
64
|
+
const caseInsensitive = ALL_ENTITIES.find((e) => e.record.name.toLowerCase() === lower);
|
|
65
|
+
if (caseInsensitive) {
|
|
66
|
+
return {
|
|
67
|
+
status: "resolved",
|
|
68
|
+
entity: caseInsensitive,
|
|
69
|
+
matchedBy: "case-insensitive",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (!/^(gtk|adw)/i.test(trimmed)) {
|
|
73
|
+
for (const prefix of PREFIXES) {
|
|
74
|
+
const prefixedLower = `${prefix}${trimmed}`.toLowerCase();
|
|
75
|
+
const match = ALL_ENTITIES.find((e) => e.record.name.toLowerCase() === prefixedLower);
|
|
76
|
+
if (match) {
|
|
77
|
+
return { status: "resolved", entity: match, matchedBy: "prefix" };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const substringMatches = ALL_ENTITIES.filter((e) => e.record.name.toLowerCase().includes(lower));
|
|
82
|
+
if (substringMatches.length === 1) {
|
|
83
|
+
return {
|
|
84
|
+
status: "resolved",
|
|
85
|
+
entity: substringMatches[0],
|
|
86
|
+
matchedBy: "substring",
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (substringMatches.length > 1) {
|
|
90
|
+
return {
|
|
91
|
+
status: "ambiguous",
|
|
92
|
+
query: trimmed,
|
|
93
|
+
candidates: substringMatches.slice(0, MAX_CANDIDATES).map(candidateOf),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const suggestions = ALL_ENTITIES.map((entity) => ({
|
|
97
|
+
entity,
|
|
98
|
+
distance: levenshtein(lower, entity.record.name.toLowerCase()),
|
|
99
|
+
}))
|
|
100
|
+
.sort((a, b) => a.distance - b.distance)
|
|
101
|
+
.slice(0, MAX_SUGGESTIONS)
|
|
102
|
+
.map(({ entity }) => candidateOf(entity));
|
|
103
|
+
return { status: "not-found", query: trimmed, suggestions };
|
|
104
|
+
};
|
|
105
|
+
/** Formats a resolved entity into the plain object the tool returns as
|
|
106
|
+
* JSON text — one shape per kind, matching what docs/api.md and
|
|
107
|
+
* docs/platform-layer.md actually document for it. */
|
|
108
|
+
const formatEntity = (entity) => {
|
|
109
|
+
switch (entity.kind) {
|
|
110
|
+
case "portable-component":
|
|
111
|
+
case "portable-api": {
|
|
112
|
+
const record = entity.record;
|
|
113
|
+
return {
|
|
114
|
+
kind: entity.kind,
|
|
115
|
+
name: record.name,
|
|
116
|
+
subpath: record.subpath,
|
|
117
|
+
...(record.gtkImplementation !== undefined
|
|
118
|
+
? { gtkImplementation: record.gtkImplementation }
|
|
119
|
+
: {}),
|
|
120
|
+
supported: record.supported,
|
|
121
|
+
differences: record.differences,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
case "common": {
|
|
125
|
+
const record = entity.record;
|
|
126
|
+
return {
|
|
127
|
+
kind: entity.kind,
|
|
128
|
+
name: record.name,
|
|
129
|
+
subpath: record.subpath,
|
|
130
|
+
summary: record.summary,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
case "gtk-widget":
|
|
134
|
+
case "adw-widget": {
|
|
135
|
+
const record = entity.record;
|
|
136
|
+
const note = record.wrapped
|
|
137
|
+
? "Takes `style` + `onLayout`; React Native layout positions and styles it. Outside RN layout (e.g. inside an AdwHeaderBar slot) it steps aside and renders as the bare widget."
|
|
138
|
+
: `Raw export — exactly as gtkx binds it, no \`style\`/\`onLayout\` (wrap it yourself with wrapReactNative from react-native-gtkx/gtk if you need RN layout to drive it).${record.reason ? ` Why raw: ${record.reason}` : ""}`;
|
|
139
|
+
return {
|
|
140
|
+
kind: entity.kind,
|
|
141
|
+
name: record.name,
|
|
142
|
+
subpath: record.subpath,
|
|
143
|
+
wrapped: record.wrapped,
|
|
144
|
+
...(record.reason !== undefined ? { reason: record.reason } : {}),
|
|
145
|
+
note,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
export { formatEntity, resolveComponent, };
|
|
151
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/mcp/resolve.ts"],"names":[],"mappings":"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","sourcesContent":["// Name resolution for rn_gtkx_describe_component: turns a free-form name\n// into exactly one of the surface's entities, or says clearly why it\n// could not (ambiguous vs. not-found) instead of guessing.\nimport {\n ADW_WIDGETS,\n COMMON_PRIMITIVES,\n GTK_WIDGETS,\n PORTABLE_APIS,\n PORTABLE_COMPONENTS,\n type CommonRecord,\n type PortableRecord,\n type WidgetRecord,\n} from \"./data/generated.js\"\n\ntype Entity =\n | { kind: \"portable-component\"; record: PortableRecord }\n | { kind: \"portable-api\"; record: PortableRecord }\n | { kind: \"common\"; record: CommonRecord }\n | { kind: \"gtk-widget\"; record: WidgetRecord }\n | { kind: \"adw-widget\"; record: WidgetRecord }\n\ntype Candidate = { name: string; subpath: string; kind: Entity[\"kind\"] }\n\ntype ResolvedEntity = {\n status: \"resolved\"\n entity: Entity\n matchedBy: \"exact\" | \"case-insensitive\" | \"prefix\" | \"substring\"\n}\ntype AmbiguousResult = {\n status: \"ambiguous\"\n query: string\n candidates: readonly Candidate[]\n}\ntype NotFoundResult = {\n status: \"not-found\"\n query: string\n suggestions: readonly Candidate[]\n}\ntype ResolveResult = ResolvedEntity | AmbiguousResult | NotFoundResult\n\nconst ALL_ENTITIES: readonly Entity[] = [\n ...PORTABLE_COMPONENTS.map((record): Entity => ({\n kind: \"portable-component\",\n record,\n })),\n ...PORTABLE_APIS.map((record): Entity => ({ kind: \"portable-api\", record })),\n ...COMMON_PRIMITIVES.map((record): Entity => ({ kind: \"common\", record })),\n ...GTK_WIDGETS.map((record): Entity => ({ kind: \"gtk-widget\", record })),\n ...ADW_WIDGETS.map((record): Entity => ({ kind: \"adw-widget\", record })),\n]\n\nconst candidateOf = (entity: Entity): Candidate => ({\n name: entity.record.name,\n subpath: entity.record.subpath,\n kind: entity.kind,\n})\n\n// Unweighted Levenshtein distance — 200-odd short names, no need for\n// anything fancier to rank \"closest known name\" suggestions.\nconst levenshtein = (a: string, b: string): number => {\n const rows = a.length + 1\n const cols = b.length + 1\n const dp: number[][] = Array.from({ length: rows }, () =>\n new Array<number>(cols).fill(0),\n )\n for (let i = 0; i < rows; i++) {\n dp[i]![0] = i\n }\n for (let j = 0; j < cols; j++) {\n dp[0]![j] = j\n }\n for (let i = 1; i < rows; i++) {\n for (let j = 1; j < cols; j++) {\n const cost = a[i - 1] === b[j - 1] ? 0 : 1\n dp[i]![j] = Math.min(\n dp[i - 1]![j]! + 1,\n dp[i]![j - 1]! + 1,\n dp[i - 1]![j - 1]! + cost,\n )\n }\n }\n return dp[rows - 1]![cols - 1]!\n}\n\nconst MAX_CANDIDATES = 20\nconst MAX_SUGGESTIONS = 5\nconst PREFIXES = [\"Gtk\", \"Adw\"] as const\n\n/**\n * Resolves a free-form name to exactly one surface entity:\n * 1. exact match, then case-insensitive exact match;\n * 2. Gtk<name>/Adw<name> exact match — tried BEFORE substring search, so\n * \"Popover\" resolves cleanly to GtkPopover instead of landing ambiguous\n * against GtkPopoverBin/GtkPopoverMenu/GtkPopoverMenuBar too;\n * 3. substring search (case-insensitive): one hit resolves, several are\n * reported as ambiguous candidates (never guessed), zero fall through\n * to a not-found response with the closest known names by edit\n * distance.\n */\nconst resolveComponent = (query: string): ResolveResult => {\n const trimmed = query.trim()\n const lower = trimmed.toLowerCase()\n\n if (lower.length === 0) {\n return { status: \"not-found\", query: trimmed, suggestions: [] }\n }\n\n const exact = ALL_ENTITIES.find((e) => e.record.name === trimmed)\n if (exact) {\n return { status: \"resolved\", entity: exact, matchedBy: \"exact\" }\n }\n\n const caseInsensitive = ALL_ENTITIES.find(\n (e) => e.record.name.toLowerCase() === lower,\n )\n if (caseInsensitive) {\n return {\n status: \"resolved\",\n entity: caseInsensitive,\n matchedBy: \"case-insensitive\",\n }\n }\n\n if (!/^(gtk|adw)/i.test(trimmed)) {\n for (const prefix of PREFIXES) {\n const prefixedLower = `${prefix}${trimmed}`.toLowerCase()\n const match = ALL_ENTITIES.find(\n (e) => e.record.name.toLowerCase() === prefixedLower,\n )\n if (match) {\n return { status: \"resolved\", entity: match, matchedBy: \"prefix\" }\n }\n }\n }\n\n const substringMatches = ALL_ENTITIES.filter((e) =>\n e.record.name.toLowerCase().includes(lower),\n )\n if (substringMatches.length === 1) {\n return {\n status: \"resolved\",\n entity: substringMatches[0]!,\n matchedBy: \"substring\",\n }\n }\n if (substringMatches.length > 1) {\n return {\n status: \"ambiguous\",\n query: trimmed,\n candidates: substringMatches.slice(0, MAX_CANDIDATES).map(candidateOf),\n }\n }\n\n const suggestions = ALL_ENTITIES.map((entity) => ({\n entity,\n distance: levenshtein(lower, entity.record.name.toLowerCase()),\n }))\n .sort((a, b) => a.distance - b.distance)\n .slice(0, MAX_SUGGESTIONS)\n .map(({ entity }) => candidateOf(entity))\n\n return { status: \"not-found\", query: trimmed, suggestions }\n}\n\n/** Formats a resolved entity into the plain object the tool returns as\n * JSON text — one shape per kind, matching what docs/api.md and\n * docs/platform-layer.md actually document for it. */\nconst formatEntity = (entity: Entity): Record<string, unknown> => {\n switch (entity.kind) {\n case \"portable-component\":\n case \"portable-api\": {\n const record = entity.record\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n ...(record.gtkImplementation !== undefined\n ? { gtkImplementation: record.gtkImplementation }\n : {}),\n supported: record.supported,\n differences: record.differences,\n }\n }\n case \"common\": {\n const record = entity.record\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n summary: record.summary,\n }\n }\n case \"gtk-widget\":\n case \"adw-widget\": {\n const record = entity.record\n const note = record.wrapped\n ? \"Takes `style` + `onLayout`; React Native layout positions and styles it. Outside RN layout (e.g. inside an AdwHeaderBar slot) it steps aside and renders as the bare widget.\"\n : `Raw export — exactly as gtkx binds it, no \\`style\\`/\\`onLayout\\` (wrap it yourself with wrapReactNative from react-native-gtkx/gtk if you need RN layout to drive it).${record.reason ? ` Why raw: ${record.reason}` : \"\"}`\n return {\n kind: entity.kind,\n name: record.name,\n subpath: record.subpath,\n wrapped: record.wrapped,\n ...(record.reason !== undefined ? { reason: record.reason } : {}),\n note,\n }\n }\n }\n}\n\nexport {\n formatEntity,\n resolveComponent,\n type Candidate,\n type Entity,\n type ResolveResult,\n}\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type DocChunk } from "./data/generated.js";
|
|
2
|
+
type SearchResult = DocChunk & {
|
|
3
|
+
score: number;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Scores every doc chunk by how many times each query term occurs in it
|
|
7
|
+
* (body text: 1 point per occurrence; the same term appearing in the
|
|
8
|
+
* heading: +3, so a chunk that is literally ABOUT the query ranks above
|
|
9
|
+
* one that merely mentions it in passing). No stemming, no synonyms — this
|
|
10
|
+
* is deliberately the least clever tool: a fallback, not the star.
|
|
11
|
+
*/
|
|
12
|
+
declare const searchDocs: (query: string, limit: number) => readonly SearchResult[];
|
|
13
|
+
export { searchDocs, type SearchResult };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// rn_gtkx_search_docs: free-text fallback for anything the structured
|
|
2
|
+
// tools (list_surface, describe_component) do not cover by name — symptoms
|
|
3
|
+
// ("list blanks on scroll"), cross-cutting workarounds, prose-only
|
|
4
|
+
// sections (navigation options, the escape hatch, ...).
|
|
5
|
+
import { DOC_CHUNKS } from "./data/generated.js";
|
|
6
|
+
const tokenize = (text) => text.toLowerCase().match(/[a-z0-9]+/g) ?? [];
|
|
7
|
+
const countOccurrences = (haystack, needle) => {
|
|
8
|
+
if (needle.length === 0) {
|
|
9
|
+
return 0;
|
|
10
|
+
}
|
|
11
|
+
let count = 0;
|
|
12
|
+
let index = haystack.indexOf(needle);
|
|
13
|
+
while (index !== -1) {
|
|
14
|
+
count++;
|
|
15
|
+
index = haystack.indexOf(needle, index + needle.length);
|
|
16
|
+
}
|
|
17
|
+
return count;
|
|
18
|
+
};
|
|
19
|
+
const HEADING_MATCH_WEIGHT = 3;
|
|
20
|
+
/**
|
|
21
|
+
* Scores every doc chunk by how many times each query term occurs in it
|
|
22
|
+
* (body text: 1 point per occurrence; the same term appearing in the
|
|
23
|
+
* heading: +3, so a chunk that is literally ABOUT the query ranks above
|
|
24
|
+
* one that merely mentions it in passing). No stemming, no synonyms — this
|
|
25
|
+
* is deliberately the least clever tool: a fallback, not the star.
|
|
26
|
+
*/
|
|
27
|
+
const searchDocs = (query, limit) => {
|
|
28
|
+
const terms = tokenize(query);
|
|
29
|
+
if (terms.length === 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const scored = DOC_CHUNKS.map((chunk) => {
|
|
33
|
+
const headingLower = chunk.heading.toLowerCase();
|
|
34
|
+
const bodyLower = chunk.text.toLowerCase();
|
|
35
|
+
let score = 0;
|
|
36
|
+
for (const term of terms) {
|
|
37
|
+
score += countOccurrences(bodyLower, term);
|
|
38
|
+
score += countOccurrences(headingLower, term) * HEADING_MATCH_WEIGHT;
|
|
39
|
+
}
|
|
40
|
+
return { ...chunk, score };
|
|
41
|
+
});
|
|
42
|
+
return scored
|
|
43
|
+
.filter((chunk) => chunk.score > 0)
|
|
44
|
+
.sort((a, b) => b.score - a.score)
|
|
45
|
+
.slice(0, limit);
|
|
46
|
+
};
|
|
47
|
+
export { searchDocs };
|
|
48
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/mcp/search.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,2EAA2E;AAC3E,mEAAmE;AACnE,wDAAwD;AACxD,OAAO,EAAE,UAAU,EAAiB,MAAM,qBAAqB,CAAA;AAI/D,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAY,EAAE,CAC1C,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;AAE9C,MAAM,gBAAgB,GAAG,CAAC,QAAgB,EAAE,MAAc,EAAU,EAAE;IACpE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,CAAA;IACV,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,IAAI,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACpC,OAAO,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACpB,KAAK,EAAE,CAAA;QACP,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IACzD,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAE9B;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CAAC,KAAa,EAAE,KAAa,EAA2B,EAAE;IAC3E,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAA;IACX,CAAC;IAED,MAAM,MAAM,GAAmB,UAAU,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACtD,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;QAChD,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;QAC1C,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,KAAK,IAAI,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;YAC1C,KAAK,IAAI,gBAAgB,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,oBAAoB,CAAA;QACtE,CAAC;QACD,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAA;IAC5B,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM;SACV,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC;SAClC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;SACjC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;AACpB,CAAC,CAAA;AAED,OAAO,EAAE,UAAU,EAAqB,CAAA","sourcesContent":["// rn_gtkx_search_docs: free-text fallback for anything the structured\n// tools (list_surface, describe_component) do not cover by name — symptoms\n// (\"list blanks on scroll\"), cross-cutting workarounds, prose-only\n// sections (navigation options, the escape hatch, ...).\nimport { DOC_CHUNKS, type DocChunk } from \"./data/generated.js\"\n\ntype SearchResult = DocChunk & { score: number }\n\nconst tokenize = (text: string): string[] =>\n text.toLowerCase().match(/[a-z0-9]+/g) ?? []\n\nconst countOccurrences = (haystack: string, needle: string): number => {\n if (needle.length === 0) {\n return 0\n }\n let count = 0\n let index = haystack.indexOf(needle)\n while (index !== -1) {\n count++\n index = haystack.indexOf(needle, index + needle.length)\n }\n return count\n}\n\nconst HEADING_MATCH_WEIGHT = 3\n\n/**\n * Scores every doc chunk by how many times each query term occurs in it\n * (body text: 1 point per occurrence; the same term appearing in the\n * heading: +3, so a chunk that is literally ABOUT the query ranks above\n * one that merely mentions it in passing). No stemming, no synonyms — this\n * is deliberately the least clever tool: a fallback, not the star.\n */\nconst searchDocs = (query: string, limit: number): readonly SearchResult[] => {\n const terms = tokenize(query)\n if (terms.length === 0) {\n return []\n }\n\n const scored: SearchResult[] = DOC_CHUNKS.map((chunk) => {\n const headingLower = chunk.heading.toLowerCase()\n const bodyLower = chunk.text.toLowerCase()\n let score = 0\n for (const term of terms) {\n score += countOccurrences(bodyLower, term)\n score += countOccurrences(headingLower, term) * HEADING_MATCH_WEIGHT\n }\n return { ...chunk, score }\n })\n\n return scored\n .filter((chunk) => chunk.score > 0)\n .sort((a, b) => b.score - a.score)\n .slice(0, limit)\n}\n\nexport { searchDocs, type SearchResult }\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type Tool } from "./tool.js";
|
|
3
|
+
declare const SERVER_NAME = "react-native-gtkx-mcp";
|
|
4
|
+
declare const buildTools: () => Tool[];
|
|
5
|
+
declare const createMcpServer: () => McpServer;
|
|
6
|
+
declare const main: () => Promise<void>;
|
|
7
|
+
export { buildTools, createMcpServer, main, SERVER_NAME };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// The MCP server itself: three tools over stdio, backed entirely by the
|
|
2
|
+
// generated data in ./data/generated.ts — no @gtkx/* import anywhere in
|
|
3
|
+
// this module or its dependents, so this runs on any OS without GTK
|
|
4
|
+
// installed (an agent reading a react-native-gtkx project on a Mac, for
|
|
5
|
+
// instance). See docs/getting-started.md for how a consumer registers it.
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
import { listSurface, SURFACE_AREAS } from "./list.js";
|
|
11
|
+
import { formatEntity, resolveComponent } from "./resolve.js";
|
|
12
|
+
import { searchDocs } from "./search.js";
|
|
13
|
+
import { defineTool, registerTool, textContent, textError, } from "./tool.js";
|
|
14
|
+
const require = createRequire(import.meta.url);
|
|
15
|
+
// Package root is two levels up from dist/mcp/server.js (dist/mcp -> dist
|
|
16
|
+
// -> package root). package.json ships in every npm tarball regardless of
|
|
17
|
+
// the "files" allowlist, so this needs no extra packaging step.
|
|
18
|
+
const { version } = require("../../package.json");
|
|
19
|
+
const SERVER_NAME = "react-native-gtkx-mcp";
|
|
20
|
+
const DEFAULT_SEARCH_LIMIT = 10;
|
|
21
|
+
const MAX_SEARCH_LIMIT = 20;
|
|
22
|
+
const listSurfaceShape = {
|
|
23
|
+
area: z
|
|
24
|
+
.enum(SURFACE_AREAS)
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Area to list. Omit for an overview with counts across all areas: " +
|
|
27
|
+
SURFACE_AREAS.join(", ") +
|
|
28
|
+
"."),
|
|
29
|
+
};
|
|
30
|
+
const describeComponentShape = {
|
|
31
|
+
name: z
|
|
32
|
+
.string()
|
|
33
|
+
.describe("A component/API/widget name: a portable react-native export (e.g. " +
|
|
34
|
+
'"FlatList"), a react-native-gtkx/gtk or /adw widget with or ' +
|
|
35
|
+
'without its Gtk/Adw prefix (e.g. "Popover" resolves to ' +
|
|
36
|
+
'"GtkPopover"), or a react-native-gtkx/common export (e.g. ' +
|
|
37
|
+
'"NavigationStack"). Case-insensitive.'),
|
|
38
|
+
};
|
|
39
|
+
const searchDocsShape = {
|
|
40
|
+
query: z
|
|
41
|
+
.string()
|
|
42
|
+
.describe("Free-text query — a symptom, a GTK term, an option name. Use this " +
|
|
43
|
+
"when rn_gtkx_describe_component does not have a name to look up, " +
|
|
44
|
+
'e.g. "known issues", "chrome content", "windowSize".'),
|
|
45
|
+
limit: z
|
|
46
|
+
.number()
|
|
47
|
+
.int()
|
|
48
|
+
.min(1)
|
|
49
|
+
.max(MAX_SEARCH_LIMIT)
|
|
50
|
+
.optional()
|
|
51
|
+
.describe(`Maximum number of results (default ${DEFAULT_SEARCH_LIMIT}, max ${MAX_SEARCH_LIMIT}).`),
|
|
52
|
+
};
|
|
53
|
+
const listSurfaceTool = () => defineTool({
|
|
54
|
+
name: "rn_gtkx_list_surface",
|
|
55
|
+
title: "List the react-native-gtkx surface",
|
|
56
|
+
description: "Browse the react-native-gtkx surface without knowing an exact name first: " +
|
|
57
|
+
"portable react-native components/APIs, the react-native-gtkx/gtk and " +
|
|
58
|
+
"/adw widget lists (wrapped vs. raw), and react-native-gtkx/common. " +
|
|
59
|
+
"Without `area`, returns counts and where to look next.",
|
|
60
|
+
inputSchema: listSurfaceShape,
|
|
61
|
+
handler: ({ area }) => textContent(listSurface(area)),
|
|
62
|
+
});
|
|
63
|
+
const describeComponentTool = () => defineTool({
|
|
64
|
+
name: "rn_gtkx_describe_component",
|
|
65
|
+
title: "Describe a react-native-gtkx component",
|
|
66
|
+
description: "Look up one component/widget/API by name: does it exist, which " +
|
|
67
|
+
"subpath it is exported from, what GTK widget backs it (for " +
|
|
68
|
+
"portable components), what differs from React Native, and whether " +
|
|
69
|
+
"a gtk/adw widget is wrapped (takes `style`/`onLayout`) or raw. The " +
|
|
70
|
+
"single most useful tool here — start with this before " +
|
|
71
|
+
"rn_gtkx_search_docs.",
|
|
72
|
+
inputSchema: describeComponentShape,
|
|
73
|
+
handler: ({ name }) => {
|
|
74
|
+
const result = resolveComponent(name);
|
|
75
|
+
if (result.status === "resolved") {
|
|
76
|
+
const body = {
|
|
77
|
+
matchedBy: result.matchedBy,
|
|
78
|
+
...formatEntity(result.entity),
|
|
79
|
+
};
|
|
80
|
+
return textContent(JSON.stringify(body, null, 2));
|
|
81
|
+
}
|
|
82
|
+
if (result.status === "ambiguous") {
|
|
83
|
+
const lines = result.candidates
|
|
84
|
+
.map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)
|
|
85
|
+
.join("\n");
|
|
86
|
+
return textError(`"${result.query}" matches ${result.candidates.length} names — call again with one of:\n${lines}`);
|
|
87
|
+
}
|
|
88
|
+
const lines = result.suggestions
|
|
89
|
+
.map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)
|
|
90
|
+
.join("\n");
|
|
91
|
+
return textError(`No component named "${result.query}".` +
|
|
92
|
+
(lines.length > 0
|
|
93
|
+
? ` Closest known names:\n${lines}`
|
|
94
|
+
: " rn_gtkx_list_surface has the full index."));
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
const searchDocsTool = () => defineTool({
|
|
98
|
+
name: "rn_gtkx_search_docs",
|
|
99
|
+
title: "Search react-native-gtkx docs",
|
|
100
|
+
description: "Free-text search over the docs (getting-started, the platform layer, " +
|
|
101
|
+
"gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it " +
|
|
102
|
+
"for symptoms and known-issue questions that rn_gtkx_describe_component " +
|
|
103
|
+
"cannot answer by name (workarounds are keyed by mechanism, not by " +
|
|
104
|
+
"component).",
|
|
105
|
+
inputSchema: searchDocsShape,
|
|
106
|
+
handler: ({ query, limit }) => {
|
|
107
|
+
const results = searchDocs(query, limit ?? DEFAULT_SEARCH_LIMIT);
|
|
108
|
+
if (results.length === 0) {
|
|
109
|
+
return textContent(`No matches for "${query}". Try rn_gtkx_list_surface or rn_gtkx_describe_component instead.`);
|
|
110
|
+
}
|
|
111
|
+
const body = results
|
|
112
|
+
.map((r) => `## ${r.doc} — ${r.heading}\n\n${r.text}`)
|
|
113
|
+
.join("\n\n---\n\n");
|
|
114
|
+
return textContent(body);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
const buildTools = () => [
|
|
118
|
+
listSurfaceTool(),
|
|
119
|
+
describeComponentTool(),
|
|
120
|
+
searchDocsTool(),
|
|
121
|
+
];
|
|
122
|
+
const createMcpServer = () => {
|
|
123
|
+
const server = new McpServer({ name: SERVER_NAME, version });
|
|
124
|
+
for (const tool of buildTools()) {
|
|
125
|
+
registerTool(server, tool);
|
|
126
|
+
}
|
|
127
|
+
return server;
|
|
128
|
+
};
|
|
129
|
+
const main = async () => {
|
|
130
|
+
const server = createMcpServer();
|
|
131
|
+
const transport = new StdioServerTransport();
|
|
132
|
+
await server.connect(transport);
|
|
133
|
+
};
|
|
134
|
+
export { buildTools, createMcpServer, main, SERVER_NAME };
|
|
135
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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","sourcesContent":["// The MCP server itself: three tools over stdio, backed entirely by the\n// generated data in ./data/generated.ts — no @gtkx/* import anywhere in\n// this module or its dependents, so this runs on any OS without GTK\n// installed (an agent reading a react-native-gtkx project on a Mac, for\n// instance). See docs/getting-started.md for how a consumer registers it.\nimport { createRequire } from \"node:module\"\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { listSurface, SURFACE_AREAS } from \"./list.js\"\nimport { formatEntity, resolveComponent } from \"./resolve.js\"\nimport { searchDocs } from \"./search.js\"\nimport {\n defineTool,\n registerTool,\n textContent,\n textError,\n type Tool,\n} from \"./tool.js\"\n\nconst require = createRequire(import.meta.url)\n// Package root is two levels up from dist/mcp/server.js (dist/mcp -> dist\n// -> package root). package.json ships in every npm tarball regardless of\n// the \"files\" allowlist, so this needs no extra packaging step.\nconst { version } = require(\"../../package.json\") as { version: string }\n\nconst SERVER_NAME = \"react-native-gtkx-mcp\"\nconst DEFAULT_SEARCH_LIMIT = 10\nconst MAX_SEARCH_LIMIT = 20\n\nconst listSurfaceShape = {\n area: z\n .enum(SURFACE_AREAS)\n .optional()\n .describe(\n \"Area to list. Omit for an overview with counts across all areas: \" +\n SURFACE_AREAS.join(\", \") +\n \".\",\n ),\n}\n\nconst describeComponentShape = {\n name: z\n .string()\n .describe(\n \"A component/API/widget name: a portable react-native export (e.g. \" +\n '\"FlatList\"), a react-native-gtkx/gtk or /adw widget with or ' +\n 'without its Gtk/Adw prefix (e.g. \"Popover\" resolves to ' +\n '\"GtkPopover\"), or a react-native-gtkx/common export (e.g. ' +\n '\"NavigationStack\"). Case-insensitive.',\n ),\n}\n\nconst searchDocsShape = {\n query: z\n .string()\n .describe(\n \"Free-text query — a symptom, a GTK term, an option name. Use this \" +\n \"when rn_gtkx_describe_component does not have a name to look up, \" +\n 'e.g. \"known issues\", \"chrome content\", \"windowSize\".',\n ),\n limit: z\n .number()\n .int()\n .min(1)\n .max(MAX_SEARCH_LIMIT)\n .optional()\n .describe(\n `Maximum number of results (default ${DEFAULT_SEARCH_LIMIT}, max ${MAX_SEARCH_LIMIT}).`,\n ),\n}\n\nconst listSurfaceTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_list_surface\",\n title: \"List the react-native-gtkx surface\",\n description:\n \"Browse the react-native-gtkx surface without knowing an exact name first: \" +\n \"portable react-native components/APIs, the react-native-gtkx/gtk and \" +\n \"/adw widget lists (wrapped vs. raw), and react-native-gtkx/common. \" +\n \"Without `area`, returns counts and where to look next.\",\n inputSchema: listSurfaceShape,\n handler: ({ area }) => textContent(listSurface(area)),\n })\n\nconst describeComponentTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_describe_component\",\n title: \"Describe a react-native-gtkx component\",\n description:\n \"Look up one component/widget/API by name: does it exist, which \" +\n \"subpath it is exported from, what GTK widget backs it (for \" +\n \"portable components), what differs from React Native, and whether \" +\n \"a gtk/adw widget is wrapped (takes `style`/`onLayout`) or raw. The \" +\n \"single most useful tool here — start with this before \" +\n \"rn_gtkx_search_docs.\",\n inputSchema: describeComponentShape,\n handler: ({ name }) => {\n const result = resolveComponent(name)\n if (result.status === \"resolved\") {\n const body = {\n matchedBy: result.matchedBy,\n ...formatEntity(result.entity),\n }\n return textContent(JSON.stringify(body, null, 2))\n }\n if (result.status === \"ambiguous\") {\n const lines = result.candidates\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `\"${result.query}\" matches ${result.candidates.length} names — call again with one of:\\n${lines}`,\n )\n }\n const lines = result.suggestions\n .map((c) => `- ${c.name} (${c.subpath}, ${c.kind})`)\n .join(\"\\n\")\n return textError(\n `No component named \"${result.query}\".` +\n (lines.length > 0\n ? ` Closest known names:\\n${lines}`\n : \" rn_gtkx_list_surface has the full index.\"),\n )\n },\n })\n\nconst searchDocsTool = (): Tool =>\n defineTool({\n name: \"rn_gtkx_search_docs\",\n title: \"Search react-native-gtkx docs\",\n description:\n \"Free-text search over the docs (getting-started, the platform layer, \" +\n \"gtkx rc.3 workarounds/quirks, navigation). The fallback tool: use it \" +\n \"for symptoms and known-issue questions that rn_gtkx_describe_component \" +\n \"cannot answer by name (workarounds are keyed by mechanism, not by \" +\n \"component).\",\n inputSchema: searchDocsShape,\n handler: ({ query, limit }) => {\n const results = searchDocs(query, limit ?? DEFAULT_SEARCH_LIMIT)\n if (results.length === 0) {\n return textContent(\n `No matches for \"${query}\". Try rn_gtkx_list_surface or rn_gtkx_describe_component instead.`,\n )\n }\n const body = results\n .map((r) => `## ${r.doc} — ${r.heading}\\n\\n${r.text}`)\n .join(\"\\n\\n---\\n\\n\")\n return textContent(body)\n },\n })\n\nconst buildTools = (): Tool[] => [\n listSurfaceTool(),\n describeComponentTool(),\n searchDocsTool(),\n]\n\nconst createMcpServer = (): McpServer => {\n const server = new McpServer({ name: SERVER_NAME, version })\n for (const tool of buildTools()) {\n registerTool(server, tool)\n }\n return server\n}\n\nconst main = async (): Promise<void> => {\n const server = createMcpServer()\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nexport { buildTools, createMcpServer, main, SERVER_NAME }\n"]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import type { z } from "zod";
|
|
4
|
+
type ToolArgs<Shape extends Record<string, z.ZodType>> = {
|
|
5
|
+
[K in keyof Shape]: z.output<Shape[K]>;
|
|
6
|
+
};
|
|
7
|
+
type Tool<Shape extends Record<string, z.ZodType> = Record<string, z.ZodType>> = {
|
|
8
|
+
name: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
inputSchema: Shape;
|
|
12
|
+
handler: (args: ToolArgs<Shape>) => CallToolResult;
|
|
13
|
+
};
|
|
14
|
+
declare const textContent: (text: string) => CallToolResult;
|
|
15
|
+
declare const textError: (text: string) => CallToolResult;
|
|
16
|
+
/** Identity function with a narrower input type — keeps tool definitions
|
|
17
|
+
* readable (named fields) while erasing the generic Shape at the call site,
|
|
18
|
+
* matching the pattern registerTool below expects. */
|
|
19
|
+
declare const defineTool: <Shape extends Record<string, z.ZodType>>(tool: Tool<Shape>) => Tool;
|
|
20
|
+
declare const registerTool: (server: McpServer, tool: Tool) => void;
|
|
21
|
+
export { defineTool, registerTool, textContent, textError, type Tool, type ToolArgs, };
|
package/dist/mcp/tool.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const textContent = (text) => ({
|
|
2
|
+
content: [{ type: "text", text }],
|
|
3
|
+
});
|
|
4
|
+
const textError = (text) => ({
|
|
5
|
+
content: [{ type: "text", text }],
|
|
6
|
+
isError: true,
|
|
7
|
+
});
|
|
8
|
+
const errorToResult = (error) => textError(error instanceof Error ? error.message : String(error));
|
|
9
|
+
const runTool = (handler, args) => {
|
|
10
|
+
try {
|
|
11
|
+
return handler(args);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
return errorToResult(error);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/** Identity function with a narrower input type — keeps tool definitions
|
|
18
|
+
* readable (named fields) while erasing the generic Shape at the call site,
|
|
19
|
+
* matching the pattern registerTool below expects. */
|
|
20
|
+
const defineTool = (tool) => tool;
|
|
21
|
+
const registerTool = (server, tool) => {
|
|
22
|
+
const callback = ((args) => runTool(tool.handler, args));
|
|
23
|
+
server.registerTool(tool.name, {
|
|
24
|
+
title: tool.title,
|
|
25
|
+
description: tool.description,
|
|
26
|
+
inputSchema: tool.inputSchema,
|
|
27
|
+
annotations: {
|
|
28
|
+
title: tool.title,
|
|
29
|
+
readOnlyHint: true,
|
|
30
|
+
destructiveHint: false,
|
|
31
|
+
openWorldHint: false,
|
|
32
|
+
},
|
|
33
|
+
}, callback);
|
|
34
|
+
};
|
|
35
|
+
export { defineTool, registerTool, textContent, textError, };
|
|
36
|
+
//# sourceMappingURL=tool.js.map
|