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:
$ref: '#/components/schemas/Connector'
'400':
description: Bad Request
$ref: '#/components/responses/BadRequest400'
description: Bad Request
'429':
description: Too Many Requests
$ref: '#/components/responses/TooManyRequests429'
description: Too Many Requests
'500':
description: Internal Server Error
$ref: '#/components/responses/InternalServerError500'
description: Internal Server Error
default:
description: Default Response
$ref: '#/components/responses/DefaultError'
description: Default Response
tags:
- Connectors
summary: Get Connector
description: Get a connector by its ID.
operationId: get_connector_v1alpha_connectors__connector_id__get
parameters:
- name: include_tools
in: query
required: false
schema:
type: boolean
default: false
title: Include Tools
- name: connector_id
in: path
required: true
@ -3930,11 +3937,11 @@ paths:
get:
responses:
'200':
description: A MCPListToolsTool.
description: A ToolDef.
content:
application/json:
schema:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
'400':
description: Bad Request
$ref: '#/components/responses/BadRequest400'
@ -11884,7 +11891,7 @@ components:
tools:
anyOf:
- items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
- type: 'null'
description: List of tools available from the connector
@ -12026,7 +12033,7 @@ components:
properties:
data:
items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
title: Data
type: object
@ -12975,6 +12982,33 @@ components:
- url
title: RegistryInput
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:
description: Input for creating a connector.
properties:
@ -12991,6 +13025,19 @@ components:
description: URL of the connector
title: Url
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:
- url
title: ConnectorInput
@ -13079,33 +13126,6 @@ components:
- items
title: ConversationItemCreateRequest
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:
description: Enumeration of all available APIs in the Llama Stack system.
enum:

View file

@ -8672,7 +8672,7 @@ components:
tools:
anyOf:
- items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
- type: 'null'
description: List of tools available from the connector
@ -8814,7 +8814,7 @@ components:
properties:
data:
items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
title: Data
type: object
@ -9763,6 +9763,33 @@ components:
- url
title: RegistryInput
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:
description: Input for creating a connector.
properties:
@ -9779,6 +9806,19 @@ components:
description: URL of the connector
title: Url
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:
- url
title: ConnectorInput
@ -9867,33 +9907,6 @@ components:
- items
title: ConversationItemCreateRequest
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:
description: Enumeration of all available APIs in the Llama Stack system.
enum:

View file

@ -640,23 +640,30 @@ paths:
schema:
$ref: '#/components/schemas/Connector'
'400':
description: Bad Request
$ref: '#/components/responses/BadRequest400'
description: Bad Request
'429':
description: Too Many Requests
$ref: '#/components/responses/TooManyRequests429'
description: Too Many Requests
'500':
description: Internal Server Error
$ref: '#/components/responses/InternalServerError500'
description: Internal Server Error
default:
description: Default Response
$ref: '#/components/responses/DefaultError'
description: Default Response
tags:
- Connectors
summary: Get Connector
description: Get a connector by its ID.
operationId: get_connector_v1alpha_connectors__connector_id__get
parameters:
- name: include_tools
in: query
required: false
schema:
type: boolean
default: false
title: Include Tools
- name: connector_id
in: path
required: true
@ -667,11 +674,11 @@ paths:
get:
responses:
'200':
description: A MCPListToolsTool.
description: A ToolDef.
content:
application/json:
schema:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
'400':
description: Bad Request
$ref: '#/components/responses/BadRequest400'
@ -7725,7 +7732,7 @@ components:
tools:
anyOf:
- items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
- type: 'null'
description: List of tools available from the connector
@ -7855,7 +7862,7 @@ components:
properties:
data:
items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
title: Data
type: object
@ -8735,6 +8742,33 @@ components:
- url
title: RegistryInput
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:
description: Input for creating a connector.
properties:
@ -8751,6 +8785,19 @@ components:
description: URL of the connector
title: Url
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:
- url
title: ConnectorInput
@ -8839,33 +8886,6 @@ components:
- items
title: ConversationItemCreateRequest
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:
description: Enumeration of all available APIs in the Llama Stack system.
enum:

View file

@ -10114,7 +10114,7 @@ components:
tools:
anyOf:
- items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
- type: 'null'
description: List of tools available from the connector
@ -10256,7 +10256,7 @@ components:
properties:
data:
items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
title: Data
type: object
@ -11205,6 +11205,33 @@ components:
- url
title: RegistryInput
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:
description: Input for creating a connector.
properties:
@ -11221,6 +11248,19 @@ components:
description: URL of the connector
title: Url
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:
- url
title: ConnectorInput
@ -11309,33 +11349,6 @@ components:
- items
title: ConversationItemCreateRequest
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:
description: Enumeration of all available APIs in the Llama Stack system.
enum:

View file

