Skip to content

fix(setup): resolve OpenCode MCP path in generated config#114

Merged
Alan-TheGentleman merged 1 commit intomainfrom
fix/opencode-mcp-absolute-path
Mar 21, 2026
Merged

fix(setup): resolve OpenCode MCP path in generated config#114
Alan-TheGentleman merged 1 commit intomainfrom
fix/opencode-mcp-absolute-path

Conversation

@Alan-TheGentleman
Copy link
Collaborator

Linked Issue

Closes #112

Summary

  • use the resolved engram command when generating OpenCode MCP config instead of hardcoded engram
  • improve the fallback warning so manual config snippets also use the resolved command
  • add focused tests for Windows absolute paths and Unix fallback behavior

Problem

Headless OpenCode setups can fail to spawn engram mcp when the generated MCP config relies on PATH resolution. New evidence on #112 showed stdio itself is still viable in headless mode if the config points to the correct binary.

Fix

This keeps stdio as the default transport and improves setup ergonomics by writing the resolved command into the OpenCode MCP config.

Test Plan

  • go test ./internal/setup

Copilot AI review requested due to automatic review settings March 21, 2026 22:55
@Alan-TheGentleman Alan-TheGentleman added the type:bug Bug fix label Mar 21, 2026
@Alan-TheGentleman Alan-TheGentleman merged commit 2c8349e into main Mar 21, 2026
10 of 13 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates OpenCode MCP config generation to write a resolved engram command (not a hardcoded string) and adds regression tests for OS-specific behavior, addressing headless OpenCode spawn failures discussed in #112.

Changes:

  • Use resolveEngramCommand() when injecting OpenCode MCP config so the generated command points at the appropriate binary.
  • Improve the manual-config warning snippet to use the resolved command.
  • Add tests covering Windows absolute-path behavior and Unix fallback behavior for OpenCode MCP config + warnings.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
internal/setup/setup.go Switch OpenCode MCP injection + warning snippet to use resolveEngramCommand() and update package/docs comments.
internal/setup/setup_test.go Add focused tests validating command resolution in generated OpenCode config and in fallback warning output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +6
// path (absolute on Windows, bare "engram" on Unix) so the MCP subprocess
// never requires PATH resolution in headless/systemd environments.
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

Package doc comment says the OpenCode MCP config uses a resolved binary path so the subprocess "never requires PATH resolution in headless/systemd environments", but on Unix resolveEngramCommand() intentionally returns the bare "engram" name (PATH-based). Please adjust the comment to reflect the actual behavior (or change the implementation if the intent is truly to avoid PATH on Unix too).

Suggested change
// path (absolute on Windows, bare "engram" on Unix) so the MCP subprocess
// never requires PATH resolution in headless/systemd environments.
// path (absolute on Windows to avoid PATH resolution issues; bare "engram"
// on Unix, where the MCP subprocess relies on the environment's PATH).

Copilot uses AI. Check for mistakes.
fmt.Fprintf(os.Stderr, "warning: could not auto-register MCP server in opencode.json: %v\n", err)
fmt.Fprintf(os.Stderr, " Add manually to your opencode.json under \"mcp\":\n")
fmt.Fprintf(os.Stderr, " \"engram\": { \"type\": \"local\", \"command\": [\"engram\", \"mcp\", \"--tools=agent\"], \"enabled\": true }\n")
fmt.Fprintf(os.Stderr, " \"engram\": { \"type\": \"local\", \"command\": [%q, \"mcp\", \"--tools=agent\"], \"enabled\": true }\n", cmd)
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

The warning snippet is meant to be pasted as JSON, but formatting the command with %q uses Go string escaping (e.g., it can emit \UXXXXXXXX escapes) which is not guaranteed to be valid JSON. Consider JSON-escaping the command via json.Marshal(cmd) and printing the resulting string bytes (without introducing double-quotes) so the snippet is always valid JSON.

Suggested change
fmt.Fprintf(os.Stderr, " \"engram\": { \"type\": \"local\", \"command\": [%q, \"mcp\", \"--tools=agent\"], \"enabled\": true }\n", cmd)
// Use JSON escaping for the command so the snippet is always valid JSON.
if cmdJSON, errJSON := jsonMarshalFn(cmd); errJSON == nil {
fmt.Fprintf(os.Stderr, " \"engram\": { \"type\": \"local\", \"command\": [%s, \"mcp\", \"--tools=agent\"], \"enabled\": true }\n", cmdJSON)
} else {
// Fallback: preserve original behavior if JSON marshalling fails.
fmt.Fprintf(os.Stderr, " \"engram\": { \"type\": \"local\", \"command\": [%q, \"mcp\", \"--tools=agent\"], \"enabled\": true }\n", cmd)
}

Copilot uses AI. Check for mistakes.
Comment on lines +327 to +328
// Use resolveEngramCommand() so Windows users (and headless Linux setups
// where PATH is not inherited) get the absolute binary path.
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

This comment mentions "headless Linux setups where PATH is not inherited" as a reason to use an absolute binary path, but resolveEngramCommand() currently returns bare "engram" on non-Windows. Please clarify the comment so it matches the implementation (or update resolveEngramCommand() if Linux headless PATH inheritance is a real requirement).

Suggested change
// Use resolveEngramCommand() so Windows users (and headless Linux setups
// where PATH is not inherited) get the absolute binary path.
// Use resolveEngramCommand() so Windows users get an absolute binary path,
// while Unix-like systems use the bare "engram" command resolved via PATH.

Copilot uses AI. Check for mistakes.
Comment on lines +2050 to +2056
mcp := cfg["mcp"].(map[string]any)
engram := mcp["engram"].(map[string]any)
cmd := engram["command"].([]any)
if len(cmd) == 0 {
t.Fatalf("expected non-empty command array")
}
first := cmd[0].(string)
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

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

These tests use chained type assertions like cfg["mcp"].(map[string]any) / engram["command"].([]any). If the JSON shape changes or the key is missing, the test will panic instead of producing a clear failure. Using the comma-ok form (and failing with t.Fatalf) makes failures easier to diagnose.

Suggested change
mcp := cfg["mcp"].(map[string]any)
engram := mcp["engram"].(map[string]any)
cmd := engram["command"].([]any)
if len(cmd) == 0 {
t.Fatalf("expected non-empty command array")
}
first := cmd[0].(string)
mcpVal, ok := cfg["mcp"]
if !ok {
t.Fatalf("config missing %q key", "mcp")
}
mcp, ok := mcpVal.(map[string]any)
if !ok {
t.Fatalf("config %q key has unexpected type %T, want map[string]any", "mcp", mcpVal)
}
engramVal, ok := mcp["engram"]
if !ok {
t.Fatalf("mcp config missing %q key", "engram")
}
engram, ok := engramVal.(map[string]any)
if !ok {
t.Fatalf("mcp %q key has unexpected type %T, want map[string]any", "engram", engramVal)
}
cmdVal, ok := engram["command"]
if !ok {
t.Fatalf("engram config missing %q key", "command")
}
cmd, ok := cmdVal.([]any)
if !ok {
t.Fatalf("engram %q key has unexpected type %T, want []any", "command", cmdVal)
}
if len(cmd) == 0 {
t.Fatalf("expected non-empty command array")
}
first, ok := cmd[0].(string)
if !ok {
t.Fatalf("first command element has unexpected type %T, want string", cmd[0])
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support HTTP/SSE MCP transport for headless OpenCode serve mode

2 participants