@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,379 @@
1
+ // libs/react/src/api/useApiClient.ts
2
+ import { useContext, useEffect, useRef } 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/api/createFetchClient.ts
196
+ function createFetchClient(fetchFn) {
197
+ const fn = fetchFn ?? globalThis.fetch.bind(globalThis);
198
+ return {
199
+ async request(config) {
200
+ const fetchOptions = {
201
+ method: config.method,
202
+ headers: config.headers
203
+ };
204
+ if (config.body !== void 0) {
205
+ fetchOptions.body = JSON.stringify(config.body);
206
+ const headers = fetchOptions.headers;
207
+ if (headers && !headers["Content-Type"] && !headers["content-type"]) {
208
+ headers["Content-Type"] = "application/json";
209
+ }
210
+ }
211
+ const response = await fn(config.url, fetchOptions);
212
+ const responseText = await response.text();
213
+ let data;
214
+ try {
215
+ data = JSON.parse(responseText);
216
+ } catch {
217
+ data = responseText;
218
+ }
219
+ return {
220
+ status: response.status,
221
+ statusText: response.statusText,
222
+ data
223
+ };
224
+ }
225
+ };
226
+ }
227
+
228
+ // libs/react/src/api/useApiClient.ts
229
+ function interpolatePath(path, params) {
230
+ return path.replace(/\{(\w+)\}/g, (_, key) => {
231
+ const value = params[key];
232
+ return value != null ? encodeURIComponent(String(value)) : `{${key}}`;
233
+ });
234
+ }
235
+ function useApiClient(options) {
236
+ const { baseUrl, operations, headers, prefix = "api", client, fetch: customFetch } = options;
237
+ const { getDynamicRegistry } = useContext(FrontMcpContext);
238
+ const dynamicRegistry = getDynamicRegistry(options.server);
239
+ const headersRef = useRef(headers);
240
+ headersRef.current = headers;
241
+ const clientRef = useRef(client ?? createFetchClient(customFetch));
242
+ clientRef.current = client ?? createFetchClient(customFetch);
243
+ useEffect(() => {
244
+ const cleanups = [];
245
+ for (const op of operations) {
246
+ const toolName = `${prefix}_${op.operationId}`;
247
+ const execute = async (args) => {
248
+ const resolvedHeaders = {
249
+ "Content-Type": "application/json",
250
+ ...typeof headersRef.current === "function" ? headersRef.current() : headersRef.current ?? {}
251
+ };
252
+ const url = baseUrl + interpolatePath(op.path, args);
253
+ const body = args["body"];
254
+ const method = op.method;
255
+ const requestConfig = {
256
+ method,
257
+ url,
258
+ headers: resolvedHeaders
259
+ };
260
+ if (body !== void 0 && method !== "GET" && method !== "HEAD") {
261
+ requestConfig.body = body;
262
+ }
263
+ const response = await clientRef.current.request(requestConfig);
264
+ return {
265
+ content: [
266
+ {
267
+ type: "text",
268
+ text: JSON.stringify({
269
+ status: response.status,
270
+ statusText: response.statusText,
271
+ data: response.data
272
+ })
273
+ }
274
+ ],
275
+ isError: response.status >= 400
276
+ };
277
+ };
278
+ cleanups.push(
279
+ dynamicRegistry.registerTool({
280
+ name: toolName,
281
+ description: op.description,
282
+ inputSchema: op.inputSchema,
283
+ execute
284
+ })
285
+ );
286
+ }
287
+ return () => {
288
+ cleanups.forEach((fn) => {
289
+ fn();
290
+ });
291
+ };
292
+ }, [dynamicRegistry, baseUrl, operations, prefix, client, customFetch]);
293
+ }
294
+
295
+ // libs/react/src/api/parseOpenApiSpec.ts
296
+ var HTTP_METHODS = ["get", "post", "put", "delete", "patch", "options", "head"];
297
+ function parseOpenApiSpec(spec) {
298
+ const paths = spec["paths"];
299
+ if (!paths) return [];
300
+ const operations = [];
301
+ const usedIds = /* @__PURE__ */ new Set();
302
+ for (const [path, pathItem] of Object.entries(paths)) {
303
+ if (!pathItem || typeof pathItem !== "object") continue;
304
+ for (const method of HTTP_METHODS) {
305
+ const operation = pathItem[method];
306
+ if (!operation || typeof operation !== "object") continue;
307
+ const rawOperationId = operation["operationId"];
308
+ let operationId = typeof rawOperationId === "string" ? rawOperationId : `${method}_${path.replace(/[^a-zA-Z0-9]/g, "_")}`;
309
+ if (usedIds.has(operationId)) {
310
+ let suffix = 1;
311
+ while (usedIds.has(`${operationId}_${suffix}`)) suffix++;
312
+ operationId = `${operationId}_${suffix}`;
313
+ }
314
+ usedIds.add(operationId);
315
+ const rawSummary = operation["summary"];
316
+ const rawDescription = operation["description"];
317
+ const description = (typeof rawSummary === "string" ? rawSummary : void 0) ?? (typeof rawDescription === "string" ? rawDescription : void 0) ?? `${method.toUpperCase()} ${path}`;
318
+ const properties = /* @__PURE__ */ Object.create(null);
319
+ const required = [];
320
+ const rawPathParams = pathItem["parameters"];
321
+ const rawOpParams = operation["parameters"];
322
+ const pathParams = Array.isArray(rawPathParams) ? rawPathParams.filter(Boolean) : [];
323
+ const opParams = Array.isArray(rawOpParams) ? rawOpParams.filter(Boolean) : [];
324
+ const paramMap = /* @__PURE__ */ new Map();
325
+ for (const param of pathParams) {
326
+ if (typeof param === "object" && param !== null && param.name) {
327
+ paramMap.set(`${param.in}:${param.name}`, param);
328
+ }
329
+ }
330
+ for (const param of opParams) {
331
+ if (typeof param === "object" && param !== null && param.name) {
332
+ paramMap.set(`${param.in}:${param.name}`, param);
333
+ }
334
+ }
335
+ const propertyLocations = /* @__PURE__ */ new Map();
336
+ for (const param of paramMap.values()) {
337
+ if (param.name === "__proto__" || param.name === "constructor" || param.name === "prototype") continue;
338
+ const existingIn = propertyLocations.get(param.name);
339
+ if (existingIn && existingIn !== param.in) {
340
+ throw new Error(
341
+ `Parameter "${param.name}" appears in both "${existingIn}" and "${param.in}" \u2014 ambiguous mapping`
342
+ );
343
+ }
344
+ propertyLocations.set(param.name, param.in);
345
+ properties[param.name] = {
346
+ ...param.schema ?? { type: "string" },
347
+ description: param.description
348
+ };
349
+ if (param.required) required.push(param.name);
350
+ }
351
+ const requestBody = operation["requestBody"];
352
+ if (requestBody) {
353
+ const content = requestBody["content"];
354
+ const jsonContent = content?.["application/json"];
355
+ if (jsonContent?.["schema"]) {
356
+ properties["body"] = {
357
+ ...jsonContent["schema"],
358
+ description: "Request body"
359
+ };
360
+ if (requestBody["required"]) required.push("body");
361
+ }
362
+ }
363
+ const inputSchema = {
364
+ type: "object",
365
+ properties
366
+ };
367
+ if (required.length > 0) {
368
+ inputSchema["required"] = required;
369
+ }
370
+ operations.push({ operationId, description, method: method.toUpperCase(), path, inputSchema });
371
+ }
372
+ }
373
+ return operations;
374
+ }
375
+ export {
376
+ createFetchClient,
377
+ parseOpenApiSpec,
378
+ useApiClient
379
+ };