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.
Files changed (51) hide show
  1. mcp_generator/__init__.py +60 -0
  2. mcp_generator/__main__.py +11 -0
  3. mcp_generator/a2a.py +238 -0
  4. mcp_generator/cli.py +722 -0
  5. mcp_generator/config.py +28 -0
  6. mcp_generator/display_renderers.py +445 -0
  7. mcp_generator/generate_client.py +702 -0
  8. mcp_generator/generator.py +677 -0
  9. mcp_generator/introspection.py +669 -0
  10. mcp_generator/models.py +183 -0
  11. mcp_generator/overlay.py +319 -0
  12. mcp_generator/register_mcp.py +288 -0
  13. mcp_generator/renderers.py +855 -0
  14. mcp_generator/run_mcp.py +170 -0
  15. mcp_generator/scripts/__init__.py +5 -0
  16. mcp_generator/scripts/generate_jwt_keypair.py +117 -0
  17. mcp_generator/scripts/generate_openapi_client.py +416 -0
  18. mcp_generator/scripts/test_with_inspector.py +175 -0
  19. mcp_generator/scripts/validate_openapi.py +434 -0
  20. mcp_generator/templates/authentication.py +284 -0
  21. mcp_generator/templates/cache_middleware.py +287 -0
  22. mcp_generator/templates/display_tools.py +551 -0
  23. mcp_generator/templates/dockerfile_template.py +251 -0
  24. mcp_generator/templates/event_store.py +189 -0
  25. mcp_generator/templates/fastmcp_template.json +99 -0
  26. mcp_generator/templates/oauth_provider.py +599 -0
  27. mcp_generator/templates/pyproject_template.toml +45 -0
  28. mcp_generator/templates/storage_backend.py +286 -0
  29. mcp_generator/templates/test/__init__.py +1 -0
  30. mcp_generator/templates/test/test_auth_flows.py +598 -0
  31. mcp_generator/templates/test/test_behavioral.py +838 -0
  32. mcp_generator/templates/test/test_cache.py +280 -0
  33. mcp_generator/templates/test/test_e2e_http_basic.py +548 -0
  34. mcp_generator/templates/test/test_e2e_openapi_features.py +662 -0
  35. mcp_generator/templates/test/test_e2e_performance.py +502 -0
  36. mcp_generator/templates/test/test_multi_auth.py +217 -0
  37. mcp_generator/templates/test/test_oauth_persistence.py +326 -0
  38. mcp_generator/templates/test/test_resources.py +253 -0
  39. mcp_generator/templates/test/test_runner.py +376 -0
  40. mcp_generator/templates/test/test_server_integration.py +516 -0
  41. mcp_generator/templates/test/test_tool_schemas.py +285 -0
  42. mcp_generator/templates/test/test_tools.py +178 -0
  43. mcp_generator/templates/test/test_transforms.py +228 -0
  44. mcp_generator/test_generator.py +372 -0
  45. mcp_generator/utils.py +220 -0
  46. mcp_generator/writers.py +713 -0
  47. mcp_generator-3.2.1.dist-info/METADATA +847 -0
  48. mcp_generator-3.2.1.dist-info/RECORD +51 -0
  49. mcp_generator-3.2.1.dist-info/WHEEL +4 -0
  50. mcp_generator-3.2.1.dist-info/entry_points.txt +4 -0
  51. 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
+ ]
@@ -0,0 +1,11 @@
1
+ """
2
+ MCP Generator - Package entry point.
3
+
4
+ Allows running the generator as a module:
5
+ python -m mcp_generator
6
+ """
7
+
8
+ from .cli import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
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
+ '''