mcp-minion 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. mcp_minion-0.1.0/.gitignore +9 -0
  2. mcp_minion-0.1.0/LICENSE +21 -0
  3. mcp_minion-0.1.0/PKG-INFO +149 -0
  4. mcp_minion-0.1.0/README.md +133 -0
  5. mcp_minion-0.1.0/e2e_tests/__init__.py +0 -0
  6. mcp_minion-0.1.0/e2e_tests/runs/mcp_ipython/config.json +16 -0
  7. mcp_minion-0.1.0/e2e_tests/runs/mcp_ipython/task.md +1 -0
  8. mcp_minion-0.1.0/e2e_tests/runs/multi_step/config.json +6 -0
  9. mcp_minion-0.1.0/e2e_tests/runs/multi_step/task.md +1 -0
  10. mcp_minion-0.1.0/e2e_tests/runs/no_tools/config.json +6 -0
  11. mcp_minion-0.1.0/e2e_tests/runs/no_tools/task.md +1 -0
  12. mcp_minion-0.1.0/e2e_tests/runs/simple_add/config.json +6 -0
  13. mcp_minion-0.1.0/e2e_tests/runs/simple_add/task.md +1 -0
  14. mcp_minion-0.1.0/e2e_tests/test_e2e.py +174 -0
  15. mcp_minion-0.1.0/pyproject.toml +64 -0
  16. mcp_minion-0.1.0/src/mcp_minion/__init__.py +29 -0
  17. mcp_minion-0.1.0/src/mcp_minion/agent.py +559 -0
  18. mcp_minion-0.1.0/src/mcp_minion/artifacts.py +81 -0
  19. mcp_minion-0.1.0/src/mcp_minion/cli.py +297 -0
  20. mcp_minion-0.1.0/src/mcp_minion/logging.py +168 -0
  21. mcp_minion-0.1.0/src/mcp_minion/mcp_client.py +321 -0
  22. mcp_minion-0.1.0/src/mcp_minion/test_mcp_server.py +91 -0
  23. mcp_minion-0.1.0/src/mcp_minion/tools.py +153 -0
  24. mcp_minion-0.1.0/tests/__init__.py +0 -0
  25. mcp_minion-0.1.0/tests/test_agent.py +232 -0
  26. mcp_minion-0.1.0/tests/test_artifacts.py +81 -0
  27. mcp_minion-0.1.0/tests/test_cli.py +334 -0
  28. mcp_minion-0.1.0/tests/test_kernel.py +124 -0
  29. mcp_minion-0.1.0/tests/test_logging.py +128 -0
  30. mcp_minion-0.1.0/tests/test_mcp_client.py +132 -0
  31. mcp_minion-0.1.0/tests/test_system_prompt.py +61 -0
  32. mcp_minion-0.1.0/tests/test_tools.py +139 -0
