An MCP server that lets Claude, Claude Code, and other AI agents call authenticated APIs — without exposing raw API tokens, keys, or credentials to the LLM.
When an AI agent needs to call an authenticated API, most MCP setups put the secret directly in the client configuration:
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_actual_secret_value_here"
}
}
}
}
This means the raw credential lives in mcp.json, .claude.json, or shell environment — visible to the agent’s configuration layer, potentially logged, and exposed if the configuration is shared or leaked.
secure-actions keeps credentials inside a trusted execution boundary. The AI agent references a secret by name using a placeholder (<<secret:github-pat>>). The actual credential is resolved, decrypted, and injected at request time — inside the MCP server process — and never returned to the model’s context.
flowchart LR
A[Claude / AI Agent] -->|"Action request with placeholder"| B[secure-actions MCP Server]
B -->|Resolve secret internally| C[Encrypted Secret Store]
B -->|Authenticated request| D[External API]
C -.->|"Credential never returned to model"| B
The agent sees <<secret:github-pat>> in tool inputs and outputs. The decrypted token value exists only transiently during HTTP request execution or MCP tool forwarding, and is never logged or included in responses.
Store a secret — The MCP client (e.g., Claude Code) presents a secure input form via MCP elicitation. The user enters the credential directly. The value is encrypted with AES-256-GCM and stored in MongoDB. The LLM never sees it.
Use a secret in HTTP requests — The agent calls http_request with placeholders like Authorization: Bearer <<secret:github-pat>>. secure-actions decrypts and injects the credential at request time.
Use a secret to call other MCP tools — The agent calls mcp_tool_request to invoke any MCP server, with secrets injected into the spawned process environment. This lets you securely use tools like @modelcontextprotocol/server-github without putting tokens in your MCP configuration files.
Manage secrets — List stored identifiers (values are never exposed), delete secrets with confirmation prompts.
curl -sSL https://raw.githubusercontent.com/Kota-Karthik/secure-actions/main/scripts/install.sh | sh
Or install from source:
go install github.com/kotakarthik/secure-actions/cmd/secure-actions@latest
docker compose -f deployments/docker-compose.yml up -d
Add to your MCP configuration (.claude.json, project settings, or Claude Code UI):
{
"mcpServers": {
"secure-actions": {
"type": "stdio",
"command": "secure-actions",
"env": {
"MONGO_URI": "mongodb://localhost:27018"
}
}
}
}
On first run, secure-actions automatically generates a master encryption key at ~/.secure-actions/master.key and creates the required database collections.
request_secretSecurely collect and store a secret from the user via an interactive form (MCP elicitation). The secret value is encrypted before storage and never enters the LLM context.
| Field | Required | Description |
|---|---|---|
name |
Yes | Identifier for the secret (normalized: My API Key becomes my-api-key) |
prompt |
No | Custom prompt message shown in the input form |
description |
No | Description shown above the form |
If the identifier already exists, the user is prompted to confirm before updating.
http_requestExecute an authenticated HTTP request with automatic secret injection.
| Field | Required | Description |
|---|---|---|
method |
Yes | GET, POST, PUT, PATCH, or DELETE |
url |
Yes | Target URL (supports <<secret:id>> placeholders) |
headers |
No | Request headers (supports <<secret:id>> placeholders) |
body |
No | Request body (supports <<secret:id>> placeholders) |
Example — call the GitHub API without exposing your token:
{
"method": "GET",
"url": "https://api.github.com/user/repos",
"headers": {
"Authorization": "Bearer <<secret:github-pat>>",
"Accept": "application/vnd.github+json"
}
}
mcp_tool_requestCall any MCP tool on a remote MCP server with secret injection into the spawned process environment. This is a universal MCP-to-MCP gateway — secure-actions acts as a proxy that injects credentials without exposing them in configuration files.
| Field | Required | Description |
|---|---|---|
mcpConfig |
Yes | Target MCP server configuration (type, command, args, env) |
tool |
Yes | Name of the tool to call on the target server |
parameters |
No | Tool parameters (supports <<secret:id>> placeholders recursively) |
timeoutMs |
No | Timeout in milliseconds (default: 30000) |
Example — create a GitHub issue via @modelcontextprotocol/server-github without tokens in your config:
{
"mcpConfig": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "<<secret:github-pat>>"
}
},
"tool": "create_issue",
"parameters": {
"owner": "my-org",
"repo": "my-repo",
"title": "Bug: login page broken",
"body": "Steps to reproduce..."
}
}
Features: connection pooling (reuses MCP processes), automatic retries on transient errors, idle timeout eviction.
list_secretsReturns all stored secret identifiers and count. Never exposes actual secret values.
delete_secretPermanently delete a stored secret. Requires explicit user confirmation via MCP elicitation before deletion.
pingHealth check — returns pong to verify the server is running.
You: "Store my GitHub personal access token"
→ secure-actions shows a secure input form (MCP elicitation)
→ You enter the token — it's encrypted, never seen by the LLM
You: "List my repos"
→ http_request: GET https://api.github.com/user/repos
Authorization: Bearer <<secret:github-pat>>
→ The actual token is injected server-side, invisible to the model
You: "Create an issue in my repo via GitHub MCP"
→ mcp_tool_request spawns the GitHub MCP server
with GITHUB_PERSONAL_ACCESS_TOKEN=<<secret:github-pat>>
→ The PAT is decrypted and injected into the process environment
→ The LLM never sees the credential value
You: "What secrets do I have?"
→ list_secrets: ["github-pat", "slack-webhook", "openai-key"]
You: "Remove the slack webhook"
→ delete_secret prompts: "Are you sure?" → confirmed → deleted
"GITHUB_TOKEN": "ghp_abc123" in mcp.json or .claude.json, you use <<secret:github-pat>> and secure-actions resolves it at runtime.~/.secure-actions/master.key (file permissions 0600).The credential exists in plaintext only:
request_secret, that bypasses the protection. The security depends on using the tools correctly.| Environment Variable | Default | Description |
|---|---|---|
MONGO_URI |
mongodb://localhost:27018 |
MongoDB connection string |
SECURE_ACTIONS_KEY_PATH |
~/.secure-actions/master.key |
Path to the master encryption key |
All data (MongoDB, master key, encrypted secrets) lives entirely on your local machine. No data leaves your system unless you explicitly make an HTTP request with http_request or mcp_tool_request.
cmd/secure-actions/ Entry point
internal/
actions/
ping/ Health check tool
request_secret/ Secret collection via MCP elicitation
list_secrets/ List stored identifiers
delete_secret/ Delete with confirmation
http_request/ HTTP client with secret injection
mcp_tool_request/ MCP-to-MCP gateway with secret injection and pooling
app/ Application wiring
config/ Configuration loading
elicit/ MCP elicitation interface
mcp/ MCP server and tool registration
mcp_config/ MCP server config types
secrets/ AES-256-GCM encryption, master key management
storage/mongo/ MongoDB client and secret repository
deployments/
docker-compose.yml Local MongoDB setup
Dockerfile Container build
scripts/
install.sh macOS/Linux installer
# Start MongoDB
docker compose -f deployments/docker-compose.yml up -d
# Run from source
go run ./cmd/secure-actions
# Build binary
go build -o secure-actions ./cmd/secure-actions
# Run tests
go test ./internal/...
Releases are automated via GitHub Actions and GoReleaser. Tag and push to create a release with binaries for all platforms (linux/darwin/windows, amd64/arm64):
git tag v0.1.0
git push origin v0.1.0
| Traditional MCP Setup | With secure-actions | |
|---|---|---|
| Where credentials live | mcp.json, .claude.json, shell env |
Encrypted in local MongoDB |
| LLM sees the credential | Potentially (in config, env, or context) | Never (only sees <<secret:id>> placeholder) |
| Configuration sharing | Must strip secrets before sharing | Config is safe to share — no secrets in it |
| Secret rotation | Edit config files | request_secret with same identifier |
| Audit | No visibility into which secrets exist | list_secrets shows all identifiers |
| Deletion | Manual file editing | delete_secret with confirmation |
Apache License 2.0 — see LICENSE.