# Least-Privilege Keys for AI Agents

When an AI agent needs access to an API, it should get the [minimum credentials required](/docs/security/scoping-and-permissions) for the task at hand, nothing more. Treating agent credentials the same as your personal developer keys is a recipe for over-permissioned access in an environment you don't fully control.

## Create Agent-Specific Keys

Never share your personal API keys with an AI agent. Create dedicated keys with a clear naming convention so you can identify and audit agent usage separately.

```
# Human developer key
sk_live_martyn_full_access_7f8a9b0c...

# Agent key -- read-only, limited scope
sk_live_agent_claude_readonly_3d4e5f6a...
```

If your API provider supports key metadata or labels, tag the key with its purpose: `created-for: ai-agent`, `tool: claude-code`, `expires: 2025-04-01`.

## Apply the Principle of Least Privilege

### Read-Only When Possible

Most agent interactions with APIs are diagnostic: reading data, checking status, querying schemas. If the agent doesn't need to create, update, or delete resources, issue a read-only key.

```json
{
  "name": "agent-claude-code",
  "permissions": ["read:projects", "read:deployments", "read:logs"],
  "created": "2025-03-15",
  "expires": "2025-04-15"
}
```

### Scope to Specific Endpoints

If your API provider supports fine-grained permissions, restrict the key to only the endpoints the agent needs. An agent helping you debug a deployment doesn't need access to billing, user management, or key administration.

### Short-Lived Tokens

Set aggressive expiration times. An agent session rarely lasts more than a few hours. A key that expires in 24 hours limits the damage window if it leaks.

```bash
# Generate a short-lived token via your API
curl -X POST https://api.example.com/keys \
  -H "Authorization: Bearer $ADMIN_KEY" \
  -d '{
    "name": "agent-session-2025-03-15",
    "permissions": ["read:data"],
    "expires_in": "24h"
  }'
```

### Rotate Frequently

Don't reuse agent keys across sessions. Generate a fresh key for each working session, or at minimum [rotate](/docs/security/key-rotation) weekly. Automate this so it doesn't become a manual burden.

## Inject Keys at Runtime with Secrets Managers

The key shouldn't live in a config file [the agent can read](/docs/ai-agents/how-ai-assistants-expose-keys). Instead, inject it at the moment the tool or server process starts.

### 1Password CLI

```bash
# Inject secrets when launching an MCP server
op run --env-file=.env.agent -- npx @my-org/mcp-server
```

Where `.env.agent` references 1Password secret URIs:

```bash
# .env.agent
API_KEY=op://Development/Agent API Key/credential
DB_READ_URL=op://Development/Agent DB ReadOnly/connection-string
```

### AWS Secrets Manager

```bash
# Retrieve a secret and export it for the agent process
export API_KEY=$(aws secretsmanager get-secret-value \
  --secret-id agent/api-key \
  --query SecretString \
  --output text)

npx @my-org/mcp-server
```

### Doppler

```bash
# Doppler injects all project secrets as environment variables
doppler run --project agent-keys --config production -- npx @my-org/mcp-server
```

### HashiCorp Vault

```bash
# Fetch a dynamic, short-lived database credential from Vault
export DB_CREDS=$(vault read -format=json database/creds/agent-readonly)
export DB_USER=$(echo $DB_CREDS | jq -r '.data.username')
export DB_PASS=$(echo $DB_CREDS | jq -r '.data.password')

npx @my-org/mcp-server
```

Vault's dynamic secrets are particularly well-suited for agents: each request generates a unique credential with a built-in TTL, and Vault automatically revokes it when the lease expires.

## Align with the MCP Authorization Spec

The MCP specification's authorization framework builds these principles directly into the protocol for HTTP-based servers:

- **OAuth 2.1 with PKCE** is the required authentication mechanism for remote MCP servers. This eliminates static keys entirely for production agent-to-server communication.
- **Token expiration** is built in. OAuth access tokens have a defined lifetime, and refresh tokens can be revoked.
- **Scoped access** is native. OAuth scopes map directly to the least-privilege principle; the agent requests only the scopes it needs.
- **Dynamic client registration** allows agents to register themselves with an authorization server without pre-shared secrets.

For stdio-based MCP servers (local processes), the OAuth flow isn't applicable. In that case, the guidance above for runtime secret injection applies.

## Checklist

- [ ] Create dedicated API keys for each agent or agent tool
- [ ] Set permissions to read-only unless writes are explicitly needed
- [ ] Scope keys to the minimum set of endpoints required
- [ ] Set expiration to 24 hours or less for session-based work
- [ ] Use a secrets manager to inject keys at runtime
- [ ] Never store agent keys in MCP config files or `.env` files in the project directory
- [ ] Audit agent key usage separately from human developer usage
