feat: add connectors implementation for static config

Signed-off-by: Jaideep Rao <jrao@redhat.com>
This commit is contained in:
Jaideep Rao 2025-12-01 10:43:12 +05:30
parent 8a5a56f166
commit 3b1b37862e
14 changed files with 558 additions and 203 deletions

View file

@ -3903,23 +3903,30 @@ paths:
schema: schema:
$ref: '#/components/schemas/Connector' $ref: '#/components/schemas/Connector'
'400': '400':
description: Bad Request
$ref: '#/components/responses/BadRequest400' $ref: '#/components/responses/BadRequest400'
description: Bad Request
'429': '429':
description: Too Many Requests
$ref: '#/components/responses/TooManyRequests429' $ref: '#/components/responses/TooManyRequests429'
description: Too Many Requests
'500': '500':
description: Internal Server Error
$ref: '#/components/responses/InternalServerError500' $ref: '#/components/responses/InternalServerError500'
description: Internal Server Error
default: default:
description: Default Response
$ref: '#/components/responses/DefaultError' $ref: '#/components/responses/DefaultError'
description: Default Response
tags: tags:
- Connectors - Connectors
summary: Get Connector summary: Get Connector
description: Get a connector by its ID. description: Get a connector by its ID.
operationId: get_connector_v1alpha_connectors__connector_id__get operationId: get_connector_v1alpha_connectors__connector_id__get
parameters: parameters:
- name: include_tools
in: query
required: false
schema:
type: boolean
default: false
title: Include Tools
- name: connector_id - name: connector_id
in: path in: path
required: true required: true
@ -3930,11 +3937,11 @@ paths:
get: get:
responses: responses:
'200': '200':
description: A MCPListToolsTool. description: A ToolDef.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
'400': '400':
description: Bad Request description: Bad Request
$ref: '#/components/responses/BadRequest400' $ref: '#/components/responses/BadRequest400'
@ -11884,7 +11891,7 @@ components:
tools: tools:
anyOf: anyOf:
- items: - items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
- type: 'null' - type: 'null'
description: List of tools available from the connector description: List of tools available from the connector
@ -12026,7 +12033,7 @@ components:
properties: properties:
data: data:
items: items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
title: Data title: Data
type: object type: object
@ -12975,6 +12982,33 @@ components:
- url - url
title: RegistryInput title: RegistryInput
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
ConnectorInput: ConnectorInput:
description: Input for creating a connector. description: Input for creating a connector.
properties: properties:
@ -12991,6 +13025,19 @@ components:
description: URL of the connector description: URL of the connector
title: Url title: Url
type: string type: string
headers:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: HTTP headers to include when connecting
nullable: true
authorization:
anyOf:
- type: string
- type: 'null'
description: OAuth access token for authentication
nullable: true
required: required:
- url - url
title: ConnectorInput title: ConnectorInput
@ -13079,33 +13126,6 @@ components:
- items - items
title: ConversationItemCreateRequest title: ConversationItemCreateRequest
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
Api: Api:
description: Enumeration of all available APIs in the Llama Stack system. description: Enumeration of all available APIs in the Llama Stack system.
enum: enum:

View file

@ -8672,7 +8672,7 @@ components:
tools: tools:
anyOf: anyOf:
- items: - items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
- type: 'null' - type: 'null'
description: List of tools available from the connector description: List of tools available from the connector
@ -8814,7 +8814,7 @@ components:
properties: properties:
data: data:
items: items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
title: Data title: Data
type: object type: object
@ -9763,6 +9763,33 @@ components:
- url - url
title: RegistryInput title: RegistryInput
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
ConnectorInput: ConnectorInput:
description: Input for creating a connector. description: Input for creating a connector.
properties: properties:
@ -9779,6 +9806,19 @@ components:
description: URL of the connector description: URL of the connector
title: Url title: Url
type: string type: string
headers:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: HTTP headers to include when connecting
nullable: true
authorization:
anyOf:
- type: string
- type: 'null'
description: OAuth access token for authentication
nullable: true
required: required:
- url - url
title: ConnectorInput title: ConnectorInput
@ -9867,33 +9907,6 @@ components:
- items - items
title: ConversationItemCreateRequest title: ConversationItemCreateRequest
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
Api: Api:
description: Enumeration of all available APIs in the Llama Stack system. description: Enumeration of all available APIs in the Llama Stack system.
enum: enum:

