-
Notifications
You must be signed in to change notification settings - Fork 2k
Add McpClientHandlersRegistry #4802
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add McpClientHandlersRegistry #4802
Conversation
cfce918 to
4835ccc
Compare
4835ccc to
2b1f49b
Compare
52676cd to
18e986a
Compare
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
- In febf86c, we broke a dependency cycle ChatClient -> McpClient - With the introduction of ClientMcpSyncHandlersRegistry and the async variant, there is no dependency McpClient -> MCP handlers anymore, breaking the cycle in a simpler way. - Here, we revert most of the changes of febf86c, but keep the tests. Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
b524ddd to
ad8d987
Compare
- Remove custom class resolution method and use AutoProxyUtils instead Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
ad8d987 to
4ab6503
Compare
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
Signed-off-by: Daniel Garnier-Moiroux <git@garnier.wf>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Thanks @Kehrlann
There are some small formatting issues I'll resolve while merging
| if (!foundAnnotations.isEmpty()) { | ||
| this.allAnnotatedBeans.add(beanName); | ||
| } | ||
| for (var foundAnnotation : foundAnnotations) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess this block can be nested in side the if (!foundAnnotations.isEmpty()) { above
|
|
||
| for (var beanName : this.allAnnotatedBeans) { | ||
| var bean = this.beanFactory.getBean(beanName); | ||
| var annotations = scan(bean.getClass()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this.scan
| // Only process singleton beans, not scoped beans | ||
| continue; | ||
| } | ||
| var foundAnnotations = scan(AutoProxyUtils.determineTargetClass(beanFactory, beanName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this prefix (e.g. this.scan)
|
|
||
| @Override | ||
| public void afterSingletonsInstantiated() { | ||
| var beansByAnnotation = getBeansByAnnotationType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this prefix
|
|
||
| @Override | ||
| public void afterSingletonsInstantiated() { | ||
| var beansByAnnotation = getBeansByAnnotationType(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this prefix
Fixes #4670
This PR introduces two variants (sync and async) of a "registry" of handlers,
ClientMcpSyncHandlersRegistry.These registries have two main responsibilities:
This allows to break the dependency between MCP clients and MCP-annotated beans. This was a problem when doing sampling, where there was a cycle MCP Client -> MCP handlers -> ChatClient -> MCP Client.
With this PR, the MCP clients depend on the registry, but the registry does not depend on the MCP-handlers. Instead, it discovers them dynamically and makes handlers available by client name. See #4751 for the initial attempt at fixing the circular dependency.
The flow to capture annotations has two steps:
BeanFactoryPostProcessor. Bean types are scanned for annotations, and the bean names found are recorded for later use. Additionally,@McpSamplingand@McpElicitationannotations are tracked per client name. This allows to bootstrap MCP clients in theMcpClientAutoConfiguration: by the time the clients are created, we know their capabilities, even if the annotated beans have not been instantiated yet.SmartInitializingSingleton, actual bean instances are retrieved, and from the bean instances and annotated methods, MCP handlers are populated in the registry, stored by client name.Notes: