@leanmcp/env-injection 0.1.0 → 0.1.2

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 (3) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +233 -206
  3. package/package.json +49 -49
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 LeanMCP Contributors
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2025 LeanMCP Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,206 +1,233 @@
1
- # @leanmcp/env-injection
2
-
3
- Request-scoped environment variable injection for LeanMCP tools. Enables user-specific secrets (API keys, tokens) to be securely fetched and accessed within MCP tool methods.
4
-
5
- ## Features
6
-
7
- - **Request-scoped isolation** - Each user's secrets are isolated using `AsyncLocalStorage`
8
- - **@RequireEnv decorator** - Validate required env vars exist before method execution
9
- - **getEnv() / getAllEnv()** - Access user-specific secrets in your tool code
10
- - **Type-safe** - Full TypeScript support
11
- - **Works with @leanmcp/auth** - Integrates with `@Authenticated` decorator
12
-
13
- ## Installation
14
-
15
- ```bash
16
- npm install @leanmcp/env-injection @leanmcp/auth @leanmcp/core
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ### 1. Configure Auth Provider with projectId
22
-
23
- ```typescript
24
- import { AuthProvider } from "@leanmcp/auth";
25
-
26
- export const projectId = process.env.LEANMCP_PROJECT_ID;
27
-
28
- export const authProvider = new AuthProvider('leanmcp', {
29
- apiKey: process.env.LEANMCP_API_KEY,
30
- orchestrationApiUrl: 'https://api.leanmcp.com',
31
- authUrl: 'https://auth.leanmcp.com'
32
- });
33
-
34
- await authProvider.init();
35
- ```
36
-
37
- ### 2. Use @RequireEnv and getEnv()
38
-
39
- ```typescript
40
- import { Tool } from "@leanmcp/core";
41
- import { Authenticated } from "@leanmcp/auth";
42
- import { RequireEnv, getEnv } from "@leanmcp/env-injection";
43
- import { authProvider, projectId } from "./config.js";
44
-
45
- @Authenticated(authProvider, { projectId })
46
- export class SlackService {
47
-
48
- @Tool({ description: 'Send a message to Slack' })
49
- @RequireEnv(["SLACK_TOKEN", "SLACK_CHANNEL"])
50
- async sendMessage(args: { message: string }) {
51
- // getEnv() returns THIS USER's secret, not a global env var
52
- const token = getEnv("SLACK_TOKEN")!;
53
- const channel = getEnv("SLACK_CHANNEL")!;
54
-
55
- // Send message using user's own Slack token
56
- await slackApi.postMessage(channel, args.message, token);
57
-
58
- return { success: true, channel };
59
- }
60
- }
61
- ```
62
-
63
- ## API Reference
64
-
65
- ### runWithEnv(env, fn)
66
-
67
- Run a function with environment variables in scope. Used internally by `@Authenticated`.
68
-
69
- ```typescript
70
- import { runWithEnv } from "@leanmcp/env-injection";
71
-
72
- await runWithEnv({ API_KEY: "secret123" }, async () => {
73
- console.log(getEnv("API_KEY")); // "secret123"
74
- });
75
- ```
76
-
77
- ### getEnv(key)
78
-
79
- Get a single environment variable from the current request context.
80
-
81
- ```typescript
82
- import { getEnv } from "@leanmcp/env-injection";
83
-
84
- const token = getEnv("SLACK_TOKEN");
85
- // Returns undefined if key doesn't exist
86
- // Throws if called outside env context (projectId not configured)
87
- ```
88
-
89
- ### getAllEnv()
90
-
91
- Get all environment variables from the current request context.
92
-
93
- ```typescript
94
- import { getAllEnv } from "@leanmcp/env-injection";
95
-
96
- const env = getAllEnv();
97
- // { SLACK_TOKEN: "xoxb-...", SLACK_CHANNEL: "#general" }
98
- ```
99
-
100
- ### hasEnvContext()
101
-
102
- Check if currently inside an env context.
103
-
104
- ```typescript
105
- import { hasEnvContext } from "@leanmcp/env-injection";
106
-
107
- if (hasEnvContext()) {
108
- // Safe to call getEnv()
109
- }
110
- ```
111
-
112
- ### @RequireEnv(keys)
113
-
114
- Decorator to validate required environment variables exist before method execution.
115
-
116
- ```typescript
117
- import { RequireEnv } from "@leanmcp/env-injection";
118
-
119
- @RequireEnv(["SLACK_TOKEN", "SLACK_CHANNEL"])
120
- async sendMessage(args: { message: string }) {
121
- // Method only executes if BOTH keys exist
122
- // Otherwise throws: "Missing required environment variables: SLACK_TOKEN, SLACK_CHANNEL"
123
- }
124
- ```
125
-
126
- **Requirements:**
127
- - Must be used with `@Authenticated(authProvider, { projectId })`
128
- - Throws clear error if `projectId` is not configured
129
-
130
- ## Error Messages
131
-
132
- ### Missing projectId Configuration
133
-
134
- ```
135
- Environment injection not configured for SlackService.sendMessage().
136
- To use @RequireEnv, you must configure 'projectId' in your @Authenticated decorator:
137
- @Authenticated(authProvider, { projectId: 'your-project-id' })
138
- ```
139
-
140
- ### Missing Required Variables
141
-
142
- ```
143
- Missing required environment variables: SLACK_TOKEN, SLACK_CHANNEL.
144
- Please configure these secrets in your LeanMCP dashboard for this project.
145
- ```
146
-
147
- ### Called Outside Context
148
-
149
- ```
150
- getEnv("SLACK_TOKEN") called outside of env context.
151
- To use getEnv(), you must configure 'projectId' in your @Authenticated decorator:
152
- @Authenticated(authProvider, { projectId: 'your-project-id' })
153
- ```
154
-
155
- ## How It Works
156
-
157
- 1. User makes request to your MCP server with auth token
158
- 2. `@Authenticated` verifies token and fetches user's secrets from LeanMCP API
159
- 3. Secrets are stored in `AsyncLocalStorage` for this request only
160
- 4. `@RequireEnv` validates required secrets exist
161
- 5. `getEnv()` / `getAllEnv()` access secrets during method execution
162
- 6. Context is automatically cleaned up after request completes
163
-
164
- ```
165
- Request → @Authenticated(projectId) → Fetch Secrets → runWithEnv() → @RequireEnv → Method → Cleanup
166
- ↓ ↓ ↓
167
- Verify token Store in ALS getEnv() works
168
- ```
169
-
170
- ## Environment Variables
171
-
172
- | Variable | Description |
173
- |----------|-------------|
174
- | `LEANMCP_API_KEY` | Your LeanMCP API key (with SDK scope) |
175
- | `LEANMCP_PROJECT_ID` | Project ID to scope secrets to |
176
- | `LEANMCP_ORCHESTRATION_API_URL` | API URL (default: https://api.leanmcp.com) |
177
- | `LEANMCP_AUTH_URL` | Auth URL (default: https://auth.leanmcp.com) |
178
-
179
- ## Best Practices
180
-
181
- 1. **Always use @Authenticated with projectId** - Required for env injection to work
182
- 2. **Use @RequireEnv for validation** - Fails fast with clear error messages
183
- 3. **Don't cache secrets** - They're request-scoped for security
184
- 4. **Configure secrets in dashboard** - Users manage their own secrets
185
- 5. **Use non-null assertion** - After @RequireEnv, secrets are guaranteed to exist
186
-
187
- ```typescript
188
- @RequireEnv(["API_KEY"])
189
- async method() {
190
- const key = getEnv("API_KEY")!; // Safe to use ! here
191
- }
192
- ```
193
-
194
- ## License
195
-
196
- MIT
197
-
198
- ## Related Packages
199
-
200
- - [@leanmcp/auth](../auth) - Authentication decorators
201
- - [@leanmcp/core](../core) - Core MCP server functionality
202
-
203
- ## Links
204
-
205
- - [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
206
- - [Documentation](https://github.com/LeanMCP/leanmcp-sdk#readme)
1
+ <p align="center">
2
+ <img
3
+ src="https://raw.githubusercontent.com/LeanMCP/leanmcp-sdk/refs/heads/main/assets/logo.png"
4
+ alt="LeanMCP Logo"
5
+ width="400"
6
+ />
7
+ </p>
8
+
9
+ <p align="center">
10
+ <strong>@leanmcp/env-injection</strong><br/>
11
+ Request-scoped environment variable injection for LeanMCP tools.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@leanmcp/env-injection">
16
+ <img src="https://img.shields.io/npm/v/@leanmcp/env-injection" alt="npm version" />
17
+ </a>
18
+ <a href="https://www.npmjs.com/package/@leanmcp/env-injection">
19
+ <img src="https://img.shields.io/npm/dm/@leanmcp/env-injection" alt="npm downloads" />
20
+ </a>
21
+ <a href="https://docs.leanmcp.com/sdk/env-injection">
22
+ <img src="https://img.shields.io/badge/Docs-leanmcp-0A66C2?" />
23
+ </a>
24
+ <a href="https://discord.com/invite/DsRcA3GwPy">
25
+ <img src="https://img.shields.io/badge/Discord-Join-5865F2?logo=discord&logoColor=white" />
26
+ </a>
27
+ <a href="https://x.com/LeanMcp">
28
+ <img src="https://img.shields.io/badge/@LeanMCP-f5f5f5?logo=x&logoColor=000000" />
29
+ </a>
30
+ </p>
31
+
32
+ ## Features
33
+
34
+ - **Request-scoped isolation** — Each user's secrets are isolated using `AsyncLocalStorage`
35
+ - **@RequireEnv decorator** — Validate required env vars exist before method execution
36
+ - **getEnv() / getAllEnv()** — Access user-specific secrets in your tool code
37
+ - **Type-safe** Full TypeScript support
38
+ - **Works with @leanmcp/auth** — Integrates with `@Authenticated` decorator
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install @leanmcp/env-injection @leanmcp/auth @leanmcp/core
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ### 1. Configure Auth Provider with projectId
49
+
50
+ ```typescript
51
+ import { AuthProvider } from "@leanmcp/auth";
52
+
53
+ export const projectId = process.env.LEANMCP_PROJECT_ID;
54
+
55
+ export const authProvider = new AuthProvider('leanmcp', {
56
+ apiKey: process.env.LEANMCP_API_KEY,
57
+ orchestrationApiUrl: 'https://api.leanmcp.com',
58
+ authUrl: 'https://auth.leanmcp.com'
59
+ });
60
+
61
+ await authProvider.init();
62
+ ```
63
+
64
+ ### 2. Use @RequireEnv and getEnv()
65
+
66
+ ```typescript
67
+ import { Tool } from "@leanmcp/core";
68
+ import { Authenticated } from "@leanmcp/auth";
69
+ import { RequireEnv, getEnv } from "@leanmcp/env-injection";
70
+ import { authProvider, projectId } from "./config.js";
71
+
72
+ @Authenticated(authProvider, { projectId })
73
+ export class SlackService {
74
+
75
+ @Tool({ description: 'Send a message to Slack' })
76
+ @RequireEnv(["SLACK_TOKEN", "SLACK_CHANNEL"])
77
+ async sendMessage(args: { message: string }) {
78
+ // getEnv() returns THIS USER's secret, not a global env var
79
+ const token = getEnv("SLACK_TOKEN")!;
80
+ const channel = getEnv("SLACK_CHANNEL")!;
81
+
82
+ // Send message using user's own Slack token
83
+ await slackApi.postMessage(channel, args.message, token);
84
+
85
+ return { success: true, channel };
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## API Reference
91
+
92
+ ### runWithEnv(env, fn)
93
+
94
+ Run a function with environment variables in scope. Used internally by `@Authenticated`.
95
+
96
+ ```typescript
97
+ import { runWithEnv } from "@leanmcp/env-injection";
98
+
99
+ await runWithEnv({ API_KEY: "secret123" }, async () => {
100
+ console.log(getEnv("API_KEY")); // "secret123"
101
+ });
102
+ ```
103
+
104
+ ### getEnv(key)
105
+
106
+ Get a single environment variable from the current request context.
107
+
108
+ ```typescript
109
+ import { getEnv } from "@leanmcp/env-injection";
110
+
111
+ const token = getEnv("SLACK_TOKEN");
112
+ // Returns undefined if key doesn't exist
113
+ // Throws if called outside env context (projectId not configured)
114
+ ```
115
+
116
+ ### getAllEnv()
117
+
118
+ Get all environment variables from the current request context.
119
+
120
+ ```typescript
121
+ import { getAllEnv } from "@leanmcp/env-injection";
122
+
123
+ const env = getAllEnv();
124
+ // { SLACK_TOKEN: "xoxb-...", SLACK_CHANNEL: "#general" }
125
+ ```
126
+
127
+ ### hasEnvContext()
128
+
129
+ Check if currently inside an env context.
130
+
131
+ ```typescript
132
+ import { hasEnvContext } from "@leanmcp/env-injection";
133
+
134
+ if (hasEnvContext()) {
135
+ // Safe to call getEnv()
136
+ }
137
+ ```
138
+
139
+ ### @RequireEnv(keys)
140
+
141
+ Decorator to validate required environment variables exist before method execution.
142
+
143
+ ```typescript
144
+ import { RequireEnv } from "@leanmcp/env-injection";
145
+
146
+ @RequireEnv(["SLACK_TOKEN", "SLACK_CHANNEL"])
147
+ async sendMessage(args: { message: string }) {
148
+ // Method only executes if BOTH keys exist
149
+ // Otherwise throws: "Missing required environment variables: SLACK_TOKEN, SLACK_CHANNEL"
150
+ }
151
+ ```
152
+
153
+ **Requirements:**
154
+ - Must be used with `@Authenticated(authProvider, { projectId })`
155
+ - Throws clear error if `projectId` is not configured
156
+
157
+ ## Error Messages
158
+
159
+ ### Missing projectId Configuration
160
+
161
+ ```
162
+ Environment injection not configured for SlackService.sendMessage().
163
+ To use @RequireEnv, you must configure 'projectId' in your @Authenticated decorator:
164
+ @Authenticated(authProvider, { projectId: 'your-project-id' })
165
+ ```
166
+
167
+ ### Missing Required Variables
168
+
169
+ ```
170
+ Missing required environment variables: SLACK_TOKEN, SLACK_CHANNEL.
171
+ Please configure these secrets in your LeanMCP dashboard for this project.
172
+ ```
173
+
174
+ ### Called Outside Context
175
+
176
+ ```
177
+ getEnv("SLACK_TOKEN") called outside of env context.
178
+ To use getEnv(), you must configure 'projectId' in your @Authenticated decorator:
179
+ @Authenticated(authProvider, { projectId: 'your-project-id' })
180
+ ```
181
+
182
+ ## How It Works
183
+
184
+ 1. User makes request to your MCP server with auth token
185
+ 2. `@Authenticated` verifies token and fetches user's secrets from LeanMCP API
186
+ 3. Secrets are stored in `AsyncLocalStorage` for this request only
187
+ 4. `@RequireEnv` validates required secrets exist
188
+ 5. `getEnv()` / `getAllEnv()` access secrets during method execution
189
+ 6. Context is automatically cleaned up after request completes
190
+
191
+ ```
192
+ Request → @Authenticated(projectId) → Fetch Secrets → runWithEnv() → @RequireEnv → Method → Cleanup
193
+ ↓ ↓ ↓
194
+ Verify token Store in ALS getEnv() works
195
+ ```
196
+
197
+ ## Environment Variables
198
+
199
+ | Variable | Description |
200
+ |----------|-------------|
201
+ | `LEANMCP_API_KEY` | Your LeanMCP API key (with SDK scope) |
202
+ | `LEANMCP_PROJECT_ID` | Project ID to scope secrets to |
203
+ | `LEANMCP_ORCHESTRATION_API_URL` | API URL (default: https://api.leanmcp.com) |
204
+ | `LEANMCP_AUTH_URL` | Auth URL (default: https://auth.leanmcp.com) |
205
+
206
+ ## Best Practices
207
+
208
+ 1. **Always use @Authenticated with projectId** - Required for env injection to work
209
+ 2. **Use @RequireEnv for validation** - Fails fast with clear error messages
210
+ 3. **Don't cache secrets** - They're request-scoped for security
211
+ 4. **Configure secrets in dashboard** - Users manage their own secrets
212
+ 5. **Use non-null assertion** - After @RequireEnv, secrets are guaranteed to exist
213
+
214
+ ```typescript
215
+ @RequireEnv(["API_KEY"])
216
+ async method() {
217
+ const key = getEnv("API_KEY")!; // Safe to use ! here
218
+ }
219
+ ```
220
+
221
+ ## License
222
+
223
+ MIT
224
+
225
+ ## Related Packages
226
+
227
+ - [@leanmcp/auth](../auth) - Authentication decorators
228
+ - [@leanmcp/core](../core) - Core MCP server functionality
229
+
230
+ ## Links
231
+
232
+ - [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
233
+ - [Documentation](https://github.com/LeanMCP/leanmcp-sdk#readme)
package/package.json CHANGED
@@ -1,49 +1,49 @@
1
- {
2
- "name": "@leanmcp/env-injection",
3
- "version": "0.1.0",
4
- "description": "Request-scoped environment variable injection for LeanMCP",
5
- "main": "dist/index.js",
6
- "module": "dist/index.mjs",
7
- "types": "dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "require": "./dist/index.js",
12
- "import": "./dist/index.mjs"
13
- }
14
- },
15
- "files": [
16
- "dist",
17
- "README.md"
18
- ],
19
- "scripts": {
20
- "build": "tsup src/index.ts --format esm,cjs --dts",
21
- "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
22
- "test": "jest --passWithNoTests"
23
- },
24
- "dependencies": {
25
- "reflect-metadata": "^0.2.1"
26
- },
27
- "devDependencies": {
28
- "@types/node": "^20.0.0",
29
- "tsup": "^8.0.0",
30
- "typescript": "^5.0.0"
31
- },
32
- "repository": {
33
- "type": "git",
34
- "url": "git+https://github.com/LeanMCP/leanmcp-sdk.git",
35
- "directory": "packages/env-injection"
36
- },
37
- "keywords": [
38
- "mcp",
39
- "model-context-protocol",
40
- "environment",
41
- "injection",
42
- "secrets"
43
- ],
44
- "author": "LeanMCP <admin@leanmcp.com>",
45
- "license": "MIT",
46
- "publishConfig": {
47
- "access": "public"
48
- }
49
- }
1
+ {
2
+ "name": "@leanmcp/env-injection",
3
+ "version": "0.1.2",
4
+ "description": "Request-scoped environment variable injection for LeanMCP",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "require": "./dist/index.js",
12
+ "import": "./dist/index.mjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup src/index.ts --format esm,cjs --dts",
21
+ "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
22
+ "test": "jest --passWithNoTests"
23
+ },
24
+ "dependencies": {
25
+ "reflect-metadata": "^0.2.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^20.0.0",
29
+ "tsup": "^8.0.0",
30
+ "typescript": "^5.0.0"
31
+ },
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/LeanMCP/leanmcp-sdk.git",
35
+ "directory": "packages/env-injection"
36
+ },
37
+ "keywords": [
38
+ "mcp",
39
+ "model-context-protocol",
40
+ "environment",
41
+ "injection",
42
+ "secrets"
43
+ ],
44
+ "author": "LeanMCP <admin@leanmcp.com>",
45
+ "license": "MIT",
46
+ "publishConfig": {
47
+ "access": "public"
48
+ }
49
+ }