Skip to main content
Glama

Janee πŸ”

Secrets management for AI agents via MCP

npm version npm downloads License: MIT GitHub stars

Your AI agents need API access to be useful. But they shouldn't have your raw API keys. Janee sits between your agents and your APIs β€” injecting credentials, enforcing policies, and logging everything.

✨ Features

πŸ”’ Zero-knowledge agents

Agents call APIs without ever seeing keys

πŸ“‹ Full audit trail

Every request logged with timestamp, method, path, status

πŸ›‘οΈ Request policies

Allow/deny rules per capability (e.g., read-only Stripe)

⏱️ Session TTLs

Time-limited access with instant revocation

πŸ”Œ Works with any MCP client

Claude Desktop, Cursor, OpenClaw, and more

🏠 Local-first

Keys encrypted on your machine, never sent to a cloud

πŸ–₯️ Exec mode

Run CLI tools with injected credentials β€” agents never see the keys

πŸ€– GitHub App auth

Short-lived tokens for autonomous agents β€” no static PATs

🐦 Twitter/X OAuth 1.0a

Per-request OAuth signing β€” 4 secrets stay encrypted

☁️ AWS SigV4

Sign AWS API requests server-side β€” SES, S3, and more

πŸ”§ Automatic git auth

git push/pull just works when credentials include GitHub tokens


The Problem

AI agents need API access to be useful. The current approach is to give them your keys and hope they behave.

  • πŸ”“ Agents have full access to Stripe, Gmail, databases

  • πŸ“Š No audit trail of what was accessed or why

  • 🚫 No kill switch when things go wrong

  • πŸ’‰ One prompt injection away from disaster


Related MCP server: guardrails-mcp-server

The Solution

Janee is an MCP server that manages API secrets for AI agents:

  1. Store your API keys β€” encrypted locally in ~/.janee/

  2. Run janee serve β€” starts MCP server

  3. Agent requests access β€” via execute MCP tool

  4. Janee injects the real key β€” agent never sees it

  5. Everything is logged β€” full audit trail

Your keys stay on your machine. Agents never see them. You stay in control.


Configure Once, Use Everywhere

Set up your APIs in Janee once:

services:
  stripe:
    baseUrl: https://api.stripe.com
    auth: { type: bearer, key: sk_live_xxx }
  github:
    baseUrl: https://api.github.com
    auth: { type: bearer, key: ghp_xxx }
  openai:
    baseUrl: https://api.openai.com
    auth: { type: bearer, key: sk-xxx }

Now every agent that connects to Janee can use them:

  • Claude Desktop β€” access your APIs

  • Cursor β€” access your APIs

  • OpenClaw β€” access your APIs

  • Any MCP client β€” access your APIs

No more copying keys between tools. No more "which agent has which API configured?" Add a new agent? It already has access to everything. Revoke a key? Update it once in Janee.

One config. Every agent. Full audit trail.


Quick Start

Install

npm install -g @true-and-useful/janee

Initialize

janee init

This creates ~/.janee/config.yaml with example services.

Add Services

Option 1: Interactive (recommended for first-time users)

janee add

Janee will guide you through adding a service:

Service name: stripe
Base URL: https://api.stripe.com
Auth type: bearer
API key: sk_live_xxx

βœ“ Added service "stripe"

Create a capability for this service? (Y/n): y
Capability name (default: stripe): 
TTL (e.g., 1h, 30m): 1h
Auto-approve? (Y/n): y

βœ“ Added capability "stripe"

Done! Run 'janee serve' to start.

Using an AI agent? See Non-interactive Setup for flags that skip prompts, or the agent-specific guides below.

Option 2: Edit config directly

Edit ~/.janee/config.yaml:

services:
  stripe:
    baseUrl: https://api.stripe.com
    auth:
      type: bearer
      key: sk_live_xxx

capabilities:
  stripe:
    service: stripe
    ttl: 1h
    autoApprove: true

Add CLI tools (exec mode)

Some tools need credentials as environment variables, not HTTP headers. Exec mode handles this:

janee add twitter --exec \
  --key "tvly-xxx" \
  --allow-commands "bird,tweet-cli" \
  --env-map "TWITTER_API_KEY={{credential}}"

Now agents can run CLI tools through Janee without ever seeing the API key:

// Agent calls janee_exec tool
janee_exec({
  capability: "twitter",
  command: ["bird", "post", "Hello world!"],
  cwd: "/home/agent/project",  // optional working directory
  reason: "User asked to post a tweet"
})

Janee spawns the process with TWITTER_API_KEY injected, runs the command, and returns stdout/stderr. The credential never enters the agent's context.

Key flags:

  • --exec β€” configure as exec-mode (CLI wrapper instead of HTTP proxy)

  • --allow-commands β€” whitelist of allowed executables (security)

  • --env-map β€” map credentials to environment variables

  • --work-dir β€” working directory for the subprocess

  • --timeout β€” max execution time (default: 30s)

Git operations (automatic HTTPS auth)

When using exec mode with GitHub credentials, Janee automatically handles git authentication. No extra configuration needed β€” git push, git pull, and git clone just work:

capabilities:
  - name: git-ops
    service: github
    mode: exec
    allowCommands: [git]
    env:
      GH_TOKEN: "{{credential}}"
// Agent can push code without ever seeing the token
janee_exec({
  capability: "git-ops",
  command: ["git", "push", "origin", "main"],
  cwd: "/workspace/my-repo"
})

Janee detects git commands with GH_TOKEN/GITHUB_TOKEN in the environment and creates a temporary askpass script for HTTPS authentication. The script is cleaned up automatically after the command completes.

Add GitHub App auth (for autonomous agents)

Static tokens are risky for long-running agents. GitHub App auth generates short-lived installation tokens on demand β€” no long-lived PATs required.

Option 1: Use create-gh-app (recommended)

npx @true-and-useful/create-gh-app create my-agent --owner @me
# Opens browser β†’ creates app β†’ saves credentials locally

# Install the app on your repos
# https://github.com/apps/my-agent/installations/new

# Register with Janee in one command
npx @true-and-useful/create-gh-app janee-add my-agent

Done. Your agent now gets short-lived GitHub tokens through Janee's MCP proxy.

Option 2: Manual setup

janee add github-app \
  --auth-type github-app \
  --app-id 123456 \
  --pem-file /path/to/private-key.pem \
  --installation-id 789

Or via config:

services:
  github:
    baseUrl: https://api.github.com
    auth:
      type: github-app
      appId: "123456"
      pemFile: /path/to/private-key.pem
      installationId: "789"

How it works: When an agent requests access, Janee signs a JWT with the app's private key, exchanges it for a 1-hour installation token via GitHub's API, and caches the token until expiry. The agent never sees the private key β€” only the short-lived token reaches the API.

Start the MCP server

janee serve

Use with your agent

Agents that support MCP (Claude Desktop, Cursor, OpenClaw) can now call the execute tool to make API requests through Janee:

// Agent calls the execute tool
execute({
  capability: "stripe",
  method: "GET",
  path: "/v1/balance",
  reason: "User asked for account balance"
})

Janee decrypts the key, makes the request, logs everything, and returns the response.


Integrations

Works with any agent that speaks MCP:


OpenClaw Integration

If you're using OpenClaw, install the plugin for native tool support:

npm install -g @true-and-useful/janee
janee init
# Edit ~/.janee/config.yaml with your services

# Install the OpenClaw plugin
openclaw plugins install @true-and-useful/janee-openclaw

Enable in your agent config:

{
  agents: {
    list: [{
      id: "main",
      tools: { allow: ["janee"] }
    }]
  }
}

Your agent now has these tools:

  • janee_list_services β€” Discover available APIs

  • janee_execute β€” Make API requests through Janee

The plugin spawns janee serve automatically. All requests are logged to ~/.janee/logs/.


MCP Tools

