@fragments-sdk/webmcp 0.2.0 → 0.2.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.
@@ -0,0 +1,88 @@
1
+ import { CompiledFragmentsFile } from '@fragments-sdk/context/types';
2
+ import { WebMCPTool } from '@webmcp-sdk/core';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import { ReactNode } from 'react';
5
+
6
+ interface CreateFragmentsWebMCPToolsOptions {
7
+ /** Tool name prefix. Default: 'fragments'. */
8
+ prefix?: string;
9
+ /** Subset of tools to register. If omitted, all tools are registered. */
10
+ tools?: Array<'discover' | 'inspect' | 'blocks' | 'tokens' | 'implement' | 'graph'>;
11
+ }
12
+ /**
13
+ * Build WebMCPTool[] from compiled fragments data.
14
+ *
15
+ * Reuses `buildMcpTools()` from @fragments-sdk/context for schema conversion
16
+ * (single source of truth — no schema drift). Execute handlers search/filter
17
+ * CompiledFragmentsFile data in-browser.
18
+ */
19
+ declare function createFragmentsWebMCPTools(data: CompiledFragmentsFile, options?: CreateFragmentsWebMCPToolsOptions): WebMCPTool[];
20
+
21
+ interface SearchIndex {
22
+ /** component name → lowercased searchable string (name + description + tags + category) */
23
+ components: Map<string, string>;
24
+ /** block name → lowercased searchable string */
25
+ blocks: Map<string, string>;
26
+ }
27
+ declare function buildSearchIndex(data: CompiledFragmentsFile): SearchIndex;
28
+ type FragmentHandler = (data: CompiledFragmentsFile, args: Record<string, unknown>, index: SearchIndex) => unknown;
29
+ declare const HANDLERS: Record<string, FragmentHandler>;
30
+
31
+ interface UseFragmentToolsOptions extends CreateFragmentsWebMCPToolsOptions {
32
+ /** Disable registration. Default: true. */
33
+ enabled?: boolean;
34
+ }
35
+ /**
36
+ * Auto-register all Fragments design system tools via WebMCP.
37
+ *
38
+ * Internally creates tools with `createFragmentsWebMCPTools()` and registers
39
+ * them using `useWebMCPTools()`. Memoized by data reference + prefix.
40
+ */
41
+ declare function useFragmentTools(data: CompiledFragmentsFile | null, options?: UseFragmentToolsOptions): {
42
+ tools: WebMCPTool[];
43
+ count: number;
44
+ };
45
+
46
+ interface UseCompiledFragmentsFromUrlResult {
47
+ data: CompiledFragmentsFile | null;
48
+ loading: boolean;
49
+ error: string | null;
50
+ }
51
+ /**
52
+ * Fetch compiled fragments data from a URL.
53
+ * Useful for user apps that want to integrate WebMCP tools.
54
+ */
55
+ declare function useCompiledFragmentsFromUrl(url: string): UseCompiledFragmentsFromUrlResult;
56
+
57
+ interface FragmentsWebMCPBaseProps {
58
+ /** Tool name prefix. Default: 'fragments'. */
59
+ prefix?: string;
60
+ /** Subset of tools to register. */
61
+ tools?: Array<'discover' | 'inspect' | 'blocks' | 'tokens' | 'implement' | 'graph'>;
62
+ children: ReactNode;
63
+ }
64
+ interface FragmentsWebMCPDataProps extends FragmentsWebMCPBaseProps {
65
+ /** Pre-loaded compiled fragments data. */
66
+ data: CompiledFragmentsFile;
67
+ url?: never;
68
+ }
69
+ interface FragmentsWebMCPUrlProps extends FragmentsWebMCPBaseProps {
70
+ /** URL to fetch compiled fragments data from. */
71
+ url: string;
72
+ data?: never;
73
+ }
74
+ type FragmentsWebMCPProps = FragmentsWebMCPDataProps | FragmentsWebMCPUrlProps;
75
+ /**
76
+ * Batteries-included WebMCP integration for Fragments design system.
77
+ *
78
+ * Makes any app AI-native in 3 lines:
79
+ * ```tsx
80
+ * import compiledData from './fragments.compiled.json';
81
+ * import { FragmentsWebMCP } from '@fragments-sdk/webmcp/fragments';
82
+ *
83
+ * <FragmentsWebMCP data={compiledData}><App /></FragmentsWebMCP>
84
+ * ```
85
+ */
86
+ declare function FragmentsWebMCP(props: FragmentsWebMCPProps): react_jsx_runtime.JSX.Element;
87
+
88
+ export { type CreateFragmentsWebMCPToolsOptions, type FragmentHandler, FragmentsWebMCP, type FragmentsWebMCPProps, HANDLERS, type SearchIndex, type UseFragmentToolsOptions, buildSearchIndex, createFragmentsWebMCPTools, useCompiledFragmentsFromUrl, useFragmentTools };
@@ -0,0 +1,579 @@
1
+ // src/fragments/create-tools.ts
2
+ import {
3
+ buildMcpTools,
4
+ buildToolNames
5
+ } from "@fragments-sdk/context/mcp-tools";
6
+
7
+ // src/fragments/handlers.ts
8
+ import { ComponentGraphEngine, deserializeGraph } from "@fragments-sdk/context/graph";
9
+ function buildSearchIndex(data) {
10
+ const components = /* @__PURE__ */ new Map();
11
+ for (const [key, frag] of Object.entries(data.fragments)) {
12
+ const searchable = [
13
+ frag.meta.name,
14
+ frag.meta.description,
15
+ frag.meta.category,
16
+ ...frag.meta.tags ?? []
17
+ ].join(" ").toLowerCase();
18
+ components.set(key, searchable);
19
+ }
20
+ const blocks = /* @__PURE__ */ new Map();
21
+ if (data.blocks) {
22
+ for (const [key, block] of Object.entries(data.blocks)) {
23
+ const searchable = [
24
+ block.name,
25
+ block.description,
26
+ block.category,
27
+ ...block.tags ?? [],
28
+ ...block.components
29
+ ].join(" ").toLowerCase();
30
+ blocks.set(key, searchable);
31
+ }
32
+ }
33
+ return { components, blocks };
34
+ }
35
+ function clampLimit(value, defaultVal, max = 50) {
36
+ if (typeof value !== "number") return defaultVal;
37
+ return Math.max(1, Math.min(max, Math.floor(value)));
38
+ }
39
+ function truncStr(value, maxLen = 200) {
40
+ if (typeof value !== "string") return "";
41
+ return value.slice(0, maxLen);
42
+ }
43
+ function validateEnum(value, allowed) {
44
+ if (typeof value !== "string") return void 0;
45
+ return allowed.includes(value) ? value : void 0;
46
+ }
47
+ function handleDiscover(data, args, index) {
48
+ const limit = clampLimit(args.limit, 10);
49
+ const search = truncStr(args.search);
50
+ const category = truncStr(args.category);
51
+ const status = validateEnum(args.status, [
52
+ "stable",
53
+ "beta",
54
+ "deprecated",
55
+ "experimental"
56
+ ]);
57
+ const component = truncStr(args.component);
58
+ const compact = args.compact === true;
59
+ const verbosity = validateEnum(args.verbosity, [
60
+ "compact",
61
+ "standard",
62
+ "full"
63
+ ]);
64
+ let results = Object.values(data.fragments);
65
+ if (category) {
66
+ const lower = category.toLowerCase();
67
+ results = results.filter(
68
+ (f) => f.meta.category.toLowerCase() === lower
69
+ );
70
+ }
71
+ if (status) {
72
+ results = results.filter((f) => f.meta.status === status);
73
+ }
74
+ if (component) {
75
+ const lower = component.toLowerCase();
76
+ const target = results.find(
77
+ (f) => f.meta.name.toLowerCase() === lower
78
+ );
79
+ if (target) {
80
+ results = results.filter(
81
+ (f) => f.meta.category === target.meta.category && f.meta.name !== target.meta.name
82
+ );
83
+ }
84
+ }
85
+ if (search) {
86
+ const lower = search.toLowerCase();
87
+ results = results.filter((f) => {
88
+ const searchable = index.components.get(f.meta.name) ?? "";
89
+ return searchable.includes(lower);
90
+ });
91
+ }
92
+ results = results.slice(0, limit);
93
+ if (compact || verbosity === "compact") {
94
+ return {
95
+ components: results.map((f) => ({
96
+ name: f.meta.name,
97
+ category: f.meta.category
98
+ })),
99
+ count: results.length
100
+ };
101
+ }
102
+ if (verbosity === "full") {
103
+ return { components: results, count: results.length };
104
+ }
105
+ return {
106
+ components: results.map((f) => ({
107
+ name: f.meta.name,
108
+ description: f.meta.description,
109
+ category: f.meta.category,
110
+ tags: f.meta.tags,
111
+ status: f.meta.status,
112
+ usage: f.usage
113
+ })),
114
+ count: results.length
115
+ };
116
+ }
117
+ function handleInspect(data, args, _index) {
118
+ const componentName = truncStr(args.component);
119
+ if (!componentName) {
120
+ return { error: true, message: "component parameter is required" };
121
+ }
122
+ const lower = componentName.toLowerCase();
123
+ const fragment = Object.values(data.fragments).find(
124
+ (f) => f.meta.name.toLowerCase() === lower
125
+ );
126
+ if (!fragment) {
127
+ return { error: true, message: `Component "${componentName}" not found` };
128
+ }
129
+ const fields = Array.isArray(args.fields) ? args.fields.map((f) => truncStr(f)) : null;
130
+ if (!fields) {
131
+ const variant = truncStr(args.variant);
132
+ if (variant) {
133
+ const lower2 = variant.toLowerCase();
134
+ return {
135
+ ...fragment,
136
+ variants: fragment.variants.filter(
137
+ (v) => v.name.toLowerCase() === lower2
138
+ )
139
+ };
140
+ }
141
+ return fragment;
142
+ }
143
+ const result = {};
144
+ for (const field of fields) {
145
+ const normalized = field === "usage" ? "usage" : field;
146
+ const parts = normalized.split(".");
147
+ let value = fragment;
148
+ for (const part of parts) {
149
+ if (value && typeof value === "object") {
150
+ value = value[part];
151
+ } else {
152
+ value = void 0;
153
+ break;
154
+ }
155
+ }
156
+ if (value !== void 0) {
157
+ result[field] = value;
158
+ }
159
+ }
160
+ return result;
161
+ }
162
+ function handleBlocks(data, args, index) {
163
+ if (!data.blocks) {
164
+ return { blocks: [], count: 0 };
165
+ }
166
+ const limit = clampLimit(args.limit, 50);
167
+ const search = truncStr(args.search);
168
+ const category = truncStr(args.category);
169
+ const component = truncStr(args.component);
170
+ const name = truncStr(args.name);
171
+ const verbosity = validateEnum(args.verbosity, [
172
+ "compact",
173
+ "standard",
174
+ "full"
175
+ ]);
176
+ let results = Object.values(data.blocks);
177
+ if (name) {
178
+ const lower = name.toLowerCase();
179
+ results = results.filter((b) => b.name.toLowerCase() === lower);
180
+ return { blocks: results, count: results.length };
181
+ }
182
+ if (category) {
183
+ const lower = category.toLowerCase();
184
+ results = results.filter((b) => b.category.toLowerCase() === lower);
185
+ }
186
+ if (component) {
187
+ const lower = component.toLowerCase();
188
+ results = results.filter(
189
+ (b) => b.components.some((c) => c.toLowerCase() === lower)
190
+ );
191
+ }
192
+ if (search) {
193
+ const lower = search.toLowerCase();
194
+ results = results.filter((b) => {
195
+ const searchable = index.blocks.get(b.name) ?? "";
196
+ return searchable.includes(lower);
197
+ });
198
+ }
199
+ results = results.slice(0, limit);
200
+ if (verbosity === "compact") {
201
+ return {
202
+ blocks: results.map((b) => ({
203
+ name: b.name,
204
+ description: b.description,
205
+ category: b.category,
206
+ components: b.components
207
+ })),
208
+ count: results.length
209
+ };
210
+ }
211
+ return { blocks: results, count: results.length };
212
+ }
213
+ function handleTokens(data, args, _index) {
214
+ if (!data.tokens) {
215
+ return { tokens: [], category: void 0 };
216
+ }
217
+ const limit = clampLimit(args.limit, 25);
218
+ const category = truncStr(args.category);
219
+ const search = truncStr(args.search);
220
+ if (category) {
221
+ const lower = category.toLowerCase();
222
+ const matchingCategory = Object.keys(data.tokens.categories).find(
223
+ (c) => c.toLowerCase() === lower
224
+ );
225
+ if (!matchingCategory) {
226
+ return { tokens: [], category };
227
+ }
228
+ let tokens = data.tokens.categories[matchingCategory];
229
+ if (search) {
230
+ const searchLower = search.toLowerCase();
231
+ tokens = tokens.filter((t) => t.name.toLowerCase().includes(searchLower));
232
+ }
233
+ return {
234
+ tokens: tokens.slice(0, limit),
235
+ category: matchingCategory
236
+ };
237
+ }
238
+ if (search) {
239
+ const lower = search.toLowerCase();
240
+ const tokens = [];
241
+ for (const entries of Object.values(data.tokens.categories)) {
242
+ for (const token of entries) {
243
+ if (token.name.toLowerCase().includes(lower)) {
244
+ tokens.push(token);
245
+ if (tokens.length >= limit) break;
246
+ }
247
+ }
248
+ if (tokens.length >= limit) break;
249
+ }
250
+ return { tokens };
251
+ }
252
+ const summary = {};
253
+ for (const [cat, entries] of Object.entries(data.tokens.categories)) {
254
+ summary[cat] = entries.length;
255
+ }
256
+ return { prefix: data.tokens.prefix, total: data.tokens.total, categories: summary };
257
+ }
258
+ function handleImplement(data, args, index) {
259
+ const useCase = truncStr(args.useCase);
260
+ const limit = clampLimit(args.limit, 5);
261
+ const verbosity = validateEnum(args.verbosity, [
262
+ "compact",
263
+ "standard",
264
+ "full"
265
+ ]);
266
+ if (!useCase) {
267
+ return { error: true, message: "useCase parameter is required" };
268
+ }
269
+ const discoverResult = handleDiscover(
270
+ data,
271
+ { search: useCase, limit, verbosity: "full" },
272
+ index
273
+ );
274
+ const components = discoverResult.count > 0 ? discoverResult.components : Object.values(data.fragments).slice(0, limit);
275
+ const componentNames = new Set(components.map((c) => c.meta.name));
276
+ let relevantBlocks = [];
277
+ if (data.blocks) {
278
+ relevantBlocks = Object.values(data.blocks).filter(
279
+ (b) => b.components.some((c) => componentNames.has(c))
280
+ );
281
+ }
282
+ if (relevantBlocks.length === 0 && data.blocks) {
283
+ const blocksResult = handleBlocks(
284
+ data,
285
+ { search: useCase, limit: 3 },
286
+ index
287
+ );
288
+ relevantBlocks = blocksResult.blocks;
289
+ }
290
+ let relevantTokens = [];
291
+ if (data.tokens) {
292
+ const words = useCase.toLowerCase().split(/\s+/);
293
+ for (const word of words) {
294
+ if (word.length < 3) continue;
295
+ const result = handleTokens(
296
+ data,
297
+ { search: word, limit: 5 },
298
+ index
299
+ );
300
+ if (result.tokens) {
301
+ relevantTokens.push(...result.tokens);
302
+ }
303
+ }
304
+ const seen = /* @__PURE__ */ new Set();
305
+ relevantTokens = relevantTokens.filter((t) => {
306
+ if (seen.has(t.name)) return false;
307
+ seen.add(t.name);
308
+ return true;
309
+ });
310
+ }
311
+ if (verbosity === "compact") {
312
+ return {
313
+ components: components.map((c) => ({
314
+ name: c.meta.name,
315
+ category: c.meta.category
316
+ })),
317
+ blocks: relevantBlocks.map((b) => ({
318
+ name: b.name,
319
+ category: b.category
320
+ })),
321
+ tokens: relevantTokens.map((t) => t.name)
322
+ };
323
+ }
324
+ return {
325
+ components,
326
+ blocks: relevantBlocks,
327
+ tokens: relevantTokens
328
+ };
329
+ }
330
+ var cachedEngine = null;
331
+ function getEngine(data) {
332
+ const key = data.generatedAt;
333
+ if (cachedEngine && cachedEngine.key === key) return cachedEngine.engine;
334
+ const graph = deserializeGraph(data.graph);
335
+ const engine = new ComponentGraphEngine(graph, data.blocks);
336
+ cachedEngine = { engine, key };
337
+ return engine;
338
+ }
339
+ function handleGraph(data, args, _index) {
340
+ if (!data.graph) {
341
+ return { error: true, message: "No graph data available" };
342
+ }
343
+ const mode = validateEnum(args.mode, [
344
+ "dependencies",
345
+ "dependents",
346
+ "alternatives",
347
+ "health",
348
+ "impact",
349
+ "path",
350
+ "composition",
351
+ "islands"
352
+ ]);
353
+ if (!mode) {
354
+ return { error: true, message: "mode parameter is required" };
355
+ }
356
+ const component = truncStr(args.component);
357
+ const graph = data.graph;
358
+ switch (mode) {
359
+ case "health": {
360
+ const engine = getEngine(data);
361
+ return engine.getHealth();
362
+ }
363
+ case "dependencies": {
364
+ if (!component) {
365
+ return { error: true, message: "component parameter is required for dependencies mode" };
366
+ }
367
+ const edges = graph.edges.filter(
368
+ (e) => e.s.toLowerCase() === component.toLowerCase()
369
+ );
370
+ return {
371
+ component,
372
+ dependencies: edges.map((e) => ({
373
+ target: e.t,
374
+ type: e.ty
375
+ }))
376
+ };
377
+ }
378
+ case "dependents": {
379
+ if (!component) {
380
+ return { error: true, message: "component parameter is required for dependents mode" };
381
+ }
382
+ const edges = graph.edges.filter(
383
+ (e) => e.t.toLowerCase() === component.toLowerCase()
384
+ );
385
+ return {
386
+ component,
387
+ dependents: edges.map((e) => ({
388
+ source: e.s,
389
+ type: e.ty
390
+ }))
391
+ };
392
+ }
393
+ case "alternatives": {
394
+ if (!component) {
395
+ return { error: true, message: "component parameter is required for alternatives mode" };
396
+ }
397
+ const altEdges = graph.edges.filter(
398
+ (e) => e.ty === "alternative-to" && (e.s.toLowerCase() === component.toLowerCase() || e.t.toLowerCase() === component.toLowerCase())
399
+ );
400
+ const alternatives = altEdges.map(
401
+ (e) => e.s.toLowerCase() === component.toLowerCase() ? e.t : e.s
402
+ );
403
+ return { component, alternatives };
404
+ }
405
+ case "impact": {
406
+ if (!component) {
407
+ return { error: true, message: "component parameter is required for impact mode" };
408
+ }
409
+ const engine = getEngine(data);
410
+ const maxDepth = typeof args.maxDepth === "number" ? Math.min(args.maxDepth, 10) : 3;
411
+ return engine.impact(component, maxDepth);
412
+ }
413
+ case "path": {
414
+ if (!component) {
415
+ return { error: true, message: "component parameter is required for path mode" };
416
+ }
417
+ const target = truncStr(args.target);
418
+ if (!target) {
419
+ return { error: true, message: "target parameter is required for path mode" };
420
+ }
421
+ const engine = getEngine(data);
422
+ return engine.path(component, target);
423
+ }
424
+ case "composition": {
425
+ if (!component) {
426
+ return { error: true, message: "component parameter is required for composition mode" };
427
+ }
428
+ const engine = getEngine(data);
429
+ return engine.composition(component);
430
+ }
431
+ case "islands": {
432
+ const engine = getEngine(data);
433
+ return { islands: engine.islands() };
434
+ }
435
+ default:
436
+ return { error: true, message: `Unknown mode: ${mode}` };
437
+ }
438
+ }
439
+ var HANDLERS = {
440
+ discover: handleDiscover,
441
+ inspect: handleInspect,
442
+ blocks: handleBlocks,
443
+ tokens: handleTokens,
444
+ implement: handleImplement,
445
+ graph: handleGraph
446
+ };
447
+
448
+ // src/fragments/create-tools.ts
449
+ var WEBMCP_TOOL_KEYS = [
450
+ "discover",
451
+ "inspect",
452
+ "blocks",
453
+ "tokens",
454
+ "implement",
455
+ "graph"
456
+ ];
457
+ function createFragmentsWebMCPTools(data, options) {
458
+ const prefix = options?.prefix ?? "fragments";
459
+ const toolNames = buildToolNames(prefix);
460
+ const mcpSchemas = buildMcpTools(prefix);
461
+ const searchIndex = buildSearchIndex(data);
462
+ const enabledKeys = options?.tools ? WEBMCP_TOOL_KEYS.filter((key) => options.tools.includes(key)) : WEBMCP_TOOL_KEYS;
463
+ return enabledKeys.filter((key) => key !== "graph" || data.graph != null).map((key) => {
464
+ const schema = mcpSchemas.find((s) => s.name === toolNames[key]);
465
+ const handler = HANDLERS[key];
466
+ return {
467
+ name: schema.name,
468
+ description: schema.description,
469
+ inputSchema: schema.inputSchema,
470
+ annotations: { readOnlyHint: true },
471
+ execute: async (input, _client) => {
472
+ try {
473
+ return handler(data, input, searchIndex);
474
+ } catch (err) {
475
+ return {
476
+ error: true,
477
+ message: err instanceof Error ? err.message : String(err)
478
+ };
479
+ }
480
+ }
481
+ };
482
+ });
483
+ }
484
+
485
+ // src/fragments/use-fragment-tools.ts
486
+ import { useMemo } from "react";
487
+ import { useWebMCPTools } from "@webmcp-sdk/react";
488
+ function useFragmentTools(data, options) {
489
+ const prefix = options?.prefix ?? "fragments";
490
+ const enabled = options?.enabled ?? true;
491
+ const toolsFilter = options?.tools;
492
+ const toolsKey = toolsFilter ? JSON.stringify(toolsFilter) : "";
493
+ const tools = useMemo(() => {
494
+ if (!data) return [];
495
+ return createFragmentsWebMCPTools(data, { prefix, tools: toolsFilter });
496
+ }, [data, prefix, toolsKey]);
497
+ useWebMCPTools(tools, { enabled });
498
+ return { tools, count: tools.length };
499
+ }
500
+
501
+ // src/fragments/use-compiled.ts
502
+ import { useState, useEffect } from "react";
503
+ function useCompiledFragmentsFromUrl(url) {
504
+ const [data, setData] = useState(null);
505
+ const [loading, setLoading] = useState(true);
506
+ const [error, setError] = useState(null);
507
+ useEffect(() => {
508
+ let cancelled = false;
509
+ setLoading(true);
510
+ setError(null);
511
+ fetch(url).then((res) => {
512
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
513
+ return res.json();
514
+ }).then((json) => {
515
+ if (!cancelled) {
516
+ setData(json);
517
+ setLoading(false);
518
+ }
519
+ }).catch((err) => {
520
+ if (!cancelled) {
521
+ setError(err instanceof Error ? err.message : String(err));
522
+ setLoading(false);
523
+ }
524
+ });
525
+ return () => {
526
+ cancelled = true;
527
+ };
528
+ }, [url]);
529
+ return { data, loading, error };
530
+ }
531
+
532
+ // src/fragments/FragmentsWebMCP.tsx
533
+ import { WebMCPProvider } from "@webmcp-sdk/react";
534
+ import { jsx, jsxs } from "react/jsx-runtime";
535
+ function FragmentsToolsRegistrar({
536
+ data,
537
+ prefix,
538
+ tools
539
+ }) {
540
+ useFragmentTools(data, { prefix, tools });
541
+ return null;
542
+ }
543
+ function FragmentsWebMCPWithUrl({
544
+ url,
545
+ prefix,
546
+ tools,
547
+ children
548
+ }) {
549
+ const { data } = useCompiledFragmentsFromUrl(url);
550
+ return /* @__PURE__ */ jsxs(WebMCPProvider, { devShim: true, children: [
551
+ /* @__PURE__ */ jsx(FragmentsToolsRegistrar, { data, prefix, tools }),
552
+ children
553
+ ] });
554
+ }
555
+ function FragmentsWebMCPWithData({
556
+ data,
557
+ prefix,
558
+ tools,
559
+ children
560
+ }) {
561
+ return /* @__PURE__ */ jsxs(WebMCPProvider, { devShim: true, children: [
562
+ /* @__PURE__ */ jsx(FragmentsToolsRegistrar, { data, prefix, tools }),
563
+ children
564
+ ] });
565
+ }
566
+ function FragmentsWebMCP(props) {
567
+ if ("url" in props && props.url) {
568
+ return /* @__PURE__ */ jsx(FragmentsWebMCPWithUrl, { ...props });
569
+ }
570
+ return /* @__PURE__ */ jsx(FragmentsWebMCPWithData, { ...props });
571
+ }
572
+ export {
573
+ FragmentsWebMCP,
574
+ HANDLERS,
575
+ buildSearchIndex,
576
+ createFragmentsWebMCPTools,
577
+ useCompiledFragmentsFromUrl,
578
+ useFragmentTools
579
+ };
@@ -0,0 +1,2 @@
1
+ export { DefineToolConfig, InferInput, InputSchema, JSONSchemaInput, ModelContextAPI, ModelContextClient, PropertyDef, SPEC_URL, SPEC_VERSION, ShimRegistry, ToolAnnotations, ToolCallEvent, ToolExecuteCallback, WebMCPTool, defineTool, getExperimentalModelContext, getModelContext, installWebMCPShim, isShimInstalled, isWebMCPSupported, requireModelContext, uninstallWebMCPShim } from '@webmcp-sdk/core';
2
+ import '@webmcp-sdk/react';
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ // src/index.ts
2
+ import {
3
+ SPEC_VERSION,
4
+ SPEC_URL,
5
+ isWebMCPSupported,
6
+ getModelContext,
7
+ getExperimentalModelContext,
8
+ requireModelContext,
9
+ installWebMCPShim,
10
+ uninstallWebMCPShim,
11
+ isShimInstalled,
12
+ defineTool
13
+ } from "@webmcp-sdk/core";
14
+ export {
15
+ SPEC_URL,
16
+ SPEC_VERSION,
17
+ defineTool,
18
+ getExperimentalModelContext,
19
+ getModelContext,
20
+ installWebMCPShim,
21
+ isShimInstalled,
22
+ isWebMCPSupported,
23
+ requireModelContext,
24
+ uninstallWebMCPShim
25
+ };
@@ -0,0 +1 @@
1
+ export { UseWebMCPToolOptions, UseWebMCPToolsOptions, WebMCPContextValue, WebMCPProvider, WebMCPProviderProps, useWebMCPContext, useWebMCPStatus, useWebMCPTool, useWebMCPTools } from '@webmcp-sdk/react';
@@ -0,0 +1,15 @@
1
+ // src/react/index.ts
2
+ import {
3
+ WebMCPProvider,
4
+ useWebMCPContext,
5
+ useWebMCPTool,
6
+ useWebMCPTools,
7
+ useWebMCPStatus
8
+ } from "@webmcp-sdk/react";
9
+ export {
10
+ WebMCPProvider,
11
+ useWebMCPContext,
12
+ useWebMCPStatus,
13
+ useWebMCPTool,
14
+ useWebMCPTools
15
+ };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@fragments-sdk/webmcp",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "license": "FSL-1.1-MIT",
5
- "description": "WebMCP (W3C) React integration for exposing design system tools to AI agents via navigator.modelContext",
5
+ "description": "Fragments design system WebMCP integration exposes component intelligence to AI agents via @webmcp-sdk",
6
6
  "author": "Conan McNicholl",
