@tanstack/cli 0.0.2 → 0.0.4
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/dist/bin.cjs +293 -2
- package/dist/bin.mjs +293 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +17 -17
- package/dist/index.d.mts +17 -17
- package/dist/index.mjs +1 -1
- package/dist/{template-BYKvtZrT.cjs → template-B-i2qO8E.cjs} +83 -34
- package/dist/{template-2SxOxCJc.mjs → template-BgEATWaG.mjs} +83 -34
- package/package.json +1 -1
- package/src/api/fetch.test.ts +3 -3
- package/src/api/fetch.ts +65 -37
- package/src/cache/index.ts +89 -0
- package/src/commands/create.ts +1 -1
- package/src/commands/mcp.test.ts +3 -3
- package/src/commands/mcp.ts +5 -1
- package/src/engine/compile-with-addons.test.ts +7 -7
- package/src/engine/compile.test.ts +1 -1
- package/src/engine/compile.ts +6 -6
- package/src/engine/custom-addons/shared.ts +1 -1
- package/src/engine/template.test.ts +1 -1
- package/src/engine/template.ts +1 -1
- package/src/engine/types.ts +6 -4
- package/src/mcp/api.ts +42 -0
- package/src/mcp/tools.ts +323 -0
- package/src/mcp/types.ts +46 -0
- package/src/templates/base.ts +3 -0
package/dist/bin.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const require_template = require('./template-
|
|
2
|
+
const require_template = require('./template-B-i2qO8E.cjs');
|
|
3
3
|
let node_fs = require("node:fs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
let zod = require("zod");
|
|
@@ -309,6 +309,296 @@ async function runCreate(projectName, options) {
|
|
|
309
309
|
for (const warning of output.warnings) _clack_prompts.log.warning(warning);
|
|
310
310
|
}
|
|
311
311
|
|
|
312
|
+
//#endregion
|
|
313
|
+
//#region src/mcp/types.ts
|
|
314
|
+
const LibrarySchema = zod.z.object({
|
|
315
|
+
id: zod.z.string(),
|
|
316
|
+
name: zod.z.string(),
|
|
317
|
+
tagline: zod.z.string(),
|
|
318
|
+
description: zod.z.string().optional(),
|
|
319
|
+
frameworks: zod.z.array(zod.z.string()),
|
|
320
|
+
latestVersion: zod.z.string(),
|
|
321
|
+
latestBranch: zod.z.string().optional(),
|
|
322
|
+
availableVersions: zod.z.array(zod.z.string()),
|
|
323
|
+
repo: zod.z.string(),
|
|
324
|
+
docsRoot: zod.z.string().optional(),
|
|
325
|
+
defaultDocs: zod.z.string().optional(),
|
|
326
|
+
docsUrl: zod.z.string().optional(),
|
|
327
|
+
githubUrl: zod.z.string().optional()
|
|
328
|
+
});
|
|
329
|
+
const LibrariesResponseSchema = zod.z.object({
|
|
330
|
+
libraries: zod.z.array(LibrarySchema),
|
|
331
|
+
groups: zod.z.record(zod.z.array(zod.z.string())),
|
|
332
|
+
groupNames: zod.z.record(zod.z.string())
|
|
333
|
+
});
|
|
334
|
+
const PartnerSchema = zod.z.object({
|
|
335
|
+
id: zod.z.string(),
|
|
336
|
+
name: zod.z.string(),
|
|
337
|
+
tagline: zod.z.string().optional(),
|
|
338
|
+
description: zod.z.string(),
|
|
339
|
+
category: zod.z.string(),
|
|
340
|
+
categoryLabel: zod.z.string(),
|
|
341
|
+
libraries: zod.z.array(zod.z.string()),
|
|
342
|
+
url: zod.z.string()
|
|
343
|
+
});
|
|
344
|
+
const PartnersResponseSchema = zod.z.object({
|
|
345
|
+
partners: zod.z.array(PartnerSchema),
|
|
346
|
+
categories: zod.z.array(zod.z.string()),
|
|
347
|
+
categoryLabels: zod.z.record(zod.z.string())
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
//#endregion
|
|
351
|
+
//#region src/mcp/api.ts
|
|
352
|
+
const TANSTACK_API_BASE = "https://tanstack.com/api/data";
|
|
353
|
+
async function fetchLibraries() {
|
|
354
|
+
const response = await fetch(`${TANSTACK_API_BASE}/libraries`);
|
|
355
|
+
if (!response.ok) throw new Error(`Failed to fetch libraries: ${response.statusText}`);
|
|
356
|
+
const data = await response.json();
|
|
357
|
+
return LibrariesResponseSchema.parse(data);
|
|
358
|
+
}
|
|
359
|
+
async function fetchPartners() {
|
|
360
|
+
const response = await fetch(`${TANSTACK_API_BASE}/partners`);
|
|
361
|
+
if (!response.ok) throw new Error(`Failed to fetch partners: ${response.statusText}`);
|
|
362
|
+
const data = await response.json();
|
|
363
|
+
return PartnersResponseSchema.parse(data);
|
|
364
|
+
}
|
|
365
|
+
async function fetchDocContent(repo, branch, filePath) {
|
|
366
|
+
const url = `https://raw.githubusercontent.com/${repo}/${branch}/${filePath}`;
|
|
367
|
+
const response = await fetch(url, { headers: { "User-Agent": "tanstack-cli" } });
|
|
368
|
+
if (!response.ok) {
|
|
369
|
+
if (response.status === 404) return null;
|
|
370
|
+
throw new Error(`Failed to fetch doc: ${response.statusText}`);
|
|
371
|
+
}
|
|
372
|
+
return response.text();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/mcp/tools.ts
|
|
377
|
+
const ALGOLIA_APP_ID = "FQ0DQ6MA3C";
|
|
378
|
+
const ALGOLIA_API_KEY = "10c34d6a5c89f6048cf644d601e65172";
|
|
379
|
+
const ALGOLIA_INDEX = "tanstack-test";
|
|
380
|
+
const GROUP_KEYS = [
|
|
381
|
+
"state",
|
|
382
|
+
"headlessUI",
|
|
383
|
+
"performance",
|
|
384
|
+
"tooling"
|
|
385
|
+
];
|
|
386
|
+
function jsonResult(data) {
|
|
387
|
+
return { content: [{
|
|
388
|
+
type: "text",
|
|
389
|
+
text: JSON.stringify(data, null, 2)
|
|
390
|
+
}] };
|
|
391
|
+
}
|
|
392
|
+
function errorResult(error) {
|
|
393
|
+
return {
|
|
394
|
+
content: [{
|
|
395
|
+
type: "text",
|
|
396
|
+
text: `Error: ${error}`
|
|
397
|
+
}],
|
|
398
|
+
isError: true
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function registerDocTools(server) {
|
|
402
|
+
server.tool("tanstack_list_libraries", "List TanStack libraries with metadata, frameworks, and docs URLs.", { group: zod.z.enum(GROUP_KEYS).optional().describe("Filter libraries by group. Options: state, headlessUI, performance, tooling") }, async ({ group }) => {
|
|
403
|
+
try {
|
|
404
|
+
const data = await fetchLibraries();
|
|
405
|
+
let libraries = data.libraries;
|
|
406
|
+
if (group && data.groups[group]) {
|
|
407
|
+
const groupIds = data.groups[group];
|
|
408
|
+
libraries = libraries.filter((lib) => groupIds.includes(lib.id));
|
|
409
|
+
}
|
|
410
|
+
return jsonResult({
|
|
411
|
+
group: group ? data.groupNames[group] || group : "All Libraries",
|
|
412
|
+
count: libraries.length,
|
|
413
|
+
libraries: libraries.map((lib) => ({
|
|
414
|
+
id: lib.id,
|
|
415
|
+
name: lib.name,
|
|
416
|
+
tagline: lib.tagline,
|
|
417
|
+
description: lib.description,
|
|
418
|
+
frameworks: lib.frameworks,
|
|
419
|
+
latestVersion: lib.latestVersion,
|
|
420
|
+
docsUrl: lib.docsUrl,
|
|
421
|
+
githubUrl: lib.githubUrl
|
|
422
|
+
}))
|
|
423
|
+
});
|
|
424
|
+
} catch (error) {
|
|
425
|
+
return errorResult(String(error));
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
server.tool("tanstack_doc", "Fetch a TanStack documentation page by library and path.", {
|
|
429
|
+
library: zod.z.string().describe("Library ID (e.g., query, router, table, form)"),
|
|
430
|
+
path: zod.z.string().describe("Documentation path (e.g., framework/react/overview)"),
|
|
431
|
+
version: zod.z.string().optional().describe("Version (e.g., v5, v1). Defaults to latest")
|
|
432
|
+
}, async ({ library: libraryId, path, version = "latest" }) => {
|
|
433
|
+
try {
|
|
434
|
+
const library = (await fetchLibraries()).libraries.find((l) => l.id === libraryId);
|
|
435
|
+
if (!library) return errorResult(`Library "${libraryId}" not found. Use tanstack_list_libraries to see available libraries.`);
|
|
436
|
+
if (version !== "latest" && !library.availableVersions.includes(version)) return errorResult(`Version "${version}" not found for ${library.name}. Available: ${library.availableVersions.join(", ")}`);
|
|
437
|
+
const branch = version === "latest" || version === library.latestVersion ? library.latestBranch || "main" : version;
|
|
438
|
+
const filePath = `${library.docsRoot || "docs"}/${path}.md`;
|
|
439
|
+
const content = await fetchDocContent(library.repo, branch, filePath);
|
|
440
|
+
if (!content) return errorResult(`Document not found: ${library.name} / ${path} (version: ${version})`);
|
|
441
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
442
|
+
let title = path.split("/").pop() || "Untitled";
|
|
443
|
+
let docContent = content;
|
|
444
|
+
if (frontmatterMatch && frontmatterMatch[1]) {
|
|
445
|
+
const titleMatch = frontmatterMatch[1].match(/title:\s*['"]?([^'"\n]+)['"]?/);
|
|
446
|
+
if (titleMatch && titleMatch[1]) title = titleMatch[1];
|
|
447
|
+
docContent = content.slice(frontmatterMatch[0].length).trim();
|
|
448
|
+
}
|
|
449
|
+
return jsonResult({
|
|
450
|
+
title,
|
|
451
|
+
content: docContent,
|
|
452
|
+
url: `https://tanstack.com/${libraryId}/${version}/docs/${path}`,
|
|
453
|
+
library: library.name,
|
|
454
|
+
version: version === "latest" ? library.latestVersion : version
|
|
455
|
+
});
|
|
456
|
+
} catch (error) {
|
|
457
|
+
return errorResult(String(error));
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
server.tool("tanstack_search_docs", "Search TanStack documentation. Returns matching pages with snippets.", {
|
|
461
|
+
query: zod.z.string().describe("Search query"),
|
|
462
|
+
library: zod.z.string().optional().describe("Filter to specific library (e.g., query, router)"),
|
|
463
|
+
framework: zod.z.string().optional().describe("Filter to specific framework (e.g., react, vue, solid)"),
|
|
464
|
+
limit: zod.z.number().min(1).max(50).optional().describe("Maximum number of results (default: 10, max: 50)")
|
|
465
|
+
}, async ({ query, library, framework, limit = 10 }) => {
|
|
466
|
+
try {
|
|
467
|
+
const ALL_LIBRARIES = [
|
|
468
|
+
"config",
|
|
469
|
+
"form",
|
|
470
|
+
"optimistic",
|
|
471
|
+
"pacer",
|
|
472
|
+
"query",
|
|
473
|
+
"ranger",
|
|
474
|
+
"react-charts",
|
|
475
|
+
"router",
|
|
476
|
+
"start",
|
|
477
|
+
"store",
|
|
478
|
+
"table",
|
|
479
|
+
"virtual",
|
|
480
|
+
"db",
|
|
481
|
+
"devtools"
|
|
482
|
+
];
|
|
483
|
+
const ALL_FRAMEWORKS = [
|
|
484
|
+
"react",
|
|
485
|
+
"vue",
|
|
486
|
+
"solid",
|
|
487
|
+
"svelte",
|
|
488
|
+
"angular"
|
|
489
|
+
];
|
|
490
|
+
const filterParts = ["version:latest"];
|
|
491
|
+
if (library) {
|
|
492
|
+
const exclusions = ALL_LIBRARIES.filter((l) => l !== library).map((l) => `NOT library:${l}`).join(" AND ");
|
|
493
|
+
if (exclusions) filterParts.push(`(${exclusions})`);
|
|
494
|
+
}
|
|
495
|
+
if (framework) {
|
|
496
|
+
const exclusions = ALL_FRAMEWORKS.filter((f) => f !== framework).map((f) => `NOT framework:${f}`).join(" AND ");
|
|
497
|
+
if (exclusions) filterParts.push(`(${exclusions})`);
|
|
498
|
+
}
|
|
499
|
+
const searchParams = { requests: [{
|
|
500
|
+
indexName: ALGOLIA_INDEX,
|
|
501
|
+
query,
|
|
502
|
+
hitsPerPage: Math.min(limit, 50),
|
|
503
|
+
filters: filterParts.join(" AND "),
|
|
504
|
+
attributesToRetrieve: [
|
|
505
|
+
"hierarchy",
|
|
506
|
+
"url",
|
|
507
|
+
"content",
|
|
508
|
+
"library"
|
|
509
|
+
],
|
|
510
|
+
attributesToSnippet: ["content:80"]
|
|
511
|
+
}] };
|
|
512
|
+
const response = await fetch(`https://${ALGOLIA_APP_ID}-dsn.algolia.net/1/indexes/*/queries`, {
|
|
513
|
+
method: "POST",
|
|
514
|
+
headers: {
|
|
515
|
+
"Content-Type": "application/json",
|
|
516
|
+
"X-Algolia-Application-Id": ALGOLIA_APP_ID,
|
|
517
|
+
"X-Algolia-API-Key": ALGOLIA_API_KEY
|
|
518
|
+
},
|
|
519
|
+
body: JSON.stringify(searchParams)
|
|
520
|
+
});
|
|
521
|
+
if (!response.ok) return errorResult(`Algolia search failed: ${response.statusText}`);
|
|
522
|
+
const searchResult = (await response.json()).results[0];
|
|
523
|
+
if (!searchResult) return jsonResult({
|
|
524
|
+
query,
|
|
525
|
+
totalHits: 0,
|
|
526
|
+
results: []
|
|
527
|
+
});
|
|
528
|
+
const results = searchResult.hits.map((hit) => {
|
|
529
|
+
const breadcrumb = Object.values(hit.hierarchy).filter((v) => Boolean(v));
|
|
530
|
+
return {
|
|
531
|
+
title: hit.hierarchy.lvl1 || hit.hierarchy.lvl0 || "Untitled",
|
|
532
|
+
url: hit.url,
|
|
533
|
+
snippet: hit._snippetResult?.content?.value || hit.content || "",
|
|
534
|
+
library: hit.library || "unknown",
|
|
535
|
+
breadcrumb
|
|
536
|
+
};
|
|
537
|
+
});
|
|
538
|
+
return jsonResult({
|
|
539
|
+
query,
|
|
540
|
+
totalHits: searchResult.nbHits || results.length,
|
|
541
|
+
results
|
|
542
|
+
});
|
|
543
|
+
} catch (error) {
|
|
544
|
+
return errorResult(String(error));
|
|
545
|
+
}
|
|
546
|
+
});
|
|
547
|
+
server.tool("tanstack_ecosystem", "Ecosystem partner recommendations. Filter by category (database, auth, deployment, monitoring, cms, api, data-grid) or library.", {
|
|
548
|
+
category: zod.z.string().optional().describe("Filter by category: database, auth, deployment, monitoring, cms, api, data-grid, code-review, learning"),
|
|
549
|
+
library: zod.z.string().optional().describe("Filter by TanStack library (e.g., start, router, query, table)")
|
|
550
|
+
}, async ({ category, library }) => {
|
|
551
|
+
try {
|
|
552
|
+
const data = await fetchPartners();
|
|
553
|
+
const categoryAliases = {
|
|
554
|
+
db: "database",
|
|
555
|
+
postgres: "database",
|
|
556
|
+
sql: "database",
|
|
557
|
+
login: "auth",
|
|
558
|
+
authentication: "auth",
|
|
559
|
+
hosting: "deployment",
|
|
560
|
+
deploy: "deployment",
|
|
561
|
+
serverless: "deployment",
|
|
562
|
+
errors: "monitoring",
|
|
563
|
+
logging: "monitoring",
|
|
564
|
+
content: "cms",
|
|
565
|
+
"api-keys": "api",
|
|
566
|
+
grid: "data-grid",
|
|
567
|
+
review: "code-review",
|
|
568
|
+
courses: "learning"
|
|
569
|
+
};
|
|
570
|
+
let resolvedCategory;
|
|
571
|
+
if (category) {
|
|
572
|
+
const normalized = category.toLowerCase().trim();
|
|
573
|
+
resolvedCategory = categoryAliases[normalized] || normalized;
|
|
574
|
+
if (!data.categories.includes(resolvedCategory)) resolvedCategory = void 0;
|
|
575
|
+
}
|
|
576
|
+
const lib = library?.toLowerCase().trim();
|
|
577
|
+
const partners = data.partners.filter((p) => !resolvedCategory || p.category === resolvedCategory).filter((p) => !lib || p.libraries.some((l) => l === lib)).map((p) => ({
|
|
578
|
+
id: p.id,
|
|
579
|
+
name: p.name,
|
|
580
|
+
tagline: p.tagline,
|
|
581
|
+
description: p.description,
|
|
582
|
+
category: p.category,
|
|
583
|
+
categoryLabel: p.categoryLabel,
|
|
584
|
+
url: p.url,
|
|
585
|
+
libraries: p.libraries
|
|
586
|
+
}));
|
|
587
|
+
return jsonResult({
|
|
588
|
+
query: {
|
|
589
|
+
category,
|
|
590
|
+
categoryResolved: resolvedCategory,
|
|
591
|
+
library
|
|
592
|
+
},
|
|
593
|
+
count: partners.length,
|
|
594
|
+
partners
|
|
595
|
+
});
|
|
596
|
+
} catch (error) {
|
|
597
|
+
return errorResult(String(error));
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
312
602
|
//#endregion
|
|
313
603
|
//#region src/commands/mcp.ts
|
|
314
604
|
function createServer() {
|
|
@@ -324,7 +614,7 @@ function createServer() {
|
|
|
324
614
|
description: integration.description,
|
|
325
615
|
category: integration.category,
|
|
326
616
|
dependsOn: integration.dependsOn,
|
|
327
|
-
|
|
617
|
+
exclusive: integration.exclusive,
|
|
328
618
|
hasOptions: integration.hasOptions
|
|
329
619
|
}));
|
|
330
620
|
return { content: [{
|
|
@@ -338,6 +628,7 @@ function createServer() {
|
|
|
338
628
|
}] };
|
|
339
629
|
}
|
|
340
630
|
});
|
|
631
|
+
registerDocTools(server);
|
|
341
632
|
server.tool("createTanStackApplication", "Create a new TanStack Start application", {
|
|
342
633
|
projectName: zod.z.string().describe("The name of the project (will be the directory name)"),
|
|
343
634
|
targetDir: zod.z.string().describe("Absolute path where the project should be created"),
|
package/dist/bin.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as writeConfigFile, a as initIntegration, d as fetchManifest, i as compileIntegration, j as compile, n as initTemplate, r as loadTemplate, t as compileTemplate, u as fetchIntegrations } from "./template-
|
|
2
|
+
import { A as writeConfigFile, a as initIntegration, d as fetchManifest, i as compileIntegration, j as compile, n as initTemplate, r as loadTemplate, t as compileTemplate, u as fetchIntegrations } from "./template-BgEATWaG.mjs";
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { z } from "zod";
|
|
@@ -307,6 +307,296 @@ async function runCreate(projectName, options) {
|
|
|
307
307
|
for (const warning of output.warnings) log.warning(warning);
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/mcp/types.ts
|
|
312
|
+
const LibrarySchema = z.object({
|
|
313
|
+
id: z.string(),
|
|
314
|
+
name: z.string(),
|
|
315
|
+
tagline: z.string(),
|
|
316
|
+
description: z.string().optional(),
|
|
317
|
+
frameworks: z.array(z.string()),
|
|
318
|
+
latestVersion: z.string(),
|
|
319
|
+
latestBranch: z.string().optional(),
|
|
320
|
+
availableVersions: z.array(z.string()),
|
|
321
|
+
repo: z.string(),
|
|
322
|
+
docsRoot: z.string().optional(),
|
|
323
|
+
defaultDocs: z.string().optional(),
|
|
324
|
+
docsUrl: z.string().optional(),
|
|
325
|
+
githubUrl: z.string().optional()
|
|
326
|
+
});
|
|
327
|
+
const LibrariesResponseSchema = z.object({
|
|
328
|
+
libraries: z.array(LibrarySchema),
|
|
329
|
+
groups: z.record(z.array(z.string())),
|
|
330
|
+
groupNames: z.record(z.string())
|
|
331
|
+
});
|
|
332
|
+
const PartnerSchema = z.object({
|
|
333
|
+
id: z.string(),
|
|
334
|
+
name: z.string(),
|
|
335
|
+
tagline: z.string().optional(),
|
|
336
|
+
description: z.string(),
|
|
337
|
+
category: z.string(),
|
|
338
|
+
categoryLabel: z.string(),
|
|
339
|
+
libraries: z.array(z.string()),
|
|
340
|
+
url: z.string()
|
|
341
|
+
});
|
|
342
|
+
const PartnersResponseSchema = z.object({
|
|
343
|
+
partners: z.array(PartnerSchema),
|
|
344
|
+
categories: z.array(z.string()),
|
|
345
|
+
categoryLabels: z.record(z.string())
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/mcp/api.ts
|
|
350
|
+
const TANSTACK_API_BASE = "https://tanstack.com/api/data";
|
|
351
|
+
async function fetchLibraries() {
|
|
352
|
+
const response = await fetch(`${TANSTACK_API_BASE}/libraries`);
|
|
353
|
+
if (!response.ok) throw new Error(`Failed to fetch libraries: ${response.statusText}`);
|
|
354
|
+
const data = await response.json();
|
|
355
|
+
return LibrariesResponseSchema.parse(data);
|
|
356
|
+
}
|
|
357
|
+
async function fetchPartners() {
|
|
358
|
+
const response = await fetch(`${TANSTACK_API_BASE}/partners`);
|
|
359
|
+
if (!response.ok) throw new Error(`Failed to fetch partners: ${response.statusText}`);
|
|
360
|
+
const data = await response.json();
|
|
361
|
+
return PartnersResponseSchema.parse(data);
|
|
362
|
+
}
|
|
363
|
+
async function fetchDocContent(repo, branch, filePath) {
|
|
364
|
+
const url = `https://raw.githubusercontent.com/${repo}/${branch}/${filePath}`;
|
|
365
|
+
const response = await fetch(url, { headers: { "User-Agent": "tanstack-cli" } });
|
|
366
|
+
if (!response.ok) {
|
|
367
|
+
if (response.status === 404) return null;
|
|
368
|
+
throw new Error(`Failed to fetch doc: ${response.statusText}`);
|
|
369
|
+
}
|
|
370
|
+
return response.text();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
//#endregion
|
|
374
|
+
//#region src/mcp/tools.ts
|
|
375
|
+
const ALGOLIA_APP_ID = "FQ0DQ6MA3C";
|
|
376
|
+
const ALGOLIA_API_KEY = "10c34d6a5c89f6048cf644d601e65172";
|
|
377
|
+
const ALGOLIA_INDEX = "tanstack-test";
|
|
378
|
+
const GROUP_KEYS = [
|
|
379
|
+
"state",
|
|
380
|
+
"headlessUI",
|
|
381
|
+
"performance",
|
|
382
|
+
"tooling"
|
|
383
|
+
];
|
|
384
|
+
function jsonResult(data) {
|
|
385
|
+
return { content: [{
|
|
386
|
+
type: "text",
|
|
387
|
+
text: JSON.stringify(data, null, 2)
|
|
388
|
+
}] };
|
|
389
|
+
}
|
|
390
|
+
function errorResult(error) {
|
|
391
|
+
return {
|
|
392
|
+
content: [{
|
|
393
|
+
type: "text",
|
|
394
|
+
text: `Error: ${error}`
|
|
395
|
+
}],
|
|
396
|
+
isError: true
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
function registerDocTools(server) {
|
|
400
|
+
server.tool("tanstack_list_libraries", "List TanStack libraries with metadata, frameworks, and docs URLs.", { group: z.enum(GROUP_KEYS).optional().describe("Filter libraries by group. Options: state, headlessUI, performance, tooling") }, async ({ group }) => {
|
|
401
|
+
try {
|
|
402
|
+
const data = await fetchLibraries();
|
|
403
|
+
let libraries = data.libraries;
|
|
404
|
+
if (group && data.groups[group]) {
|
|
405
|
+
const groupIds = data.groups[group];
|
|
406
|
+
libraries = libraries.filter((lib) => groupIds.includes(lib.id));
|
|
407
|
+
}
|
|
408
|
+
return jsonResult({
|
|
409
|
+
group: group ? data.groupNames[group] || group : "All Libraries",
|
|
410
|
+
count: libraries.length,
|
|
411
|
+
libraries: libraries.map((lib) => ({
|
|
412
|
+
id: lib.id,
|
|
413
|
+
name: lib.name,
|
|
414
|
+
tagline: lib.tagline,
|
|
415
|
+
description: lib.description,
|
|
416
|
+
frameworks: lib.frameworks,
|
|
417
|
+
latestVersion: lib.latestVersion,
|
|
418
|
+
docsUrl: lib.docsUrl,
|
|
419
|
+
githubUrl: lib.githubUrl
|
|
420
|
+
}))
|
|
421
|
+
});
|
|
422
|
+
} catch (error) {
|
|
423
|
+
return errorResult(String(error));
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
server.tool("tanstack_doc", "Fetch a TanStack documentation page by library and path.", {
|
|
427
|
+
library: z.string().describe("Library ID (e.g., query, router, table, form)"),
|
|
428
|
+
path: z.string().describe("Documentation path (e.g., framework/react/overview)"),
|
|
429
|
+
version: z.string().optional().describe("Version (e.g., v5, v1). Defaults to latest")
|
|
430
|
+
}, async ({ library: libraryId, path, version = "latest" }) => {
|
|
431
|
+
try {
|
|
432
|
+
const library = (await fetchLibraries()).libraries.find((l) => l.id === libraryId);
|
|
433
|
+
if (!library) return errorResult(`Library "${libraryId}" not found. Use tanstack_list_libraries to see available libraries.`);
|
|
434
|
+
if (version !== "latest" && !library.availableVersions.includes(version)) return errorResult(`Version "${version}" not found for ${library.name}. Available: ${library.availableVersions.join(", ")}`);
|
|
435
|
+
const branch = version === "latest" || version === library.latestVersion ? library.latestBranch || "main" : version;
|
|
436
|
+
const filePath = `${library.docsRoot || "docs"}/${path}.md`;
|
|
437
|
+
const content = await fetchDocContent(library.repo, branch, filePath);
|
|
438
|
+
if (!content) return errorResult(`Document not found: ${library.name} / ${path} (version: ${version})`);
|
|
439
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
440
|
+
let title = path.split("/").pop() || "Untitled";
|
|
441
|
+
let docContent = content;
|
|
442
|
+
if (frontmatterMatch && frontmatterMatch[1]) {
|
|
443
|
+
const titleMatch = frontmatterMatch[1].match(/title:\s*['"]?([^'"\n]+)['"]?/);
|
|
444
|
+
if (titleMatch && titleMatch[1]) title = titleMatch[1];
|
|
445
|
+
docContent = content.slice(frontmatterMatch[0].length).trim();
|
|
446
|
+
}
|
|
447
|
+
return jsonResult({
|
|
448
|
+
title,
|
|
449
|
+
content: docContent,
|
|
450
|
+
url: `https://tanstack.com/${libraryId}/${version}/docs/${path}`,
|
|
451
|
+
library: library.name,
|
|
452
|
+
version: version === "latest" ? library.latestVersion : version
|
|
453
|
+
});
|
|
454
|
+
} catch (error) {
|
|
455
|
+
return errorResult(String(error));
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
server.tool("tanstack_search_docs", "Search TanStack documentation. Returns matching pages with snippets.", {
|
|
459
|
+
query: z.string().describe("Search query"),
|
|
460
|
+
library: z.string().optional().describe("Filter to specific library (e.g., query, router)"),
|
|
461
|
+
framework: z.string().optional().describe("Filter to specific framework (e.g., react, vue, solid)"),
|
|
462
|
+
limit: z.number().min(1).max(50).optional().describe("Maximum number of results (default: 10, max: 50)")
|
|
463
|
+
}, async ({ query, library, framework, limit = 10 }) => {
|
|
464
|
+
try {
|
|
465
|
+
const ALL_LIBRARIES = [
|
|
466
|
+
"config",
|
|
467
|
+
"form",
|
|
468
|
+
"optimistic",
|
|
469
|
+
"pacer",
|
|
470
|
+
"query",
|
|
471
|
+
"ranger",
|
|
472
|
+
"react-charts",
|
|
473
|
+
"router",
|
|
474
|
+
"start",
|
|
475
|
+
"store",
|
|
476
|
+
"table",
|
|
477
|
+
"virtual",
|
|
478
|
+
"db",
|
|
479
|
+
"devtools"
|
|
480
|
+
];
|
|
481
|
+
const ALL_FRAMEWORKS = [
|
|
482
|
+
"react",
|
|
483
|
+
"vue",
|
|
484
|
+
"solid",
|
|
485
|
+
"svelte",
|
|
486
|
+
"angular"
|
|
487
|
+
];
|
|
488
|
+
const filterParts = ["version:latest"];
|
|
489
|
+
if (library) {
|
|
490
|
+
const exclusions = ALL_LIBRARIES.filter((l) => l !== library).map((l) => `NOT library:${l}`).join(" AND ");
|
|
491
|
+
if (exclusions) filterParts.push(`(${exclusions})`);
|
|
492
|
+
}
|
|
493
|
+
if (framework) {
|
|
494
|
+
const exclusions = ALL_FRAMEWORKS.filter((f) => f !== framework).map((f) => `NOT framework:${f}`).join(" AND ");
|
|
495
|
+
if (exclusions) filterParts.push(`(${exclusions})`);
|
|
496
|
+
}
|
|
497
|
+
const searchParams = { requests: [{
|
|
498
|
+
indexName: ALGOLIA_INDEX,
|
|
499
|
+
query,
|
|
500
|
+
hitsPerPage: Math.min(limit, 50),
|
|
501
|
+
filters: filterParts.join(" AND "),
|
|
502
|
+
attributesToRetrieve: [
|
|
503
|
+
"hierarchy",
|
|
504
|
+
"url",
|
|
505
|
+
"content",
|
|
506
|
+
"library"
|
|
507
|
+
],
|
|
508
|
+
attributesToSnippet: ["content:80"]
|
|
509
|
+
}] };
|
|
510
|
+
const response = await fetch(`https://${ALGOLIA_APP_ID}-dsn.algolia.net/1/indexes/*/queries`, {
|
|
511
|
+
method: "POST",
|
|
512
|
+
headers: {
|
|
513
|
+
"Content-Type": "application/json",
|
|
514
|
+
"X-Algolia-Application-Id": ALGOLIA_APP_ID,
|
|
515
|
+
"X-Algolia-API-Key": ALGOLIA_API_KEY
|
|
516
|
+
},
|
|
517
|
+
body: JSON.stringify(searchParams)
|
|
518
|
+
});
|
|
519
|
+
if (!response.ok) return errorResult(`Algolia search failed: ${response.statusText}`);
|
|
520
|
+
const searchResult = (await response.json()).results[0];
|
|
521
|
+
if (!searchResult) return jsonResult({
|
|
522
|
+
query,
|
|
523
|
+
totalHits: 0,
|
|
524
|
+
results: []
|
|
525
|
+
});
|
|
526
|
+
const results = searchResult.hits.map((hit) => {
|
|
527
|
+
const breadcrumb = Object.values(hit.hierarchy).filter((v) => Boolean(v));
|
|
528
|
+
return {
|
|
529
|
+
title: hit.hierarchy.lvl1 || hit.hierarchy.lvl0 || "Untitled",
|
|
530
|
+
url: hit.url,
|
|
531
|
+
snippet: hit._snippetResult?.content?.value || hit.content || "",
|
|
532
|
+
library: hit.library || "unknown",
|
|
533
|
+
breadcrumb
|
|
534
|
+
};
|
|
535
|
+
});
|
|
536
|
+
return jsonResult({
|
|
537
|
+
query,
|
|
538
|
+
totalHits: searchResult.nbHits || results.length,
|
|
539
|
+
results
|
|
540
|
+
});
|
|
541
|
+
} catch (error) {
|
|
542
|
+
return errorResult(String(error));
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
server.tool("tanstack_ecosystem", "Ecosystem partner recommendations. Filter by category (database, auth, deployment, monitoring, cms, api, data-grid) or library.", {
|
|
546
|
+
category: z.string().optional().describe("Filter by category: database, auth, deployment, monitoring, cms, api, data-grid, code-review, learning"),
|
|
547
|
+
library: z.string().optional().describe("Filter by TanStack library (e.g., start, router, query, table)")
|
|
548
|
+
}, async ({ category, library }) => {
|
|
549
|
+
try {
|
|
550
|
+
const data = await fetchPartners();
|
|
551
|
+
const categoryAliases = {
|
|
552
|
+
db: "database",
|
|
553
|
+
postgres: "database",
|
|
554
|
+
sql: "database",
|
|
555
|
+
login: "auth",
|
|
556
|
+
authentication: "auth",
|
|
557
|
+
hosting: "deployment",
|
|
558
|
+
deploy: "deployment",
|
|
559
|
+
serverless: "deployment",
|
|
560
|
+
errors: "monitoring",
|
|
561
|
+
logging: "monitoring",
|
|
562
|
+
content: "cms",
|
|
563
|
+
"api-keys": "api",
|
|
564
|
+
grid: "data-grid",
|
|
565
|
+
review: "code-review",
|
|
566
|
+
courses: "learning"
|
|
567
|
+
};
|
|
568
|
+
let resolvedCategory;
|
|
569
|
+
if (category) {
|
|
570
|
+
const normalized = category.toLowerCase().trim();
|
|
571
|
+
resolvedCategory = categoryAliases[normalized] || normalized;
|
|
572
|
+
if (!data.categories.includes(resolvedCategory)) resolvedCategory = void 0;
|
|
573
|
+
}
|
|
574
|
+
const lib = library?.toLowerCase().trim();
|
|
575
|
+
const partners = data.partners.filter((p) => !resolvedCategory || p.category === resolvedCategory).filter((p) => !lib || p.libraries.some((l) => l === lib)).map((p) => ({
|
|
576
|
+
id: p.id,
|
|
577
|
+
name: p.name,
|
|
578
|
+
tagline: p.tagline,
|
|
579
|
+
description: p.description,
|
|
580
|
+
category: p.category,
|
|
581
|
+
categoryLabel: p.categoryLabel,
|
|
582
|
+
url: p.url,
|
|
583
|
+
libraries: p.libraries
|
|
584
|
+
}));
|
|
585
|
+
return jsonResult({
|
|
586
|
+
query: {
|
|
587
|
+
category,
|
|
588
|
+
categoryResolved: resolvedCategory,
|
|
589
|
+
library
|
|
590
|
+
},
|
|
591
|
+
count: partners.length,
|
|
592
|
+
partners
|
|
593
|
+
});
|
|
594
|
+
} catch (error) {
|
|
595
|
+
return errorResult(String(error));
|
|
596
|
+
}
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
310
600
|
//#endregion
|
|
311
601
|
//#region src/commands/mcp.ts
|
|
312
602
|
function createServer() {
|
|
@@ -322,7 +612,7 @@ function createServer() {
|
|
|
322
612
|
description: integration.description,
|
|
323
613
|
category: integration.category,
|
|
324
614
|
dependsOn: integration.dependsOn,
|
|
325
|
-
|
|
615
|
+
exclusive: integration.exclusive,
|
|
326
616
|
hasOptions: integration.hasOptions
|
|
327
617
|
}));
|
|
328
618
|
return { content: [{
|
|
@@ -336,6 +626,7 @@ function createServer() {
|
|
|
336
626
|
}] };
|
|
337
627
|
}
|
|
338
628
|
});
|
|
629
|
+
registerDocTools(server);
|
|
339
630
|
server.tool("createTanStackApplication", "Create a new TanStack Start application", {
|
|
340
631
|
projectName: z.string().describe("The name of the project (will be the directory name)"),
|
|
341
632
|
targetDir: z.string().describe("Absolute path where the project should be created"),
|
package/dist/index.cjs
CHANGED