capy-mcp 1.0.0 → 1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anant Singhal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # capy-mcp
2
+
3
+ `capy-mcp` is a stdio MCP server that inspects a React or Next.js repo and returns a structured brief for building or updating a local `/preview` route.
4
+
5
+ It does not generate the preview page itself. It gives an AI coding agent the repo map, inspection order, constraints, warnings, and target preview file so the agent can implement `/preview` in the host app.
6
+
7
+ ## What it exposes
8
+
9
+ The server currently registers one MCP tool:
10
+
11
+ - `get_preview_brief`
12
+
13
+ The tool accepts:
14
+
15
+ - `task`: `"build_preview"` or `"update_preview"`
16
+ - `changedFiles?`: a list of changed files to bias incremental updates
17
+ - `userGoal?`: the end-user request so the brief stays aligned with the task
18
+
19
+ The tool returns structured content with:
20
+
21
+ - `project_facts`
22
+ - `inspection_plan`
23
+ - `constraints`
24
+ - `deliverable_spec`
25
+ - `update_strategy`
26
+ - `warnings`
27
+ - `instructions`
28
+
29
+ ## Install
30
+
31
+ ```bash
32
+ npm install capy-mcp
33
+ ```
34
+
35
+ Run it directly:
36
+
37
+ ```bash
38
+ npx capy-mcp
39
+ ```
40
+
41
+ ## MCP config
42
+
43
+ Example stdio MCP config:
44
+
45
+ ```json
46
+ {
47
+ "mcpServers": {
48
+ "capy": {
49
+ "command": "npx",
50
+ "args": ["-y", "capy-mcp"]
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ## Library usage
57
+
58
+ ```ts
59
+ import { buildPreviewBrief, detectFramework } from "capy-mcp";
60
+
61
+ const framework = await detectFramework(process.cwd());
62
+ const brief = await buildPreviewBrief(process.cwd(), {
63
+ task: "build_preview",
64
+ });
65
+ ```
66
+
67
+ ## Local development
68
+
69
+ ```bash
70
+ npm install
71
+ npm test
72
+ npx capy-mcp
73
+ ```
74
+
75
+ ## Publish checklist
76
+
77
+ ```bash
78
+ npm test
79
+ npm publish
80
+ ```
81
+
82
+ If `capy-mcp` is not owned by your npm account, rename the package before publishing.
@@ -0,0 +1,6 @@
1
+ import type { PreviewBrief } from "./types.js";
2
+ export declare function buildPreviewBrief(projectRoot: string, input: {
3
+ task: "build_preview" | "update_preview";
4
+ changedFiles?: string[];
5
+ userGoal?: string;
6
+ }): Promise<PreviewBrief>;
package/dist/brief.js ADDED
@@ -0,0 +1,189 @@
1
+ import { glob } from "glob";
2
+ import { join } from "path";
3
+ import { detectFramework } from "./framework.js";
4
+ import { fileExists, toPosixPath } from "./files.js";
5
+ const SECTION_ORDER = [
6
+ "Foundations",
7
+ "Colors",
8
+ "Typography",
9
+ "Spacing",
10
+ "Inputs",
11
+ "Actions",
12
+ "Navigation",
13
+ "Data Display",
14
+ "Feedback",
15
+ "Overlays",
16
+ "Feature or Page Sections",
17
+ ];
18
+ export async function buildPreviewBrief(projectRoot, input) {
19
+ const framework = await detectFramework(projectRoot);
20
+ const projectFacts = await buildProjectFacts(projectRoot, framework);
21
+ const warnings = buildWarnings(framework, input.changedFiles);
22
+ return {
23
+ projectFacts,
24
+ inspectionPlan: buildInspectionPlan(projectFacts),
25
+ constraints: buildConstraints(framework),
26
+ deliverableSpec: {
27
+ goal: input.task === "update_preview"
28
+ ? "Update the existing /preview surface incrementally based on the files or areas that changed."
29
+ : "Create a local /preview route that helps humans and agents inspect the app's real UI system quickly.",
30
+ layout: "vertical-scroll",
31
+ allowHorizontalRows: true,
32
+ previewRoute: projectFacts.previewRoute,
33
+ previewEntryFile: projectFacts.previewEntryFile,
34
+ sections: SECTION_ORDER,
35
+ useExistingComponentsFirst: true,
36
+ },
37
+ updateStrategy: buildUpdateStrategy(input.changedFiles),
38
+ warnings,
39
+ instructions: buildInstructions(projectFacts, input),
40
+ };
41
+ }
42
+ async function buildProjectFacts(projectRoot, framework) {
43
+ const componentDirs = await findExistingDirectories(projectRoot, [
44
+ "src/components",
45
+ "src/ui",
46
+ "src/features",
47
+ "components",
48
+ "ui",
49
+ "features",
50
+ ]);
51
+ const pageDirs = await findExistingDirectories(projectRoot, [
52
+ "src/app",
53
+ "app",
54
+ "src/pages",
55
+ "pages",
56
+ "src/routes",
57
+ "routes",
58
+ ]);
59
+ const styleFiles = await findStyleFiles(projectRoot);
60
+ const likelyUiDirs = Array.from(new Set([...componentDirs, ...pageDirs.filter((dir) => !dir.endsWith("/preview"))]));
61
+ return {
62
+ framework: framework.kind,
63
+ routingStyle: framework.routingStyle,
64
+ previewRoute: framework.previewRoute,
65
+ previewEntryFile: framework.previewEntryFile,
66
+ packageManager: framework.packageManager,
67
+ likelyComponentDirs: componentDirs,
68
+ likelyStyleFiles: styleFiles,
69
+ likelyPageDirs: pageDirs,
70
+ likelyUiDirs,
71
+ };
72
+ }
73
+ function buildInspectionPlan(projectFacts) {
74
+ const appShellTargets = uniqueCompact([
75
+ firstMatching(projectFacts.likelyPageDirs, "src/app")
76
+ ? "src/app/layout.tsx"
77
+ : undefined,
78
+ firstMatching(projectFacts.likelyPageDirs, "src/app")
79
+ ? "src/app/page.tsx"
80
+ : undefined,
81
+ firstMatching(projectFacts.likelyPageDirs, "app") ? "app/layout.tsx" : undefined,
82
+ firstMatching(projectFacts.likelyPageDirs, "app") ? "app/page.tsx" : undefined,
83
+ firstMatching(projectFacts.likelyPageDirs, "src/pages") ? "src/pages/_app.tsx" : undefined,
84
+ firstMatching(projectFacts.likelyPageDirs, "pages") ? "pages/_app.tsx" : undefined,
85
+ ]);
86
+ return [
87
+ {
88
+ step: 1,
89
+ action: "Read the app shell and routing entry points first",
90
+ targets: appShellTargets.length > 0 ? appShellTargets : projectFacts.likelyPageDirs,
91
+ reason: "Understand the project structure, routing style, and baseline visual language.",
92
+ },
93
+ {
94
+ step: 2,
95
+ action: "Read global styles and theme sources",
96
+ targets: projectFacts.likelyStyleFiles.slice(0, 8),
97
+ reason: "Extract colors, spacing, typography, layout rules, and theme conventions from the real repo.",
98
+ },
99
+ {
100
+ step: 3,
101
+ action: "Inspect UI/component directories",
102
+ targets: projectFacts.likelyComponentDirs.length > 0 ? projectFacts.likelyComponentDirs : projectFacts.likelyUiDirs,
103
+ reason: "Find existing primitives, composites, and sections before inventing new preview-only UI.",
104
+ },
105
+ {
106
+ step: 4,
107
+ action: "Implement or update the preview route",
108
+ targets: [projectFacts.previewEntryFile],
109
+ reason: "Build a neat, scrollable /preview page that reflects the real app structure.",
110
+ },
111
+ ];
112
+ }
113
+ function buildConstraints(framework) {
114
+ const constraints = [
115
+ "Do not invent colors, spacing, typography, or component APIs before inspecting the repo files listed in inspection_plan.",
116
+ "Build a vertically scrollable preview page.",
117
+ "Use horizontal specimen rows only when they make scanning easier.",
118
+ "Prefer existing components over creating preview-only components.",
119
+ "Keep the page neat, easy to scan, and aligned with the app's current design language.",
120
+ ];
121
+ if (framework.needsConfirmation && framework.confirmationMessage) {
122
+ constraints.push(framework.confirmationMessage);
123
+ }
124
+ return constraints;
125
+ }
126
+ function buildUpdateStrategy(changedFiles) {
127
+ const strategies = [
128
+ "When updating, inspect changed files first and revise only affected preview sections where possible.",
129
+ "If a changed file affects shared foundations, then update every preview section that depends on those foundations.",
130
+ "Do not rebuild the entire preview page unless the current structure is no longer representative of the app.",
131
+ ];
132
+ if (changedFiles && changedFiles.length > 0) {
133
+ strategies.unshift(`Prioritize these changed files: ${changedFiles.join(", ")}`);
134
+ }
135
+ else {
136
+ strategies.unshift("If changed files are not provided, inspect git diff or the user's latest edits before deciding what to update.");
137
+ }
138
+ return strategies;
139
+ }
140
+ function buildWarnings(framework, changedFiles) {
141
+ const warnings = [];
142
+ if (framework.confirmationMessage) {
143
+ warnings.push(framework.confirmationMessage);
144
+ }
145
+ if (!changedFiles || changedFiles.length === 0) {
146
+ warnings.push("No changedFiles were provided. The agent should inspect git diff or recent edits when performing update_preview.");
147
+ }
148
+ return warnings;
149
+ }
150
+ function buildInstructions(projectFacts, input) {
151
+ const lead = input.task === "update_preview"
152
+ ? "Update the existing /preview route incrementally."
153
+ : "Create the /preview route from scratch.";
154
+ const userGoal = input.userGoal ? ` User goal: ${input.userGoal}.` : "";
155
+ return `${lead}${userGoal} Read the app shell first, then global styles, then component directories. After that, implement ${projectFacts.previewEntryFile} as a vertically scrollable preview surface using the repo's real design language and existing components.`;
156
+ }
157
+ async function findExistingDirectories(projectRoot, candidates) {
158
+ const results = [];
159
+ for (const candidate of candidates) {
160
+ if (await fileExists(join(projectRoot, candidate))) {
161
+ results.push(candidate);
162
+ }
163
+ }
164
+ return results;
165
+ }
166
+ async function findStyleFiles(projectRoot) {
167
+ const files = await glob(["**/*.{css,scss,sass,less}"], {
168
+ cwd: projectRoot,
169
+ nodir: true,
170
+ ignore: [
171
+ "**/node_modules/**",
172
+ "**/.next/**",
173
+ "**/dist/**",
174
+ "**/build/**",
175
+ "**/.capy/**",
176
+ "**/coverage/**",
177
+ ],
178
+ });
179
+ return files
180
+ .map((file) => toPosixPath(file))
181
+ .filter((file) => !file.includes("/preview/"))
182
+ .sort();
183
+ }
184
+ function uniqueCompact(values) {
185
+ return Array.from(new Set(values.filter(Boolean)));
186
+ }
187
+ function firstMatching(values, needle) {
188
+ return values.find((value) => value === needle);
189
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { main } from "./server.js";
3
+ main().catch((error) => {
4
+ console.error(error);
5
+ process.exitCode = 1;
6
+ });
@@ -0,0 +1,3 @@
1
+ export declare function fileExists(path: string): Promise<boolean>;
2
+ export declare function readText(path: string): Promise<string | null>;
3
+ export declare function toPosixPath(path: string): string;
package/dist/files.js ADDED
@@ -0,0 +1,21 @@
1
+ import { readFile, stat } from "fs/promises";
2
+ export async function fileExists(path) {
3
+ try {
4
+ await stat(path);
5
+ return true;
6
+ }
7
+ catch {
8
+ return false;
9
+ }
10
+ }
11
+ export async function readText(path) {
12
+ try {
13
+ return await readFile(path, "utf8");
14
+ }
15
+ catch {
16
+ return null;
17
+ }
18
+ }
19
+ export function toPosixPath(path) {
20
+ return path.replace(/\\/g, "/");
21
+ }
@@ -0,0 +1,2 @@
1
+ import type { FrameworkInfo } from "./types.js";
2
+ export declare function detectFramework(projectRoot: string): Promise<FrameworkInfo>;
@@ -0,0 +1,115 @@
1
+ import { join } from "path";
2
+ import { fileExists, readText } from "./files.js";
3
+ export async function detectFramework(projectRoot) {
4
+ const packageJson = await loadPackageJson(projectRoot);
5
+ const deps = {
6
+ ...packageJson?.dependencies,
7
+ ...packageJson?.devDependencies,
8
+ };
9
+ const hasNext = Boolean(deps.next);
10
+ const hasReact = Boolean(deps.react);
11
+ const hasReactRouter = Boolean(deps["react-router-dom"]);
12
+ const srcAppDir = join(projectRoot, "src/app");
13
+ const appDir = join(projectRoot, "app");
14
+ const srcPagesDir = join(projectRoot, "src/pages");
15
+ const pagesDir = join(projectRoot, "pages");
16
+ if (hasNext && (await fileExists(srcAppDir))) {
17
+ return {
18
+ kind: "next-app-router",
19
+ label: "Next.js App Router",
20
+ routingStyle: "app-router",
21
+ previewRoute: "/preview",
22
+ previewEntryFile: "src/app/preview/page.tsx",
23
+ packageManager: detectPackageManager(packageJson?.packageManager),
24
+ needsConfirmation: false,
25
+ };
26
+ }
27
+ if (hasNext && (await fileExists(appDir))) {
28
+ return {
29
+ kind: "next-app-router",
30
+ label: "Next.js App Router",
31
+ routingStyle: "app-router",
32
+ previewRoute: "/preview",
33
+ previewEntryFile: "app/preview/page.tsx",
34
+ packageManager: detectPackageManager(packageJson?.packageManager),
35
+ needsConfirmation: false,
36
+ };
37
+ }
38
+ if (hasNext && (await fileExists(srcPagesDir))) {
39
+ return {
40
+ kind: "next-pages-router",
41
+ label: "Next.js Pages Router",
42
+ routingStyle: "pages-router",
43
+ previewRoute: "/preview",
44
+ previewEntryFile: "src/pages/preview.tsx",
45
+ packageManager: detectPackageManager(packageJson?.packageManager),
46
+ needsConfirmation: false,
47
+ };
48
+ }
49
+ if (hasNext && (await fileExists(pagesDir))) {
50
+ return {
51
+ kind: "next-pages-router",
52
+ label: "Next.js Pages Router",
53
+ routingStyle: "pages-router",
54
+ previewRoute: "/preview",
55
+ previewEntryFile: "pages/preview.tsx",
56
+ packageManager: detectPackageManager(packageJson?.packageManager),
57
+ needsConfirmation: false,
58
+ };
59
+ }
60
+ if (hasReact && hasReactRouter) {
61
+ return {
62
+ kind: "react-router",
63
+ label: "React + React Router",
64
+ routingStyle: "react-router",
65
+ previewRoute: "/preview",
66
+ previewEntryFile: "src/routes/preview.tsx",
67
+ packageManager: detectPackageManager(packageJson?.packageManager),
68
+ needsConfirmation: false,
69
+ };
70
+ }
71
+ if (hasReact) {
72
+ return {
73
+ kind: "react-no-router",
74
+ label: "React without router",
75
+ routingStyle: "none",
76
+ previewRoute: "/preview",
77
+ previewEntryFile: "src/routes/preview.tsx",
78
+ packageManager: detectPackageManager(packageJson?.packageManager),
79
+ needsConfirmation: true,
80
+ confirmationMessage: "This repo looks like React without react-router-dom. The agent should confirm router setup before implementing /preview.",
81
+ };
82
+ }
83
+ return {
84
+ kind: "unknown",
85
+ label: "Unknown framework",
86
+ routingStyle: "unknown",
87
+ previewRoute: "/preview",
88
+ previewEntryFile: "preview-entry-file-unknown",
89
+ packageManager: detectPackageManager(packageJson?.packageManager),
90
+ needsConfirmation: true,
91
+ confirmationMessage: "Framework detection was inconclusive. The agent should inspect routing manually before building /preview.",
92
+ };
93
+ }
94
+ async function loadPackageJson(projectRoot) {
95
+ const raw = await readText(join(projectRoot, "package.json"));
96
+ if (!raw)
97
+ return null;
98
+ try {
99
+ return JSON.parse(raw);
100
+ }
101
+ catch {
102
+ return null;
103
+ }
104
+ }
105
+ function detectPackageManager(packageManager) {
106
+ if (!packageManager)
107
+ return "npm";
108
+ if (packageManager.startsWith("pnpm"))
109
+ return "pnpm";
110
+ if (packageManager.startsWith("yarn"))
111
+ return "yarn";
112
+ if (packageManager.startsWith("bun"))
113
+ return "bun";
114
+ return "npm";
115
+ }
@@ -0,0 +1,4 @@
1
+ export { buildPreviewBrief } from "./brief.js";
2
+ export { detectFramework } from "./framework.js";
3
+ export { createServer, main } from "./server.js";
4
+ export type { DeliverableSpec, FrameworkInfo, FrameworkKind, InspectionStep, PreviewBrief, ProjectFacts, RoutingStyle, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { buildPreviewBrief } from "./brief.js";
2
+ export { detectFramework } from "./framework.js";
3
+ export { createServer, main } from "./server.js";
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ export declare function createServer(projectRoot?: string): McpServer;
4
+ export declare function main(): Promise<void>;
package/dist/server.js ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ import { buildPreviewBrief } from "./brief.js";
6
+ export function createServer(projectRoot = process.cwd()) {
7
+ const server = new McpServer({
8
+ name: "capy",
9
+ version: "0.2.0",
10
+ });
11
+ server.registerTool("get_preview_brief", {
12
+ title: "Get Preview Brief",
13
+ description: "Call this first for any UI preview, design-system, component-catalog, or style-audit task. Returns the repo map, inspection plan, constraints, and exact /preview spec the agent should follow before writing code.",
14
+ inputSchema: {
15
+ task: z
16
+ .enum(["build_preview", "update_preview"])
17
+ .default("build_preview")
18
+ .describe("Whether the agent is creating /preview from scratch or updating it."),
19
+ changedFiles: z
20
+ .array(z.string())
21
+ .optional()
22
+ .describe("Files changed since the previous preview pass, when known."),
23
+ userGoal: z
24
+ .string()
25
+ .optional()
26
+ .describe("The user's request, so the returned instructions can stay aligned."),
27
+ },
28
+ outputSchema: {
29
+ project_facts: z.object({
30
+ framework: z.enum([
31
+ "next-app-router",
32
+ "next-pages-router",
33
+ "react-router",
34
+ "react-no-router",
35
+ "unknown",
36
+ ]),
37
+ routing_style: z.enum([
38
+ "app-router",
39
+ "pages-router",
40
+ "react-router",
41
+ "none",
42
+ "unknown",
43
+ ]),
44
+ preview_route: z.string(),
45
+ preview_entry_file: z.string(),
46
+ package_manager: z.enum(["npm", "pnpm", "yarn", "bun"]),
47
+ likely_component_dirs: z.array(z.string()),
48
+ likely_style_files: z.array(z.string()),
49
+ likely_page_dirs: z.array(z.string()),
50
+ likely_ui_dirs: z.array(z.string()),
51
+ }),
52
+ inspection_plan: z.array(z.object({
53
+ step: z.number(),
54
+ action: z.string(),
55
+ targets: z.array(z.string()),
56
+ reason: z.string(),
57
+ })),
58
+ constraints: z.array(z.string()),
59
+ deliverable_spec: z.object({
60
+ goal: z.string(),
61
+ layout: z.literal("vertical-scroll"),
62
+ allow_horizontal_rows: z.boolean(),
63
+ preview_route: z.string(),
64
+ preview_entry_file: z.string(),
65
+ sections: z.array(z.string()),
66
+ use_existing_components_first: z.boolean(),
67
+ }),
68
+ update_strategy: z.array(z.string()),
69
+ warnings: z.array(z.string()),
70
+ instructions: z.string(),
71
+ },
72
+ }, async ({ task = "build_preview", changedFiles, userGoal }) => {
73
+ const brief = await buildPreviewBrief(projectRoot, {
74
+ task,
75
+ changedFiles,
76
+ userGoal,
77
+ });
78
+ const structuredContent = {
79
+ project_facts: {
80
+ framework: brief.projectFacts.framework,
81
+ routing_style: brief.projectFacts.routingStyle,
82
+ preview_route: brief.projectFacts.previewRoute,
83
+ preview_entry_file: brief.projectFacts.previewEntryFile,
84
+ package_manager: brief.projectFacts.packageManager,
85
+ likely_component_dirs: brief.projectFacts.likelyComponentDirs,
86
+ likely_style_files: brief.projectFacts.likelyStyleFiles,
87
+ likely_page_dirs: brief.projectFacts.likelyPageDirs,
88
+ likely_ui_dirs: brief.projectFacts.likelyUiDirs,
89
+ },
90
+ inspection_plan: brief.inspectionPlan.map((item) => ({
91
+ step: item.step,
92
+ action: item.action,
93
+ targets: item.targets,
94
+ reason: item.reason,
95
+ })),
96
+ constraints: brief.constraints,
97
+ deliverable_spec: {
98
+ goal: brief.deliverableSpec.goal,
99
+ layout: brief.deliverableSpec.layout,
100
+ allow_horizontal_rows: brief.deliverableSpec.allowHorizontalRows,
101
+ preview_route: brief.deliverableSpec.previewRoute,
102
+ preview_entry_file: brief.deliverableSpec.previewEntryFile,
103
+ sections: brief.deliverableSpec.sections,
104
+ use_existing_components_first: brief.deliverableSpec.useExistingComponentsFirst,
105
+ },
106
+ update_strategy: brief.updateStrategy,
107
+ warnings: brief.warnings,
108
+ instructions: brief.instructions,
109
+ };
110
+ return {
111
+ content: [
112
+ {
113
+ type: "text",
114
+ text: JSON.stringify(structuredContent, null, 2),
115
+ },
116
+ ],
117
+ structuredContent,
118
+ };
119
+ });
120
+ return server;
121
+ }
122
+ export async function main() {
123
+ const server = createServer();
124
+ const transport = new StdioServerTransport();
125
+ await server.connect(transport);
126
+ }
@@ -0,0 +1,47 @@
1
+ export type FrameworkKind = "next-app-router" | "next-pages-router" | "react-router" | "react-no-router" | "unknown";
2
+ export type RoutingStyle = "app-router" | "pages-router" | "react-router" | "none" | "unknown";
3
+ export interface FrameworkInfo {
4
+ kind: FrameworkKind;
5
+ label: string;
6
+ routingStyle: RoutingStyle;
7
+ previewRoute: string;
8
+ previewEntryFile: string;
9
+ packageManager: "npm" | "pnpm" | "yarn" | "bun";
10
+ needsConfirmation: boolean;
11
+ confirmationMessage?: string;
12
+ }
13
+ export interface ProjectFacts {
14
+ framework: FrameworkKind;
15
+ routingStyle: RoutingStyle;
16
+ previewRoute: string;
17
+ previewEntryFile: string;
18
+ packageManager: FrameworkInfo["packageManager"];
19
+ likelyComponentDirs: string[];
20
+ likelyStyleFiles: string[];
21
+ likelyPageDirs: string[];
22
+ likelyUiDirs: string[];
23
+ }
24
+ export interface InspectionStep {
25
+ step: number;
26
+ action: string;
27
+ targets: string[];
28
+ reason: string;
29
+ }
30
+ export interface DeliverableSpec {
31
+ goal: string;
32
+ layout: "vertical-scroll";
33
+ allowHorizontalRows: boolean;
34
+ previewRoute: string;
35
+ previewEntryFile: string;
36
+ sections: string[];
37
+ useExistingComponentsFirst: boolean;
38
+ }
39
+ export interface PreviewBrief {
40
+ projectFacts: ProjectFacts;
41
+ inspectionPlan: InspectionStep[];
42
+ constraints: string[];
43
+ deliverableSpec: DeliverableSpec;
44
+ updateStrategy: string[];
45
+ warnings: string[];
46
+ instructions: string;
47
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,11 +1,61 @@
1
1
  {
2
2
  "name": "capy-mcp",
3
- "version": "1.0.0",
4
- "description": "Capy MCP - Coming soon",
3
+ "version": "1.0.1",
4
+ "description": "MCP server that inspects a repo and returns a structured /preview brief for AI coding agents",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
5
8
  "bin": {
6
- "capy": "./bin/cli.js"
9
+ "capy-mcp": "dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./dist/server.d.ts",
18
+ "import": "./dist/server.js"
19
+ }
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "dev": "tsc --watch",
24
+ "test": "npm run build && node --test test/*.test.mjs",
25
+ "prepack": "npm run build",
26
+ "prepublishOnly": "npm test"
7
27
  },
8
28
  "engines": {
9
29
  "node": ">=18"
10
- }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "keywords": [
35
+ "mcp",
36
+ "model-context-protocol",
37
+ "ai",
38
+ "nextjs",
39
+ "react",
40
+ "preview"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/GithubAnant/capy.git",
45
+ "directory": "capy-mcp"
46
+ },
47
+ "bugs": {
48
+ "url": "https://github.com/GithubAnant/capy/issues"
49
+ },
50
+ "homepage": "https://github.com/GithubAnant/capy/tree/main/capy-mcp",
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "^1.12.1",
53
+ "glob": "^11.0.1",
54
+ "zod": "^3.24.2"
55
+ },
56
+ "devDependencies": {
57
+ "@types/node": "^22.13.10",
58
+ "typescript": "^5.8.2"
59
+ },
60
+ "license": "MIT"
11
61
  }
package/bin/cli.js DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- console.log("capy-cli coming soon!");