Skip to content

feat(core): wire MCP servers into Environment via adapter layer - #216

Open
field-claw wants to merge 2 commits into
Prism-Shadow:mainfrom
field-claw:feat/mcp-adapter
Open

feat(core): wire MCP servers into Environment via adapter layer#216
field-claw wants to merge 2 commits into
Prism-Shadow:mainfrom
field-claw:feat/mcp-adapter

Conversation

@field-claw

@field-claw field-claw commented Aug 6, 2026

Copy link
Copy Markdown

Summary

PenguinHarness declared mcpServers in tool config but never connected them (the docs note "enumerating concrete MCP tools is reserved for a later adapter layer"). This PR implements that layer so configured MCP servers become real, LLM-callable tools.

What changed

  • New McpToolAdapter (packages/core/src/environment/mcp/client.ts): connects each declared stdio/SSE MCP server, enumerates tools, and wraps them as BuiltinTool instances.
    • Tool names namespaced as mcp__<server>__<tool> (collision-safe).
    • MCP inputSchema (JSON Schema) passes straight into ToolDefinitionConfig.parameters — both are Record<string,unknown>, no conversion layer.
    • Per-server connect failures are isolated (logged to stderr, don't abort the Environment).
  • Environment gains a static async create(): when mcpServers is non-empty it connects them and registers their tools. The sync constructor is preserved (callers without MCP are unaffected).
  • agent.ts now uses Environment.create() so the real agent loop can reach MCP tools.
  • Abort forwarding: ToolExecutionContext.signal is forwarded into the SDK callTool options.signal, so user-interrupt / Environment timeout cancels an in-flight MCP request.
  • Adds @modelcontextprotocol/sdk (MIT/Apache-2.0, license-compatible with Apache-2.0).

Verification

  • pnpm --filter @prismshadow/penguin-core typecheck → passes.
  • Unit test (mocked SDK) test/mcp-client.test.ts → 3/3 green (namespacing, inputSchema passthrough, BuiltinTool delegation, fault isolation).
  • Real e2e test/mcp-e2e.mts (spins up a live local stdio MCP server via tsx, drives Environment.create → listTools → executeTool) → reaches and calls the tool, streams output, finalizes completed.

Backward compatibility

Callers that don't configure mcpServers continue using the sync new Environment(...) with zero changes. Only the runtime agent path switched to await Environment.create(...).

Notes / follow-ups (out of scope)

  • CLI/Web config UI for mcpServers not added; JSON edit + docs suffices for now.
  • sse transport is wired in buildTransport (client-side SSEClientTransport) but is not covered by a live e2e — server-side SSE was removed from the MCP spec and the SDK's SSEServerTransport is unstable to stand up as a test fixture. The stdio path is fully e2e-tested.
  • signal is now forwarded into callTool; Environment continues to enforce timeout/abort on the tool stream as a backstop.

Generated with Claude as a PenguinHarness MCP evaluation follow-up.

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.

1 participant