@nitrostack/cli 1.0.0

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 (100) hide show
  1. package/README.md +131 -0
  2. package/dist/commands/build.d.ts +6 -0
  3. package/dist/commands/build.d.ts.map +1 -0
  4. package/dist/commands/build.js +185 -0
  5. package/dist/commands/dev.d.ts +7 -0
  6. package/dist/commands/dev.d.ts.map +1 -0
  7. package/dist/commands/dev.js +365 -0
  8. package/dist/commands/generate-types.d.ts +8 -0
  9. package/dist/commands/generate-types.d.ts.map +1 -0
  10. package/dist/commands/generate-types.js +219 -0
  11. package/dist/commands/generate.d.ts +12 -0
  12. package/dist/commands/generate.d.ts.map +1 -0
  13. package/dist/commands/generate.js +375 -0
  14. package/dist/commands/init.d.ts +7 -0
  15. package/dist/commands/init.d.ts.map +1 -0
  16. package/dist/commands/init.js +324 -0
  17. package/dist/commands/install.d.ts +10 -0
  18. package/dist/commands/install.d.ts.map +1 -0
  19. package/dist/commands/install.js +80 -0
  20. package/dist/commands/start.d.ts +6 -0
  21. package/dist/commands/start.d.ts.map +1 -0
  22. package/dist/commands/start.js +70 -0
  23. package/dist/commands/upgrade.d.ts +10 -0
  24. package/dist/commands/upgrade.d.ts.map +1 -0
  25. package/dist/commands/upgrade.js +214 -0
  26. package/dist/index.d.ts +11 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +94 -0
  29. package/dist/mcp-dev-wrapper.d.ts +15 -0
  30. package/dist/mcp-dev-wrapper.d.ts.map +1 -0
  31. package/dist/mcp-dev-wrapper.js +187 -0
  32. package/dist/ui/branding.d.ts +31 -0
  33. package/dist/ui/branding.d.ts.map +1 -0
  34. package/dist/ui/branding.js +136 -0
  35. package/package.json +69 -0
  36. package/templates/typescript-oauth/.env.example +27 -0
  37. package/templates/typescript-oauth/OAUTH_SETUP.md +592 -0
  38. package/templates/typescript-oauth/README.md +263 -0
  39. package/templates/typescript-oauth/package.json +29 -0
  40. package/templates/typescript-oauth/src/app.module.ts +92 -0
  41. package/templates/typescript-oauth/src/guards/oauth.guard.ts +126 -0
  42. package/templates/typescript-oauth/src/health/system.health.ts +55 -0
  43. package/templates/typescript-oauth/src/index.ts +63 -0
  44. package/templates/typescript-oauth/src/modules/flights/booking.tools.ts +323 -0
  45. package/templates/typescript-oauth/src/modules/flights/flights.module.ts +14 -0
  46. package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +228 -0
  47. package/templates/typescript-oauth/src/modules/flights/flights.resources.ts +215 -0
  48. package/templates/typescript-oauth/src/modules/flights/flights.tools.ts +457 -0
  49. package/templates/typescript-oauth/src/services/duffel.service.ts +285 -0
  50. package/templates/typescript-oauth/src/widgets/app/airport-search/page.tsx +270 -0
  51. package/templates/typescript-oauth/src/widgets/app/flight-details/page.tsx +261 -0
  52. package/templates/typescript-oauth/src/widgets/app/flight-search-results/page.tsx +378 -0
  53. package/templates/typescript-oauth/src/widgets/app/globals.css +167 -0
  54. package/templates/typescript-oauth/src/widgets/app/layout.tsx +18 -0
  55. package/templates/typescript-oauth/src/widgets/app/order-cancellation/page.tsx +207 -0
  56. package/templates/typescript-oauth/src/widgets/app/order-summary/page.tsx +245 -0
  57. package/templates/typescript-oauth/src/widgets/app/payment-confirmation/page.tsx +152 -0
  58. package/templates/typescript-oauth/src/widgets/app/seat-selection/page.tsx +486 -0
  59. package/templates/typescript-oauth/src/widgets/next-env.d.ts +5 -0
  60. package/templates/typescript-oauth/src/widgets/next.config.js +45 -0
  61. package/templates/typescript-oauth/src/widgets/package-lock.json +4493 -0
  62. package/templates/typescript-oauth/src/widgets/package.json +24 -0
  63. package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
  64. package/templates/typescript-oauth/src/widgets/widget-manifest.json +395 -0
  65. package/templates/typescript-oauth/tsconfig.json +23 -0
  66. package/templates/typescript-pizzaz/README.md +252 -0
  67. package/templates/typescript-pizzaz/package.json +34 -0
  68. package/templates/typescript-pizzaz/src/app.module.ts +28 -0
  69. package/templates/typescript-pizzaz/src/index.ts +30 -0
  70. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.data.ts +106 -0
  71. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.module.ts +11 -0
  72. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.service.ts +60 -0
  73. package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.tools.ts +197 -0
  74. package/templates/typescript-pizzaz/src/widgets/app/layout.tsx +18 -0
  75. package/templates/typescript-pizzaz/src/widgets/app/pizza-list/page.tsx +272 -0
  76. package/templates/typescript-pizzaz/src/widgets/app/pizza-map/page.tsx +216 -0
  77. package/templates/typescript-pizzaz/src/widgets/app/pizza-shop/page.tsx +374 -0
  78. package/templates/typescript-pizzaz/src/widgets/components/CompactShopCard.tsx +144 -0
  79. package/templates/typescript-pizzaz/src/widgets/components/PizzaCard.tsx +191 -0
  80. package/templates/typescript-pizzaz/src/widgets/next.config.js +45 -0
  81. package/templates/typescript-pizzaz/src/widgets/package.json +30 -0
  82. package/templates/typescript-pizzaz/src/widgets/tsconfig.json +28 -0
  83. package/templates/typescript-pizzaz/src/widgets/widget-manifest.json +253 -0
  84. package/templates/typescript-pizzaz/tsconfig.json +30 -0
  85. package/templates/typescript-starter/README.md +320 -0
  86. package/templates/typescript-starter/package.json +25 -0
  87. package/templates/typescript-starter/src/app.module.ts +34 -0
  88. package/templates/typescript-starter/src/health/system.health.ts +55 -0
  89. package/templates/typescript-starter/src/index.ts +29 -0
  90. package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
  91. package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
  92. package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +59 -0
  93. package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +166 -0
  94. package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +180 -0
  95. package/templates/typescript-starter/src/widgets/app/layout.tsx +18 -0
  96. package/templates/typescript-starter/src/widgets/next.config.js +45 -0
  97. package/templates/typescript-starter/src/widgets/package.json +24 -0
  98. package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
  99. package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
  100. package/templates/typescript-starter/tsconfig.json +23 -0
