Skip to content

Comments

Add IMessagePreambleRegistry extension point and IMessageMetadata interface#363

Merged
brichet merged 11 commits intojupyterlab:mainfrom
andrii-i:tool-call-preamble-registry
Feb 23, 2026
Merged

Add IMessagePreambleRegistry extension point and IMessageMetadata interface#363
brichet merged 11 commits intojupyterlab:mainfrom
andrii-i:tool-call-preamble-registry

Conversation

@andrii-i
Copy link
Collaborator

@andrii-i andrii-i commented Feb 18, 2026

Description

Adds a message preamble registry (IMessagePreambleRegistry) and IToolCall type support to jupyter-chat.

The preamble registry is an extension point that allows JupyterLab extensions to register React components rendered above the message body, after the header. It follows the same pattern as the existing footer registry (Token + interface + implementation + plugin), including the same IMessageContent-based props signature. Components render vertically in insertion order. This enables downstream packages to plug UI into chat messages above the message body — for example, jupyter-ai-acp-client#12 uses it to display real-time tool call status for AI agent actions.

Adds IToolCall interface and tool_calls?: IToolCall[] field on IMessageContent (TypeScript) and Message (Python), providing first-class support for tool call metadata on chat messages.

Tool Call UI (implementation) Preview

Preview from jupyter-ai-contrib/jupyter-ai-acp-client#12 that uses the message preamble registry:

  • Simple read
    read_justfile
  • Multiple agents
    multiple_agents

Tests

Unit tests for the preamble registry covering: empty initialization, component addition, insertion order preservation, and defensive copy (mutating the returned array does not affect internal state).

Reviewer instructions

  • Run jlpm build, ensure compiles clean
  • Run jlpm test, ensure preamble registry tests pass
  • Verify that MessagePreambleComponent renders between header and body in messages.tsx. To test end-to-end, install jupyter-ai-acp-client#12 alongside this branch. jupyter-ai-devrepo can simplify the multi-repo setup.

@andrii-i andrii-i added the enhancement New feature or request label Feb 18, 2026
@github-actions
Copy link
Contributor

Binder 👈 Launch a Binder on branch andrii-i/jupyter-chat/tool-call-preamble-registry

@andrii-i andrii-i marked this pull request as ready for review February 18, 2026 08:19
@andrii-i
Copy link
Collaborator Author

Hi @brichet, other maintainers. Could you please give this a look?

@andrii-i andrii-i requested review from brichet and dlqqq February 18, 2026 08:20
@andrii-i andrii-i force-pushed the tool-call-preamble-registry branch from 9a579c3 to a88058c Compare February 18, 2026 21:53
… getter to Message class and subscribe preamble to changed signal
@andrii-i
Copy link
Collaborator Author

andrii-i commented Feb 18, 2026

@jtpio updated the PR description with GIF showing multiple agents using Tool Call UI from jupyter-ai-contrib/jupyter-ai-acp-client#12 at the same time (also below).

As you can see, each agent response gets its own message, so multiple concurrent agents maintain fully independent tool call timelines — each message's preamble re-renders only when its own tool_calls field changes. Tool call state is stored directly on each chat message (tool_calls field).
multiple_agents

Copy link
Member

@dlqqq dlqqq left a comment

Choose a reason for hiding this comment

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

These changes look good to me. The message preamble makes a lot of sense since we already have a message footer. My only minor nitpick is that the naming convention is a bit awkward, but it makes sense given that the username & avatar is already the "header", so we should call this component the "preamble".

If I understand correctly, I think the main area of discussion concerns the addition of the tool_calls field to the message model. IMO, there are a few good reasons for us to add this:

  1. Jupyter Chat's de facto purpose is to serve as a chat library for our AI extensions, i.e. Jupyter AI & JupyterLite AI. We should feel free to add general AI-relevant fields to the message model to make it easier for our extensions to append metadata to AI response messages. It may go unused by consumers, which is fine because empty keys occupy minimal memory/disk relative to the message contents.

  2. The tool call object schema is well standardized now so we're not locking consumers into any specific implementation by adding the tool_calls field here. The required components of the schema are being used in LangChain and other AI libraries/providers.

  3. While we could add some extension point for making the message model configurable, this introduces several new design questions to work through. What happens if two different extensions need to each add fields to the message model? How can we still preserve type safety without just making the message an arbitrary dictionary? What happens if a user uninstalls an extension that adds a custom field, then opens an old chat file containing that custom field in its messages? IMO, the proposed approach of adding universally helpful but optional fields makes more sense pragmatically. We can update this in the future before the v1.0 release if needed.

Comment on lines 67 to 73
export interface IToolCall {
tool_call_id: string;
title: string;
kind?: string;
status?: string;
raw_output?: unknown;
}
Copy link
Member

Choose a reason for hiding this comment

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

It may be good to add docstrings here to clarify the purpose of these fields. For example, title is the string shown to the user, kind helps indicate the type of action allowing consumers to switch icons, status is a string indicating the state of the tool call (i.e. success/failure), and raw_output is the output of the command.

Comment on lines 67 to 73
export interface IToolCall {
tool_call_id: string;
title: string;
kind?: string;
status?: string;
raw_output?: unknown;
}
Copy link
Member

Choose a reason for hiding this comment

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

