Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-get-env-key-parameter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@modelcontextprotocol/server-everything": patch
---

Fix security issue in get-env tool: require a specific key parameter instead of returning the entire process.env object unfiltered. The tool now accepts a required `key` argument and returns only the value of that specific environment variable, preventing accidental exposure of sensitive data such as API keys, tokens, and credentials.
18 changes: 10 additions & 8 deletions src/everything/__tests__/tools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,31 +151,33 @@ describe('Tools', () => {
});

describe('get-env', () => {
it('should return all environment variables as JSON', async () => {
it('should return the value of a specific environment variable', async () => {
const { mockServer, handlers } = createMockServer();
registerGetEnvTool(mockServer);

const handler = handlers.get('get-env')!;
process.env.TEST_VAR_EVERYTHING = 'test_value';
const result = await handler({});
const result = await handler({ key: 'TEST_VAR_EVERYTHING' });

expect(result.content).toHaveLength(1);
expect(result.content[0].type).toBe('text');

const envJson = JSON.parse(result.content[0].text);
expect(envJson.TEST_VAR_EVERYTHING).toBe('test_value');
expect(result.content[0].text).toBe('test_value');

delete process.env.TEST_VAR_EVERYTHING;
});

it('should return valid JSON', async () => {
it('should return a message when the key does not exist', async () => {
const { mockServer, handlers } = createMockServer();
registerGetEnvTool(mockServer);

const handler = handlers.get('get-env')!;
const result = await handler({});
const result = await handler({ key: 'NONEXISTENT_VAR_12345' });

expect(() => JSON.parse(result.content[0].text)).not.toThrow();
expect(result.content).toHaveLength(1);
expect(result.content[0].type).toBe('text');
expect(result.content[0].text).toBe(
'Environment variable "NONEXISTENT_VAR_12345" is not set.'
);
});
});

Expand Down
33 changes: 22 additions & 11 deletions src/everything/tools/get-env.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// Tool input schema
const GetEnvSchema = z.object({
key: z.string().describe("Specific environment variable name"),
});

// Tool configuration
const name = "get-env";
const config = {
title: "Print Environment Tool",
title: "Get Environment Variable",
description:
"Returns all environment variables, helpful for debugging MCP server configuration",
inputSchema: {},
'Returns the value of a specific environment variable. Pass the variable name via the "key" argument.',
inputSchema: GetEnvSchema,
};

/**
* Registers the 'get-env' tool.
*
* The registered tool Retrieves and returns the environment variables
* of the current process as a JSON-formatted string encapsulated in a text response.
* The registered tool retrieves and returns the value of a specific
* environment variable specified by the 'key' argument.
*
* @param {McpServer} server - The McpServer instance where the tool will be registered.
* @returns {void}
*/
export const registerGetEnvTool = (server: McpServer) => {
server.registerTool(name, config, async (args): Promise<CallToolResult> => {
const { key } = GetEnvSchema.parse(args);
const value = process.env[key];

// Return a clear message if the key doesn't exist rather than exposing the full env
if (value === undefined) {
return {
content: [{ type: "text", text: `Environment variable "${key}" is not set.` }],
};
}

return {
content: [
{
type: "text",
text: JSON.stringify(process.env, null, 2),
},
],
content: [{ type: "text", text: value }],
};
});
};
Loading