Janee exposes three MCP tools:

Tool

Description

list_services

Discover available APIs and their policies

execute

Make an API request through Janee (HTTP proxy mode)

exec

Run a CLI command with injected credentials (exec mode)

manage_credential

View, grant, or revoke access to agent-scoped credentials

reload_config

Reload config from disk after adding/removing services (available when started with janee serve)

Agents discover what's available, then call APIs through Janee. Same audit trail, same protection.


Configuration

Config lives in ~/.janee/config.yaml:

server:
  host: localhost

services:
  stripe:
    baseUrl: https://api.stripe.com
    auth:
      type: bearer
      key: sk_live_xxx  # encrypted at rest

  github:
    baseUrl: https://api.github.com
    auth:
      type: bearer
      key: ghp_xxx

capabilities:
  stripe:
    service: stripe
    ttl: 1h
    autoApprove: true

  stripe_sensitive:
    service: stripe
    ttl: 5m
    requiresReason: true

Services = Real APIs with real keys
Capabilities = What agents can request, with policies

Supported auth types

Type

Description

Example

bearer

Bearer token in Authorization header

Stripe, OpenAI, GitHub

basic

HTTP Basic Auth (username + password)

Internal APIs

hmac-bybit

HMAC-SHA256 signing for Bybit

Bybit exchange

hmac-okx

HMAC-SHA256 + passphrase for OKX

OKX exchange

hmac-mexc

HMAC-SHA256 signing for MEXC

MEXC exchange

headers

Custom key-value headers

Non-standard APIs

service-account

Google service account JSON key

Google Cloud

github-app

Short-lived GitHub installation tokens

GitHub API

oauth1a-twitter

OAuth 1.0a per-request signing

Twitter/X API v2

aws-sigv4

AWS Signature V4 per-request signing

SES, S3, and other AWS services

Twitter/X OAuth 1.0a

Janee computes OAuth 1.0a signatures (HMAC-SHA1) server-side, so your 4 Twitter secrets stay encrypted at rest and never enter the agent's context:

services:
  twitter:
    baseUrl: https://api.x.com
    auth:
      type: oauth1a-twitter
      consumerKey: xxx        # encrypted at rest
      consumerSecret: xxx     # encrypted at rest
      accessToken: xxx        # encrypted at rest
      accessTokenSecret: xxx  # encrypted at rest

capabilities:
  twitter:
    service: twitter
    ttl: 1h
    autoApprove: true

Or use the built-in template:

janee add twitter

AWS SigV4

Janee computes AWS Signature V4 (HMAC-SHA256) per-request, keeping your access keys encrypted at rest. Non-secret fields (region, awsService) stay in plain config:

services:
  aws-ses:
    baseUrl: https://email.us-east-1.amazonaws.com
    auth:
      type: aws-sigv4
      accessKeyId: AKIA...     # encrypted at rest
      secretAccessKey: xxx     # encrypted at rest
      region: us-east-1
      awsService: ses

capabilities:
  aws-ses:
    service: aws-ses
    ttl: 1h
    autoApprove: true

Built-in templates for common AWS services:

janee add aws-ses    # Amazon SES
janee add aws-s3     # Amazon S3

Access control

Control which agents can use which capabilities:

server:
  host: localhost
  defaultAccess: restricted   # capabilities require explicit allowlist

capabilities:
  stripe:
    service: stripe
    ttl: 1h
    allowedAgents: ["agent-a", "agent-b"]   # only these agents can use it

  github:
    service: github
    ttl: 1h
    # no allowedAgents + defaultAccess: restricted β†’ no agent can use this
  • defaultAccess: restricted β€” capabilities without an allowedAgents list are hidden from all agents

  • defaultAccess: open (default) β€” capabilities without an allowedAgents list are available to all agents

  • allowedAgents β€” per-capability list of agent names (matched against clientInfo.name from the MCP initialize handshake)

Credentials created by agents at runtime default to agent-only access β€” only the creating agent can use them unless it explicitly grants access via the manage_credential tool.

