eo-toolbelt 0.1.0__tar.gz

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.
Files changed (32) hide show
  1. eo_toolbelt-0.1.0/.pypirc.sample +14 -0
  2. eo_toolbelt-0.1.0/LICENSE +21 -0
  3. eo_toolbelt-0.1.0/MANIFEST.in +10 -0
  4. eo_toolbelt-0.1.0/PKG-INFO +237 -0
  5. eo_toolbelt-0.1.0/PUBLISHING.ko.md +63 -0
  6. eo_toolbelt-0.1.0/PUBLISHING.md +63 -0
  7. eo_toolbelt-0.1.0/README.ko.md +203 -0
  8. eo_toolbelt-0.1.0/README.md +203 -0
  9. eo_toolbelt-0.1.0/examples/mcp_servers.example.json +47 -0
  10. eo_toolbelt-0.1.0/overlays/mcp_tools_sdk/py.typed +1 -0
  11. eo_toolbelt-0.1.0/overlays/mcp_tools_sdk/runtime/settings.py +32 -0
  12. eo_toolbelt-0.1.0/pyproject.toml +62 -0
  13. eo_toolbelt-0.1.0/scripts/sync_runtime.py +145 -0
  14. eo_toolbelt-0.1.0/setup.cfg +4 -0
  15. eo_toolbelt-0.1.0/src/eo_toolbelt.egg-info/PKG-INFO +237 -0
  16. eo_toolbelt-0.1.0/src/eo_toolbelt.egg-info/SOURCES.txt +30 -0
  17. eo_toolbelt-0.1.0/src/eo_toolbelt.egg-info/dependency_links.txt +1 -0
  18. eo_toolbelt-0.1.0/src/eo_toolbelt.egg-info/requires.txt +12 -0
  19. eo_toolbelt-0.1.0/src/eo_toolbelt.egg-info/top_level.txt +1 -0
  20. eo_toolbelt-0.1.0/src/mcp_tools_sdk/__init__.py +35 -0
  21. eo_toolbelt-0.1.0/src/mcp_tools_sdk/custom_tools.py +185 -0
  22. eo_toolbelt-0.1.0/src/mcp_tools_sdk/provider_options.py +198 -0
  23. eo_toolbelt-0.1.0/src/mcp_tools_sdk/py.typed +1 -0
  24. eo_toolbelt-0.1.0/src/mcp_tools_sdk/runtime/__init__.py +0 -0
  25. eo_toolbelt-0.1.0/src/mcp_tools_sdk/runtime/internal_tools.py +2241 -0
  26. eo_toolbelt-0.1.0/src/mcp_tools_sdk/runtime/mcp_client.py +533 -0
  27. eo_toolbelt-0.1.0/src/mcp_tools_sdk/runtime/mcp_config.py +69 -0
  28. eo_toolbelt-0.1.0/src/mcp_tools_sdk/runtime/settings.py +32 -0
  29. eo_toolbelt-0.1.0/src/mcp_tools_sdk/runtime/tool_registry.py +657 -0
  30. eo_toolbelt-0.1.0/src/mcp_tools_sdk/tool_selection.py +235 -0
  31. eo_toolbelt-0.1.0/src/mcp_tools_sdk/toolkit.py +106 -0
  32. eo_toolbelt-0.1.0/src/mcp_tools_sdk/tools.py +29 -0
@@ -0,0 +1,14 @@
1
+ [distutils]
2
+ index-servers =
3
+ pypi
4
+ testpypi
5
+
6
+ [pypi]
7
+ repository = https://upload.pypi.org/legacy/
8
+ username = __token__
9
+ password = pypi-...
10
+
11
+ [testpypi]
12
+ repository = https://test.pypi.org/legacy/
13
+ username = __token__
14
+ password = pypi-...
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 EO Toolbelt Maintainers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ include README.md
2
+ include README.ko.md
3
+ include PUBLISHING.md
4
+ include PUBLISHING.ko.md
5
+ include LICENSE
6
+ include .pypirc.sample
7
+ include pyproject.toml
8
+ recursive-include scripts *.py
9
+ recursive-include overlays *
10
+ recursive-include examples *.json
@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: eo-toolbelt
3
+ Version: 0.1.0
4
+ Summary: Python SDK for EO Toolbelt built-in tools, MCP tools, and provider-native tool schemas
5
+ Author: EO Toolbelt Maintainers
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/easyops-io/eo-toolbelt
8
+ Project-URL: Documentation, https://github.com/easyops-io/eo-toolbelt#readme
9
+ Project-URL: Repository, https://github.com/easyops-io/eo-toolbelt
10
+ Project-URL: Issues, https://github.com/easyops-io/eo-toolbelt/issues
11
+ Keywords: mcp,tools,llm,sdk,tool-use
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: mcp>=1.8.0
23
+ Requires-Dist: jsonschema>=4.20.0
24
+ Requires-Dist: tiktoken>=0.8.0
25
+ Requires-Dist: tzdata>=2025.2
26
+ Provides-Extra: dev
27
+ Requires-Dist: setuptools>=69; extra == "dev"
28
+ Requires-Dist: wheel>=0.43; extra == "dev"
29
+ Requires-Dist: build>=1.2; extra == "dev"
30
+ Requires-Dist: twine>=5.0; extra == "dev"
31
+ Requires-Dist: pytest>=8.0; extra == "dev"
32
+ Requires-Dist: ruff>=0.4; extra == "dev"
33
+ Dynamic: license-file
34
+
35
+ # EO Toolbelt SDK
36
+
37
+ Korean documentation: [README.ko.md](README.ko.md)
38
+
39
+ EO Toolbelt SDK helps Python applications expose utility tools, custom Python functions, and MCP server tools to model providers.
40
+
41
+ It gives you three small building blocks:
42
+
43
+ - list available tool specs
44
+ - execute a selected tool by name
45
+ - build provider-native tool option payloads for OpenAI, Anthropic, Gemini, and Bedrock Converse
46
+
47
+ The SDK does not call a language model for you. Your application keeps the model message loop and passes tool results back using the provider's normal API format.
48
+
49
+ ## Install
50
+
51
+ ```bash
52
+ pip install eo-toolbelt
53
+ ```
54
+
55
+ Import package:
56
+
57
+ ```python
58
+ from mcp_tools_sdk import Tools
59
+ ```
60
+
61
+ ## Quick Start
62
+
63
+ ```python
64
+ import asyncio
65
+
66
+ from mcp_tools_sdk import Tools
67
+
68
+
69
+ async def main() -> None:
70
+ tools = Tools()
71
+
72
+ result = await tools.call_tool(
73
+ "safe_calculate",
74
+ {"expression": "(12 + 8) * 3"},
75
+ )
76
+ print(result)
77
+
78
+
79
+ asyncio.run(main())
80
+ ```
81
+
82
+ ## Build Model Tool Options
83
+
84
+ ```python
85
+ from openai import AsyncOpenAI
86
+ from mcp_tools_sdk import Tools
87
+
88
+ client = AsyncOpenAI()
89
+ tools = Tools()
90
+
91
+ tool_options = await tools.model_tool_options(
92
+ provider="openai",
93
+ tool_names=["safe_calculate", "get_current_datetime"],
94
+ )
95
+
96
+ response = await client.responses.create(
97
+ model="gpt-5",
98
+ input="What is 12 * 8, and what time is it in Seoul?",
99
+ **tool_options,
100
+ )
101
+ ```
102
+
103
+ If the model returns a tool call, parse the provider response, call `tools.call_tool(...)`, then send the tool result back to the model.
104
+
105
+ ## Tools Constructor
106
+
107
+ ```python
108
+ tools = Tools(
109
+ config_path="mcp_servers.json",
110
+ timeout_seconds=30,
111
+ custom_tools=[my_custom_tool],
112
+ )
113
+ ```
114
+
115
+ | Option | Type | Description |
116
+ | --- | --- | --- |
117
+ | `config_path` | `str | Path | None` | MCP server config path. When omitted, built-in and custom tools are available without MCP discovery. |
118
+ | `timeout_seconds` | `float | None` | Timeout for MCP operations. |
119
+ | `custom_tools` | iterable | Python callables or `CustomTool` objects registered on this instance. |
120
+ | `backend` | internal backend | Alternate runtime backend for tests and advanced embedding. |
121
+
122
+ ## Main Methods
123
+
124
+ ### `list_tools(...)`
125
+
126
+ Returns canonical tool specs.
127
+
128
+ ```python
129
+ catalog = await tools.list_tools(
130
+ include_internal=True,
131
+ include_mcp=True,
132
+ include_custom=True,
133
+ mcp_server_names=["fetch"],
134
+ selected_tool_names=["safe_calculate"],
135
+ )
136
+ ```
137
+
138
+ ### `call_tool(tool_name, arguments=None)`
139
+
140
+ Executes one built-in, custom, or MCP tool.
141
+
142
+ ```python
143
+ result = await tools.call_tool(
144
+ "safe_calculate",
145
+ {"expression": "7 * 6"},
146
+ )
147
+ ```
148
+
149
+ MCP tools use this name format:
150
+
151
+ ```text
152
+ mcp__{server_name}__{tool_name}
153
+ ```
154
+
155
+ ### `model_tool_options(provider, ...)`
156
+
157
+ Converts tool specs into provider-native request fields.
158
+
159
+ ```python
160
+ tool_options = await tools.model_tool_options(
161
+ provider="bedrock",
162
+ tool_names=["safe_calculate"],
163
+ tool_choice={"auto": {}},
164
+ )
165
+ ```
166
+
167
+ | Provider value | Returned fields |
168
+ | --- | --- |
169
+ | `openai`, `gpt`, `openai_responses` | OpenAI Responses API `tools` and optional `tool_choice`. |
170
+ | `openai_chat_completions` | OpenAI Chat Completions `tools` and optional `tool_choice`. |
171
+ | `anthropic`, `claude` | Anthropic Messages `tools` and optional `tool_choice`. |
172
+ | `gemini` | Gemini `tools` with `function_declarations` and optional `tool_config`. |
173
+ | `bedrock`, `bedrock_converse` | Bedrock Converse `toolConfig`. |
174
+
175
+ ### `select_tools(query, ...)`
176
+
177
+ Selects a compact tool list from a catalog. Pass an LLM callback for model-judged selection, or omit `llm` for the deterministic keyword fallback.
178
+
179
+ ```python
180
+ tool_names = await tools.select_tools(
181
+ query="Calculate actual vs budget variance by department.",
182
+ max_tools=4,
183
+ llm=select_with_llm,
184
+ )
185
+ ```
186
+
187
+ ## Custom Tools
188
+
189
+ ```python
190
+ from typing import Literal
191
+
192
+ from mcp_tools_sdk import Tools, custom_tool
193
+
194
+
195
+ def apply_discount(amount: float, rate: float = 0.1) -> dict:
196
+ """Apply a discount rate."""
197
+ return {"amount": amount, "rate": rate, "total": amount * (1 - rate)}
198
+
199
+
200
+ @custom_tool(name="classify_priority")
201
+ def classify_priority(level: Literal["low", "high"]) -> str:
202
+ """Classify a priority level."""
203
+ return level.upper()
204
+
205
+
206
+ tools = Tools(custom_tools=[apply_discount, classify_priority])
207
+ ```
208
+
209
+ ## MCP Config
210
+
211
+ Pass an MCP config file when your application wants to expose MCP server tools:
212
+
213
+ ```python
214
+ tools = Tools(config_path="mcp_servers.json")
215
+ ```
216
+
217
+ An example config is available at [examples/mcp_servers.example.json](examples/mcp_servers.example.json).
218
+
219
+ Environment variable placeholders use this form:
220
+
221
+ ```text
222
+ ${ENV_NAME}
223
+ ${ENV_NAME:-default}
224
+ ```
225
+
226
+ Credential values can also be attached to a `Tools` instance:
227
+
228
+ ```python
229
+ tools.configure_integration(
230
+ "github",
231
+ {"access_token": "github_pat_..."},
232
+ )
233
+ ```
234
+
235
+ ## License
236
+
237
+ MIT
@@ -0,0 +1,63 @@
1
+ # 배포 가이드
2
+
3
+ 이 문서는 `eo-toolbelt`를 PyPI에 배포하는 maintainer용 문서입니다.
4
+
5
+ ## 준비
6
+
7
+ ```bash
8
+ cd C:\project\syyoo\eo-toolbelt\packages\eo-toolbelt
9
+ python -m pip install -U pip setuptools wheel build twine
10
+ python scripts\sync_runtime.py
11
+ ```
12
+
13
+ ## 검증
14
+
15
+ ```bash
16
+ python -m py_compile (Get-ChildItem -Recurse src -Filter *.py).FullName
17
+ python -m build
18
+ python -m twine check dist\*
19
+ ```
20
+
21
+ isolated build dependency 설치가 제한된 환경에서는 아래 명령을 사용합니다.
22
+
23
+ ```bash
24
+ python -m build --no-isolation
25
+ ```
26
+
27
+ import 확인:
28
+
29
+ ```bash
30
+ python -m venv .venv-test
31
+ .venv-test\Scripts\python -m pip install dist\eo_toolbelt-0.1.0-py3-none-any.whl
32
+ .venv-test\Scripts\python -c "from mcp_tools_sdk import Tools; print(Tools.__name__)"
33
+ ```
34
+
35
+ wheel metadata 확인:
36
+
37
+ ```bash
38
+ python -c "import zipfile; n=zipfile.ZipFile('dist/eo_toolbelt-0.1.0-py3-none-any.whl').namelist(); print(any(x.startswith('mcp_tools_sdk/') for x in n), any(x.startswith('app/') for x in n))"
39
+ ```
40
+
41
+ 기대 출력:
42
+
43
+ ```text
44
+ True False
45
+ ```
46
+
47
+ ## 업로드
48
+
49
+ 대상 index에 맞게 `.pypirc`를 설정한 뒤 업로드합니다. 예시는 [.pypirc.sample](.pypirc.sample)에 있습니다.
50
+
51
+ ```bash
52
+ python -m twine upload dist\*
53
+ ```
54
+
55
+ TestPyPI:
56
+
57
+ ```bash
58
+ python -m twine upload --repository testpypi dist\*
59
+ ```
60
+
61
+ ## 버전 관리
62
+
63
+ PyPI는 같은 버전의 파일 재업로드를 허용하지 않습니다. 배포 전 `pyproject.toml`의 `version`을 올립니다.
@@ -0,0 +1,63 @@
1
+ # Publishing
2
+
3
+ This guide is for maintainers publishing `eo-toolbelt` to PyPI.
4
+
5
+ ## Prepare
6
+
7
+ ```bash
8
+ cd C:\project\syyoo\eo-toolbelt\packages\eo-toolbelt
9
+ python -m pip install -U pip setuptools wheel build twine
10
+ python scripts\sync_runtime.py
11
+ ```
12
+
13
+ ## Verify
14
+
15
+ ```bash
16
+ python -m py_compile (Get-ChildItem -Recurse src -Filter *.py).FullName
17
+ python -m build
18
+ python -m twine check dist\*
19
+ ```
20
+
21
+ If your environment blocks isolated build dependency installation, use:
22
+
23
+ ```bash
24
+ python -m build --no-isolation
25
+ ```
26
+
27
+ Quick import check:
28
+
29
+ ```bash
30
+ python -m venv .venv-test
31
+ .venv-test\Scripts\python -m pip install dist\eo_toolbelt-0.1.0-py3-none-any.whl
32
+ .venv-test\Scripts\python -c "from mcp_tools_sdk import Tools; print(Tools.__name__)"
33
+ ```
34
+
35
+ Wheel metadata check:
36
+
37
+ ```bash
38
+ python -c "import zipfile; n=zipfile.ZipFile('dist/eo_toolbelt-0.1.0-py3-none-any.whl').namelist(); print(any(x.startswith('mcp_tools_sdk/') for x in n), any(x.startswith('app/') for x in n))"
39
+ ```
40
+
41
+ Expected output:
42
+
43
+ ```text
44
+ True False
45
+ ```
46
+
47
+ ## Upload
48
+
49
+ Use a `.pypirc` configured for the target index. See [.pypirc.sample](.pypirc.sample).
50
+
51
+ ```bash
52
+ python -m twine upload dist\*
53
+ ```
54
+
55
+ For TestPyPI:
56
+
57
+ ```bash
58
+ python -m twine upload --repository testpypi dist\*
59
+ ```
60
+
61
+ ## Versioning
62
+
63
+ Increase `version` in `pyproject.toml` before every PyPI upload. PyPI does not allow replacing an existing file for the same version.
@@ -0,0 +1,203 @@
1
+ # EO Toolbelt SDK
2
+
3
+ English documentation: [README.md](README.md)
4
+
5
+ EO Toolbelt SDK는 Python 애플리케이션에서 유틸리티 도구, 사용자 정의 Python 함수, MCP 서버 도구를 모델 provider에 연결할 수 있게 해주는 SDK입니다.
6
+
7
+ 주요 기능은 세 가지입니다.
8
+
9
+ - 사용 가능한 tool spec 조회
10
+ - 선택한 도구 이름으로 실행
11
+ - OpenAI, Anthropic, Gemini, Bedrock Converse 형식의 tool option 생성
12
+
13
+ SDK가 언어 모델을 직접 호출하지는 않습니다. 모델 호출 흐름은 애플리케이션 코드가 유지하고, SDK는 도구 schema와 실행 결과를 제공합니다.
14
+
15
+ ## 설치
16
+
17
+ ```bash
18
+ pip install eo-toolbelt
19
+ ```
20
+
21
+ import 패키지:
22
+
23
+ ```python
24
+ from mcp_tools_sdk import Tools
25
+ ```
26
+
27
+ ## 빠른 시작
28
+
29
+ ```python
30
+ import asyncio
31
+
32
+ from mcp_tools_sdk import Tools
33
+
34
+
35
+ async def main() -> None:
36
+ tools = Tools()
37
+
38
+ result = await tools.call_tool(
39
+ "safe_calculate",
40
+ {"expression": "(12 + 8) * 3"},
41
+ )
42
+ print(result)
43
+
44
+
45
+ asyncio.run(main())
46
+ ```
47
+
48
+ ## 모델 Tool Option 생성
49
+
50
+ ```python
51
+ from openai import AsyncOpenAI
52
+ from mcp_tools_sdk import Tools
53
+
54
+ client = AsyncOpenAI()
55
+ tools = Tools()
56
+
57
+ tool_options = await tools.model_tool_options(
58
+ provider="openai",
59
+ tool_names=["safe_calculate", "get_current_datetime"],
60
+ )
61
+
62
+ response = await client.responses.create(
63
+ model="gpt-5",
64
+ input="What is 12 * 8, and what time is it in Seoul?",
65
+ **tool_options,
66
+ )
67
+ ```
68
+
69
+ 모델이 tool call을 반환하면 provider 응답에서 도구 이름과 인자를 읽고 `tools.call_tool(...)`을 호출한 뒤, 결과를 다시 모델에 전달하면 됩니다.
70
+
71
+ ## Tools 생성자
72
+
73
+ ```python
74
+ tools = Tools(
75
+ config_path="mcp_servers.json",
76
+ timeout_seconds=30,
77
+ custom_tools=[my_custom_tool],
78
+ )
79
+ ```
80
+
81
+ | 옵션 | 타입 | 설명 |
82
+ | --- | --- | --- |
83
+ | `config_path` | `str | Path | None` | MCP 서버 설정 파일 경로입니다. 생략하면 built-in/custom 도구를 MCP discovery 없이 사용할 수 있습니다. |
84
+ | `timeout_seconds` | `float | None` | MCP 작업 timeout입니다. |
85
+ | `custom_tools` | iterable | 이 인스턴스에 등록할 Python callable 또는 `CustomTool` 객체입니다. |
86
+ | `backend` | internal backend | 테스트와 고급 embedding을 위한 대체 runtime backend입니다. |
87
+
88
+ ## 주요 메서드
89
+
90
+ ### `list_tools(...)`
91
+
92
+ 표준 tool spec 목록을 반환합니다.
93
+
94
+ ```python
95
+ catalog = await tools.list_tools(
96
+ include_internal=True,
97
+ include_mcp=True,
98
+ include_custom=True,
99
+ mcp_server_names=["fetch"],
100
+ selected_tool_names=["safe_calculate"],
101
+ )
102
+ ```
103
+
104
+ ### `call_tool(tool_name, arguments=None)`
105
+
106
+ built-in, custom, MCP 도구 중 하나를 실행합니다.
107
+
108
+ ```python
109
+ result = await tools.call_tool(
110
+ "safe_calculate",
111
+ {"expression": "7 * 6"},
112
+ )
113
+ ```
114
+
115
+ MCP 도구 이름은 아래 형식을 사용합니다.
116
+
117
+ ```text
118
+ mcp__{server_name}__{tool_name}
119
+ ```
120
+
121
+ ### `model_tool_options(provider, ...)`
122
+
123
+ tool spec을 provider별 모델 호출 필드로 변환합니다.
124
+
125
+ ```python
126
+ tool_options = await tools.model_tool_options(
127
+ provider="bedrock",
128
+ tool_names=["safe_calculate"],
129
+ tool_choice={"auto": {}},
130
+ )
131
+ ```
132
+
133
+ | Provider 값 | 반환 필드 |
134
+ | --- | --- |
135
+ | `openai`, `gpt`, `openai_responses` | OpenAI Responses API `tools`와 선택적 `tool_choice`. |
136
+ | `openai_chat_completions` | OpenAI Chat Completions `tools`와 선택적 `tool_choice`. |
137
+ | `anthropic`, `claude` | Anthropic Messages `tools`와 선택적 `tool_choice`. |
138
+ | `gemini` | Gemini `function_declarations` 기반 `tools`와 선택적 `tool_config`. |
139
+ | `bedrock`, `bedrock_converse` | Bedrock Converse `toolConfig`. |
140
+
141
+ ### `select_tools(query, ...)`
142
+
143
+ 카탈로그에서 작은 도구 목록을 선택합니다. LLM callback을 넘기면 모델 기반 선택을 사용하고, 생략하면 deterministic keyword fallback을 사용합니다.
144
+
145
+ ```python
146
+ tool_names = await tools.select_tools(
147
+ query="Calculate actual vs budget variance by department.",
148
+ max_tools=4,
149
+ llm=select_with_llm,
150
+ )
151
+ ```
152
+
153
+ ## Custom Tools
154
+
155
+ ```python
156
+ from typing import Literal
157
+
158
+ from mcp_tools_sdk import Tools, custom_tool
159
+
160
+
161
+ def apply_discount(amount: float, rate: float = 0.1) -> dict:
162
+ """Apply a discount rate."""
163
+ return {"amount": amount, "rate": rate, "total": amount * (1 - rate)}
164
+
165
+
166
+ @custom_tool(name="classify_priority")
167
+ def classify_priority(level: Literal["low", "high"]) -> str:
168
+ """Classify a priority level."""
169
+ return level.upper()
170
+
171
+
172
+ tools = Tools(custom_tools=[apply_discount, classify_priority])
173
+ ```
174
+
175
+ ## MCP 설정
176
+
177
+ MCP 서버 도구를 연결하려면 MCP 설정 파일을 전달합니다.
178
+
179
+ ```python
180
+ tools = Tools(config_path="mcp_servers.json")
181
+ ```
182
+
183
+ 예시는 [examples/mcp_servers.example.json](examples/mcp_servers.example.json)에 있습니다.
184
+
185
+ 환경변수 placeholder는 아래 형식을 사용합니다.
186
+
187
+ ```text
188
+ ${ENV_NAME}
189
+ ${ENV_NAME:-default}
190
+ ```
191
+
192
+ 인증 값은 `Tools` 인스턴스에 직접 연결할 수도 있습니다.
193
+
194
+ ```python
195
+ tools.configure_integration(
196
+ "github",
197
+ {"access_token": "github_pat_..."},
198
+ )
199
+ ```
200
+
201
+ ## 라이선스
202
+
203
+ MIT