pyproject.toml CHANGED
@@ -16,6 +16,7 @@ dependencies = [
16
  "gradio[mcp]>=5.32.1",
17
  "huggingface-hub>=0.32.3",
18
  "langchain-aws>=0.2.24",
 
19
  "langgraph>=0.4.7",
20
  ]
21
 
@@ -103,7 +104,17 @@ line-length = 88
103
 
104
  [tool.ruff.lint]
105
  select = ["ALL"]
106
- ignore = ["D100", "D104", "D107", "D401", "EM102", "ERA001", "TRY003"]
 
 
 
 
 
 
 
 
 
 
107
 
108
  [tool.ruff.lint.flake8-quotes]
109
  inline-quotes = "double"
@@ -121,6 +132,9 @@ lines-after-imports = 2
121
  [tool.ruff.lint.mccabe]
122
  max-complexity = 18
123
 
 
 
 
124
  [tool.ruff.lint.pydocstyle]
125
  convention = "google"
126
 
 
16
  "gradio[mcp]>=5.32.1",
17
  "huggingface-hub>=0.32.3",
18
  "langchain-aws>=0.2.24",
19
+ "langchain-mcp-adapters>=0.1.1",
20
  "langgraph>=0.4.7",
21
  ]
22
 
 
104
 
105
  [tool.ruff.lint]
106
  select = ["ALL"]
107
+ ignore = [
108
+ "D100",
109
+ "D104",
110
+ "D107",
111
+ "D401",
112
+ "EM102",
113
+ "ERA001",
114
+ "TC002",
115
+ "TC003",
116
+ "TRY003",
117
+ ]
118
 
119
  [tool.ruff.lint.flake8-quotes]
120
  inline-quotes = "double"
 
132
  [tool.ruff.lint.mccabe]
133
  max-complexity = 18
134
 
135
+ [tool.ruff.lint.pylint]
136
+ max-args = 7
137
+
138
  [tool.ruff.lint.pydocstyle]
139
  convention = "google"
140
 
requirements-dev.txt CHANGED
@@ -1,33 +1,70 @@
1
  # This file was autogenerated by uv via the following command:
2
  # uv export --format requirements-txt --no-hashes --group dev --group test -o requirements-dev.txt
 
 
 
 
3
  boolean-py==5.0
 
 
4
  cachecontrol==0.14.3
5
  certifi==2025.4.26
 
6
  cfgv==3.4.0
7
  charset-normalizer==3.4.2
 
8
  colorama==0.4.6 ; sys_platform == 'win32'
9
  coverage==7.8.2
10
  cyclonedx-python-lib==9.1.0
11
  defusedxml==0.7.1
12
  distlib==0.3.9
13
  exceptiongroup==1.3.0 ; python_full_version < '3.11'
 
 
14
  filelock==3.18.0
15
  fsspec==2025.5.1
 
 
 
 
16
  hf-xet==1.1.2 ; platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'
 
 
 
17
  huggingface-hub==0.32.3
18
  identify==2.6.12
19
  idna==3.10
20
  iniconfig==2.1.0
 
 
 
 
 
 
 
 
 
 
 
 
21
  license-expression==30.4.1
22
  markdown-it-py==3.0.0
 
 
23
  mdurl==0.1.2
24
  msgpack==1.1.0
25
  mypy==1.16.0
26
  mypy-extensions==1.1.0
27
  nodeenv==1.9.1
 
 
 
 
28
  packageurl-python==0.16.0
29
- packaging==25.0
 
30
  pathspec==0.12.1
 
31
  pip==25.1.1
32
  pip-api==0.0.34
33
  pip-audit==2.9.0
@@ -36,20 +73,47 @@ platformdirs==4.3.8
36
  pluggy==1.6.0
37
  pre-commit==3.8.0
38
  py-serializable==2.0.0
 
 
 
 
 
39
  pygments==2.19.1
40
  pyparsing==3.2.3
41
  pytest==7.4.4
42
  pytest-cov==4.1.0
43
  pytest-randomly==3.16.0
 
 
 
 
44
  pyyaml==6.0.2
45
  requests==2.32.3
 
46
  rich==14.0.0
47
  ruff==0.11.12
 
 
 
 
 
 
48
  sortedcontainers==2.4.0
 
 
 
