secure-actions

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.


The Problem

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.

The secure-actions Approach

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.


How It Works

  1. 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.

  2. 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.

  3. 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.

  4. Manage secrets — List stored identifiers (values are never exposed), delete secrets with confirmation prompts.


Quick Start

Prerequisites

Install

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

Start MongoDB

docker compose -f deployments/docker-compose.yml up -d

Register in Claude Code

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.


MCP Tools

request_secret

Securely 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_request

Execute 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_request

Call 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_secrets

Returns all stored secret identifiers and count. Never exposes actual secret values.

delete_secret

Permanently delete a stored secret. Requires explicit user confirmation via MCP elicitation before deletion.

ping

Health check — returns pong to verify the server is running.


Usage Examples

Store and use a GitHub token

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

Use MCP tools without tokens in configuration

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

Manage stored secrets

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

Security Model

What secure-actions protects against

Trust boundary

The credential exists in plaintext only:

  1. When the user types it into the MCP elicitation form (client-side)
  2. Transiently in the secure-actions process memory during request execution

Limitations


Configuration

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.


Project Structure

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

Development

# 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/...

Releasing

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

Comparison: Traditional MCP vs secure-actions

  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

License

Apache License 2.0 — see LICENSE.