@@ -0,0 +1,263 @@
1
+ # āœˆļø NitroStack Flight Booking
2
+
3
+ A production-ready NitroStack template showcasing real-time flight search and booking with **Duffel API** integration. Search flights, view details, select seats, and book - all through beautiful interactive widgets.
4
+
5
+ ## ✨ Features
6
+
7
+ ### šŸ”Œ **Duffel API Integration**
8
+ - Real-time flight search across 300+ airlines
9
+ - Live pricing and availability
10
+ - Seat selection with cabin maps
11
+ - Booking and order management
12
+
13
+ ### šŸŽØ **Interactive Widgets**
14
+ - **Airport Search** - Autocomplete airport selection
15
+ - **Flight Search Results** - Compare flights with prices
16
+ - **Flight Details** - Comprehensive flight information
17
+ - **Seat Selection** - Visual cabin map with seat picker
18
+ - **Order Summary** - Complete booking overview
19
+ - **Payment Confirmation** - Secure payment flow
20
+
21
+ ### šŸ› ļø **MCP Tools**
22
+ - `search_airports` - Find airports by name/code
23
+ - `search_flights` - Search available flights
24
+ - `show_flight_details` - Detailed flight information
25
+ - `select_seats` - Interactive seat selection
26
+ - `create_booking` - Book flights
27
+ - `get_order` - Retrieve booking details
28
+
29
+ ## šŸš€ Quick Start
30
+
31
+ ### Prerequisites
32
+
33
+ ```bash
34
+ # Install NitroStack CLI globally
35
+ npm install -g nitrostack
36
+
37
+ # Or use npx
38
+ npx nitrostack --version
39
+ ```
40
+
41
+ ### 1. Create Your Project
42
+
43
+ ```bash
44
+ # Create a new project
45
+ nitrostack init my-flight-app --template typescript-oauth
46
+ cd my-flight-app
47
+
48
+ # Install all dependencies (root + widgets)
49
+ nitrostack install
50
+ ```
51
+
52
+ ### 2. Get Your Duffel API Key (Free)
53
+
54
+ [Duffel](https://duffel.com/) provides a free API for flight search and booking:
55
+
56
+ 1. **Sign up** at [duffel.com](https://duffel.com/) (click "Start now" - it's free!)
57
+ 2. Create a free account (no credit card required)
58
+ 3. Go to your **Dashboard** → **Developers** → **Access tokens**
59
+ 4. Click **"Create token"** to generate your API key
60
+ 5. Copy the token (starts with `duffel_test_` for sandbox)
61
+
62
+ > šŸ’” **Tip**: The test mode (`duffel_test_*` tokens) gives you access to realistic mock data for development. Production tokens (`duffel_live_*`) connect to real airlines.
63
+
64
+ ### 3. Configure Environment
65
+
66
+ Copy the example environment file and add your Duffel API key:
67
+
68
+ ```bash
69
+ cp .env.example .env
70
+ ```
71
+
72
+ Edit `.env` and update:
73
+
74
+ ```env
75
+ # Duffel API Configuration
76
+ DUFFEL_API_KEY=duffel_test_your_api_key_here
77
+ ```
78
+
79
+ ### 4. Run Development Server
80
+
81
+ ```bash
82
+ npm run dev
83
+ ```
84
+
85
+ This starts:
86
+ - **MCP Server** - Hot reloads on code changes
87
+ - **Studio** on http://localhost:3000 - Visual testing environment
88
+ - **Widget Dev Server** on http://localhost:3001 - Hot module replacement
89
+
90
+ ### 5. Test in Studio
91
+
92
+ Try these prompts in Studio chat:
93
+ - "Search flights from London to New York for next week"
94
+ - "Show me flight details"
95
+ - "I want to select seats"
96
+ - "Show me flights from JFK to LAX"
97
+
98
+ ## šŸ“ Project Structure
99
+
100
+ ```
101
+ typescript-oauth/
102
+ ā”œā”€ā”€ src/
103
+ │ ā”œā”€ā”€ index.ts # Main server entry
104
+ │ ā”œā”€ā”€ app.module.ts # App module
105
+ │ ā”œā”€ā”€ services/
106
+ │ │ └── duffel.service.ts # Duffel API client
107
+ │ └── modules/
108
+ │ └── flights/
109
+ │ ā”œā”€ā”€ flights.module.ts # Module definition
110
+ │ ā”œā”€ā”€ flights.tools.ts # Search & display tools
111
+ │ ā”œā”€ā”€ flights.prompts.ts # AI prompts
112
+ │ ā”œā”€ā”€ flights.resources.ts # Static resources
113
+ │ └── booking.tools.ts # Booking tools
114
+ │ └── widgets/
115
+ │ ā”œā”€ā”€ app/
116
+ │ │ ā”œā”€ā”€ airport-search/ # Airport autocomplete
117
+ │ │ ā”œā”€ā”€ flight-search-results/ # Results list
118
+ │ │ ā”œā”€ā”€ flight-details/ # Flight info
119
+ │ │ ā”œā”€ā”€ seat-selection/ # Seat picker
120
+ │ │ ā”œā”€ā”€ order-summary/ # Booking summary
121
+ │ │ └── payment-confirmation/ # Payment widget
122
+ ā”œā”€ā”€ .env.example # Environment template
123
+ ā”œā”€ā”€ OAUTH_SETUP.md # OAuth configuration guide
124
+ ā”œā”€ā”€ package.json
125
+ └── README.md
126
+ ```
127
+
128
+ ## šŸ”§ Commands
129
+
130
+ ```bash
131
+ # Installation
132
+ npm install # Install all dependencies (root + widgets)
133
+ nitrostack install # Same as above
134
+
135
+ # Development
136
+ npm run dev # Start dev server with Studio
137
+ npm run build # Build TypeScript and widgets for production
138
+ npm start # Run production server
139
+
140
+ # Upgrade
141
+ npm run upgrade # Upgrade nitrostack to latest version
142
+
143
+ # Widget Management
144
+ npm run widget <command> # Run npm command in widgets directory
145
+ npm run widget add <pkg> # Add a widget dependency
146
+ ```
147
+
148
+ ## šŸ” OAuth Setup (Optional)
149
+
150
+ This template includes OAuth 2.1 authentication support. If you want to protect your MCP server with authentication:
151
+
152
+ 1. Read the detailed guide: **[OAUTH_SETUP.md](./OAUTH_SETUP.md)**
153
+ 2. Configure Auth0 (or your OAuth provider)
154
+ 3. Update your `.env` with OAuth credentials
155
+
156
+ > **Note**: OAuth is optional for local development. The template works without it using Duffel's test API.
157
+
158
+ ## šŸ“Š Duffel API Features
159
+
160
+ ### Test Mode vs Live Mode
161
+
162
+ | Feature | Test Mode (`duffel_test_*`) | Live Mode (`duffel_live_*`) |
163
+ |---------|----------------------------|----------------------------|
164
+ | API Access | āœ… Full access | āœ… Full access |
165
+ | Pricing | Mock data | Real prices |
166
+ | Bookings | Simulated | Real bookings |
167
+ | Credit Card | Not charged | Charged |
168
+ | Rate Limits | Generous | Production limits |
169
+
170
+ ### Supported Airlines
171
+
172
+ Duffel provides access to 300+ airlines including:
173
+ - Major carriers (British Airways, Lufthansa, Emirates, etc.)
174
+ - Low-cost carriers (Ryanair, EasyJet, Spirit, etc.)
175
+ - Regional airlines
176
+
177
+ ### API Capabilities
178
+
179
+ - **Search**: Real-time availability and pricing
180
+ - **Ancillaries**: Seats, bags, meals
181
+ - **Booking**: Create and manage orders
182
+ - **Changes**: Modify existing bookings
183
+ - **Cancellations**: Cancel and refund
184
+
185
+ ## šŸŽØ Widget Features
186
+
187
+ ### Airport Search Widget
188
+ - Autocomplete with IATA codes
189
+ - City and airport name search
190
+ - Recent searches
191
+
192
+ ### Flight Search Results Widget
193
+ - Compare multiple flights
194
+ - Filter by stops, price, time
195
+ - Sort options
196
+ - Airline logos
197
+
198
+ ### Flight Details Widget
199
+ - Complete itinerary
200
+ - Fare breakdown
201
+ - Baggage allowance
202
+ - Flight duration and stops
203
+
204
+ ### Seat Selection Widget
205
+ - Visual cabin map
206
+ - Seat categories (standard, extra legroom, etc.)
207
+ - Price per seat
208
+ - Real-time availability
209
+
210
+ ## šŸ› ļø Customization
211
+
212
+ ### Adding New Airlines
213
+
214
+ Duffel handles airline integrations automatically. You don't need to configure individual airlines.
215
+
216
+ ### Modifying Search Parameters
217
+
218
+ Edit `src/modules/flights/flights.tools.ts` to customize:
219
+ - Default passenger counts
220
+ - Cabin class options
221
+ - Date ranges
222
+ - Result limits
223
+
224
+ ### Styling Widgets
225
+
226
+ Each widget in `src/widgets/app/` can be customized:
227
+ - Edit page.tsx for layout
228
+ - Add custom CSS
229
+ - Modify component styles
230
+
231
+ ## šŸ“š Resources
232
+
233
+ ### Duffel Documentation
234
+ - [Duffel API Docs](https://duffel.com/docs/api)
235
+ - [Duffel SDK (Node.js)](https://github.com/duffel/duffel-api-javascript)
236
+ - [Duffel Postman Collection](https://duffel.com/docs/postman)
237
+
238
+ ### NitroStack Documentation
239
+ - [NitroStack Docs](https://nitrostack.ai/docs)
240
+ - [Widget Development Guide](https://nitrostack.ai/docs/widgets)
241
+
242
+ ## šŸ’” Tips
243
+
244
+ - **Use Test Mode**: Start with `duffel_test_*` tokens for development
245
+ - **Check Rate Limits**: Duffel has rate limits - cache searches when possible
246
+ - **Error Handling**: The template includes comprehensive error handling
247
+ - **Responsive Design**: Widgets are mobile-friendly out of the box
248
+
249
+ ## šŸ“š Next Steps
250
+
251
+ - Try the **Starter Template** - Learn NitroStack basics
252
+ - Try the **Pizza Shop Template** - Interactive maps with Mapbox
253
+ - Read the [Duffel Getting Started Guide](https://duffel.com/docs/guides/getting-started)
254
+
255
+ ## License
256
+
257
+ MIT
258
+
259
+ ---
260
+
261
+ **Built with ā¤ļø using NitroStack + Duffel API**
262
+
263
+ Need help? Check [Duffel Support](https://duffel.com/docs) or [NitroStack Docs](https://nitrostack.ai/docs)
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "nitrostack-flight-booking",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "description": "NitroStack Flight Booking - Real-time flight search and booking with Duffel API integration",
7
+ "scripts": {
8
+ "dev": "nitrostack-cli dev",
9
+ "build": "nitrostack-cli build",
10
+ "start": "npm run build && nitrostack-cli start",
11
+ "start:prod": "nitrostack-cli start",
12
+ "upgrade": "nitrostack-cli upgrade",
13
+ "install:all": "nitrostack-cli install",
14
+ "widget": "npm --prefix src/widgets"
15
+ },
16
+ "dependencies": {
17
+ "nitrostack": "^1",
18
+ "zod": "^3.22.4",
19
+ "dotenv": "^16.3.1",
20
+ "@duffel/api": "^4.21.0",
21
+ "axios": "^1.7.9",
22
+ "date-fns": "^4.1.0"
23
+ },
24
+ "devDependencies": {
25
+ "@nitrostack/cli": "^1",
26
+ "typescript": "^5.3.3"
27
+ },
28
+ "author": ""
29
+ }
@@ -0,0 +1,92 @@
1
+ import { McpApp, Module, ConfigModule, OAuthModule } from 'nitrostack';
2
+ import { FlightsModule } from './modules/flights/flights.module.js';
3
+ import { SystemHealthCheck } from './health/system.health.js';
4
+
5
+ /**
6
+ * Root Application Module
7
+ *
8
+ * This is the main module that bootstraps the MCP server.
9
+ * It registers all feature modules and health checks.
10
+ *
11
+ * OAuth 2.1 Authentication:
12
+ * - Configured with Auth0 as the authorization server
13
+ * - Supports read, write, and admin scopes
14
+ * - Validates tokens with audience binding (RFC 8707)
15
+ *
16
+ * Flight Booking System:
17
+ * - Powered by Duffel API
18
+ * - Professional flight search and booking capabilities
19
+ * - Comprehensive widgets for search results and flight details
20
+ */
21
+ @McpApp({
22
+ module: AppModule,
23
+ server: {
24
+ name: 'airline-ticketing-server',
25
+ version: '1.0.0'
26
+ },
27
+ logging: {
28
+ level: 'info'
29
+ }
30
+ })
31
+ @Module({
32
+ name: 'app',
33
+ description: 'Airline ticketing MCP server with OAuth 2.1 authentication and Duffel integration',
34
+ imports: [
35
+ ConfigModule.forRoot(),
36
+
37
+ // Enable OAuth 2.1 authentication
38
+ OAuthModule.forRoot({
39
+ // Resource URI - YOUR MCP server's public URL
40
+ // This is used for token audience binding (RFC 8707)
41
+ // CRITICAL: Tokens must be issued specifically for this URI
42
+ resourceUri: process.env.RESOURCE_URI || 'https://mcplocal',
43
+
44
+ // Authorization Server(s) - The OAuth provider URL(s)
45
+ // Supports multiple auth servers for federation scenarios
46
+ authorizationServers: [
47
+ process.env.AUTH_SERVER_URL || 'https://dev-5dt0utuk31h13tjm.us.auth0.com',
48
+ ],
49
+
50
+ // Supported scopes for this MCP server
51
+ // Define what permissions your server supports
52
+ scopesSupported: [
53
+ 'read', // Read access to resources
54
+ 'write', // Write/modify resources
55
+ 'admin', // Administrative operations
56
+ ],
57
+
58
+ // Token Introspection (RFC 7662) - For opaque tokens
59
+ // If your OAuth provider issues opaque tokens (not JWTs),
60
+ // you MUST configure introspection to validate them
61
+ tokenIntrospectionEndpoint: process.env.INTROSPECTION_ENDPOINT,
62
+ tokenIntrospectionClientId: process.env.INTROSPECTION_CLIENT_ID,
63
+ tokenIntrospectionClientSecret: process.env.INTROSPECTION_CLIENT_SECRET,
64
+
65
+ // Expected audience (defaults to resourceUri if not provided)
66
+ // MUST match the audience claim in tokens (RFC 8707)
67
+ audience: process.env.TOKEN_AUDIENCE,
68
+
69
+ // Expected issuer (optional but recommended)
70
+ // If provided, tokens must be from this issuer
71
+ issuer: process.env.TOKEN_ISSUER,
72
+
73
+ // Custom validation (optional)
74
+ // Add any additional validation logic beyond spec requirements
75
+ customValidation: async (tokenPayload) => {
76
+ // Example: Check if user is active in your database
77
+ // const user = await db.users.findOne({ id: tokenPayload.sub });
78
+ // return user?.active === true;
79
+
80
+ // For now, accept all valid tokens
81
+ return true;
82
+ },
83
+ }),
84
+
85
+ FlightsModule
86
+ ],
87
+ providers: [
88
+ // Health Checks
89
+ SystemHealthCheck,
90
+ ]
91
+ })
92
+ export class AppModule { }
@@ -0,0 +1,126 @@
1
+ import { Guard, ExecutionContext, OAuthModule, OAuthTokenPayload } from 'nitrostack';
2
+
3
+ /**
4
+ * OAuth Guard
5
+ *
6
+ * Validates OAuth 2.1 access tokens according to MCP specification.
7
+ *
8
+ * Performs:
9
+ * - Token validation (introspection or JWT validation)
10
+ * - Audience binding (RFC 8707) - CRITICAL for security
11
+ * - Scope validation
12
+ * - Expiration checking
13
+ *
14
+ * Compatible with:
15
+ * - OpenAI Apps SDK
16
+ * - Any RFC-compliant OAuth 2.1 provider
17
+ *
18
+ * Usage:
19
+ * ```typescript
20
+ * @Tool({
21
+ * name: 'protected_resource',
22
+ * description: 'A protected tool'
23
+ * })
24
+ * @UseGuards(OAuthGuard)
25
+ * async protectedTool() {
26
+ * // Only accessible with valid OAuth token
27
+ * }
28
+ * ```
29
+ */
30
+ export class OAuthGuard implements Guard {
31
+ async canActivate(context: ExecutionContext): Promise<boolean> {
32
+ // Extract token from metadata (sent by Studio or OAuth client)
33
+ const authHeader = context.metadata?.authorization as string;
34
+ const metaToken = context.metadata?._oauth || context.metadata?.token;
35
+
36
+ let token: string | null = null;
37
+
38
+ // Try Bearer token format first
39
+ if (authHeader?.startsWith('Bearer ')) {
40
+ token = authHeader.substring(7);
41
+ } else if (metaToken) {
42
+ token = metaToken as string;
43
+ }
44
+
45
+ if (!token) {
46
+ throw new Error(
47
+ 'OAuth token required. Please authenticate in Studio (Auth → OAuth 2.1 tab) ' +
48
+ 'or provide token in Authorization header: "Bearer <token>"'
49
+ );
50
+ }
51
+
52
+ // Validate token using OAuthModule
53
+ const result = await OAuthModule.validateToken(token);
54
+
55
+ if (!result.valid) {
56
+ throw new Error(`OAuth token validation failed: ${result.error}`);
57
+ }
58
+
59
+ // Extract scopes from token payload
60
+ const payload = result.payload as OAuthTokenPayload;
61
+ const scopes = this.extractScopes(payload);
62
+
63
+ // Populate context.auth with OAuth token information
64
+ context.auth = {
65
+ subject: payload.sub,
66
+ scopes: scopes,
67
+ clientId: payload.client_id,
68
+ tokenPayload: payload,
69
+ };
70
+
71
+ return true;
72
+ }
73
+
74
+ /**
75
+ * Extract scopes from token payload
76
+ * Handles both "scope" (space-separated string) and "scopes" (array) formats
77
+ */
78
+ private extractScopes(payload: OAuthTokenPayload): string[] {
79
+ if (payload.scopes && Array.isArray(payload.scopes)) {
80
+ return payload.scopes;
81
+ }
82
+
83
+ if (payload.scope && typeof payload.scope === 'string') {
84
+ return payload.scope.split(' ').filter(s => s.length > 0);
85
+ }
86
+
87
+ return [];
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Scope Guard
93
+ *
94
+ * Validates that the OAuth token has required scopes.
95
+ * Use this in addition to OAuthGuard for fine-grained access control.
96
+ *
97
+ * Usage:
98
+ * ```typescript
99
+ * @Tool({ name: 'admin_action' })
100
+ * @UseGuards(OAuthGuard, createScopeGuard(['admin', 'write']))
101
+ * async adminAction() {
102
+ * // Requires OAuth token with 'admin' AND 'write' scopes
103
+ * }
104
+ * ```
105
+ */
106
+ export function createScopeGuard(requiredScopes: string[]): new () => Guard {
107
+ return class ScopeGuard implements Guard {
108
+ async canActivate(context: ExecutionContext): Promise<boolean> {
109
+ const userScopes = context.auth?.scopes || [];
110
+
111
+ const missingScopes = requiredScopes.filter(
112
+ scope => !userScopes.includes(scope)
113
+ );
114
+
115
+ if (missingScopes.length > 0) {
116
+ throw new Error(
117
+ `Insufficient scope. Required: ${requiredScopes.join(', ')}. ` +
118
+ `Missing: ${missingScopes.join(', ')}`
119
+ );
120
+ }
121
+
122
+ return true;
123
+ }
124
+ };
125
+ }
126
+
@@ -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,63 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Calculator MCP Server with OAuth 2.1 Authentication
4
+ *
5
+ * Main entry point for the MCP server.
6
+ * Uses the @McpApp decorator pattern for clean, NestJS-style architecture.
7
+ *
8
+ * OAuth 2.1 Compliance:
9
+ * - MCP Specification: https://modelcontextprotocol.io/specification/draft/basic/authorization
10
+ * - OpenAI Apps SDK: https://developers.openai.com/apps-sdk/build/auth
11
+ * - RFC 9728 - Protected Resource Metadata
12
+ * - RFC 8707 - Resource Indicators (Token Audience Binding)
13
+ *
14
+ * Transport Configuration:
15
+ * - Development (NODE_ENV=development): STDIO only
16
+ * - Production (NODE_ENV=production): Dual transport (STDIO + HTTP SSE)
17
+ * - With OAuth: Dual mode (STDIO + HTTP for metadata endpoints)
18
+ */
19
+
20
+ import 'dotenv/config';
21
+ import { McpApplicationFactory } from 'nitrostack';
22
+ import { AppModule } from './app.module.js';
23
+
24
+ /**
25
+ * Bootstrap the application
26
+ */
27
+ async function bootstrap() {
28
+ try {
29
+ console.error('šŸ” Starting Calculator MCP Server with OAuth 2.1...\\n');
30
+
31
+ // Validate required environment variables for OAuth
32
+ const requiredEnvVars = ['RESOURCE_URI', 'AUTH_SERVER_URL'];
33
+ const missing = requiredEnvVars.filter(v => !process.env[v]);
34
+
35
+ if (missing.length > 0) {
36
+ console.error('āŒ Missing required OAuth environment variables:');
37
+ missing.forEach(v => console.error(` - ${v}`));
38
+ console.error('\\nšŸ’” Copy .env.example to .env and configure your OAuth provider');
39
+ console.error(' Or check the test-oauth/.env for reference\\n');
40
+ process.exit(1);
41
+ }
42
+
43
+ // Create the MCP application
44
+ const server = await McpApplicationFactory.create(AppModule);
45
+
46
+ console.error('āœ… OAuth 2.1 Module configured');
47
+ console.error(` Resource URI: ${process.env.RESOURCE_URI}`);
48
+ console.error(` Auth Server: ${process.env.AUTH_SERVER_URL}`);
49
+ console.error(` Scopes: read, write, admin`);
50
+ console.error(` Audience: ${process.env.TOKEN_AUDIENCE || process.env.RESOURCE_URI}\\n`);
51
+
52
+ // Start the server
53
+ await server.start();
54
+
55
+ } catch (error) {
56
+ console.error('āŒ Failed to start server:', error);
57
+ console.error('\\nšŸ’” Check your OAuth configuration in .env\\n');
58
+ process.exit(1);
59
+ }
60
+ }
61
+
62
+ // Start the application
63
+ bootstrap();