View file

@ -640,23 +640,30 @@ paths:
schema: schema:
$ref: '#/components/schemas/Connector' $ref: '#/components/schemas/Connector'
'400': '400':
description: Bad Request
$ref: '#/components/responses/BadRequest400' $ref: '#/components/responses/BadRequest400'
description: Bad Request
'429': '429':
description: Too Many Requests
$ref: '#/components/responses/TooManyRequests429' $ref: '#/components/responses/TooManyRequests429'
description: Too Many Requests
'500': '500':
description: Internal Server Error
$ref: '#/components/responses/InternalServerError500' $ref: '#/components/responses/InternalServerError500'
description: Internal Server Error
default: default:
description: Default Response
$ref: '#/components/responses/DefaultError' $ref: '#/components/responses/DefaultError'
description: Default Response
tags: tags:
- Connectors - Connectors
summary: Get Connector summary: Get Connector
description: Get a connector by its ID. description: Get a connector by its ID.
operationId: get_connector_v1alpha_connectors__connector_id__get operationId: get_connector_v1alpha_connectors__connector_id__get
parameters: parameters:
- name: include_tools
in: query
required: false
schema:
type: boolean
default: false
title: Include Tools
- name: connector_id - name: connector_id
in: path in: path
required: true required: true
@ -667,11 +674,11 @@ paths:
get: get:
responses: responses:
'200': '200':
description: A MCPListToolsTool. description: A ToolDef.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
'400': '400':
description: Bad Request description: Bad Request
$ref: '#/components/responses/BadRequest400' $ref: '#/components/responses/BadRequest400'
@ -7725,7 +7732,7 @@ components:
tools: tools:
anyOf: anyOf:
- items: - items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
- type: 'null' - type: 'null'
description: List of tools available from the connector description: List of tools available from the connector
@ -7855,7 +7862,7 @@ components:
properties: properties:
data: data:
items: items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
title: Data title: Data
type: object type: object
@ -8735,6 +8742,33 @@ components:
- url - url
title: RegistryInput title: RegistryInput
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
ConnectorInput: ConnectorInput:
description: Input for creating a connector. description: Input for creating a connector.
properties: properties:
@ -8751,6 +8785,19 @@ components:
description: URL of the connector description: URL of the connector
title: Url title: Url
type: string type: string
headers:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: HTTP headers to include when connecting
nullable: true
authorization:
anyOf:
- type: string
- type: 'null'
description: OAuth access token for authentication
nullable: true
required: required:
- url - url
title: ConnectorInput title: ConnectorInput
@ -8839,33 +8886,6 @@ components:
- items - items
title: ConversationItemCreateRequest title: ConversationItemCreateRequest
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
Api: Api:
description: Enumeration of all available APIs in the Llama Stack system. description: Enumeration of all available APIs in the Llama Stack system.
enum: enum:

View file

@ -10114,7 +10114,7 @@ components:
tools: tools:
anyOf: anyOf:
- items: - items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
- type: 'null' - type: 'null'
description: List of tools available from the connector description: List of tools available from the connector
@ -10256,7 +10256,7 @@ components:
properties: properties:
data: data:
items: items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
title: Data title: Data
type: object type: object
@ -11205,6 +11205,33 @@ components:
- url - url
title: RegistryInput title: RegistryInput
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
ConnectorInput: ConnectorInput:
description: Input for creating a connector. description: Input for creating a connector.
properties: properties:
@ -11221,6 +11248,19 @@ components:
description: URL of the connector description: URL of the connector
title: Url title: Url
type: string type: string
headers:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: HTTP headers to include when connecting
nullable: true
authorization:
anyOf:
- type: string
- type: 'null'
description: OAuth access token for authentication
nullable: true
required: required:
- url - url
title: ConnectorInput title: ConnectorInput
@ -11309,33 +11349,6 @@ components:
- items - items
title: ConversationItemCreateRequest title: ConversationItemCreateRequest
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
Api: Api:
description: Enumeration of all available APIs in the Llama Stack system. description: Enumeration of all available APIs in the Llama Stack system.
enum: enum:

View file