7
7
  "homepage": "https://usefragments.com",
8
8
  "repository": {
@@ -39,24 +39,19 @@
39
39
  "files": [
40
40
  "dist"
41
41
  ],
42
+ "dependencies": {
43
+ "@webmcp-sdk/core": "^0.1.0",
44
+ "@webmcp-sdk/react": "^0.1.0"
45
+ },
42
46
  "peerDependencies": {
43
47
  "react": ">=18",
44
48
  "@fragments-sdk/context": ">=0.3.0"
45
49
  },
46
- "peerDependenciesMeta": {
47
- "react": {
48
- "optional": true
49
- },
50
- "@fragments-sdk/context": {
51
- "optional": true
52
- }
53
- },
54
50
  "devDependencies": {
55
51
  "@testing-library/react": "^14.0.0",
56
52
  "@types/react": "^18.3.0",
57
53
  "react": "^18.3.0",
58
54
  "react-dom": "^18.3.0",
59
- "tsx": "^4.21.0",
60
55
  "tsup": "^8.3.5",
61
56
  "typescript": "^5.7.2",
62
57
  "vitest": "^2.1.8",
@@ -67,8 +62,6 @@
67
62
  "dev": "tsup --watch",
68
63
  "test": "vitest run",
69
64
  "typecheck": "tsc --noEmit",
70
- "clean": "rm -rf dist",
71
- "spec:check": "tsx scripts/check-spec-drift.ts",
72
- "spec:update": "tsx scripts/check-spec-drift.ts --update"
65
+ "clean": "rm -rf dist"
73
66
  }
74
67
  }