mcp-as-code 0.1.1__tar.gz → 0.1.4__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 (44) hide show
  1. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/.gitignore +1 -0
  2. mcp_as_code-0.1.4/PKG-INFO +148 -0
  3. mcp_as_code-0.1.4/README.md +135 -0
  4. mcp_as_code-0.1.4/VERSION.txt +1 -0
  5. mcp_as_code-0.1.4/docs/mcp-config.md +270 -0
  6. mcp_as_code-0.1.4/examples/serve-mcp/README.md +41 -0
  7. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/pyproject.toml +1 -0
  8. mcp_as_code-0.1.4/src/maco/_build_info.py +4 -0
  9. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/cli.py +4 -1
  10. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/config.py +85 -0
  11. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/mcp_manager.py +33 -13
  12. mcp_as_code-0.1.4/src/maco/oauth.py +640 -0
  13. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/__init__.py +4 -0
  14. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/core.py +12 -0
  15. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/serve_mcp.py +5 -3
  16. mcp_as_code-0.1.1/PKG-INFO +0 -212
  17. mcp_as_code-0.1.1/README.md +0 -199
  18. mcp_as_code-0.1.1/VERSION.txt +0 -1
  19. mcp_as_code-0.1.1/examples/serve-mcp/README.md +0 -91
  20. mcp_as_code-0.1.1/src/maco/_build_info.py +0 -4
  21. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/LICENSE +0 -0
  22. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/images/sandbox/README.md +0 -0
  23. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/__init__.py +0 -0
  24. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/codegen.py +0 -0
  25. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/gateway.py +0 -0
  26. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/runner.py +0 -0
  27. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/providers/__init__.py +0 -0
  28. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/providers/base.py +0 -0
  29. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/providers/docker.py +0 -0
  30. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/providers/local.py +0 -0
  31. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/sandbox/providers/matchlock.py +0 -0
  32. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/bash_description.j2 +0 -0
  33. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/code_execute_description.j2 +0 -0
  34. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/client.py.j2 +0 -0
  35. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/model.py.j2 +0 -0
  36. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/package_init.py.j2 +0 -0
  37. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/pyproject.toml.j2 +0 -0
  38. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/root_model.py.j2 +0 -0
  39. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/server_init.py.j2 +0 -0
  40. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/tool.py.j2 +0 -0
  41. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/codegen/type_alias.py.j2 +0 -0
  42. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/serve_mcp_instructions.j2 +0 -0
  43. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/templates/server_catalog.j2 +0 -0
  44. {mcp_as_code-0.1.1 → mcp_as_code-0.1.4}/src/maco/version.py +0 -0
@@ -49,3 +49,4 @@ gateway.json
49
49
  examples/**/.maco/
50
50
  examples/**/scratch/
51
51
  examples/**/maco-serve-mcp/