49
  toml==0.10.2
50
  tomli==2.2.1 ; python_full_version <= '3.11'
 
51
  tqdm==4.67.1
 
52
  typing-extensions==4.13.2
 
 
53
  urllib3==2.4.0
 
54
  virtualenv==20.31.2
 
55
  xdoctest==1.2.0
 
 
 
1
  # This file was autogenerated by uv via the following command:
2
  # uv export --format requirements-txt --no-hashes --group dev --group test -o requirements-dev.txt
3
+ aiofiles==24.1.0
4
+ annotated-types==0.7.0
5
+ anyio==4.9.0
6
+ audioop-lts==0.2.1 ; python_full_version >= '3.13'
7
  boolean-py==5.0
8
+ boto3==1.38.27
9
+ botocore==1.38.27
10
  cachecontrol==0.14.3
11
  certifi==2025.4.26
12
+ cffi==1.17.1 ; platform_python_implementation == 'PyPy'
13
  cfgv==3.4.0
14
  charset-normalizer==3.4.2
15
+ click==8.2.1 ; sys_platform != 'emscripten'
16
  colorama==0.4.6 ; sys_platform == 'win32'
17
  coverage==7.8.2
18
  cyclonedx-python-lib==9.1.0
19
  defusedxml==0.7.1
20
  distlib==0.3.9
21
  exceptiongroup==1.3.0 ; python_full_version < '3.11'
22
+ fastapi==0.115.12
23
+ ffmpy==0.6.0
24
  filelock==3.18.0
25
  fsspec==2025.5.1
26
+ gradio==5.32.1
27
+ gradio-client==1.10.2
28
+ groovy==0.1.2
29
+ h11==0.16.0
30
  hf-xet==1.1.2 ; platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'
31
+ httpcore==1.0.9
32
+ httpx==0.28.1
33
+ httpx-sse==0.4.0
34
  huggingface-hub==0.32.3
35
  identify==2.6.12
36
  idna==3.10
37
  iniconfig==2.1.0
38
+ jinja2==3.1.6
39
+ jmespath==1.0.1
40
+ jsonpatch==1.33
41
+ jsonpointer==3.0.0
42
+ langchain-aws==0.2.24
43
+ langchain-core==0.3.63
44
+ langchain-mcp-adapters==0.1.1
45
+ langgraph==0.4.7
46
+ langgraph-checkpoint==2.0.26
47
+ langgraph-prebuilt==0.2.2
48
+ langgraph-sdk==0.1.70
49
+ langsmith==0.3.43
50
  license-expression==30.4.1
51
  markdown-it-py==3.0.0
52
+ markupsafe==3.0.2
53
+ mcp==1.9.0
54
  mdurl==0.1.2
55
  msgpack==1.1.0
56
  mypy==1.16.0
57
  mypy-extensions==1.1.0
58
  nodeenv==1.9.1
59
+ numpy==1.26.4 ; python_full_version < '3.12'
60
+ numpy==2.2.6 ; python_full_version >= '3.12'
61
+ orjson==3.10.18
62
+ ormsgpack==1.10.0
63
  packageurl-python==0.16.0
64
+ packaging==24.2
65
+ pandas==2.2.3
66
  pathspec==0.12.1
67
+ pillow==11.2.1
68
  pip==25.1.1
69
  pip-api==0.0.34
70
  pip-audit==2.9.0
 
73
  pluggy==1.6.0
74
  pre-commit==3.8.0
75
  py-serializable==2.0.0
76
+ pycparser==2.22 ; platform_python_implementation == 'PyPy'
77
+ pydantic==2.11.5
78
+ pydantic-core==2.33.2
79
+ pydantic-settings==2.9.1
80
+ pydub==0.25.1
81
  pygments==2.19.1
82
  pyparsing==3.2.3
83
  pytest==7.4.4
84
  pytest-cov==4.1.0
85
  pytest-randomly==3.16.0
86
+ python-dateutil==2.9.0.post0
87
+ python-dotenv==1.1.0
88
+ python-multipart==0.0.20
89
+ pytz==2025.2
90
  pyyaml==6.0.2
91
  requests==2.32.3
92
+ requests-toolbelt==1.0.0
93
  rich==14.0.0
94
  ruff==0.11.12