@ -3903,23 +3903,30 @@ paths:
schema: schema:
$ref: '#/components/schemas/Connector' $ref: '#/components/schemas/Connector'
'400': '400':
description: Bad Request
$ref: '#/components/responses/BadRequest400' $ref: '#/components/responses/BadRequest400'
description: Bad Request
'429': '429':
description: Too Many Requests
$ref: '#/components/responses/TooManyRequests429' $ref: '#/components/responses/TooManyRequests429'
description: Too Many Requests
'500': '500':
description: Internal Server Error
$ref: '#/components/responses/InternalServerError500' $ref: '#/components/responses/InternalServerError500'
description: Internal Server Error
default: default:
description: Default Response
$ref: '#/components/responses/DefaultError' $ref: '#/components/responses/DefaultError'
description: Default Response
tags: tags:
- Connectors - Connectors
summary: Get Connector summary: Get Connector
description: Get a connector by its ID. description: Get a connector by its ID.
operationId: get_connector_v1alpha_connectors__connector_id__get operationId: get_connector_v1alpha_connectors__connector_id__get
parameters: parameters:
- name: include_tools
in: query
required: false
schema:
type: boolean
default: false
title: Include Tools
- name: connector_id - name: connector_id
in: path in: path
required: true required: true
@ -3930,11 +3937,11 @@ paths:
get: get:
responses: responses:
'200': '200':
description: A MCPListToolsTool. description: A ToolDef.
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
'400': '400':
description: Bad Request description: Bad Request
$ref: '#/components/responses/BadRequest400' $ref: '#/components/responses/BadRequest400'
@ -11884,7 +11891,7 @@ components:
tools: tools:
anyOf: anyOf:
- items: - items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
- type: 'null' - type: 'null'
description: List of tools available from the connector description: List of tools available from the connector
@ -12026,7 +12033,7 @@ components:
properties: properties:
data: data:
items: items:
$ref: '#/components/schemas/MCPListToolsTool' $ref: '#/components/schemas/ToolDef'
type: array type: array
title: Data title: Data
type: object type: object
@ -12975,6 +12982,33 @@ components:
- url - url
title: RegistryInput title: RegistryInput
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
ConnectorInput: ConnectorInput:
description: Input for creating a connector. description: Input for creating a connector.
properties: properties:
@ -12991,6 +13025,19 @@ components:
description: URL of the connector description: URL of the connector
title: Url title: Url
type: string type: string
headers:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
description: HTTP headers to include when connecting
nullable: true
authorization:
anyOf:
- type: string
- type: 'null'
description: OAuth access token for authentication
nullable: true
required: required:
- url - url
title: ConnectorInput title: ConnectorInput
@ -13079,33 +13126,6 @@ components:
- items - items
title: ConversationItemCreateRequest title: ConversationItemCreateRequest
type: object type: object
ToolGroupInput:
description: Input data for registering a tool group.
properties:
toolgroup_id:
title: Toolgroup Id
type: string
provider_id:
title: Provider Id
type: string
args:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
nullable: true
mcp_endpoint:
anyOf:
- $ref: '#/components/schemas/URL'
title: URL
- type: 'null'
nullable: true
title: URL
required:
- toolgroup_id
- provider_id
title: ToolGroupInput
type: object
Api: Api:
description: Enumeration of all available APIs in the Llama Stack system. description: Enumeration of all available APIs in the Llama Stack system.
enum: enum:

View file

@ -0,0 +1,5 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.

View file

