feat: Add relative path and multi-platform support for Craft Agents Sources - #918
Open
rayliverified wants to merge 4 commits into
Open
feat: Add relative path and multi-platform support for Craft Agents Sources#918rayliverified wants to merge 4 commits into
rayliverified wants to merge 4 commits into
Conversation
Extend expandPath() with optional extraVars parameter supporting , , and variables. Apply expansion to MCP stdio fields (command, args, env values) at load time in loadSourceConfig(). Existing configs with absolute paths work unchanged — expansion is idempotent and variables are purely opt-in. Builds on PR craft-ai-agents#851 (CONFIG_DIR centralization) which makes reliable as a single source of truth. Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Three bugs found during live testing: 1. Non-path env values treated as relative paths: expandPath() coerced bare strings like 'true' and 'dart' to absolute paths by resolving against cwd. Fixed by adding expandVars() which does variable substitution ONLY, leaving non-variable strings unchanged. 2. Expanded values persisted to config.json: Expansion at load time mutated the in-memory config object, which source_test then wrote back. Moved MCP field expansion from loadSourceConfig() to buildMcpServer() (runtime-only). 3. Bare command names treated as relative paths: 'dart' became '/path/to/repo/dart'. Now uses expandVars() for command/args/env which leaves bare names as-is. expandPath() now delegates to expandVars() for the variable substitution step, then adds relative→absolute resolution on top. expandPath() is still used for local source paths only. Co-Authored-By: Craft Agent <agents-noreply@craft.do>
Adds a `platform` field to McpSourceConfig that allows overriding
command, args, and env per OS (win32, darwin, linux). This makes
source configs portable across different operating systems.
Override rules:
- command: replaces the default
- args: replaces the default entirely
- env: merges on top of the default (not replaced)
Resolution happens at runtime via resolveStdioConfig(), which also
handles path variable expansion. The original config.json is never
mutated — overrides and variable expansion are runtime-only.
Example:
{
command: npx,
platform: { win32: { command: npx.cmd } }
}
Co-Authored-By: Craft Agent <agents-noreply@craft.do>
MCP stdio subprocesses now spawn with cwd set to the source's own
folder. This means relative paths in args (e.g. server/bin/main.dart)
resolve naturally without requiring ${SOURCE_DIR}/ prefixes.
Before:
args: [${SOURCE_DIR}/server/bin/main.dart]
After (both work):
args: [server/bin/main.dart]
Applies to:
- server-builder.ts: runtime server startup (buildMcpServer)
- source-test.ts: connection validation (validateStdioMcpConnection)
- validation.ts: StdioClientTransport now passes cwd
Co-Authored-By: Craft Agent <agents-noreply@craft.do>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds path variable expansion and platform overrides for MCP stdio source configs, making them portable across machines and operating systems.
Builds on #851 (
CONFIG_DIRcentralization).Problem
Source configs contain absolute paths and platform-specific commands that break when shared across machines or operating systems:
{ "command": "/Users/ray/.crawl4ai/venv/bin/python", "args": ["/Users/ray/projects/crawl4ai/server/bin/main.py"], "env": { "PYTHONPATH": "/Users/ray/.crawl4ai/crawl4ai-mcp-server" } }After — cross-platform portable config:
{ "command": "${HOME}/.crawl4ai/venv/bin/python", "args": ["${SOURCE_DIR}/server/bin/main.py"], "env": { "PYTHONPATH": "${HOME}/.crawl4ai/crawl4ai-mcp-server" }, "platform": { "win32": { "command": "C:\\Users\\dev\\.crawl4ai\\venv\\Scripts\\python.exe" } } }This config works on macOS, Linux, and Windows without modification.
Path Variables API
Optional
${...}references expanded at runtime incommand,args, andenvvalues.Supported Variables
${HOME}${HOME}/.local/bin/node${CRAFT_CONFIG_DIR}~/.craft-agent)${CRAFT_CONFIG_DIR}/sources/my-mcp/data${WORKSPACE}${WORKSPACE}/shared-tools/server.js${SOURCE_DIR}${SOURCE_DIR}/server/index.jsBehavior
${...}syntax is present — bare strings like"true","dart","production"pass through unchangedconfig.jsonis never modifiedExample
{ "mcp": { "transport": "stdio", "command": "${HOME}/.venv/bin/python", "args": ["${SOURCE_DIR}/server/main.py"], "env": { "PYTHONPATH": "${HOME}/.local/lib", "DEBUG": "true" } } }Platform Overrides API
Optional per-OS overrides for
command,args, andenv. Keys arewin32,darwin, andlinux(matching Node.jsprocess.platform).Config Format
{ "mcp": { "transport": "stdio", "command": "python", "args": ["${SOURCE_DIR}/server/main.py"], "env": { "LOG_LEVEL": "info" }, "platform": { "win32": { "command": "string", "args": ["string"], "env": { "KEY": "value" } }, "darwin": { "command": "string", "args": ["string"], "env": { "KEY": "value" } }, "linux": { "command": "string", "args": ["string"], "env": { "KEY": "value" } } } } }Override Rules
commandargsenvIf a platform key is absent or a field is omitted from the override, the default value is used.
Example
{ "command": "${HOME}/.crawl4ai/venv/bin/python", "args": ["${SOURCE_DIR}/server/main.py"], "env": { "PYTHONPATH": "${HOME}/.crawl4ai/crawl4ai-mcp-server", "LOG_LEVEL": "info" }, "platform": { "win32": { "command": "C:\\Users\\dev\\.crawl4ai\\venv\\Scripts\\python.exe" }, "darwin": { "args": ["${SOURCE_DIR}/server/main.py", "--verbose"], "env": { "LOG_LEVEL": "debug" } } } }Resolved on macOS (
darwin):command:"~/.crawl4ai/venv/bin/python"(default — no override)args:[".../server/main.py", "--verbose"](override replaces)env:{ PYTHONPATH: "...", LOG_LEVEL: "debug" }(override merges)Resolved on Linux:
command:"~/.crawl4ai/venv/bin/python"(default)args:[".../server/main.py"](default)env:{ PYTHONPATH: "...", LOG_LEVEL: "info" }(default)Resolved on Windows (
win32):command:"C:\\Users\\dev\\.crawl4ai\\venv\\Scripts\\python.exe"(override replaces)args:["...\\server\\main.py"](default)env:{ PYTHONPATH: "...", LOG_LEVEL: "info" }(default)Test Results
${HOME}in command/args/env${SOURCE_DIR}in args${CRAFT_CONFIG_DIR}in envsource_test"true") not coercedcommandreplacesargsreplacesenvmergesChanges
packages/shared/src/utils/paths.tsexpandVars(),resolveStdioConfig(),PathVarsinterfacepackages/shared/src/sources/types.tsplatformfield +McpPlatform/McpPlatformOverridetypespackages/shared/src/sources/server-builder.tsresolveStdioConfig()at build timepackages/session-tools-core/src/handlers/source-test.tsresolveStdioConfig()for connection testingapps/electron/resources/docs/sources.mdValidation
cd packages/shared && bun run tsc --noEmitcd packages/shared && bun test— 2891 pass, 0 failCo-Authored-By: Craft Agent agents-noreply@craft.do