@ -3903,23 +3903,30 @@ paths:
schema:
$ref: '#/components/schemas/Connector'
'400':
description: Bad Request
$ref: '#/components/responses/BadRequest400'
description: Bad Request
'429':
description: Too Many Requests
$ref: '#/components/responses/TooManyRequests429'
description: Too Many Requests
'500':
description: Internal Server Error
$ref: '#/components/responses/InternalServerError500'
description: Internal Server Error
default:
description: Default Response
$ref: '#/components/responses/DefaultError'
description: Default Response
tags:
- Connectors
summary: Get Connector
description: Get a connector by its ID.
operationId: get_connector_v1alpha_connectors__connector_id__get
parameters:
- name: include_tools
in: query
required: false
schema:
type: boolean
default: false
title: Include Tools
- name: connector_id
in: path
required: true
@ -3930,11 +3937,11 @@ paths:
get:
responses:
'200':
description: A MCPListToolsTool.
description: A ToolDef.
content:
application/json:
schema:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
'400':
description: Bad Request
$ref: '#/components/responses/BadRequest400'
@ -11884,7 +11891,7 @@ components:
tools:
anyOf:
- items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
- type: 'null'
description: List of tools available from the connector
@ -12026,7 +12033,7 @@ components:
properties:
data:
items:
$ref: '#/components/schemas/MCPListToolsTool'
$ref: '#/components/schemas/ToolDef'
type: array
title: Data
type: object
@ -12975,6 +12982,33 @@ components:
- url
title: RegistryInput
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:
description: Input for creating a connector.
properties:
@ -12991,6 +13025,19 @@ components:
description: URL of the connector
title: Url
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:
- url
title: ConnectorInput
@ -13079,33 +13126,6 @@ components:
- items
title: ConversationItemCreateRequest
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:
description: Enumeration of all available APIs in the Llama Stack system.
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,
Benchmark,
BenchmarkInput,
ConnectorInput,
Dataset,
DatasetInput,
DatasetIO,
@ -429,6 +430,7 @@ class RegisteredResources(BaseModel):
scoring_fns: list[ScoringFnInput] = Field(default_factory=list)
benchmarks: list[BenchmarkInput] = Field(default_factory=list)
tool_groups: list[ToolGroupInput] = Field(default_factory=list)
connectors: list[ConnectorInput] = Field(default_factory=list)
class ServerConfig(BaseModel):

View file

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

View file

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

View file

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

View file

@ -228,3 +228,44 @@ async def invoke_mcp_tool(
content=content,
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)
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):
"""raised when Llama Stack cannot find a referenced conversation"""

View file

@ -6,15 +6,15 @@
from datetime import datetime
from enum import StrEnum
from typing import Literal, Protocol
from typing import Any, Literal, Protocol
from pydantic import BaseModel, Field
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.resource import Resource, ResourceType
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
@ -54,7 +54,9 @@ class Connector(Resource):
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_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")
def _generate_connector_id(self) -> str:
@ -67,6 +69,11 @@ class Connector(Resource):
def connector_id(self) -> str:
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
class ConnectorInput(BaseModel):
@ -75,11 +82,15 @@ class ConnectorInput(BaseModel):
:param connector_type: Type of connector
:param connector_id: Unique identifier for 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_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")
authorization: str | None = Field(default=None, description="OAuth access token for authentication")
@json_schema_type
@ -99,11 +110,14 @@ class ListToolsResponse(BaseModel):
:param data: List of tools
"""
data: list[MCPListToolsTool]
data: list[ToolDef]
@runtime_checkable
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)
async def list_connectors(
self,
@ -118,19 +132,38 @@ class Connectors(Protocol):
"""
...
@webmethod(route="/connectors/{connector_id:path}", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def get_connector(
self,
connector_id: str,
) -> Connector:
"""Get a connector by its ID.
@webmethod(route="/connectors/registries", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def list_registries(self) -> ListRegistriesResponse:
"""List all registries.
:param connector_id: The ID of the connector to get.
:returns: A Connector.
:returns: A ListRegistriesResponse.
"""
...
@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(
self,
connector_id: str,
@ -142,35 +175,15 @@ class Connectors(Protocol):
"""
...
@webmethod(
route="/connectors/{connector_id:path}/tools/{tool_name:path}", method="GET", level=LLAMA_STACK_API_V1ALPHA
)
async def get_connector_tool(
@webmethod(route="/connectors/{connector_id}", method="GET", level=LLAMA_STACK_API_V1ALPHA)
async def get_connector(
self,
connector_id: str,
tool_name: str,
) -> MCPListToolsTool:
"""Get a tool definition by its name from a connector.
include_tools: bool = False,
) -> Connector:
"""Get a connector by its ID.
: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 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.
:param connector_id: The ID of the connector to get.
:returns: A Connector.
"""
...