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,157 @@
1
+ import { Guard, ExecutionContext, ApiKeyModule } from 'nitrostack';
2
+
3
+ /**
4
+ * Multi-Auth Guard
5
+ *
6
+ * Accepts EITHER JWT token OR API key authentication.
7
+ * This demonstrates how to support multiple authentication methods in a single tool.
8
+ *
9
+ * Use Cases:
10
+ * - Tools that can be accessed by users (JWT) or services (API key)
11
+ * - Migration from one auth method to another
12
+ * - Supporting legacy clients
13
+ *
14
+ * Usage:
15
+ * ```typescript
16
+ * @Tool({
17
+ * name: 'flexible_auth_tool',
18
+ * description: 'Works with JWT or API key'
19
+ * })
20
+ * @UseGuards(MultiAuthGuard)
21
+ * async flexibleTool() {
22
+ * // Accessible with EITHER JWT OR API key
23
+ * }
24
+ * ```
25
+ */
26
+ export class MultiAuthGuard implements Guard {
27
+ async canActivate(context: ExecutionContext): Promise<boolean> {
28
+ let authenticated = false;
29
+ let authMethod = 'none';
30
+
31
+ // Try JWT authentication first
32
+ const jwtToken = context.metadata?._jwt;
33
+ const authHeader = context.metadata?.authorization;
34
+
35
+ if (jwtToken || authHeader) {
36
+ // For this demo, we accept any JWT token
37
+ // In production, you'd validate it with JWTModule
38
+ const token = jwtToken || authHeader?.toString().replace('Bearer ', '');
39
+ if (token) {
40
+ context.auth = {
41
+ subject: 'jwt_user',
42
+ scopes: ['read', 'write'],
43
+ };
44
+ authenticated = true;
45
+ authMethod = 'JWT';
46
+ }
47
+ }
48
+
49
+ // If JWT didn't work, try API key
50
+ if (!authenticated) {
51
+ const apiKey = context.metadata?.apiKey || context.metadata?.['x-api-key'];
52
+
53
+ if (apiKey) {
54
+ const isValid = await ApiKeyModule.validate(apiKey as string);
55
+
56
+ if (isValid) {
57
+ context.auth = {
58
+ subject: `apikey_${(apiKey as string).substring(0, 12)}`,
59
+ scopes: ['*'],
60
+ };
61
+ authenticated = true;
62
+ authMethod = 'API Key';
63
+ }
64
+ }
65
+ }
66
+
67
+ if (!authenticated) {
68
+ throw new Error(
69
+ 'Authentication required. Please provide either:\n' +
70
+ ' 1. JWT token (in Studio: Auth → JWT Token)\n' +
71
+ ' 2. API key (in Studio: Auth → API Key)'
72
+ );
73
+ }
74
+
75
+ // Store auth method in context for debugging/logging
76
+ if (!context.metadata) {
77
+ (context as any).metadata = {};
78
+ }
79
+ (context.metadata as any)._authMethod = authMethod;
80
+
81
+ return true;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Dual-Auth Guard
87
+ *
88
+ * Requires BOTH JWT token AND API key authentication.
89
+ * This is for highly sensitive operations that need multiple authentication factors.
90
+ *
91
+ * Use Cases:
92
+ * - Financial transactions
93
+ * - Account deletion
94
+ * - Admin operations
95
+ * - Critical data access
96
+ *
97
+ * Usage:
98
+ * ```typescript
99
+ * @Tool({
100
+ * name: 'critical_operation',
101
+ * description: 'Requires both JWT and API key'
102
+ * })
103
+ * @UseGuards(DualAuthGuard)
104
+ * async criticalOperation() {
105
+ * // Only accessible with BOTH JWT AND API key
106
+ * }
107
+ * ```
108
+ */
109
+ export class DualAuthGuard implements Guard {
110
+ async canActivate(context: ExecutionContext): Promise<boolean> {
111
+ const errors: string[] = [];
112
+
113
+ // Check JWT token
114
+ const jwtToken = context.metadata?._jwt;
115
+ const authHeader = context.metadata?.authorization;
116
+ const hasJWT = !!(jwtToken || authHeader);
117
+
118
+ if (!hasJWT) {
119
+ errors.push('JWT token is required (set in Studio: Auth → JWT Token)');
120
+ }
121
+
122
+ // Check API key
123
+ const apiKey = context.metadata?.apiKey || context.metadata?.['x-api-key'];
124
+
125
+ if (!apiKey) {
126
+ errors.push('API key is required (set in Studio: Auth → API Key)');
127
+ } else {
128
+ const isValidKey = await ApiKeyModule.validate(apiKey as string);
129
+ if (!isValidKey) {
130
+ errors.push('Invalid API key');
131
+ }
132
+ }
133
+
134
+ if (errors.length > 0) {
135
+ throw new Error(
136
+ 'Dual authentication required:\n' +
137
+ errors.map(e => ` - ${e}`).join('\n')
138
+ );
139
+ }
140
+
141
+ // Both auth methods present and valid
142
+ context.auth = {
143
+ subject: 'dual_auth_user',
144
+ scopes: ['*'], // Full access with dual auth
145
+ };
146
+
147
+ // Store auth info
148
+ if (!context.metadata) {
149
+ (context as any).metadata = {};
150
+ }
151
+ (context.metadata as any)._authMethod = 'JWT + API Key';
152
+
153
+ return true;
154
+ }
155
+ }
156
+
157
+
@@ -0,0 +1,55 @@
1
+ import { HealthCheck, HealthCheckInterface, HealthCheckResult } from 'nitrostack';
2
+
3
+ /**
4
+ * System Health Check
5
+ *
6
+ * Monitors system resources and uptime
7
+ */
8
+ @HealthCheck({
9
+ name: 'system',
10
+ description: 'System resource and uptime check',
11
+ interval: 30 // Check every 30 seconds
12
+ })
13
+ export class SystemHealthCheck implements HealthCheckInterface {
14
+ private startTime: number;
15
+
16
+ constructor() {
17
+ this.startTime = Date.now();
18
+ }
19
+
20
+ async check(): Promise<HealthCheckResult> {
21
+ try {
22
+ const memoryUsage = process.memoryUsage();
23
+ const uptime = Date.now() - this.startTime;
24
+ const uptimeSeconds = Math.floor(uptime / 1000);
25
+
26
+ // Convert memory to MB
27
+ const memoryUsedMB = Math.round(memoryUsage.heapUsed / 1024 / 1024);
28
+ const memoryTotalMB = Math.round(memoryUsage.heapTotal / 1024 / 1024);
29
+
30
+ // Consider unhealthy if memory usage is > 90%
31
+ const memoryPercent = (memoryUsage.heapUsed / memoryUsage.heapTotal) * 100;
32
+ const isHealthy = memoryPercent < 90;
33
+
34
+ return {
35
+ status: isHealthy ? 'up' : 'degraded',
36
+ message: isHealthy
37
+ ? 'System is healthy'
38
+ : 'High memory usage detected',
39
+ details: {
40
+ uptime: `${uptimeSeconds}s`,
41
+ memory: `${memoryUsedMB}MB / ${memoryTotalMB}MB (${Math.round(memoryPercent)}%)`,
42
+ pid: process.pid,
43
+ nodeVersion: process.version,
44
+ },
45
+ };
46
+ } catch (error: any) {
47
+ return {
48
+ status: 'down',
49
+ message: 'System health check failed',
50
+ details: error.message,
51
+ };
52
+ }
53
+ }
54
+ }
55
+
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+ import { McpApplicationFactory } from 'nitrostack';
3
+ import { AppModule } from './app.module.js';
4
+
5
+ /**
6
+ * API Key Authentication MCP Server
7
+ *
8
+ * This template demonstrates how to build an MCP server with API key authentication.
9
+ *
10
+ * Features:
11
+ * - Public tools (no authentication)
12
+ * - Protected tools (API key required)
13
+ * - Easy integration with NitroStack Studio
14
+ * - Environment-based key management
15
+ *
16
+ * Test API Keys (provided in .env):
17
+ * - sk_test_public_demo_key_12345
18
+ * - sk_test_admin_demo_key_67890
19
+ */
20
+
21
+ async function bootstrap() {
22
+ try {
23
+ console.error('🔑 Starting API Key Auth MCP Server...\n');
24
+
25
+ // Create the MCP application
26
+ const app = await McpApplicationFactory.create(AppModule);
27
+
28
+ console.error('✅ API Key Module configured');
29
+ console.error(' - Keys loaded from environment variables (API_KEY_*)');
30
+ console.error(' - Protected tools require valid API key');
31
+ console.error(' - Public tools accessible without authentication\n');
32
+
33
+ // Start the MCP server
34
+ await app.start();
35
+
36
+ console.error('🚀 Server started successfully!');
37
+ console.error(' Open NitroStack Studio to test the server');
38
+ console.error(' Set your API key in Studio → Auth → API Key section\n');
39
+
40
+ } catch (error) {
41
+ console.error('❌ Failed to start server:', error);
42
+ process.exit(1);
43
+ }
44
+ }
45
+
46
+ bootstrap();
47
+
@@ -0,0 +1,12 @@
1
+ import { Module } from 'nitrostack';
2
+ import { CalculatorTools } from './calculator.tools.js';
3
+ import { CalculatorResources } from './calculator.resources.js';
4
+ import { CalculatorPrompts } from './calculator.prompts.js';
5
+
6
+ @Module({
7
+ name: 'calculator',
8
+ description: 'Basic arithmetic calculator',
9
+ controllers: [CalculatorTools, CalculatorResources, CalculatorPrompts]
10
+ })
11
+ export class CalculatorModule {}
12
+
@@ -0,0 +1,73 @@
1
+ import { PromptDecorator as Prompt, ExecutionContext } from 'nitrostack';
2
+
3
+ export class CalculatorPrompts {
4
+ @Prompt({
5
+ name: 'calculator_help',
6
+ description: 'Get help with calculator operations',
7
+ arguments: [
8
+ {
9
+ name: 'operation',
10
+ description: 'The operation to get help with (optional)',
11
+ required: false
12
+ }
13
+ ]
14
+ })
15
+ async getHelp(args: any, ctx: ExecutionContext) {
16
+ ctx.logger.info('Generating calculator help prompt');
17
+
18
+ const operation = args.operation;
19
+
20
+ if (operation) {
21
+ // Help for specific operation
22
+ const helpText = this.getOperationHelp(operation);
23
+ return [
24
+ {
25
+ role: 'user' as const,
26
+ content: `How do I use the ${operation} operation in the calculator?`
27
+ },
28
+ {
29
+ role: 'assistant' as const,
30
+ content: helpText
31
+ }
32
+ ];
33
+ }
34
+
35
+ // General help
36
+ return [
37
+ {
38
+ role: 'user' as const,
39
+ content: 'How do I use the calculator?'
40
+ },
41
+ {
42
+ role: 'assistant' as const,
43
+ content: `The calculator supports four basic operations:
44
+
45
+ 1. **Addition** - Add two numbers together
46
+ Example: calculate(operation="add", a=5, b=3) = 8
47
+
48
+ 2. **Subtraction** - Subtract one number from another
49
+ Example: calculate(operation="subtract", a=10, b=4) = 6
50
+
51
+ 3. **Multiplication** - Multiply two numbers
52
+ Example: calculate(operation="multiply", a=6, b=7) = 42
53
+
54
+ 4. **Division** - Divide one number by another
55
+ Example: calculate(operation="divide", a=20, b=5) = 4
56
+
57
+ Just call the 'calculate' tool with the operation and two numbers!`
58
+ }
59
+ ];
60
+ }
61
+
62
+ private getOperationHelp(operation: string): string {
63
+ const helps: Record<string, string> = {
64
+ add: 'Use addition to sum two numbers. Call calculate(operation="add", a=5, b=3) to get 8.',
65
+ subtract: 'Use subtraction to find the difference. Call calculate(operation="subtract", a=10, b=4) to get 6.',
66
+ multiply: 'Use multiplication to find the product. Call calculate(operation="multiply", a=6, b=7) to get 42.',
67
+ divide: 'Use division to find the quotient. Call calculate(operation="divide", a=20, b=5) to get 4. Note: Cannot divide by zero!'
68
+ };
69
+
70
+ return helps[operation] || 'Unknown operation. Available operations: add, subtract, multiply, divide.';
71
+ }
72
+ }
73
+
@@ -0,0 +1,60 @@
1
+ import { ResourceDecorator as Resource, Widget, ExecutionContext } from 'nitrostack';
2
+
3
+ export class CalculatorResources {
4
+ @Resource({
5
+ uri: 'calculator://operations',
6
+ name: 'Calculator Operations',
7
+ description: 'List of available calculator operations',
8
+ mimeType: 'application/json',
9
+ examples: {
10
+ response: {
11
+ operations: [
12
+ { name: 'add', symbol: '+', description: 'Addition' },
13
+ { name: 'subtract', symbol: '-', description: 'Subtraction' },
14
+ { name: 'multiply', symbol: '×', description: 'Multiplication' },
15
+ { name: 'divide', symbol: '÷', description: 'Division' }
16
+ ]
17
+ }
18
+ }
19
+ })
20
+ @Widget('calculator-operations')
21
+ async getOperations(uri: string, ctx: ExecutionContext) {
22
+ ctx.logger.info('Fetching calculator operations');
23
+
24
+ const operations = [
25
+ {
26
+ name: 'add',
27
+ symbol: '+',
28
+ description: 'Addition',
29
+ example: '5 + 3 = 8'
30
+ },
31
+ {
32
+ name: 'subtract',
33
+ symbol: '-',
34
+ description: 'Subtraction',
35
+ example: '10 - 4 = 6'
36
+ },
37
+ {
38
+ name: 'multiply',
39
+ symbol: '×',
40
+ description: 'Multiplication',
41
+ example: '6 × 7 = 42'
42
+ },
43
+ {
44
+ name: 'divide',
45
+ symbol: '÷',
46
+ description: 'Division',
47
+ example: '20 ÷ 5 = 4'
48
+ }
49
+ ];
50
+
51
+ return {
52
+ contents: [{
53
+ uri,
54
+ mimeType: 'application/json',
55
+ text: JSON.stringify({ operations }, null, 2)
56
+ }]
57
+ };
58
+ }
59
+ }
60
+
@@ -0,0 +1,71 @@
1
+ import { ToolDecorator as Tool, Widget, ExecutionContext, z } from 'nitrostack';
2
+
3
+ export class CalculatorTools {
4
+ @Tool({
5
+ name: 'calculate',
6
+ description: 'Perform basic arithmetic calculations',
7
+ inputSchema: z.object({
8
+ operation: z.enum(['add', 'subtract', 'multiply', 'divide']).describe('The operation to perform'),
9
+ a: z.number().describe('First number'),
10
+ b: z.number().describe('Second number')
11
+ }),
12
+ examples: {
13
+ request: {
14
+ operation: 'add',
15
+ a: 5,
16
+ b: 3
17
+ },
18
+ response: {
19
+ operation: 'add',
20
+ a: 5,
21
+ b: 3,
22
+ result: 8,
23
+ expression: '5 + 3 = 8'
24
+ }
25
+ }
26
+ })
27
+ @Widget('calculator-result')
28
+ async calculate(input: any, ctx: ExecutionContext) {
29
+ ctx.logger.info('Performing calculation', {
30
+ operation: input.operation,
31
+ a: input.a,
32
+ b: input.b
33
+ });
34
+
35
+ let result: number;
36
+ let symbol: string;
37
+
38
+ switch (input.operation) {
39
+ case 'add':
40
+ result = input.a + input.b;
41
+ symbol = '+';
42
+ break;
43
+ case 'subtract':
44
+ result = input.a - input.b;
45
+ symbol = '-';
46
+ break;
47
+ case 'multiply':
48
+ result = input.a * input.b;
49
+ symbol = '×';
50
+ break;
51
+ case 'divide':
52
+ if (input.b === 0) {
53
+ throw new Error('Cannot divide by zero');
54
+ }
55
+ result = input.a / input.b;
56
+ symbol = '÷';
57
+ break;
58
+ default:
59
+ throw new Error('Invalid operation');
60
+ }
61
+
62
+ return {
63
+ operation: input.operation,
64
+ a: input.a,
65
+ b: input.b,
66
+ result,
67
+ expression: `${input.a} ${symbol} ${input.b} = ${result}`
68
+ };
69
+ }
70
+ }
71
+
@@ -0,0 +1,18 @@
1
+ import { Module } from 'nitrostack';
2
+ import { DemoTools } from './demo.tools.js';
3
+ import { MultiAuthTools } from './multi-auth.tools.js';
4
+
5
+ /**
6
+ * Demo Module
7
+ *
8
+ * Contains example tools demonstrating:
9
+ * - API key authentication
10
+ * - Multi-auth patterns (JWT + API Key)
11
+ */
12
+ @Module({
13
+ name: 'demo',
14
+ description: 'Demo module with API key authentication examples',
15
+ controllers: [DemoTools, MultiAuthTools],
16
+ })
17
+ export class DemoModule {}
18
+
@@ -0,0 +1,155 @@
1
+ import { Injectable, ToolDecorator as Tool, UseGuards, z, ExecutionContext } from 'nitrostack';
2
+ import { ApiKeyGuard } from '../../guards/apikey.guard.js';
3
+
4
+ /**
5
+ * Demo Tools Module
6
+ *
7
+ * Demonstrates:
8
+ * 1. Public tools (no authentication required)
9
+ * 2. Protected tools (API key required)
10
+ * 3. Multi-auth scenarios
11
+ */
12
+
13
+ @Injectable()
14
+ export class DemoTools {
15
+
16
+ /**
17
+ * PUBLIC TOOL - No authentication required
18
+ * Anyone can call this tool without an API key
19
+ */
20
+ @Tool({
21
+ name: 'get_public_info',
22
+ description: 'Get public information (no authentication required)',
23
+ inputSchema: z.object({
24
+ topic: z.string().describe('The topic to get information about'),
25
+ }),
26
+ })
27
+ async getPublicInfo(args: { topic: string }) {
28
+ return {
29
+ message: `Public information about ${args.topic}`,
30
+ data: {
31
+ topic: args.topic,
32
+ info: 'This is public information that anyone can access without authentication.',
33
+ timestamp: new Date().toISOString(),
34
+ authRequired: false,
35
+ },
36
+ };
37
+ }
38
+
39
+ /**
40
+ * PROTECTED TOOL - API key required
41
+ * Only accessible with a valid API key
42
+ */
43
+ @Tool({
44
+ name: 'get_protected_data',
45
+ description: 'Get protected data (requires API key authentication)',
46
+ inputSchema: z.object({
47
+ dataType: z.enum(['user', 'account', 'settings']).describe('Type of protected data to retrieve'),
48
+ }),
49
+ })
50
+ @UseGuards(ApiKeyGuard)
51
+ async getProtectedData(args: { dataType: string }, context?: ExecutionContext) {
52
+ // Access auth information populated by the guard
53
+ const apiKeySubject = context?.auth?.subject || 'unknown';
54
+
55
+ return {
56
+ message: `Protected ${args.dataType} data accessed successfully`,
57
+ data: {
58
+ dataType: args.dataType,
59
+ accessedBy: apiKeySubject,
60
+ timestamp: new Date().toISOString(),
61
+ protectedInfo: `This is sensitive ${args.dataType} data that requires API key authentication.`,
62
+ // In real apps, you'd fetch actual data from a database
63
+ sampleData: {
64
+ user: { id: 1, name: 'John Doe', role: 'admin' },
65
+ account: { id: 'acc_123', balance: 1000, currency: 'USD' },
66
+ settings: { theme: 'dark', notifications: true, language: 'en' },
67
+ }[args.dataType],
68
+ },
69
+ };
70
+ }
71
+
72
+ /**
73
+ * PROTECTED TOOL - API key required
74
+ * Performs an action that requires authentication
75
+ */
76
+ @Tool({
77
+ name: 'perform_protected_action',
78
+ description: 'Perform a protected action (requires API key authentication)',
79
+ inputSchema: z.object({
80
+ action: z.enum(['create', 'update', 'delete']).describe('The action to perform'),
81
+ resourceType: z.string().describe('Type of resource (e.g., "user", "post", "file")'),
82
+ resourceId: z.string().optional().describe('Resource ID (for update/delete)'),
83
+ }),
84
+ })
85
+ @UseGuards(ApiKeyGuard)
86
+ async performProtectedAction(
87
+ args: { action: string; resourceType: string; resourceId?: string },
88
+ context?: ExecutionContext
89
+ ) {
90
+ const apiKeySubject = context?.auth?.subject || 'unknown';
91
+
92
+ let result;
93
+ switch (args.action) {
94
+ case 'create':
95
+ result = {
96
+ status: 'success',
97
+ message: `Created new ${args.resourceType}`,
98
+ resourceId: `${args.resourceType}_${Date.now()}`,
99
+ };
100
+ break;
101
+ case 'update':
102
+ result = {
103
+ status: 'success',
104
+ message: `Updated ${args.resourceType} ${args.resourceId}`,
105
+ resourceId: args.resourceId,
106
+ };
107
+ break;
108
+ case 'delete':
109
+ result = {
110
+ status: 'success',
111
+ message: `Deleted ${args.resourceType} ${args.resourceId}`,
112
+ resourceId: args.resourceId,
113
+ };
114
+ break;
115
+ default:
116
+ result = { status: 'error', message: 'Invalid action' };
117
+ }
118
+
119
+ return {
120
+ message: `Action performed by ${apiKeySubject}`,
121
+ action: args.action,
122
+ resourceType: args.resourceType,
123
+ timestamp: new Date().toISOString(),
124
+ result,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * UTILITY TOOL - Check API key status
130
+ * Helps developers test their API key configuration
131
+ */
132
+ @Tool({
133
+ name: 'check_api_key_status',
134
+ description: 'Check if your API key is valid and working (requires API key)',
135
+ inputSchema: z.object({}),
136
+ })
137
+ @UseGuards(ApiKeyGuard)
138
+ async checkApiKeyStatus(args: {}, context?: ExecutionContext) {
139
+ const apiKeySubject = context?.auth?.subject || 'unknown';
140
+ const scopes = context?.auth?.scopes || [];
141
+
142
+ return {
143
+ message: 'API key is valid and working! ✓',
144
+ status: 'authenticated',
145
+ apiKeyInfo: {
146
+ subject: apiKeySubject,
147
+ scopes: scopes,
148
+ hasFullAccess: scopes.includes('*'),
149
+ },
150
+ timestamp: new Date().toISOString(),
151
+ hint: 'Your API key is properly configured in the Studio Auth tab.',
152
+ };
153
+ }
154
+ }
155
+