mcp-generator 3.2.1__py3-none-any.whl
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.
- mcp_generator/__init__.py +60 -0
- mcp_generator/__main__.py +11 -0
- mcp_generator/a2a.py +238 -0
- mcp_generator/cli.py +722 -0
- mcp_generator/config.py +28 -0
- mcp_generator/display_renderers.py +445 -0
- mcp_generator/generate_client.py +702 -0
- mcp_generator/generator.py +677 -0
- mcp_generator/introspection.py +669 -0
- mcp_generator/models.py +183 -0
- mcp_generator/overlay.py +319 -0
- mcp_generator/register_mcp.py +288 -0
- mcp_generator/renderers.py +855 -0
- mcp_generator/run_mcp.py +170 -0
- mcp_generator/scripts/__init__.py +5 -0
- mcp_generator/scripts/generate_jwt_keypair.py +117 -0
- mcp_generator/scripts/generate_openapi_client.py +416 -0
- mcp_generator/scripts/test_with_inspector.py +175 -0
- mcp_generator/scripts/validate_openapi.py +434 -0
- mcp_generator/templates/authentication.py +284 -0
- mcp_generator/templates/cache_middleware.py +287 -0
- mcp_generator/templates/display_tools.py +551 -0
- mcp_generator/templates/dockerfile_template.py +251 -0
- mcp_generator/templates/event_store.py +189 -0
- mcp_generator/templates/fastmcp_template.json +99 -0
- mcp_generator/templates/oauth_provider.py +599 -0
- mcp_generator/templates/pyproject_template.toml +45 -0
- mcp_generator/templates/storage_backend.py +286 -0
- mcp_generator/templates/test/__init__.py +1 -0
- mcp_generator/templates/test/test_auth_flows.py +598 -0
- mcp_generator/templates/test/test_behavioral.py +838 -0
- mcp_generator/templates/test/test_cache.py +280 -0
- mcp_generator/templates/test/test_e2e_http_basic.py +548 -0
- mcp_generator/templates/test/test_e2e_openapi_features.py +662 -0
- mcp_generator/templates/test/test_e2e_performance.py +502 -0
- mcp_generator/templates/test/test_multi_auth.py +217 -0
- mcp_generator/templates/test/test_oauth_persistence.py +326 -0
- mcp_generator/templates/test/test_resources.py +253 -0
- mcp_generator/templates/test/test_runner.py +376 -0
- mcp_generator/templates/test/test_server_integration.py +516 -0
- mcp_generator/templates/test/test_tool_schemas.py +285 -0
- mcp_generator/templates/test/test_tools.py +178 -0
- mcp_generator/templates/test/test_transforms.py +228 -0
- mcp_generator/test_generator.py +372 -0
- mcp_generator/utils.py +220 -0
- mcp_generator/writers.py +713 -0
- mcp_generator-3.2.1.dist-info/METADATA +847 -0
- mcp_generator-3.2.1.dist-info/RECORD +51 -0
- mcp_generator-3.2.1.dist-info/WHEEL +4 -0
- mcp_generator-3.2.1.dist-info/entry_points.txt +4 -0
- mcp_generator-3.2.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP Generator Package.
|
|
3
|
+
|
|
4
|
+
A modular code generator for creating FastMCP 3.x servers from OpenAPI specifications.
|
|
5
|
+
|
|
6
|
+
Main modules:
|
|
7
|
+
- cli: Command-line interface and entry point
|
|
8
|
+
- generator: Core orchestration and generation logic
|
|
9
|
+
- introspection: API discovery and metadata extraction
|
|
10
|
+
- renderers: Code generation and template rendering
|
|
11
|
+
- models: Data structures for configuration and specs
|
|
12
|
+
- utils: Helper functions for sanitization and formatting
|
|
13
|
+
- templates: Template generators for middleware and auth
|
|
14
|
+
- writers: File writing and directory management
|
|
15
|
+
- test_generator: Generates authentication flow demonstration tests
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
python -m mcp_generator
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from importlib.metadata import version as _pkg_version
|
|
22
|
+
|
|
23
|
+
from .cli import main
|
|
24
|
+
from .generate_client import generate_client_package
|
|
25
|
+
from .generator import generate_all, generate_main_composition_server, generate_modular_servers
|
|
26
|
+
from .introspection import (
|
|
27
|
+
enrich_spec_tags,
|
|
28
|
+
get_api_metadata,
|
|
29
|
+
get_api_modules,
|
|
30
|
+
get_resource_endpoints,
|
|
31
|
+
get_security_config,
|
|
32
|
+
)
|
|
33
|
+
from .models import ApiMetadata, ModuleSpec, ParameterInfo, ResourceSpec, SecurityConfig, ToolSpec
|
|
34
|
+
from .test_generator import generate_auth_flow_tests, generate_tool_tests
|
|
35
|
+
from .utils import get_pydantic_model_schema, sanitize_name
|
|
36
|
+
|
|
37
|
+
__version__ = _pkg_version("mcp-generator")
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"main",
|
|
41
|
+
"generate_client_package",
|
|
42
|
+
"generate_all",
|
|
43
|
+
"generate_modular_servers",
|
|
44
|
+
"generate_main_composition_server",
|
|
45
|
+
"enrich_spec_tags",
|
|
46
|
+
"get_api_modules",
|
|
47
|
+
"get_api_metadata",
|
|
48
|
+
"get_security_config",
|
|
49
|
+
"get_resource_endpoints",
|
|
50
|
+
"ApiMetadata",
|
|
51
|
+
"SecurityConfig",
|
|
52
|
+
"ModuleSpec",
|
|
53
|
+
"ParameterInfo",
|
|
54
|
+
"ToolSpec",
|
|
55
|
+
"ResourceSpec",
|
|
56
|
+
"sanitize_name",
|
|
57
|
+
"get_pydantic_model_schema",
|
|
58
|
+
"generate_auth_flow_tests",
|
|
59
|
+
"generate_tool_tests",
|
|
60
|
+
]
|
mcp_generator/a2a.py
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""
|
|
2
|
+
A2A (Agent-to-Agent) adapter generation.
|
|
3
|
+
|
|
4
|
+
Generates an AgentCard JSON and a thin A2A adapter that exposes the
|
|
5
|
+
generated MCP server as an A2A-compatible agent.
|
|
6
|
+
|
|
7
|
+
See: https://github.com/a2aproject/A2A
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
from .models import ApiMetadata, ModuleSpec
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def generate_agent_card(
|
|
21
|
+
api_metadata: ApiMetadata,
|
|
22
|
+
modules: dict[str, ModuleSpec],
|
|
23
|
+
*,
|
|
24
|
+
agent_url: str = "http://localhost:8000",
|
|
25
|
+
) -> dict[str, Any]:
|
|
26
|
+
"""Build an A2A AgentCard from OpenAPI metadata and generated modules.
|
|
27
|
+
|
|
28
|
+
The AgentCard follows the A2A specification and describes the agent's
|
|
29
|
+
capabilities, skills, and supported content types so other agents or
|
|
30
|
+
orchestrators can discover and invoke it.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
api_metadata: Extracted API metadata.
|
|
34
|
+
modules: Generated MCP module specs (used to derive skills).
|
|
35
|
+
agent_url: Base URL where the A2A agent will be hosted.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
AgentCard as a JSON-serialisable dict.
|
|
39
|
+
"""
|
|
40
|
+
skills = []
|
|
41
|
+
for mod_name, mod in sorted(modules.items()):
|
|
42
|
+
# Each module becomes a skill
|
|
43
|
+
tag_label = mod_name.replace("_", " ")
|
|
44
|
+
skills.append(
|
|
45
|
+
{
|
|
46
|
+
"id": f"{mod_name}_skill",
|
|
47
|
+
"name": f"{tag_label.title()} Operations",
|
|
48
|
+
"description": f"Handles {tag_label} API operations via MCP tools ({mod.tool_count} tools).",
|
|
49
|
+
"tags": [mod_name, "mcp", "api"],
|
|
50
|
+
"examples": [f"Manage {tag_label} resources"],
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
card: dict[str, Any] = {
|
|
55
|
+
"name": api_metadata.title,
|
|
56
|
+
"description": api_metadata.description or f"A2A agent for {api_metadata.title}",
|
|
57
|
+
"url": agent_url,
|
|
58
|
+
"version": api_metadata.version,
|
|
59
|
+
"capabilities": {
|
|
60
|
+
"streaming": True,
|
|
61
|
+
"pushNotifications": False,
|
|
62
|
+
},
|
|
63
|
+
"defaultInputModes": ["text", "text/plain"],
|
|
64
|
+
"defaultOutputModes": ["text", "text/plain"],
|
|
65
|
+
"skills": skills,
|
|
66
|
+
}
|
|
67
|
+
return card
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def render_a2a_adapter(api_metadata: ApiMetadata) -> str:
|
|
71
|
+
"""Generate a Python adapter that exposes the MCP server as an A2A agent.
|
|
72
|
+
|
|
73
|
+
The adapter:
|
|
74
|
+
- Loads the AgentCard from ``agent_card.json``
|
|
75
|
+
- Wraps the existing FastMCP server and translates A2A requests to MCP tool calls
|
|
76
|
+
- Runs a Starlette app on a configurable port
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Python source code for ``a2a_adapter.py``.
|
|
80
|
+
"""
|
|
81
|
+
from .utils import sanitize_server_name
|
|
82
|
+
|
|
83
|
+
server_name = sanitize_server_name(api_metadata.title)
|
|
84
|
+
server_module = f"{server_name}_mcp_generated"
|
|
85
|
+
|
|
86
|
+
return f'''"""
|
|
87
|
+
A2A Adapter — exposes the MCP server as an A2A-compatible agent.
|
|
88
|
+
|
|
89
|
+
Translates incoming A2A task requests into MCP tool calls and streams
|
|
90
|
+
results back using the A2A protocol.
|
|
91
|
+
|
|
92
|
+
Requirements:
|
|
93
|
+
pip install a2a-sdk starlette uvicorn
|
|
94
|
+
|
|
95
|
+
Usage:
|
|
96
|
+
python a2a_adapter.py [--host 0.0.0.0] [--port 8000]
|
|
97
|
+
|
|
98
|
+
Auto-generated by mcp_generator.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
import asyncio
|
|
102
|
+
import json
|
|
103
|
+
import logging
|
|
104
|
+
import os
|
|
105
|
+
import sys
|
|
106
|
+
from pathlib import Path
|
|
107
|
+
|
|
108
|
+
logger = logging.getLogger(__name__)
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------------------------
|
|
111
|
+
# Lazy imports — fail gracefully if a2a-sdk is not installed
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
from a2a.server.apps import A2AStarletteApplication
|
|
116
|
+
from a2a.server.request_handlers import DefaultRequestHandler
|
|
117
|
+
from a2a.server.tasks import InMemoryTaskStore
|
|
118
|
+
from a2a.types import AgentCard, AgentSkill, AgentCapabilities
|
|
119
|
+
A2A_AVAILABLE = True
|
|
120
|
+
except ImportError:
|
|
121
|
+
A2A_AVAILABLE = False
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _load_agent_card() -> dict:
|
|
125
|
+
"""Load the generated agent_card.json from the same directory."""
|
|
126
|
+
card_path = Path(__file__).parent / "agent_card.json"
|
|
127
|
+
with open(card_path, encoding="utf-8") as f:
|
|
128
|
+
return json.load(f)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _build_agent_card(raw: dict, agent_url: str) -> "AgentCard":
|
|
132
|
+
"""Construct a typed AgentCard from the raw JSON dict."""
|
|
133
|
+
skills = [
|
|
134
|
+
AgentSkill(
|
|
135
|
+
id=s["id"],
|
|
136
|
+
name=s["name"],
|
|
137
|
+
description=s["description"],
|
|
138
|
+
tags=s.get("tags", []),
|
|
139
|
+
examples=s.get("examples", []),
|
|
140
|
+
)
|
|
141
|
+
for s in raw.get("skills", [])
|
|
142
|
+
]
|
|
143
|
+
return AgentCard(
|
|
144
|
+
name=raw["name"],
|
|
145
|
+
description=raw.get("description", ""),
|
|
146
|
+
url=agent_url,
|
|
147
|
+
version=raw.get("version", "1.0.0"),
|
|
148
|
+
capabilities=AgentCapabilities(
|
|
149
|
+
streaming=raw.get("capabilities", {{}}).get("streaming", True),
|
|
150
|
+
pushNotifications=raw.get("capabilities", {{}}).get("pushNotifications", False),
|
|
151
|
+
),
|
|
152
|
+
defaultInputModes=raw.get("defaultInputModes", ["text"]),
|
|
153
|
+
defaultOutputModes=raw.get("defaultOutputModes", ["text"]),
|
|
154
|
+
skills=skills,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ---------------------------------------------------------------------------
|
|
159
|
+
# Simple MCP-backed agent executor
|
|
160
|
+
# ---------------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
class MCPAgentExecutor:
|
|
163
|
+
"""Translates A2A task execution into MCP tool calls.
|
|
164
|
+
|
|
165
|
+
This executor:
|
|
166
|
+
1. Receives a user message from the A2A framework.
|
|
167
|
+
2. Forwards it to the MCP server (in-process) as a tool call.
|
|
168
|
+
3. Returns the tool result as the A2A task output.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
def __init__(self):
|
|
172
|
+
# Import the generated MCP server
|
|
173
|
+
sys.path.insert(0, str(Path(__file__).parent))
|
|
174
|
+
from {server_module} import mcp # noqa: F811
|
|
175
|
+
self.mcp = mcp
|
|
176
|
+
|
|
177
|
+
async def execute(self, task_input: str, **kwargs) -> str:
|
|
178
|
+
"""Execute a task by delegating to the MCP server's tools."""
|
|
179
|
+
# List available tools and try to match the input
|
|
180
|
+
tools = await self.mcp._tool_manager.list_tools()
|
|
181
|
+
tool_names = [t.name for t in tools]
|
|
182
|
+
|
|
183
|
+
# Simple heuristic: if input matches a tool name, call it directly
|
|
184
|
+
input_lower = task_input.lower().strip()
|
|
185
|
+
for tool in tools:
|
|
186
|
+
if tool.name.lower() == input_lower:
|
|
187
|
+
result = await self.mcp._tool_manager.call_tool(tool.name, {{}})
|
|
188
|
+
return str(result)
|
|
189
|
+
|
|
190
|
+
# Otherwise return available tools as guidance
|
|
191
|
+
return (
|
|
192
|
+
f"Available MCP tools: {{', '.join(tool_names)}}. "
|
|
193
|
+
f"Please specify which tool to call and its parameters."
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ---------------------------------------------------------------------------
|
|
198
|
+
# CLI entry point
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
def main():
|
|
202
|
+
import argparse
|
|
203
|
+
|
|
204
|
+
parser = argparse.ArgumentParser(description="A2A Adapter for {api_metadata.title}")
|
|
205
|
+
parser.add_argument("--host", default=os.getenv("A2A_HOST", "localhost"))
|
|
206
|
+
parser.add_argument("--port", type=int, default=int(os.getenv("A2A_PORT", "8000")))
|
|
207
|
+
args = parser.parse_args()
|
|
208
|
+
|
|
209
|
+
if not A2A_AVAILABLE:
|
|
210
|
+
print("❌ a2a-sdk is required: pip install a2a-sdk")
|
|
211
|
+
sys.exit(1)
|
|
212
|
+
|
|
213
|
+
raw_card = _load_agent_card()
|
|
214
|
+
agent_url = f"http://{{args.host}}:{{args.port}}"
|
|
215
|
+
agent_card = _build_agent_card(raw_card, agent_url)
|
|
216
|
+
|
|
217
|
+
executor = MCPAgentExecutor()
|
|
218
|
+
handler = DefaultRequestHandler(
|
|
219
|
+
agent_executor=executor,
|
|
220
|
+
task_store=InMemoryTaskStore(),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
server = A2AStarletteApplication(
|
|
224
|
+
agent_card=agent_card,
|
|
225
|
+
http_handler=handler,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
print(f"🤖 A2A Agent: {{agent_card.name}}")
|
|
229
|
+
print(f" URL: {{agent_url}}")
|
|
230
|
+
print(f" Skills: {{len(agent_card.skills)}}")
|
|
231
|
+
|
|
232
|
+
import uvicorn
|
|
233
|
+
uvicorn.run(server.build(), host=args.host, port=args.port)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
if __name__ == "__main__":
|
|
237
|
+
main()
|
|
238
|
+
'''
|