95
+ s3transfer==0.13.0
96
+ safehttpx==0.1.6
97
+ semantic-version==2.10.0
98
+ shellingham==1.5.4 ; sys_platform != 'emscripten'
99
+ six==1.17.0
100
+ sniffio==1.3.1
101
  sortedcontainers==2.4.0
102
+ sse-starlette==2.3.6
103
+ starlette==0.46.2
104
+ tenacity==9.1.2
105
  toml==0.10.2
106
  tomli==2.2.1 ; python_full_version <= '3.11'
107
+ tomlkit==0.13.2
108
  tqdm==4.67.1
109
+ typer==0.16.0 ; sys_platform != 'emscripten'
110
  typing-extensions==4.13.2
111
+ typing-inspection==0.4.1
112
+ tzdata==2025.2
113
  urllib3==2.4.0
114
+ uvicorn==0.34.3 ; sys_platform != 'emscripten'
115
  virtualenv==20.31.2
116
+ websockets==15.0.1
117
  xdoctest==1.2.0
118
+ xxhash==3.5.0
119
+ zstandard==0.23.0
requirements.txt CHANGED
@@ -1,25 +1,94 @@
1
  # This file was autogenerated by uv via the following command:
2
  # uv export --format requirements-txt --no-hashes --no-dev -o requirements.txt
 
 
 
 
 
 
3
  certifi==2025.4.26
 
4
  charset-normalizer==3.4.2
 
5
  colorama==0.4.6 ; sys_platform == 'win32'
6
  coverage==7.8.2
7
  exceptiongroup==1.3.0 ; python_full_version < '3.11'
 
 
8
  filelock==3.18.0
9
  fsspec==2025.5.1
 
 
 
 
10
  hf-xet==1.1.2 ; platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'
 
 
 
11
  huggingface-hub==0.32.3
12
  idna==3.10
13
  iniconfig==2.1.0
14
- packaging==25.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  pluggy==1.6.0
 
 
 
 
 
 
16
  pytest==7.4.4
17
  pytest-cov==4.1.0
18
  pytest-randomly==3.16.0
 
 
 
 
19
  pyyaml==6.0.2
20
  requests==2.32.3
 
 
 
 
 
 
 
 
 
 
 
 
21
  tomli==2.2.1 ; python_full_version <= '3.11'
 
22
  tqdm==4.67.1
 
23
  typing-extensions==4.13.2
 
 
24
  urllib3==2.4.0
 
 
25
  xdoctest==1.2.0
 
 
 
1
  # This file was autogenerated by uv via the following command:
2
  # uv export --format requirements-txt --no-hashes --no-dev -o requirements.txt
3
+ aiofiles==24.1.0
4
+ annotated-types==0.7.0
5
+ anyio==4.9.0
6
+ audioop-lts==0.2.1 ; python_full_version >= '3.13'
7
+ boto3==1.38.27
8
+ botocore==1.38.27
9
  certifi==2025.4.26
10
+ cffi==1.17.1 ; platform_python_implementation == 'PyPy'
11
  charset-normalizer==3.4.2
12
+ click==8.2.1 ; sys_platform != 'emscripten'
13
  colorama==0.4.6 ; sys_platform == 'win32'
14
  coverage==7.8.2
15
  exceptiongroup==1.3.0 ; python_full_version < '3.11'
16
+ fastapi==0.115.12
17
+ ffmpy==0.6.0
18
  filelock==3.18.0
19
  fsspec==2025.5.1
20
+ gradio==5.32.1
21
+ gradio-client==1.10.2
22
+ groovy==0.1.2
23
+ h11==0.16.0
24
  hf-xet==1.1.2 ; platform_machine == 'aarch64' or platform_machine == 'amd64' or platform_machine == 'arm64' or platform_machine == 'x86_64'
25
+ httpcore==1.0.9
26
+ httpx==0.28.1
27
+ httpx-sse==0.4.0
28
  huggingface-hub==0.32.3
29
  idna==3.10
30
  iniconfig==2.1.0
