skeleton-mcp

skeleton-mcp

A starter template for building an MCP server with Vault-based secret management and Postgres-backed configuration, featuring tool-level authorization and redacted output.

Category
Visit Server

README

skeleton-mcp

Node.js MCP skeleton with:

  • Vault-backed secret management
  • Postgres-backed configuration management
  • Security and operational defaults for production-style patterns

Purpose

This repository is a starter template for building an MCP server that needs:

  • Secret reads and writes through Vault
  • Config reads and writes through Postgres
  • Tool-level authorization for mutating operations
  • Redacted tool output by default
  • Basic reliability controls for external secret writes

Skeleton Architecture

Runtime flow:

  1. src/index.js boots the app, creates services, and connects MCP stdio transport.
  2. src/config/env.js loads and validates environment configuration.
  3. src/services/configStore.js handles Postgres persistence.
  4. src/services/vault.js handles Vault operations and write retry queue.
  5. src/services/security.js redacts sensitive fields.
  6. src/mcp/server.js registers MCP tools and applies auth/error wrappers.
  7. src/http/server.js exposes MCP over HTTP with auth, limits, and access logs.
  8. src/http/index.js boots the dedicated HTTP MCP process.
  9. src/start-both.js starts stdio and HTTP as separate child processes.

Local infrastructure:

Tool Catalog

Read-only tools:

  • connection_info
  • vault_connection_info
  • healthcheck
  • list_configs
  • get_config
  • list_secrets
  • get_secret
  • vault_agent_token_read
  • token_lookup_self
  • token_rotation_config

Mutating tools:

  • set_config
  • delete_config
  • set_secret
  • delete_secret
  • token_renew_self
  • token_create
  • token_revoke
  • token_revoke_self

If MCP_ADMIN_AUTH_KEY is configured, mutating tools require authorizationKey.

Security Behavior

  • Sensitive fields are redacted unless MCP_ALLOW_SENSITIVE_OUTPUT=true.
  • Mutating operations can be access controlled with MCP_ADMIN_AUTH_KEY.
  • Vault write operations are serialized through an internal queue and retried with exponential backoff.

Environment Variables

Core:

  • MCP_SERVER_NAME
  • MCP_SERVER_VERSION
  • MCP_ALLOW_SENSITIVE_OUTPUT
  • MCP_ADMIN_AUTH_KEY
  • MCP_TRANSPORT_MODE (stdio, http, or both)
  • MCP_CONFIG_DEFAULT_USER_ID
  • MCP_TOKEN_ROTATION_DEFAULT_INTERVAL_MS
  • MCP_TOKEN_ROTATION_USER_INTERVAL_CONFIG_KEY
  • MCP_VAULT_AGENT_AUTH_MODE_CONFIG_KEY
  • MCP_VAULT_AGENT_TOKEN_FILE_PATH_CONFIG_KEY
  • MCP_VAULT_AGENT_LISTENER_ADDR_CONFIG_KEY

HTTP transport:

  • MCP_HTTP_HOST
  • MCP_HTTP_PORT
  • MCP_HTTP_PATH
  • MCP_HTTP_HEALTH_PATH
  • MCP_HTTP_AUTH_MODE (token, oauth2, both)
  • MCP_HTTP_TOKEN_SOURCE (vault, env)
  • MCP_HTTP_AUTH_TOKENS (comma-separated bearer tokens)
  • MCP_HTTP_TRUST_PROXY
  • MCP_HTTP_ALLOWED_ORIGINS (comma-separated)
  • MCP_HTTP_ALLOWED_IPS (comma-separated)
  • MCP_HTTP_MAX_BODY_BYTES
  • MCP_HTTP_RATE_LIMIT_WINDOW_MS
  • MCP_HTTP_RATE_LIMIT_MAX_REQUESTS
  • MCP_HTTP_VAULT_TOKEN_INDEX_PATH
  • MCP_HTTP_VAULT_TOKEN_DEFAULT_USER_ID
  • MCP_HTTP_VAULT_TOKEN_REQUIRED_SCOPES
  • MCP_HTTP_VAULT_TOKEN_REQUIRED_AUDIENCE
  • MCP_HTTP_VAULT_TOKEN_CACHE_TTL_MS
  • MCP_HTTP_OAUTH2_INTROSPECTION_URL
  • MCP_HTTP_OAUTH2_CLIENT_ID
  • MCP_HTTP_OAUTH2_CLIENT_SECRET
  • MCP_HTTP_OAUTH2_REQUIRED_SCOPES
  • MCP_HTTP_OAUTH2_REQUIRED_AUDIENCE
  • MCP_HTTP_OAUTH2_TIMEOUT_MS
  • MCP_HTTP_OAUTH2_CACHE_TTL_MS
  • MCP_HTTP_TLS_ENABLED
  • MCP_HTTP_TLS_CERT_PATH
  • MCP_HTTP_TLS_KEY_PATH

