mcp-as-code 0.1.0__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.
- maco/__init__.py +3 -0
- maco/_build_info.py +4 -0
- maco/cli.py +258 -0
- maco/codegen.py +680 -0
- maco/config.py +177 -0
- maco/gateway.py +305 -0
- maco/mcp_manager.py +157 -0
- maco/runner.py +104 -0
- maco/sandbox/__init__.py +43 -0
- maco/sandbox/core.py +216 -0
- maco/sandbox/providers/__init__.py +7 -0
- maco/sandbox/providers/base.py +69 -0
- maco/sandbox/providers/docker.py +228 -0
- maco/sandbox/providers/local.py +46 -0
- maco/sandbox/providers/matchlock.py +224 -0
- maco/serve_mcp.py +527 -0
- maco/templates/bash_description.j2 +8 -0
- maco/templates/code_execute_description.j2 +14 -0
- maco/templates/codegen/client.py.j2 +104 -0
- maco/templates/codegen/model.py.j2 +6 -0
- maco/templates/codegen/package_init.py.j2 +2 -0
- maco/templates/codegen/pyproject.toml.j2 +8 -0
- maco/templates/codegen/root_model.py.j2 +3 -0
- maco/templates/codegen/server_init.py.j2 +11 -0
- maco/templates/codegen/tool.py.j2 +38 -0
- maco/templates/codegen/type_alias.py.j2 +2 -0
- maco/templates/serve_mcp_instructions.j2 +17 -0
- maco/templates/server_catalog.j2 +8 -0
- maco/version.py +72 -0
- mcp_as_code-0.1.0.dist-info/METADATA +212 -0
- mcp_as_code-0.1.0.dist-info/RECORD +34 -0
- mcp_as_code-0.1.0.dist-info/WHEEL +4 -0
- mcp_as_code-0.1.0.dist-info/entry_points.txt +2 -0
- mcp_as_code-0.1.0.dist-info/licenses/LICENSE +203 -0
maco/__init__.py
ADDED
maco/_build_info.py
ADDED
maco/cli.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Command line interface for maco."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from .codegen import generate, generate_sandbox_sdk_from_gateway
|
|
10
|
+
from .config import ConfigError, load_config
|
|
11
|
+
from .gateway import ServeOptions, serve
|
|
12
|
+
from .runner import RunnerError, exit_with_error, run_code
|
|
13
|
+
from .sandbox import DEFAULT_SANDBOX_IMAGE, SANDBOX_SDK_ROOT
|
|
14
|
+
from .serve_mcp import ServeMcpOptions, serve_mcp
|
|
15
|
+
from .version import get_version_info
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
19
|
+
parser = argparse.ArgumentParser(
|
|
20
|
+
prog="maco",
|
|
21
|
+
description="Generate and execute Python code interfaces for MCP tools.",
|
|
22
|
+
)
|
|
23
|
+
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
24
|
+
|
|
25
|
+
version_parser = subparsers.add_parser("version", help="print maco version and release metadata")
|
|
26
|
+
version_parser.set_defaults(func=_cmd_version)
|
|
27
|
+
|
|
28
|
+
gen = subparsers.add_parser("gen", help="generate Python wrappers for configured MCP tools")
|
|
29
|
+
gen.add_argument("--config", default="mcp.json", help="MCP config path (default: mcp.json)")
|
|
30
|
+
gen.add_argument("--workspace", default=".maco", help="generated workspace directory (default: .maco)")
|
|
31
|
+
gen.add_argument("--server", help="only generate wrappers for one configured server")
|
|
32
|
+
gen.add_argument("--clean", action="store_true", help="remove the workspace before generating")
|
|
33
|
+
gen.set_defaults(func=_cmd_gen)
|
|
34
|
+
|
|
35
|
+
serve_parser = subparsers.add_parser("serve", help="run the local maco gateway server")
|
|
36
|
+
serve_parser.add_argument("--config", default="mcp.json", help="MCP config path (default: mcp.json)")
|
|
37
|
+
serve_parser.add_argument("--workspace", default=".maco", help="generated workspace directory (default: .maco)")
|
|
38
|
+
serve_parser.add_argument("--clean", action="store_true", help="remove the workspace before generating")
|
|
39
|
+
serve_parser.add_argument("--host", default="127.0.0.1", help="host to bind (default: 127.0.0.1)")
|
|
40
|
+
serve_parser.add_argument("--port", default=0, type=int, help="port to bind (default: 0, an ephemeral port)")
|
|
41
|
+
serve_parser.add_argument("--token", help="explicit bearer token for generated code")
|
|
42
|
+
serve_parser.add_argument("--no-token", action="store_true", help="disable gateway bearer-token protection")
|
|
43
|
+
serve_parser.set_defaults(func=_cmd_serve)
|
|
44
|
+
|
|
45
|
+
run = subparsers.add_parser("run", help="run a Python file with generated wrappers available")
|
|
46
|
+
run.add_argument("--workspace", help="generated workspace directory (default: auto-detect)")
|
|
47
|
+
run.add_argument("--cwd", help="working directory for the script")
|
|
48
|
+
run.add_argument("--python", help="Python version/interpreter to pass to uv run")
|
|
49
|
+
run.add_argument("code_path", help="Python file to execute")
|
|
50
|
+
run.add_argument("script_args", nargs=argparse.REMAINDER, help="arguments passed to the Python file")
|
|
51
|
+
run.set_defaults(func=_cmd_run)
|
|
52
|
+
|
|
53
|
+
serve_mcp = subparsers.add_parser(
|
|
54
|
+
"serve-mcp",
|
|
55
|
+
help="run an HTTP MCP server exposing sandboxed bash and code execution",
|
|
56
|
+
)
|
|
57
|
+
_add_serve_mcp_options(serve_mcp)
|
|
58
|
+
serve_mcp.set_defaults(func=_cmd_serve_mcp)
|
|
59
|
+
|
|
60
|
+
sandbox_bootstrap = subparsers.add_parser(
|
|
61
|
+
"sandbox-bootstrap",
|
|
62
|
+
help=argparse.SUPPRESS,
|
|
63
|
+
)
|
|
64
|
+
sandbox_bootstrap.add_argument("--gateway-url", help=argparse.SUPPRESS)
|
|
65
|
+
sandbox_bootstrap.add_argument("--gateway-token", help=argparse.SUPPRESS)
|
|
66
|
+
sandbox_bootstrap.add_argument("--workspace", default=SANDBOX_SDK_ROOT, help=argparse.SUPPRESS)
|
|
67
|
+
sandbox_bootstrap.add_argument("--no-clean", action="store_true", help=argparse.SUPPRESS)
|
|
68
|
+
sandbox_bootstrap.set_defaults(func=_cmd_sandbox_bootstrap)
|
|
69
|
+
|
|
70
|
+
return parser
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _add_serve_mcp_options(command: argparse.ArgumentParser) -> None:
|
|
74
|
+
command.add_argument("--config", default="mcp.json", help="MCP config path (default: mcp.json)")
|
|
75
|
+
command.add_argument(
|
|
76
|
+
"--provider",
|
|
77
|
+
choices=["local", "docker", "matchlock"],
|
|
78
|
+
default="local",
|
|
79
|
+
help="sandbox provider to use (default: local)",
|
|
80
|
+
)
|
|
81
|
+
command.add_argument("--workspace", default=".maco", help="generated workspace directory")
|
|
82
|
+
command.add_argument("--clean", action="store_true", help="remove the workspace before generating")
|
|
83
|
+
command.add_argument("--scratch", help="writable scratch directory for sandbox code")
|
|
84
|
+
command.add_argument(
|
|
85
|
+
"--gateway-file",
|
|
86
|
+
help="connect to an existing gateway.json instead of starting a managed gateway",
|
|
87
|
+
)
|
|
88
|
+
command.add_argument(
|
|
89
|
+
"--gateway-host",
|
|
90
|
+
help="host for the managed gateway started by serve-mcp",
|
|
91
|
+
)
|
|
92
|
+
command.add_argument(
|
|
93
|
+
"--gateway-port",
|
|
94
|
+
default=0,
|
|
95
|
+
type=int,
|
|
96
|
+
help="port for the managed gateway started by serve-mcp (default: 0)",
|
|
97
|
+
)
|
|
98
|
+
command.add_argument("--gateway-token", help="explicit bearer token for the managed gateway")
|
|
99
|
+
command.add_argument(
|
|
100
|
+
"--no-gateway-token",
|
|
101
|
+
action="store_true",
|
|
102
|
+
help="disable bearer-token protection for the managed gateway",
|
|
103
|
+
)
|
|
104
|
+
command.add_argument("--host", default="127.0.0.1", help="HTTP MCP bind host")
|
|
105
|
+
command.add_argument("--port", default=8789, type=int, help="HTTP MCP bind port")
|
|
106
|
+
command.add_argument("--timeout", default=60, type=int, help="default command timeout in seconds")
|
|
107
|
+
command.add_argument(
|
|
108
|
+
"--debug",
|
|
109
|
+
action="store_true",
|
|
110
|
+
help="log provider command summaries to server stderr",
|
|
111
|
+
)
|
|
112
|
+
command.add_argument(
|
|
113
|
+
"--image",
|
|
114
|
+
help=f"container image for docker/matchlock providers (default: {DEFAULT_SANDBOX_IMAGE})",
|
|
115
|
+
)
|
|
116
|
+
command.add_argument("--python-command", help="guest command prefix used by code_execute")
|
|
117
|
+
command.add_argument("--docker-binary", default="docker", help="docker binary path/name")
|
|
118
|
+
command.add_argument("--docker-network", help="docker network passed to `docker run --network`")
|
|
119
|
+
command.add_argument(
|
|
120
|
+
"--docker-gateway-host",
|
|
121
|
+
default="host.docker.internal",
|
|
122
|
+
help="hostname inside docker that reaches the host gateway",
|
|
123
|
+
)
|
|
124
|
+
command.add_argument(
|
|
125
|
+
"--docker-gateway-ip",
|
|
126
|
+
help="explicit host gateway IP to map inside Docker; usually auto-detected",
|
|
127
|
+
)
|
|
128
|
+
command.add_argument("--matchlock-binary", default="matchlock", help="matchlock binary path/name")
|
|
129
|
+
command.add_argument(
|
|
130
|
+
"--matchlock-gateway-host",
|
|
131
|
+
default="maco-gateway.internal",
|
|
132
|
+
help="hostname inside matchlock that reaches the host gateway",
|
|
133
|
+
)
|
|
134
|
+
command.add_argument(
|
|
135
|
+
"--matchlock-gateway-ip",
|
|
136
|
+
help="IP for --add-host <gateway-host>:<ip> inside matchlock (default: 192.168.100.1 for managed gateways)",
|
|
137
|
+
)
|
|
138
|
+
command.add_argument(
|
|
139
|
+
"--matchlock-allow-host",
|
|
140
|
+
action="append",
|
|
141
|
+
default=[],
|
|
142
|
+
help="extra host to allow from the matchlock sandbox (repeatable)",
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def main(argv: list[str] | None = None) -> int:
|
|
147
|
+
parser = build_parser()
|
|
148
|
+
args = parser.parse_args(argv)
|
|
149
|
+
try:
|
|
150
|
+
return args.func(args)
|
|
151
|
+
except (ConfigError, RunnerError, OSError, ValueError) as exc:
|
|
152
|
+
exit_with_error(exc)
|
|
153
|
+
return 0
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _cmd_version(args: argparse.Namespace) -> int:
|
|
157
|
+
info = get_version_info()
|
|
158
|
+
print(f"version: {info.version}")
|
|
159
|
+
print(f"commit: {info.commit_sha}")
|
|
160
|
+
print(f"release date: {info.release_date}")
|
|
161
|
+
return 0
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _cmd_gen(args: argparse.Namespace) -> int:
|
|
165
|
+
config = load_config(args.config)
|
|
166
|
+
stats = generate(
|
|
167
|
+
config,
|
|
168
|
+
workspace=args.workspace,
|
|
169
|
+
server_filter=args.server,
|
|
170
|
+
clean=args.clean,
|
|
171
|
+
)
|
|
172
|
+
print(f"Generated {stats.tool_count} tools from {stats.server_count} servers")
|
|
173
|
+
print(f"Workspace: {stats.workspace}")
|
|
174
|
+
print("Explore: rg --files {}/maco_generated/servers".format(stats.workspace))
|
|
175
|
+
return 0
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _cmd_serve(args: argparse.Namespace) -> int:
|
|
179
|
+
config = load_config(args.config)
|
|
180
|
+
stats = generate(config, workspace=args.workspace, clean=args.clean)
|
|
181
|
+
print(f"Generated {stats.tool_count} tools from {stats.server_count} servers")
|
|
182
|
+
print(f"Workspace: {stats.workspace}")
|
|
183
|
+
serve(
|
|
184
|
+
config,
|
|
185
|
+
ServeOptions(
|
|
186
|
+
host=args.host,
|
|
187
|
+
port=args.port,
|
|
188
|
+
workspace=args.workspace,
|
|
189
|
+
token=args.token,
|
|
190
|
+
use_token=not args.no_token,
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
return 0
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _cmd_run(args: argparse.Namespace) -> int:
|
|
197
|
+
script_args = list(args.script_args or [])
|
|
198
|
+
if script_args and script_args[0] == "--":
|
|
199
|
+
script_args = script_args[1:]
|
|
200
|
+
return run_code(
|
|
201
|
+
args.code_path,
|
|
202
|
+
script_args,
|
|
203
|
+
workspace=args.workspace,
|
|
204
|
+
cwd=args.cwd,
|
|
205
|
+
python=args.python,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _cmd_serve_mcp(args: argparse.Namespace) -> int:
|
|
210
|
+
serve_mcp(
|
|
211
|
+
ServeMcpOptions(
|
|
212
|
+
config=args.config,
|
|
213
|
+
provider=args.provider,
|
|
214
|
+
workspace=args.workspace,
|
|
215
|
+
clean=args.clean,
|
|
216
|
+
scratch=args.scratch,
|
|
217
|
+
gateway_file=args.gateway_file,
|
|
218
|
+
gateway_host=args.gateway_host,
|
|
219
|
+
gateway_port=args.gateway_port,
|
|
220
|
+
gateway_token=args.gateway_token,
|
|
221
|
+
gateway_use_token=not args.no_gateway_token,
|
|
222
|
+
host=args.host,
|
|
223
|
+
port=args.port,
|
|
224
|
+
timeout=args.timeout,
|
|
225
|
+
debug=args.debug,
|
|
226
|
+
image=args.image,
|
|
227
|
+
python_command=args.python_command,
|
|
228
|
+
docker_binary=args.docker_binary,
|
|
229
|
+
docker_network=args.docker_network,
|
|
230
|
+
docker_gateway_host=args.docker_gateway_host,
|
|
231
|
+
docker_gateway_ip=args.docker_gateway_ip,
|
|
232
|
+
matchlock_binary=args.matchlock_binary,
|
|
233
|
+
matchlock_gateway_host=args.matchlock_gateway_host,
|
|
234
|
+
matchlock_gateway_ip=args.matchlock_gateway_ip,
|
|
235
|
+
matchlock_allow_host=tuple(args.matchlock_allow_host or []),
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
return 0
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _cmd_sandbox_bootstrap(args: argparse.Namespace) -> int:
|
|
242
|
+
gateway_url = args.gateway_url or os.environ.get("MACO_GATEWAY_URL")
|
|
243
|
+
if not gateway_url:
|
|
244
|
+
raise ValueError("sandbox bootstrap requires --gateway-url or MACO_GATEWAY_URL")
|
|
245
|
+
stats = generate_sandbox_sdk_from_gateway(
|
|
246
|
+
gateway_url,
|
|
247
|
+
token=args.gateway_token or os.environ.get("MACO_GATEWAY_TOKEN"),
|
|
248
|
+
workspace=args.workspace,
|
|
249
|
+
clean=not args.no_clean,
|
|
250
|
+
)
|
|
251
|
+
print(f"Generated sandbox SDK with {stats.tool_count} tools from {stats.server_count} servers")
|
|
252
|
+
print(f"Workspace: {stats.workspace}")
|
|
253
|
+
print(f"Explore: rg --files {stats.workspace}/tools")
|
|
254
|
+
return 0
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
if __name__ == "__main__": # pragma: no cover
|
|
258
|
+
raise SystemExit(main(sys.argv[1:]))
|