Exec mode capabilities

services:
  twitter:
    auth:
      type: bearer
      key: tvly-xxx

capabilities:
  twitter:
    service: twitter
    mode: exec
    allowCommands: ["bird", "tweet-cli"]
    envMap:
      TWITTER_API_KEY: "{{credential}}"
    ttl: 1h
    autoApprove: true

Exec-mode capabilities use janee_exec instead of execute. The credential is injected as an environment variable β€” the agent sees only stdout/stderr.

Runner hardening defaults in exec mode:

  • isolated minimal environment (no full host env inheritance)

  • temporary HOME per command

  • timeout kills the process group

Runner/Authority mode (for containers)

When agents run inside Docker containers, janee_exec on a remote host cannot access the container filesystem. The Runner/Authority architecture solves this:

  • Authority runs on the host: holds credentials, enforces policy, proxies API requests

  • Runner runs inside each container: serves MCP to the agent, forwards non-exec calls to the Authority, runs janee_exec locally

# Host: start Authority (MCP + exec authorization on one port)
janee serve -t http -p 3100 --host 0.0.0.0 --runner-key "$JANEE_RUNNER_KEY"

# Container: start Runner (agent talks to this)
janee serve -t http -p 3200 --host 127.0.0.1 \
  --authority http://host.docker.internal:3100 --runner-key "$JANEE_RUNNER_KEY"

The agent only needs JANEE_URL=http://localhost:3200.

You can also run the Authority as a standalone process:

janee authority --runner-key "$JANEE_RUNNER_KEY" --host 127.0.0.1 --port 9120

See the Runner/Authority guide for the full architecture, exec authorization flow, Docker Compose example, and troubleshooting.


Request Policies

Control exactly what requests each capability can make using rules:

