Skip to content

Expose MCP feature through Slint platform setup#11520

Open
tilladam wants to merge 4 commits intoslint-ui:masterfrom
tilladam:feature/slint-mcp-custom-platform-init
Open

Expose MCP feature through Slint platform setup#11520
tilladam wants to merge 4 commits intoslint-ui:masterfrom
tilladam:feature/slint-mcp-custom-platform-init

Conversation

@tilladam
Copy link
Copy Markdown
Contributor

Summary

  • add a public slint/mcp feature that wires through the selector and testing backend MCP support
  • initialize the embedded MCP server from slint::platform::set_platform(), matching the existing backend-selector initialization path
  • make MCP initialization idempotent while allowing retries if hook registration fails
  • keep slint/mcp from compiling the public testing-backend helper APIs and suppress generated protobuf dead-code warnings in the generated proto module

Notes

This keeps the current MCP implementation in i-slint-backend-testing because it reuses the existing introspection/search infrastructure. A possible future cleanup is to split that shared introspection layer into its own internal crate/module, with clearer boundaries:

  • accessibility: semantic runtime contract for assistive technologies
  • introspection/devtools: debug/test element tree, handles, geometry, metadata, and search
  • MCP: transport/server layer built on introspection
  • testing backend: fake backend, mock time, deterministic fonts, and test utilities built on introspection where needed

That split is orthogonal to this PR, but would avoid making MCP look conceptually tied to the testing backend long term.

Verification

  • cargo build -p slint --release --no-default-features --features std,compat-1-2,mcp
  • cargo check -p i-slint-backend-testing --features internal

@tronical
Copy link
Copy Markdown
Member

cc @ogoffart

I've been thinking along the same lines of having just a public MCP feature and otherwise no changes needed. It won't expose additional api.

Implementation wise I think it would be better not to do the shadowing and instead use the same approach as the system testing hook.

@tronical
Copy link
Copy Markdown
Member

I’m very confused. Why is all the extra code necessary when we have

#[cfg(feature = "mcp")]
?

Right now, once has to merely enable the MCP feature in the selector and instead we could say that one need to enable it on the slint crate instead and that forwards.

But clearly I’m missing something as this diff does so much more. What am I missing?

@tilladam
Copy link
Copy Markdown
Contributor Author

The key scenario the selector's with_global_context at line 175 doesn't cover is custom platform users — applications that drive their own event loop and call slint::platform::set_platform() directly, bypassing the selector entirely. For those apps, with_global_context is never called, so MCP would never initialize.

With add_platform_init_hook and slint::mcp::register(), custom platform authors can opt into MCP without coupling back into the selector. This is the same pattern set_window_shown_hook already established in i_slint_core::context.

So in summary: if the goal is only "make mcp work for selector-based apps," then yes, just forwarding the feature is sufficient and simpler. But if we want custom platform authors to be able to opt into MCP (which is the stated motivation for this PR), the hook mechanism is needed and slint::mcp::register() is the right surface for that.

BackendSelector::select() calls set_platform() directly, bypassing
with_global_context, so MCP and system-testing were not initialized
when using that path. Call init() directly after set_platform succeeds.

Also update the skills file: the MCP server is now enabled via the
public slint crate's mcp feature, not i-slint-backend-selector.
Replace the shadowed set_platform in api/rs/slint with an
add_platform_init_hook() mechanism in i_slint_core::context,
analogous to set_window_shown_hook. Hooks are stored in a
thread-local Vec and drained by set_platform immediately after
SlintContext is installed, before update_timers_and_animations(),
so introspection hooks are in place before any timer or change
callbacks can create or show a window.

The selector registers system-testing and mcp hooks inside the
backend factory closure so hooks are only queued when backend
creation succeeds, preserving the old result.is_ok() behavior.
Custom platform authors can opt in via slint::mcp::register()
before calling set_platform.

Reverts the mcp-gated cfg guards on testing init functions,
which were artifacts of the shadow approach.
@tilladam tilladam force-pushed the feature/slint-mcp-custom-platform-init branch from 2ebc2bb to 25ff187 Compare April 27, 2026 18:41
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.

2 participants