-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat(api): add readonly connectors API #4258
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?
Conversation
fe03f3e to
f711d52
Compare
cdoern
left a comment
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.
thanks for calling this out! I did see this PR as well, though it looks like @leseb has taken care to make his PR backward compatible, so maybe (hopefully) they shouldn't break each other? |
Would love to see this being written as a FastAPI Router since that's the new way moving fw. I think #4191 is pretty ready, but since it's not merged I think that 4258 PR should stay as is and we will convert it later since we can maintain both router and non-router. |
ack, I think that makes sense |
f711d52 to
4cf2f32
Compare
✱ Stainless preview buildsThis PR will update the Edit this comment to update it. It will appear in the SDK's changelogs.
|
⚠️ llama-stack-client-kotlin studio · code · diff
There was a regression in your SDK.
generate ⚠️→lint ✅→test ❗New diagnostics (4 warning)
⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}/tools/{tool_name}` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}/tools` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.
⚠️ llama-stack-client-python studio · code · diff
There was a regression in your SDK.
generate ⚠️→build ⏳→lint ⏳→test ⏳New diagnostics (4 warning, 1 note)
⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}/tools/{tool_name}` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}/tools` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.💡 Model/NeedsRename: Consider renaming model `file`; e.g. `api_file`'
⚠️ llama-stack-client-go studio · code · diff
There was a regression in your SDK.
generate ⚠️→lint ❗→test ❗go get github.com/stainless-sdks/llama-stack-client-go@90c63ea2b4778c6926796289f224fbec8c240373New diagnostics (4 warning)
⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}/tools/{tool_name}` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors/{connector_id}/tools` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.⚠️ Endpoint/NotConfigured: `get /v1alpha/connectors` exists in the OpenAPI spec, but isn't specified in the Stainless config, so code will not be generated for it.
This comment is auto-generated by GitHub Actions and is automatically kept up to date as you push.
Last updated: 2025-12-10 17:27:26 UTC
4cf2f32 to
ca6068d
Compare
ashwinb
left a comment
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 think we should only do Connectors for now without Registries.
6e939b3 to
565df6c
Compare
src/llama_stack_api/connectors.py
Outdated
| default=None, alias="connector_id", description="User-specified identifier for the connector" | ||
| ) | ||
| url: str = Field(..., description="URL of the connector") | ||
| created_at: datetime = Field(..., description="Timestamp of creation") |
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.
it's surprising these two fields aren't tracked in Resource -- or are they?
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.
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.
could we add these fields to all resources instead? it could be a separate PR..
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.
added
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.
update - i realized it would take more work than I thought to move these timestamps to the base resource class
so instead, i have only removed it from here for now and will make a separate PR later
src/llama_stack_api/connectors.py
Outdated
| server_description: str | None = Field(default=None, description="Description of the server") | ||
| # TODO: using ToolDef for now, but MCPListToolsTool should probably be updated and used instead | ||
| # once toolgroups are removed completely | ||
| tools: list[ToolDef] | None = Field(default=None, description="List of tools available from the connector") |
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.
why not use MCPListToolsTool as the comment indicates? but more importantly, I don't think we should persist tools at all? The Stack should list tools dynamically and cache the list for what could be a reasonable period of time. I think the MCP protocol is explicitly about being able to discover tools dynamically. Am I mistaken?
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.
why not use MCPListToolsTool as the comment indicates?
I was using it initially, but then I saw ToolDef is more comprehensive including output_schema and metadata (which are missing in MCPListToolsTool) also the utility function to list MCP server tools also returns a list of ToolDefs, so I chose to stay with it for now until toolgroups and related types are removed completely
I don't think we should persist tools at all? The Stack should list tools dynamically and cache the list for what could be a reasonable period of time. I think the MCP protocol is explicitly about being able to discover tools dynamically. Am I mistaken?
I am not too familiar with the specific intentions expressed in the protocol itself so I can't speak to that, but I only included this here because I had initially proposed 2 separate endpoints to list all available connectors and all available tools. I got feedback that these could be combined into a single endpoint so I inlcude an include_tools filter and needed to store the tools somewhere
However, we already have the ability to list tools only for specific connector_ids when requested, which can be done dynamically without storing them in the connector object, so if we are okay with not listing a connector's tools along with all its other information, I can remove this field from the connector
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.
removed the persistence of tools in connector, and added authorization to the query params for the endpoints that will return tool list or individual tools
src/llama_stack_api/connectors.py
Outdated
| connector_type: ConnectorType = Field(default=ConnectorType.MCP) | ||
| connector_id: str | None = Field(default=None, description="Unique identifier for the connector") | ||
| url: str = Field(..., description="URL of the connector") | ||
| headers: dict[str, Any] | None = Field(default=None, description="HTTP headers to include when connecting") |
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.
how come the other fields (server_*, etc.) from Connector aren't present here?
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 can include the server_label here, the other fields can be queried from the server itself so user doesn't have to provide them
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.
added server_label to connector input
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 think we should have ConnectorInput mirror very closely to Connector -- at least as long as Connector inherits from Resource. Why? Because that is what goes into the registry. Auxiliary state (like tools, other attributes about the server) which would be queried by the server at least at startup should be kept separately and only in memory. It might be reasonable to split the "connector datatype which gets stored in registry db" from the "connector datatype which is returned when you query in the API, especially if you want the auxilliary state to be provided"
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.
It might be reasonable to split the "connector datatype which gets stored in registry db" from the "connector datatype which is returned when you query in the API,
Sure, I like this. I will update connector input to include everything except maybe server name and description. Those can remain as API object fields and move the timestamp ones to the base resource class
e3b87d9 to
8d93b53
Compare
src/llama_stack_api/connectors.py
Outdated
| default=None, alias="connector_id", description="User-specified identifier for the connector" | ||
| ) | ||
| url: str = Field(..., description="URL of the connector") | ||
| created_at: datetime = Field(..., description="Timestamp of creation") |
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.
could we add these fields to all resources instead? it could be a separate PR..
src/llama_stack_api/connectors.py
Outdated
| connector_type: ConnectorType = Field(default=ConnectorType.MCP) | ||
| connector_id: str | None = Field(default=None, description="Unique identifier for the connector") | ||
| url: str = Field(..., description="URL of the connector") | ||
| headers: dict[str, Any] | None = Field(default=None, description="HTTP headers to include when connecting") |
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 think we should have ConnectorInput mirror very closely to Connector -- at least as long as Connector inherits from Resource. Why? Because that is what goes into the registry. Auxiliary state (like tools, other attributes about the server) which would be queried by the server at least at startup should be kept separately and only in memory. It might be reasonable to split the "connector datatype which gets stored in registry db" from the "connector datatype which is returned when you query in the API, especially if you want the auxilliary state to be provided"
src/llama_stack_api/connectors.py
Outdated
| url: str = Field(..., description="URL of the connector") | ||
| server_label: str | None = Field(default=None, description="Label of the server") | ||
| headers: dict[str, Any] | None = Field(default=None, description="HTTP headers to include when connecting") | ||
| authorization: str | None = Field(default=None, description="OAuth access token for authentication") |
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.
is that a good idea to keep this unencrypted in a run.yaml even in an enterprise world? stuff like this is almost always a Secret
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 included this here with the intention that stack admins can set auth tokens etc in env vars and references those vars in the run.yaml, then the stack can resolve them
The only reason for allowing admins to configure auth in the first place is if we want to handle cases where we want to enable users to access infra-owned MCP servers that they won't have their own credentials for. In such cases, maybe a stack admin will need to put some creds in place that can be used for this purpose.
I am not 100% sure how realistic this scenario would be - for most use cases where we expect users to bring their own auth tokens we can remove this field from the connector input altogether I think
thoughts?
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.
@jaideepr97 the scenario is quite reasonable but the mechanism is not. auth tokens like these should never be in plain text and so need to be read via some kind of Secrets reading mechanism. the Stack currently does not have in built support to tie into things like the AWS SecretsManager, etc. so I suggest we just not have it for now.
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.
makes sense, removed them
|
FYI #4191 merged so it'd be nice to have this added a router :) |
@leseb that's awesome. Given that there is limited time to get stuff merged for the 0.4.0 release, I am inclined to try and get this merged as is for now so that this functionality at least exists in 0.4.0 Does that work? |
0eab243 to
8082317
Compare
src/llama_stack_api/connectors.py
Outdated
|
|
||
|
|
||
| @json_schema_type | ||
| class ConnectorInput(BaseModel): |
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 wonder if we can share common attributes between Connector and ConnectorInput via a shared BaseModel?
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.
updated
8082317 to
3f93a61
Compare
|
@ashwinb Any idea why this particular integration test keeps failing consistently? Seems unrelated to the PR itself |
3f93a61 to
5949507
Compare
5949507 to
de26e6b
Compare
Closes llamastack#4235 and llamastack#4061 (partially) Signed-off-by: Jaideep Rao <[email protected]>
… info from connectors api response Signed-off-by: Jaideep Rao <[email protected]>
Signed-off-by: Jaideep Rao <[email protected]>
de26e6b to
980f4b8
Compare
| :param type: Type of resource, always 'connector' for connectors | ||
| :param connector_type: Type of connector (e.g., MCP) | ||
| :param connector_id: Identifier for the connector | ||
| :param url: URL of the connector |
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 documentation is no longer in sync
| async def get_connector( | ||
| self, | ||
| connector_id: str, | ||
| authorization: str | None = None, |
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.
why is there an authorization parameter here?

What does this PR do?
Adds a new API for connectors and MCP registry support along with required types.
Does not include any implementation for it
Closes #4235 and #4061 (partially)
Test Plan
no tests included