@@ -0,0 +1,9 @@
1
+ # Run logs written inside run folders by the agent
2
+ run_*.json
3
+
4
+ # Tool caches
5
+ .ruff_cache/
6
+ .pytest_cache/
7
+
8
+ # Local env / secrets
9
+ .env
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stefan Szeider
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,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-minion
3
+ Version: 0.1.0
4
+ Summary: A minimal ReAct agent over MCP servers via OpenRouter
5
+ Author-email: Stefan Szeider <stefan@szeider.net>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: <3.14,>=3.13
9
+ Requires-Dist: mcp>=1.25.0
10
+ Requires-Dist: openai>=1.0.0
11
+ Requires-Dist: python-dotenv>=1.0.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
14
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # mcp-minion
18
+
19
+ A minimal ReAct agent that drives one or more [MCP](https://modelcontextprotocol.io)
20
+ servers through an OpenAI-compatible API (OpenRouter by default). It reads a
21
+ task from a *run folder*, loops "think → call tool → observe" until the model
22
+ produces a final answer, and writes a complete JSON log of the run.
23
+
24
+ `mcp-minion` is developed in the [mcp-solver](https://github.com/szeider/mcp-solver)
25
+ monorepo, where it is used to run solver templates over code-execution MCP
26
+ servers, but it has no dependency on the solver and works with any MCP server.
27
+
28
+ ## Install
29
+
30
+ From the monorepo (workspace member):
31
+
32
+ ```bash
33
+ uv sync --package mcp-minion
34
+ ```
35
+
36
+ Or standalone:
37
+
38
+ ```bash
39
+ pip install -e minion
40
+ ```
41
+
42
+ Set an OpenRouter API key, either in `~/.mcp-minion` or in a run folder's `.env`:
43
+
44
+ ```
45
+ OPENROUTER_API_KEY=sk-...
46
+ ```
47
+
48
+ ## Run-folder config
49
+
50
+ A run folder holds everything for a single run:
51
+
52
+ | File | Required | Purpose |
53
+ | --- | --- | --- |
54
+ | `config.json` | yes | Model, agent, MCP server, and kernel settings |
55
+ | `task.md` | yes | The specific task for this run |
56
+ | `project.md` | no | Shared instructions prepended to the task |
57
+ | `files.system` target | no | System-prompt body (see below) |
58
+ | `run_*.json` | auto | Per-run JSON log (written automatically) |
59
+
60
+ `config.json` (nested format):
61
+
62
+ ```json
63
+ {
64
+ "model": {
65
+ "name": "google/gemini-2.5-flash",
66
+ "temperature": 0,
67
+ "max_tokens": 2048
68
+ },
69
+ "agent": { "max_steps": 10 },
70
+ "files": { "system": "system.md" },
71
+ "packages": ["z3-solver"],
72
+ "mcpServers": {
73
+ "ipython": { "command": "ipython_mcp", "args": [] }
74
+ }
75
+ }
76
+ ```
77
+
78
+ Key options:
79
+
80
+ - **`files.system`** — path (relative to the run folder) to a markdown file
81
+ whose contents become the system prompt. If the file contains the literal
82
+ placeholder `{tool_sections}`, the generated per-server tool list is spliced
83
+ in at that spot (via a literal replace, so other `{`/`}` in the file — e.g.
84
+ ASP templates — are left untouched); otherwise the tool list is appended
85
+ after the file contents. When `files.system` is absent, a built-in default
86
+ system prompt is used.
87
+ - **`packages`** — packages to preinstall in a code-execution kernel. On the
88
+ first call to a `python_exec` tool, if no `python_reset` has happened yet,
89
+ `mcp-minion` automatically calls `python_reset` with these packages and
90
+ reuses any returned `kernel_id` for subsequent `python_exec` calls.
91
+
92
+ ## Examples
93
+
94
+ ### Bundled test server
95
+
96
+ The package ships a tiny MCP server exposing `echo` and `add`:
97
+
98
+ ```json
99
+ {
100
+ "model": { "name": "google/gemini-2.5-flash", "temperature": 0 },
101
+ "agent": { "max_steps": 5 },
102
+ "mcpServers": {
103
+ "test": {
104
+ "command": "python",
105
+ "args": ["-m", "mcp_minion.test_mcp_server"]
106
+ }
107
+ }
108
+ }
109
+ ```
110
+
111
+ ```bash
112
+ mcp-minion path/to/run_folder -v
113
+ ```
114
+
115
+ ### IPython code execution
116
+
117
+ With an [`ipython_mcp`](https://pypi.org/project/ipython-mcp/) server available:
118
+
119
+ ```json
120
+ {
121
+ "model": { "name": "google/gemini-2.5-flash", "temperature": 0 },
122
+ "agent": { "max_steps": 5 },
123
+ "mcpServers": {
124
+ "ipython": { "command": "ipython_mcp", "args": [] }
125
+ }
126
+ }
127
+ ```
128
+
129
+ `task.md`:
130
+
131
+ ```
132
+ Use the python_exec tool to compute sum(range(1, 11)) and report the result.
133
+ ```
134
+
135
+ ## Artifacts
136
+
137
+ `mcp_minion.artifacts.extract_last_submission(log_or_steps, tool_name="submit_code")`
138
+ returns the `code` argument of the last *successful* call to a submission tool,
139
+ so callers can persist an agent's final code alongside the run log.
140
+
141
+ ## Tests
142
+
143
+ ```bash
144
+ uv run --with-editable ./minion --with pytest --with pytest-asyncio \
145
+ python -m pytest minion/tests -q
146
+ ```
147
+
148
+ The end-to-end tests under `minion/e2e_tests/` make real API calls and are
149
+ skipped unless `OPENROUTER_API_KEY` is set.
@@ -0,0 +1,133 @@
1
+ # mcp-minion
2
+
3
+ A minimal ReAct agent that drives one or more [MCP](https://modelcontextprotocol.io)
4
+ servers through an OpenAI-compatible API (OpenRouter by default). It reads a
5
+ task from a *run folder*, loops "think → call tool → observe" until the model
6
+ produces a final answer, and writes a complete JSON log of the run.
7
+
8
+ `mcp-minion` is developed in the [mcp-solver](https://github.com/szeider/mcp-solver)
9
+ monorepo, where it is used to run solver templates over code-execution MCP
10
+ servers, but it has no dependency on the solver and works with any MCP server.
11
+
12
+ ## Install
13
+
14
+ From the monorepo (workspace member):
15
+
16
+ ```bash
17
+ uv sync --package mcp-minion
18
+ ```
19
+
20
+ Or standalone:
21
+
22
+ ```bash
23
+ pip install -e minion
24
+ ```
25
+
26
+ Set an OpenRouter API key, either in `~/.mcp-minion` or in a run folder's `.env`:
27
+
28
+ ```
29
+ OPENROUTER_API_KEY=sk-...
30
+ ```
31
+
32
+ ## Run-folder config
33
+
34
+ A run folder holds everything for a single run:
35
+
36
+ | File | Required | Purpose |
37
+ | --- | --- | --- |
38
+ | `config.json` | yes | Model, agent, MCP server, and kernel settings |
39
+ | `task.md` | yes | The specific task for this run |
40
+ | `project.md` | no | Shared instructions prepended to the task |
41
+ | `files.system` target | no | System-prompt body (see below) |
42
+ | `run_*.json` | auto | Per-run JSON log (written automatically) |
43
+
44
+ `config.json` (nested format):
45
+
46
+ ```json
47
+ {
48
+ "model": {
49
+ "name": "google/gemini-2.5-flash",
50
+ "temperature": 0,
51
+ "max_tokens": 2048
52
+ },
53
+ "agent": { "max_steps": 10 },
54
+ "files": { "system": "system.md" },
55
+ "packages": ["z3-solver"],
56
+ "mcpServers": {
57
+ "ipython": { "command": "ipython_mcp", "args": [] }
58
+ }
59
+ }
60
+ ```
61
+
62
+ Key options:
63
+
64
+ - **`files.system`** — path (relative to the run folder) to a markdown file
65
+ whose contents become the system prompt. If the file contains the literal
66
+ placeholder `{tool_sections}`, the generated per-server tool list is spliced
67
+ in at that spot (via a literal replace, so other `{`/`}` in the file — e.g.
68
+ ASP templates — are left untouched); otherwise the tool list is appended
69
+ after the file contents. When `files.system` is absent, a built-in default
70
+ system prompt is used.
71
+ - **`packages`** — packages to preinstall in a code-execution kernel. On the
72
+ first call to a `python_exec` tool, if no `python_reset` has happened yet,
73
+ `mcp-minion` automatically calls `python_reset` with these packages and
74
+ reuses any returned `kernel_id` for subsequent `python_exec` calls.
75
+
76
+ ## Examples
77
+
78
+ ### Bundled test server
79
+
80
+ The package ships a tiny MCP server exposing `echo` and `add`:
81
+
82
+ ```json
83
+ {
84
+ "model": { "name": "google/gemini-2.5-flash", "temperature": 0 },
85
+ "agent": { "max_steps": 5 },
86
+ "mcpServers": {
87
+ "test": {
88
+ "command": "python",
89
+ "args": ["-m", "mcp_minion.test_mcp_server"]
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ ```bash
96
+ mcp-minion path/to/run_folder -v
97
+ ```
98
+
99
+ ### IPython code execution
100
+
101
+ With an [`ipython_mcp`](https://pypi.org/project/ipython-mcp/) server available:
102
+
103
+ ```json
104
+ {
105
+ "model": { "name": "google/gemini-2.5-flash", "temperature": 0 },
106
+ "agent": { "max_steps": 5 },
107
+ "mcpServers": {
108
+ "ipython": { "command": "ipython_mcp", "args": [] }
109
+ }
110
+ }
111
+ ```
112
+
113
+ `task.md`:
114
+
115
+ ```
116
+ Use the python_exec tool to compute sum(range(1, 11)) and report the result.
117
+ ```
118
+
119
+ ## Artifacts
120
+
121
+ `mcp_minion.artifacts.extract_last_submission(log_or_steps, tool_name="submit_code")`
122
+ returns the `code` argument of the last *successful* call to a submission tool,
123
+ so callers can persist an agent's final code alongside the run log.
124
+
125
+ ## Tests
126
+
127
+ ```bash
128
+ uv run --with-editable ./minion --with pytest --with pytest-asyncio \
129
+ python -m pytest minion/tests -q
130
+ ```
131
+
132
+ The end-to-end tests under `minion/e2e_tests/` make real API calls and are
133
+ skipped unless `OPENROUTER_API_KEY` is set.
File without changes
@@ -0,0 +1,16 @@
1
+ {
2
+ "model": {
3
+ "name": "google/gemini-3.1-flash-lite-preview",
4
+ "temperature": 0,
5
+ "max_tokens": 2048
6
+ },
7
+ "agent": {
8
+ "max_steps": 5
9
+ },
10
+ "mcpServers": {
11
+ "ipython": {
12
+ "command": "ipython_mcp",
13
+ "args": []
14
+ }
15
+ }
16
+ }
@@ -0,0 +1 @@
1
+ Use the python_exec tool to compute sum(range(1, 11)) and report the result.
@@ -0,0 +1,6 @@
1
+ {
2
+ "modelstring": "google/gemini-3.1-flash-lite-preview",
3
+ "max_steps": 10,
4
+ "temperature": 0,
5
+ "max_tokens": 2048
6
+ }
@@ -0,0 +1 @@
1
+ Calculate (8 * 7) + (12 - 5). Show the intermediate results.
@@ -0,0 +1,6 @@
1
+ {
2
+ "modelstring": "google/gemini-3.1-flash-lite-preview",
3
+ "max_steps": 3,
4
+ "temperature": 0,
5
+ "max_tokens": 512
6
+ }
@@ -0,0 +1 @@
1
+ What is 2 + 2? Answer directly without using any tools - this is a simple test.
@@ -0,0 +1,6 @@
1
+ {
2
+ "modelstring": "google/gemini-3.1-flash-lite-preview",
3
+ "max_steps": 5,
4
+ "temperature": 0,
5
+ "max_tokens": 1024
6
+ }
@@ -0,0 +1 @@
1
+ What is 15 + 27? Use the add tool to calculate this.
@@ -0,0 +1,174 @@
1
+ """End-to-end tests using google/gemini-2.5-flash-lite.
2
+
3
+ These tests make real API calls and require OPENROUTER_API_KEY to be set.
4
+ Run with: uv run pytest e2e_tests/ -v
5
+ """
6
+
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+
11
+ import pytest
12
+ from dotenv import load_dotenv
13
+
14
+ from mcp_minion.agent import Agent
15
+ from mcp_minion.cli import load_run_folder
16
+ from mcp_minion.logging import RunLogger
17
+ from mcp_minion.mcp_client import MCPManager
18
+ from mcp_minion.tools import create_default_registry
19
+
20
+ # Load environment variables
21
+ load_dotenv()
22
+
23
+ # Skip all tests if no API key
24
+ pytestmark = pytest.mark.skipif(
25
+ not os.getenv("OPENROUTER_API_KEY"),
26
+ reason="OPENROUTER_API_KEY not set",
27
+ )
28
+
29
+ # Path to e2e test run folders
30
+ E2E_RUNS = Path(__file__).parent / "runs"
31
+
32
+
33
+ def run_agent_from_folder(folder: Path) -> tuple:
34
+ """Run agent from a folder and return (result, logger)."""
35
+ api_key = os.getenv("OPENROUTER_API_KEY")
36
+ config, mcp_servers, prompt = load_run_folder(folder)
37
+ logger = RunLogger(log_dir=folder)
38
+ tools = create_default_registry()
39
+ if mcp_servers:
40
+ return _run_with_mcp(api_key, config, mcp_servers, prompt, tools, logger)
41
+ agent = Agent(api_key=api_key, tools=tools, config=config, logger=logger)
42
+ result = agent.run(prompt)
43
+ return result, logger
44
+
45
+
46
+ async def _run_with_mcp_async(api_key, config, mcp_servers, prompt, tools, logger):
47
+ """Run agent with MCP servers."""
48
+ mcp_manager = MCPManager(mcp_servers)
49
+ async with mcp_manager:
50
+ agent = Agent(
51
+ api_key=api_key,
52
+ tools=tools,
53
+ config=config,
54
+ logger=logger,
55
+ mcp_manager=mcp_manager,
56
+ )
57
+ result = await agent.run_async(prompt)
58
+ return result, logger
59
+
60
+
61
+ def _run_with_mcp(api_key, config, mcp_servers, prompt, tools, logger):
62
+ """Sync wrapper for MCP agent run."""
63
+ import asyncio
64
+
65
+ return asyncio.run(
66
+ _run_with_mcp_async(api_key, config, mcp_servers, prompt, tools, logger)
67
+ )
68
+
69
+
70
+ class TestSimpleAdd:
71
+ """Test simple addition using the add tool."""
72
+
73
+ def test_simple_add(self) -> None:
74
+ folder = E2E_RUNS / "simple_add"
75
+ result, logger = run_agent_from_folder(folder)
76
+
77
+ # Check that the answer contains 42 (15 + 27)
78
+ assert "42" in result.answer
79
+
80
+ # Check that at least one tool call was made
81
+ assert result.tool_calls_made >= 1
82
+
83
+ # Verify log file was created and is valid
84
+ log_data = json.loads(logger.log_path.read_text())
85
+ assert log_data["status"] == "completed"
86
+
87
+
88
+ class TestMultiStep:
89
+ """Test multi-step calculation requiring multiple tool calls."""
90
+
91
+ def test_multi_step_calculation(self) -> None:
92
+ folder = E2E_RUNS / "multi_step"
93
+ result, logger = run_agent_from_folder(folder)
94
+
95
+ # (8 * 7) + (12 - 5) = 56 + 7 = 63
96
+ assert "63" in result.answer
97
+
98
+ # Should make multiple tool calls
99
+ assert result.tool_calls_made >= 2
100
+
101
+ # Verify log contains multiple steps
102
+ log_data = json.loads(logger.log_path.read_text())
103
+ assert log_data["status"] == "completed"
104
+
105
+ # Count total tool calls in log
106
+ total_tool_calls = sum(
107
+ len(step.get("tool_calls", [])) for step in log_data["steps"]
108
+ )
109
+ assert total_tool_calls >= 2
110
+
111
+
112
+ class TestNoTools:
113
+ """Test question that model can answer directly without tools."""
114
+
115
+ def test_no_tools_needed(self) -> None:
116
+ folder = E2E_RUNS / "no_tools"
117
+ result, logger = run_agent_from_folder(folder)
118
+
119
+ # Should answer with "4" (or use a tool anyway - both are acceptable)
120
+ assert "4" in result.answer
121
+
122
+ # Verify log
123
+ log_data = json.loads(logger.log_path.read_text())
124
+ assert log_data["status"] == "completed"
125
+
126
+
127
+ class TestLogIntegrity:
128
+ """Test that logs are complete and well-formed."""
129
+
130
+ def test_log_has_all_fields(self) -> None:
131
+ folder = E2E_RUNS / "simple_add"
132
+ result, logger = run_agent_from_folder(folder)
133
+
134
+ log_data = json.loads(logger.log_path.read_text())
135
+
136
+ # Check required top-level fields
137
+ assert "run_id" in log_data
138
+ assert "started_at" in log_data
139
+ assert "completed_at" in log_data
140
+ assert "status" in log_data
141
+ assert "config" in log_data
142
+ assert "prompt" in log_data
143
+ assert "steps" in log_data
144
+ assert "result" in log_data
145
+
146
+ # Check config was logged
147
+ assert "model" in log_data["config"]
148
+ assert "max_steps" in log_data["config"]
149
+
150
+ # Check prompt was logged
151
+ assert len(log_data["prompt"]) > 0
152
+
153
+ # Check steps have required fields
154
+ for step in log_data["steps"]:
155
+ assert "step" in step
156
+ assert "started_at" in step
157
+
158
+
159
+ class TestMCPIPython:
160
+ """Test MCP integration with the IPython code execution server."""
161
+
162
+ def test_ipython_code_execution(self) -> None:
163
+ folder = E2E_RUNS / "mcp_ipython"
164
+ result, logger = run_agent_from_folder(folder)
165
+
166
+ # sum(range(1, 11)) = 55
167
+ assert "55" in result.answer
168
+
169
+ # Should have made at least one tool call
170
+ assert result.tool_calls_made >= 1
171
+
172
+ # Verify log
173
+ log_data = json.loads(logger.log_path.read_text())
174
+ assert log_data["status"] == "completed"
@@ -0,0 +1,64 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "mcp-minion"
7
+ version = "0.1.0"
8
+ description = "A minimal ReAct agent over MCP servers via OpenRouter"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ authors = [
12
+ { name = "Stefan Szeider", email = "stefan@szeider.net" },
13
+ ]
14
+ requires-python = ">=3.13,<3.14"
15
+ dependencies = [
16
+ "openai>=1.0.0",
17
+ "python-dotenv>=1.0.0",
18
+ "mcp>=1.25.0",
19
+ ]
20
+
21
+ [project.optional-dependencies]
22
+ dev = [
23
+ "pytest>=8.0.0",
24
+ "pytest-asyncio>=0.23.0",
25
+ ]
26
+
27
+ [project.scripts]
28
+ mcp-minion = "mcp_minion.cli:main"
29
+
30
+ [tool.hatch.build.targets.wheel]
31
+ packages = ["src/mcp_minion"]
32
+
33
+ [tool.pytest.ini_options]
34
+ asyncio_mode = "auto"
35
+ testpaths = ["tests"]
36
+
37
+ [tool.ruff]
38
+ target-version = "py313"
39
+ line-length = 88
40
+ fix = true
41
+
42
+ [tool.ruff.format]
43
+ quote-style = "double"
44
+ indent-style = "space"
45
+ docstring-code-format = true
46
+
47
+ [tool.ruff.lint]
48
+ select = [
49
+ "E", # pycodestyle errors
50
+ "F", # Pyflakes
51
+ "I", # isort
52
+ "UP", # pyupgrade
53
+ "B", # flake8-bugbear
54
+ "SIM", # flake8-simplify
55
+ "RUF", # Ruff-specific rules
56
+ ]
57
+ ignore = [
58
+ "E501", # line length (formatter handles this)
59
+ ]
60
+
61
+ [tool.ruff.lint.per-file-ignores]
62
+ "tests/*" = ["S101"]
63
+ "e2e_tests/*" = ["S101"]
64
+ "__init__.py" = ["F401"]
@@ -0,0 +1,29 @@
1
+ """mcp-minion: a minimal ReAct agent over MCP servers via OpenRouter."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ from mcp_minion.agent import Agent, AgentConfig, AgentResult
6
+ from mcp_minion.artifacts import extract_last_submission
7
+ from mcp_minion.logging import RunLogger
8
+ from mcp_minion.mcp_client import MCPManager, MCPResourceInfo, MCPToolInfo
9
+ from mcp_minion.tools import Tool, ToolRegistry, create_default_registry
10
+
11
+ try:
12
+ __version__ = version("mcp-minion")
13
+ except PackageNotFoundError: # pragma: no cover - source checkout without install
14
+ __version__ = "0.1.0"
15
+
16
+ __all__ = [
17
+ "Agent",
18
+ "AgentConfig",
19
+ "AgentResult",
20
+ "MCPManager",
21
+ "MCPResourceInfo",
22
+ "MCPToolInfo",
23
+ "RunLogger",
24
+ "Tool",
25
+ "ToolRegistry",
26
+ "__version__",
27
+ "create_default_registry",
28
+ "extract_last_submission",
29
+ ]