nitrostack 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.
- package/CHANGELOG.md +227 -0
- package/CONTRIBUTING.md +182 -0
- package/LICENSE +201 -0
- package/LICENSE_URLS_UPDATE_COMPLETE.md +388 -0
- package/NOTICE +153 -0
- package/README.md +571 -0
- package/dist/auth/api-key.d.ts +118 -0
- package/dist/auth/api-key.d.ts.map +1 -0
- package/dist/auth/api-key.js +168 -0
- package/dist/auth/api-key.js.map +1 -0
- package/dist/auth/client.d.ts +151 -0
- package/dist/auth/client.d.ts.map +1 -0
- package/dist/auth/client.js +330 -0
- package/dist/auth/client.js.map +1 -0
- package/dist/auth/index.d.ts +30 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +43 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware.d.ts +95 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +260 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/pkce.d.ts +53 -0
- package/dist/auth/pkce.d.ts.map +1 -0
- package/dist/auth/pkce.js +105 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/quick-setup.d.ts +94 -0
- package/dist/auth/quick-setup.d.ts.map +1 -0
- package/dist/auth/quick-setup.js +210 -0
- package/dist/auth/quick-setup.js.map +1 -0
- package/dist/auth/server-integration.d.ts +97 -0
- package/dist/auth/server-integration.d.ts.map +1 -0
- package/dist/auth/server-integration.js +182 -0
- package/dist/auth/server-integration.js.map +1 -0
- package/dist/auth/server-metadata.d.ts +51 -0
- package/dist/auth/server-metadata.d.ts.map +1 -0
- package/dist/auth/server-metadata.js +106 -0
- package/dist/auth/server-metadata.js.map +1 -0
- package/dist/auth/simple-jwt.d.ts +88 -0
- package/dist/auth/simple-jwt.d.ts.map +1 -0
- package/dist/auth/simple-jwt.js +152 -0
- package/dist/auth/simple-jwt.js.map +1 -0
- package/dist/auth/token-store.d.ts +104 -0
- package/dist/auth/token-store.d.ts.map +1 -0
- package/dist/auth/token-store.js +205 -0
- package/dist/auth/token-store.js.map +1 -0
- package/dist/auth/token-validation.d.ts +47 -0
- package/dist/auth/token-validation.d.ts.map +1 -0
- package/dist/auth/token-validation.js +237 -0
- package/dist/auth/token-validation.js.map +1 -0
- package/dist/auth/types.d.ts +215 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +6 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/cli/commands/build.d.ts +6 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +104 -0
- package/dist/cli/commands/build.js.map +1 -0
- package/dist/cli/commands/dev.d.ts +7 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +312 -0
- package/dist/cli/commands/dev.js.map +1 -0
- package/dist/cli/commands/generate-types.d.ts +8 -0
- package/dist/cli/commands/generate-types.d.ts.map +1 -0
- package/dist/cli/commands/generate-types.js +220 -0
- package/dist/cli/commands/generate-types.js.map +1 -0
- package/dist/cli/commands/generate.d.ts +5 -0
- package/dist/cli/commands/generate.d.ts.map +1 -0
- package/dist/cli/commands/generate.js +365 -0
- package/dist/cli/commands/generate.js.map +1 -0
- package/dist/cli/commands/init.d.ts +7 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +365 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/start.d.ts +6 -0
- package/dist/cli/commands/start.d.ts.map +1 -0
- package/dist/cli/commands/start.js +61 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +47 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/mcp-dev-wrapper.d.ts +3 -0
- package/dist/cli/mcp-dev-wrapper.d.ts.map +1 -0
- package/dist/cli/mcp-dev-wrapper.js +116 -0
- package/dist/cli/mcp-dev-wrapper.js.map +1 -0
- package/dist/core/apikey-module.d.ts +69 -0
- package/dist/core/apikey-module.d.ts.map +1 -0
- package/dist/core/apikey-module.js +114 -0
- package/dist/core/apikey-module.js.map +1 -0
- package/dist/core/app-decorator.d.ts +58 -0
- package/dist/core/app-decorator.d.ts.map +1 -0
- package/dist/core/app-decorator.js +261 -0
- package/dist/core/app-decorator.js.map +1 -0
- package/dist/core/builders.d.ts +38 -0
- package/dist/core/builders.d.ts.map +1 -0
- package/dist/core/builders.js +129 -0
- package/dist/core/builders.js.map +1 -0
- package/dist/core/component.d.ts +105 -0
- package/dist/core/component.d.ts.map +1 -0
- package/dist/core/component.js +182 -0
- package/dist/core/component.js.map +1 -0
- package/dist/core/config-module.d.ts +55 -0
- package/dist/core/config-module.d.ts.map +1 -0
- package/dist/core/config-module.js +94 -0
- package/dist/core/config-module.js.map +1 -0
- package/dist/core/decorators/cache.decorator.d.ts +61 -0
- package/dist/core/decorators/cache.decorator.d.ts.map +1 -0
- package/dist/core/decorators/cache.decorator.js +115 -0
- package/dist/core/decorators/cache.decorator.js.map +1 -0
- package/dist/core/decorators/health-check.decorator.d.ts +80 -0
- package/dist/core/decorators/health-check.decorator.d.ts.map +1 -0
- package/dist/core/decorators/health-check.decorator.js +153 -0
- package/dist/core/decorators/health-check.decorator.js.map +1 -0
- package/dist/core/decorators/rate-limit.decorator.d.ts +62 -0
- package/dist/core/decorators/rate-limit.decorator.d.ts.map +1 -0
- package/dist/core/decorators/rate-limit.decorator.js +129 -0
- package/dist/core/decorators/rate-limit.decorator.js.map +1 -0
- package/dist/core/decorators.d.ts +151 -0
- package/dist/core/decorators.d.ts.map +1 -0
- package/dist/core/decorators.js +142 -0
- package/dist/core/decorators.js.map +1 -0
- package/dist/core/di/container.d.ts +42 -0
- package/dist/core/di/container.d.ts.map +1 -0
- package/dist/core/di/container.js +76 -0
- package/dist/core/di/container.js.map +1 -0
- package/dist/core/di/injectable.decorator.d.ts +35 -0
- package/dist/core/di/injectable.decorator.d.ts.map +1 -0
- package/dist/core/di/injectable.decorator.js +57 -0
- package/dist/core/di/injectable.decorator.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +87 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/events/event-emitter.d.ts +50 -0
- package/dist/core/events/event-emitter.d.ts.map +1 -0
- package/dist/core/events/event-emitter.js +94 -0
- package/dist/core/events/event-emitter.js.map +1 -0
- package/dist/core/events/event.decorator.d.ts +48 -0
- package/dist/core/events/event.decorator.d.ts.map +1 -0
- package/dist/core/events/event.decorator.js +68 -0
- package/dist/core/events/event.decorator.js.map +1 -0
- package/dist/core/filters/exception-filter.decorator.d.ts +40 -0
- package/dist/core/filters/exception-filter.decorator.d.ts.map +1 -0
- package/dist/core/filters/exception-filter.decorator.js +54 -0
- package/dist/core/filters/exception-filter.decorator.js.map +1 -0
- package/dist/core/filters/exception-filter.interface.d.ts +30 -0
- package/dist/core/filters/exception-filter.interface.d.ts.map +1 -0
- package/dist/core/filters/exception-filter.interface.js +2 -0
- package/dist/core/filters/exception-filter.interface.js.map +1 -0
- package/dist/core/guards/apikey.guard.d.ts +22 -0
- package/dist/core/guards/apikey.guard.d.ts.map +1 -0
- package/dist/core/guards/apikey.guard.js +11 -0
- package/dist/core/guards/apikey.guard.js.map +1 -0
- package/dist/core/guards/guard.interface.d.ts +18 -0
- package/dist/core/guards/guard.interface.d.ts.map +1 -0
- package/dist/core/guards/guard.interface.js +2 -0
- package/dist/core/guards/guard.interface.js.map +1 -0
- package/dist/core/guards/jwt.guard.d.ts +18 -0
- package/dist/core/guards/jwt.guard.d.ts.map +1 -0
- package/dist/core/guards/jwt.guard.js +2 -0
- package/dist/core/guards/jwt.guard.js.map +1 -0
- package/dist/core/guards/oauth.guard.d.ts +35 -0
- package/dist/core/guards/oauth.guard.d.ts.map +1 -0
- package/dist/core/guards/oauth.guard.js +2 -0
- package/dist/core/guards/oauth.guard.js.map +1 -0
- package/dist/core/guards/use-guards.decorator.d.ts +25 -0
- package/dist/core/guards/use-guards.decorator.d.ts.map +1 -0
- package/dist/core/guards/use-guards.decorator.js +32 -0
- package/dist/core/guards/use-guards.decorator.js.map +1 -0
- package/dist/core/health/health-checks.resource.d.ts +14 -0
- package/dist/core/health/health-checks.resource.d.ts.map +1 -0
- package/dist/core/health/health-checks.resource.js +29 -0
- package/dist/core/health/health-checks.resource.js.map +1 -0
- package/dist/core/index.d.ts +55 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +57 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/interceptors/interceptor.decorator.d.ts +37 -0
- package/dist/core/interceptors/interceptor.decorator.d.ts.map +1 -0
- package/dist/core/interceptors/interceptor.decorator.js +51 -0
- package/dist/core/interceptors/interceptor.decorator.js.map +1 -0
- package/dist/core/interceptors/interceptor.interface.d.ts +31 -0
- package/dist/core/interceptors/interceptor.interface.d.ts.map +1 -0
- package/dist/core/interceptors/interceptor.interface.js +2 -0
- package/dist/core/interceptors/interceptor.interface.js.map +1 -0
- package/dist/core/jwt-module.d.ts +51 -0
- package/dist/core/jwt-module.d.ts.map +1 -0
- package/dist/core/jwt-module.js +52 -0
- package/dist/core/jwt-module.js.map +1 -0
- package/dist/core/logger.d.ts +18 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +51 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/middleware/middleware.decorator.d.ts +39 -0
- package/dist/core/middleware/middleware.decorator.d.ts.map +1 -0
- package/dist/core/middleware/middleware.decorator.js +53 -0
- package/dist/core/middleware/middleware.decorator.js.map +1 -0
- package/dist/core/middleware/middleware.interface.d.ts +29 -0
- package/dist/core/middleware/middleware.interface.d.ts.map +1 -0
- package/dist/core/middleware/middleware.interface.js +2 -0
- package/dist/core/middleware/middleware.interface.js.map +1 -0
- package/dist/core/module.d.ts +74 -0
- package/dist/core/module.d.ts.map +1 -0
- package/dist/core/module.js +82 -0
- package/dist/core/module.js.map +1 -0
- package/dist/core/oauth-module.d.ts +144 -0
- package/dist/core/oauth-module.d.ts.map +1 -0
- package/dist/core/oauth-module.js +190 -0
- package/dist/core/oauth-module.js.map +1 -0
- package/dist/core/pipes/pipe.decorator.d.ts +55 -0
- package/dist/core/pipes/pipe.decorator.d.ts.map +1 -0
- package/dist/core/pipes/pipe.decorator.js +85 -0
- package/dist/core/pipes/pipe.decorator.js.map +1 -0
- package/dist/core/pipes/pipe.interface.d.ts +36 -0
- package/dist/core/pipes/pipe.interface.d.ts.map +1 -0
- package/dist/core/pipes/pipe.interface.js +2 -0
- package/dist/core/pipes/pipe.interface.js.map +1 -0
- package/dist/core/prompt.d.ts +37 -0
- package/dist/core/prompt.d.ts.map +1 -0
- package/dist/core/prompt.js +76 -0
- package/dist/core/prompt.js.map +1 -0
- package/dist/core/resource.d.ts +42 -0
- package/dist/core/resource.d.ts.map +1 -0
- package/dist/core/resource.js +90 -0
- package/dist/core/resource.js.map +1 -0
- package/dist/core/server.d.ts +72 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/server.js +406 -0
- package/dist/core/server.js.map +1 -0
- package/dist/core/tool.d.ts +78 -0
- package/dist/core/tool.d.ts.map +1 -0
- package/dist/core/tool.js +190 -0
- package/dist/core/tool.js.map +1 -0
- package/dist/core/transports/http-server.d.ts +102 -0
- package/dist/core/transports/http-server.d.ts.map +1 -0
- package/dist/core/transports/http-server.js +265 -0
- package/dist/core/transports/http-server.js.map +1 -0
- package/dist/core/types.d.ts +123 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/widgets/widget-examples.resource.d.ts +17 -0
- package/dist/core/widgets/widget-examples.resource.d.ts.map +1 -0
- package/dist/core/widgets/widget-examples.resource.js +28 -0
- package/dist/core/widgets/widget-examples.resource.js.map +1 -0
- package/dist/core/widgets/widget-registry.d.ts +56 -0
- package/dist/core/widgets/widget-registry.d.ts.map +1 -0
- package/dist/core/widgets/widget-registry.js +75 -0
- package/dist/core/widgets/widget-registry.js.map +1 -0
- package/dist/testing/index.d.ts +82 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +164 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/ui-next/index.d.ts +31 -0
- package/dist/ui-next/index.d.ts.map +1 -0
- package/dist/ui-next/index.js +687 -0
- package/dist/ui-next/index.js.map +1 -0
- package/dist/widgets/index.d.ts +9 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +9 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/metadata.d.ts +53 -0
- package/dist/widgets/metadata.d.ts.map +1 -0
- package/dist/widgets/metadata.js +29 -0
- package/dist/widgets/metadata.js.map +1 -0
- package/dist/widgets/withToolData.d.ts +19 -0
- package/dist/widgets/withToolData.d.ts.map +1 -0
- package/dist/widgets/withToolData.js +240 -0
- package/dist/widgets/withToolData.js.map +1 -0
- package/jest.config.js +21 -0
- package/package.json +108 -0
- package/templates/typescript-auth/AI_AGENT_CLI_REFERENCE.md +702 -0
- package/templates/typescript-auth/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-auth/README.md +400 -0
- package/templates/typescript-auth/package.json +44 -0
- package/templates/typescript-auth-api-key/AI_AGENT_CLI_REFERENCE.md +701 -0
- package/templates/typescript-auth-api-key/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-auth-api-key/README.md +483 -0
- package/templates/typescript-auth-api-key/package-lock.json +124 -0
- package/templates/typescript-auth-api-key/package.json +29 -0
- package/templates/typescript-oauth/AI_AGENT_CLI_REFERENCE.md +701 -0
- package/templates/typescript-oauth/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-oauth/OAUTH_SETUP.md +406 -0
- package/templates/typescript-oauth/README.md +350 -0
- package/templates/typescript-oauth/package.json +30 -0
- package/templates/typescript-starter/AI_AGENT_CLI_REFERENCE.md +701 -0
- package/templates/typescript-starter/AI_AGENT_SDK_REFERENCE.md +1260 -0
- package/templates/typescript-starter/README.md +312 -0
- package/templates/typescript-starter/package.json +32 -0
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
# API Key Authentication MCP Server
|
|
2
|
+
|
|
3
|
+
A complete example of building an MCP server with **API key authentication** using NitroStack.
|
|
4
|
+
|
|
5
|
+
## 🎯 What You'll Learn
|
|
6
|
+
|
|
7
|
+
- ✅ How to add API key authentication to your MCP server
|
|
8
|
+
- ✅ How to create public and protected tools
|
|
9
|
+
- ✅ How to use `ApiKeyModule` and `ApiKeyGuard`
|
|
10
|
+
- ✅ How to test API key auth in NitroStack Studio
|
|
11
|
+
- ✅ How to combine JWT and API key authentication (multi-auth)
|
|
12
|
+
|
|
13
|
+
## 🔑 Test API Keys
|
|
14
|
+
|
|
15
|
+
This template provides **test API keys** for immediate testing:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
sk_test_public_demo_key_12345
|
|
19
|
+
sk_test_admin_demo_key_67890
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
⚠️ **Important**: These are demo keys only. Never use them in production!
|
|
23
|
+
|
|
24
|
+
## 📦 Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Install dependencies
|
|
28
|
+
npm install
|
|
29
|
+
|
|
30
|
+
# Build the project
|
|
31
|
+
npm run build
|
|
32
|
+
|
|
33
|
+
# Run in development mode
|
|
34
|
+
npm run dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## 🚀 Quick Start
|
|
38
|
+
|
|
39
|
+
### 1. Start the Server
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm run build
|
|
43
|
+
nitrostack dev
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The server will start with API key authentication enabled.
|
|
47
|
+
|
|
48
|
+
### 2. Open NitroStack Studio
|
|
49
|
+
|
|
50
|
+
Navigate to `http://localhost:3000` (Studio should auto-open)
|
|
51
|
+
|
|
52
|
+
### 3. Set Your API Key
|
|
53
|
+
|
|
54
|
+
1. Go to **Auth** tab in Studio
|
|
55
|
+
2. Find the **API Key** section
|
|
56
|
+
3. Paste one of the test keys:
|
|
57
|
+
```
|
|
58
|
+
sk_test_public_demo_key_12345
|
|
59
|
+
```
|
|
60
|
+
4. Click **Set Key**
|
|
61
|
+
|
|
62
|
+
### 4. Test the Tools
|
|
63
|
+
|
|
64
|
+
#### Try a Public Tool (No Auth Required)
|
|
65
|
+
1. Go to **Tools** tab
|
|
66
|
+
2. Select `get_public_info`
|
|
67
|
+
3. Enter any topic
|
|
68
|
+
4. Execute ✓ - Works without API key!
|
|
69
|
+
|
|
70
|
+
#### Try a Protected Tool (Auth Required)
|
|
71
|
+
1. Select `get_protected_data`
|
|
72
|
+
2. Choose data type: `user`, `account`, or `settings`
|
|
73
|
+
3. Execute ✓ - Works with valid API key!
|
|
74
|
+
4. Try clearing your API key → You'll get an authentication error
|
|
75
|
+
|
|
76
|
+
## 🏗️ Project Structure
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
typescript-auth-api-key/
|
|
80
|
+
├── src/
|
|
81
|
+
│ ├── guards/
|
|
82
|
+
│ │ └── apikey.guard.ts # API key validation guard
|
|
83
|
+
│ ├── modules/
|
|
84
|
+
│ │ └── demo/
|
|
85
|
+
│ │ ├── demo.module.ts # Demo module
|
|
86
|
+
│ │ └── demo.tools.ts # Example tools
|
|
87
|
+
│ ├── app.module.ts # Root module
|
|
88
|
+
│ └── index.ts # Main entry point (ApiKeyModule setup)
|
|
89
|
+
├── .env # Test API keys
|
|
90
|
+
├── package.json
|
|
91
|
+
├── tsconfig.json
|
|
92
|
+
└── README.md
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 🛡️ How API Key Auth Works
|
|
96
|
+
|
|
97
|
+
### 1. Configure ApiKeyModule
|
|
98
|
+
|
|
99
|
+
In `src/index.ts`:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const app = await McpApplicationFactory.create(AppModule, {
|
|
103
|
+
// Enable API key authentication
|
|
104
|
+
apiKey: ApiKeyModule.forRoot({
|
|
105
|
+
keysEnvPrefix: 'API_KEY', // Reads API_KEY_1, API_KEY_2, etc.
|
|
106
|
+
headerName: 'x-api-key',
|
|
107
|
+
metadataField: 'apiKey',
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### 2. Create an API Key Guard
|
|
113
|
+
|
|
114
|
+
In `src/guards/apikey.guard.ts`:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
export class ApiKeyGuard implements Guard {
|
|
118
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
119
|
+
const apiKey = context.metadata?.apiKey;
|
|
120
|
+
|
|
121
|
+
if (!apiKey) {
|
|
122
|
+
throw new Error('API key required');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const isValid = await ApiKeyModule.validate(apiKey);
|
|
126
|
+
|
|
127
|
+
if (!isValid) {
|
|
128
|
+
throw new Error('Invalid API key');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
context.auth = {
|
|
132
|
+
subject: `apikey_${apiKey.substring(0, 12)}`,
|
|
133
|
+
scopes: ['*'],
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 3. Protect Your Tools
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
@Injectable()
|
|
145
|
+
export class DemoTools {
|
|
146
|
+
|
|
147
|
+
// PUBLIC: Anyone can call
|
|
148
|
+
@Tool({ name: 'get_public_info' })
|
|
149
|
+
async getPublicInfo() {
|
|
150
|
+
return { data: 'public' };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// PROTECTED: Requires API key
|
|
154
|
+
@Tool({ name: 'get_protected_data' })
|
|
155
|
+
@UseGuards(ApiKeyGuard) // 🔒 Protected!
|
|
156
|
+
async getProtectedData() {
|
|
157
|
+
return { data: 'protected' };
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## 🔐 API Key Management
|
|
163
|
+
|
|
164
|
+
### Environment Variables
|
|
165
|
+
|
|
166
|
+
API keys are loaded from environment variables:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# .env
|
|
170
|
+
API_KEY_1=sk_test_public_demo_key_12345
|
|
171
|
+
API_KEY_2=sk_test_admin_demo_key_67890
|
|
172
|
+
API_KEY_3=your_custom_key_here
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
The `ApiKeyModule` automatically reads all `API_KEY_*` variables.
|
|
176
|
+
|
|
177
|
+
### Studio Integration
|
|
178
|
+
|
|
179
|
+
NitroStack Studio provides a **dedicated API Key UI**:
|
|
180
|
+
|
|
181
|
+
1. **Auth Tab** → **API Key** section
|
|
182
|
+
2. Enter your API key
|
|
183
|
+
3. It's automatically included in all tool calls
|
|
184
|
+
4. Persists across page refreshes
|
|
185
|
+
5. Clear button to reset
|
|
186
|
+
|
|
187
|
+
### How Keys Are Sent
|
|
188
|
+
|
|
189
|
+
When you execute a tool in Studio, the API key is sent in the `_meta` field:
|
|
190
|
+
|
|
191
|
+
```javascript
|
|
192
|
+
{
|
|
193
|
+
args: { /* your tool arguments */ },
|
|
194
|
+
_meta: {
|
|
195
|
+
apiKey: "sk_test_public_demo_key_12345",
|
|
196
|
+
"x-api-key": "sk_test_public_demo_key_12345"
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The guard extracts it and validates it.
|
|
202
|
+
|
|
203
|
+
## 🔄 Multi-Auth Example (JWT + API Key)
|
|
204
|
+
|
|
205
|
+
You can use **both JWT and API key** authentication together!
|
|
206
|
+
|
|
207
|
+
### Example: JWT Guard + API Key Guard
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
// Guard that accepts EITHER JWT OR API Key
|
|
211
|
+
export class MultiAuthGuard implements Guard {
|
|
212
|
+
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
213
|
+
// Try JWT first
|
|
214
|
+
const jwt = context.metadata?._jwt || context.metadata?.authorization;
|
|
215
|
+
if (jwt) {
|
|
216
|
+
// Validate JWT...
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Try API key
|
|
221
|
+
const apiKey = context.metadata?.apiKey;
|
|
222
|
+
if (apiKey) {
|
|
223
|
+
const isValid = await ApiKeyModule.validate(apiKey);
|
|
224
|
+
return isValid;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
throw new Error('Either JWT or API key required');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Use on a tool
|
|
232
|
+
@Tool({ name: 'multi_auth_tool' })
|
|
233
|
+
@UseGuards(MultiAuthGuard)
|
|
234
|
+
async multiAuthTool() {
|
|
235
|
+
// Accessible with JWT OR API key
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Example: Both Required
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
@Tool({ name: 'double_auth_tool' })
|
|
243
|
+
@UseGuards(JWTGuard, ApiKeyGuard) // Both required!
|
|
244
|
+
async doubleAuthTool() {
|
|
245
|
+
// Requires BOTH JWT token AND API key
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## 🧪 Testing Your Tools
|
|
250
|
+
|
|
251
|
+
### In NitroStack Studio
|
|
252
|
+
|
|
253
|
+
1. **Set API Key** in Auth tab
|
|
254
|
+
2. Navigate to **Tools** tab
|
|
255
|
+
3. Test public tools (work without key)
|
|
256
|
+
4. Test protected tools (work with key)
|
|
257
|
+
5. Test `check_api_key_status` to verify your setup
|
|
258
|
+
|
|
259
|
+
### Programmatically (TypeScript Client)
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
import { McpClient } from 'nitrostack';
|
|
263
|
+
|
|
264
|
+
const client = new McpClient({
|
|
265
|
+
transport: 'stdio',
|
|
266
|
+
command: 'node',
|
|
267
|
+
args: ['dist/index.js'],
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
await client.connect();
|
|
271
|
+
|
|
272
|
+
// Call protected tool with API key
|
|
273
|
+
const result = await client.callTool('get_protected_data', {
|
|
274
|
+
dataType: 'user',
|
|
275
|
+
_meta: {
|
|
276
|
+
apiKey: 'sk_test_public_demo_key_12345'
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
console.log(result);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### In Claude Desktop or Cline
|
|
284
|
+
|
|
285
|
+
Add to your MCP settings:
|
|
286
|
+
|
|
287
|
+
```json
|
|
288
|
+
{
|
|
289
|
+
"mcpServers": {
|
|
290
|
+
"api-key-auth": {
|
|
291
|
+
"command": "node",
|
|
292
|
+
"args": ["/path/to/dist/index.js"],
|
|
293
|
+
"env": {
|
|
294
|
+
"API_KEY_1": "sk_test_public_demo_key_12345"
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## 🔒 Production Best Practices
|
|
302
|
+
|
|
303
|
+
### 1. Use Hashed Keys
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
apiKey: ApiKeyModule.forRoot({
|
|
307
|
+
keysEnvPrefix: 'API_KEY',
|
|
308
|
+
hashed: true, // Store keys as SHA-256 hashes
|
|
309
|
+
})
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
Then hash your keys before storing:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { ApiKeyModule } from 'nitrostack';
|
|
316
|
+
|
|
317
|
+
const apiKey = 'sk_prod_real_secret_key';
|
|
318
|
+
const hashed = ApiKeyModule.hashKey(apiKey);
|
|
319
|
+
console.log('Store this in .env:', hashed);
|
|
320
|
+
// API_KEY_1=<hash>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### 2. Use Strong Keys
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { ApiKeyModule } from 'nitrostack';
|
|
327
|
+
|
|
328
|
+
// Generate cryptographically secure keys
|
|
329
|
+
const newKey = ApiKeyModule.generateKey('sk');
|
|
330
|
+
console.log(newKey);
|
|
331
|
+
// sk_vK8mQ2xPnL...
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### 3. Store Keys Securely
|
|
335
|
+
|
|
336
|
+
- ✅ Use environment variables
|
|
337
|
+
- ✅ Use secret management (AWS Secrets Manager, Vault, etc.)
|
|
338
|
+
- ❌ Never commit keys to git
|
|
339
|
+
- ❌ Never hardcode keys in source
|
|
340
|
+
|
|
341
|
+
### 4. Custom Validation
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
apiKey: ApiKeyModule.forRoot({
|
|
345
|
+
keysEnvPrefix: 'API_KEY',
|
|
346
|
+
customValidation: async (key) => {
|
|
347
|
+
// Check against database, rate limits, expiration, etc.
|
|
348
|
+
const keyRecord = await db.apiKeys.findOne({ key });
|
|
349
|
+
return keyRecord && !keyRecord.expired;
|
|
350
|
+
},
|
|
351
|
+
})
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## 📚 Available Tools
|
|
355
|
+
|
|
356
|
+
### Public Tools (No Auth)
|
|
357
|
+
|
|
358
|
+
#### `get_public_info`
|
|
359
|
+
Get public information without authentication.
|
|
360
|
+
|
|
361
|
+
**Arguments:**
|
|
362
|
+
- `topic` (string): The topic to get information about
|
|
363
|
+
|
|
364
|
+
**Example:**
|
|
365
|
+
```json
|
|
366
|
+
{
|
|
367
|
+
"topic": "weather"
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Protected Tools (API Key Required)
|
|
372
|
+
|
|
373
|
+
#### `get_protected_data`
|
|
374
|
+
Retrieve protected data (requires API key).
|
|
375
|
+
|
|
376
|
+
**Arguments:**
|
|
377
|
+
- `dataType` (enum): `user`, `account`, or `settings`
|
|
378
|
+
|
|
379
|
+
**Example:**
|
|
380
|
+
```json
|
|
381
|
+
{
|
|
382
|
+
"dataType": "user"
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
#### `perform_protected_action`
|
|
387
|
+
Perform a protected action (requires API key).
|
|
388
|
+
|
|
389
|
+
**Arguments:**
|
|
390
|
+
- `action` (enum): `create`, `update`, or `delete`
|
|
391
|
+
- `resourceType` (string): Type of resource
|
|
392
|
+
- `resourceId` (string, optional): Resource ID for update/delete
|
|
393
|
+
|
|
394
|
+
**Example:**
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"action": "create",
|
|
398
|
+
"resourceType": "post"
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### `check_api_key_status`
|
|
403
|
+
Verify your API key is working correctly.
|
|
404
|
+
|
|
405
|
+
**Arguments:** None
|
|
406
|
+
|
|
407
|
+
## 🐛 Troubleshooting
|
|
408
|
+
|
|
409
|
+
### "API key required" Error
|
|
410
|
+
|
|
411
|
+
**Solution**: Set your API key in Studio Auth tab:
|
|
412
|
+
1. Go to **Auth** tab
|
|
413
|
+
2. Find **API Key** section
|
|
414
|
+
3. Paste: `sk_test_public_demo_key_12345`
|
|
415
|
+
4. Click **Set Key**
|
|
416
|
+
|
|
417
|
+
### "Invalid API key" Error
|
|
418
|
+
|
|
419
|
+
**Causes:**
|
|
420
|
+
- Wrong key format
|
|
421
|
+
- Key not in `.env` file
|
|
422
|
+
- Environment variables not loaded
|
|
423
|
+
|
|
424
|
+
**Solution:**
|
|
425
|
+
1. Check `.env` has the key
|
|
426
|
+
2. Restart the dev server: `npm run dev`
|
|
427
|
+
3. Verify key in Studio matches `.env`
|
|
428
|
+
|
|
429
|
+
### Public Tools Not Working
|
|
430
|
+
|
|
431
|
+
Public tools should work without authentication. If they fail:
|
|
432
|
+
1. Check server logs for errors
|
|
433
|
+
2. Ensure no guards are accidentally applied
|
|
434
|
+
3. Test in Studio Tools tab
|
|
435
|
+
|
|
436
|
+
## 🎓 Next Steps
|
|
437
|
+
|
|
438
|
+
1. ✅ Explore the code in `src/`
|
|
439
|
+
2. ✅ Add your own protected tools
|
|
440
|
+
3. ✅ Combine with JWT auth (see `typescript-auth` template)
|
|
441
|
+
4. ✅ Add custom validation logic
|
|
442
|
+
5. ✅ Deploy to production with secure keys
|
|
443
|
+
|
|
444
|
+
## 📖 Related Resources
|
|
445
|
+
|
|
446
|
+
- **NitroStack Docs**: Full documentation
|
|
447
|
+
- **typescript-auth Template**: JWT authentication example
|
|
448
|
+
- **typescript-starter Template**: Basic MCP server
|
|
449
|
+
- **ApiKeyModule API**: Core API key functionality
|
|
450
|
+
|
|
451
|
+
## 💡 Tips
|
|
452
|
+
|
|
453
|
+
- 🔑 Use `sk_` prefix for secret keys (standard convention)
|
|
454
|
+
- 🔐 Enable hashing in production
|
|
455
|
+
- 🎯 Mix public and protected tools in the same server
|
|
456
|
+
- 🔄 Support multiple auth methods (JWT + API Key)
|
|
457
|
+
- 📊 Add logging to track API key usage
|
|
458
|
+
|
|
459
|
+
## ❓ FAQ
|
|
460
|
+
|
|
461
|
+
**Q: Can I use both JWT and API key auth?**
|
|
462
|
+
A: Yes! See the Multi-Auth Example section above.
|
|
463
|
+
|
|
464
|
+
**Q: How do I rotate API keys?**
|
|
465
|
+
A: Add new keys to `.env`, deploy, then remove old keys after clients update.
|
|
466
|
+
|
|
467
|
+
**Q: Can I have per-key permissions?**
|
|
468
|
+
A: Yes! Use `customValidation` to check permissions in your database.
|
|
469
|
+
|
|
470
|
+
**Q: Is this production-ready?**
|
|
471
|
+
A: Yes, but enable `hashed: true` and use secure key storage.
|
|
472
|
+
|
|
473
|
+
**Q: How do I integrate with my existing auth system?**
|
|
474
|
+
A: Use `customValidation` to validate against your auth API/database.
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## 🎉 You're Ready!
|
|
479
|
+
|
|
480
|
+
You now have a fully functional MCP server with API key authentication. Try it out, modify it, and build something amazing!
|
|
481
|
+
|
|
482
|
+
**Happy coding! 🚀**
|
|
483
|
+
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-key-auth-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "api-key-auth-mcp-server",
|
|
9
|
+
"version": "1.0.0",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"nitrostack": "file:../../",
|
|
13
|
+
"zod": "^3.23.8"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^20",
|
|
17
|
+
"typescript": "^5"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"../..": {
|
|
21
|
+
"version": "1.0.0",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@google/generative-ai": "^0.24.1",
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
26
|
+
"bcryptjs": "^2.4.3",
|
|
27
|
+
"better-sqlite3": "^11.8.1",
|
|
28
|
+
"chokidar": "^3.6.0",
|
|
29
|
+
"commander": "^12.1.0",
|
|
30
|
+
"cors": "^2.8.5",
|
|
31
|
+
"dotenv": "^17.2.3",
|
|
32
|
+
"express": "^4.21.2",
|
|
33
|
+
"fs-extra": "^11.3.2",
|
|
34
|
+
"http-proxy-middleware": "^3.0.3",
|
|
35
|
+
"inquirer": "^12.10.0",
|
|
36
|
+
"jose": "^6.1.0",
|
|
37
|
+
"jsonwebtoken": "^9.0.2",
|
|
38
|
+
"open": "^10.2.0",
|
|
39
|
+
"openai": "^6.5.0",
|
|
40
|
+
"ora": "^9.0.0",
|
|
41
|
+
"reflect-metadata": "^0.2.1",
|
|
42
|
+
"uuid": "^11.0.5",
|
|
43
|
+
"winston": "^3.17.0",
|
|
44
|
+
"ws": "^8.18.3",
|
|
45
|
+
"zod": "^3.24.1",
|
|
46
|
+
"zod-to-json-schema": "^3.24.6"
|
|
47
|
+
},
|
|
48
|
+
"bin": {
|
|
49
|
+
"nitrostack": "dist/cli/index.js"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/bcryptjs": "^2.4.6",
|
|
53
|
+
"@types/better-sqlite3": "^7.6.12",
|
|
54
|
+
"@types/cors": "^2.8.19",
|
|
55
|
+
"@types/express": "^5.0.0",
|
|
56
|
+
"@types/fs-extra": "^11.0.4",
|
|
57
|
+
"@types/jest": "^29.5.14",
|
|
58
|
+
"@types/jsonwebtoken": "^9.0.7",
|
|
59
|
+
"@types/node": "^22.10.5",
|
|
60
|
+
"@types/react": "^19.2.2",
|
|
61
|
+
"@types/uuid": "^10.0.0",
|
|
62
|
+
"@types/ws": "^8.18.1",
|
|
63
|
+
"jest": "^29.7.0",
|
|
64
|
+
"ts-jest": "^29.2.5",
|
|
65
|
+
"typescript": "^5.7.2"
|
|
66
|
+
},
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=18.0.0"
|
|
69
|
+
},
|
|
70
|
+
"peerDependencies": {
|
|
71
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"react": {
|
|
75
|
+
"optional": true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"node_modules/@types/node": {
|
|
80
|
+
"version": "20.19.23",
|
|
81
|
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.23.tgz",
|
|
82
|
+
"integrity": "sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==",
|
|
83
|
+
"dev": true,
|
|
84
|
+
"license": "MIT",
|
|
85
|
+
"dependencies": {
|
|
86
|
+
"undici-types": "~6.21.0"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"node_modules/nitrostack": {
|
|
90
|
+
"resolved": "../..",
|
|
91
|
+
"link": true
|
|
92
|
+
},
|
|
93
|
+
"node_modules/typescript": {
|
|
94
|
+
"version": "5.9.3",
|
|
95
|
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
|
96
|
+
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
|
97
|
+
"dev": true,
|
|
98
|
+
"license": "Apache-2.0",
|
|
99
|
+
"bin": {
|
|
100
|
+
"tsc": "bin/tsc",
|
|
101
|
+
"tsserver": "bin/tsserver"
|
|
102
|
+
},
|
|
103
|
+
"engines": {
|
|
104
|
+
"node": ">=14.17"
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"node_modules/undici-types": {
|
|
108
|
+
"version": "6.21.0",
|
|
109
|
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
|
110
|
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
|
111
|
+
"dev": true,
|
|
112
|
+
"license": "MIT"
|
|
113
|
+
},
|
|
114
|
+
"node_modules/zod": {
|
|
115
|
+
"version": "3.25.76",
|
|
116
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
|
117
|
+
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
|
118
|
+
"license": "MIT",
|
|
119
|
+
"funding": {
|
|
120
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "api-key-auth-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "NitroStack server demonstrating API key authentication",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"dev": "nitrostack dev",
|
|
9
|
+
"build": "nitrostack build",
|
|
10
|
+
"start": "nitrostack start",
|
|
11
|
+
"widget": "npm --prefix src/widgets"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"nitrostack",
|
|
16
|
+
"api-key",
|
|
17
|
+
"authentication"
|
|
18
|
+
],
|
|
19
|
+
"author": "",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"nitrostack": "^1.0.0",
|
|
23
|
+
"zod": "^3.23.8"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20",
|
|
27
|
+
"typescript": "^5"
|
|
28
|
+
}
|
|
29
|
+
}
|