Skip to content

Commit c05cb8d

Browse files
Merge pull request #310 from basementstudio/canary
v0.5.3
2 parents 8fdeb9b + 997ed21 commit c05cb8d

File tree

4 files changed

+74
-8
lines changed

4 files changed

+74
-8
lines changed

packages/cli/src/utils/client-definitions.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,14 @@ function normalizeClientDefinitions(
8787
return undefined;
8888
}
8989

90+
function isValidHeader(header: unknown): boolean {
91+
if (!header || typeof header !== "object") return false;
92+
const h = header as Record<string, unknown>;
93+
if (typeof h.name !== "string") return false;
94+
// Must have either 'value' (static) or 'env' (environment variable reference)
95+
return typeof h.value === "string" || typeof h.env === "string";
96+
}
97+
9098
function createClientDefinition(
9199
entry: unknown,
92100
fallbackName?: string
@@ -112,10 +120,12 @@ function createClientDefinition(
112120
"url" in entry && typeof (entry as any).url === "string"
113121
? (entry as any).url
114122
: undefined;
115-
const headersProp =
116-
"headers" in entry && Array.isArray((entry as any).headers)
117-
? (entry as any).headers
118-
: undefined;
123+
124+
let headersProp: CustomHeaders | undefined;
125+
if ("headers" in entry && Array.isArray((entry as any).headers)) {
126+
const rawHeaders = (entry as any).headers;
127+
headersProp = rawHeaders.filter(isValidHeader) as CustomHeaders;
128+
}
119129

120130
if (!nameProp || !urlProp) {
121131
return undefined;

packages/cli/src/utils/templates.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@ async function ${identifier}(client: HttpClient${
170170
? JSON.stringify(headers, null, 2)
171171
: undefined;
172172

173+
// Warn if any headers contain literal values (potential secrets)
174+
if (headers && headers.length > 0) {
175+
const staticHeaders = headers.filter(
176+
(h) => "value" in h && h.value && !h.value.startsWith("$")
177+
);
178+
if (staticHeaders.length > 0) {
179+
console.warn(
180+
`Warning: Headers with static values detected. Consider using { env: "ENV_VAR_NAME" } for sensitive values like API keys.`
181+
);
182+
}
183+
}
184+
173185
const headersConstant = headersLiteral
174186
? `\nconst DEFAULT_HEADERS: CustomHeaders = ${headersLiteral};\n`
175187
: "";

packages/xmcp/src/client/headers.ts

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,60 @@
1-
export interface CustomHeader {
1+
/**
2+
* A header with a static value (use only for non-sensitive values)
3+
*/
4+
export interface StaticHeader {
25
name: string;
36
value: string;
47
}
58

9+
/**
10+
* A header that reads its value from an environment variable at runtime.
11+
* Use this for sensitive values like API keys and authorization tokens.
12+
*/
13+
export interface EnvHeader {
14+
name: string;
15+
/** The name of the environment variable to read at runtime */
16+
env: string;
17+
}
18+
19+
export type CustomHeader = StaticHeader | EnvHeader;
20+
621
export type CustomHeaders = CustomHeader[];
722

8-
export const createEmptyHeader = (): CustomHeader => ({
23+
export const createEmptyHeader = (): StaticHeader => ({
924
name: "",
1025
value: "",
1126
});
1227

28+
/**
29+
* Check if a header uses an environment variable reference
30+
*/
31+
export const isEnvHeader = (header: CustomHeader): header is EnvHeader => {
32+
return "env" in header && typeof header.env === "string";
33+
};
34+
35+
/**
36+
* Converts CustomHeaders to a Record, resolving environment variables at runtime.
37+
* Throws if a required env var is not set.
38+
*/
1339
export const headersToRecord = (
1440
headers: CustomHeaders
1541
): Record<string, string> => {
1642
const record: Record<string, string> = {};
1743

1844
headers.forEach((header) => {
19-
record[header.name.trim()] = header.value.trim();
45+
const name = header.name.trim();
46+
47+
if (isEnvHeader(header)) {
48+
const envValue = process.env[header.env];
49+
if (envValue === undefined) {
50+
throw new Error(
51+
`Environment variable "${header.env}" is not set (required for header "${name}")`
52+
);
53+
}
54+
record[name] = envValue;
55+
} else {
56+
record[name] = header.value.trim();
57+
}
2058
});
2159

2260
return record;

packages/xmcp/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,10 @@ export { completable } from "@modelcontextprotocol/sdk/server/completable";
2424

2525
export { createHTTPClient } from "./client";
2626
export type { HttpClient } from "./client/types";
27-
export type { CustomHeaders } from "./client/headers";
27+
export type {
28+
CustomHeaders,
29+
CustomHeader,
30+
StaticHeader,
31+
EnvHeader,
32+
} from "./client/headers";
33+
export { isEnvHeader, headersToRecord } from "./client/headers";

0 commit comments

Comments
 (0)