@ -0,0 +1,175 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
from datetime import UTC, datetime
from typing import Any
from pydantic import BaseModel
from llama_stack.core.datatypes import StackRunConfig
from llama_stack.log import get_logger
from llama_stack.providers.utils.tools.mcp import get_mcp_server_info, list_mcp_tools
from llama_stack_api import (
Connector,
Connectors,
ConnectorType,
ListConnectorsResponse,
ListRegistriesResponse,
ListToolsResponse,
Registry,
ToolDef,
)
from llama_stack_api.common.errors import (
ConnectorNotFoundError,
ConnectorToolNotFoundError,
RegistryNotFoundError,
)
logger = get_logger(name=__name__, category="connectors")
class ConnectorServiceConfig(BaseModel):
"""Configuration for the built-in connector service.
:param run_config: Stack run configuration for resolving persistence
"""
run_config: StackRunConfig
async def get_provider_impl(config: ConnectorServiceConfig):
"""Get the connector service implementation."""
impl = ConnectorServiceImpl(config)
return impl
class ConnectorServiceImpl(Connectors):
"""Built-in connector service implementation."""
def __init__(self, config: ConnectorServiceConfig):
self.config = config
# TODO: should these be stored in a kvstore?
self.connectors_map: dict[str, Connector] = {}
self.registries_map: dict[str, Registry] = {}
async def register_connector(
self,
url: str,
connector_id: str | None = None,
connector_type: ConnectorType = ConnectorType.MCP,
headers: dict[str, Any] | None = None,
authorization: str | None = None,
) -> Connector:
"""Register a new connector.
:param url: URL of the MCP server to connect to.
:param connector_id: (Optional) User-specified identifier for the connector.
:param connector_type: (Optional) Type of connector, defaults to MCP.
:param headers: (Optional) HTTP headers to include when connecting to the server.
:param authorization: (Optional) OAuth access token for authenticating with the MCP server.
:returns: The registered Connector.
"""
# Fetch server info and tools from the MCP server
# TODO: documentation item: users should be able to pass headers and authorization in the connector input as env variables.
server_info = await get_mcp_server_info(url, headers=headers, authorization=authorization)
tools_response = await list_mcp_tools(url, headers=headers, authorization=authorization)
connector = Connector(
identifier=server_info.name,
provider_id="builtin::connectors",
user_connector_id=connector_id,
connector_type=connector_type,
url=url,
created_at=datetime.now(UTC),
updated_at=datetime.now(UTC),
server_name=server_info.name,
server_label=server_info.title,
server_description=server_info.description,
tools=tools_response.data,
)
logger.info(f"Registered connector {connector.connector_id} with server name {connector.server_name}")
self.connectors_map[connector.connector_id] = connector
return connector
async def list_connectors(
self,
registry_id: str | None = None,
include_tools: bool = False,
) -> ListConnectorsResponse:
"""List all configured connectors.
:param registry_id: (Optional) The ID of a registry to filter connectors for.
:param include_tools: (Optional) Whether to include tools in the response.
:returns: A ListConnectorsResponse.
"""
connectors = [c for c in self.connectors_map.values() if registry_id is None or c.registry_id == registry_id]
if not include_tools:
return ListConnectorsResponse(data=[c.without_tools for c in connectors])
return ListConnectorsResponse(data=connectors)
async def get_connector(self, connector_id: str, include_tools: bool = False) -> Connector:
"""Get a connector by its ID.
:param connector_id: The ID of the connector to get.
:returns: A Connector.
:raises ConnectorNotFoundError: If the connector is not found.
"""
connector = self.connectors_map.get(connector_id)
if connector is None:
raise ConnectorNotFoundError(connector_id)
if not include_tools:
return connector.without_tools
return connector
async def list_connector_tools(self, connector_id: str) -> ListToolsResponse:
"""List tools available from a connector.
:param connector_id: The ID of the connector to list tools for.
:returns: A ListToolsResponse.
:raises ConnectorNotFoundError: If the connector is not found.
"""
connector = await self.get_connector(connector_id, include_tools=True)
# Return empty list if no tools, rather than raising
return ListToolsResponse(data=connector.tools or [])
async def get_connector_tool(self, connector_id: str, tool_name: str) -> ToolDef:
"""Get a tool definition by its name from a connector.
:param connector_id: The ID of the connector to get the tool from.
:param tool_name: The name of the tool to get.
:returns: A ToolDef.
:raises ConnectorNotFoundError: If the connector is not found.
:raises ConnectorToolNotFoundError: If the tool is not found in the connector.
"""
connector_tools = await self.list_connector_tools(connector_id)
for tool in connector_tools.data:
if tool.name == tool_name:
return tool
raise ConnectorToolNotFoundError(connector_id, tool_name)
async def list_registries(self) -> ListRegistriesResponse:
"""List all registries.
:returns: A ListRegistriesResponse.
"""
return ListRegistriesResponse(data=list(self.registries_map.values()))
async def get_registry(self, registry_id: str) -> Registry:
"""Get a registry by its ID.
:param registry_id: The ID of the registry to get.
:returns: A Registry.
:raises RegistryNotFoundError: If the registry is not found.
"""
registry = self.registries_map.get(registry_id)
if registry is None:
raise RegistryNotFoundError(registry_id)
return registry
async def shutdown(self) -> None:
self.connectors_map.clear()
self.registries_map.clear()

