Emanuell150 downloadsStore API tokens and environment variables encrypted, reference them in notes by key, and let AI agents use them in requests without ever seeing the values.
English
Environment Keys keeps your API tokens, passwords and other sensitive values encrypted inside your Obsidian vault, and lets AI agents such as Claude Code, Codex and Cursor use them without ever seeing them. Your notes refer to each value only by its name, and the plugin fills in the real value at the last moment, only for the websites you allowed.
The plugin also turns your vault into a local MCP server: you describe API calls and tools in ordinary notes, and every connected AI agent can use them right away, with your AI client asking for your permission before it runs them. Everything runs on your computer, with no external service in between.
Português (PT-BR)
O Environment Keys guarda os seus tokens de API, senhas e outros dados sensíveis cifrados dentro do cofre do Obsidian e deixa que agentes de IA, como o Claude Code, o Codex e o Cursor, usem esses dados sem nunca vê-los. As suas notas citam cada valor só pelo nome, e o plugin coloca o valor real no último momento, apenas nos sites que você autorizou.
O plugin também transforma o seu cofre num servidor MCP local: você descreve chamadas de API e ferramentas em notas comuns, e qualquer agente de IA conectado passa a usá-las na hora, com o seu cliente de IA pedindo a sua permissão antes de executá-las. Tudo roda no seu computador, sem nenhum serviço externo no meio.
Hide your API tokens and other sensitive data from AI agents such as Claude Code, Codex, Antigravity and Cursor.
Store API tokens and environment variables encrypted inside your vault, reference them in notes by key, and let AI agents use them in HTTP requests without ever seeing the values.
Your notes contain only a key such as {{basic:JIRA_ACME}}. When an AI agent (Claude Code, Claude Desktop, Cursor, or any MCP client) needs to call an API, it sends the request to this plugin with the key. The plugin replaces the key with the real value right before sending, checks that the destination is allowed, masks the value in the response, and returns the result.
Available in English and Brazilian Portuguese. The language follows Obsidian's, or choose it in Settings → Environment Keys → Language.
Desktop only, Obsidian 1.13 or later. The plugin runs a local server, which needs Node.js APIs that are not available on mobile.
main.js, manifest.json and styles.css from the latest release into <vault>/.obsidian/plugins/environment-variables/, then enable the plugin in Settings → Community plugins.JIRA_ACME), a type, the value and the allowed hosts (acme.atlassian.net).{{basic:JIRA_ACME}} instead of the token. Type {{secret: to autocomplete names, in the note text or in a property value, even while the vault is locked. Keys are shown as a lock chip in reading view and in the Properties panel; click a chip in Properties to edit it. A chip turns red when the name does not exist in the unlocked vault.Already have tokens in your notes? Select one and run Convert selection into a variable. When you paste something that looks like a token (Atlassian, GitHub, GitLab, OpenAI, Anthropic, AWS, Google, Slack, Stripe or a JWT) into a note, the plugin offers to store it encrypted and put a key in its place. You can turn this warning off in the settings.
| Key | Becomes |
|---|---|
{{secret:NAME}} |
The raw value |
{{secret:NAME.user}} |
The username of a username + token variable |
{{basic:NAME}} |
Basic base64(username:token) |
{{bearer:NAME}} |
Bearer <token> |
Open the panel, expand AI clients and click Connect. The plugin:
claude mcp add --transport http --scope user environment-variables ..., so it is available in every project;[mcp_servers.environment-variables] block to ~/.codex/config.toml (or $CODEX_HOME/config.toml). The rest of the file is left untouched;environment-variables entry (serverUrl + headers) to ~/.gemini/config/mcp_config.json, keeping your other servers;environment-variables entry to ~/.cursor/mcp.json, keeping your other servers.Start a new session in the tool to load it. Disconnect revokes the token and removes the entry. If you change the port, connected tools are updated automatically.
Each vault runs its own server: its own port, its own MCP server name, its own variables and client tokens. A token from one vault is refused by the server of another.
environment-variables-work-notes (Settings → MCP server name), so connecting one vault never replaces another vault's entry in Claude Code, Codex, Antigravity or Cursor. A vault connected before 1.3.0 keeps the name environment-variables only if the entry registered under it holds that vault's token; otherwise it gets a name of its own and its connected tools are registered again under it./v1/health tells which vault owns a port (a hash of its folder and its name, no secrets).Use Other client (manual setup) to get a token and a ready-made command.
Add an HTTP MCP server at http://127.0.0.1:<port>/mcp with the header Authorization: Bearer <client token>. Tools:
list_secrets: names, types, descriptions and allowed hosts. Never values.http_request: { method, url, headers, body } with keys in header values.curl -s http://127.0.0.1:27150/v1/request \
-H "Authorization: Bearer <client token>" \
-H "Content-Type: application/json" \
-d '{"url":"https://acme.atlassian.net/rest/api/3/myself","headers":{"Authorization":"{{basic:JIRA_ACME}}"}}'
Turn on Settings → Environment Keys → Vault tools and every note tagged #mcp/tool (configurable) becomes an MCP tool for all connected AI clients. Nothing is written in code: tools are notes, and the plugin finds them by tag and link, never by folder. The list updates by itself when a note changes (notifications/tools/list_changed).
There are three kinds of notes:
| Note | Purpose |
|---|---|
| Service | One note per instance or account, with its values, e.g. url: https://acme.atlassian.net and auth: "{{basic:JIRA_ACME}}". All service notes of an app share a tag. |
Request (#api/request) |
A generic, saved HTTP request, like in Postman, in an http code block. {{service.url}} comes from the service note of the chosen instance, {{param:key}} from the tool call. |
Tool (#mcp/tool) |
What the AI can call: tool, description, params, writes. kind: request runs a request note; kind: script runs the js code block of the note itself. |
A request note:
---
tags: [api/request]
---
```http
GET {{service.url}}/rest/api/3/issue/{{param:key}}?fields={{param:fields}}
Authorization: {{service.auth}}
Accept: application/json
```
And one generic tool for every instance (the notes tagged jira/instance):
---
tags: [mcp/tool]
tool: jira_get_issue
kind: request
request: "[[Jira - Get issue]]"
service_tag: jira/instance
service_param: instance
description: Gets a Jira issue by key, e.g. ACME-123, on the chosen instance.
params:
key: { type: string, required: true }
fields: { type: string, default: "summary,status,assignee" }
---
The plugin adds the instance parameter by itself, with the instances it finds ("Jira - ACME" and "Jira - Globex" become ACME and Globex), so a new instance only needs a new service note: jira_get_issue({ instance: "ACME", key: "ACME-123" }).
When the plugin is installed, it creates one instance (service note) template for each service the catalog's packages use: Atlassian Access - Template (atlassian/instance: Jira, JSM, Confluence, Automation, Assets), Bitbucket Access - Template (bitbucket/workspace) and Trello Access - Template (trello/account), in an Instances folder next to the MCP Tools note and in the plugin's language. Each one is created once: a template you delete is not created again. Duplicate a template, name the copy after your instance, remove the template tag and fill in its properties; each template, and How to use → How to fill in each template in the panel, explains every property.
You do not need to write these notes yourself. Ask your AI agent, for example "create tools to list and search files in Google Drive":
CLAUDE.md so later conversations follow them. It also adds one or several tools to an app the vault already has, reusing its service notes and variables. Clients connected to the plugin get it through the configure_vault_tools MCP prompt and the get_tool_authoring_guide tool. You can also copy it from the panel (Vault tools → Prompt for AI agents) or with the Copy the AI agent prompt command, and paste it into any agent.list_vault_tools and list_secrets. It ends with a mandatory question (which environment variables, for which app or service and which tools), and the agent must not create anything until it has the answer.list_vault_tools and tests the read-only tools.search_vault_tools finds the tool for a task, hidden ones too: pass the method and URL you would send with http_request (or keywords) and it returns the matching tools with how to run each one. When http_request calls an endpoint a vault tool covers, its response carries a vault_tool_hint pointing to that tool, and the server instructions tell agents to search before using http_request.run_vault_tool runs any tool by name, including tools with expose: false and tools created after the client loaded its list. The same is available over REST: GET /v1/tools, POST /v1/tools/<name>, GET /v1/tools/guide.The panel's Vault tools section lists every tool with its status (ready, script tools off, invalid) and the problems to fix, and lets you open and test each one.
Script tools are off until you turn on Enable script tools. The code runs in QuickJS, a JavaScript engine compiled to WebAssembly, not in a Web Worker: Obsidian gives workers access to Node.js. The script has no network, no Node.js, no timers and no access to Obsidian; its only way out is the ctx API (ctx.requests.run, ctx.http, ctx.notes.get, ctx.notes.query, ctx.log). CPU time and memory are limited.
The plugin does not ask for approval to run a tool: permission is left to your AI client, which asks before calling an MCP tool (in Claude Code, unless you allowed that tool or turned permissions off). Requests made by vault tools skip the per-variable Approval setting too, including variables that allow any host; it still applies to http_request and REST calls. For any-host variables the agent guide requires the AI to show you the destination URL and ask before every run: the plugin itself no longer checks that address for tool requests, so prefer exact hosts. Keep in mind that an agent that can edit notes can also change a script, so review the tool notes it creates.
api.example.com/v1/workspaces/123/*. Paths with encoded slashes or backslashes are refused so a server cannot decode them into another path.*.atlassian.net or *.s3.amazonaws.com also matches other customers' tenants. The plugin asks for confirmation and shows a warning; prefer the exact host. Wildcards on public suffixes (*.com, *.com.br) are refused.list_secrets.http://localhost when explicitly allowed.*** in response headers and bodies, error messages and the usage log, including their base64, base64url, URL-encoded (upper and lower case), form-encoded, hexadecimal, JSON-escaped and Basic auth forms. Fragments of 12 or more characters from long values are masked too, which covers APIs that echo a truncated token in an error.127.0.0.1, requires a client token, rejects requests from web browsers (Origin header) and foreign Host headers (DNS rebinding). The server is off until you turn it on.http_request: allowed hosts, placement, masking and per-client access still apply, with the caller's own access. The per-variable approval is skipped for tool requests, any-host variables included: the AI client asks for permission, and the agent guide requires it to ask before every run that uses an any-host variable. Tool arguments may not contain secret placeholders; only service and request notes can. Script tools run sandboxed in QuickJS (no network, no Node.js). Permission to run a tool is asked by the AI client, not by the plugin.127.0.0.1 (port 27150 by default) when you turn it on, and sends HTTP requests only to the hosts you configure for each variable, when an authorized client asks it to. It does not contact any other service.<vault>/.obsidian/plugins/environment-variables/vault.enc. Settings, client token hashes and the usage log are stored in the plugin's data.json, which contains no values. Vault tools only read notes; they never write to your vault. By default it also keeps the names and types of your variables (never the values), so you can insert references while the vault is locked; turn off Show variable names while locked to keep names inside the encrypted file only.claude program in its usual install locations and runs claude mcp add / claude mcp remove. Claude Code then updates its own configuration. To tell its own entry from another vault's, the plugin also reads (never writes) ~/.claude.json.~/.codex/config.toml. It only adds or removes its own block, between # >>> environment-variables and # <<< environment-variables markers, and refuses to write if you already added that server by hand.~/.gemini/config/mcp_config.json.~/.cursor/mcp.json.environment-variables entry.{{secret:NAME}}, the client token in manual setup, or the AI agent prompt). When you paste into a note, it checks the pasted text for token formats, on your device only. It never copies a secret value.npm install
npm test # unit and integration tests
npm run build # type-check and bundle main.js
npm run deploy # build and copy to the vault in .deploy-target
MIT