> ## Documentation Index
> Fetch the complete documentation index at: https://docs.leanmcp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# @leanmcp/env-injection

> Request-scoped user secrets injection for MCP tools

# @leanmcp/env-injection

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.

<Note>
  This package **only works with the LeanMCP auth provider** and requires a `projectId` to be configured. Users manage their own secrets through the LeanMCP dashboard.
</Note>

## Features

* **Request-scoped isolation** - Each user's secrets are isolated using `AsyncLocalStorage`
* **@RequireEnv decorator** - Validate required secrets exist before method execution
* **getEnv() / getAllEnv()** - Access user-specific secrets in your tool code
* **Concurrency safe** - Each request has its own isolated context

## Installation

```bash theme={null}
npm install @leanmcp/env-injection @leanmcp/auth @leanmcp/core
```

## Quick Start

### 1. Configure Auth Provider with projectId

```typescript theme={null}
import { AuthProvider } from "@leanmcp/auth";

const projectId = process.env.LEANMCP_PROJECT_ID;

const authProvider = new AuthProvider('leanmcp', {
  apiKey: process.env.LEANMCP_API_KEY
});

await authProvider.init();
```

### 2. Use @RequireEnv and getEnv()

```typescript theme={null}
import { Tool } from "@leanmcp/core";
import { Authenticated } from "@leanmcp/auth";
import { RequireEnv, getEnv } from "@leanmcp/env-injection";

@Authenticated(authProvider, { projectId })
export class SlackService {

  @Tool({ description: 'Send a message to Slack' })
  @RequireEnv(["SLACK_TOKEN", "SLACK_CHANNEL"])
  async sendMessage(args: { message: string }) {
    // getEnv() returns THIS USER's secret, not a global env var
    const token = getEnv("SLACK_TOKEN")!;
    const channel = getEnv("SLACK_CHANNEL")!;

    await slackApi.postMessage(channel, args.message, token);

    return { success: true, channel };
  }
}
```

## How It Works

```
Request → @Authenticated(projectId) → Fetch Secrets → runWithEnv() → @RequireEnv → Method → Cleanup
           ↓                                             ↓                ↓
     Verify token                               Store in ALS        getEnv() works
```

1. User makes request with auth token
2. `@Authenticated` verifies token and fetches user's secrets from LeanMCP API
3. Secrets are stored in `AsyncLocalStorage` for this request only
4. `@RequireEnv` validates required secrets exist
5. `getEnv()` accesses secrets during method execution
6. Context is automatically cleaned up after request completes

## API Reference

### @RequireEnv(keys)

Decorator to validate required environment variables exist before method execution.

```typescript theme={null}
import { RequireEnv, getEnv } from "@leanmcp/env-injection";

@RequireEnv(["SLACK_TOKEN", "SLACK_CHANNEL"])
async sendMessage(args: { message: string }) {
  // Method only executes if BOTH keys exist
  const token = getEnv("SLACK_TOKEN")!;  // Safe to use !
  const channel = getEnv("SLACK_CHANNEL")!;
}
```

**Requirements:**

* Must be used with `@Authenticated(authProvider, { projectId })`
* Throws clear error if `projectId` is not configured
* Throws if required keys are missing

***

### getEnv(key)

Get a single environment variable from the current request context.

```typescript theme={null}
import { getEnv } from "@leanmcp/env-injection";

const token = getEnv("SLACK_TOKEN");
// Returns undefined if key doesn't exist
// Throws if called outside env context
```

***

### getAllEnv()

Get all environment variables from the current request context.

```typescript theme={null}
import { getAllEnv } from "@leanmcp/env-injection";

const env = getAllEnv();
// { SLACK_TOKEN: "xoxb-...", SLACK_CHANNEL: "#general" }
```

***

### hasEnvContext()

Check if currently inside an env context.

```typescript theme={null}
import { hasEnvContext, getEnv } from "@leanmcp/env-injection";

if (hasEnvContext()) {
  const token = getEnv("API_KEY");  // Safe to call
}
```

***

### runWithEnv(env, fn)

Run a function with environment variables in scope. Used internally by `@Authenticated`.

```typescript theme={null}
import { runWithEnv, getEnv } from "@leanmcp/env-injection";

await runWithEnv({ API_KEY: "secret123" }, async () => {
  console.log(getEnv("API_KEY"));  // "secret123"
});
```

## Error Messages

### Missing projectId Configuration

```
Environment injection not configured for SlackService.sendMessage().
To use @RequireEnv, you must configure 'projectId' in your @Authenticated decorator:
@Authenticated(authProvider, { projectId: 'your-project-id' })
```

### Missing Required Variables

```
Missing required environment variables: SLACK_TOKEN, SLACK_CHANNEL.
Please configure these secrets in your LeanMCP dashboard for this project.
```

### Called Outside Context

```
getEnv("SLACK_TOKEN") called outside of env context.
To use getEnv(), you must configure 'projectId' in your @Authenticated decorator:
@Authenticated(authProvider, { projectId: 'your-project-id' })
```

## Environment Variables

| Variable             | Description                           |
| -------------------- | ------------------------------------- |
| `LEANMCP_API_KEY`    | Your LeanMCP API key (with SDK scope) |
| `LEANMCP_PROJECT_ID` | Project ID to scope secrets to        |

## Best Practices

<AccordionGroup>
  <Accordion title="Always use @Authenticated with projectId">
    Environment injection requires the `projectId` option to know which project's secrets to fetch.

    ```typescript theme={null}
    @Authenticated(authProvider, { projectId: 'my-project' })
    ```
  </Accordion>

  <Accordion title="Use @RequireEnv for validation">
    Fails fast with clear error messages if secrets are missing.

    ```typescript theme={null}
    @RequireEnv(["API_KEY", "SECRET"])
    ```
  </Accordion>

  <Accordion title="Use non-null assertion after @RequireEnv">
    After `@RequireEnv` validates, secrets are guaranteed to exist.

    ```typescript theme={null}
    @RequireEnv(["API_KEY"])
    async method() {
      const key = getEnv("API_KEY")!;  // Safe to use !
    }
    ```
  </Accordion>

  <Accordion title="Don't cache secrets">
    They're request-scoped for security. Always call `getEnv()` when needed.
  </Accordion>
</AccordionGroup>

## Related Packages

* [@leanmcp/auth](/sdk/auth) - Authentication decorators (required)
* [@leanmcp/core](/sdk/core) - Core MCP server functionality

## Links

* [GitHub Repository](https://github.com/LeanMCP/leanmcp-sdk)
* [NPM Package](https://www.npmjs.com/package/@leanmcp/env-injection)