31
+ jinja2==3.1.6
32
+ jmespath==1.0.1
33
+ jsonpatch==1.33
34
+ jsonpointer==3.0.0
35
+ langchain-aws==0.2.24
36
+ langchain-core==0.3.63
37
+ langchain-mcp-adapters==0.1.1
38
+ langgraph==0.4.7
39
+ langgraph-checkpoint==2.0.26
40
+ langgraph-prebuilt==0.2.2
41
+ langgraph-sdk==0.1.70
42
+ langsmith==0.3.43
43
+ markdown-it-py==3.0.0 ; sys_platform != 'emscripten'
44
+ markupsafe==3.0.2
45
+ mcp==1.9.0
46
+ mdurl==0.1.2 ; sys_platform != 'emscripten'
47
+ numpy==1.26.4 ; python_full_version < '3.12'
48
+ numpy==2.2.6 ; python_full_version >= '3.12'
49
+ orjson==3.10.18
50
+ ormsgpack==1.10.0
51
+ packaging==24.2
52
+ pandas==2.2.3
53
+ pillow==11.2.1
54
  pluggy==1.6.0
55
+ pycparser==2.22 ; platform_python_implementation == 'PyPy'
56
+ pydantic==2.11.5
57
+ pydantic-core==2.33.2
58
+ pydantic-settings==2.9.1
59
+ pydub==0.25.1
60
+ pygments==2.19.1 ; sys_platform != 'emscripten'
61
  pytest==7.4.4
62
  pytest-cov==4.1.0
63
  pytest-randomly==3.16.0
64
+ python-dateutil==2.9.0.post0
65
+ python-dotenv==1.1.0
66
+ python-multipart==0.0.20
67
+ pytz==2025.2
68
  pyyaml==6.0.2
69
  requests==2.32.3
70
+ requests-toolbelt==1.0.0
71
+ rich==14.0.0 ; sys_platform != 'emscripten'
72
+ ruff==0.11.12 ; sys_platform != 'emscripten'
73
+ s3transfer==0.13.0
74
+ safehttpx==0.1.6
75
+ semantic-version==2.10.0
76
+ shellingham==1.5.4 ; sys_platform != 'emscripten'
77
+ six==1.17.0
78
+ sniffio==1.3.1
79
+ sse-starlette==2.3.6
80
+ starlette==0.46.2
81
+ tenacity==9.1.2
82
  tomli==2.2.1 ; python_full_version <= '3.11'
83
+ tomlkit==0.13.2
84
  tqdm==4.67.1
85
+ typer==0.16.0 ; sys_platform != 'emscripten'
86
  typing-extensions==4.13.2
87
+ typing-inspection==0.4.1
88
+ tzdata==2025.2
89
  urllib3==2.4.0
90
+ uvicorn==0.34.3 ; sys_platform != 'emscripten'
91
+ websockets==15.0.1
92
  xdoctest==1.2.0
93
+ xxhash==3.5.0
94
+ zstandard==0.23.0
tdagent/grchat.py CHANGED
@@ -1,7 +1,8 @@
1
  from __future__ import annotations
2
 
3
- from collections.abc import Mapping
4
  from types import MappingProxyType
 
5
 
6
  import boto3
7
  import botocore
@@ -9,9 +10,15 @@ import botocore.exceptions
9
  import gradio as gr
10
  from langchain_aws import ChatBedrock
11
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
12
- from langgraph.graph.graph import CompiledGraph
13
  from langgraph.prebuilt import create_react_agent
14
 
 
 
 
 
 
 
15
 
16
  #### Constants ####
17
 
