Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@missionsquad/mcp-api",
"version": "1.10.2",
"version": "1.10.3",
"description": "MCP Servers exposed via HTTP API",
"main": "dist/index.js",
"repository": "missionsquad/mcp-api",
Expand Down
29 changes: 17 additions & 12 deletions src/services/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -883,15 +883,21 @@ export class MCPService implements Resource {
// Get server's declared secret names from metadata
const serverSecretNames = server.secretNames ?? (server.secretName ? [server.secretName] : [])

// Extract secrets belonging to this server: keys stored as "${serverName}.${secretName}"
const prefix = `${serverName}.`
const serverSecrets: Record<string, string> = {}
for (const [key, value] of Object.entries(allSecrets)) {
if (key.startsWith(prefix)) {
serverSecrets[key.slice(prefix.length)] = value
}
}

if (serverSecretNames.length > 0) {
// Inject ONLY secrets declared by this server
// Inject only the declared secret names
const scopedSecrets: Record<string, string> = {}
for (const name of serverSecretNames) {
const prefixedName = `${serverName}.${name}`
if (allSecrets[name] !== undefined) {
scopedSecrets[name] = allSecrets[name]
} else if (allSecrets[prefixedName] !== undefined) {
scopedSecrets[name] = allSecrets[prefixedName]
if (serverSecrets[name] !== undefined) {
scopedSecrets[name] = serverSecrets[name]
}
}
args = { ...args, ...scopedSecrets }
Expand All @@ -900,17 +906,16 @@ export class MCPService implements Resource {
msg: `Scoped secrets applied to tool call - ${serverName}:${methodName} - ${Object.keys(scopedSecrets).join(', ')}`
})
} else {
// Backward compatibility: if server has no declared secretNames,
// fall back to injecting all secrets (preserves existing behavior
// for servers not yet migrated to secretNames metadata).
args = { ...args, ...allSecrets }
// No declared secretNames — inject all secrets belonging to this server
args = { ...args, ...serverSecrets }
log({
level: 'warn',
msg: `Server ${serverName} has no declared secretNames — injecting all secrets (legacy behavior)`
level: 'info',
msg: `Server secrets applied to tool call - ${serverName}:${methodName} - ${Object.keys(serverSecrets).join(', ')}`
})
}
}
log({ level: 'info', msg: `Calling tool - ${serverName}:${methodName}` })
log({ level: 'debug', msg: `callTool arguments keys: ${Object.keys(args).join(', ')}` })
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This DEBUG log prints all argument keys after secrets are injected, which can reveal secret names (and in the legacy path, potentially the full set of stored secret keys). Consider filtering/removing known secret-related keys (or logging only non-secret keys / a count) to avoid leaking sensitive metadata when DEBUG is enabled.

Suggested change
log({ level: 'debug', msg: `callTool arguments keys: ${Object.keys(args).join(', ')}` })
const argKeys = Object.keys(args)
if (allSecrets != null) {
const secretKeys = new Set(Object.keys(allSecrets))
const nonSecretKeys = argKeys.filter(key => !secretKeys.has(key))
const secretKeyCount = argKeys.length - nonSecretKeys.length
log({
level: 'debug',
msg: `callTool arguments: totalKeys=${argKeys.length}, nonSecretKeys=[${nonSecretKeys.join(
', '
)}], secretKeyCount=${secretKeyCount}`
})
} else {
log({
level: 'debug',
msg: `callTool arguments keys (no secrets injected): ${argKeys.join(', ')}`
})
}

Copilot uses AI. Check for mistakes.
const requestOptions: RequestOptions = {}
Comment on lines 917 to 919
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object.keys(args) will throw if args is null/undefined. Since request bodies aren’t runtime-validated, args can be missing even though it’s typed as a record. Consider defaulting args to {} (or guarding with args && typeof args === 'object') before calling Object.keys so DEBUG logging can’t crash tool calls.

Copilot uses AI. Check for mistakes.
if (server.startupTimeout) {
requestOptions.timeout = server.startupTimeout
Expand Down