# How to Configure API Key Authentication for MetaMCP Endpoints

> Secure MetaMCP endpoints with API key authentication. Learn configuration steps using environment variables or UI toggles and understand how middleware validates your API key.

- Repository: [metatool-ai/metamcp](https://github.com/metatool-ai/metamcp)
- Tags: how-to-guide
- Published: 2026-03-07

---

**MetaMCP secures its REST-style endpoints using API key authentication configured via the `BOOTSTRAP_ENDPOINTS` environment variable or UI toggles, validated through the `X-API-Key` header or query parameters by middleware in [`apps/backend/src/middleware/api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/middleware/api-key-oauth.middleware.ts).**

The `metatool-ai/metamcp` repository implements a flexible authentication system for its Model Context Protocol (MCP) endpoints. This guide explains how to enable and configure **API key authentication for MetaMCP endpoints**, create keys, and understand the underlying validation logic.

## Enabling API Key Authentication for Endpoints

MetaMCP stores authentication settings in the `endpoints` table, defined by the Zod schema in [`packages/zod-types/src/endpoints.zod.ts`](https://github.com/metatool-ai/metamcp/blob/main/packages/zod-types/src/endpoints.zod.ts). The boolean column `enable_api_key_auth` controls whether an endpoint requires API key validation.

### Configuration via Environment Variables

During initial deployment, use the `BOOTSTRAP_ENDPOINTS` JSON array to define endpoints and their authentication settings. The `bootstrapEndpoints` function in [`apps/backend/src/lib/bootstrap.service.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/lib/bootstrap.service.ts) (lines 640-645) translates these fields into database columns:

```dotenv
BOOTSTRAP_ENDPOINTS=[
  {
    "name": "private-api",
    "description": "Requires API key authentication",
    "enable_auth": true,
    "enable_auth_query": true,
    "enable_auth_oauth": false,
    "is_public": false,
    "user_email": "admin@example.com"
  }
]

```

The mapping works as follows:

- `enable_auth` → `enable_api_key_auth`
- `enable_auth_query` → `use_query_param_auth`
- `enable_auth_oauth` → `enable_oauth`

### Configuration via the Frontend UI

For existing deployments, navigate to the endpoint management interface. The React component in [`apps/frontend/components/edit-endpoint.tsx`](https://github.com/metatool-ai/metamcp/blob/main/apps/frontend/components/edit-endpoint.tsx) (line 138) renders a toggle for `enableApiKeyAuth`. When checked, the form submits `enable_api_key_auth: true` to the backend API.

## Creating and Managing API Keys

API keys are stored in the `api_keys` table and validated by `apiKeysRepository.validateApiKey` in [`apps/backend/src/db/repositories/api-keys.repo.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/db/repositories/api-keys.repo.ts).

### Bootstrap Configuration

Define keys in the `BOOTSTRAP_API_KEYS` environment variable:

```dotenv
BOOTSTRAP_API_KEYS=[
  {"name": "Public", "is_public": true},
  {"name": "AdminKey", "is_public": false, "user_email": "admin@example.com"}
]

```

The bootstrap service generates random keys with the format `sk_mt_<64-hex-chars>` and inserts them into the database (lines 95-107 in [`apps/backend/src/lib/bootstrap.service.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/lib/bootstrap.service.ts)).

### Key Validation Logic

The middleware distinguishes between **public** and **private** keys:

- **Public keys** can only access endpoints where `is_public = true`
- **Private keys** are linked to a specific user via `user_id` and can access that user's private endpoints

The `checkApiKeyAccess` function in [`apps/backend/src/middleware/api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/middleware/api-key-oauth.middleware.ts) (lines 73-85) enforces these rules.

## How the Authentication Middleware Works

The core logic resides in [`apps/backend/src/middleware/api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/middleware/api-key-oauth.middleware.ts), specifically the `authenticateApiKey` function (lines 80-115).

### Token Extraction

The `extractAuthToken` function (lines 49-52) checks for credentials in this order:

1. **`X-API-Key`** header (always checked)
2. **Query parameters** (`api_key` or `apikey`) — only if `endpoint.use_query_param_auth` is `true`

### Authentication Flow

The middleware implements a decision tree with four branches:

1. **No authentication required** — Both `enable_api_key_auth` and `enable_oauth` are `false`. The middleware calls `next()` immediately.
2. **API key only** — Validates the key via `apiKeysRepository.validateApiKey` and checks access with `checkApiKeyAccess`.
3. **OAuth only** — Validates bearer tokens via `validateOAuthToken`.
4. **Both enabled** — Attempts OAuth validation first (if the token looks like an OAuth token), otherwise falls back to API key validation.

If validation fails, the middleware returns structured JSON errors (`invalid_api_key`, `invalid_token`, etc.) and respects rate limiting via `authRateLimiter`.

## Making Authenticated Requests

Once configured, clients can authenticate using the generated keys.

### Using the X-API-Key Header

```bash
curl -H "X-API-Key: sk_mt_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" \
     https://meta-mcp.example.com/api/v1/private-api/health

```

### Using Query Parameters

If the endpoint has `use_query_param_auth` enabled (set via `enable_auth_query: true` in bootstrap or the UI):

```bash
curl "https://meta-mcp.example.com/api/v1/private-api/health?api_key=sk_mt_0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"

```

## Troubleshooting Common Issues

| Symptom | Likely Cause | Fix |
|---------|--------------|-----|
| **401 `invalid_api_key`** | The endpoint has `enable_api_key_auth` set to `false`, or you're using query parameters but `use_query_param_auth` is `false`. | Enable `enable_auth: true` (bootstrap) or check the **Enable API-key authentication** toggle in the UI. For query params, also enable `enable_auth_query: true`. |
| **403 `Access denied`** | You're using a **public** API key to access a **private** endpoint, or a private key owned by a different user than the endpoint owner. | Create a **private** key linked to the endpoint owner's email in `BOOTSTRAP_API_KEYS`, or ensure the endpoint `is_public: true` if using public keys. |
| **429 Rate limit** | Multiple failed authentication attempts triggered the `authRateLimiter`. | Wait for the rate limit window to reset, or verify your key value matches exactly (check for trailing whitespace or copy-paste errors). |

## Summary

- **Enable API key authentication** by setting `enable_api_key_auth: true` via the `BOOTSTRAP_ENDPOINTS` environment variable or the **Enable API-key authentication** toggle in the UI ([`apps/frontend/components/edit-endpoint.tsx`](https://github.com/metatool-ai/metamcp/blob/main/apps/frontend/components/edit-endpoint.tsx)).
- **Create keys** through `BOOTSTRAP_API_KEYS` (generating `sk_mt_<64-hex>` format) or the admin interface, distinguishing between public and private keys.
- **Configure query parameter support** by setting `use_query_param_auth: true` (mapped from `enable_auth_query` in bootstrap) to allow `?api_key=` in URLs.
- **Validate requests** using the `X-API-Key` header or query parameters, processed by `authenticateApiKey` in [`apps/backend/src/middleware/api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/middleware/api-key-oauth.middleware.ts).
- **Enforce access control** through `checkApiKeyAccess`, ensuring public keys access only public endpoints and private keys match endpoint ownership.

## Frequently Asked Questions

### How do I enable query parameter authentication for MetaMCP endpoints?

Set `enable_auth_query: true` in your `BOOTSTRAP_ENDPOINTS` JSON configuration, which maps to the `use_query_param_auth` database column. Alternatively, enable the corresponding toggle in the endpoint editing UI. This allows clients to pass the API key via `?api_key=` or `?apikey=` query strings instead of the `X-API-Key` header.

### What is the difference between public and private API keys in MetaMCP?

**Public keys** (`is_public: true`) are not linked to a specific user and can only access endpoints marked as public (`is_public: true`). **Private keys** are associated with a user via the `user_id` column and can access that specific user's private endpoints. The `checkApiKeyAccess` function in [`apps/backend/src/middleware/api-key-oauth.middleware.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/middleware/api-key-oauth.middleware.ts) enforces these restrictions, returning 403 errors for unauthorized access attempts.

### Can I use both API key and OAuth authentication on the same MetaMCP endpoint?

Yes. When both `enable_api_key_auth` and `enable_oauth` are set to `true` for an endpoint, the middleware attempts OAuth validation first if the token resembles an OAuth token (starts with `mcp_token_`). If OAuth validation fails or the token format indicates an API key, it falls back to API key validation via `apiKeysRepository.validateApiKey`. This allows flexible authentication strategies for different client types.

### Where are API keys stored and how are they validated in MetaMCP?

API keys are stored in the `api_keys` table with columns for `key` (the token string), `user_id` (nullable for public keys), `is_active`, and metadata. Validation occurs in [`apps/backend/src/db/repositories/api-keys.repo.ts`](https://github.com/metatool-ai/metamcp/blob/main/apps/backend/src/db/repositories/api-keys.repo.ts) via the `validateApiKey` method, which checks if the key exists, is active, and matches the provided token. The middleware then uses `checkApiKeyAccess` to verify the key has permission to access the specific endpoint based on public/private status and ownership.