ibm-watsonx-orchestrate-mcp-server 1.13.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- .gitignore +17 -0
- __init__.py +0 -0
- ibm_watsonx_orchestrate_mcp_server/__init__.py +0 -0
- ibm_watsonx_orchestrate_mcp_server/server.py +46 -0
- ibm_watsonx_orchestrate_mcp_server/src/__init__.py +10 -0
- ibm_watsonx_orchestrate_mcp_server/src/agents/mcp_tools.py +162 -0
- ibm_watsonx_orchestrate_mcp_server/src/agents/types.py +122 -0
- ibm_watsonx_orchestrate_mcp_server/src/connections/helpers.py +31 -0
- ibm_watsonx_orchestrate_mcp_server/src/connections/mcp_tools.py +149 -0
- ibm_watsonx_orchestrate_mcp_server/src/connections/types.py +113 -0
- ibm_watsonx_orchestrate_mcp_server/src/knowledge_bases/mcp_tools.py +66 -0
- ibm_watsonx_orchestrate_mcp_server/src/models/mcp_tools.py +207 -0
- ibm_watsonx_orchestrate_mcp_server/src/models/types.py +60 -0
- ibm_watsonx_orchestrate_mcp_server/src/toolkits/mcp_tools.py +67 -0
- ibm_watsonx_orchestrate_mcp_server/src/toolkits/types.py +19 -0
- ibm_watsonx_orchestrate_mcp_server/src/tools/mcp_tools.py +155 -0
- ibm_watsonx_orchestrate_mcp_server/src/version_checker.py +7 -0
- ibm_watsonx_orchestrate_mcp_server/src/voice_configurations/mcp_tools.py +52 -0
- ibm_watsonx_orchestrate_mcp_server/utils/common.py +108 -0
- ibm_watsonx_orchestrate_mcp_server/utils/config/__init__.py +2 -0
- ibm_watsonx_orchestrate_mcp_server/utils/config/config.py +91 -0
- ibm_watsonx_orchestrate_mcp_server/utils/config/types.py +6 -0
- ibm_watsonx_orchestrate_mcp_server/utils/files/files.py +14 -0
- ibm_watsonx_orchestrate_mcp_server/utils/logging/__init__.py +1 -0
- ibm_watsonx_orchestrate_mcp_server/utils/logging/log_config.yaml +19 -0
- ibm_watsonx_orchestrate_mcp_server/utils/logging/logger.py +38 -0
- ibm_watsonx_orchestrate_mcp_server-1.13.0b0.dist-info/METADATA +12 -0
- ibm_watsonx_orchestrate_mcp_server-1.13.0b0.dist-info/RECORD +31 -0
- ibm_watsonx_orchestrate_mcp_server-1.13.0b0.dist-info/WHEEL +4 -0
- ibm_watsonx_orchestrate_mcp_server-1.13.0b0.dist-info/entry_points.txt +2 -0
- pyproject.toml +39 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
from typing import List, Literal, Optional
|
2
|
+
from ibm_watsonx_orchestrate.cli.commands.knowledge_bases.knowledge_bases_controller import KnowledgeBaseController
|
3
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats
|
4
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.common import silent_call
|
5
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.files.files import get_working_directory_path
|
6
|
+
|
7
|
+
def list_knowledge_bases(verbose:bool=False)-> List[dict]:
|
8
|
+
"""
|
9
|
+
Lists the avalible knowledge bases available on the watsonx Orchestrate platform.
|
10
|
+
"""
|
11
|
+
kbc: KnowledgeBaseController = KnowledgeBaseController()
|
12
|
+
format: Literal[ListFormats.JSON] | None = ListFormats.JSON if not verbose else None
|
13
|
+
knowledge_bases: List[dict] = silent_call(fn=kbc.list_knowledge_bases, verbose=verbose, format=format)
|
14
|
+
return knowledge_bases
|
15
|
+
|
16
|
+
def import_knowledge_bases(file_path: str, app_id: Optional[str] = None) -> str:
|
17
|
+
"""
|
18
|
+
Import a knowledge base from a spec file into the watsonx Orchestrate platform.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
file_path (str): The path to the knowledge base spec file.
|
22
|
+
app_id (str, optional): The app id of a connection in the watsonx Orchestrate platform. Used to authenticate to external knowledge base systems like Milvus or Elastic Search. Defaults to None.
|
23
|
+
|
24
|
+
Returns:
|
25
|
+
str: A success message indicating the knowledge base was imported successfully.
|
26
|
+
"""
|
27
|
+
working_directory_file_path: str = get_working_directory_path(file_path)
|
28
|
+
|
29
|
+
kbc: KnowledgeBaseController = KnowledgeBaseController()
|
30
|
+
silent_call(fn=kbc.import_knowledge_base, file=working_directory_file_path, app_id=app_id)
|
31
|
+
return "Knowledge base imported successfully."
|
32
|
+
|
33
|
+
def remove_knowledge_base(id: Optional[str] = None, name: Optional[str] = None) -> str:
|
34
|
+
"""
|
35
|
+
Remove a knowledge base from the watsonx Orchestrate platform.
|
36
|
+
Requires either a name or id to identify the knowledge base to remove. If both are provided, the id will be used.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
id (str, optional): The id of the knowledge base to remove. Defaults to None.
|
40
|
+
name (str, optional): The name of the knowledge base to remove. Defaults to None.
|
41
|
+
|
42
|
+
Returns:
|
43
|
+
"""
|
44
|
+
kbc: KnowledgeBaseController = KnowledgeBaseController()
|
45
|
+
silent_call(fn=kbc.remove_knowledge_base, id=id, name=name)
|
46
|
+
identifier: str | None = id if id else name
|
47
|
+
return f"Knowledge base '{identifier}' removed successfully."
|
48
|
+
|
49
|
+
def check_knowledge_base_status(id: Optional[str] = None, name: Optional[str] = None) -> dict:
|
50
|
+
"""
|
51
|
+
Check the status of a knowledge base in the watsonx Orchestrate platform.
|
52
|
+
This will show if the knowledge base has been indexed and if ready to be used.
|
53
|
+
Requires either a name or id to identify the knowledge base to check. If both are provided, the id will be used.
|
54
|
+
|
55
|
+
Args:
|
56
|
+
id (str, optional): The id of the knowledge base to check. Defaults to None.
|
57
|
+
name (str, optional): The name of the knowledge base to check. Defaults to None.
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
str: The status of the knowledge base.
|
61
|
+
"""
|
62
|
+
kbc: KnowledgeBaseController = KnowledgeBaseController()
|
63
|
+
knowledge_base: dict = silent_call(fn=kbc.knowledge_base_status, id=id, name=name, format=ListFormats.JSON)
|
64
|
+
return knowledge_base
|
65
|
+
|
66
|
+
__tools__ = [list_knowledge_bases, import_knowledge_bases, remove_knowledge_base, check_knowledge_base_status]
|
@@ -0,0 +1,207 @@
|
|
1
|
+
from typing import Any, List, Literal, Optional
|
2
|
+
from ibm_watsonx_orchestrate.agent_builder.model_policies.types import ModelPolicy
|
3
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats
|
4
|
+
from ibm_watsonx_orchestrate_mcp_server.src.connections.helpers import ConnectionsHelper
|
5
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.common import silent_call
|
6
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.files.files import get_working_directory_path
|
7
|
+
from ibm_watsonx_orchestrate.agent_builder.models.types import ModelListEntry, VirtualModel, ListVirtualModel, ModelType
|
8
|
+
from ibm_watsonx_orchestrate.cli.commands.models.models_controller import ModelsController
|
9
|
+
from ibm_watsonx_orchestrate_mcp_server.src.models.types import CreateModelOptions, CreateModelPolicyOptions
|
10
|
+
|
11
|
+
def __format_model_name(model_name: str) -> str:
|
12
|
+
return f"virtual-model/{model_name}" if not model_name.startswith("virtual-model/") else model_name
|
13
|
+
|
14
|
+
def __format_model_policy_name(policy_name: str) -> str:
|
15
|
+
return f"virtual-policy/{policy_name}" if not policy_name.startswith("virtual-policy/") else policy_name
|
16
|
+
|
17
|
+
def list_models() -> List[ModelListEntry]:
|
18
|
+
"""
|
19
|
+
List all models and model policies in the watsonx Orchestrate platform.
|
20
|
+
Custom models will being with the prefix "virtual-model/".
|
21
|
+
Model policies will being with the prefix "virtual-policy/"
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
List[ModelListEntry]: A list of models configurations.
|
25
|
+
"""
|
26
|
+
mc: ModelsController = ModelsController()
|
27
|
+
models = silent_call(fn=mc.list_models, format=ListFormats.JSON)
|
28
|
+
return models
|
29
|
+
|
30
|
+
def import_model(file_path: str, app_id: Optional[str]=None) -> List[VirtualModel]:
|
31
|
+
"""
|
32
|
+
Import a model in the watsonx Orchestrate platform
|
33
|
+
Args:
|
34
|
+
file_path (str): Path to the model file.
|
35
|
+
app_id (str, optional): The app_id of a key_value connection containing secrets for model authentication such as api_key. These values are merged with the provider_config field in the spec and provide a secure way of passing secret data to the model config. Defaults to None.
|
36
|
+
Returns:
|
37
|
+
List[VirtualModel]: The response from the watsonx Orchestrate platform.
|
38
|
+
"""
|
39
|
+
working_directory_path = get_working_directory_path(file_path)
|
40
|
+
mc: ModelsController = ModelsController()
|
41
|
+
models = silent_call(
|
42
|
+
fn=mc.import_model,
|
43
|
+
file=working_directory_path,
|
44
|
+
app_id=app_id
|
45
|
+
)
|
46
|
+
for model in models:
|
47
|
+
silent_call(
|
48
|
+
fn=mc.publish_or_update_models,
|
49
|
+
model=model,
|
50
|
+
)
|
51
|
+
return models
|
52
|
+
|
53
|
+
def create_or_update_model(options: CreateModelOptions) -> VirtualModel:
|
54
|
+
"""
|
55
|
+
Create a model in the watsonx Orchestrate platform. If a model with the same name already exists update it instead.
|
56
|
+
Args:
|
57
|
+
options (CreateModelOptions): The options for creating the model.
|
58
|
+
Returns:
|
59
|
+
VirtualModel: The created model.
|
60
|
+
"""
|
61
|
+
mc: ModelsController = ModelsController()
|
62
|
+
models_client = silent_call(fn=mc.get_models_client)
|
63
|
+
existing_name: str = __format_model_name(options.name)
|
64
|
+
existing_models = silent_call(fn=models_client.get_draft_by_name, model_name=existing_name)
|
65
|
+
provider_config_dict: dict[str, Any] | None = options.provider_config.model_dump() if options.provider_config else None
|
66
|
+
|
67
|
+
existing_model_data: ListVirtualModel | None = None
|
68
|
+
if len(existing_models) == 1:
|
69
|
+
existing_model_data = existing_models[0]
|
70
|
+
elif len(existing_models) > 1:
|
71
|
+
raise Exception(f"Multiple models found with name {options.name}. Update is ambiguous. Please delete the duplicate and try again.")
|
72
|
+
|
73
|
+
if not existing_model_data:
|
74
|
+
model = silent_call(
|
75
|
+
fn=mc.create_model,
|
76
|
+
name=options.name,
|
77
|
+
description=options.description,
|
78
|
+
display_name=options.display_name,
|
79
|
+
provider_config_dict=provider_config_dict,
|
80
|
+
model_type=options.type,
|
81
|
+
app_id=options.app_id
|
82
|
+
)
|
83
|
+
else:
|
84
|
+
connections_helper: ConnectionsHelper = ConnectionsHelper()
|
85
|
+
existing_app_id: str | None = None
|
86
|
+
if existing_model_data.connection_id:
|
87
|
+
existing_app_id = connections_helper.get_app_id_by_connection_id(existing_model_data.connection_id)
|
88
|
+
existing_model_provider_config: dict[str, Any] | None = existing_model_data.provider_config.model_dump() if existing_model_data.provider_config else None
|
89
|
+
existing_model_type: str | Literal[ModelType.CHAT] = existing_model_data.model_type if existing_model_data.model_type else ModelType.CHAT
|
90
|
+
existing_model = silent_call(
|
91
|
+
fn=mc.create_model,
|
92
|
+
name=existing_model_data.name,
|
93
|
+
description=existing_model_data.description,
|
94
|
+
display_name=existing_model_data.display_name,
|
95
|
+
provider_config_dict=existing_model_provider_config,
|
96
|
+
model_type=existing_model_type,
|
97
|
+
app_id=existing_app_id
|
98
|
+
)
|
99
|
+
|
100
|
+
options.name = existing_name
|
101
|
+
|
102
|
+
model = existing_model.model_copy(update=options.model_dump(exclude_unset=True))
|
103
|
+
|
104
|
+
|
105
|
+
silent_call(
|
106
|
+
fn=mc.publish_or_update_models,
|
107
|
+
model=model,
|
108
|
+
)
|
109
|
+
return model
|
110
|
+
|
111
|
+
def remove_model(name: str) -> str:
|
112
|
+
"""
|
113
|
+
Remove a model from the watsonx Orchestrate platform.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
name (str): The name of the model to remove.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
str: A message indicating the success of the removal operation.
|
120
|
+
"""
|
121
|
+
model_name: str = __format_model_name(model_name=name)
|
122
|
+
mc: ModelsController = ModelsController()
|
123
|
+
silent_call(fn=mc.remove_model, name=model_name)
|
124
|
+
return f"Successfully removed model '{model_name}'."
|
125
|
+
|
126
|
+
def import_model_policy(file_path: str) -> List[ModelPolicy]:
|
127
|
+
"""
|
128
|
+
Import a model policy from a file into the watsonx Orchestrate platform.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
file_path (str): The path to the file containing the model policy.
|
132
|
+
|
133
|
+
Returns:
|
134
|
+
List[ModelPolicy]: A list of ModelPolicy objects representing the imported model policy.
|
135
|
+
"""
|
136
|
+
working_directory_path = get_working_directory_path(file_path)
|
137
|
+
mc: ModelsController = ModelsController()
|
138
|
+
policies = silent_call(
|
139
|
+
fn=mc.import_model_policy,
|
140
|
+
file=working_directory_path
|
141
|
+
)
|
142
|
+
for policy in policies:
|
143
|
+
silent_call(
|
144
|
+
fn=mc.publish_or_update_model_policies,
|
145
|
+
policy=policy,
|
146
|
+
)
|
147
|
+
return policies
|
148
|
+
|
149
|
+
def create_or_update_model_policy(options: CreateModelPolicyOptions) -> ModelPolicy:
|
150
|
+
"""
|
151
|
+
Create or update a model policy in the watsonx Orchestrate platform. If a model policy with the name is found it will update else it will create a new one.
|
152
|
+
|
153
|
+
Args:
|
154
|
+
options (CreateModelPolicyOptions): The options for creating or updating the model policy.
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
ModelPolicy: The created or updated model policy.
|
158
|
+
"""
|
159
|
+
mc: ModelsController = ModelsController()
|
160
|
+
model_policies_client = silent_call(fn=mc.get_model_policies_client)
|
161
|
+
existing_name: str = __format_model_policy_name(options.name)
|
162
|
+
existing_policies = silent_call(fn=model_policies_client.get_draft_by_name, policy_name=existing_name)
|
163
|
+
|
164
|
+
existing_policy: ModelPolicy | None = None
|
165
|
+
if len(existing_policies) == 1:
|
166
|
+
existing_policy = existing_policies[0]
|
167
|
+
elif len(existing_policies) > 1:
|
168
|
+
raise Exception(f"Multiple model policies found with name {options.name}. Update is ambiguous. Please delete the duplicate and try again.")
|
169
|
+
|
170
|
+
if not existing_policy:
|
171
|
+
model = silent_call(
|
172
|
+
fn=mc.create_model_policy,
|
173
|
+
name=options.name,
|
174
|
+
description=options.description,
|
175
|
+
display_name=options.display_name,
|
176
|
+
models=options.models,
|
177
|
+
strategy=options.strategy,
|
178
|
+
strategy_on_code=options.strategy_on_code,
|
179
|
+
retry_on_code=options.retry_on_code,
|
180
|
+
retry_attempts=options.retry_attempts
|
181
|
+
)
|
182
|
+
else:
|
183
|
+
model: ModelPolicy = existing_policy.model_copy(update=options.model_dump(exclude_unset=True))
|
184
|
+
|
185
|
+
|
186
|
+
silent_call(
|
187
|
+
fn=mc.publish_or_update_models,
|
188
|
+
model=model,
|
189
|
+
)
|
190
|
+
return model
|
191
|
+
|
192
|
+
def remove_model_policy(name: str) -> str:
|
193
|
+
"""
|
194
|
+
Remove a model policy from the watsonx Orchestrate platform
|
195
|
+
|
196
|
+
Args:
|
197
|
+
name (str): The name of the model policy to remove.
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
str: A message indicating the success of the removal operation.
|
201
|
+
"""
|
202
|
+
policy_name: str = __format_model_policy_name(policy_name=name)
|
203
|
+
mc: ModelsController = ModelsController()
|
204
|
+
silent_call(fn=mc.remove_policy, name=policy_name)
|
205
|
+
return f"Successfully removed model policy '{policy_name}'."
|
206
|
+
|
207
|
+
__tools__ = [list_models, import_model, create_or_update_model, remove_model, import_model_policy, create_or_update_model_policy, remove_model_policy]
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from pydantic import BaseModel, Field
|
2
|
+
from typing import Optional, List
|
3
|
+
from ibm_watsonx_orchestrate.agent_builder.models.types import ProviderConfig, ModelType
|
4
|
+
from ibm_watsonx_orchestrate.agent_builder.model_policies.types import ModelPolicyStrategyMode
|
5
|
+
|
6
|
+
class CreateModelOptions(BaseModel):
|
7
|
+
name: str = Field(
|
8
|
+
description="Name of the model you wish to create. Must be in the format '<provider>/<model_name>'"
|
9
|
+
)
|
10
|
+
description: Optional[str] = Field(
|
11
|
+
default=None,
|
12
|
+
description="Description of the model you wish to create."
|
13
|
+
)
|
14
|
+
display_name: Optional[str] = Field(
|
15
|
+
default=None,
|
16
|
+
description="A human readable name for the model."
|
17
|
+
)
|
18
|
+
provider_config: Optional[ProviderConfig] = Field(
|
19
|
+
default=None,
|
20
|
+
description="Provider specific configuration for the model such as connection details or cloud resource identifiers."
|
21
|
+
)
|
22
|
+
app_id: Optional[str] = Field(
|
23
|
+
default=None,
|
24
|
+
description="The app_id of a key_value connection containing secrets for model authentication such as api_key. These values are merged with the provider_config and provide a secure way of passing secret data to the model config"
|
25
|
+
)
|
26
|
+
type: ModelType = Field(
|
27
|
+
default=ModelType.CHAT,
|
28
|
+
description="The type of model being created. Defaults to 'chat'"
|
29
|
+
)
|
30
|
+
|
31
|
+
class CreateModelPolicyOptions(BaseModel):
|
32
|
+
name: str = Field(
|
33
|
+
description="Name of the model policy you wish to create."
|
34
|
+
)
|
35
|
+
description: Optional[str] = Field(
|
36
|
+
default=None,
|
37
|
+
description="Description of the model policy you wish to create."
|
38
|
+
)
|
39
|
+
display_name: Optional[str] = Field(
|
40
|
+
default=None,
|
41
|
+
description="A human readable display name for the model policy."
|
42
|
+
)
|
43
|
+
models: List[str] = Field(
|
44
|
+
description="List of models to be used by the policy."
|
45
|
+
)
|
46
|
+
strategy: ModelPolicyStrategyMode = Field(
|
47
|
+
description="How the policy will handle model selection. 'fallback' will select the first model in the list and move down the list if that errors. 'loadbalance' will share request between all the models."
|
48
|
+
)
|
49
|
+
retry_attempts: Optional[int] = Field(
|
50
|
+
default=None,
|
51
|
+
description="Number of times a model will be retried before failing."
|
52
|
+
)
|
53
|
+
strategy_on_code: Optional[List[int]] = Field(
|
54
|
+
default=None,
|
55
|
+
description="List of HTTP status codes to envoke the strategy on. Primarily used by fallback to specify what HTTP codes trigger a different model to be called."
|
56
|
+
)
|
57
|
+
retry_on_code: Optional[List[int]] = Field(
|
58
|
+
default=None,
|
59
|
+
description="List of HTTP status codes that should trigger a retry."
|
60
|
+
)
|
@@ -0,0 +1,67 @@
|
|
1
|
+
from typing import List, Literal, Optional
|
2
|
+
from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_command import import_toolkit as import_toolkit_command
|
3
|
+
from ibm_watsonx_orchestrate.cli.commands.toolkit.toolkit_controller import ToolkitController
|
4
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats
|
5
|
+
|
6
|
+
from ibm_watsonx_orchestrate_mcp_server.src.toolkits.types import ImportToolKitOptions
|
7
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.common import silent_call
|
8
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.files.files import get_working_directory_path
|
9
|
+
|
10
|
+
def list_toolkits(verbose:bool=False)-> List[dict]:
|
11
|
+
"""
|
12
|
+
Lists the avalible toolkits (MCP Servers) available on the watsonx Orchestrate platform.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
verbose (bool, optional): Return verbose information without processing. Should only be used for getting extra details. Defaults to False.
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
List[dict]: A list of dictionaries containing information about the toolkits available on the watsonx Orchestrate platform.
|
19
|
+
"""
|
20
|
+
tc: ToolkitController = ToolkitController()
|
21
|
+
format: Literal[ListFormats.JSON] | None = ListFormats.JSON if not verbose else None
|
22
|
+
tools: List[dict] = silent_call(fn=tc.list_toolkits, verbose=verbose, format=format)
|
23
|
+
return tools
|
24
|
+
|
25
|
+
def import_toolkit(options: ImportToolKitOptions) -> str:
|
26
|
+
"""
|
27
|
+
Import a toolkit (MCP server) into the watsonx Orchestrate platform.
|
28
|
+
|
29
|
+
Args:
|
30
|
+
options (ImportToolKitOptions): The options required to import a toolkit into the watsonx Orchestrate platform
|
31
|
+
|
32
|
+
Returns:
|
33
|
+
str: A success message indicating the toolkit was successfully imported
|
34
|
+
"""
|
35
|
+
working_directory_package_root: str | None = get_working_directory_path(options.package_root) if options.package_root else None
|
36
|
+
tools: Optional[str] = ",".join(options.tools) if options.tools else None
|
37
|
+
silent_call(
|
38
|
+
fn=import_toolkit_command,
|
39
|
+
kind=options.kind,
|
40
|
+
name=options.name,
|
41
|
+
description=options.description,
|
42
|
+
package=options.package,
|
43
|
+
package_root=working_directory_package_root,
|
44
|
+
language=options.language,
|
45
|
+
command=options.command,
|
46
|
+
url=options.url,
|
47
|
+
transport=options.transport,
|
48
|
+
tools=tools,
|
49
|
+
app_id=options.app_id
|
50
|
+
)
|
51
|
+
return f"The toolking {options.name} has been imported successfully"
|
52
|
+
|
53
|
+
def remove_toolkit(name: str)-> str:
|
54
|
+
"""
|
55
|
+
Remove a toolkit (MCP server) from the watsonx Orchestrate platform.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
name (str): The name of the toolkit to remove
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
str: A success message indicating the toolkit was successfully removed
|
62
|
+
"""
|
63
|
+
tc: ToolkitController = ToolkitController()
|
64
|
+
silent_call(fn=tc.remove_toolkit, name=name)
|
65
|
+
return f"The toolkit {name} has been removed successfully"
|
66
|
+
|
67
|
+
__tools__ = [list_toolkits, import_toolkit, remove_toolkit]
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from ibm_watsonx_orchestrate.agent_builder.toolkits.types import Language, ToolkitKind, ToolkitTransportKind
|
2
|
+
from pydantic import BaseModel, Field
|
3
|
+
from typing import List, Optional
|
4
|
+
|
5
|
+
class ImportToolKitOptions(BaseModel):
|
6
|
+
"""
|
7
|
+
Options for importing a toolkit (MCP server) into the watsonx Orchestrate platform.
|
8
|
+
"""
|
9
|
+
kind: ToolkitKind = Field(description="Kind of toolkit. Allowed values ['mcp']")
|
10
|
+
name: str = Field(description="Arbitrary name to identify the toolkit. It should be unique from other toolkits")
|
11
|
+
description: str = Field(description ="A description if the toolkit and its contents")
|
12
|
+
package: Optional[str] = Field(default=None, description="Name of the package in the npm or pypi repository. Used when pulling the MCP server files from a public repository. Cannot be used with the 'package-root' option")
|
13
|
+
package_root: Optional[str] = Field(default=None, description="File path to the root of the MCP Server or a zip file with the MCP Server. Used when uploading the MCP server files from the local machine. Cannot be used with the 'package'")
|
14
|
+
language: Optional[Language] = Field(default=None, description="The language of the toolkit. Used to infer a default 'command' when using the package option (For node its 'npx -y <package>' and for python its 'python -m <package>')")
|
15
|
+
command: Optional[str] = Field(default=None, description="Command used to start the MCP server")
|
16
|
+
url: Optional[str] = Field(default=None, description="URL to the MCP server. Used when using a remote MCP server via transports 'streamable_http' or 'sse'")
|
17
|
+
transport: Optional[ToolkitTransportKind] = Field(default=None, description="Used for remote MCP server. Valid options are 'streamable_http' or 'sse'. For 'stdio' leave this as null and specify either the 'package' or 'package-root' options")
|
18
|
+
tools: Optional[List[str]] = Field(default=["*"], description="List of tools to be used with the MCP server. '[*]' means all tools on the server will be imported. Additionally, null will also cause all tools to be imported.")
|
19
|
+
app_id: Optional[List[str]] = Field(default=None, description="List of app ids related to connections in the Orchestrate platform. For 'stdio'/local these should be key_value connections which will be exposed as env vars for the server. For remote MCP servers these will be used to authenticate requests and can be other kinds like basic or api key auth.")
|
@@ -0,0 +1,155 @@
|
|
1
|
+
from typing import Literal
|
2
|
+
from typing_extensions import List, Optional
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from tempfile import TemporaryDirectory
|
6
|
+
from ibm_watsonx_orchestrate.agent_builder.tools import BaseTool
|
7
|
+
from ibm_watsonx_orchestrate.cli.commands.tools.tools_controller import ToolKind, ToolsController
|
8
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats
|
9
|
+
|
10
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.files.files import get_working_directory_path
|
11
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.common import silent_call
|
12
|
+
|
13
|
+
# Pseudo-resources
|
14
|
+
def get_tool_template() -> str:
|
15
|
+
"""
|
16
|
+
Get a string template with placeholders for the format of a watsonx Orchestrate tool.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
str: The structure required for a watsonx Orchestrate python tool. Template placeholders are marked in angled brackets '<>'.
|
20
|
+
"""
|
21
|
+
return """
|
22
|
+
from ibm_watsonx_orchestrate.agent_builder.tools import tool
|
23
|
+
<Any additional imports>
|
24
|
+
|
25
|
+
@tool(
|
26
|
+
expected_credentials = [
|
27
|
+
{"app_id": <An identifier used to reference the credentials>, "type": <A 'ConnectionType' or list of 'ConnectionType' specifying what type of credentials the tool supports>}
|
28
|
+
] # expected_credentials is optional and is only needed when tools make use of credentials/connections
|
29
|
+
)
|
30
|
+
def <tool_name>(<arguments with type hints>) -> <return type hint>:
|
31
|
+
<python code>
|
32
|
+
"""
|
33
|
+
|
34
|
+
def create_tool(content: str, app_id: Optional[str] = None, requirements_filepath: Optional[str] = None) -> BaseTool:
|
35
|
+
"""
|
36
|
+
Create a watsonx Orchestrate tool from a string of python code. The string should follow the pattern outlined in the template that can be gotten from the tool 'get_tool_template'.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
content (str): Valid python code that conforms to the schema found using the tool 'get_tool_template'
|
40
|
+
app_id (str | None): An optional app_id which relates an Orchestrate connection object to the tool. Used to provide credentials for the tool to make use of. Should match the app id of the expect_credentials object.
|
41
|
+
requirements_filepath (str | None): An optional path to a requirements.txt file for python tools to allow for external packages avalible on pypi to be used.
|
42
|
+
|
43
|
+
Returns:
|
44
|
+
str: The constructed tool.
|
45
|
+
"""
|
46
|
+
working_directory = get_working_directory_path(".")
|
47
|
+
working_directory_requirements_path: str | None = get_working_directory_path(requirements_filepath) if requirements_filepath else None
|
48
|
+
|
49
|
+
with TemporaryDirectory(dir=working_directory) as tmp_dir:
|
50
|
+
tool_file_path = Path(tmp_dir) / "temp_tool.py"
|
51
|
+
tool_file_path.write_text(content)
|
52
|
+
|
53
|
+
tc: ToolsController = ToolsController(tool_kind=ToolKind.python, file=str(tool_file_path), requirements_file=working_directory_requirements_path)
|
54
|
+
tools: List[BaseTool] = silent_call(
|
55
|
+
fn=tc.import_tool,
|
56
|
+
kind=ToolKind.python,
|
57
|
+
file=str(tool_file_path),
|
58
|
+
app_id=app_id,
|
59
|
+
requirements_file=working_directory_requirements_path,
|
60
|
+
package_root=None,
|
61
|
+
)
|
62
|
+
tools_list: list[BaseTool] = list(tools)
|
63
|
+
silent_call(tc.publish_or_update_tools, tools_list, package_root=None)
|
64
|
+
|
65
|
+
if len(tools_list):
|
66
|
+
return tools_list[0]
|
67
|
+
else:
|
68
|
+
raise Exception("No valid tools created")
|
69
|
+
|
70
|
+
|
71
|
+
def import_tool(kind: ToolKind, path: str, app_id: Optional[str] = None, requirements_filepath: Optional[str] = None, package_root: Optional[str] = None) -> List[BaseTool]:
|
72
|
+
"""
|
73
|
+
Import a tool into the watsonx Orchestrate platform
|
74
|
+
|
75
|
+
Args:
|
76
|
+
kind (ToolKind): The kind of tool to import. Valid options are ['python' | 'openapi' | 'flow']
|
77
|
+
path (str): The path to the tool definition file.
|
78
|
+
app_id (str | None): An optional app_id which relates an Orchestrate connection object to the tool. Used to provide credentials for the tool to make use of.
|
79
|
+
requirements_filepath (str | None): An optional path to a requirements.txt file for python tools.
|
80
|
+
package_root (str | None): An optional path to a packageroot for python tools with multiple files.
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
List[BaseTool]: The newly created tools.
|
84
|
+
"""
|
85
|
+
|
86
|
+
working_directory_path: str = get_working_directory_path(path)
|
87
|
+
working_directory_requirements_path: str | None = get_working_directory_path(requirements_filepath) if requirements_filepath else None
|
88
|
+
working_directory_package_root: str | None = get_working_directory_path(package_root) if package_root else None
|
89
|
+
|
90
|
+
tc: ToolsController = ToolsController(tool_kind=kind, file=working_directory_path, requirements_file=working_directory_requirements_path)
|
91
|
+
tools: List[BaseTool] = silent_call(
|
92
|
+
fn=tc.import_tool,
|
93
|
+
kind=kind,
|
94
|
+
file=working_directory_path,
|
95
|
+
app_id=app_id,
|
96
|
+
requirements_file=working_directory_requirements_path,
|
97
|
+
package_root=working_directory_package_root,
|
98
|
+
)
|
99
|
+
tools_list: list[BaseTool] = list(tools)
|
100
|
+
silent_call(tc.publish_or_update_tools, tools_list, package_root=working_directory_package_root)
|
101
|
+
return tools_list
|
102
|
+
|
103
|
+
def list_tools(verbose:bool=False)-> List[dict]:
|
104
|
+
"""
|
105
|
+
Lists the tools available on the watsonx Orchestrate platform.
|
106
|
+
|
107
|
+
Args:
|
108
|
+
verbose (bool, optional): Return verbose information without processing. Should only be used for getting extra details. Defaults to False.
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
List[dict]: A list of dictionaries containing information about the tools available on the watsonx Orchestrate platform.
|
112
|
+
"""
|
113
|
+
tc: ToolsController = ToolsController()
|
114
|
+
format: Literal[ListFormats.JSON] | None = ListFormats.JSON if not verbose else None
|
115
|
+
tools: List[dict] = silent_call(fn=tc.list_tools, verbose=verbose, format=format)
|
116
|
+
return tools
|
117
|
+
|
118
|
+
|
119
|
+
def remove_tool(name: str) -> str:
|
120
|
+
"""
|
121
|
+
Removes a tool from the watsonx Orchestrate platform.
|
122
|
+
|
123
|
+
Args:
|
124
|
+
name (str): The name of the tool to remove.
|
125
|
+
|
126
|
+
Returns:
|
127
|
+
str: A message indicating the success of the removal
|
128
|
+
"""
|
129
|
+
|
130
|
+
tc: ToolsController = ToolsController()
|
131
|
+
silent_call(
|
132
|
+
fn=tc.remove_tool,
|
133
|
+
name=name
|
134
|
+
)
|
135
|
+
|
136
|
+
return f"The tool {name} has been removed successfully"
|
137
|
+
|
138
|
+
def export_tool(name: str, output_file_path: str) -> str:
|
139
|
+
"""
|
140
|
+
Exports a tool from the watsonx Orchestrate platform.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
name (str): The name of the tool to export
|
144
|
+
output_file_path (str): The path to where the tool should create the output file. Should have a .zip extension
|
145
|
+
"""
|
146
|
+
working_directory_output_path: str = get_working_directory_path(output_file_path)
|
147
|
+
tc: ToolsController = ToolsController()
|
148
|
+
silent_call(
|
149
|
+
fn=tc.export_tool,
|
150
|
+
name=name,
|
151
|
+
output_path=working_directory_output_path
|
152
|
+
)
|
153
|
+
return f"The tool {name} has been exported successfully"
|
154
|
+
|
155
|
+
__tools__ = [get_tool_template, import_tool, list_tools, remove_tool, export_tool, create_tool]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
from typing import Any, List, Literal
|
2
|
+
from ibm_watsonx_orchestrate.agent_builder.voice_configurations import VoiceConfiguration
|
3
|
+
from ibm_watsonx_orchestrate.agent_builder.voice_configurations.types import VoiceConfigurationListEntry
|
4
|
+
from ibm_watsonx_orchestrate.cli.commands.voice_configurations.voice_configurations_controller import VoiceConfigurationsController
|
5
|
+
from ibm_watsonx_orchestrate.cli.common import ListFormats
|
6
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.common import silent_call
|
7
|
+
from ibm_watsonx_orchestrate_mcp_server.utils.files.files import get_working_directory_path
|
8
|
+
|
9
|
+
def list_voice_configs(verbose: bool = False) -> List[VoiceConfigurationListEntry] | List[dict[str, Any]]:
|
10
|
+
"""
|
11
|
+
List voice configurations in the watsonx Orchestrate platform
|
12
|
+
|
13
|
+
Args:
|
14
|
+
verbose (bool, optional): Return verbose information without processing. Should only be used for getting extra details. Defaults to False.
|
15
|
+
|
16
|
+
Returns:
|
17
|
+
List[VoiceConfigurationListEntry]: A list of voice configurations.
|
18
|
+
"""
|
19
|
+
vcc: VoiceConfigurationsController = VoiceConfigurationsController()
|
20
|
+
format: Literal[ListFormats.JSON] | None = ListFormats.JSON if not verbose else None
|
21
|
+
voice_configs = silent_call(fn=vcc.list_voice_configs, format=format, verbose=verbose)
|
22
|
+
return voice_configs
|
23
|
+
|
24
|
+
def import_voice_config(file_path: str) -> VoiceConfiguration:
|
25
|
+
"""
|
26
|
+
Imports voice configurations from a spec file into the watsonx Orchestrate platform.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
file_path (str): The path to the voice configuration spec file.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
VoiceConfiguration: The voice configuration imported from the spec file.
|
33
|
+
"""
|
34
|
+
working_directory_file_path: str = get_working_directory_path(file_path)
|
35
|
+
vcc: VoiceConfigurationsController = VoiceConfigurationsController()
|
36
|
+
voice_config = silent_call(fn=vcc.import_voice_config, file=working_directory_file_path)
|
37
|
+
silent_call(fn=vcc.publish_or_update_voice_config, voice_config=voice_config)
|
38
|
+
return voice_config
|
39
|
+
|
40
|
+
def remove_voice_config(voice_config_name: str) -> str:
|
41
|
+
"""
|
42
|
+
Removes a voice configuration from the watsonx Orchestrate platform.
|
43
|
+
Args:
|
44
|
+
voice_config_name (str): The name of the voice configuration to be removed.
|
45
|
+
Returns:
|
46
|
+
str: A success message indicating the voice configuration was removed successfully.
|
47
|
+
"""
|
48
|
+
vcc: VoiceConfigurationsController = VoiceConfigurationsController()
|
49
|
+
silent_call(fn=vcc.remove_voice_config_by_name, voice_config_name=voice_config_name)
|
50
|
+
return f"Voice configuration '{voice_config_name}' removed successfully."
|
51
|
+
|
52
|
+
__tools__ = [list_voice_configs, import_voice_config, remove_voice_config]
|