Skip to content

Commit c514b10

Browse files
authored
feat(run): allow empty strings for required config fields during runtime (#110)
1 parent c8b857a commit c514b10

4 files changed

Lines changed: 74 additions & 7 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [1.1.66] - 2025-04-03
8+
9+
### Changed
10+
- Modified runtime config validation to allow empty strings for required fields
11+
- Added separate config validation for run vs install commands
12+
- Improved error handling for missing required fields during runtime
13+
714
## [1.1.65] - 2025-04-02
815

916
### Added

‎package.json‎

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,7 @@
4949
"tsx": "^4.19.2",
5050
"typescript": "^5.0.0"
5151
},
52-
"files": [
53-
"dist",
54-
"README.md",
55-
"package.json"
56-
],
52+
"files": ["dist", "README.md", "package.json"],
5753
"exports": {
5854
".": {
5955
"import": "./dist/index.js"

‎src/commands/run/index.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
initializeSettings,
66
} from "../../smithery-config.js"
77
import type { RegistryServer, ServerConfig } from "../../types/registry.js"
8-
import { chooseConnection } from "../../utils/config.js"
8+
import { chooseConnection, formatRunConfigValues } from "../../utils/config.js"
99
import { createStdioRunner as startSTDIOrunner } from "./stdio-runner.js"
1010
import { createWSRunner as startWSRunner } from "./ws-runner.js"
1111

@@ -40,7 +40,12 @@ export async function run(
4040
try {
4141
const result = await fetchConfigWithApiKey(qualifiedName, apiKey)
4242
resolvedServer = result.server
43-
finalConfig = { ...result.config, ...config } // Merge configs, with local config taking precedence
43+
// Merge configs with proper schema validation
44+
const connection = chooseConnection(result.server)
45+
finalConfig = await formatRunConfigValues(connection, {
46+
...result.config,
47+
...config,
48+
})
4449
console.error("[Runner] Using saved configuration")
4550
} catch (error) {
4651
console.error("[Runner] Failed to fetch config with API key:", error)
@@ -58,6 +63,12 @@ export async function run(
5863
throw new Error(`Could not resolve server: ${qualifiedName}`)
5964
}
6065

66+
// Format final config with schema validation if not already done
67+
if (!apiKey) {
68+
const connection = chooseConnection(resolvedServer)
69+
finalConfig = await formatRunConfigValues(connection, finalConfig)
70+
}
71+
6172
console.error("[Runner] Connecting to server:", {
6273
id: resolvedServer.qualifiedName,
6374
connectionTypes: resolvedServer.connections.map((c) => c.type),

‎src/utils/config.ts‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,3 +445,56 @@ export function formatServerConfig(
445445
args: npxArgs,
446446
}
447447
}
448+
449+
/**
450+
* Formats and validates configuration values for the run command.
451+
* This is a special case that allows empty strings for required fields.
452+
*
453+
* @param connection - Server connection details containing the config schema
454+
* @param configValues - Optional existing configuration values to format
455+
* @returns Formatted configuration values with proper types according to schema
456+
*/
457+
export async function formatRunConfigValues(
458+
connection: ConnectionDetails,
459+
configValues?: ServerConfig,
460+
): Promise<ServerConfig> {
461+
const formattedValues: ServerConfig = {}
462+
463+
if (!connection.configSchema?.properties) {
464+
return configValues || {}
465+
}
466+
467+
const required = new Set<string>(connection.configSchema.required || [])
468+
469+
// First pass: collect all values and track missing required fields
470+
for (const [key, prop] of Object.entries(
471+
connection.configSchema.properties,
472+
)) {
473+
const schemaProp = prop as { type?: string; default?: unknown }
474+
const value = configValues?.[key]
475+
476+
try {
477+
let finalValue: unknown
478+
if (value !== undefined) {
479+
finalValue = value
480+
} else if (!required.has(key)) {
481+
finalValue = schemaProp.default
482+
} else {
483+
finalValue = "" // Use empty string for required fields that are missing
484+
}
485+
486+
if (finalValue === undefined) {
487+
// Use empty string for optional values without defaults
488+
formattedValues[key] = ""
489+
continue
490+
}
491+
492+
formattedValues[key] = convertValueToType(finalValue, schemaProp.type)
493+
} catch (error) {
494+
// For any errors, use empty string for required fields and null for optional ones
495+
formattedValues[key] = required.has(key) ? "" : null
496+
}
497+
}
498+
499+
return formattedValues
500+
}

0 commit comments

Comments
 (0)