feat: store and expose hostContext in App class (#139)

* feat: store and expose hostContext in App class

Fixes #129 - The App class now stores the initial hostContext received
during initialization and exposes it via getHostContext().

Changes:
- Add _hostContext private property to App class
- Store result.hostContext in connect() method
- Add getHostContext() getter method (parallel to getHostCapabilities())
- Update onhostcontextchanged setter to merge partial updates into
  stored context
- Add default notification handler to update context even when
  user hasn't set onhostcontextchanged

This enables apps to:
- Access toolInfo immediately after connection (only in initial context)
- Render with correct theme/locale without waiting for first notification
- Synchronously access viewport, timezone, and other initial state

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: use sendHostContextChange for partial update accumulation test

Address PR review feedback: The test was using setHostContext with full
context spreads which didn't clearly demonstrate partial update
accumulation. Now uses sendHostContextChange directly to send truly
partial updates (only theme, then only viewport), making it clear
the App correctly merges multiple partial notifications.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Olivier Chafik 2025-12-12 00:39:58 +01:00 • committed by GitHub
commit eaea3f9e07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -22,6 +22,7 @@ import {
LATEST_PROTOCOL_VERSION,
McpUiAppCapabilities,
McpUiHostCapabilities,
McpUiHostContext,
McpUiHostContextChangedNotification,
McpUiHostContextChangedNotificationSchema,
McpUiInitializedNotification,
@ -191,6 +192,7 @@ type RequestHandlerExtra = Parameters<
export class App extends Protocol<Request, Notification, Result> {
private _hostCapabilities?: McpUiHostCapabilities;
private _hostInfo?: Implementation;
private _hostContext?: McpUiHostContext;
/**
* Create a new MCP App instance.
@ -219,6 +221,10 @@ export class App extends Protocol<Request, Notification, Result> {
console.log("Received ping:", request.params);
return {};
});
// Set up default handler to update _hostContext when notifications arrive.
// Users can override this by setting onhostcontextchanged.
this.onhostcontextchanged = () => {};
}
/**
@ -276,6 +282,42 @@ export class App extends Protocol<Request, Notification, Result> {
return this._hostInfo;
}
/**
* Get the host context discovered during initialization.
*
* Returns the host context that was provided in the initialization response,
* including tool info, theme, viewport, locale, and other environment details.
* This context is automatically updated when the host sends
* `ui/notifications/host-context-changed` notifications.
*
* Returns `undefined` if called before connection is established.
*
* @returns Host context, or `undefined` if not yet connected
*
* @example Access host context after connection
* ```typescript
* await app.connect(transport);
* const context = app.getHostContext();
* if (context === undefined) {
* console.error("Not connected");
* return;
* }
* if (context.theme === "dark") {
* document.body.classList.add("dark-theme");
* }
* if (context.toolInfo) {
* console.log("Tool:", context.toolInfo.tool.name);
* }
* ```
*
* @see {@link connect} for the initialization handshake
* @see {@link onhostcontextchanged} for context change notifications
* @see {@link McpUiHostContext} for the context structure
*/
getHostContext(): McpUiHostContext | undefined {
return this._hostContext;
}
/**
* Convenience handler for receiving complete tool input from the host.
*
@ -463,7 +505,11 @@ export class App extends Protocol<Request, Notification, Result> {
) {
this.setNotificationHandler(
McpUiHostContextChangedNotificationSchema,
(n) => callback(n.params),
(n) => {
// Merge the partial update into the stored context
this._hostContext = { ...this._hostContext, ...n.params };
callback(n.params);
},
);
}
@ -961,6 +1007,7 @@ export class App extends Protocol<Request, Notification, Result> {
this._hostCapabilities = result.hostCapabilities;
this._hostInfo = result.hostInfo;
this._hostContext = result.hostContext;
await this.notification(<McpUiInitializedNotification>{
method: "ui/notifications/initialized",