Janee 🔐
Secrets management for AI agents via MCP
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
The Solution
Janee is an MCP server that manages API secrets for AI agents:
- Store your API keys — encrypted locally in
~/.janee/ - Run
janee serve— starts MCP server - Agent requests access — via
executeMCP tool - Janee injects the real key — agent never sees it
- 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/janeeInitialize
janee initThis creates ~/.janee/config.yaml with example services.
Add Services
Option 1: Interactive (recommended for first-time users)
janee addJanee 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: trueAdd 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-agentDone. 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 789Or 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 serveUse 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 — Native plugin (
@true-and-useful/janee-openclaw)- Containerized agents? See Container setup guide
- Cursor — Setup guide
- Claude Code — Setup guide
- Codex CLI — Setup guide
- Any MCP client — just point at
janee serve
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-openclawEnable in your agent config:
{
agents: {
list: [{
id: "main",
tools: { allow: ["janee"] }
}]
}
}Your agent now has these tools:
janee_list_services— Discover available APIsjanee_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: trueServices = 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: trueOr use the built-in template:
janee add twitterAWS 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
…