@frontmcp/react 0.0.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.
Files changed (140) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +263 -0
  3. package/ai/createToolHandler.d.ts +10 -0
  4. package/ai/createToolHandler.d.ts.map +1 -0
  5. package/ai/index.d.ts +7 -0
  6. package/ai/index.d.ts.map +1 -0
  7. package/ai/index.js +416 -0
  8. package/ai/types.d.ts +52 -0
  9. package/ai/types.d.ts.map +1 -0
  10. package/ai/useAITools.d.ts +17 -0
  11. package/ai/useAITools.d.ts.map +1 -0
  12. package/ai/useTools.d.ts +19 -0
  13. package/ai/useTools.d.ts.map +1 -0
  14. package/api/api.types.d.ts +61 -0
  15. package/api/api.types.d.ts.map +1 -0
  16. package/api/createFetchClient.d.ts +9 -0
  17. package/api/createFetchClient.d.ts.map +1 -0
  18. package/api/index.d.ts +12 -0
  19. package/api/index.d.ts.map +1 -0
  20. package/api/index.js +402 -0
  21. package/api/parseOpenApiSpec.d.ts +9 -0
  22. package/api/parseOpenApiSpec.d.ts.map +1 -0
  23. package/api/useApiClient.d.ts +9 -0
  24. package/api/useApiClient.d.ts.map +1 -0
  25. package/components/AgentContent.d.ts +30 -0
  26. package/components/AgentContent.d.ts.map +1 -0
  27. package/components/AgentSearch.d.ts +35 -0
  28. package/components/AgentSearch.d.ts.map +1 -0
  29. package/components/ComponentRegistry.d.ts +36 -0
  30. package/components/ComponentRegistry.d.ts.map +1 -0
  31. package/components/DomResources.d.ts +16 -0
  32. package/components/DomResources.d.ts.map +1 -0
  33. package/components/DynamicRenderer.d.ts +19 -0
  34. package/components/DynamicRenderer.d.ts.map +1 -0
  35. package/components/OutputDisplay.d.ts +11 -0
  36. package/components/OutputDisplay.d.ts.map +1 -0
  37. package/components/PromptForm.d.ts +13 -0
  38. package/components/PromptForm.d.ts.map +1 -0
  39. package/components/ResourceViewer.d.ts +18 -0
  40. package/components/ResourceViewer.d.ts.map +1 -0
  41. package/components/ToolForm.d.ts +16 -0
  42. package/components/ToolForm.d.ts.map +1 -0
  43. package/components/index.d.ts +21 -0
  44. package/components/index.d.ts.map +1 -0
  45. package/components/mcpComponent.d.ts +48 -0
  46. package/components/mcpComponent.d.ts.map +1 -0
  47. package/esm/ai/index.mjs +393 -0
  48. package/esm/api/index.mjs +379 -0
  49. package/esm/index.mjs +1814 -0
  50. package/esm/package.json +111 -0
  51. package/esm/router/index.mjs +157 -0
  52. package/esm/state/index.mjs +450 -0
  53. package/hooks/index.d.ts +21 -0
  54. package/hooks/index.d.ts.map +1 -0
  55. package/hooks/useCallTool.d.ts +9 -0
  56. package/hooks/useCallTool.d.ts.map +1 -0
  57. package/hooks/useComponentTree.d.ts +21 -0
  58. package/hooks/useComponentTree.d.ts.map +1 -0
  59. package/hooks/useDynamicResource.d.ts +20 -0
  60. package/hooks/useDynamicResource.d.ts.map +1 -0
  61. package/hooks/useDynamicTool.d.ts +39 -0
  62. package/hooks/useDynamicTool.d.ts.map +1 -0
  63. package/hooks/useFrontMcp.d.ts +8 -0
  64. package/hooks/useFrontMcp.d.ts.map +1 -0
  65. package/hooks/useGetPrompt.d.ts +13 -0
  66. package/hooks/useGetPrompt.d.ts.map +1 -0
  67. package/hooks/useListPrompts.d.ts +10 -0
  68. package/hooks/useListPrompts.d.ts.map +1 -0
  69. package/hooks/useListResources.d.ts +14 -0
  70. package/hooks/useListResources.d.ts.map +1 -0
  71. package/hooks/useListTools.d.ts +10 -0
  72. package/hooks/useListTools.d.ts.map +1 -0
  73. package/hooks/useReadResource.d.ts +23 -0
  74. package/hooks/useReadResource.d.ts.map +1 -0
  75. package/hooks/useResolvedServer.d.ts +16 -0
  76. package/hooks/useResolvedServer.d.ts.map +1 -0
  77. package/hooks/useServer.d.ts +17 -0
  78. package/hooks/useServer.d.ts.map +1 -0
  79. package/hooks/useStoreResource.d.ts +22 -0
  80. package/hooks/useStoreResource.d.ts.map +1 -0
  81. package/index.d.ts +33 -0
  82. package/index.d.ts.map +1 -0
  83. package/index.js +1821 -0
  84. package/package.json +111 -0
  85. package/provider/FrontMcpContext.d.ts +6 -0
  86. package/provider/FrontMcpContext.d.ts.map +1 -0
  87. package/provider/FrontMcpProvider.d.ts +34 -0
  88. package/provider/FrontMcpProvider.d.ts.map +1 -0
  89. package/provider/index.d.ts +4 -0
  90. package/provider/index.d.ts.map +1 -0
  91. package/registry/DynamicRegistry.d.ts +55 -0
  92. package/registry/DynamicRegistry.d.ts.map +1 -0
  93. package/registry/ServerRegistry.d.ts +43 -0
  94. package/registry/ServerRegistry.d.ts.map +1 -0
  95. package/registry/createWrappedServer.d.ts +14 -0
  96. package/registry/createWrappedServer.d.ts.map +1 -0
  97. package/registry/index.d.ts +5 -0
  98. package/registry/index.d.ts.map +1 -0
  99. package/router/current-route.resource.d.ts +11 -0
  100. package/router/current-route.resource.d.ts.map +1 -0
  101. package/router/go-back.tool.d.ts +18 -0
  102. package/router/go-back.tool.d.ts.map +1 -0
  103. package/router/index.d.ts +9 -0
  104. package/router/index.d.ts.map +1 -0
  105. package/router/index.js +180 -0
  106. package/router/navigate.tool.d.ts +35 -0
  107. package/router/navigate.tool.d.ts.map +1 -0
  108. package/router/router-bridge.d.ts +20 -0
  109. package/router/router-bridge.d.ts.map +1 -0
  110. package/router/router.entries.d.ts +23 -0
  111. package/router/router.entries.d.ts.map +1 -0
  112. package/router/useRouterBridge.d.ts +7 -0
  113. package/router/useRouterBridge.d.ts.map +1 -0
  114. package/state/adapters/createStore.d.ts +15 -0
  115. package/state/adapters/createStore.d.ts.map +1 -0
  116. package/state/adapters/index.d.ts +7 -0
  117. package/state/adapters/index.d.ts.map +1 -0
  118. package/state/adapters/reduxAdapter.d.ts +21 -0
  119. package/state/adapters/reduxAdapter.d.ts.map +1 -0
  120. package/state/adapters/valtioAdapter.d.ts +19 -0
  121. package/state/adapters/valtioAdapter.d.ts.map +1 -0
  122. package/state/index.d.ts +15 -0
  123. package/state/index.d.ts.map +1 -0
  124. package/state/index.js +473 -0
  125. package/state/state.types.d.ts +48 -0
  126. package/state/state.types.d.ts.map +1 -0
  127. package/state/useReduxResource.d.ts +8 -0
  128. package/state/useReduxResource.d.ts.map +1 -0
  129. package/state/useStoreRegistration.d.ts +14 -0
  130. package/state/useStoreRegistration.d.ts.map +1 -0
  131. package/state/useStoreResource.d.ts +10 -0
  132. package/state/useStoreResource.d.ts.map +1 -0
  133. package/state/useValtioResource.d.ts +9 -0
  134. package/state/useValtioResource.d.ts.map +1 -0
  135. package/types.d.ts +127 -0
  136. package/types.d.ts.map +1 -0
  137. package/utils/index.d.ts +2 -0
  138. package/utils/index.d.ts.map +1 -0
  139. package/utils/zodToJsonSchema.d.ts +9 -0
  140. package/utils/zodToJsonSchema.d.ts.map +1 -0
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@frontmcp/react",
3
+ "version": "1.0.0",
4
+ "description": "React hooks, components, and AI SDK integration for FrontMCP",
5
+ "author": "AgentFront <info@agentfront.dev>",
6
+ "homepage": "https://docs.agentfront.dev",
7
+ "license": "Apache-2.0",
8
+ "keywords": [
9
+ "mcp",
10
+ "react",
11
+ "hooks",
12
+ "ai",
13
+ "tools",
14
+ "openai",
15
+ "claude",
16
+ "vercel-ai",
17
+ "agentfront",
18
+ "frontmcp"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/agentfront/frontmcp.git"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/agentfront/frontmcp/issues"
26
+ },
27
+ "type": "module",
28
+ "main": "../index.js",
29
+ "module": "./index.mjs",
30
+ "types": "../index.d.ts",
31
+ "sideEffects": false,
32
+ "exports": {
33
+ "./package.json": "../package.json",
34
+ ".": {
35
+ "require": {
36
+ "types": "../index.d.ts",
37
+ "default": "../index.js"
38
+ },
39
+ "import": {
40
+ "types": "../index.d.ts",
41
+ "default": "./index.mjs"
42
+ }
43
+ },
44
+ "./ai": {
45
+ "require": {
46
+ "types": "../ai/index.d.ts",
47
+ "default": "../ai/index.js"
48
+ },
49
+ "import": {
50
+ "types": "../ai/index.d.ts",
51
+ "default": "./ai/index.mjs"
52
+ }
53
+ },
54
+ "./router": {
55
+ "require": {
56
+ "types": "../router/index.d.ts",
57
+ "default": "../router/index.js"
58
+ },
59
+ "import": {
60
+ "types": "../router/index.d.ts",
61
+ "default": "./router/index.mjs"
62
+ }
63
+ },
64
+ "./state": {
65
+ "require": {
66
+ "types": "../state/index.d.ts",
67
+ "default": "../state/index.js"
68
+ },
69
+ "import": {
70
+ "types": "../state/index.d.ts",
71
+ "default": "./state/index.mjs"
72
+ }
73
+ },
74
+ "./api": {
75
+ "require": {
76
+ "types": "../api/index.d.ts",
77
+ "default": "../api/index.js"
78
+ },
79
+ "import": {
80
+ "types": "../api/index.d.ts",
81
+ "default": "./api/index.mjs"
82
+ }
83
+ }
84
+ },
85
+ "engines": {
86
+ "node": ">=22.0.0"
87
+ },
88
+ "peerDependencies": {
89
+ "react": "^18.0.0 || ^19.0.0",
90
+ "react-dom": "^18.0.0 || ^19.0.0",
91
+ "@frontmcp/sdk": "1.0.0",
92
+ "@frontmcp/utils": "1.0.0",
93
+ "react-router-dom": "^7.0.0",
94
+ "zod": "^4.0.0"
95
+ },
96
+ "peerDependenciesMeta": {
97
+ "react-router-dom": {
98
+ "optional": true
99
+ },
100
+ "zod": {
101
+ "optional": true
102
+ }
103
+ },
104
+ "dependencies": {
105
+ "@frontmcp/sdk": "1.0.0",
106
+ "@frontmcp/utils": "1.0.0"
107
+ },
108
+ "devDependencies": {
109
+ "typescript": "^5.9.3"
110
+ }
111
+ }
@@ -0,0 +1,157 @@
1
+ // libs/react/src/router/useRouterBridge.ts
2
+ import { useEffect } from "react";
3
+ import { useNavigate, useLocation } from "react-router-dom";
4
+
5
+ // libs/react/src/router/router-bridge.ts
6
+ var currentNavigate = null;
7
+ var currentLocation = null;
8
+ function setNavigate(fn) {
9
+ currentNavigate = fn;
10
+ }
11
+ function setLocation(loc) {
12
+ currentLocation = loc;
13
+ }
14
+ function getNavigate() {
15
+ return currentNavigate;
16
+ }
17
+ function getLocation() {
18
+ return currentLocation;
19
+ }
20
+ function clearBridge() {
21
+ currentNavigate = null;
22
+ currentLocation = null;
23
+ }
24
+
25
+ // libs/react/src/router/useRouterBridge.ts
26
+ function useRouterBridge() {
27
+ const navigate = useNavigate();
28
+ const location = useLocation();
29
+ useEffect(() => {
30
+ setNavigate(navigate);
31
+ return () => {
32
+ clearBridge();
33
+ };
34
+ }, [navigate]);
35
+ useEffect(() => {
36
+ setLocation({
37
+ pathname: location.pathname,
38
+ search: location.search,
39
+ hash: location.hash
40
+ });
41
+ }, [location.pathname, location.search, location.hash]);
42
+ }
43
+
44
+ // libs/react/src/router/navigate.tool.ts
45
+ var NavigateTool = class {
46
+ static toolName = "navigate";
47
+ static description = "Navigate to a URL path in the application";
48
+ static inputSchema = {
49
+ type: "object",
50
+ properties: {
51
+ path: { type: "string", description: "The URL path to navigate to" },
52
+ replace: { type: "boolean", description: "Replace current history entry instead of pushing" }
53
+ },
54
+ required: ["path"]
55
+ };
56
+ static async execute(args) {
57
+ const navigate = getNavigate();
58
+ if (!navigate) {
59
+ return {
60
+ content: [
61
+ {
62
+ type: "text",
63
+ text: "Router bridge not connected. Ensure useRouterBridge() is called inside a React Router tree."
64
+ }
65
+ ]
66
+ };
67
+ }
68
+ navigate(args.path, { replace: args.replace });
69
+ return {
70
+ content: [{ type: "text", text: `Navigated to ${args.path}` }]
71
+ };
72
+ }
73
+ };
74
+
75
+ // libs/react/src/router/go-back.tool.ts
76
+ var GoBackTool = class {
77
+ static toolName = "go_back";
78
+ static description = "Go back to the previous page in browser history";
79
+ static inputSchema = {
80
+ type: "object",
81
+ properties: {}
82
+ };
83
+ static async execute() {
84
+ const navigate = getNavigate();
85
+ if (!navigate) {
86
+ return {
87
+ content: [
88
+ {
89
+ type: "text",
90
+ text: "Router bridge not connected. Ensure useRouterBridge() is called inside a React Router tree."
91
+ }
92
+ ]
93
+ };
94
+ }
95
+ navigate(-1);
96
+ return {
97
+ content: [{ type: "text", text: "Navigated back" }]
98
+ };
99
+ }
100
+ };
101
+
102
+ // libs/react/src/router/current-route.resource.ts
103
+ var CurrentRouteResource = class _CurrentRouteResource {
104
+ static uri = "route://current";
105
+ static resourceName = "Current Route";
106
+ static description = "Read the current URL path, search params, and hash";
107
+ static read() {
108
+ const location = getLocation();
109
+ if (!location) {
110
+ return {
111
+ contents: [
112
+ {
113
+ uri: _CurrentRouteResource.uri,
114
+ mimeType: "application/json",
115
+ text: JSON.stringify({
116
+ error: "Router bridge not connected. Ensure useRouterBridge() is called inside a React Router tree."
117
+ })
118
+ }
119
+ ]
120
+ };
121
+ }
122
+ return {
123
+ contents: [
124
+ {
125
+ uri: _CurrentRouteResource.uri,
126
+ mimeType: "application/json",
127
+ text: JSON.stringify({
128
+ pathname: location.pathname,
129
+ search: location.search,
130
+ hash: location.hash,
131
+ href: `${location.pathname}${location.search}${location.hash}`
132
+ })
133
+ }
134
+ ]
135
+ };
136
+ }
137
+ };
138
+
139
+ // libs/react/src/router/router.entries.ts
140
+ function createRouterEntries() {
141
+ return {
142
+ tools: [NavigateTool, GoBackTool],
143
+ resources: [CurrentRouteResource]
144
+ };
145
+ }
146
+ export {
147
+ CurrentRouteResource,
148
+ GoBackTool,
149
+ NavigateTool,
150
+ clearBridge,
151
+ createRouterEntries,
152
+ getLocation,
153
+ getNavigate,
154
+ setLocation,
155
+ setNavigate,
156
+ useRouterBridge
157
+ };
@@ -0,0 +1,450 @@
1
+ // libs/react/src/state/useStoreResource.ts
2
+ import { useContext, useEffect, useRef, useCallback } from "react";
3
+
4
+ // libs/react/src/provider/FrontMcpContext.ts
5
+ import { createContext } from "react";
6
+
7
+ // libs/react/src/components/ComponentRegistry.ts
8
+ var ComponentRegistry = class {
9
+ entries = /* @__PURE__ */ new Map();
10
+ register(uri, component, meta) {
11
+ const name = extractName(uri);
12
+ this.entries.set(uri, { uri, name, component, description: meta?.description });
13
+ }
14
+ registerAll(map) {
15
+ for (const [uri, component] of Object.entries(map)) {
16
+ this.register(uri, component);
17
+ }
18
+ }
19
+ get(uri) {
20
+ return this.entries.get(uri)?.component;
21
+ }
22
+ /**
23
+ * Resolve a shorthand name like `'UserCard'` by trying
24
+ * `component://UserCard`, `element://UserCard`, `page://UserCard`.
25
+ */
26
+ resolve(type) {
27
+ const exact = this.entries.get(type);
28
+ if (exact) return exact.component;
29
+ for (const protocol of ["component://", "element://", "page://"]) {
30
+ const entry = this.entries.get(`${protocol}${type}`);
31
+ if (entry) return entry.component;
32
+ }
33
+ return void 0;
34
+ }
35
+ has(uri) {
36
+ return this.entries.has(uri);
37
+ }
38
+ list() {
39
+ return Array.from(this.entries.values()).map(({ uri, name, description }) => ({
40
+ uri,
41
+ name,
42
+ description
43
+ }));
44
+ }
45
+ clear() {
46
+ this.entries.clear();
47
+ }
48
+ };
49
+ function extractName(uri) {
50
+ const idx = uri.indexOf("://");
51
+ return idx >= 0 ? uri.slice(idx + 3) : uri;
52
+ }
53
+
54
+ // libs/react/src/registry/DynamicRegistry.ts
55
+ var DynamicRegistry = class {
56
+ tools = /* @__PURE__ */ new Map();
57
+ resources = /* @__PURE__ */ new Map();
58
+ toolRefCounts = /* @__PURE__ */ new Map();
59
+ resourceRefCounts = /* @__PURE__ */ new Map();
60
+ listeners = /* @__PURE__ */ new Set();
61
+ version = 0;
62
+ /**
63
+ * Register a dynamic tool. Returns an unregister function
64
+ * suitable for useEffect cleanup.
65
+ *
66
+ * Multiple registrations of the same name are ref-counted:
67
+ * subsequent registrations update the definition but the tool
68
+ * is only removed when every registrant has unregistered.
69
+ */
70
+ registerTool(def) {
71
+ const existing = this.toolRefCounts.get(def.name) ?? 0;
72
+ this.toolRefCounts.set(def.name, existing + 1);
73
+ this.tools.set(def.name, def);
74
+ if (existing === 0) {
75
+ this.notify();
76
+ }
77
+ let called = false;
78
+ return () => {
79
+ if (called) return;
80
+ called = true;
81
+ this.unregisterTool(def.name);
82
+ };
83
+ }
84
+ unregisterTool(name) {
85
+ const count = this.toolRefCounts.get(name);
86
+ if (count == null) return;
87
+ if (count <= 1) {
88
+ this.toolRefCounts.delete(name);
89
+ this.tools.delete(name);
90
+ this.notify();
91
+ } else {
92
+ this.toolRefCounts.set(name, count - 1);
93
+ }
94
+ }
95
+ /**
96
+ * Register a dynamic resource. Returns an unregister function
97
+ * suitable for useEffect cleanup.
98
+ *
99
+ * Multiple registrations of the same URI are ref-counted.
100
+ */
101
+ registerResource(def) {
102
+ const existing = this.resourceRefCounts.get(def.uri) ?? 0;
103
+ this.resourceRefCounts.set(def.uri, existing + 1);
104
+ this.resources.set(def.uri, def);
105
+ if (existing === 0) {
106
+ this.notify();
107
+ }
108
+ let called = false;
109
+ return () => {
110
+ if (called) return;
111
+ called = true;
112
+ this.unregisterResource(def.uri);
113
+ };
114
+ }
115
+ unregisterResource(uri) {
116
+ const count = this.resourceRefCounts.get(uri);
117
+ if (count == null) return;
118
+ if (count <= 1) {
119
+ this.resourceRefCounts.delete(uri);
120
+ this.resources.delete(uri);
121
+ this.notify();
122
+ } else {
123
+ this.resourceRefCounts.set(uri, count - 1);
124
+ }
125
+ }
126
+ /** Update the execute function for an existing tool (for stale closure prevention). */
127
+ updateToolExecute(name, execute) {
128
+ const existing = this.tools.get(name);
129
+ if (existing) {
130
+ existing.execute = execute;
131
+ }
132
+ }
133
+ /** Update the read function for an existing resource and notify subscribers. */
134
+ updateResourceRead(uri, read) {
135
+ const existing = this.resources.get(uri);
136
+ if (existing) {
137
+ existing.read = read;
138
+ this.notify();
139
+ }
140
+ }
141
+ getTools() {
142
+ return [...this.tools.values()];
143
+ }
144
+ getResources() {
145
+ return [...this.resources.values()];
146
+ }
147
+ findTool(name) {
148
+ return this.tools.get(name);
149
+ }
150
+ findResource(uri) {
151
+ return this.resources.get(uri);
152
+ }
153
+ hasTool(name) {
154
+ return this.tools.has(name);
155
+ }
156
+ hasResource(uri) {
157
+ return this.resources.has(uri);
158
+ }
159
+ subscribe(listener) {
160
+ this.listeners.add(listener);
161
+ return () => {
162
+ this.listeners.delete(listener);
163
+ };
164
+ }
165
+ getVersion() {
166
+ return this.version;
167
+ }
168
+ clear() {
169
+ if (this.tools.size === 0 && this.resources.size === 0) return;
170
+ this.tools.clear();
171
+ this.resources.clear();
172
+ this.toolRefCounts.clear();
173
+ this.resourceRefCounts.clear();
174
+ this.notify();
175
+ }
176
+ notify() {
177
+ this.version++;
178
+ this.listeners.forEach((l) => {
179
+ l();
180
+ });
181
+ }
182
+ };
183
+
184
+ // libs/react/src/provider/FrontMcpContext.ts
185
+ var defaultDynamicRegistry = new DynamicRegistry();
186
+ var FrontMcpContext = createContext({
187
+ name: "default",
188
+ registry: new ComponentRegistry(),
189
+ dynamicRegistry: defaultDynamicRegistry,
190
+ getDynamicRegistry: () => defaultDynamicRegistry,
191
+ connect: async () => {
192
+ }
193
+ });
194
+
195
+ // libs/react/src/state/useStoreResource.ts
196
+ var VALID_NAME_RE = /^[a-zA-Z0-9_-]+$/;
197
+ function useStoreResource(options) {
198
+ const { name, getState, subscribe, selectors, actions } = options;
199
+ const { getDynamicRegistry } = useContext(FrontMcpContext);
200
+ const dynamicRegistry = getDynamicRegistry(options.server);
201
+ if (!name || !VALID_NAME_RE.test(name)) {
202
+ throw new Error(`useStoreResource: invalid store name "${name}". Names must match ${VALID_NAME_RE}.`);
203
+ }
204
+ const getStateRef = useRef(getState);
205
+ getStateRef.current = getState;
206
+ const readState = useCallback(
207
+ async () => ({
208
+ contents: [
209
+ {
210
+ uri: `state://${name}`,
211
+ mimeType: "application/json",
212
+ text: JSON.stringify(getStateRef.current() ?? null)
213
+ }
214
+ ]
215
+ }),
216
+ [name]
217
+ );
218
+ useEffect(() => {
219
+ const unregister = dynamicRegistry.registerResource({
220
+ uri: `state://${name}`,
221
+ name: `${name}-state`,
222
+ description: `Full state of ${name} store`,
223
+ mimeType: "application/json",
224
+ read: readState
225
+ });
226
+ const unsubscribe = subscribe(() => {
227
+ dynamicRegistry.updateResourceRead(`state://${name}`, readState);
228
+ });
229
+ return () => {
230
+ unregister();
231
+ unsubscribe();
232
+ };
233
+ }, [dynamicRegistry, name, subscribe, readState]);
234
+ useEffect(() => {
235
+ if (!selectors) return;
236
+ const cleanups = [];
237
+ const selectorUris = [];
238
+ for (const [key, selector] of Object.entries(selectors)) {
239
+ if (!key || !VALID_NAME_RE.test(key)) {
240
+ throw new Error(`useStoreResource: invalid selector key "${key}". Keys must match ${VALID_NAME_RE}.`);
241
+ }
242
+ const uri = `state://${name}/${key}`;
243
+ const selectorRef = { current: selector };
244
+ const readSelector = async () => ({
245
+ contents: [
246
+ {
247
+ uri,
248
+ mimeType: "application/json",
249
+ text: JSON.stringify(selectorRef.current(getStateRef.current()) ?? null)
250
+ }
251
+ ]
252
+ });
253
+ selectorUris.push({ uri, readSelector });
254
+ cleanups.push(
255
+ dynamicRegistry.registerResource({
256
+ uri,
257
+ name: `${name}-${key}`,
258
+ description: `Selector "${key}" from ${name} store`,
259
+ mimeType: "application/json",
260
+ read: readSelector
261
+ })
262
+ );
263
+ }
264
+ const unsubscribe = subscribe(() => {
265
+ for (const { uri, readSelector } of selectorUris) {
266
+ dynamicRegistry.updateResourceRead(uri, readSelector);
267
+ }
268
+ });
269
+ cleanups.push(unsubscribe);
270
+ return () => {
271
+ cleanups.forEach((fn) => {
272
+ fn();
273
+ });
274
+ };
275
+ }, [dynamicRegistry, name, selectors, subscribe]);
276
+ useEffect(() => {
277
+ if (!actions) return;
278
+ const cleanups = [];
279
+ for (const [key, action] of Object.entries(actions)) {
280
+ const toolName = `${name}_${key}`;
281
+ const execute = async (args) => {
282
+ const argsArray = args["args"];
283
+ const result = await (Array.isArray(argsArray) ? action(...argsArray) : action(args));
284
+ return {
285
+ content: [{ type: "text", text: JSON.stringify({ success: true, result }) }]
286
+ };
287
+ };
288
+ cleanups.push(
289
+ dynamicRegistry.registerTool({
290
+ name: toolName,
291
+ description: `Action "${key}" on ${name} store`,
292
+ inputSchema: {
293
+ type: "object",
294
+ properties: {
295
+ args: { type: "array", description: "Arguments to pass to the action" }
296
+ }
297
+ },
298
+ execute
299
+ })
300
+ );
301
+ }
302
+ return () => {
303
+ cleanups.forEach((fn) => {
304
+ fn();
305
+ });
306
+ };
307
+ }, [dynamicRegistry, name, actions]);
308
+ }
309
+
310
+ // libs/react/src/state/useReduxResource.ts
311
+ import { useMemo } from "react";
312
+ function useReduxResource(options) {
313
+ const { store, name = "redux", selectors, actions, server } = options;
314
+ const wrappedActions = useMemo(() => {
315
+ if (!actions) return void 0;
316
+ const wrapped = {};
317
+ for (const [key, actionCreator] of Object.entries(actions)) {
318
+ wrapped[key] = (...args) => {
319
+ const action = actionCreator(...args);
320
+ return store.dispatch(action);
321
+ };
322
+ }
323
+ return wrapped;
324
+ }, [actions, store]);
325
+ useStoreResource({
326
+ name,
327
+ getState: store.getState.bind(store),
328
+ subscribe: store.subscribe.bind(store),
329
+ selectors,
330
+ actions: wrappedActions,
331
+ server
332
+ });
333
+ }
334
+
335
+ // libs/react/src/state/useValtioResource.ts
336
+ import { useMemo as useMemo2 } from "react";
337
+ function getByPath(obj, path) {
338
+ const parts = path.split(".");
339
+ let current = obj;
340
+ for (const part of parts) {
341
+ if (current == null || typeof current !== "object") return void 0;
342
+ current = current[part];
343
+ }
344
+ return current;
345
+ }
346
+ function useValtioResource(options) {
347
+ const { proxy, subscribe: valtioSubscribe, name = "valtio", paths, mutations, server } = options;
348
+ const selectors = useMemo2(() => {
349
+ if (!paths) return void 0;
350
+ const sels = {};
351
+ for (const [key, path] of Object.entries(paths)) {
352
+ sels[key] = (state) => getByPath(state, path);
353
+ }
354
+ return sels;
355
+ }, [paths]);
356
+ const subscribe = useMemo2(() => (cb) => valtioSubscribe(proxy, cb), [proxy, valtioSubscribe]);
357
+ const actions = useMemo2(() => {
358
+ if (!mutations) return void 0;
359
+ const wrapped = {};
360
+ for (const [key, mutation] of Object.entries(mutations)) {
361
+ wrapped[key] = (...args) => mutation(...args);
362
+ }
363
+ return wrapped;
364
+ }, [mutations]);
365
+ const getState = useMemo2(() => () => JSON.parse(JSON.stringify(proxy)), [proxy]);
366
+ useStoreResource({
367
+ name,
368
+ getState,
369
+ subscribe,
370
+ selectors,
371
+ actions,
372
+ server
373
+ });
374
+ }
375
+
376
+ // libs/react/src/state/adapters/reduxAdapter.ts
377
+ function reduxStore(options) {
378
+ const { store, name = "redux", selectors, actions: rawActions } = options;
379
+ let actions;
380
+ if (rawActions) {
381
+ actions = {};
382
+ for (const [key, actionCreator] of Object.entries(rawActions)) {
383
+ actions[key] = (...args) => {
384
+ const action = actionCreator(...args);
385
+ return store.dispatch(action);
386
+ };
387
+ }
388
+ }
389
+ return {
390
+ name,
391
+ getState: store.getState.bind(store),
392
+ subscribe: store.subscribe.bind(store),
393
+ selectors,
394
+ actions
395
+ };
396
+ }
397
+
398
+ // libs/react/src/state/adapters/valtioAdapter.ts
399
+ function getByPath2(obj, path) {
400
+ const parts = path.split(".");
401
+ let current = obj;
402
+ for (const part of parts) {
403
+ if (current == null || typeof current !== "object") return void 0;
404
+ current = current[part];
405
+ }
406
+ return current;
407
+ }
408
+ function valtioStore(options) {
409
+ const { proxy, subscribe: valtioSubscribe, name = "valtio", paths, mutations } = options;
410
+ let selectors;
411
+ if (paths) {
412
+ selectors = {};
413
+ for (const [key, path] of Object.entries(paths)) {
414
+ selectors[key] = (state) => getByPath2(state, path);
415
+ }
416
+ }
417
+ let actions;
418
+ if (mutations) {
419
+ actions = {};
420
+ for (const [key, mutation] of Object.entries(mutations)) {
421
+ actions[key] = (...args) => mutation(...args);
422
+ }
423
+ }
424
+ return {
425
+ name,
426
+ getState: () => JSON.parse(JSON.stringify(proxy)),
427
+ subscribe: (cb) => valtioSubscribe(proxy, cb),
428
+ selectors,
429
+ actions
430
+ };
431
+ }
432
+
433
+ // libs/react/src/state/adapters/createStore.ts
434
+ function createStore(options) {
435
+ return {
436
+ name: options.name,
437
+ getState: options.getState,
438
+ subscribe: options.subscribe,
439
+ selectors: options.selectors,
440
+ actions: options.actions
441
+ };
442
+ }
443
+ export {
444
+ createStore,
445
+ reduxStore,
446
+ useReduxResource,
447
+ useStoreResource,
448
+ useValtioResource,
449
+ valtioStore
450
+ };