Skip to content

Bannerlord/Valheim/Lib.GAB Integration Support#59

Merged
pardeike merged 11 commits intopardeike:mainfrom
BUTR:feature/butr-all-changes-rebased
Mar 15, 2026
Merged

Bannerlord/Valheim/Lib.GAB Integration Support#59
pardeike merged 11 commits intopardeike:mainfrom
BUTR:feature/butr-all-changes-rebased

Conversation

@Aragas
Copy link
Copy Markdown
Contributor

@Aragas Aragas commented Mar 15, 2026

Summary

Adds the changes needed to make GABS work with Mount & Blade II: Bannerlord and Lib.GAB.

  • Improved process detection: Bannerlord launches through BLSE (a mod loader), which means the direct child process exits while the real game keeps running. IsRunning now falls back to StopProcessName lookup when the child process is gone. Process liveness uses platform-specific implementations (Win32 OpenProcess/GetExitCodeProcess on Windows, Signal(0) on Unix) instead of spawning external processes.

  • Lib.GAB tool format parsing: Lib.GAB sends tools as a {"tools": [...]} envelope with flat parameters arrays and C# type names. Added ToolDescriptorRaw conversion to JSON Schema inputSchema, and passes outputSchema end-to-end from GABP → MCP so AI agents can see tool return types.

  • Context-based GABP connection: Replaced the fixed 5-attempt retry loop in Client.Connect with context.Context-driven retries. This prevents both premature failure (Bannerlord's mod loading is slow) and infinite hangs. GABP connect timeout increased from 30s to 120s.

  • games.connect and games.call_tool MCP tools: Claude Code ignores tools/list_changed SSE notifications, so dynamically registered game tools are invisible. games.connect lets the AI manually trigger GABP connection and tool sync. games.call_tool proxies tool calls to the game's GABP server with configurable timeout support for long-running tools like wait_for_screen.
    Here's the Claude Bug

  • Synchronous tool mirroring: setupToolMirroring now runs synchronously and calls the actual syncGABPTools/exposeGABPResources implementations (previously a TODO stub running in a goroutine).

Companion PR

Lib.GAB side: pardeike/Lib.GAB#10

jneb802 and others added 8 commits January 12, 2026 20:25
- Call syncGABPTools() and exposeGABPResources() in setupToolMirroring
- Extract tools from {"tools": [...]} wrapper in ListTools response
- Convert Lib.GAB's parameters array to MCP JSON Schema inputSchema
When using DirectPath or CustomCommand mode with a launcher that spawns
the actual game as a separate process (e.g., BLSE for Bannerlord),
the direct child process exits after launching the game. IsRunning()
would then report false even though the game is running.

This adds a fallback to findProcessesByName(StopProcessName) when the
direct child process is no longer alive, matching the behavior already
used by SteamAppId/EpicAppId modes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Windows, os.Process.Signal(syscall.Signal(0)) always returns an error
because Windows does not support signal 0. This caused IsRunning() to
always take the error path even when the child process was alive.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signal(0) is unsupported on Windows and always errors. The previous
fix incorrectly returned true when ProcessState was nil, which meant
a dead process could be reported as running. Instead, always fall
back to isRunningByName() on Windows, which uses tasklist for
accurate process detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of always falling back to process name lookup on Windows,
first check if the direct child PID is still alive using tasklist.
Only fall back to name lookup if the child is dead (launcher pattern).
This matches the Unix Signal(0) behavior more accurately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace tasklist subprocess with direct OpenProcess+GetExitCodeProcess
call on Windows. Extract platform-specific isProcessAlive() into
alive_windows.go and alive_unix.go for clean cross-platform support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pass outputSchema from GABP tools through to MCP Tool struct
- Parse Lib.GAB raw tool format (ToolDescriptorRaw) into JSON Schema
- Add games.connect and games.call_tool MCP tools
- Add context.Context to Client.Connect for caller-controlled timeouts
- Wire GABPConnector.AttemptConnection through with context
- Implement synchronous tool mirroring in gabp_connector
- Add CallToolWithTimeout and sendRequestWithTimeout
- Increase GABP connect timeout to 120s for slow-loading games

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

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

Adds Mount & Blade II: Bannerlord / Lib.GAB compatibility improvements to GABS by hardening process-liveness detection and expanding MCP↔GABP connectivity and tool interoperability.

Changes:

  • Context-driven GABP connection retries/timeouts (incl. longer default connect timeout).
  • Cross-platform process liveness checks with fallback to StopProcessName lookup when launcher child exits.
  • Lib.GAB tool format support and propagation of outputSchema, plus new MCP tools (games.connect, games.call_tool) to work around clients that miss dynamic tool updates.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
internal/process/serialized_starter.go Increases GABP connect timeout and switches connector API to accept context.Context.
internal/process/controller.go Adds PID-based liveness checks and a StopProcessName fallback when child process exits.
internal/process/alive_windows.go Windows implementation of lightweight PID liveness check (OpenProcess/GetExitCodeProcess).
internal/process/alive_unix.go Unix implementation of lightweight PID liveness check (signal 0).
internal/mirror/mirror.go Mirrors GABP tool outputSchema into MCP tool definitions.
internal/mcp/types.go Extends MCP Tool type to include optional outputSchema.
internal/mcp/stdio_server.go Adds games.connect / games.call_tool tools and makes GABP connect use context timeout.
internal/mcp/gabp_mirroring_test.go Updates tests for context-aware Client.Connect.
internal/mcp/gabp_connector.go Makes tool/resource mirroring run synchronously and context-aware connect.
internal/mcp/dynamic_tool_discovery_test.go Updates expected core tool list to include new game-management tools.
internal/gabp/client_test.go Updates client tests for context-aware connect behavior.
internal/gabp/client.go Implements context-driven connect retries; adds Lib.GAB tool parsing + per-request timeout support.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread internal/gabp/client_test.go Outdated
Comment thread internal/process/controller.go Outdated
Comment thread internal/process/alive_unix.go Outdated
Comment thread internal/gabp/client.go
Comment thread internal/mcp/stdio_server.go Outdated
Comment thread internal/mcp/stdio_server.go
Aragas and others added 3 commits March 15, 2026 11:55
games.tools now includes parameter names, types, and descriptions
from inputSchema, plus return field info from outputSchema. This
lets AI agents discover the correct argument names for games.call_tool.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TestBackoffJitter: reduce context timeout from 5s to 500ms and call
  cancel() explicitly instead of deferring inside the loop
- IsRunning: guard c.cmd.Wait() with sync.Once to prevent multiple
  concurrent Wait calls when IsRunning is called repeatedly
- alive_unix: treat EPERM from Signal(0) as "process alive" since it
  means the process exists but we lack permission to signal it

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- client.go: Thread ctx into handshake via goroutine+select so Connect
  doesn't block after ctx cancellation. Close connection on handshake
  failure to prevent leaked goroutines.
- stdio_server.go: games.call_tool now accepts both original GABP names
  (bannerlord.core/ping) and normalized MCP names (bannerlord.bannerlord.core.ping)
  by stripping the gameId prefix and converting dots back to slashes.
- alive_unix.go: Explicitly check ESRCH (no such process) for clarity,
  with conservative fallback for unknown errors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@pardeike pardeike merged commit 206db56 into pardeike:main Mar 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants