UNPKG

@modelcontextprotocol/server-everything

Version:

MCP server that exercises all the features of the MCP protocol

91 lines (90 loc) • 3.26 kB
import { TOOL_ICONS } from "../server/icons.js"; /** The placeholder that replaces a masked value. */ export const REDACTED = "<redacted>"; /** The opt-in that returns every value unmasked. */ export const INCLUDE_SECRETS_VAR = "GET_ENV_INCLUDE_SECRETS"; /** Name segments that mark a variable as holding a credential. */ const CREDENTIAL_SEGMENTS = new Set([ "KEY", "TOKEN", "SECRET", "PASSWORD", "PASSWD", "CREDENTIAL", "AUTH", "COOKIE", ]); /** Values of `GET_ENV_INCLUDE_SECRETS` that turn the opt-in on. */ const TRUTHY = new Set(["1", "true", "yes", "on"]); // Tool configuration const name = "get-env"; const config = { title: "Print Environment Tool", description: "Returns all environment variables, helpful for debugging MCP server configuration. " + `Values of credential-like names (a KEY, TOKEN, SECRET, PASSWORD, PASSWD, CREDENTIAL, AUTH or COOKIE name segment) are masked as "${REDACTED}" unless ${INCLUDE_SECRETS_VAR} is set to 1 or true in the server's environment.`, inputSchema: {}, icons: TOOL_ICONS, annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, }, }; /** * Whether an environment variable name looks like it holds a credential: * one of its underscore-separated segments, compared case-insensitively, is * a credential word. * * @param {string} key - The environment variable name. * @returns {boolean} True when the value should be masked. */ export const isCredentialName = (key) => key .toUpperCase() .split("_") .some((segment) => CREDENTIAL_SEGMENTS.has(segment)); /** * Whether `GET_ENV_INCLUDE_SECRETS` is set to a truthy value * (`1`, `true`, `yes` or `on`, case-insensitively). * * @param {NodeJS.ProcessEnv} env - The environment to read the opt-in from. * @returns {boolean} True when values should be returned unmasked. */ export const includeSecrets = (env) => TRUTHY.has((env[INCLUDE_SECRETS_VAR] ?? "").trim().toLowerCase()); /** * The environment as the tool returns it: every name, with the value of each * credential-like name masked unless the opt-in is set. * * @param {NodeJS.ProcessEnv} env - The environment to render. * @returns {Record<string, string | undefined>} The (possibly masked) copy. */ export const maskEnv = (env) => { if (includeSecrets(env)) return { ...env }; return Object.fromEntries(Object.entries(env).map(([key, value]) => [ key, isCredentialName(key) ? REDACTED : value, ])); }; /** * Registers the 'get-env' tool. * * The registered tool returns the environment variables of the current * process as a JSON-formatted string in a text response, with the values of * credential-like names masked unless `GET_ENV_INCLUDE_SECRETS` is set. * * @param {McpServer} server - The McpServer instance where the tool will be registered. * @returns {void} */ export const registerGetEnvTool = (server) => { server.registerTool(name, config, async () => { return { content: [ { type: "text", text: JSON.stringify(maskEnv(process.env), null, 2), }, ], }; }); };