nitrostack 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.
Files changed (115) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/package.json +1 -1
  3. package/templates/typescript-auth/.env.example +23 -0
  4. package/templates/typescript-auth/src/app.module.ts +103 -0
  5. package/templates/typescript-auth/src/db/database.ts +163 -0
  6. package/templates/typescript-auth/src/db/seed.ts +374 -0
  7. package/templates/typescript-auth/src/db/setup.ts +87 -0
  8. package/templates/typescript-auth/src/events/analytics.service.ts +52 -0
  9. package/templates/typescript-auth/src/events/notification.service.ts +40 -0
  10. package/templates/typescript-auth/src/filters/global-exception.filter.ts +28 -0
  11. package/templates/typescript-auth/src/guards/README.md +75 -0
  12. package/templates/typescript-auth/src/guards/jwt.guard.ts +105 -0
  13. package/templates/typescript-auth/src/health/database.health.ts +41 -0
  14. package/templates/typescript-auth/src/index.ts +26 -0
  15. package/templates/typescript-auth/src/interceptors/transform.interceptor.ts +24 -0
  16. package/templates/typescript-auth/src/middleware/logging.middleware.ts +42 -0
  17. package/templates/typescript-auth/src/modules/addresses/addresses.module.ts +16 -0
  18. package/templates/typescript-auth/src/modules/addresses/addresses.prompts.ts +114 -0
  19. package/templates/typescript-auth/src/modules/addresses/addresses.resources.ts +40 -0
  20. package/templates/typescript-auth/src/modules/addresses/addresses.tools.ts +241 -0
  21. package/templates/typescript-auth/src/modules/auth/auth.module.ts +16 -0
  22. package/templates/typescript-auth/src/modules/auth/auth.prompts.ts +147 -0
  23. package/templates/typescript-auth/src/modules/auth/auth.resources.ts +84 -0
  24. package/templates/typescript-auth/src/modules/auth/auth.tools.ts +139 -0
  25. package/templates/typescript-auth/src/modules/cart/cart.module.ts +16 -0
  26. package/templates/typescript-auth/src/modules/cart/cart.prompts.ts +95 -0
  27. package/templates/typescript-auth/src/modules/cart/cart.resources.ts +44 -0
  28. package/templates/typescript-auth/src/modules/cart/cart.tools.ts +281 -0
  29. package/templates/typescript-auth/src/modules/orders/orders.module.ts +16 -0
  30. package/templates/typescript-auth/src/modules/orders/orders.prompts.ts +88 -0
  31. package/templates/typescript-auth/src/modules/orders/orders.resources.ts +48 -0
  32. package/templates/typescript-auth/src/modules/orders/orders.tools.ts +281 -0
  33. package/templates/typescript-auth/src/modules/products/products.module.ts +16 -0
  34. package/templates/typescript-auth/src/modules/products/products.prompts.ts +146 -0
  35. package/templates/typescript-auth/src/modules/products/products.resources.ts +98 -0
  36. package/templates/typescript-auth/src/modules/products/products.tools.ts +266 -0
  37. package/templates/typescript-auth/src/pipes/validation.pipe.ts +42 -0
  38. package/templates/typescript-auth/src/services/database.service.ts +90 -0
  39. package/templates/typescript-auth/src/widgets/app/add-to-cart/page.tsx +122 -0
  40. package/templates/typescript-auth/src/widgets/app/address-added/page.tsx +116 -0
  41. package/templates/typescript-auth/src/widgets/app/address-deleted/page.tsx +105 -0
  42. package/templates/typescript-auth/src/widgets/app/address-list/page.tsx +139 -0
  43. package/templates/typescript-auth/src/widgets/app/address-updated/page.tsx +153 -0
  44. package/templates/typescript-auth/src/widgets/app/cart-cleared/page.tsx +86 -0
  45. package/templates/typescript-auth/src/widgets/app/cart-updated/page.tsx +116 -0
  46. package/templates/typescript-auth/src/widgets/app/categories/page.tsx +134 -0
  47. package/templates/typescript-auth/src/widgets/app/layout.tsx +21 -0
  48. package/templates/typescript-auth/src/widgets/app/login-result/page.tsx +129 -0
  49. package/templates/typescript-auth/src/widgets/app/order-confirmation/page.tsx +206 -0
  50. package/templates/typescript-auth/src/widgets/app/order-details/page.tsx +225 -0
  51. package/templates/typescript-auth/src/widgets/app/order-history/page.tsx +218 -0
  52. package/templates/typescript-auth/src/widgets/app/product-card/page.tsx +121 -0
  53. package/templates/typescript-auth/src/widgets/app/products-grid/page.tsx +173 -0
  54. package/templates/typescript-auth/src/widgets/app/shopping-cart/page.tsx +187 -0
  55. package/templates/typescript-auth/src/widgets/app/whoami/page.tsx +165 -0
  56. package/templates/typescript-auth/src/widgets/next.config.js +38 -0
  57. package/templates/typescript-auth/src/widgets/package.json +18 -0
  58. package/templates/typescript-auth/src/widgets/styles/ecommerce.ts +169 -0
  59. package/templates/typescript-auth/src/widgets/tsconfig.json +28 -0
  60. package/templates/typescript-auth/src/widgets/types/tool-data.ts +141 -0
  61. package/templates/typescript-auth/src/widgets/widget-manifest.json +464 -0
  62. package/templates/typescript-auth/tsconfig.json +27 -0
  63. package/templates/typescript-auth-api-key/.env +15 -0
  64. package/templates/typescript-auth-api-key/.env.example +4 -0
  65. package/templates/typescript-auth-api-key/src/app.module.ts +38 -0
  66. package/templates/typescript-auth-api-key/src/guards/apikey.guard.ts +47 -0
  67. package/templates/typescript-auth-api-key/src/guards/multi-auth.guard.ts +157 -0
  68. package/templates/typescript-auth-api-key/src/health/system.health.ts +55 -0
  69. package/templates/typescript-auth-api-key/src/index.ts +47 -0
  70. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.module.ts +12 -0
  71. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.prompts.ts +73 -0
  72. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.resources.ts +60 -0
  73. package/templates/typescript-auth-api-key/src/modules/calculator/calculator.tools.ts +71 -0
  74. package/templates/typescript-auth-api-key/src/modules/demo/demo.module.ts +18 -0
  75. package/templates/typescript-auth-api-key/src/modules/demo/demo.tools.ts +155 -0
  76. package/templates/typescript-auth-api-key/src/modules/demo/multi-auth.tools.ts +123 -0
  77. package/templates/typescript-auth-api-key/src/widgets/app/calculator-operations/page.tsx +133 -0
  78. package/templates/typescript-auth-api-key/src/widgets/app/calculator-result/page.tsx +134 -0
  79. package/templates/typescript-auth-api-key/src/widgets/app/layout.tsx +14 -0
  80. package/templates/typescript-auth-api-key/src/widgets/next.config.js +37 -0
  81. package/templates/typescript-auth-api-key/src/widgets/package.json +24 -0
  82. package/templates/typescript-auth-api-key/src/widgets/tsconfig.json +28 -0
  83. package/templates/typescript-auth-api-key/src/widgets/widget-manifest.json +48 -0
  84. package/templates/typescript-auth-api-key/tsconfig.json +23 -0
  85. package/templates/typescript-oauth/.env.example +91 -0
  86. package/templates/typescript-oauth/src/app.module.ts +89 -0
  87. package/templates/typescript-oauth/src/guards/oauth.guard.ts +127 -0
  88. package/templates/typescript-oauth/src/index.ts +74 -0
  89. package/templates/typescript-oauth/src/modules/demo/demo.module.ts +16 -0
  90. package/templates/typescript-oauth/src/modules/demo/demo.tools.ts +190 -0
  91. package/templates/typescript-oauth/src/widgets/app/calculator-operations/page.tsx +133 -0
  92. package/templates/typescript-oauth/src/widgets/app/calculator-result/page.tsx +134 -0
  93. package/templates/typescript-oauth/src/widgets/app/layout.tsx +14 -0
  94. package/templates/typescript-oauth/src/widgets/next.config.js +37 -0
  95. package/templates/typescript-oauth/src/widgets/package.json +24 -0
  96. package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
  97. package/templates/typescript-oauth/src/widgets/widget-manifest.json +48 -0
  98. package/templates/typescript-oauth/tsconfig.json +23 -0
  99. package/templates/typescript-starter/.env.example +4 -0
  100. package/templates/typescript-starter/src/app.module.ts +34 -0
  101. package/templates/typescript-starter/src/health/system.health.ts +55 -0
  102. package/templates/typescript-starter/src/index.ts +27 -0
  103. package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
  104. package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
  105. package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +60 -0
  106. package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +71 -0
  107. package/templates/typescript-starter/src/widgets/app/calculator-operations/page.tsx +133 -0
  108. package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +134 -0
  109. package/templates/typescript-starter/src/widgets/app/layout.tsx +14 -0
  110. package/templates/typescript-starter/src/widgets/next.config.js +37 -0
  111. package/templates/typescript-starter/src/widgets/package.json +24 -0
  112. package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
  113. package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
  114. package/templates/typescript-starter/tsconfig.json +23 -0
  115. package/LICENSE_URLS_UPDATE_COMPLETE.md +0 -388