View file

@ -22,6 +22,7 @@ from llama_stack_api import (
Api, Api,
Benchmark, Benchmark,
BenchmarkInput, BenchmarkInput,
ConnectorInput,
Dataset, Dataset,
DatasetInput, DatasetInput,
DatasetIO, DatasetIO,
@ -429,6 +430,7 @@ class RegisteredResources(BaseModel):
scoring_fns: list[ScoringFnInput] = Field(default_factory=list) scoring_fns: list[ScoringFnInput] = Field(default_factory=list)
benchmarks: list[BenchmarkInput] = Field(default_factory=list) benchmarks: list[BenchmarkInput] = Field(default_factory=list)
tool_groups: list[ToolGroupInput] = Field(default_factory=list) tool_groups: list[ToolGroupInput] = Field(default_factory=list)
connectors: list[ConnectorInput] = Field(default_factory=list)
class ServerConfig(BaseModel): class ServerConfig(BaseModel):

View file

@ -457,6 +457,7 @@ def create_app() -> StackApp:
apis_to_serve.add("providers") apis_to_serve.add("providers")
apis_to_serve.add("prompts") apis_to_serve.add("prompts")
apis_to_serve.add("conversations") apis_to_serve.add("conversations")
apis_to_serve.add("connectors")
for api_str in apis_to_serve: for api_str in apis_to_serve:
api = Api(api_str) api = Api(api_str)

View file

@ -13,6 +13,7 @@ from typing import Any
import yaml import yaml
from llama_stack.core.connectors.connectors import ConnectorServiceConfig, ConnectorServiceImpl
from llama_stack.core.conversations.conversations import ConversationServiceConfig, ConversationServiceImpl from llama_stack.core.conversations.conversations import ConversationServiceConfig, ConversationServiceImpl
from llama_stack.core.datatypes import Provider, SafetyConfig, StackRunConfig, VectorStoresConfig from llama_stack.core.datatypes import Provider, SafetyConfig, StackRunConfig, VectorStoresConfig
from llama_stack.core.distribution import get_provider_registry from llama_stack.core.distribution import get_provider_registry
@ -39,6 +40,7 @@ from llama_stack_api import (
Api, Api,
Batches, Batches,
Benchmarks, Benchmarks,
Connectors,
Conversations, Conversations,
DatasetIO, DatasetIO,
Datasets, Datasets,
@ -64,6 +66,7 @@ logger = get_logger(name=__name__, category="core")
class LlamaStack( class LlamaStack(
Providers, Providers,
Connectors,
Inference, Inference,
Agents, Agents,
Batches, Batches,
@ -100,6 +103,7 @@ RESOURCES = [
), ),
("benchmarks", Api.benchmarks, "register_benchmark", "list_benchmarks"), ("benchmarks", Api.benchmarks, "register_benchmark", "list_benchmarks"),
("tool_groups", Api.tool_groups, "register_tool_group", "list_tool_groups"), ("tool_groups", Api.tool_groups, "register_tool_group", "list_tool_groups"),
("connectors", Api.connectors, "register_connector", "list_connectors"),
] ]
@ -372,6 +376,11 @@ def add_internal_implementations(impls: dict[Api, Any], run_config: StackRunConf
) )
impls[Api.conversations] = conversations_impl impls[Api.conversations] = conversations_impl
connectors_impl = ConnectorServiceImpl(
ConnectorServiceConfig(run_config=run_config),
)
impls[Api.connectors] = connectors_impl
def _initialize_storage(run_config: StackRunConfig): def _initialize_storage(run_config: StackRunConfig):
kv_backends: dict[str, StorageBackendConfig] = {} kv_backends: dict[str, StorageBackendConfig] = {}

View file

@ -50,6 +50,7 @@ CATEGORIES = [
"post_training", "post_training",
"scoring", "scoring",
"tests", "tests",
"connectors",
] ]
UNCATEGORIZED = "uncategorized" UNCATEGORIZED = "uncategorized"

View file