52
+ examples/**/.playwright-mcp/
@@ -0,0 +1,148 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcp-as-code
3
+ Version: 0.1.4
4
+ Summary: Execute MCP tools through generated Python code interfaces
5
+ License-Expression: Apache-2.0
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: jinja2>=3.1
9
+ Requires-Dist: matchlock==0.2.15
10
+ Requires-Dist: mcp>=1.24.0
11
+ Requires-Dist: pydantic>=2.0
12
+ Description-Content-Type: text/markdown
13
+
14
+ # maco
15
+
16
+ **Connect every MCP server you need, keeping your agent's context lean.**
17
+
18
+ As the number of MCP servers you connect grows, tool schemas and intermediate tool call results clutter your agent's context. `maco` (mcp-as-code) collapses them all into a single endpoint with a programmatic interface.
19
+
20
+ Instead of loading hundreds if not thousands of tool schemas upfront, `maco` reconstructs every MCP tool as Pydantic models and Python functions in a virtual filesystem and hands your agent just two of its favourite tools: `bash` to navigate, and `code_execute` to run. The agent discovers and composes tools as code, the thing frontier models do best.
21
+
22
+ ## How it works
23
+
24
+ **Small context footprint:** the agent starts with two tools (`bash` and `code_execute`), not every MCP tool schema upfront.
25
+
26
+ **Progressive discovery:** frontier models excel at navigating filesystems. By representing the tool interface as code on a filesystem, the agent can leverage `rg`, `fd` and all the POSIX tools to discover and execute relevant MCP tools.
27
+
28
+ ```bash
29
+ tools
30
+ ├── playwright
31
+ │ ├── browserClick.py
32
+ │ ├── browserClose.py
33
+ │ ├── ... many other tools
34
+ │ └── __init__.py
35
+ └── github
36
+ ├── addIssueComment.py
37
+ └── __init__.py
38
+ ```
39
+
40
+ **Programmatic leverage:** the agent is given a real programming language, Python, allowing it to orchestrate complex control flows with exceptional context-efficiency using loops, conditions, and state management.
41
+
42
+ ```python
43
+ from collections import Counter
44
+ from tools.github import listCommits
45
+
46
+ owner, repo, page, counts = "openclaw", "openclaw", 1, Counter()
47
+
48
+ while True:
49
+ commits = listCommits(owner=owner, repo=repo, perPage=100, page=page)
50
+ for commit in commits:
51
+ login = (commit.get("author") or {}).get("login")
52
+ if login and "bot" not in login.lower():
53
+ counts[login] += 1
54
+ if len(commits) < 100 or page >= 20:
55
+ break
56
+ page += 1
57
+
58
+ total = sum(counts.values())
59
+ for login, count in counts.most_common():
60
+ if count / total < 0.01:
61
+ break
62
+ print(f"@{login}: {count} commits ({count / total:.1%})")
63
+ ```
64
+
65
+ The example above illustrates the MCP code that will be executed to find the top contributors to an open-source repository.
66
+
67
+ ## Installation
68
+
69
+ Install the Python package `mcp-as-code`; it provides the `maco` executable:
70
+
71
+ ```bash
72
+ uv tool install mcp-as-code
73
+ ```
74
+
75
+ Then verify the CLI:
76
+
77
+ ```bash
78
+ maco version
79
+ ```
80
+
81
+ ## Quick start
82
+
83
+ Create a `mcp.json`:
84
+
85
+ ```json
86
+ {
87
+ "mcpServers": {
88
+ "playwright": {
89
+ "command": "npx",
90
+ "args": ["-y", "@playwright/mcp@latest"]
91
+ },
92
+ "github": {
93
+ "url": "https://api.githubcopilot.com/mcp/",
94
+ "headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ This config needs `npx` (for Playwright MCP), a GitHub token in `GITHUB_TOKEN`, and Docker if you use the `docker` provider.
101
+
102
+ Start the `maco` MCP server:
103
+
104
+ ```bash
105
+ maco serve-mcp --config mcp.json --provider docker
106
+ ```
107
+
108
+ Use `--provider local` for a faster, non-isolated local feedback loop.
109
+
110
+ By default this serves Streamable HTTP MCP at `http://127.0.0.1:8789/mcp`.
111
+
112
+ Configure an MCP client to connect to that endpoint:
113
+
114
+ <details>
115
+ <summary>Codex</summary>
116
+
117
+ ```bash
118
+ codex mcp add maco --url http://127.0.0.1:8789/mcp
119
+ ```
120
+
121
+ </details>
122
+
123
+ <details>
124
+ <summary>Claude Code</summary>
125
+
126
+ ```bash
127
+ claude mcp add --transport http maco http://127.0.0.1:8789/mcp
128
+ ```
129
+
130
+ </details>
131
+
132
+ See [`examples/serve-mcp`](examples/serve-mcp) for a complete example that wraps multiple upstream MCP servers behind one `maco` endpoint.
133
+
134
+ ## MCP config
135
+
136
+ See [`docs/mcp-config.md`](docs/mcp-config.md) for the full config reference, including environment expansion, headers, OAuth hints, token caching, and tool filtering.
137
+
138
+ ## Sandbox providers
139
+
140
+ Choose the execution provider with `--provider`:
141
+
142
+ - `local`: fastest feedback loop; runs commands as local subprocesses.
143
+ - `docker`: runs commands in a long-lived Docker container.
144
+ - `matchlock`: runs commands in a long-lived Matchlock micro-VM.
145
+
146
+ ## License
147
+
148
+ Apache License 2.0. See [`LICENSE`](LICENSE).
@@ -0,0 +1,135 @@
1
+ # maco
2
+
3
+ **Connect every MCP server you need, keeping your agent's context lean.**
4
+
5
+ As the number of MCP servers you connect grows, tool schemas and intermediate tool call results clutter your agent's context. `maco` (mcp-as-code) collapses them all into a single endpoint with a programmatic interface.
6
+
7
+ Instead of loading hundreds if not thousands of tool schemas upfront, `maco` reconstructs every MCP tool as Pydantic models and Python functions in a virtual filesystem and hands your agent just two of its favourite tools: `bash` to navigate, and `code_execute` to run. The agent discovers and composes tools as code, the thing frontier models do best.
8
+
9
+ ## How it works
10
+
11
+ **Small context footprint:** the agent starts with two tools (`bash` and `code_execute`), not every MCP tool schema upfront.
12
+
13
+ **Progressive discovery:** frontier models excel at navigating filesystems. By representing the tool interface as code on a filesystem, the agent can leverage `rg`, `fd` and all the POSIX tools to discover and execute relevant MCP tools.
14
+
15
+ ```bash
16
+ tools
17
+ ├── playwright
18
+ │ ├── browserClick.py
19
+ │ ├── browserClose.py
20
+ │ ├── ... many other tools
21
+ │ └── __init__.py
22
+ └── github
23
+ ├── addIssueComment.py
24
+ └── __init__.py
25
+ ```
26
+
27
+ **Programmatic leverage:** the agent is given a real programming language, Python, allowing it to orchestrate complex control flows with exceptional context-efficiency using loops, conditions, and state management.
28
+
29
+ ```python
30
+ from collections import Counter
31
+ from tools.github import listCommits
32
+
33
+ owner, repo, page, counts = "openclaw", "openclaw", 1, Counter()
34
+
35
+ while True:
36
+ commits = listCommits(owner=owner, repo=repo, perPage=100, page=page)
37
+ for commit in commits:
38
+ login = (commit.get("author") or {}).get("login")
39
+ if login and "bot" not in login.lower():
40
+ counts[login] += 1
41
+ if len(commits) < 100 or page >= 20:
42
+ break
43
+ page += 1
44
+
45
+ total = sum(counts.values())
46
+ for login, count in counts.most_common():
47
+ if count / total < 0.01:
48
+ break
49
+ print(f"@{login}: {count} commits ({count / total:.1%})")
50
+ ```
51
+
52
+ The example above illustrates the MCP code that will be executed to find the top contributors to an open-source repository.
53
+
54
+ ## Installation
55
+
56
+ Install the Python package `mcp-as-code`; it provides the `maco` executable:
57
+
58
+ ```bash
59
+ uv tool install mcp-as-code
60
+ ```
61
+
62
+ Then verify the CLI:
63
+
64
+ ```bash
65
+ maco version
66
+ ```
67
+
68
+ ## Quick start
69
+
70
+ Create a `mcp.json`:
71
+
72
+ ```json
73
+ {
74
+ "mcpServers": {
75
+ "playwright": {
76
+ "command": "npx",
77
+ "args": ["-y", "@playwright/mcp@latest"]
78
+ },
79
+ "github": {
80
+ "url": "https://api.githubcopilot.com/mcp/",
81
+ "headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" }
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ This config needs `npx` (for Playwright MCP), a GitHub token in `GITHUB_TOKEN`, and Docker if you use the `docker` provider.
88
+
89
+ Start the `maco` MCP server:
90
+
91
+ ```bash
92
+ maco serve-mcp --config mcp.json --provider docker
93
+ ```
94
+
95
+ Use `--provider local` for a faster, non-isolated local feedback loop.
96
+
97
+ By default this serves Streamable HTTP MCP at `http://127.0.0.1:8789/mcp`.
98
+
99
+ Configure an MCP client to connect to that endpoint:
100
+
101
+ <details>
102
+ <summary>Codex</summary>
103
+
104
+ ```bash
105
+ codex mcp add maco --url http://127.0.0.1:8789/mcp
106
+ ```
107
+
108
+ </details>
109
+
110
+ <details>
111
+ <summary>Claude Code</summary>
112
+
113
+ ```bash
114
+ claude mcp add --transport http maco http://127.0.0.1:8789/mcp
115
+ ```
116
+
117
+ </details>
118
+
119
+ See [`examples/serve-mcp`](examples/serve-mcp) for a complete example that wraps multiple upstream MCP servers behind one `maco` endpoint.
120
+
121
+ ## MCP config
122
+
123
+ See [`docs/mcp-config.md`](docs/mcp-config.md) for the full config reference, including environment expansion, headers, OAuth hints, token caching, and tool filtering.
124
+
125
+ ## Sandbox providers
126
+
127
+ Choose the execution provider with `--provider`:
128
+
129
+ - `local`: fastest feedback loop; runs commands as local subprocesses.
130
+ - `docker`: runs commands in a long-lived Docker container.
131
+ - `matchlock`: runs commands in a long-lived Matchlock micro-VM.
132
+
133
+ ## License
134
+
135
+ Apache License 2.0. See [`LICENSE`](LICENSE).
@@ -0,0 +1 @@
1
+ 0.1.4
@@ -0,0 +1,270 @@
1
+ # MCP configuration
2
+
3
+ `maco` reads Claude-style MCP configuration files with a top-level `mcpServers` object. The same config is used by `maco gen`, `maco serve`, and `maco serve-mcp`.
4
+
5
+ ```json
6
+ {
7
+ "mcpServers": {
8
+ "server-name": {
9
+ "command": "..."
10
+ }
11
+ }
12
+ }
13
+ ```
14
+
15
+ ## Supported transports
16
+
17
+ | Transport | When to use it | Required fields |
18
+ | --- | --- | --- |
19
+ | `stdio` | Local subprocess MCP servers | `command` |
20
+ | `http` / `streamable_http` | Remote Streamable HTTP MCP servers | `url` or `base_url` |
21
+ | `sse` | Deprecated SSE MCP servers | `url` or `base_url` |
22
+
23
+ If `type`, `server_type`, or `transport` is omitted, maco infers `http` when `url`/`base_url` is present and `stdio` otherwise.
24
+
25
+ ## Common fields
26
+
27
+ | Field | Type | Applies to | Description |
28
+ | --- | --- | --- | --- |
29
+ | `type`, `server_type`, `transport` | string | all | Transport name. `streamable-http` and `streamablehttp` are normalized to `streamable_http`. |
30
+ | `command` | string | stdio | Executable used to start the MCP server. |
31
+ | `args` | string array | stdio | Arguments passed to `command`. |
32
+ | `env` | object | stdio | Environment variables passed to the subprocess. Values expand `$VAR`, `${VAR}`, and `~`. |
33
+ | `cwd` | string | stdio | Working directory for the subprocess. |
34
+ | `url`, `base_url` | string | HTTP/SSE | MCP endpoint URL. Values expand `$VAR`, `${VAR}`, and `~`. |
35
+ | `headers` | object | HTTP/SSE | Static HTTP headers. Values expand environment variables. |
36
+ | `oauth` | object | HTTP/SSE | Optional OAuth hints and interaction settings. See [OAuth](#oauth). |
37
+ | `tools`, `tool_white_list`, `tool_whitelist` | string array | all | Optional allow-list of upstream tool names to expose. |
38
+
39
+ ## Stdio servers
40
+
41
+ Use stdio for MCP servers that run as local subprocesses.
42
+
43
+ ```json
44
+ {
45
+ "mcpServers": {
46
+ "filesystem": {
47
+ "command": "npx",
48
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ ### Environment variables
55
+
56
+ Prefer listing required environment variables under `env`. maco expands `$VAR` and `${VAR}` using the environment of the maco process, then passes the resolved values to the upstream MCP server.
57
+
58
+ ```json
59
+ {
60
+ "mcpServers": {
61
+ "github": {
62
+ "command": "docker",
63
+ "args": [
64
+ "run",
65
+ "-i",
66
+ "--rm",
67
+ "-e",
68
+ "GITHUB_PERSONAL_ACCESS_TOKEN",
69
+ "ghcr.io/github/github-mcp-server"
70
+ ],
71
+ "env": {
72
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
73
+ }
74
+ }
75
+ }
76
+ }
77
+ ```
78
+
79
+ Arguments and string fields are expanded too, so this is also valid:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "custom": {
85
+ "command": "uv",
86
+ "args": ["run", "server.py", "--token", "$TOKEN"],
87
+ "env": {"TOKEN": "$TOKEN"}
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ## Streamable HTTP servers
94
+
95
+ Use `type: "http"` or `type: "streamable_http"` for remote Streamable HTTP MCP servers.
96
+
97
+ ```json
98
+ {
99
+ "mcpServers": {
100
+ "remote": {
101
+ "type": "http",
102
+ "url": "https://example.com/mcp"
103
+ }
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Static headers
109
+
110
+ Use `headers` for API-key or static bearer-token servers. Static `Authorization` headers take precedence over OAuth and disable OAuth for that server.
111
+
112
+ ```json
113
+ {
114
+ "mcpServers": {
115
+ "remote": {
116
+ "type": "http",
117
+ "url": "https://example.com/mcp",
118
+ "headers": {
119
+ "Authorization": "Bearer ${MCP_TOKEN}"
120
+ }
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## SSE servers
127
+
128
+ SSE is supported for compatibility with older MCP servers. Set `type: "sse"` explicitly.
129
+
130
+ ```json
131
+ {
132
+ "mcpServers": {
133
+ "legacy": {
134
+ "type": "sse",
135
+ "url": "https://example.com/sse"
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ ## OAuth
142
+
143
+ For remote HTTP/SSE servers without a static `Authorization` header, maco can automatically perform OAuth when the upstream server returns an HTTP `401` with a Bearer challenge. The flow is:
144
+
145
+ 1. maco sends the MCP request without a bearer token.
146
+ 2. The upstream server responds with `WWW-Authenticate: Bearer ...`.
147
+ 3. maco discovers protected-resource and authorization-server metadata.
148
+ 4. maco uses configured client hints or dynamic client registration.
149
+ 5. maco opens or prints an authorization URL.
150
+ 6. maco receives the loopback callback, exchanges the code with PKCE, caches the token, and retries the MCP request.
151
+
152
+ You can omit `oauth` entirely when the provider supports standard discovery and dynamic client registration.
153
+
154
+ ```json
155
+ {
156
+ "mcpServers": {
157
+ "remote": {
158
+ "type": "http",
159
+ "url": "https://example.com/mcp"
160
+ }
161
+ }
162
+ }
163
+ ```
164
+
165
+ Add `oauth` when the provider needs hints such as a pre-registered client, scopes, a fixed callback URL, or non-standard metadata discovery.
166
+
167
+ ```json
168
+ {
169
+ "mcpServers": {
170
+ "remote": {
171
+ "type": "http",
172
+ "url": "https://example.com/mcp",
173
+ "oauth": {
174
+ "client_id": "${MCP_CLIENT_ID}",
175
+ "client_secret": "${MCP_CLIENT_SECRET}",
176
+ "scopes": ["mcp.read", "mcp.write"],
177
+ "redirect_uri": "http://127.0.0.1:1456/mcp/oauth/callback",
178
+ "auth_server_metadata_url": "https://auth.example.com/.well-known/oauth-authorization-server",
179
+ "interactive": "auto",
180
+ "open_browser": true,
181
+ "callback_timeout": "2m"
182
+ }
183
+ }
184
+ }
185
+ }
186
+ ```
187
+
188
+ ### OAuth fields
189
+
190
+ | Field | Type | Description |
191
+ | --- | --- | --- |
192
+ | `client_id` | string | Pre-registered OAuth client ID. Omit when dynamic client registration is supported. |
193
+ | `client_secret` | string | Optional client secret. When present, token requests use `client_secret_post`. |
194
+ | `scopes` | string array | Requested OAuth scopes. If omitted, maco uses scopes from the Bearer challenge or protected-resource metadata. |
195
+ | `redirect_uri` | string | Loopback callback URI. Defaults to an ephemeral `http://127.0.0.1:<port>/mcp/oauth/callback`. `:0` is allowed and is replaced with the actual bound port. |
196
+ | `auth_server_metadata_url` | string | Optional direct authorization-server metadata URL for providers with non-standard discovery. |
197
+ | `interactive` | string | `auto`, `always`, or `never`. `auto` allows browser authorization only in an interactive terminal. |
198
+ | `open_browser` | boolean | Whether to attempt opening the default browser. The URL is always printed as a fallback. |
199
+ | `callback_timeout` | number or string | Time to wait for the callback. Supports seconds as a number or strings like `30s`, `2m`, `1h`. |
200
+
201
+ ### OAuth cache and environment overrides
202
+
203
+ OAuth tokens and client registration metadata are cached under:
204
+
205
+ ```text
206
+ ~/.maco/mcp/oauth/
207
+ ```
208
+
209
+ Credential files are written with `0600` permissions.
210
+
211
+ Environment variables can override interaction behavior:
212
+
213
+ | Variable | Description |
214
+ | --- | --- |
215
+ | `MACO_MCP_OAUTH_INTERACTIVE` | Overrides `oauth.interactive`. Use `never` in CI/headless runs to fail fast. |
216
+ | `MACO_MCP_OAUTH_OPEN_BROWSER` | Overrides `oauth.open_browser`. Accepts values like `true`, `false`, `1`, `0`. |
217
+ | `MACO_MCP_OAUTH_CALLBACK_TIMEOUT` | Overrides `oauth.callback_timeout`. Accepts seconds or duration strings like `2m`. |
218
+
219
+ ## Tool filtering
220
+
221
+ Use `tools`, `tool_white_list`, or `tool_whitelist` to expose only selected upstream tools through generated wrappers and the maco gateway.
222
+
223
+ ```json
224
+ {
225
+ "mcpServers": {
226
+ "remote": {
227
+ "type": "http",
228
+ "url": "https://example.com/mcp",
229
+ "tools": ["search", "fetch"]
230
+ }
231
+ }
232
+ }
233
+ ```
234
+
235
+ Filtering uses the upstream MCP tool names, not generated Python function names.
236
+
237
+ ## Complete mixed example
238
+
239
+ ```json
240
+ {
241
+ "mcpServers": {
242
+ "filesystem": {
243
+ "command": "npx",
244
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
245
+ },
246
+ "github": {
247
+ "command": "docker",
248
+ "args": [
249
+ "run",
250
+ "-i",
251
+ "--rm",
252
+ "-e",
253
+ "GITHUB_PERSONAL_ACCESS_TOKEN",
254
+ "ghcr.io/github/github-mcp-server"
255
+ ],
256
+ "env": {
257
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
258
+ }
259
+ },
260
+ "remote": {
261
+ "type": "http",
262
+ "url": "https://example.com/mcp",
263
+ "oauth": {
264
+ "scopes": ["mcp.read"]
265
+ },
266
+ "tools": ["search", "fetch"]
267
+ }
268
+ }
269
+ }
270
+ ```
@@ -0,0 +1,41 @@
1
+ # `maco serve-mcp` example
2
+
3
+ This example shows how to expose several upstream MCP servers through one compact `maco serve-mcp` endpoint. The upstream servers here are:
4
+
5
+ - [Playwright MCP](https://playwright.dev/mcp/introduction), launched with `npx -y @playwright/mcp@latest`
6
+ - [GitHub MCP server](https://github.com/github/github-mcp-server), using GitHub's hosted Streamable HTTP endpoint at `https://api.githubcopilot.com/mcp/`
7
+
8
+ ## Prerequisites
9
+
10
+ - `uv`
11
+ - `node`/`npx`, for Playwright MCP
12
+ - A GitHub personal access token in `GITHUB_TOKEN`, used by the hosted GitHub MCP server
13
+ - Docker, only if you use the Docker sandbox provider
14
+ - Optional: Matchlock, for the Matchlock sandbox provider
15
+
16
+ If you are already authenticated with the GitHub CLI, export a token directly:
17
+
18
+ ```bash
19
+ export GITHUB_TOKEN=$(gh auth token)
20
+ ```
21
+
22
+ ## 1. Start `maco serve-mcp`
23
+
24
+ The short path is to run from this example directory so the defaults line up with the local files:
25
+
26
+ ```bash
27
+ cd examples/serve-mcp
28
+ uv run maco serve-mcp --provider local
29
+ ```
30
+
31
+ This uses `mcp.json`, writes `.maco/gateway.json`, uses `maco-serve-mcp/` as scratch, starts the gateway, and serves HTTP MCP at `http://127.0.0.1:8789/mcp`. Add `--clean` only when you want to recreate the local generated SDK from scratch.
32
+
33
+ ## 2. Connect an agent to the MCP gateway
34
+
35
+ Configure your MCP client with `mcp-client.json` to connect to `maco serve-mcp`. For example, in your MCP client's settings, set the MCP server URL to `http://127.0.0.1:8789/mcp`.
36
+
37
+ If you are using codex you may connect to it via:
38
+
39
+ ```bash
40
+ codex mcp add maco --url http://127.0.0.1:8789/mcp
41
+ ```
@@ -36,6 +36,7 @@ packages = ["src/maco"]
36
36
  [tool.hatch.build.targets.sdist]
37
37
  include = [
38
38
  "src/maco",
39
+ "docs",
39
40
  "README.md",
40
41
  "LICENSE",
41
42
  "VERSION.txt",
@@ -0,0 +1,4 @@
1
+ """Build metadata embedded during release builds."""
2
+
3
+ COMMIT_SHA = "b592f828ae0d4766fbeff34d47d0b10e6f4158e1"
4
+ RELEASE_DATE = "2026-06-16"
@@ -133,7 +133,10 @@ def _add_serve_mcp_options(command: argparse.ArgumentParser) -> None:
133
133
  )
134
134
  command.add_argument(
135
135
  "--matchlock-gateway-ip",
136
- help="IP for --add-host <gateway-host>:<ip> inside matchlock (default: 192.168.100.1 for managed gateways)",
136
+ help=(
137
+ "IP for --add-host <gateway-host>:<ip> inside matchlock "
138
+ "(managed default: 192.168.64.1 on macOS, 192.168.100.1 elsewhere)"
139
+ ),
137
140
  )
138
141
  command.add_argument(
139
142
  "--matchlock-allow-host",