Skip to content

Commit 63d53cc

Browse files
authored
feat: provide basic prompts (#4)
* feat: provide basic prompts * chore: update * chore: fix build * chore: update lock
1 parent c90bb7f commit 63d53cc

File tree

4 files changed

+238
-1
lines changed

4 files changed

+238
-1
lines changed

packages/nuxt-mcp/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"peerDependencies": {
3333
"nitropack": "^2 || ^3",
3434
"nuxi": ">=3",
35-
"nuxt": ">=3.5.0"
35+
"nuxt": ">=3.5.0",
36+
"vite": ">=6"
3637
},
3738
"dependencies": {
3839
"@modelcontextprotocol/sdk": "catalog:prod",

packages/nuxt-mcp/src/module.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { McpToolContext } from './types'
44
import { addVitePlugin, defineNuxtModule } from '@nuxt/kit'
55
import { ViteMcp } from 'vite-plugin-mcp'
66
import { version } from '../package.json'
7+
import { promptNuxtBasic } from './prompts/basic'
78
import { toolsNuxtDotComInfo } from './tools/nuxt-dot-com'
89
import { toolsNuxtRuntime } from './tools/runtime'
910
import { toolsScaffold } from './tools/scaffold'
@@ -70,10 +71,13 @@ export default defineNuxtModule<ModuleOptions>({
7071
mcp,
7172
}
7273

74+
promptNuxtBasic(context)
7375
toolsNuxtRuntime(context)
7476
toolsNuxtDotComInfo(context)
7577
toolsScaffold(context)
7678

79+
// eslint-disable-next-line ts/ban-ts-comment
80+
// @ts-ignore skip type infer
7781
await nuxt.callHook('mcp:setup', context)
7882
},
7983
}), { client: true })
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
import type { McpToolContext } from '../types'
2+
3+
export function promptNuxtBasic({ mcp, nuxt }: McpToolContext): void {
4+
mcp.prompt('nuxt-basic', () => {
5+
const lines: string[] = []
6+
7+
lines.push(`
8+
You are a professional developer specializing in Nuxt and Vue.
9+
10+
This nuxt project is configured with following structure:
11+
- Root directory: ${nuxt.options.rootDir}
12+
- App directory: ${nuxt.options.appDir}
13+
- Source code directory: ${nuxt.options.srcDir}
14+
- Server directory: ${nuxt.options.serverDir}
15+
16+
`)
17+
18+
// TODO: components, typescript, tailwind, unocss
19+
20+
if (nuxt.options.ssr) {
21+
lines.push(`
22+
## SSR
23+
24+
This is a Nuxt 3 application with SSR enabled. Components should be isomorphic and can be executed on both server or client side.
25+
26+
In scenarios where you need different logic for server and client, use \`import.meta.client\` and \`import.meta.server\`
27+
to branch the code, and use dynamic imports if needed.
28+
`)
29+
}
30+
else {
31+
lines.push(`
32+
## CSR
33+
34+
This is a Nuxt 3 application with SSR disabled. While components are primarily rendered on the client side, still try to make the code as isomorphic as possible to be future-proof.
35+
`)
36+
}
37+
38+
if (nuxt.options.imports.autoImport) {
39+
lines.push(`
40+
## Auto-imports
41+
42+
This Nuxt project is configured have auto-imports enabled.
43+
44+
For example, Vue apis (ref, computed, watch, etc.) are auto-imported, so you can directly use them in your code without import statements.
45+
46+
You can find the full auto-import items with tool \`list-nuxt-auto-imports-items\`.
47+
`)
48+
}
49+
else {
50+
lines.push(`
51+
## No auto-imports
52+
53+
This Nuxt project is configured have auto-imports disabled.
54+
55+
You can still reference the registered entries with tool \`list-nuxt-auto-imports-items\`.
56+
But you need to always manually import the usages when modifying the code, either directly import the item from their source,
57+
or use explicit \`import { xxx } from '#imports'\` to import any items from the registry.
58+
`)
59+
}
60+
61+
// Referene prompts from https://github.com/antfu/nuxt-mcp/issues/2 by @DannyVogel
62+
// lines.push(`
63+
// - This is a Nuxt 3 application using the Composition API with <script setup> syntax.
64+
// - Leverage Nuxt 3's auto-imports system:
65+
// - Vue features (ref, computed, watch, etc.) are auto-imported
66+
// - Components in the /components directory are auto-imported
67+
// - Composables in the /composables directory are auto-imported
68+
// - Utils in the /utils directory are auto-imported
69+
// - Never manually import these items
70+
// - For data fetching:
71+
// - Always use useFetch or useAsyncData at the top level of <script setup> (never inside functions or lifecycle hooks)
72+
// - Always destructure and use the status property (not the deprecated pending property)
73+
// - Status values are: 'idle', 'pending', 'success', or 'error'
74+
// - Use $fetch for imperative API calls within methods or event handlers
75+
// - useAsyncData example:
76+
// <script setup>
77+
// const { data, status, error, refresh, clear } = await useAsyncData(
78+
// 'mountains',
79+
// () => $fetch('https://api.nuxtjs.dev/mountains')
80+
// )
81+
// </script>
82+
// - Always handle all data fetching states in templates:
83+
// <template>
84+
// <div v-if="status === 'pending'">Loading...</div>
85+
// <div v-else-if="status === 'error'">Error: {{ error }}</div>
86+
// <div v-else>{{ data }}</div>
87+
// </template>
88+
89+
// - Follow component best practices:
90+
91+
// - Create small, focused components for specific tasks
92+
// - Use defineModel for two-way binding between parent and child components
93+
// - Use props for passing data down to child components
94+
// - Use emits for communicating events up to parent components
95+
// - Use composables for shared state and logic across components
96+
// - Use Pinia for global state management
97+
// - Consider provide/inject only for specific cases like theme providers or deeply nested component trees
98+
99+
// - Always structure Vue Single File Components in this exact order:
100+
101+
// 1. <script setup> section first
102+
// 2. <template> section second
103+
// 3. <style> section last
104+
105+
// - When styling Vue components:
106+
107+
// - If you see Tailwind being used in a file, use Tailwind for styling.
108+
// - Prioritize Tailwind utility classes in the template for most styling needs.
109+
// - Only use the <style> section for CSS properties that Tailwind doesn't support well, such as:
110+
// - Complex animations and transitions
111+
// - Advanced selectors and pseudo-elements
112+
// - Custom scrollbar styling
113+
// - CSS variables for dynamic theming
114+
// - Keyframe animations
115+
116+
// - Accessibility:
117+
118+
// - Ensure proper ARIA attributes on interactive elements
119+
// - Maintain keyboard navigation support
120+
// - Use semantic HTML elements appropriately
121+
122+
// - For component props and emits:
123+
124+
// - Always use TypeScript interfaces or type aliases with defineProps and defineEmits
125+
// - Define complex types in separate files within the /types directory
126+
// - Use the withDefaults helper for props with default values
127+
// - Mark optional props with the ? symbol
128+
// - Required props should not have the ? symbol
129+
130+
// Example:
131+
132+
// \`\`\`ts
133+
// // In types/card.ts
134+
// export type CardProps = {
135+
// title: string;
136+
// description: string;
137+
// image?: string;
138+
// variant?: "primary" | "secondary";
139+
// };
140+
141+
// // In the component
142+
// import type { CardProps } from "~/types/card";
143+
144+
// const props = withDefaults(defineProps<CardProps>(), {
145+
// image: "/default.png",
146+
// variant: "primary",
147+
// });
148+
149+
// const emit = defineEmits<{
150+
// "update:selected": [value: boolean];
151+
// click: [event: MouseEvent];
152+
// }>();
153+
// \`\`\`
154+
155+
// - Transitions and animations:
156+
157+
// * Use Vue's built-in \`<Transition>\` and \`<TransitionGroup>\` components
158+
// * Combine with CSS classes for complex animations (apply Tailwind if available and possible)
159+
160+
// - Vue-specific TypeScript patterns:
161+
162+
// - Type ref() values explicitly when TypeScript can't infer correctly: ref<string>('')
163+
// - Type event handlers with appropriate event types (MouseEvent, KeyboardEvent, etc.)
164+
// - Use PropType for complex prop types in runtime declarations
165+
// - Return explicitly typed objects from composables
166+
// - Use generics for reusable composables that work with different data types
167+
168+
// ## TypeScript best practices:
169+
170+
// - Create dedicated type files in the /types directory organized by domain
171+
// - Use namespaces or barrel exports (index.ts) to organize related types
172+
// - Define API response types that match your backend contracts
173+
// - Use readonly modifiers for immutable properties
174+
// - Use Record<K, V> instead of {[key: string]: T} for better type safety
175+
// - Avoid using type assertions (as Type) whenever possible
176+
// - Never use "as any" as it defeats TypeScript's type checking
177+
// - Instead of type casting, prefer:
178+
// - Type guards (if (typeof x === 'string') or custom is\* functions)
179+
// - Type narrowing with instanceof, in operators, or discriminated unions
180+
// - Proper generic types to maintain type information throughout the code
181+
// - Only use type assertions when:
182+
// - You have more information than TypeScript can determine
183+
// - Working with external libraries with incomplete type definitions
184+
// - After validating the type with runtime checks
185+
// - When needed, prefer "as unknown as Type" over direct "as Type" for safer casting
186+
// - Consider using type predicates (user is User) for custom type guards
187+
// - Code should be self-documenting; limit the use of comments
188+
189+
// ## Directory Structure
190+
191+
// - All TypeScript interfaces, types, and enums must be defined in dedicated files within the \`/types\` directory
192+
// - Types should be organized by domain (e.g., \`user.ts\`, \`post.ts\`, \`auth.ts\`)
193+
// - Use barrel exports with an \`index.ts\` file to simplify imports
194+
195+
// ## Naming Conventions
196+
197+
// - Use PascalCase for interface, type, and enum names
198+
// - Use singular nouns for entity types (e.g., \`Post\` not \`Posts\`)
199+
// - Suffix interfaces representing props with \`Props\` (e.g., \`ButtonProps\`)
200+
// - Suffix interfaces representing state with \`State\` (e.g., \`AuthState\`)
201+
202+
// ## Import Pattern
203+
204+
// - Always use named imports with the \`type\` keyword: \`import type { Post } from '~/types'\`
205+
// - Import from the barrel file when possible: \`import type { Post, User } from '~/types'\`
206+
// - Only import directly from specific files when the type is not exported in the barrel file
207+
208+
// ## Type Definitions
209+
210+
// - Keep interfaces focused and cohesive - one interface per concept
211+
// - Use composition over inheritance (prefer interface extension over class inheritance)
212+
// - Document complex types with JSDoc comments when necessary
213+
// - Use TypeScript utility types (Partial, Pick, Omit, etc.) to derive types from base interfaces
214+
// `,
215+
// )
216+
217+
return {
218+
messages: [
219+
{
220+
role: 'user',
221+
content: {
222+
type: 'text',
223+
text: lines.join('\n'),
224+
},
225+
},
226+
],
227+
}
228+
})
229+
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)