@@ -0,0 +1,40 @@
1
+ import { ResourceDecorator as Resource, ExecutionContext } from 'nitrostack';
2
+
3
+ /**
4
+ * Addresses Resources
5
+ * Provides address-related resource endpoints
6
+ */
7
+ export class AddressesResources {
8
+ /**
9
+ * Address schema resource
10
+ */
11
+ @Resource({
12
+ uri: 'addresses://schema',
13
+ name: 'Address Schema',
14
+ description: 'Schema definition for shipping address objects',
15
+ mimeType: 'application/json',
16
+ })
17
+ async getAddressSchema(context: ExecutionContext) {
18
+ return {
19
+ type: 'json' as const,
20
+ data: {
21
+ type: 'object',
22
+ properties: {
23
+ id: { type: 'string', format: 'uuid', description: 'Address ID' },
24
+ user_id: { type: 'string', description: 'User ID' },
25
+ full_name: { type: 'string', description: 'Full name for delivery' },
26
+ street: { type: 'string', description: 'Street address' },
27
+ city: { type: 'string', description: 'City' },
28
+ state: { type: 'string', description: 'State/Province' },
29
+ zip_code: { type: 'string', description: 'ZIP/Postal code' },
30
+ country: { type: 'string', default: 'USA', description: 'Country' },
31
+ phone: { type: 'string', description: 'Contact phone number' },
32
+ is_default: { type: 'boolean', description: 'Is default shipping address' },
33
+ created_at: { type: 'string', format: 'date-time', description: 'Address creation time' },
34
+ },
35
+ required: ['full_name', 'street', 'city', 'state', 'zip_code', 'phone'],
36
+ },
37
+ };
38
+ }
39
+ }
40
+
@@ -0,0 +1,241 @@
1
+ import { ToolDecorator as Tool, Widget, UseGuards, z, ExecutionContext } from 'nitrostack';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import { getDatabase } from '../../db/database.js';
4
+ import { JWTGuard } from '../../guards/jwt.guard.js';
5
+
6
+ /**
7
+ * Addresses Tools
8
+ * Handles user shipping addresses (requires authentication)
9
+ */
10
+ export class AddressesTools {
11
+ /**
12
+ * Add new address - Requires authentication
13
+ */
14
+ @Tool({
15
+ name: 'add_address',
16
+ description: 'Add a new shipping address. Requires authentication.',
17
+ inputSchema: z.object({
18
+ full_name: z.string().describe('Full name for delivery'),
19
+ street: z.string().describe('Street address'),
20
+ city: z.string().describe('City'),
21
+ state: z.string().describe('State/Province'),
22
+ zip_code: z.string().describe('ZIP/Postal code'),
23
+ country: z.string().default('USA').describe('Country'),
24
+ phone: z.string().describe('Contact phone number'),
25
+ is_default: z.boolean().default(false).describe('Set as default address'),
26
+ }),
27
+ examples: {
28
+ request: {
29
+ full_name: 'Emily Johnson',
30
+ street: '123 Main Street',
31
+ city: 'San Francisco',
32
+ state: 'CA',
33
+ zip_code: '94102',
34
+ country: 'USA',
35
+ phone: '+1-555-0100',
36
+ is_default: true
37
+ },
38
+ response: {
39
+ message: 'Address added successfully!',
40
+ addressId: 'addr-1',
41
+ isDefault: true
42
+ }
43
+ }
44
+ })
45
+ @Widget('address-added')
46
+ @UseGuards(JWTGuard)
47
+ async addAddress(input: any, context: ExecutionContext) {
48
+ const db = getDatabase();
49
+ const userId = context.auth?.subject;
50
+ const addressId = uuidv4();
51
+
52
+ // If this is set as default, unset other defaults
53
+ if (input.is_default) {
54
+ db.prepare(`
55
+ UPDATE addresses SET is_default = 0 WHERE user_id = ?
56
+ `).run(userId);
57
+ }
58
+
59
+ db.prepare(`
60
+ INSERT INTO addresses (id, user_id, full_name, street, city, state, zip_code, country, phone, is_default)
61
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
62
+ `).run(
63
+ addressId,
64
+ userId,
65
+ input.full_name,
66
+ input.street,
67
+ input.city,
68
+ input.state,
69
+ input.zip_code,
70
+ input.country,
71
+ input.phone,
72
+ input.is_default ? 1 : 0
73
+ );
74
+
75
+ context.logger.info(`Address added: ${input.city}, ${input.state}`);
76
+
77
+ return {
78
+ message: 'Address added successfully!',
79
+ addressId,
80
+ isDefault: input.is_default,
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Get all addresses - Requires authentication
86
+ */
87
+ @Tool({
88
+ name: 'get_addresses',
89
+ description: 'Get all saved shipping addresses. Requires authentication.',
90
+ inputSchema: z.object({}),
91
+ examples: {
92
+ request: {},
93
+ response: {
94
+ addresses: [
95
+ {
96
+ id: 'addr-1',
97
+ full_name: 'Emily Johnson',
98
+ street: '123 Main Street',
99
+ city: 'San Francisco',
100
+ state: 'CA',
101
+ zip_code: '94102',
102
+ country: 'USA',
103
+ phone: '+1-555-0100',
104
+ is_default: true
105
+ },
106
+ {
107
+ id: 'addr-2',
108
+ full_name: 'Emily Johnson',
109
+ street: '456 Office Blvd',
110
+ city: 'San Francisco',
111
+ state: 'CA',
112
+ zip_code: '94103',
113
+ country: 'USA',
114
+ phone: '+1-555-0100',
115
+ is_default: false
116
+ }
117
+ ]
118
+ }
119
+ }
120
+ })
121
+ @Widget('address-list')
122
+ @UseGuards(JWTGuard)
123
+ async getAddresses(input: any, context: ExecutionContext) {
124
+ const db = getDatabase();
125
+ const userId = context.auth?.subject;
126
+
127
+ const addresses: any[] = db.prepare(`
128
+ SELECT * FROM addresses WHERE user_id = ? ORDER BY is_default DESC, created_at DESC
129
+ `).all(userId);
130
+
131
+ context.logger.info(`Retrieved ${addresses.length} addresses`);
132
+
133
+ return {
134
+ addresses: addresses.map(addr => ({
135
+ ...addr,
136
+ is_default: addr.is_default === 1,
137
+ })),
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Set default address - Requires authentication
143
+ */
144
+ @Tool({
145
+ name: 'set_default_address',
146
+ description: 'Set an address as the default shipping address. Requires authentication.',
147
+ examples: {
148
+ request: {
149
+ address_id: 'addr-2'
150
+ },
151
+ response: {
152
+ message: 'Default address updated!',
153
+ addressId: 'addr-2'
154
+ }
155
+ },
156
+ inputSchema: z.object({
157
+ address_id: z.string().describe('The ID of the address to set as default'),
158
+ }),
159
+ })
160
+ @Widget('address-updated')
161
+ @UseGuards(JWTGuard)
162
+ async setDefaultAddress(input: any, context: ExecutionContext) {
163
+ const db = getDatabase();
164
+ const userId = context.auth?.subject;
165
+
166
+ // Verify address belongs to user
167
+ const address: any = db.prepare(`
168
+ SELECT * FROM addresses WHERE id = ? AND user_id = ?
169
+ `).get(input.address_id, userId);
170
+
171
+ if (!address) {
172
+ throw new Error('Address not found or you don\'t have access to it.');
173
+ }
174
+
175
+ // Unset all defaults
176
+ db.prepare(`
177
+ UPDATE addresses SET is_default = 0 WHERE user_id = ?
178
+ `).run(userId);
179
+
180
+ // Set new default
181
+ db.prepare(`
182
+ UPDATE addresses SET is_default = 1 WHERE id = ?
183
+ `).run(input.address_id);
184
+
185
+ context.logger.info(`Set default address: ${address.city}, ${address.state}`);
186
+
187
+ return {
188
+ message: 'Default address updated!',
189
+ address: {
190
+ ...address,
191
+ is_default: true,
192
+ },
193
+ };
194
+ }
195
+
196
+ /**
197
+ * Delete address - Requires authentication
198
+ */
199
+ @Tool({
200
+ name: 'delete_address',
201
+ description: 'Delete a shipping address. Requires authentication.',
202
+ inputSchema: z.object({
203
+ address_id: z.string().describe('The ID of the address to delete'),
204
+ }),
205
+ examples: {
206
+ request: {
207
+ address_id: 'addr-2'
208
+ },
209
+ response: {
210
+ message: 'Address deleted successfully!',
211
+ addressId: 'addr-2'
212
+ }
213
+ }
214
+ })
215
+ @Widget('address-deleted')
216
+ @UseGuards(JWTGuard)
217
+ async deleteAddress(input: any, context: ExecutionContext) {
218
+ const db = getDatabase();
219
+ const userId = context.auth?.subject;
220
+
221
+ // Verify address belongs to user
222
+ const address: any = db.prepare(`
223
+ SELECT * FROM addresses WHERE id = ? AND user_id = ?
224
+ `).get(input.address_id, userId);
225
+
226
+ if (!address) {
227
+ throw new Error('Address not found or you don\'t have access to it.');
228
+ }
229
+
230
+ db.prepare(`
231
+ DELETE FROM addresses WHERE id = ?
232
+ `).run(input.address_id);
233
+
234
+ context.logger.info(`Deleted address: ${address.city}, ${address.state}`);
235
+
236
+ return {
237
+ message: 'Address deleted successfully!',
238
+ };
239
+ }
240
+ }
241
+
@@ -0,0 +1,16 @@
1
+ import { Module } from 'nitrostack';
2
+ import { AuthTools } from './auth.tools.js';
3
+ import { AuthResources } from './auth.resources.js';
4
+ import { AuthPrompts } from './auth.prompts.js';
5
+
6
+ /**
7
+ * Authentication Module
8
+ * Provides login, user profile tools, resources, and prompts
9
+ */
10
+ @Module({
11
+ name: 'auth',
12
+ description: 'Authentication and user management',
13
+ controllers: [AuthTools, AuthResources, AuthPrompts],
14
+ })
15
+ export class AuthModule {}
16
+
@@ -0,0 +1,147 @@
1
+ import { PromptDecorator as Prompt, ExecutionContext } from 'nitrostack';
2
+
3
+ /**
4
+ * Authentication Prompts
5
+ * Provides helpful prompts for authentication-related tasks
6
+ */
7
+ export class AuthPrompts {
8
+ /**
9
+ * Authentication help prompt
10
+ */
11
+ @Prompt({
12
+ name: 'auth-help',
13
+ description: 'Get help with authentication and login process',
14
+ arguments: [
15
+ {
16
+ name: 'issue',
17
+ description: 'The authentication issue you are facing',
18
+ required: false,
19
+ },
20
+ ],
21
+ })
22
+ async authHelp(args: Record<string, any>, context: ExecutionContext) {
23
+ const issue = args.issue || 'general';
24
+
25
+ const baseMessages = [
26
+ {
27
+ role: 'user' as const,
28
+ content: `I need help with authentication in the e-commerce system. My issue: ${issue}`,
29
+ },
30
+ {
31
+ role: 'assistant' as const,
32
+ content: `I'll help you with authentication. Here's what you need to know:
33
+
34
+ **Login Process:**
35
+ 1. Use the \`login\` tool with email and password
36
+ 2. You'll receive a JWT token valid for 24 hours
37
+ 3. Include this token in subsequent requests
38
+
39
+ **Test Accounts:**
40
+ - alice@example.com / password123
41
+ - bob@example.com / password123
42
+ - charlie@example.com / password123
43
+
44
+ **Including Token:**
45
+ - In Inspector: Paste token in "JWT Token" field
46
+ - Or use: Authorization: Bearer <your-token>
47
+
48
+ **Protected Tools:**
49
+ The following tools require authentication:
50
+ - whoami - Get your profile
51
+ - add_to_cart - Add items to cart
52
+ - view_cart - View your cart
53
+ - create_order - Place orders
54
+ - get_order_history - View order history
55
+ - add_address - Manage addresses
56
+
57
+ **Common Issues:**
58
+ ${issue === 'invalid-token' ? '- Token expired: Login again to get a new token\n- Invalid format: Ensure you copied the complete token' : ''}
59
+ ${issue === 'unauthorized' ? '- Missing token: Make sure to login first\n- Wrong credentials: Check your email and password' : ''}
60
+ ${issue === 'general' ? '- Token expired? Login again\n- Forgot which email? Use test accounts above\n- Need to test? All test users have password: password123' : ''}
61
+
62
+ **Security Notes:**
63
+ - Tokens expire after 24 hours for security
64
+ - Passwords are hashed with bcrypt
65
+ - Never share your token with others`,
66
+ },
67
+ ];
68
+
69
+ return baseMessages;
70
+ }
71
+
72
+ /**
73
+ * JWT token explanation prompt
74
+ */
75
+ @Prompt({
76
+ name: 'jwt-explained',
77
+ description: 'Understand how JWT authentication works in this system',
78
+ })
79
+ async jwtExplained(args: Record<string, any>, context: ExecutionContext) {
80
+ return [
81
+ {
82
+ role: 'user' as const,
83
+ content: 'Explain how JWT authentication works in this e-commerce system',
84
+ },
85
+ {
86
+ role: 'assistant' as const,
87
+ content: `**JWT Authentication in E-commerce MCP Server**
88
+
89
+ **What is JWT?**
90
+ JWT (JSON Web Token) is a secure way to transmit information between parties. It's a token that contains:
91
+ - User identity (subject)
92
+ - User information (email, name)
93
+ - Expiration time
94
+ - Digital signature
95
+
96
+ **How It Works Here:**
97
+
98
+ 1. **Login** (Public)
99
+ - User provides email + password
100
+ - Server verifies with bcrypt
101
+ - Server generates JWT token
102
+ - Token valid for 24 hours
103
+
104
+ 2. **Using Protected Tools**
105
+ - Client includes token in request
106
+ - Server automatically validates token
107
+ - If valid: grants access
108
+ - If invalid/missing: denies access
109
+
110
+ 3. **Token Contents**
111
+ \`\`\`json
112
+ {
113
+ "sub": "user-id",
114
+ "email": "alice@example.com",
115
+ "name": "Alice Johnson",
116
+ "iat": 1234567890,
117
+ "exp": 1234654290
118
+ }
119
+ \`\`\`
120
+
121
+ **Security Features:**
122
+ - ✅ Tokens are signed (can't be tampered)
123
+ - ✅ Passwords hashed with bcrypt
124
+ - ✅ Tokens expire after 24 hours
125
+ - ✅ No password storage in tokens
126
+
127
+ **Developer Experience:**
128
+ With V3 decorators, authentication is automatic:
129
+ \`\`\`typescript
130
+ @UseGuards(JWTGuard)
131
+ async myProtectedTool(input, context) {
132
+ // context.auth is already populated!
133
+ const userId = context.auth.subject;
134
+ }
135
+ \`\`\`
136
+
137
+ **No Manual Checking Needed!**
138
+ The \`@UseGuards(JWTGuard)\` decorator handles:
139
+ - Token extraction
140
+ - Signature validation
141
+ - Expiry checking
142
+ - Context population`,
143
+ },
144
+ ];
145
+ }
146
+ }
147
+
@@ -0,0 +1,84 @@
1
+ import { ResourceDecorator as Resource, ExecutionContext } from 'nitrostack';
2
+ import { getDatabase } from '../../db/database.js';
3
+
4
+ /**
5
+ * Authentication Resources
6
+ * Provides auth-related resource endpoints
7
+ */
8
+ export class AuthResources {
9
+ /**
10
+ * User schema resource
11
+ */
12
+ @Resource({
13
+ uri: 'auth://schema/user',
14
+ name: 'User Schema',
15
+ description: 'Schema definition for user objects in the authentication system',
16
+ mimeType: 'application/json',
17
+ })
18
+ async getUserSchema(context: ExecutionContext) {
19
+ return {
20
+ type: 'json' as const,
21
+ data: {
22
+ type: 'object',
23
+ properties: {
24
+ id: { type: 'string', format: 'uuid', description: 'Unique user identifier' },
25
+ email: { type: 'string', format: 'email', description: 'User email address' },
26
+ name: { type: 'string', description: 'Full name' },
27
+ profile_picture: { type: 'string', format: 'uri', nullable: true, description: 'Profile picture URL' },
28
+ created_at: { type: 'string', format: 'date-time', description: 'Account creation timestamp' },
29
+ },
30
+ required: ['id', 'email', 'name'],
31
+ },
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Available test users resource
37
+ */
38
+ @Resource({
39
+ uri: 'auth://test-users',
40
+ name: 'Test Users',
41
+ description: 'List of available test users for the demo',
42
+ mimeType: 'application/json',
43
+ })
44
+ async getTestUsers(context: ExecutionContext) {
45
+ return {
46
+ type: 'json' as const,
47
+ data: {
48
+ users: [
49
+ { email: 'alice@example.com', password: 'password123', name: 'Alice Johnson' },
50
+ { email: 'bob@example.com', password: 'password123', name: 'Bob Smith' },
51
+ { email: 'charlie@example.com', password: 'password123', name: 'Charlie Brown' },
52
+ ],
53
+ note: 'All test users have the same password: password123',
54
+ },
55
+ };
56
+ }
57
+
58
+ /**
59
+ * Authentication statistics
60
+ */
61
+ @Resource({
62
+ uri: 'auth://stats',
63
+ name: 'Auth Statistics',
64
+ description: 'Authentication system statistics',
65
+ mimeType: 'application/json',
66
+ })
67
+ async getAuthStats(context: ExecutionContext) {
68
+ const db = getDatabase();
69
+
70
+ const totalUsers: any = db.prepare('SELECT COUNT(*) as count FROM users').get();
71
+ const recentLogins: any[] = db.prepare('SELECT email, created_at FROM users ORDER BY created_at DESC LIMIT 5').all();
72
+
73
+ return {
74
+ type: 'json' as const,
75
+ data: {
76
+ totalUsers: totalUsers.count,
77
+ recentUsers: recentLogins,
78
+ authMethod: 'JWT with bcrypt password hashing',
79
+ tokenExpiry: '24 hours',
80
+ },
81
+ };
82
+ }
83
+ }
84
+
@@ -0,0 +1,139 @@
1
+ import { ToolDecorator as Tool, Widget, UseGuards, z, ExecutionContext } from 'nitrostack';
2
+ import bcrypt from 'bcryptjs';
3
+ import { getDatabase } from '../../db/database.js';
4
+ import { generateJWT } from 'nitrostack';
5
+ import { JWTGuard } from '../../guards/jwt.guard.js';
6
+
7
+ /**
8
+ * Authentication Tools
9
+ * Handles user authentication and profile management
10
+ */
11
+ export class AuthTools {
12
+ /**
13
+ * Login tool - Authenticate user and return JWT token
14
+ * No authentication required (public endpoint)
15
+ */
16
+ @Tool({
17
+ name: 'login',
18
+ description: 'Login with email and password to get a JWT token. Use this token in the Authorization header for authenticated requests.',
19
+ inputSchema: z.object({
20
+ email: z.string().email().describe('User email address'),
21
+ password: z.string().describe('User password'),
22
+ }),
23
+ examples: {
24
+ request: {
25
+ email: 'emily.johnson@x.dummyjson.com',
26
+ password: 'password123'
27
+ },
28
+ response: {
29
+ message: 'Login successful!',
30
+ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
31
+ user: {
32
+ id: 'user-emily',
33
+ email: 'emily.johnson@x.dummyjson.com',
34
+ name: 'Emily Johnson'
35
+ }
36
+ }
37
+ }
38
+ })
39
+ @Widget('login-result')
40
+ async login(input: any, context: ExecutionContext) {
41
+ const db = getDatabase();
42
+
43
+ // Find user
44
+ const user: any = db.prepare('SELECT * FROM users WHERE email = ?').get(input.email);
45
+
46
+ if (!user) {
47
+ throw new Error('Invalid email or password');
48
+ }
49
+
50
+ // Verify password
51
+ const valid = bcrypt.compareSync(input.password, user.password_hash);
52
+
53
+ if (!valid) {
54
+ throw new Error('Invalid email or password');
55
+ }
56
+
57
+ // Generate JWT token
58
+ const token = generateJWT({
59
+ secret: process.env.JWT_SECRET!,
60
+ payload: {
61
+ sub: user.id,
62
+ email: user.email,
63
+ name: user.name,
64
+ },
65
+ expiresIn: '24h',
66
+ audience: process.env.JWT_AUDIENCE || 'ecommerce-mcp-server',
67
+ issuer: process.env.JWT_ISSUER || 'ecommerce-app',
68
+ });
69
+
70
+ context.logger.info(`User logged in: ${user.email}`);
71
+
72
+ return {
73
+ message: 'Login successful!',
74
+ token,
75
+ user: {
76
+ id: user.id,
77
+ email: user.email,
78
+ name: user.name,
79
+ },
80
+ instructions: {
81
+ step1: 'Copy the token above',
82
+ step2: 'In the inspector, go to Chat tab',
83
+ step3: 'Paste the token in the "JWT Token" field (if available)',
84
+ step4: 'Or use: Authorization: Bearer <token>',
85
+ expiresIn: '24 hours',
86
+ },
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Get current user information
92
+ * Requires JWT authentication
93
+ */
94
+ @Tool({
95
+ name: 'whoami',
96
+ description: 'Get information about the currently authenticated user. Requires authentication.',
97
+ inputSchema: z.object({}),
98
+ examples: {
99
+ request: {},
100
+ response: {
101
+ user: {
102
+ id: 'user-emily',
103
+ email: 'emily.johnson@x.dummyjson.com',
104
+ name: 'Emily Johnson',
105
+ profile_picture: 'https://dummyjson.com/icon/emilys/128',
106
+ memberSince: '2024-01-01T00:00:00Z'
107
+ },
108
+ authenticated: true
109
+ }
110
+ }
111
+ })
112
+ @Widget('whoami')
113
+ @UseGuards(JWTGuard)
114
+ async whoami(input: any, context: ExecutionContext) {
115
+ // User is already authenticated by JWTGuard
116
+ const userId = context.auth?.subject;
117
+
118
+ const db = getDatabase();
119
+ const user: any = db.prepare(`
120
+ SELECT id, email, name, profile_picture, created_at FROM users WHERE id = ?
121
+ `).get(userId);
122
+
123
+ if (!user) {
124
+ throw new Error('User not found');
125
+ }
126
+
127
+ return {
128
+ user: {
129
+ id: user.id,
130
+ email: user.email,
131
+ name: user.name,
132
+ profile_picture: user.profile_picture,
133
+ memberSince: user.created_at,
134
+ },
135
+ authenticated: true,
136
+ };
137
+ }
138
+ }
139
+
@@ -0,0 +1,16 @@
1
+ import { Module } from 'nitrostack';
2
+ import { CartTools } from './cart.tools.js';
3
+ import { CartResources } from './cart.resources.js';
4
+ import { CartPrompts } from './cart.prompts.js';
5
+
6
+ /**
7
+ * Cart Module
8
+ * Provides shopping cart management, resources, and prompts
9
+ */
10
+ @Module({
11
+ name: 'cart',
12
+ description: 'Shopping cart management',
13
+ controllers: [CartTools, CartResources, CartPrompts],
14
+ })
15
+ export class CartModule {}
16
+