@ -228,3 +228,44 @@ async def invoke_mcp_tool(
content=content, content=content,
error_code=1 if result.isError else 0, error_code=1 if result.isError else 0,
) )
from dataclasses import dataclass
@dataclass
class MCPServerInfo:
"""Server information from an MCP server."""
name: str
version: str
title: str | None = None
description: str | None = None
async def get_mcp_server_info(
endpoint: str,
headers: dict[str, str] | None = None,
authorization: str | None = None,
) -> MCPServerInfo:
"""Get server info from an MCP server.
Args:
endpoint: MCP server endpoint URL
headers: Optional base headers to include
authorization: Optional OAuth access token (just the token, not "Bearer <token>")
Returns:
MCPServerInfo containing name, version, title, and description
"""
final_headers = prepare_mcp_headers(headers, authorization)
async with client_wrapper(endpoint, final_headers) as session:
init_result = await session.initialize()
return MCPServerInfo(
name=init_result.serverInfo.name,
version=init_result.serverInfo.version,
title=init_result.serverInfo.title,
description=init_result.instructions,
)

View file

@ -80,6 +80,28 @@ class TokenValidationError(ValueError):
super().__init__(message) super().__init__(message)
class ConnectorNotFoundError(ResourceNotFoundError):
"""raised when Llama Stack cannot find a referenced connector"""
def __init__(self, connector_id: str) -> None:
super().__init__(connector_id, "Connector", "client.connectors.list()")
class ConnectorToolNotFoundError(ValueError):
"""raised when Llama Stack cannot find a referenced tool in a connector"""
def __init__(self, connector_id: str, tool_name: str) -> None:
message = f"Tool '{tool_name}' not found in connector '{connector_id}'. Use 'client.connectors.list_tools(\"{connector_id}\")' to list available tools."
super().__init__(message)
class RegistryNotFoundError(ResourceNotFoundError):
"""raised when Llama Stack cannot find a referenced registry"""
def __init__(self, registry_id: str) -> None:
super().__init__(registry_id, "Registry", "client.connectors.list_registries()")
class ConversationNotFoundError(ResourceNotFoundError): class ConversationNotFoundError(ResourceNotFoundError):
"""raised when Llama Stack cannot find a referenced conversation""" """raised when Llama Stack cannot find a referenced conversation"""

View file