@@ -59,7 +66,7 @@ def create_bedrock_llm(
59
  try:
60
  bedrock_client = boto3.client("bedrock-runtime", **boto3_config)
61
  llm = ChatBedrock(
62
- model=bedrock_model_id,
63
  client=bedrock_client,
64
  model_kwargs={"temperature": 0.7},
65
  )
@@ -78,6 +85,7 @@ async def gr_connect_to_bedrock(
78
  secret_key: str,
79
  session_token: str,
80
  region: str,
 
81
  ) -> str:
82
  """Initialize Bedrock agent."""
83
  global llm_agent # noqa: PLW0603
@@ -96,9 +104,33 @@ async def gr_connect_to_bedrock(
96
  if llm is None:
97
  return f"❌ Connection failed: {error}"
98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  llm_agent = create_react_agent(
100
  model=llm,
101
- tools=[],
102
  prompt=SYSTEM_MESSAGE,
103
  )
104
 
@@ -134,6 +166,18 @@ async def gr_chat_function( # noqa: D103
134
  with gr.Blocks() as gr_app:
135
  gr.Markdown("# 🔐 Secure Bedrock Chatbot")
136
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  # Credentials section (collapsible)
138
  with gr.Accordion("🔑 Bedrock Configuration", open=True):
139
  gr.Markdown(
@@ -185,6 +229,7 @@ with gr.Blocks() as gr_app:
185
  aws_secret_key_textbox,
186
  aws_session_token_textbox,
187
  aws_region_dropdown,
 
188
  ],
189
  outputs=[status_textbox],
190
  )
 
1
  from __future__ import annotations
2
 
3
+ from collections.abc import Mapping, Sequence
4
  from types import MappingProxyType
5
+ from typing import TYPE_CHECKING
6
 
7
  import boto3
8
  import botocore
 
10
  import gradio as gr
11
  from langchain_aws import ChatBedrock
12
  from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
13
+ from langchain_mcp_adapters.client import MultiServerMCPClient
14
  from langgraph.prebuilt import create_react_agent
15
 
16
+ from tdagent.grcomponents import MutableCheckBoxGroup, MutableCheckBoxGroupEntry
17
+
18
+
19
+ if TYPE_CHECKING:
20
+ from langgraph.graph.graph import CompiledGraph
21
+
22
 
23
  #### Constants ####
24
 
 
66
  try:
67
  bedrock_client = boto3.client("bedrock-runtime", **boto3_config)
68
  llm = ChatBedrock(
69
+ model_id=bedrock_model_id,
70
  client=bedrock_client,
71
  model_kwargs={"temperature": 0.7},
72
  )
 
85
  secret_key: str,
86
  session_token: str,
87
  region: str,
88
+ mcp_servers: Sequence[MutableCheckBoxGroupEntry] | None,
89
  ) -> str:
90
  """Initialize Bedrock agent."""
91
  global llm_agent # noqa: PLW0603
 
104
  if llm is None:
105
  return f"❌ Connection failed: {error}"
106
 
107
+ # client = MultiServerMCPClient(
108
+ # {
109
+ # "toolkit": {
110
+ # "url": "https://agents-mcp-hackathon-tdagenttools.hf.space/gradio_api/mcp/sse",
111
+ # "transport": "sse",
112
+ # },
113
+ # }
114
+ # )
115
+ # tools = await client.get_tools()
116
+
117
+ if mcp_servers:
118
+ client = MultiServerMCPClient(
119
+ {
120
+ server.name.replace(" ", "-"): {
121
+ "url": server.value,
122
+ "transport": "sse",
123
+ }
124
+ for server in mcp_servers
125
+ },
126
+ )
127
+ tools = await client.get_tools()
128
+ else:
129
+ tools = []
130
+
131
  llm_agent = create_react_agent(
132
  model=llm,
133
+ tools=tools,
134
  prompt=SYSTEM_MESSAGE,
135
  )
136
 
 
166
  with gr.Blocks() as gr_app:
167
  gr.Markdown("# 🔐 Secure Bedrock Chatbot")
168
 
169
+ ### MCP Servers ###
170
+ with gr.Accordion():
171
+ mcp_list = MutableCheckBoxGroup(
172
+ values=[
173
+ MutableCheckBoxGroupEntry(
174
+ name="TDAgent tools",
175
+ value="https://agents-mcp-hackathon-tdagenttools.hf.space/gradio_api/mcp/sse",
176
+ ),
177
+ ],
178
+ label="MCP Servers",
179
+ )
180
+
181
  # Credentials section (collapsible)
182
  with gr.Accordion("🔑 Bedrock Configuration", open=True):
183
  gr.Markdown(
 
229
  aws_secret_key_textbox,
230
  aws_session_token_textbox,
231
  aws_region_dropdown,
232
+ mcp_list.state,
233
  ],
234
  outputs=[status_textbox],
235
  )
tdagent/grcomponents/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .mcbgroup import MutableCheckBoxGroup, MutableCheckBoxGroupEntry
tdagent/grcomponents/mcbgroup.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, NamedTuple
4
+
5
+ import gradio as gr
6
+
7
+
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Callable, Sequence
10
+
11
+
12
+ class MutableCheckBoxGroupEntry(NamedTuple):
13
+ """Entry of the mutable checkbox group."""
14
+
15
+ name: str
16
+ value: str
17
+
18
+
19
+ class MutableCheckBoxGroup(gr.Blocks):
20
+ """Check box group with controls to add or remove values."""
21
+
22
+ def __init__( # noqa: PLR0913
23
+ self,
24
+ values: list[MutableCheckBoxGroupEntry] | None = None,
25
+ label: str = "Extendable List",
26
+ new_value_label: str = "New Item Value",
27
+ new_name_label: str = "New Item Name",
28
+ new_value_placeholder: str = "New item value ...",
29
+ new_name_placeholder: str = "New item name, if not given value will be used...",
30
+ on_change: Callable[[Sequence[MutableCheckBoxGroupEntry]], None] | None = None,
31
+ ) -> None:
32
+ super().__init__()
33
+ self.values = values or []
34
+
35
+ self.label = label
36
+ self.new_value_label = new_value_label
37
+ self.new_name_label = new_name_label
38
+
39
+ self.new_value_placeholder = new_value_placeholder
40
+ self.new_name_placeholder = new_name_placeholder
41
+
42
+ self.on_change = on_change
43
+ self._build_interface()
44
+
45
+ def _build_interface(self) -> None:
46
+ # Custom CSS for vertical checkbox layout
47
+ self.style = """
48
+ #vertical-container .wrap {
49
+ display: flex;
50
+ flex-direction: column;
51
+ gap: 8px;
52
+ }
53
+ #vertical-container .wrap label {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 8px;
57
+ }
58
+ """
59
+
60
+ with self:
61
+ gr.Markdown(f"### {self.label}")
62
+
63
+ # Store items in state
64
+ self.state = gr.State(self.values)
65
+
66
+ # Input row
67
+ with gr.Row():
68
+ self.input_value = gr.Textbox(
69
+ label=self.new_value_label,
70
+ placeholder=self.new_value_placeholder,
71
+ scale=4,
72
+ )
73
+ self.input_name = gr.Textbox(
74
+ label=self.new_name_label,
75
+ placeholder=self.new_name_placeholder,
76
+ scale=2,
77
+ )
78
+ with gr.Column():
79
+ self.add_btn = gr.Button("Add", variant="primary", scale=1)
80
+ self.delete_btn = gr.Button("Delete Selected", variant="stop")
81
+
82
+ # Vertical checkbox group
83
+ self.items_group = gr.CheckboxGroup(
84
+ choices=self.values,
85
+ label="Items",
86
+ elem_id="vertical-container",
87
+ container=True,
88
+ )
89
+
90
+ # Set up event handlers
91
+ self.add_btn.click(
92
+ self._add_item,
93
+ inputs=[self.state, self.input_value, self.input_name],
94
+ outputs=[
95
+ self.state,
96
+ self.items_group,
97
+ self.input_value,
98
+ self.input_name,
99
+ ],
100
+ )
101
+
102
+ self.delete_btn.click(
103
+ self._delete_selected,
104
+ inputs=[self.state, self.items_group],
105
+ outputs=[self.state, self.items_group],
106
+ )
107
+
108
+ def get_values(self) -> Sequence[str]:
109
+ """Get check box values."""
110
+ return self.state.value
111
+
112
+ def _add_item(
113
+ self,
114
+ items: list[MutableCheckBoxGroupEntry],
115
+ new_value: str,
116
+ new_name: str,
117
+ ) -> tuple[list[MutableCheckBoxGroupEntry], dict[str, Any], str, str]:
118
+ if not new_name:
119
+ new_name = new_value
120
+
121
+ if new_value:
122
+ if any(entry.name == new_name for entry in items):
123
+ raise gr.Error(
124
+ f"Entry with name '{new_name}' already exists",
125
+ duration=10,
126
+ )
127
+ if any(entry.value == new_value for entry in items):
128
+ raise gr.Error(
129
+ f"Entry with value '{new_value}' already exists",
130
+ duration=10,
131
+ )
132
+
133
+ items = [*items, MutableCheckBoxGroupEntry(new_name, new_value)]
134
+ if self.on_change:
135
+ self.on_change(items)
136
+
137
+ # State, checkbox, input_value, input_name
138
+ return items, gr.update(choices=items), "", ""
139
+
140
+ # State, checkbox, input_value, input_name
141
+ return items, gr.update(), "", ""
142
+
143
+ def _delete_selected(
144
+ self,
145
+ items: list[MutableCheckBoxGroupEntry],
146
+ selected: list[str],
147
+ ) -> tuple[list[MutableCheckBoxGroupEntry], dict[str, Any]]:
148
+ updated_items = [item for item in items if item.value not in selected]
149
+ if self.on_change:
150
+ self.on_change(updated_items)
151
+ return updated_items, gr.update(choices=updated_items, value=[])
uv.lock CHANGED
@@ -705,6 +705,19 @@ wheels = [
705
  { url = "https://files.pythonhosted.org/packages/5c/71/a748861e6a69ab6ef50ab8e65120422a1f36245c71a0dd0f02de49c208e1/langchain_core-0.3.63-py3-none-any.whl", hash = "sha256:f91db8221b1bc6808f70b2e72fded1a94d50ee3f1dff1636fb5a5a514c64b7f5", size = 438468 },
706
  ]
707
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
  [[package]]
709
  name = "langgraph"
710
  version = "0.4.7"
@@ -1905,6 +1918,7 @@ dependencies = [
1905
  { name = "gradio", extra = ["mcp"] },
1906
  { name = "huggingface-hub" },
1907
  { name = "langchain-aws" },
 
1908
  { name = "langgraph" },
1909
  ]
1910
 
@@ -1927,6 +1941,7 @@ requires-dist = [
1927
  { name = "gradio", extras = ["mcp"], specifier = ">=5.32.1" },
1928
  { name = "huggingface-hub", specifier = ">=0.32.3" },
1929
  { name = "langchain-aws", specifier = ">=0.2.24" },
 
1930
  { name = "langgraph", specifier = ">=0.4.7" },
1931
  ]
1932
 
 
705
  { url = "https://files.pythonhosted.org/packages/5c/71/a748861e6a69ab6ef50ab8e65120422a1f36245c71a0dd0f02de49c208e1/langchain_core-0.3.63-py3-none-any.whl", hash = "sha256:f91db8221b1bc6808f70b2e72fded1a94d50ee3f1dff1636fb5a5a514c64b7f5", size = 438468 },
706
  ]
707
 
708
+ [[package]]
709
+ name = "langchain-mcp-adapters"
710
+ version = "0.1.1"
711
+ source = { registry = "https://pypi.org/simple" }
712
+ dependencies = [
713
+ { name = "langchain-core" },
714
+ { name = "mcp" },
715
+ ]
716
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/de/62a6ee2c21f74eed961773e75a4e3170f8abc79fd5fd7a1b4e2ea07f4c04/langchain_mcp_adapters-0.1.1.tar.gz", hash = "sha256:e43ddf06e4ce237ad80f5c91d0efa7fd7c845b274a5a07e7e8a7f9d7239331d7", size = 17359 }
717
+ wheels = [
718
+ { url = "https://files.pythonhosted.org/packages/30/68/13405f252b38a8e1bd7ef345907a4e0eda535c2ca36fe4d6821fc7e9f5de/langchain_mcp_adapters-0.1.1-py3-none-any.whl", hash = "sha256:81594b265d824012040ebd24056fbdb5aabf0b46f780e369ed132421e3411e4d", size = 12100 },
719
+ ]
720
+
721
  [[package]]
722
  name = "langgraph"
723
  version = "0.4.7"
 
1918
  { name = "gradio", extra = ["mcp"] },
1919
  { name = "huggingface-hub" },
1920
  { name = "langchain-aws" },
1921
+ { name = "langchain-mcp-adapters" },
1922
  { name = "langgraph" },
1923
  ]
1924
 
 
1941
  { name = "gradio", extras = ["mcp"], specifier = ">=5.32.1" },
1942
  { name = "huggingface-hub", specifier = ">=0.32.3" },
1943
  { name = "langchain-aws", specifier = ">=0.2.24" },
1944
+ { name = "langchain-mcp-adapters", specifier = ">=0.1.1" },
1945
  { name = "langgraph", specifier = ">=0.4.7" },
1946
  ]
1947