Janee is an MCP server that acts as a credential proxy between AI agents and external APIs. Agents interact with APIs through Janee's MCP tools — they describe what they want to do, and Janee injects the real credentials at the last mile. Agents never see or handle raw secrets.
┌─────────────────────────────────────────────────────────────────┐
│ AI Agent (Claude, GPT, etc.) │
│ │
│ "Call Stripe API to list customers" │
└────────────────────────┬────────────────────────────────────────┘
│ MCP Protocol (stdio or HTTP)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Janee MCP Server │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────────────────┐ │
│ │ Tool Router │ │ Policy Engine│ │ Audit Logger │ │
│ │ │ │ │ │ │ │
│ │ execute() │ │ Allow/deny │ │ Every request logged │ │
│ │ exec() │ │ per service │ │ with timestamp, path, │ │
│ │ list_services│ │ per method │ │ method, status │ │
│ └──────┬───────┘ └──────┬───────┘ └───────────────────────┘ │
│ │ │ │
│ ┌──────▼─────────────────▼──────────────────────────────────┐ │
│ │ Credential Injection Layer │ │
│ │ │ │
│ │ Reads encrypted secrets from ~/.janee/config.yaml │ │
│ │ Injects auth headers (Bearer, Basic, API-Key, Custom) │ │
│ │ Injects env vars for exec-mode tools │ │
│ │ Resolves GitHub App tokens (short-lived) │ │
│ └──────┬────────────────────────────────────────────────────┘ │
│ │ │
└─────────┼───────────────────────────────────────────────────────┘
│ Real HTTP request (with credentials)
▼
┌─────────────────────┐ ┌─────────────────────┐ ┌──────────────┐
│ api.stripe.com │ │ api.github.com │ │ CLI tools │
│ (Bearer: sk_live_) │ │ (Bearer: ghp_) │ │ (env vars) │
└─────────────────────┘ └─────────────────────┘ └──────────────┘
- Agent calls
executewith service name, method, path, and body - Janee looks up the service in config
- Policy engine checks if the request is allowed (method, path patterns)
- Janee constructs the real HTTP request with injected credentials
- Request is sent to the external API
- Response is returned to the agent (with optional field filtering)
- Full request/response is logged to the audit trail
- Agent calls
execwith a command name and arguments - Janee looks up the command in config
- Janee spawns the process with injected environment variables
- stdout/stderr is captured and returned to the agent
- The agent never sees the env var values — only the output
- Agent requests a GitHub operation
- Janee generates a short-lived installation token (expires in 1 hour)
- Token is injected as a Bearer credential
- Git HTTPS operations automatically use the token via credential helper
- No static PATs needed — tokens rotate automatically
Agent ──stdio──▶ Janee ──HTTPS──▶ APIs
The simplest mode. Janee runs as a child process of the MCP client (Claude Desktop, Cursor, etc.) communicating over stdio. Config lives in ~/.janee/.
┌──────────────────────────┐ ┌──────────────────────────────┐
│ Container (Agent) │ │ Host (Authority) │
│ │ │ │
│ Agent ──stdio──▶ Runner │────▶│ Authority ──HTTPS──▶ APIs │
│ │REST │ │
│ Runner has NO secrets │ │ Authority has ALL secrets │
└──────────────────────────┘ └──────────────────────────────┘
For containerized agents (like those in OpenSeed), Janee splits into two components:
- Runner — runs inside the container alongside the agent. Has no secrets. Forwards requests over REST to the Authority.
- Authority — runs on the host. Holds all secrets. Validates requests, injects credentials, enforces policies.
This means even if the agent compromises its container, it cannot access raw credentials.
| Threat | Mitigation |
|---|---|
| Agent reads credentials from config | Config is on host; runner in container has no access |
| Agent intercepts HTTP traffic | Credentials injected at the authority level, outside the container |
| Prompt injection exfiltrates keys | Agent never sees keys — nothing to exfiltrate |
| Agent makes unauthorized API calls | Policy engine restricts methods, paths, and services |
| Credential leak in logs | Audit log records requests but redacts auth headers |
| Stolen GitHub PAT | GitHub App mode uses short-lived tokens (1-hour expiry) |
- Secrets in
config.yamlare encrypted at rest using a master key - Master key is stored in the OS keychain (macOS Keychain, Linux Secret Service) or a file with
0600permissions - Secrets are decrypted only when needed for request injection
Every API call through Janee is logged:
{
"timestamp": "2024-01-15T10:30:00Z",
"service": "stripe",
"method": "GET",
"path": "/v1/customers",
"status": 200,
"duration_ms": 145,
"agent": "claude-desktop"
}| Feature | Janee | Raw API Keys | Vault/1Password | OAuth Proxy |
|---|---|---|---|---|
| Agent never sees secrets | ✅ | ❌ | ❌¹ | ✅ |
| MCP native | ✅ | N/A | ❌ | ❌ |
| Per-request audit trail | ✅ | ❌ | ❌ | Partial |
| Request policies (method/path) | ✅ | ❌ | ❌ | Partial |
| CLI tool support (exec mode) | ✅ | ❌ | ❌ | ❌ |
| GitHub App integration | ✅ | N/A | ❌ | ❌ |
| Works in containers | ✅ | ✅ | ✅ | ✅ |
| Zero agent code changes | ✅ | ✅ | ❌ | ❌ |
| Local-first (no cloud) | ✅ | ✅ | ❌² | ✅ |
| Session TTLs with revocation | ✅ | ❌ | ✅ | ✅ |
| Install complexity | npm i -g |
None | High | Medium |
¹ Vault/1Password can manage secrets, but the agent still receives the secret to make the API call. ² HashiCorp Vault can run locally, but is complex to operate. 1Password requires cloud.
Environment variables are the most common way to give agents API access. The problems:
- No isolation — every process in the environment can read them
- No audit trail — you don't know which agent called which API
- No policies — an agent with
STRIPE_KEYcan do anything on Stripe - No revocation — to revoke, you must restart the process
- Prompt injection risk — an injected prompt can instruct the agent to read
process.envand exfiltrate keys
Janee solves all of these. The agent calls execute("stripe", "GET", "/v1/customers") and gets the response. It never handles the key.
OAuth is great for user-facing apps, but AI agents aren't users:
- Agents can't complete browser-based OAuth flows
- OAuth tokens still need to be stored somewhere the agent can access
- OAuth doesn't help with CLI tools or non-OAuth APIs
- Most APIs that agents call (Stripe, OpenAI, etc.) use API keys, not OAuth
See the main README for full configuration details, or the library usage guide for programmatic access.