Also, right now, we're just showing the raw output, but eventually we may want to add an optional content field that provides a more human-friendly tool call UI for the user: https://agentclientprotocol.com/protocol/schema#toolcallcontent

@brichet
Copy link
Collaborator

brichet commented Feb 19, 2026

Thanks @andrii-i for this PR, and for updating the screen cast. This looks good overall.

I agree that the chat is currently mainly used for AI extensions, and that tool call is somewhat standardized (de facto, I don't think there is any official standard yet).
Nevertheless, the idea behind extracting the chat from “jupyter-ai” was to make it independent of its “use,” and it can already be used as a simple collaborative chat, without any AI (although this is probably not the case so far).
That is why I wonder if we could avoid including IToolCall in the chat message schema.

Would there be any drawback to adding a generic metadata: JSONObject instead, as for a Notebook cell?
That would be future proof, and extensions could add any kind of metadata, related to AI or not.

@andrii-i
Copy link
Collaborator Author

andrii-i commented Feb 19, 2026

Thanks for the thoughtful feedback, @brichet!

To directly answer your question, there are a few drawbacks to metadata: JSONObject:

  1. Concurrent extension conflicts. update_message() replaces the entire field value. If two extensions each write to metadata, the second write overwrites the first. With separate typed fields, each extension's data is independent.
  2. Type safety loss. Consumers must cast message.metadata?.tool_calls as IToolCall[].
  3. Schema evolution is harder, not easier as @dlqqq points out above "What happens if a user uninstalls an extension that adds a custom field, then opens an old chat file containing that custom field in its messages?" Old .chat files retain metadata dictionary keys with no documented schema. A typed field at least makes the contract explicit and stable.

Potentially both approaches can coexist: typed fields for well-standardized concepts - which I believe tool calls to be - and metadata for everything else.

@SylvainCorlay
Copy link
Member

The idea behind extracting the chat from “jupyter-ai” was to make it independent of its “use,” and it can already be used as a simple collaborative chat, without any AI (although this is probably not the case so far).
That is why I wonder if we could avoid including IToolCall in the chat message schema.

Indeed, IToolCall seems very jupyter-ai specific. It seems that we are leaking abstraction here.

@jtpio
Copy link
Member

jtpio commented Feb 20, 2026

@andrii-i in jupyter-ai-contrib/jupyter-ai-acp-client#12 (which would consume this API), do you think it would be possible to quickly check what it would take to add support for more UI components than just tool calls?

Since the screencasts show the Claude Agent is being used and Claude supports tasks lists (among others), maybe the task list component could be a good candidate? This would allow checking the API covers everything needed to allow further iterations in downstream implementations like jupyter-ai-acp-client.

@andrii-i
Copy link
Collaborator Author

Thanks @brichet and @SylvainCorlay. Updated the PR to follow the proposed generic metadata approach. Please give it another look now.

@andrii-i andrii-i requested a review from jtpio February 20, 2026 10:02
@andrii-i
Copy link
Collaborator Author

andrii-i commented Feb 20, 2026

@jtpio With the metadata update, extension authors only need to augment IMessageMetadata with their fields and register a component via IMessagePreambleRegistry. No further API changes needed.

@andrii-i
Copy link
Collaborator Author

andrii-i commented Feb 20, 2026

@jtpio For a working example of an extension adding a UI component, see the tool call UI component in jupyter-ai-contrib/jupyter-ai-acp-client#12

@andrii-i andrii-i changed the title Add message preamble registry and IToolCall type support Add IMessagePreambleRegistry extension point and IMessageMetadata interface Feb 20, 2026
Copy link
Collaborator

@brichet brichet left a comment

Choose a reason for hiding this comment

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

@andrii-i thanks for the update, LGTM.

Copy link
Member

@dlqqq dlqqq left a comment

Choose a reason for hiding this comment

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

Tested and verified locally. Thanks for adding the metadata object & making the changes on the ACP client side Andrii.

@brichet Can you help merge & release this when you're back on Monday? Is there anything else to do before cutting another official v0.x release? Pre-release is fine if there are things pending.

@brichet brichet merged commit fb51a70 into jupyterlab:main Feb 23, 2026
13 checks passed
@andrii-i andrii-i deleted the tool-call-preamble-registry branch February 23, 2026 14:30
@andrii-i
Copy link
Collaborator Author

Thank you for reviews and approvals everyone!

@brichet, @jtpio please let me know if you need help cutting a release.

@brichet
Copy link
Collaborator

brichet commented Feb 23, 2026

I will cut a pre-release today.
I'd like to include #343 in the next v0.20.0 release. Don't hesitate to comment there if you have opinion on this change, since it changes the UI of the side panel.

Also, as it may be related to this change, I started https://github.com/brichet/jupyter-chat-components, which currently includes a new mimetype that display tool call component, with approval/reject buttons.

@brichet
Copy link
Collaborator

brichet commented Feb 23, 2026

@andrii-i it is released https://github.com/jupyterlab/jupyter-chat/releases/tag/v0.20.0alpha2

@andrii-i
Copy link
Collaborator Author

Thank you @brichet.

@andrii-i andrii-i restored the tool-call-preamble-registry branch February 23, 2026 16:17
@andrii-i andrii-i deleted the tool-call-preamble-registry branch February 23, 2026 16:21
@dlqqq
Copy link
Member

dlqqq commented Feb 23, 2026

TYSM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants