Skip to content

Commit 2f39582

Browse files
committed
MCP env variables
1 parent 0971627 commit 2f39582

File tree

6 files changed

+138
-2
lines changed

6 files changed

+138
-2
lines changed

.agents/notion-agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const definition: AgentDefinition = {
2424
command: 'npx',
2525
args: ['-y', '@notionhq/notion-mcp-server'],
2626
env: {
27-
NOTION_TOKEN: 'ntn_***',
27+
NOTION_TOKEN: '$NOTION_TOKEN',
2828
},
2929
},
3030
},

.agents/notion-researcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const definition: AgentDefinition = {
2828
command: 'npx',
2929
args: ['-y', '@notionhq/notion-mcp-server'],
3030
env: {
31-
NOTION_TOKEN: 'ntn_***',
31+
NOTION_TOKEN: '$NOTION_TOKEN',
3232
},
3333
},
3434
},

.agents/types/util-types.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,28 @@ export type Message =
135135

136136
// ===== MCP Server Types =====
137137

138+
/**
139+
* MCP server configuration for stdio-based servers.
140+
*
141+
* Environment variables in `env` can be:
142+
* - A plain string value (hardcoded, e.g., `'production'`)
143+
* - A `$VAR_NAME` reference to read from local environment (e.g., `'$NOTION_TOKEN'`)
144+
*
145+
* The `$VAR_NAME` syntax reads from `process.env.VAR_NAME` at agent load time.
146+
* This keeps secrets out of your agent definitions - store them in `.env.local` instead.
147+
*
148+
* @example
149+
* ```typescript
150+
* env: {
151+
* // Read NOTION_TOKEN from local .env file
152+
* NOTION_TOKEN: '$NOTION_TOKEN',
153+
* // Read MY_API_KEY from local env, pass as API_KEY to MCP server
154+
* API_KEY: '$MY_API_KEY',
155+
* // Hardcoded value (non-secret)
156+
* NODE_ENV: 'production',
157+
* }
158+
* ```
159+
*/
138160
export type MCPConfig =
139161
| {
140162
type?: 'stdio'

agents/types/util-types.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,28 @@ export type Message =
135135

136136
// ===== MCP Server Types =====
137137

138+
/**
139+
* MCP server configuration for stdio-based servers.
140+
*
141+
* Environment variables in `env` can be:
142+
* - A plain string value (hardcoded, e.g., `'production'`)
143+
* - A `$VAR_NAME` reference to read from local environment (e.g., `'$NOTION_TOKEN'`)
144+
*
145+
* The `$VAR_NAME` syntax reads from `process.env.VAR_NAME` at agent load time.
146+
* This keeps secrets out of your agent definitions - store them in `.env.local` instead.
147+
*
148+
* @example
149+
* ```typescript
150+
* env: {
151+
* // Read NOTION_TOKEN from local .env file
152+
* NOTION_TOKEN: '$NOTION_TOKEN',
153+
* // Read MY_API_KEY from local env, pass as API_KEY to MCP server
154+
* API_KEY: '$MY_API_KEY',
155+
* // Hardcoded value (non-secret)
156+
* NODE_ENV: 'production',
157+
* }
158+
* ```
159+
*/
138160
export type MCPConfig =
139161
| {
140162
type?: 'stdio'

common/src/templates/initial-agents-dir/types/util-types.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,28 @@ export type Message =
128128

129129
// ===== MCP Server Types =====
130130

131+
/**
132+
* MCP server configuration for stdio-based servers.
133+
*
134+
* Environment variables in `env` can be:
135+
* - A plain string value (hardcoded, e.g., `'production'`)
136+
* - A `$VAR_NAME` reference to read from local environment (e.g., `'$NOTION_TOKEN'`)
137+
*
138+
* The `$VAR_NAME` syntax reads from `process.env.VAR_NAME` at agent load time.
139+
* This keeps secrets out of your agent definitions - store them in `.env.local` instead.
140+
*
141+
* @example
142+
* ```typescript
143+
* env: {
144+
* // Read NOTION_TOKEN from local .env file
145+
* NOTION_TOKEN: '$NOTION_TOKEN',
146+
* // Read MY_API_KEY from local env, pass as API_KEY to MCP server
147+
* API_KEY: '$MY_API_KEY',
148+
* // Hardcoded value (non-secret)
149+
* NODE_ENV: 'production',
150+
* }
151+
* ```
152+
*/
131153
export type MCPConfig =
132154
| {
133155
type?: 'stdio'

sdk/src/agents/load-agents.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,64 @@ export type LoadedAgentDefinition = AgentDefinition & {
2222
*/
2323
export type LoadedAgents = Record<string, LoadedAgentDefinition>
2424

25+
/**
26+
* Resolves environment variable references in MCP server configs.
27+
* Values starting with `$` are treated as env var references (e.g., `'$NOTION_TOKEN'`).
28+
*
29+
* @param env - The env object from MCP config with possible $VAR_NAME references
30+
* @param agentId - The agent ID for error messages
31+
* @param mcpServerName - The MCP server name for error messages
32+
* @returns Resolved env object with all $VAR_NAME values replaced with actual values
33+
* @throws Error if a referenced environment variable is missing
34+
*/
35+
export function resolveMcpEnv(
36+
env: Record<string, string> | undefined,
37+
agentId: string,
38+
mcpServerName: string,
39+
): Record<string, string> {
40+
if (!env) return {}
41+
42+
const resolved: Record<string, string> = {}
43+
44+
for (const [key, value] of Object.entries(env)) {
45+
if (value.startsWith('$')) {
46+
// $VAR_NAME reference - resolve from process.env
47+
const envVarName = value.slice(1) // Remove the leading $
48+
const envValue = process.env[envVarName]
49+
50+
if (envValue === undefined) {
51+
throw new Error(
52+
`Missing environment variable '${envVarName}' required by agent '${agentId}' in mcpServers.${mcpServerName}.env.${key}`,
53+
)
54+
}
55+
56+
resolved[key] = envValue
57+
} else {
58+
// Plain string value - use as-is
59+
resolved[key] = value
60+
}
61+
}
62+
63+
return resolved
64+
}
65+
66+
/**
67+
* Resolves all MCP server env references in an agent definition.
68+
* Mutates the mcpServers object to replace $VAR_NAME references with resolved values.
69+
*
70+
* @param agent - The agent definition to process
71+
* @throws Error if any referenced environment variable is missing
72+
*/
73+
export function resolveAgentMcpEnv(agent: AgentDefinition): void {
74+
if (!agent.mcpServers) return
75+
76+
for (const [serverName, config] of Object.entries(agent.mcpServers)) {
77+
if ('command' in config && config.env) {
78+
config.env = resolveMcpEnv(config.env, agent.id, serverName)
79+
}
80+
}
81+
}
82+
2583
/**
2684
* Validation error for an agent that failed validation.
2785
*/
@@ -183,6 +241,18 @@ export async function loadLocalAgents({
183241
agentDefinition.handleSteps.toString()
184242
}
185243

244+
// Resolve $env references in MCP server configs
245+
try {
246+
resolveAgentMcpEnv(processedAgentDefinition)
247+
} catch (error) {
248+
if (verbose) {
249+
console.error(
250+
error instanceof Error ? error.message : String(error),
251+
)
252+
}
253+
continue
254+
}
255+
186256
agents[processedAgentDefinition.id] = processedAgentDefinition
187257
} catch (error) {
188258
if (verbose) {

0 commit comments

Comments
 (0)