Skip to content

Commit b7d8dea

Browse files
committed
fix: add name-based MCP and A2A lookup
1 parent 49b881d commit b7d8dea

9 files changed

Lines changed: 441 additions & 30 deletions

File tree

packages/uipath-platform/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "uipath-platform"
3-
version = "0.2.13"
3+
version = "0.2.14"
44
description = "HTTP client library for programmatic access to UiPath Platform"
55
readme = { file = "README.md", content-type = "text/markdown" }
66
requires-python = ">=3.11"

packages/uipath-platform/src/uipath/platform/agenthub/_remote_a2a_service.py

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import warnings
99
from typing import Any, List
10+
from urllib.parse import quote
1011

1112
from ..common._base_service import BaseService
1213
from ..common._bindings import resource_override
@@ -150,20 +151,23 @@ async def main():
150151
data = response.json()
151152
return [RemoteA2aAgent.model_validate(agent) for agent in data.get("value", [])]
152153

154+
@resource_override(resource_type="remoteA2aAgent", resource_identifier="name")
153155
@resource_override(resource_type="remoteA2aAgent", resource_identifier="slug")
154156
def retrieve(
155157
self,
156-
slug: str,
158+
slug: str | None = None,
157159
*,
160+
name: str | None = None,
158161
folder_path: str | None = None,
159162
) -> RemoteA2aAgent:
160-
"""Retrieve a specific Remote A2A agent by slug.
163+
"""Retrieve a Remote A2A agent by its display name or legacy slug.
161164
162165
.. warning::
163166
This method is experimental and subject to change.
164167
165168
Args:
166-
slug: The unique slug identifier for the agent.
169+
slug: The legacy slug identifier of the agent.
170+
name: The display name of the agent.
167171
folder_path: The folder path where the agent is located.
168172
169173
Returns:
@@ -183,7 +187,8 @@ def retrieve(
183187
"remote_a2a.retrieve is experimental and subject to change.",
184188
stacklevel=2,
185189
)
186-
spec = self._retrieve_spec(slug=slug, folder_path=folder_path)
190+
identifier = self._resolve_retrieve_identifier(name=name, slug=slug)
191+
spec = self._retrieve_spec(name=identifier, folder_path=folder_path)
187192
response = self.request(
188193
spec.method,
189194
url=spec.endpoint,
@@ -192,20 +197,23 @@ def retrieve(
192197
)
193198
return RemoteA2aAgent.model_validate(response.json())
194199

200+
@resource_override(resource_type="remoteA2aAgent", resource_identifier="name")
195201
@resource_override(resource_type="remoteA2aAgent", resource_identifier="slug")
196202
async def retrieve_async(
197203
self,
198-
slug: str,
204+
slug: str | None = None,
199205
*,
206+
name: str | None = None,
200207
folder_path: str | None = None,
201208
) -> RemoteA2aAgent:
202-
"""Asynchronously retrieve a specific Remote A2A agent by slug.
209+
"""Asynchronously retrieve a Remote A2A agent by display name or legacy slug.
203210
204211
.. warning::
205212
This method is experimental and subject to change.
206213
207214
Args:
208-
slug: The unique slug identifier for the agent.
215+
slug: The legacy slug identifier of the agent.
216+
name: The display name of the agent.
209217
folder_path: The folder path where the agent is located.
210218
211219
Returns:
@@ -229,7 +237,8 @@ async def main():
229237
"remote_a2a.retrieve_async is experimental and subject to change.",
230238
stacklevel=2,
231239
)
232-
spec = self._retrieve_spec(slug=slug, folder_path=folder_path)
240+
identifier = self._resolve_retrieve_identifier(name=name, slug=slug)
241+
spec = self._retrieve_spec(name=identifier, folder_path=folder_path)
233242
response = await self.request_async(
234243
spec.method,
235244
url=spec.endpoint,
@@ -249,6 +258,19 @@ def _resolve_folder_key(self, folder_path: str | None) -> str | None:
249258

250259
return self._folder_key
251260

261+
@staticmethod
262+
def _resolve_retrieve_identifier(
263+
name: str | None,
264+
slug: str | None,
265+
) -> str:
266+
if name is not None and slug is not None:
267+
raise ValueError("Specify either 'name' or 'slug', not both.")
268+
if name is not None:
269+
return name
270+
if slug is not None:
271+
return slug
272+
raise TypeError("Either 'name' or 'slug' must be provided.")
273+
252274
def _list_spec(
253275
self,
254276
*,
@@ -279,14 +301,16 @@ def _list_spec(
279301

280302
def _retrieve_spec(
281303
self,
282-
slug: str,
304+
name: str,
283305
*,
284306
folder_path: str | None,
285307
) -> RequestSpec:
286308
folder_key = self._resolve_folder_key(folder_path)
287309
return RequestSpec(
288310
method="GET",
289-
endpoint=Endpoint(f"/agenthub_/api/remote-a2a-agents/{slug}"),
311+
endpoint=Endpoint(
312+
f"/agenthub_/api/remote-a2a-agents/{quote(name, safe='')}"
313+
),
290314
headers={
291315
**header_folder(folder_key, None),
292316
},

packages/uipath-platform/src/uipath/platform/orchestrator/_mcp_service.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from typing import List
2+
from urllib.parse import quote
23

34
from uipath.core.tracing import traced
45

@@ -109,18 +110,21 @@ async def main():
109110

110111
return [McpServer.model_validate(server) for server in response.json()]
111112

113+
@resource_override(resource_type="mcpServer", resource_identifier="name")
112114
@resource_override(resource_type="mcpServer", resource_identifier="slug")
113115
@traced(name="mcp_retrieve", run_type="uipath")
114116
def retrieve(
115117
self,
116-
slug: str,
118+
slug: str | None = None,
117119
*,
120+
name: str | None = None,
118121
folder_path: str | None = None,
119122
) -> McpServer:
120-
"""Retrieve a specific MCP server by its slug.
123+
"""Retrieve a specific MCP server by its display name or legacy slug.
121124
122125
Args:
123-
slug (str): The unique slug identifier for the server.
126+
slug (Optional[str]): The legacy slug identifier of the server.
127+
name (Optional[str]): The display name of the server.
124128
folder_path (Optional[str]): The path of the folder where the server is located.
125129
126130
Returns:
@@ -132,12 +136,13 @@ def retrieve(
132136
133137
client = UiPath()
134138
135-
server = client.mcp.retrieve(slug="my-server-slug", folder_path="MyFolder")
139+
server = client.mcp.retrieve(name="My Server", folder_path="MyFolder")
136140
print(f"Server: {server.name}, URL: {server.mcp_url}")
137141
```
138142
"""
143+
identifier = self._resolve_retrieve_identifier(name=name, slug=slug)
139144
spec = self._retrieve_spec(
140-
slug=slug,
145+
name=identifier,
141146
folder_path=folder_path,
142147
)
143148

@@ -150,18 +155,21 @@ def retrieve(
150155

151156
return McpServer.model_validate(response.json())
152157

158+
@resource_override(resource_type="mcpServer", resource_identifier="name")
153159
@resource_override(resource_type="mcpServer", resource_identifier="slug")
154160
@traced(name="mcp_retrieve", run_type="uipath")
155161
async def retrieve_async(
156162
self,
157-
slug: str,
163+
slug: str | None = None,
158164
*,
165+
name: str | None = None,
159166
folder_path: str | None = None,
160167
) -> McpServer:
161-
"""Asynchronously retrieve a specific MCP server by its slug.
168+
"""Asynchronously retrieve an MCP server by its display name or legacy slug.
162169
163170
Args:
164-
slug (str): The unique slug identifier for the server.
171+
slug (Optional[str]): The legacy slug identifier of the server.
172+
name (Optional[str]): The display name of the server.
165173
folder_path (Optional[str]): The path of the folder where the server is located.
166174
167175
Returns:
@@ -176,14 +184,15 @@ async def retrieve_async(
176184
sdk = UiPath()
177185
178186
async def main():
179-
server = await sdk.mcp.retrieve_async(slug="my-server-slug", folder_path="MyFolder")
187+
server = await sdk.mcp.retrieve_async(name="My Server", folder_path="MyFolder")
180188
print(f"Server: {server.name}, URL: {server.mcp_url}")
181189
182190
asyncio.run(main())
183191
```
184192
"""
193+
identifier = self._resolve_retrieve_identifier(name=name, slug=slug)
185194
spec = self._retrieve_spec(
186-
slug=slug,
195+
name=identifier,
187196
folder_path=folder_path,
188197
)
189198

@@ -207,6 +216,19 @@ def _resolve_folder_key(self, folder_path: str | None) -> str | None:
207216

208217
return self._folder_key
209218

219+
@staticmethod
220+
def _resolve_retrieve_identifier(
221+
name: str | None,
222+
slug: str | None,
223+
) -> str:
224+
if name is not None and slug is not None:
225+
raise ValueError("Specify either 'name' or 'slug', not both.")
226+
if name is not None:
227+
return name
228+
if slug is not None:
229+
return slug
230+
raise TypeError("Either 'name' or 'slug' must be provided.")
231+
210232
def _list_spec(
211233
self,
212234
*,
@@ -223,14 +245,14 @@ def _list_spec(
223245

224246
def _retrieve_spec(
225247
self,
226-
slug: str,
248+
name: str,
227249
*,
228250
folder_path: str | None,
229251
) -> RequestSpec:
230252
folder_key = self._resolve_folder_key(folder_path)
231253
return RequestSpec(
232254
method="GET",
233-
endpoint=Endpoint(f"/agenthub_/api/servers/{slug}"),
255+
endpoint=Endpoint(f"/agenthub_/api/servers/{quote(name, safe='')}"),
234256
headers={
235257
**header_folder(folder_key, None),
236258
},

0 commit comments

Comments
 (0)