@ -6,15 +6,15 @@
from datetime import datetime from datetime import datetime
from enum import StrEnum from enum import StrEnum
from typing import Literal, Protocol from typing import Any, Literal, Protocol
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from typing_extensions import runtime_checkable from typing_extensions import runtime_checkable
from llama_stack_api.openai_responses import MCPListToolsTool
from llama_stack_api.registries import ListRegistriesResponse, Registry from llama_stack_api.registries import ListRegistriesResponse, Registry
from llama_stack_api.resource import Resource, ResourceType from llama_stack_api.resource import Resource, ResourceType
from llama_stack_api.schema_utils import json_schema_type, webmethod from llama_stack_api.schema_utils import json_schema_type, webmethod
from llama_stack_api.tools import ToolDef
from llama_stack_api.version import LLAMA_STACK_API_V1ALPHA from llama_stack_api.version import LLAMA_STACK_API_V1ALPHA
@ -54,7 +54,9 @@ class Connector(Resource):
server_name: str | None = Field(default=None, description="Name of the server") server_name: str | None = Field(default=None, description="Name of the server")
server_label: str | None = Field(default=None, description="Label of the server") server_label: str | None = Field(default=None, description="Label of the server")
server_description: str | None = Field(default=None, description="Description of the server") server_description: str | None = Field(default=None, description="Description of the server")
tools: list[MCPListToolsTool] | None = Field(default=None, description="List of tools available from the connector") # 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")
registry_id: str | None = Field(default=None, description="ID of the registry this connector belongs to") registry_id: str | None = Field(default=None, description="ID of the registry this connector belongs to")
def _generate_connector_id(self) -> str: def _generate_connector_id(self) -> str:
@ -67,6 +69,11 @@ class Connector(Resource):
def connector_id(self) -> str: def connector_id(self) -> str:
return self.user_connector_id if self.user_connector_id is not None else self._generate_connector_id() return self.user_connector_id if self.user_connector_id is not None else self._generate_connector_id()
@property
def without_tools(self) -> "Connector":
"""Return a copy of this connector with tools removed."""
return self.model_copy(update={"tools": None})
@json_schema_type @json_schema_type
class ConnectorInput(BaseModel): class ConnectorInput(BaseModel):
@ -75,11 +82,15 @@ class ConnectorInput(BaseModel):
:param connector_type: Type of connector :param connector_type: Type of connector
:param connector_id: Unique identifier for the connector :param connector_id: Unique identifier for the connector
:param url: URL of the connector :param url: URL of the connector
:param headers: (Optional) HTTP headers to include when connecting to the server
:param authorization: (Optional) OAuth access token for authenticating with the MCP server
""" """
connector_type: ConnectorType = Field(default=ConnectorType.MCP) connector_type: ConnectorType = Field(default=ConnectorType.MCP)
connector_id: str | None = Field(default=None, description="Unique identifier for the connector") connector_id: str | None = Field(default=None, description="Unique identifier for the connector")
url: str = Field(..., description="URL of 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")
authorization: str | None = Field(default=None, description="OAuth access token for authentication")
@json_schema_type @json_schema_type
@ -99,11 +110,14 @@ class ListToolsResponse(BaseModel):
:param data: List of tools :param data: List of tools
""" """
data: list[MCPListToolsTool] data: list[ToolDef]
@runtime_checkable @runtime_checkable
class Connectors(Protocol): class Connectors(Protocol):
# NOTE: Route order matters! More specific routes must come before less specific ones.
# Routes with {param:path} are greedy and will match everything including slashes.
@webmethod(route="/connectors", method="GET", level=LLAMA_STACK_API_V1ALPHA) @webmethod(route="/connectors", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def list_connectors( async def list_connectors(
self, self,
@ -118,19 +132,38 @@ class Connectors(Protocol):
""" """
... ...
@webmethod(route="/connectors/{connector_id:path}", method="GET", level=LLAMA_STACK_API_V1ALPHA) @webmethod(route="/connectors/registries", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def get_connector( async def list_registries(self) -> ListRegistriesResponse:
self, """List all registries.
connector_id: str,
) -> Connector:
"""Get a connector by its ID.
:param connector_id: The ID of the connector to get. :returns: A ListRegistriesResponse.
:returns: A Connector.
""" """
... ...
@webmethod(route="/connectors/{connector_id:path}/tools", method="GET", level=LLAMA_STACK_API_V1ALPHA) @webmethod(route="/connectors/registries/{registry_id}", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def get_registry(self, registry_id: str) -> Registry:
"""Get a registry by its ID.
:param registry_id: The ID of the registry to get.
:returns: A Registry.
"""
...
@webmethod(route="/connectors/{connector_id}/tools/{tool_name}", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def get_connector_tool(
self,
connector_id: str,
tool_name: str,
) -> ToolDef:
"""Get a tool definition by its name from a connector.
:param connector_id: The ID of the connector to get the tool from.
:param tool_name: The name of the tool to get.
:returns: A ToolDef.
"""
...
@webmethod(route="/connectors/{connector_id}/tools", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def list_connector_tools( async def list_connector_tools(
self, self,
connector_id: str, connector_id: str,
@ -142,35 +175,15 @@ class Connectors(Protocol):
""" """
... ...
@webmethod( @webmethod(route="/connectors/{connector_id}", method="GET", level=LLAMA_STACK_API_V1ALPHA)
route="/connectors/{connector_id:path}/tools/{tool_name:path}", method="GET", level=LLAMA_STACK_API_V1ALPHA async def get_connector(
)
async def get_connector_tool(
self, self,
connector_id: str, connector_id: str,
tool_name: str, include_tools: bool = False,
) -> MCPListToolsTool: ) -> Connector:
"""Get a tool definition by its name from a connector. """Get a connector by its ID.
:param connector_id: The ID of the connector to get the tool from. :param connector_id: The ID of the connector to get.
:param tool_name: The name of the tool to get. :returns: A Connector.
:returns: A MCPListToolsTool.
"""
...
@webmethod(route="/connectors/registries", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def list_registries(self) -> ListRegistriesResponse:
"""List all registries.
:returns: A ListRegistriesResponse.
"""
...
@webmethod(route="/connectors/registries/{registry_id:path}", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def get_registry(self, registry_id: str) -> Registry:
"""Get a registry by its ID.
:param registry_id: The ID of the registry to get.
:returns: A Registry.
""" """
... ...