capabilities:
  stripe_readonly:
    service: stripe
    ttl: 1h
    rules:
      allow:
        - GET *
      deny:
        - POST *
        - PUT *
        - DELETE *

  stripe_billing:
    service: stripe
    ttl: 15m
    requiresReason: true
    rules:
      allow:
        - GET *
        - POST /v1/refunds/*
        - POST /v1/invoices/*
      deny:
        - POST /v1/charges/*  # Can't charge cards
        - DELETE *

How rules work:

  1. deny patterns are checked first β€” explicit deny always wins

  2. Then allow patterns are checked β€” must match to proceed

  3. No rules defined β†’ allow all (backward compatible)

  4. Rules defined but no match β†’ denied by default

Pattern format: METHOD PATH

  • GET * β†’ any GET request

  • POST /v1/charges/* β†’ POST to /v1/charges/ and subpaths

  • * /v1/customers β†’ any method to /v1/customers

  • DELETE /v1/customers/* β†’ DELETE any customer

This makes security real: Even if an agent lies about its "reason", it can only access the endpoints the policy allows. Enforcement happens server-side.


CLI Reference

janee init                    # Set up ~/.janee/ with example config
janee add                     # Add a service (interactive)
janee add stripe -u https://api.stripe.com -k sk_xxx  # Add with args
janee remove <service>        # Remove a service
janee remove <service> --yes  # Remove without confirmation
janee list                    # List configured services
janee list --json             # Output as JSON (for integrations)
janee search [query]          # Search service directory
janee search stripe --json    # Search with JSON output
janee cap list                # List capabilities
janee cap list --json         # List capabilities as JSON
janee cap add <name> --service <service>  # Add capability
janee cap edit <name>         # Edit capability
janee cap remove <name>       # Remove capability
janee serve                   # Start MCP server (stdio, default)
janee serve --transport http --port 9100  # Start with HTTP transport (for containers)
janee serve --authority https://janee.example.com --runner-key $JANEE_RUNNER_KEY  # Runner mode
janee authority --runner-key $JANEE_RUNNER_KEY  # Start authority API
janee logs                    # View audit log
janee logs -f                 # Tail audit log
janee logs --json             # Output as JSON
janee sessions                # List active sessions
janee sessions --json         # Output as JSON
janee revoke <id>             # Kill a session

Non-interactive Setup (for AI agents)

AI agents can't respond to interactive prompts. Use --*-from-env flags to read credentials from environment variables β€” this keeps secrets out of the agent's context window:

# Bearer auth (Stripe, OpenAI, etc.)
janee add stripe -u https://api.stripe.com --auth-type bearer --key-from-env STRIPE_KEY

# HMAC auth (Bybit)
janee add bybit --auth-type hmac-bybit --key-from-env BYBIT_KEY --secret-from-env BYBIT_SECRET

# HMAC auth with passphrase (OKX)
janee add okx --auth-type hmac-okx --key-from-env OKX_KEY --secret-from-env OKX_SECRET --passphrase-from-env OKX_PASS

# GitHub App auth (short-lived tokens)
janee add github --auth-type github-app --app-id-from-env GH_APP_ID --pem-from-env GH_PEM --installation-id-from-env GH_INSTALL_ID

# Twitter/X OAuth 1.0a (per-request signing)
janee add twitter --consumer-key $TWITTER_CONSUMER_KEY --consumer-secret $TWITTER_CONSUMER_SECRET \
  --access-token $TWITTER_ACCESS_TOKEN --access-token-secret $TWITTER_ACCESS_TOKEN_SECRET

# AWS SigV4 (SES, S3, etc.)
janee add aws-ses --access-key-id $AWS_ACCESS_KEY_ID --secret-access-key $AWS_SECRET_ACCESS_KEY \
  --region us-east-1 --aws-service ses

When all required credentials are provided via flags, Janee:

  • Never opens readline (no hanging on stdin)

  • Auto-creates a capability with sensible defaults (1h TTL, auto-approve)

You can also edit ~/.janee/config.yaml directly if you prefer.


How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  AI Agent   │─────▢│  Janee   │─────▢│  Stripe β”‚
β”‚             β”‚ MCP  β”‚   MCP    β”‚ HTTP β”‚   API   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
      β”‚                   β”‚
   No key           Injects key
                    + logs request
  1. Agent calls execute MCP tool with capability, method, path

  2. Janee looks up service config, decrypts the real key

  3. Makes HTTP request to real API with key

  4. Logs: timestamp, service, method, path, status

  5. Returns response to agent

Agent never touches the real key.

πŸ“ Deep dive: See Architecture & Security Model for detailed diagrams, threat model, and comparison with alternatives.


Security

  • Encryption: Keys stored with AES-256-GCM

  • Agent identity: Derived from clientInfo.name in the MCP initialize handshake β€” no custom headers needed

  • Agent isolation: Each agent gets its own session with isolated identity (HTTP transport creates a Server+Transport per session)

  • Access control: Per-capability allowedAgents whitelist + server-wide defaultAccess policy

  • Credential scoping: Agent-created credentials default to agent-only

  • Audit log: Every request logged to ~/.janee/logs/

  • Sessions: Time-limited, revocable

  • Kill switch: janee revoke or delete config


Docker

Run Janee as a container β€” no local Node.js required:

# Build
docker build -t janee .

# Run in HTTP mode
docker run -d -p 3000:3000 \
  -v ~/.janee:/root/.janee:ro \
  janee --transport http --port 3000 --host 0.0.0.0

Or use Docker Compose:

mkdir -p config && cp ~/.janee/config.yaml config/
docker compose up -d

For Claude Desktop with Docker, see Docker docs.


Contributing

We welcome contributions! Please read CONTRIBUTING.md before submitting a PR β€” it includes the required PR checklist (tests, changelog, version bump, etc.).


License

MIT β€” Built by True and Useful LLC


Stop giving AI agents your keys. Start controlling access. πŸ”

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

–Maintainers
<1hResponse time
0dRelease cycle
8Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rsdouglas/janee'

If you have feedback or need assistance with the MCP directory API, please join our Discord server