Skip to content

feat(mcpx): WASM tool-source spike — wazero host, capability test (#1970) - #2002

Merged
dezren39 merged 1 commit into
mainfrom
queue/2907-1970-wasm-spike
Oct 29, 2026
Merged

dezren39 merged 1 commit into
mainfrom
queue/2907-1970-wasm-spike

Conversation

@dezren39

@dezren39 dezren39 commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Spike for #1970 (queue task 2907): Approach B — a core-WASM .wasm module as an mcpx tool source, hosted by wazero.

What this adds

  • pkgs/mcpx/internal/wasmtools: a standalone wazero host for a core-WASM tool source. It instantiates no WASI at all, so a guest gets no filesystem, network, clock, or environment by default; the only imports that can exist are host grants (today: mcpx.log). Any ungranted import — WASI included — fails instantiation: deny-by-default is a property of the loader.
  • Guest ABI mirrors mcpclient.Client (ListTools / CallTool shapes in internal/mcpclient/client.go), shaped so a component-model ABI can replace it underneath later (Approach C).
  • Test guests (Zig sources + prebuilt .wasm in testdata/): echo (lists/calls one tool, logs via the granted import) and greedy (imports mcpx.read_file, which the host never offers).

Interface gap (per the spike brief)

There is no registration point yet for a new tool-source kind: upstreams are concrete transports in internal/mcpclient and internal/adapter, and the tool-source interface is still tracked by #1885. So this package is deliberately a standalone spike, not a parallel/fifth composition path. Wiring it in waits for #1885's interface; the ABI above is designed to sit behind it unchanged.

Capability test (acceptance)

  • TestListAndCallWithGrantedLog — granted log import works; tool list + call round-trip.
  • TestSameModuleWithoutGrantIsDenied — same module without the grant fails to instantiate.
  • TestUngrantedCapabilityImportIsDenied — greedy.wasm is denied even with every grantable capability granted; the error names the missing read_file import.

Verified on the laptop worktree: go test -count=1 ./internal/wasmtools/ — PASS.

Constraints

  • Pure Go: no cgo introduced (wazero only, already in-tree for embeddings).
  • Existing JS-runtime script behaviour and process/HTTP tool sources unchanged — this adds one new internal package only.

Gating research answer

Posted on #1970: wazero does not host Component Model components at acceptable maturity today (component support is still under active development upstream); the mature component host is wasmtime, whose Go bindings require cgo — which would forfeit the pure-Go premise. Recommendation: proceed with B on core WASM now; defer C until wazero component hosting matures or a pure-Go lowering path lands.

Refs #1970, #1885


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@dezren39
dezren39 requested a review from a team as a code owner October 28, 2026 23:43
@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 34 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0207bd8a-5b2d-4be9-9b89-db14fc431ca0
Commits

Reviewing files that changed from the base of the PR and between 77918e0 and 019ee02.

Files ignored due to path filters: 2
  • pkgs/mcpx/internal/wasmtools/testdata/echo.wasm is excluded by !**/*.wasm
  • pkgs/mcpx/internal/wasmtools/testdata/greedy.wasm is excluded by !**/*.wasm
Files selected for processing: 4
  • pkgs/mcpx/internal/wasmtools/testdata/guest/echo.zig
  • pkgs/mcpx/internal/wasmtools/testdata/guest/greedy.zig
  • pkgs/mcpx/internal/wasmtools/wasmtools.go
  • pkgs/mcpx/internal/wasmtools/wasmtools_test.go
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-28T23:46:56.314809Z 019ee02 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 019ee02b90

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +98 to +102
for _, fn := range []string{inputPtrFn, outputPtrFn, listFn, callFn} {
if mod.ExportedFunction(fn) == nil {
rt.Close(ctx)
return nil, fmt.Errorf("wasmtools: guest does not export %s", fn)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate guest memory and function signatures during loading

If a guest exports all four required names but omits linear memory or uses an incompatible result signature, Load succeeds even though subsequent calls can panic the Go host. For example, a valid WASM module whose mcpx_list_tools returns no values reaches res[0] in ListTools, while a module without memory reaches a nil memory dereference in readOutput or CallTool. Validate the required memory and function parameter/result types before returning the source so malformed guests produce load errors rather than host panics.

Useful? React with 👍 / 👎.

// grant set — WASI included — makes Load fail.
func Load(ctx context.Context, src []byte, grants ...string) (*Source, error) {
s := &Source{}
rt := wazero.NewRuntime(ctx)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Enable context cancellation for guest execution

If a guest tool enters an infinite loop, canceling or timing out the context passed to ListTools or CallTool will not interrupt it, leaving the caller goroutine executing indefinitely. The wazero v1.9.0 runtime configuration documentation explicitly states that execution-termination checks are disabled by default, which is the configuration selected by NewRuntime here. Construct the runtime with WithCloseOnContextDone(true) so callers can actually bound guest execution through the contexts these methods accept.

Useful? React with 👍 / 👎.

@dezren39

Copy link
Copy Markdown
Member Author

🤖 Agent note · parked

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