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.
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:
- src/index.js boots the app, creates services, and connects MCP stdio transport.
- src/config/env.js loads and validates environment configuration.
- src/services/configStore.js handles Postgres persistence.
- src/services/vault.js handles Vault operations and write retry queue.
- src/services/security.js redacts sensitive fields.
- src/mcp/server.js registers MCP tools and applies auth/error wrappers.
- src/http/server.js exposes MCP over HTTP with auth, limits, and access logs.
- src/http/index.js boots the dedicated HTTP MCP process.
- src/start-both.js starts stdio and HTTP as separate child processes.
Local infrastructure:
- docker-compose.yml runs Postgres and Vault for local development.
- initdb/001_config.sql creates and seeds the app_config table.
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, orboth) - 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_configusing composite key(user_id, key). - MCP config tools accept optional
userId; when omitted,MCP_CONFIG_DEFAULT_USER_IDis used. - Seed records include:
default/sample.featuredefault/app.defaultsfor future default parameters.default/token.rotation.intervalMsdefault/vault.agent.auth.modedefault/vault.agent.tokenFilePathdefault/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
- Install dependencies.
- Copy .env.example to .env.
- Start local services with docker compose up -d.
- Resolve the managed unseal key:
npm run vault:unseal-key -- --json. - 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>
- Seed a test secret in Vault.
- Start the MCP server with npm start.
- 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
/mcprequest requiresAuthorization: Bearer <token>. - For
MCP_HTTP_AUTH_MODE=token, setMCP_HTTP_TOKEN_SOURCE=vaultto 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
authorizationKeywhenMCP_ADMIN_AUTH_KEYis set. - Request limits are enforced with:
MCP_HTTP_MAX_BODY_BYTESMCP_HTTP_RATE_LIMIT_WINDOW_MSMCP_HTTP_RATE_LIMIT_MAX_REQUESTS
- Optional network restrictions:
MCP_HTTP_ALLOWED_ORIGINSMCP_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_IDdefaults todefault.- 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_SCOPESandMCP_HTTP_VAULT_TOKEN_REQUIRED_AUDIENCEenforce 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->tokenLookupSelftoken_renew_self->tokenRenewSelftoken_create->tokenCreatetoken_revoke->tokenRevoketoken_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 filelistener: use Vault Agent listener endpointboth: 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_readwhen 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_KEYMCP_VAULT_AGENT_TOKEN_FILE_PATH_CONFIG_KEYMCP_VAULT_AGENT_LISTENER_ADDR_CONFIG_KEY
- Recommended values in Postgres:
vault.agent.auth.modevault.agent.tokenFilePathvault.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(defaulttoken.rotation.intervalMs)
Effective value resolution is:
- User-scoped Postgres config (
userId+ key) - Default user Postgres config (
default+ key) - 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=falsein 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:
- vault-production/config/vault.hcl: Raft-backed Vault server configuration.
- vault-production/docker-compose.vault-prod.yml: Compose definition for Vault in server mode (non-dev).
- vault-production/scripts/convert-dev-to-prod.sh: Script to export dev KV data, start Raft Vault, initialize/unseal, and import secrets.
- vault-production/scripts/bootstrap-post-conversion.sh: Script to enable audit, write policy, configure AppRole, and emit service credentials.
- scripts/vault-unseal-key.js: Script to resolve unseal key from
VAULT_UNSEAL_KEYorsrc/config/vault.unseal.key.json. - vault-production/README.md: Detailed migration notes and options.
Managed unseal key flow:
VAULT_UNSEAL_KEYis optional and can be injected at runtime.- If
VAULT_UNSEAL_KEYis not set,npm run vault:unseal-keyreadssrc/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-initservice 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:
- agent/README.md: Overview of agent assets.
- agent/playbooks/service-onboarding.md: Step-by-step onboarding checklist.
- agent/templates/service-spec.md: Request template for describing new service integrations.
Workspace custom agent:
Use this custom agent when you want GitHub Copilot in VS Code to:
- Configure new service adapters under
src/services. - Register matching MCP tools in
src/mcp/server.js. - Update env validation in
src/config/env.js. - Preserve authorization and redaction behavior.
- 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
- Add domain services under src/services.
- Register new tools in src/mcp/server.js.
- Add corresponding tests under tests.
- Keep mutating tools behind authorization checks.
- 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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。