agent-mcp-framework 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.
- agent_mcp_framework-0.1.0/.gitignore +20 -0
- agent_mcp_framework-0.1.0/LICENSE +21 -0
- agent_mcp_framework-0.1.0/PKG-INFO +134 -0
- agent_mcp_framework-0.1.0/README.md +99 -0
- agent_mcp_framework-0.1.0/examples/code_review_server.py +180 -0
- agent_mcp_framework-0.1.0/examples/llm_pipeline.py +91 -0
- agent_mcp_framework-0.1.0/examples/quickstart.py +44 -0
- agent_mcp_framework-0.1.0/pyproject.toml +65 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/__init__.py +35 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/agent.py +190 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/cli.py +117 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/formatters.py +61 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/pipeline.py +238 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/py.typed +0 -0
- agent_mcp_framework-0.1.0/src/agent_mcp_framework/server.py +155 -0
- agent_mcp_framework-0.1.0/tests/__init__.py +0 -0
- agent_mcp_framework-0.1.0/tests/conftest.py +74 -0
- agent_mcp_framework-0.1.0/tests/test_agent.py +223 -0
- agent_mcp_framework-0.1.0/tests/test_cli.py +91 -0
- agent_mcp_framework-0.1.0/tests/test_formatters.py +90 -0
- agent_mcp_framework-0.1.0/tests/test_pipeline.py +292 -0
- agent_mcp_framework-0.1.0/tests/test_server.py +106 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jarrad Bermingham / Bifrost Labs
|
|
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,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-mcp-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python framework for building multi-agent MCP servers
|
|
5
|
+
Project-URL: Homepage, https://github.com/bifrostlabs/agent-mcp-framework
|
|
6
|
+
Project-URL: Documentation, https://github.com/bifrostlabs/agent-mcp-framework#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/bifrostlabs/agent-mcp-framework
|
|
8
|
+
Project-URL: Issues, https://github.com/bifrostlabs/agent-mcp-framework/issues
|
|
9
|
+
Author-email: Jarrad Bermingham <jarrad@bifrostlabs.ai>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agents,ai,llm,mcp,model-context-protocol,multi-agent
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: anthropic>=0.40.0
|
|
25
|
+
Requires-Dist: click>=8.0.0
|
|
26
|
+
Requires-Dist: mcp>=1.0.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: rich>=13.0.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest-asyncio>=0.24.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.5.0; extra == 'dev'
|
|
34
|
+
Description-Content-Type: text/markdown
|
|
35
|
+
|
|
36
|
+
# agent-mcp-framework
|
|
37
|
+
|
|
38
|
+
A Python framework for building multi-agent MCP (Model Context Protocol) servers.
|
|
39
|
+
|
|
40
|
+
Build production-ready multi-agent systems that expose their capabilities as MCP tools — ready to integrate with Claude, VSCode, and any MCP-compatible client.
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- **Agent abstractions** — `Agent`, `LLMAgent`, `FunctionAgent` with lifecycle hooks
|
|
45
|
+
- **Pipeline composition** — Sequential, Parallel, Conditional, and MapReduce patterns
|
|
46
|
+
- **MCP integration** — Expose agent pipelines as MCP tools over stdio or SSE
|
|
47
|
+
- **Output formatting** — JSON, Markdown, and plain text output modes
|
|
48
|
+
- **CLI** — Run servers and pipelines from the command line
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install agent-mcp-framework
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from agent_mcp_framework import Agent, AgentContext, AgentResult, SequentialPipeline, AgentMCPServer
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class AnalyzerAgent(Agent):
|
|
63
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
64
|
+
code = context.get("code", "")
|
|
65
|
+
issues = []
|
|
66
|
+
if len(code.splitlines()) > 500:
|
|
67
|
+
issues.append("File exceeds 500 lines — consider splitting")
|
|
68
|
+
if "import *" in code:
|
|
69
|
+
issues.append("Wildcard imports detected")
|
|
70
|
+
context.set("issues", issues)
|
|
71
|
+
return AgentResult(success=True, output={"issues": issues, "count": len(issues)})
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ScorerAgent(Agent):
|
|
75
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
76
|
+
issues = context.get("issues", [])
|
|
77
|
+
score = max(0, 100 - len(issues) * 15)
|
|
78
|
+
return AgentResult(success=True, output={"score": score, "grade": "A" if score >= 90 else "B" if score >= 70 else "C"})
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Compose agents into a pipeline
|
|
82
|
+
pipeline = SequentialPipeline("code-review", agents=[
|
|
83
|
+
AnalyzerAgent("analyzer", description="Find code issues"),
|
|
84
|
+
ScorerAgent("scorer", description="Score code quality"),
|
|
85
|
+
])
|
|
86
|
+
|
|
87
|
+
# Expose as an MCP server
|
|
88
|
+
server = AgentMCPServer("code-review-server", description="Multi-agent code review")
|
|
89
|
+
server.add_pipeline_tool(
|
|
90
|
+
pipeline,
|
|
91
|
+
name="review_code",
|
|
92
|
+
description="Analyze code quality and return a score",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
server.run() # Starts MCP server on stdio
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Agent Types
|
|
100
|
+
|
|
101
|
+
### `Agent` — Base class
|
|
102
|
+
Implement `run()` to define your agent's logic.
|
|
103
|
+
|
|
104
|
+
### `LLMAgent` — Claude-powered agent
|
|
105
|
+
Built-in Anthropic client with `complete()` helper for LLM calls.
|
|
106
|
+
|
|
107
|
+
### `FunctionAgent` — Quick inline agents
|
|
108
|
+
Wrap any async function as an agent without subclassing.
|
|
109
|
+
|
|
110
|
+
## Pipeline Patterns
|
|
111
|
+
|
|
112
|
+
| Pattern | Description |
|
|
113
|
+
|---------|-------------|
|
|
114
|
+
| `SequentialPipeline` | Run agents one after another, each seeing updated context |
|
|
115
|
+
| `ParallelPipeline` | Run agents concurrently with optional concurrency limits |
|
|
116
|
+
| `ConditionalPipeline` | Route to agents based on a condition function |
|
|
117
|
+
| `MapReducePipeline` | Split work, fan out, and reduce results |
|
|
118
|
+
|
|
119
|
+
## CLI
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Start an MCP server
|
|
123
|
+
agent-mcp serve my_project.server
|
|
124
|
+
|
|
125
|
+
# Run a pipeline directly
|
|
126
|
+
agent-mcp run my_project.pipeline --input '{"code": "import *"}'
|
|
127
|
+
|
|
128
|
+
# Show framework info
|
|
129
|
+
agent-mcp info
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# agent-mcp-framework
|
|
2
|
+
|
|
3
|
+
A Python framework for building multi-agent MCP (Model Context Protocol) servers.
|
|
4
|
+
|
|
5
|
+
Build production-ready multi-agent systems that expose their capabilities as MCP tools — ready to integrate with Claude, VSCode, and any MCP-compatible client.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Agent abstractions** — `Agent`, `LLMAgent`, `FunctionAgent` with lifecycle hooks
|
|
10
|
+
- **Pipeline composition** — Sequential, Parallel, Conditional, and MapReduce patterns
|
|
11
|
+
- **MCP integration** — Expose agent pipelines as MCP tools over stdio or SSE
|
|
12
|
+
- **Output formatting** — JSON, Markdown, and plain text output modes
|
|
13
|
+
- **CLI** — Run servers and pipelines from the command line
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install agent-mcp-framework
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from agent_mcp_framework import Agent, AgentContext, AgentResult, SequentialPipeline, AgentMCPServer
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AnalyzerAgent(Agent):
|
|
28
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
29
|
+
code = context.get("code", "")
|
|
30
|
+
issues = []
|
|
31
|
+
if len(code.splitlines()) > 500:
|
|
32
|
+
issues.append("File exceeds 500 lines — consider splitting")
|
|
33
|
+
if "import *" in code:
|
|
34
|
+
issues.append("Wildcard imports detected")
|
|
35
|
+
context.set("issues", issues)
|
|
36
|
+
return AgentResult(success=True, output={"issues": issues, "count": len(issues)})
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ScorerAgent(Agent):
|
|
40
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
41
|
+
issues = context.get("issues", [])
|
|
42
|
+
score = max(0, 100 - len(issues) * 15)
|
|
43
|
+
return AgentResult(success=True, output={"score": score, "grade": "A" if score >= 90 else "B" if score >= 70 else "C"})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# Compose agents into a pipeline
|
|
47
|
+
pipeline = SequentialPipeline("code-review", agents=[
|
|
48
|
+
AnalyzerAgent("analyzer", description="Find code issues"),
|
|
49
|
+
ScorerAgent("scorer", description="Score code quality"),
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
# Expose as an MCP server
|
|
53
|
+
server = AgentMCPServer("code-review-server", description="Multi-agent code review")
|
|
54
|
+
server.add_pipeline_tool(
|
|
55
|
+
pipeline,
|
|
56
|
+
name="review_code",
|
|
57
|
+
description="Analyze code quality and return a score",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if __name__ == "__main__":
|
|
61
|
+
server.run() # Starts MCP server on stdio
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Agent Types
|
|
65
|
+
|
|
66
|
+
### `Agent` — Base class
|
|
67
|
+
Implement `run()` to define your agent's logic.
|
|
68
|
+
|
|
69
|
+
### `LLMAgent` — Claude-powered agent
|
|
70
|
+
Built-in Anthropic client with `complete()` helper for LLM calls.
|
|
71
|
+
|
|
72
|
+
### `FunctionAgent` — Quick inline agents
|
|
73
|
+
Wrap any async function as an agent without subclassing.
|
|
74
|
+
|
|
75
|
+
## Pipeline Patterns
|
|
76
|
+
|
|
77
|
+
| Pattern | Description |
|
|
78
|
+
|---------|-------------|
|
|
79
|
+
| `SequentialPipeline` | Run agents one after another, each seeing updated context |
|
|
80
|
+
| `ParallelPipeline` | Run agents concurrently with optional concurrency limits |
|
|
81
|
+
| `ConditionalPipeline` | Route to agents based on a condition function |
|
|
82
|
+
| `MapReducePipeline` | Split work, fan out, and reduce results |
|
|
83
|
+
|
|
84
|
+
## CLI
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
# Start an MCP server
|
|
88
|
+
agent-mcp serve my_project.server
|
|
89
|
+
|
|
90
|
+
# Run a pipeline directly
|
|
91
|
+
agent-mcp run my_project.pipeline --input '{"code": "import *"}'
|
|
92
|
+
|
|
93
|
+
# Show framework info
|
|
94
|
+
agent-mcp info
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""Example: Multi-agent code review MCP server.
|
|
2
|
+
|
|
3
|
+
This demonstrates building a production-ready multi-agent system that:
|
|
4
|
+
1. Analyzes code for quality issues
|
|
5
|
+
2. Checks for security vulnerabilities
|
|
6
|
+
3. Reviews architecture patterns
|
|
7
|
+
4. Produces a scored report
|
|
8
|
+
|
|
9
|
+
Run as MCP server:
|
|
10
|
+
agent-mcp serve examples.code_review_server
|
|
11
|
+
|
|
12
|
+
Or run the pipeline directly:
|
|
13
|
+
agent-mcp run examples.code_review_server -i '{"code": "import os; os.system(input())"}'
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from agent_mcp_framework import (
|
|
17
|
+
Agent,
|
|
18
|
+
AgentContext,
|
|
19
|
+
AgentMCPServer,
|
|
20
|
+
AgentResult,
|
|
21
|
+
ParallelPipeline,
|
|
22
|
+
SequentialPipeline,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class QualityAnalyzer(Agent):
|
|
27
|
+
"""Checks code quality metrics."""
|
|
28
|
+
|
|
29
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
30
|
+
code = context.get("code", "")
|
|
31
|
+
lines = code.splitlines()
|
|
32
|
+
issues = []
|
|
33
|
+
|
|
34
|
+
if len(lines) > 500:
|
|
35
|
+
issues.append({"severity": "warning", "message": "File exceeds 500 lines"})
|
|
36
|
+
if any("import *" in line for line in lines):
|
|
37
|
+
issues.append({"severity": "error", "message": "Wildcard imports detected"})
|
|
38
|
+
if any(len(line) > 120 for line in lines):
|
|
39
|
+
issues.append({"severity": "info", "message": "Lines exceed 120 chars"})
|
|
40
|
+
has_docstring = any(
|
|
41
|
+
line.strip().startswith('"""') or line.strip().startswith("'''")
|
|
42
|
+
for line in lines
|
|
43
|
+
)
|
|
44
|
+
if not has_docstring:
|
|
45
|
+
issues.append({"severity": "info", "message": "No docstrings found"})
|
|
46
|
+
|
|
47
|
+
context.set("quality_issues", issues)
|
|
48
|
+
return AgentResult(
|
|
49
|
+
success=True,
|
|
50
|
+
output={"issues": issues, "count": len(issues)},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SecurityScanner(Agent):
|
|
55
|
+
"""Checks for common security issues."""
|
|
56
|
+
|
|
57
|
+
PATTERNS = [
|
|
58
|
+
("eval(", "Use of eval() — potential code injection"),
|
|
59
|
+
("exec(", "Use of exec() — potential code injection"),
|
|
60
|
+
("os.system(", "Use of os.system() — potential command injection"),
|
|
61
|
+
("subprocess.call(", "Use of subprocess.call with shell=True risk"),
|
|
62
|
+
("pickle.loads(", "Deserialization of untrusted data"),
|
|
63
|
+
("__import__(", "Dynamic import — potential security risk"),
|
|
64
|
+
("password", "Potential hardcoded credentials"),
|
|
65
|
+
("secret", "Potential hardcoded secrets"),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
69
|
+
code = context.get("code", "")
|
|
70
|
+
findings = []
|
|
71
|
+
|
|
72
|
+
for pattern, message in self.PATTERNS:
|
|
73
|
+
if pattern in code.lower():
|
|
74
|
+
findings.append({"severity": "critical", "message": message, "pattern": pattern})
|
|
75
|
+
|
|
76
|
+
context.set("security_findings", findings)
|
|
77
|
+
return AgentResult(
|
|
78
|
+
success=True,
|
|
79
|
+
output={"findings": findings, "count": len(findings)},
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class ArchitectureReviewer(Agent):
|
|
84
|
+
"""Reviews architectural patterns and best practices."""
|
|
85
|
+
|
|
86
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
87
|
+
code = context.get("code", "")
|
|
88
|
+
observations = []
|
|
89
|
+
|
|
90
|
+
if "class " in code:
|
|
91
|
+
class_count = code.count("class ")
|
|
92
|
+
if class_count > 5:
|
|
93
|
+
observations.append("Multiple classes in one file — consider splitting")
|
|
94
|
+
if code.count("def ") > 20:
|
|
95
|
+
observations.append("Many functions — consider modularizing")
|
|
96
|
+
if "global " in code:
|
|
97
|
+
observations.append("Global state detected — consider dependency injection")
|
|
98
|
+
if code.count("try:") > 10:
|
|
99
|
+
observations.append("Many try/except blocks — consider structured error handling")
|
|
100
|
+
|
|
101
|
+
context.set("architecture_notes", observations)
|
|
102
|
+
return AgentResult(
|
|
103
|
+
success=True,
|
|
104
|
+
output={"observations": observations, "count": len(observations)},
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ReportGenerator(Agent):
|
|
109
|
+
"""Combines all analysis into a final scored report."""
|
|
110
|
+
|
|
111
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
112
|
+
quality = context.get("quality_issues", [])
|
|
113
|
+
security = context.get("security_findings", [])
|
|
114
|
+
architecture = context.get("architecture_notes", [])
|
|
115
|
+
|
|
116
|
+
# Score calculation
|
|
117
|
+
score = 100
|
|
118
|
+
for issue in quality:
|
|
119
|
+
score -= {"error": 15, "warning": 10, "info": 3}.get(issue.get("severity", "info"), 5)
|
|
120
|
+
for finding in security:
|
|
121
|
+
score -= {"critical": 25, "high": 15, "medium": 10}.get(
|
|
122
|
+
finding.get("severity", "medium"), 10
|
|
123
|
+
)
|
|
124
|
+
score -= len(architecture) * 5
|
|
125
|
+
score = max(0, score)
|
|
126
|
+
|
|
127
|
+
grade = "A" if score >= 90 else "B" if score >= 70 else "C" if score >= 50 else "F"
|
|
128
|
+
|
|
129
|
+
report = {
|
|
130
|
+
"score": score,
|
|
131
|
+
"grade": grade,
|
|
132
|
+
"quality": {"count": len(quality), "issues": quality},
|
|
133
|
+
"security": {"count": len(security), "findings": security},
|
|
134
|
+
"architecture": {"count": len(architecture), "notes": architecture},
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return AgentResult(success=True, output=report)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# Build the pipeline: analyze in parallel, then generate report
|
|
141
|
+
analysis_pipeline = ParallelPipeline(
|
|
142
|
+
"analysis",
|
|
143
|
+
agents=[
|
|
144
|
+
QualityAnalyzer("quality", description="Code quality analysis"),
|
|
145
|
+
SecurityScanner("security", description="Security vulnerability scan"),
|
|
146
|
+
ArchitectureReviewer("architecture", description="Architecture review"),
|
|
147
|
+
],
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
report_pipeline = SequentialPipeline(
|
|
151
|
+
"report",
|
|
152
|
+
agents=[ReportGenerator("reporter", description="Generate scored report")],
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Combined pipeline for CLI usage
|
|
157
|
+
class PipelineRunner(Agent):
|
|
158
|
+
"""Runs the analysis pipeline then generates a report."""
|
|
159
|
+
|
|
160
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
161
|
+
await analysis_pipeline.execute(context)
|
|
162
|
+
result = await report_pipeline.execute(context)
|
|
163
|
+
if result.results:
|
|
164
|
+
return result.results[0]
|
|
165
|
+
return AgentResult(success=False, error="No report")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
pipeline = SequentialPipeline("code-review", agents=[PipelineRunner("runner")])
|
|
169
|
+
|
|
170
|
+
# MCP Server
|
|
171
|
+
server = AgentMCPServer("code-review", description="Multi-agent code review server")
|
|
172
|
+
|
|
173
|
+
server.add_pipeline_tool(
|
|
174
|
+
SequentialPipeline("review", agents=[PipelineRunner("runner")]),
|
|
175
|
+
name="review_code",
|
|
176
|
+
description="Analyze code for quality, security, and architecture issues.",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
server.run()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Example: LLM-powered multi-agent pipeline.
|
|
2
|
+
|
|
3
|
+
This shows how to use LLMAgent with Claude for intelligent analysis.
|
|
4
|
+
Requires ANTHROPIC_API_KEY environment variable.
|
|
5
|
+
|
|
6
|
+
Run:
|
|
7
|
+
python examples/llm_pipeline.py
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
|
|
12
|
+
from agent_mcp_framework import AgentContext, AgentResult, SequentialPipeline
|
|
13
|
+
from agent_mcp_framework.agent import LLMAgent
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SummarizerAgent(LLMAgent):
|
|
17
|
+
"""Summarizes text content using Claude."""
|
|
18
|
+
|
|
19
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
20
|
+
text = context.get("text", "")
|
|
21
|
+
if not text:
|
|
22
|
+
return AgentResult(success=False, error="No text provided")
|
|
23
|
+
|
|
24
|
+
summary = await self.complete(
|
|
25
|
+
f"Summarize this text in 2-3 sentences:\n\n{text}"
|
|
26
|
+
)
|
|
27
|
+
context.set("summary", summary)
|
|
28
|
+
return AgentResult(success=True, output=summary)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SentimentAgent(LLMAgent):
|
|
32
|
+
"""Analyzes sentiment of text using Claude."""
|
|
33
|
+
|
|
34
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
35
|
+
text = context.get("text", "")
|
|
36
|
+
if not text:
|
|
37
|
+
return AgentResult(success=False, error="No text provided")
|
|
38
|
+
|
|
39
|
+
analysis = await self.complete(
|
|
40
|
+
f"Analyze the sentiment of this text. "
|
|
41
|
+
f"Return one of: positive, negative, neutral, mixed.\n\n{text}"
|
|
42
|
+
)
|
|
43
|
+
context.set("sentiment", analysis.strip().lower())
|
|
44
|
+
return AgentResult(success=True, output=analysis.strip())
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InsightsAgent(LLMAgent):
|
|
48
|
+
"""Generates insights combining summary and sentiment."""
|
|
49
|
+
|
|
50
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
51
|
+
summary = context.get("summary", "")
|
|
52
|
+
sentiment = context.get("sentiment", "")
|
|
53
|
+
|
|
54
|
+
insights = await self.complete(
|
|
55
|
+
f"Given this summary: {summary}\n"
|
|
56
|
+
f"And this sentiment: {sentiment}\n\n"
|
|
57
|
+
f"Provide 3 actionable insights in bullet points."
|
|
58
|
+
)
|
|
59
|
+
return AgentResult(success=True, output=insights)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
pipeline = SequentialPipeline("text-analysis", agents=[
|
|
63
|
+
SummarizerAgent("summarizer", system_prompt="You are a concise summarizer."),
|
|
64
|
+
SentimentAgent("sentiment", system_prompt="You are a sentiment analyst."),
|
|
65
|
+
InsightsAgent("insights", system_prompt="You are a business analyst."),
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
async def main():
|
|
70
|
+
sample_text = """
|
|
71
|
+
The new product launch exceeded expectations with 50,000 signups in the first week.
|
|
72
|
+
Customer feedback has been overwhelmingly positive, with particular praise for the
|
|
73
|
+
intuitive interface and fast performance. However, several users reported issues with
|
|
74
|
+
the mobile experience and the onboarding flow could be smoother. The engineering team
|
|
75
|
+
is already working on fixes for the next release.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
ctx = AgentContext(data={"text": sample_text})
|
|
79
|
+
result = await pipeline.execute(ctx)
|
|
80
|
+
|
|
81
|
+
print(f"Pipeline: {result.pipeline_name}")
|
|
82
|
+
print(f"Success: {result.success}")
|
|
83
|
+
print(f"Duration: {result.duration_ms:.0f}ms\n")
|
|
84
|
+
for r in result.results:
|
|
85
|
+
print(f"--- {r.agent_name} ({r.duration_ms:.0f}ms) ---")
|
|
86
|
+
print(r.output)
|
|
87
|
+
print()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Minimal quickstart example showing core concepts.
|
|
2
|
+
|
|
3
|
+
Run:
|
|
4
|
+
python examples/quickstart.py
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
|
|
9
|
+
from agent_mcp_framework import Agent, AgentContext, AgentResult, SequentialPipeline
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class GreeterAgent(Agent):
|
|
13
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
14
|
+
name = context.get("name", "World")
|
|
15
|
+
greeting = f"Hello, {name}!"
|
|
16
|
+
context.set("greeting", greeting)
|
|
17
|
+
return AgentResult(success=True, output=greeting)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ShoutAgent(Agent):
|
|
21
|
+
async def run(self, context: AgentContext) -> AgentResult:
|
|
22
|
+
greeting = context.get("greeting", "")
|
|
23
|
+
shouted = greeting.upper()
|
|
24
|
+
return AgentResult(success=True, output=shouted)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
async def main():
|
|
28
|
+
pipeline = SequentialPipeline("greet-and-shout", agents=[
|
|
29
|
+
GreeterAgent("greeter", description="Says hello"),
|
|
30
|
+
ShoutAgent("shouter", description="Makes it loud"),
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
ctx = AgentContext(data={"name": "Developer"})
|
|
34
|
+
result = await pipeline.execute(ctx)
|
|
35
|
+
|
|
36
|
+
print(f"Pipeline: {result.pipeline_name}")
|
|
37
|
+
print(f"Success: {result.success}")
|
|
38
|
+
print(f"Duration: {result.duration_ms:.0f}ms")
|
|
39
|
+
for r in result.results:
|
|
40
|
+
print(f" [{r.agent_name}] {r.output}")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
asyncio.run(main())
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "agent-mcp-framework"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A Python framework for building multi-agent MCP servers"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Jarrad Bermingham", email = "jarrad@bifrostlabs.ai" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["mcp", "agents", "multi-agent", "llm", "ai", "model-context-protocol"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"mcp>=1.0.0",
|
|
30
|
+
"anthropic>=0.40.0",
|
|
31
|
+
"click>=8.0.0",
|
|
32
|
+
"pydantic>=2.0.0",
|
|
33
|
+
"rich>=13.0.0",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=8.0.0",
|
|
39
|
+
"pytest-asyncio>=0.24.0",
|
|
40
|
+
"pytest-cov>=5.0.0",
|
|
41
|
+
"ruff>=0.5.0",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
agent-mcp = "agent_mcp_framework.cli:main"
|
|
46
|
+
|
|
47
|
+
[project.urls]
|
|
48
|
+
Homepage = "https://github.com/bifrostlabs/agent-mcp-framework"
|
|
49
|
+
Documentation = "https://github.com/bifrostlabs/agent-mcp-framework#readme"
|
|
50
|
+
Repository = "https://github.com/bifrostlabs/agent-mcp-framework"
|
|
51
|
+
Issues = "https://github.com/bifrostlabs/agent-mcp-framework/issues"
|
|
52
|
+
|
|
53
|
+
[tool.hatch.build.targets.wheel]
|
|
54
|
+
packages = ["src/agent_mcp_framework"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
asyncio_mode = "auto"
|
|
59
|
+
|
|
60
|
+
[tool.ruff]
|
|
61
|
+
target-version = "py310"
|
|
62
|
+
line-length = 100
|
|
63
|
+
|
|
64
|
+
[tool.ruff.lint]
|
|
65
|
+
select = ["E", "F", "I", "N", "W"]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""agent-mcp-framework: Build multi-agent MCP servers in Python."""
|
|
2
|
+
|
|
3
|
+
from agent_mcp_framework.agent import (
|
|
4
|
+
Agent,
|
|
5
|
+
AgentContext,
|
|
6
|
+
AgentResult,
|
|
7
|
+
FunctionAgent,
|
|
8
|
+
LLMAgent,
|
|
9
|
+
)
|
|
10
|
+
from agent_mcp_framework.pipeline import (
|
|
11
|
+
ConditionalPipeline,
|
|
12
|
+
MapReducePipeline,
|
|
13
|
+
ParallelPipeline,
|
|
14
|
+
Pipeline,
|
|
15
|
+
PipelineResult,
|
|
16
|
+
SequentialPipeline,
|
|
17
|
+
)
|
|
18
|
+
from agent_mcp_framework.server import AgentMCPServer
|
|
19
|
+
|
|
20
|
+
__version__ = "0.1.0"
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"Agent",
|
|
24
|
+
"AgentContext",
|
|
25
|
+
"AgentResult",
|
|
26
|
+
"AgentMCPServer",
|
|
27
|
+
"ConditionalPipeline",
|
|
28
|
+
"FunctionAgent",
|
|
29
|
+
"LLMAgent",
|
|
30
|
+
"MapReducePipeline",
|
|
31
|
+
"ParallelPipeline",
|
|
32
|
+
"Pipeline",
|
|
33
|
+
"PipelineResult",
|
|
34
|
+
"SequentialPipeline",
|
|
35
|
+
]
|