Postgres:

  • POSTGRES_HOST
  • POSTGRES_PORT
  • POSTGRES_DB
  • POSTGRES_USER
  • POSTGRES_PASSWORD

Postgres config model:

  • Configuration data is multi-user scoped in app_config using composite key (user_id, key).
  • MCP config tools accept optional userId; when omitted, MCP_CONFIG_DEFAULT_USER_ID is used.
  • Seed records include:
    • default/sample.feature
    • default/app.defaults for future default parameters.
    • default/token.rotation.intervalMs
    • default/vault.agent.auth.mode
    • default/vault.agent.tokenFilePath
    • default/vault.agent.listener.addr

Vault:

  • VAULT_ADDR
  • VAULT_TOKEN
  • VAULT_AGENT_ENABLED
  • VAULT_AGENT_AUTH_MODE (none, file, listener, both)
  • VAULT_AGENT_TOKEN_FILE_PATH
  • VAULT_AGENT_LISTENER_ENABLED
  • VAULT_AGENT_LISTENER_ADDR
  • VAULT_UNSEAL_KEY
  • VAULT_KV_MOUNT
  • VAULT_WRITE_RETRY_ATTEMPTS
  • VAULT_WRITE_RETRY_BASE_DELAY_MS
  • VAULT_WRITE_RETRY_MAX_DELAY_MS

Reference values are in .env.example.

Quick Start

  1. Install dependencies.
  2. Copy .env.example to .env.
  3. Start local services with docker compose up -d.
  4. Resolve the managed unseal key: npm run vault:unseal-key -- --json.
  5. Initialize and unseal local Vault (first run):
docker exec -e VAULT_ADDR=http://127.0.0.1:8200 skeleton-mcp-vault vault operator init -key-shares=1 -key-threshold=1 -format=json
docker exec -e VAULT_ADDR=http://127.0.0.1:8200 skeleton-mcp-vault vault operator unseal <unseal_key_from_init_or_env>
  1. Seed a test secret in Vault.
  2. Start the MCP server with npm start.
  3. Run tests with npm test.

Test Coverage Notes

Current automation includes listener-related coverage for Vault Agent runtime resolution.

  • tests/vault-agent-runtime.test.js validates:
    • listener mode resolution from Postgres defaults
    • both mode resolution (listener + file)
    • fallback to environment values when database mode is invalid

Transport scripts:

# stdio only (default)
npm run start:stdio

# HTTP transport only
npm run start:http

# run stdio + HTTP as two processes
npm run start:both

HTTP MCP Endpoint

Default endpoint values:

  • MCP URL: http://127.0.0.1:3000/mcp
  • Health URL: http://127.0.0.1:3000/healthz

HTTP transport security controls:

  • Every /mcp request requires Authorization: Bearer <token>.
  • For MCP_HTTP_AUTH_MODE=token, set MCP_HTTP_TOKEN_SOURCE=vault to validate tokens from Vault.
  • For MCP_HTTP_AUTH_MODE=oauth2, bearer tokens are validated by OAuth2 introspection.
  • For MCP_HTTP_AUTH_MODE=both, either token strategy can authorize requests.
  • Mutating tools still require authorizationKey when MCP_ADMIN_AUTH_KEY is set.
  • Request limits are enforced with:
    • MCP_HTTP_MAX_BODY_BYTES
    • MCP_HTTP_RATE_LIMIT_WINDOW_MS
    • MCP_HTTP_RATE_LIMIT_MAX_REQUESTS
  • Optional network restrictions:
    • MCP_HTTP_ALLOWED_ORIGINS
    • MCP_HTTP_ALLOWED_IPS

Vault Multi-User Token Model

Store HTTP bearer tokens in Vault at MCP_HTTP_VAULT_TOKEN_INDEX_PATH.

Default-user fallback behavior:

  • MCP_HTTP_VAULT_TOKEN_DEFAULT_USER_ID defaults to default.
  • If no non-default users exist in the token index, the default user is always used as fallback.

Supported index shape:

{
	"tokens": {
		"<sha256(token)>": {
			"userId": "user-123",
			"tokenId": "tok-123",
			"active": true,
			"scopes": ["mcp:invoke", "mcp:read"],
			"audience": ["codex", "claude"],
			"expiresAt": "2026-12-31T23:59:59Z"
		}
	}
}

Notes:

  • Store only token hashes in Vault index data, never plaintext tokens.
  • MCP_HTTP_VAULT_TOKEN_REQUIRED_SCOPES and MCP_HTTP_VAULT_TOKEN_REQUIRED_AUDIENCE enforce policy checks.
  • This keeps secrets in Vault while configuration remains in Postgres.

Vault Token Lifecycle MCP Tools

The skeleton exposes node-vault token lifecycle methods as MCP tools:

  • token_lookup_self -> tokenLookupSelf
  • token_renew_self -> tokenRenewSelf
  • token_create -> tokenCreate
  • token_revoke -> tokenRevoke
  • token_revoke_self -> tokenRevokeSelf

These tools are intended for controlled operational usage and are guarded by admin authorization when MCP_ADMIN_AUTH_KEY is configured.

Vault Agent Auto-Auth and Token Renewal

Vault Agent can own token auth/renewal while this service reads the sink token file.

  • Enable with VAULT_AGENT_ENABLED=true
  • Choose auth mode with VAULT_AGENT_AUTH_MODE:
    • file: use Vault Agent token sink file
    • listener: use Vault Agent listener endpoint
    • both: enable listener operations and file-based token read workflows
  • Configure sink file path with VAULT_AGENT_TOKEN_FILE_PATH
  • Enable listener with VAULT_AGENT_LISTENER_ENABLED=true
  • Configure listener with VAULT_AGENT_LISTENER_ADDR
  • Use vault_agent_token_read when application workflows need token sink visibility

When Vault Agent mode is enabled:

  • File mode refreshes token state from the configured token sink path.
  • Listener mode routes Vault operations through the configured Vault Agent listener.
  • Both mode supports listener operations and token sink read workflows.

Option 3: Postgres-Backed Non-Secret Vault Agent Settings

This skeleton supports storing non-secret Vault Agent runtime pointers/settings in Postgres while keeping token material in Vault.

  • Runtime settings are read from default user config scope (MCP_CONFIG_DEFAULT_USER_ID).
  • Key names are configurable with:
    • MCP_VAULT_AGENT_AUTH_MODE_CONFIG_KEY
    • MCP_VAULT_AGENT_TOKEN_FILE_PATH_CONFIG_KEY
    • MCP_VAULT_AGENT_LISTENER_ADDR_CONFIG_KEY
  • Recommended values in Postgres:
    • vault.agent.auth.mode
    • vault.agent.tokenFilePath
    • vault.agent.listener.addr

Rotation Time Configuration

Rotation interval supports both global defaults and user-scoped overrides:

  • Global default env variable: MCP_TOKEN_ROTATION_DEFAULT_INTERVAL_MS
  • User-scoped config key name: MCP_TOKEN_ROTATION_USER_INTERVAL_CONFIG_KEY (default token.rotation.intervalMs)

Effective value resolution is:

  1. User-scoped Postgres config (userId + key)
  2. Default user Postgres config (default + key)
  3. Global env default

Use token_rotation_config tool to inspect the resolved rotation interval for a user scope.

Minimal remote call example:

curl -i http://127.0.0.1:3000/mcp \
	-H "Authorization: Bearer replace-me-token" \
	-H "Accept: application/json, text/event-stream" \
	-H "Content-Type: application/json" \
	-d '{
		"jsonrpc": "2.0",
		"id": 1,
		"method": "initialize",
		"params": {
			"protocolVersion": "2025-11-25",
			"capabilities": {},
			"clientInfo": { "name": "client", "version": "1.0.0" }
		}
	}'

HTTPS Deployment Choice

This repository uses a reverse proxy (recommended): terminate TLS at a reverse proxy or load balancer.

  • Keep this app on internal HTTP.
  • Enforce HTTPS, client allowlists, and edge-level controls at the proxy/LB.
  • Forward traffic to MCP_HTTP_HOST:MCP_HTTP_PORT.
  • Keep MCP_HTTP_TLS_ENABLED=false in this process mode.

Popular patterns include Nginx, Traefik, Envoy, ALB/NLB, or Cloudflare Tunnel in front of /mcp.

Vault Production Migration (Raft)

The repository now includes a Vault production migration scaffold under vault-production:

Managed unseal key flow:

  • VAULT_UNSEAL_KEY is optional and can be injected at runtime.
  • If VAULT_UNSEAL_KEY is not set, npm run vault:unseal-key reads src/config/vault.unseal.key.json.
  • If the key file is missing or empty, a 24-character key is generated and saved to src/config/vault.unseal.key.json.
  • Both compose stacks run a one-shot vault-unseal-key-init service before Vault startup to ensure key material exists.

Run the conversion:

bash vault-production/scripts/convert-dev-to-prod.sh --init-keys-out vault-production/backups/vault-init.json

Common options:

# Use a different KV mount
bash vault-production/scripts/convert-dev-to-prod.sh --mount secret

# Migrate infra only (skip data movement)
bash vault-production/scripts/convert-dev-to-prod.sh --skip-export --skip-import

# Use when Raft Vault is already initialized
bash vault-production/scripts/convert-dev-to-prod.sh --skip-init

# Explicitly set key file path used by convert script
bash vault-production/scripts/convert-dev-to-prod.sh --unseal-key-path src/config/vault.unseal.key.json

# Post-conversion hardening bootstrap (audit, policy, AppRole)
bash vault-production/scripts/bootstrap-post-conversion.sh --vault-token <root_or_admin_token>

# CI-friendly machine output
bash vault-production/scripts/bootstrap-post-conversion.sh \
	--vault-token <root_or_admin_token> \
	--output json

VS Code Agent Structure

This repository includes a project agent structure for adapting the skeleton to additional service-backed MCP implementations:

Workspace custom agent:

Use this custom agent when you want GitHub Copilot in VS Code to:

  1. Configure new service adapters under src/services.
  2. Register matching MCP tools in src/mcp/server.js.
  3. Update env validation in src/config/env.js.
  4. Preserve authorization and redaction behavior.
  5. Add tests and documentation updates.

Test Coverage

Integration tests in tests/server.integration.test.js cover:

  • Healthcheck behavior
  • Authorization on mutating tools
  • Redaction behavior for secret output

HTTP transport tests in tests/http.integration.test.js cover:

  • Unauthorized requests are rejected
  • Authorized MCP initialization succeeds
  • Internal failures return JSON-RPC-compatible error responses
  • Health endpoint behavior

Vault token auth tests in tests/vault-token-auth.test.js cover:

  • Multi-user token index lookup by SHA-256 hash
  • Inactive token rejection
  • Scope/audience-aware authorization inputs

Production migration tests in tests/vault-production.test.js cover:

  • Presence of Vault production scaffold files
  • Raft config expectations
  • Non-dev Vault compose command validation
  • Conversion/bootstrap script help and bash syntax checks

Extend The Skeleton

  1. Add domain services under src/services.
  2. Register new tools in src/mcp/server.js.
  3. Add corresponding tests under tests.
  4. Keep mutating tools behind authorization checks.
  5. Keep secret-bearing fields redacted by default.

Notes

  • docker-compose.yml now runs Vault with Raft-backed storage for local persistence.
  • The managed key script is an automation helper and not a Vault KMS/HSM auto-unseal backend.
  • The migration scaffold starts with bootstrap-friendly defaults and still requires TLS, production auth methods, and credential rotation before real production use.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured