Skip to content

Conversation

@jaideepr97
Copy link
Contributor

@jaideepr97 jaideepr97 commented Nov 28, 2025

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

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 28, 2025
@jaideepr97 jaideepr97 changed the title feat: add connectors API feat: add readonly connectors API Nov 28, 2025
@jaideepr97 jaideepr97 changed the title feat: add readonly connectors API feat(api): add readonly connectors API Nov 28, 2025
@jaideepr97 jaideepr97 force-pushed the add-connectors-api branch 2 times, most recently from fe03f3e to f711d52 Compare November 28, 2025 18:35
Copy link
Collaborator

@cdoern cdoern left a comment

Choose a reason for hiding this comment

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

this looks like its on the right path, but might conflict with #4191 , @leseb wdyt the order of operations should be here?

@jaideepr97
Copy link
Contributor Author

this looks like its on the right path, but might conflict with #4191 , @leseb wdyt the order of operations should be here?

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?

@leseb
Copy link
Collaborator

leseb commented Dec 1, 2025

this looks like its on the right path, but might conflict with #4191 , @leseb wdyt the order of operations should be here?

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.

@jaideepr97
Copy link
Contributor Author

this looks like its on the right path, but might conflict with #4191 , @leseb wdyt the order of operations should be here?

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
happy to work on a follow up PR to port this to router post merge

@github-actions
Copy link
Contributor

github-actions bot commented Dec 1, 2025

✱ Stainless preview builds

This PR will update the llama-stack-client SDKs with the following commit message.

feat(api): add readonly connectors API

Edit this comment to update it. It will appear in the SDK's changelogs.

⚠️ llama-stack-client-node studio · code · diff

There was a regression in your SDK.
generate ⚠️build ✅lint ✅test ✅

npm install https://pkg.stainless.com/s/llama-stack-client-node/84dcaf181be4867372ad49f296f8735318702dc4/dist.tar.gz
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-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@90c63ea2b4778c6926796289f224fbec8c240373
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.

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

@ashwinb
Copy link
Contributor

ashwinb commented Dec 1, 2025

image

Stainless warnings above ^ you need to update client-sdks/stainless/config.yml accordingly to add a new connectors sub-resource. Note that it should be under the alpha resource.

Copy link
Contributor

@ashwinb ashwinb left a 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.

@jaideepr97 jaideepr97 force-pushed the add-connectors-api branch 3 times, most recently from 6e939b3 to 565df6c Compare December 2, 2025 05:27
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")
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

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..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

Copy link
Contributor Author

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

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")
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor Author

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

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")
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor Author

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

Copy link
Contributor

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"

Copy link
Contributor Author

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

@jaideepr97 jaideepr97 force-pushed the add-connectors-api branch 4 times, most recently from e3b87d9 to 8d93b53 Compare December 3, 2025 18:16
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")
Copy link
Contributor

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..

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")
Copy link
Contributor

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"

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")
Copy link
Contributor

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

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

makes sense, removed them

@leseb
Copy link
Collaborator

leseb commented Dec 4, 2025

FYI #4191 merged so it'd be nice to have this added a router :)

@jaideepr97
Copy link
Contributor Author

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
I am happy to port it to router after the fact, rather than add another few rounds of review to this PR and risk missing the release

Does that work?

@jaideepr97 jaideepr97 force-pushed the add-connectors-api branch 2 times, most recently from 0eab243 to 8082317 Compare December 4, 2025 17:55


@json_schema_type
class ConnectorInput(BaseModel):
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

updated

@jaideepr97
Copy link
Contributor Author

@ashwinb Any idea why this particular integration test keeps failing consistently? Seems unrelated to the PR itself

@mattf
Copy link
Collaborator

mattf commented Dec 8, 2025

@ashwinb Any idea why this particular integration test keeps failing consistently? Seems unrelated to the PR itself

hopefully #4335

@jaideepr97
Copy link
Contributor Author

@ashwinb Any idea why this particular integration test keeps failing consistently? Seems unrelated to the PR itself

hopefully #4335

gotcha, thanks for the pointer! that should do it 🤞

: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
Copy link
Contributor

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,
Copy link
Contributor

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?

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

Labels

CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce a new API for connector discovery

5 participants