mcpsync-cli 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Repo Factory
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,240 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcpsync-cli
3
+ Version: 0.1.0
4
+ Summary: Synchronous MCP Python client — call MCP servers from sync Python without async/await
5
+ Author-email: Repo Factory <noreply@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/prasad-a-abhishek/mcpsync
8
+ Project-URL: Repository, https://github.com/prasad-a-abhishek/mcpsync
9
+ Project-URL: Issues, https://github.com/prasad-a-abhishek/mcpsync/issues
10
+ Keywords: mcp,model-context-protocol,synchronous,sync,client,stdio,http,streamable-http
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: mcp>=1.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: pytest>=7.0; extra == "dev"
26
+ Dynamic: license-file
27
+
28
+ # mcpsync
29
+
30
+ [![PyPI](https://img.shields.io/badge/version-0.1.0-blue)](https://pypi.org/project/mcpsync)
31
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
32
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)
33
+ [![Tests](https://img.shields.io/badge/tests-129%20passing-brightgreen.svg)](tests/)
34
+ [![Min runtime deps](https://img.shields.io/badge/dependencies-mcp-blue)](pyproject.toml)
35
+
36
+ > **Synchronous Python API for MCP servers — stdio and HTTP transports, plus a CLI for ad-hoc inspection.**
37
+
38
+ `mcpsync` wraps the official `mcp` Python SDK's async client behind a
39
+ blocking `SyncMCPClient` so you can call MCP tools, list resources, and
40
+ read resources from synchronous code (CLI tools, Django views, Flask
41
+ handlers, scripts). It also ships a CLI for ad-hoc server inspection
42
+ from the shell.
43
+
44
+ ## Quick Start
45
+
46
+ Install from source:
47
+
48
+ ```bash
49
+ pip install git+https://github.com/prasad-a-abhishek/mcpsync.git
50
+ ```
51
+
52
+ Connect to a stdio MCP server and call a tool:
53
+
54
+ ```python
55
+ from mcpsync import SyncMCPClient, StdioServerParameters
56
+
57
+ params = StdioServerParameters(
58
+ command="python", args=("-m", "my_mcp_server"),
59
+ env={"DEBUG": "1"},
60
+ cwd="/srv/myapp",
61
+ )
62
+
63
+ with SyncMCPClient(params) as client:
64
+ tools = client.list_tools()
65
+ for tool in tools:
66
+ print(f"- {tool.name}: {tool.description}")
67
+
68
+ result = client.call_tool("add", {"a": 2, "b": 3})
69
+ for block in result.content:
70
+ print(block.text)
71
+ ```
72
+
73
+ Connect to an HTTP MCP server:
74
+
75
+ ```python
76
+ from mcpsync import SyncMCPClient, HttpServerParameters
77
+
78
+ params = HttpServerParameters(
79
+ url="https://mcp.example.com/api",
80
+ headers={"Authorization": "Bearer ..."},
81
+ timeout=10.0,
82
+ )
83
+
84
+ with SyncMCPClient(params) as client:
85
+ resources = client.list_resources()
86
+ contents = client.read_resource(resources[0].uri)
87
+ ```
88
+
89
+ Turn any async MCP helper into a sync function with the `@sync`
90
+ decorator:
91
+
92
+ ```python
93
+ from mcpsync import sync
94
+
95
+ @sync
96
+ async def load_schema(server_url: str) -> dict:
97
+ async with some_async_helper(server_url) as helper:
98
+ return await helper.fetch_schema()
99
+ ```
100
+
101
+ ## ⚡ Performance & Benchmarks
102
+
103
+ We publish `benchmarks/BENCHMARK.md` per Invariant 14 — including
104
+ the methodology caveat. The honest summary: mcpsync uses
105
+ `asyncio.Runner` per call, which actually benchmarks ~15% faster
106
+ than the SDK's `asyncio.run` pattern in this setup (interpreter
107
+ boot dominates; relative comparison is meaningful). Reproduce
108
+ locally:
109
+
110
+ ```bash
111
+ python benchmarks/run_benchmark.py
112
+ ```
113
+
114
+ The full results table, methodology, and the trade-off
115
+ transparency statement are in
116
+ [benchmarks/BENCHMARK.md](benchmarks/BENCHMARK.md).
117
+
118
+ ## Why `mcpsync`? (Problem & Trade-Off Statement)
119
+
120
+ The MCP Python SDK ships an **async-only** client. Synchronous callers
121
+ (Django views, CLI tools, scripts) hit the same wall: every
122
+ `asyncio.run()` from sync code re-creates an event loop, leaks
123
+ resources, and breaks under `asyncio.run()` recursion if the
124
+ surrounding runtime already runs a loop.
125
+
126
+ Two GitHub issues document the gap:
127
+
128
+ - [`modelcontextprotocol/python-sdk#1223`](https://github.com/modelcontextprotocol/python-sdk/issues/1223) — "Sync client API" (open, multiple reactions)
129
+ - `modelcontextprotocol/python-sdk` — the SDK explicitly documents
130
+ the `client.session.stdio` use pattern as **async only**.
131
+
132
+ **What mcpsync is:** a thin wrapper that gives you `SyncMCPClient` +
133
+ a `@sync` decorator, plus a CLI for ad-hoc inspection. It uses the
134
+ official `mcp` SDK under the hood and never re-implements the JSON-RPC
135
+ protocol or transport.
136
+
137
+ **What mcpsync is NOT:**
138
+ - Not a replacement for the `mcp` SDK — it depends on it.
139
+ - Not an MCP server implementation. It's a client.
140
+ - Not magic. Each `SyncMCPClient.list_tools()` call creates a
141
+ one-shot event loop on a worker thread. If you need 100k calls/sec,
142
+ use the SDK's async client directly.
143
+
144
+ **Trade-offs you accept by using mcpsync:**
145
+ - Each `SyncMCPClient` call creates a fresh `asyncio.Runner` on a
146
+ worker thread. The bench shows this is ~15% faster than the SDK
147
+ baseline, but for a true long-lived async loop you should use
148
+ the SDK's async client directly.
149
+ - The close path needs a wall-clock fuse to escape a known deadlock
150
+ in the `mcp` SDK's `stdio_client.__aexit__` shielded cancel scope.
151
+ Without the fuse the caller's process hangs. See
152
+ `src/mcpsync/client.py::_run_bounded` and the docstring on
153
+ `SyncMCPClient.close()`.
154
+ - HTTP transport requires the `mcp` SDK's optional `httpx2` dep,
155
+ which is re-exported by `mcp` for convenience.
156
+
157
+ ## Key Features & Complete API / CLI Reference
158
+
159
+ ### Library API
160
+
161
+ | Name | Returns | Notes |
162
+ |---|---|---|
163
+ | `StdioServerParameters(command, args, env, cwd)` | dataclass | spawn the server as a subprocess |
164
+ | `HttpServerParameters(url, headers, timeout)` | dataclass | connect to a streamable-HTTP MCP server |
165
+ | `SyncMCPClient(params)` | context manager | open the session; use as `with` block |
166
+ | `client.list_tools()` | `list[Tool]` | tool descriptors from `mcp.types.Tool` |
167
+ | `client.list_resources()` | `list[Resource]` | resource descriptors from `mcp.types.Resource` |
168
+ | `client.call_tool(name, arguments)` | `CallToolResult` | invoke a tool; `result.content` is a list of typed blocks |
169
+ | `client.read_resource(uri)` | `ReadResourceResult` | fetch a resource by URI |
170
+ | `@sync` decorator | wraps an async function into a sync one | uses a per-call event loop |
171
+ | `MCPError` | exception | re-exported from `mcp.shared.exceptions` |
172
+
173
+ All public types are fully type-hinted. A `py.typed` marker ships in
174
+ `src/mcpsync/` for PEP 561.
175
+
176
+ ### CLI
177
+
178
+ ```text
179
+ $ mcpsync --help
180
+ usage: mcpsync [-h] [--version] {stdio,http} ...
181
+
182
+ Synchronous client for MCP servers.
183
+
184
+ $ mcpsync stdio list-tools -- python -m my_server
185
+ [
186
+ {"name": "echo", "description": "Echo the input message back", "inputSchema": {...}},
187
+ ...
188
+ ]
189
+
190
+ $ mcpsync stdio call-tool add '{"a":2,"b":3}' -- python -m my_server
191
+ {"content":[{"type":"text","text":"5"}]}
192
+
193
+ $ mcpsync stdio list-resources -- python -m my_server
194
+ [{"uri": "file:///greeting.txt", "name": "greeting", "mimeType": "text/plain"}, ...]
195
+
196
+ $ mcpsync stdio call-resource 'file:///greeting.txt' -- python -m my_server
197
+ {"contents":[{"uri": "file:///greeting.txt", "text": "Hello!", "mimeType": "text/plain"}]}
198
+
199
+ $ mcpsync http list-tools --url https://mcp.example.com/api
200
+ $ mcpsync http call-tool add '{"a":2}' --url https://mcp.example.com/api
201
+ ```
202
+
203
+ The `stdio` subcommand takes a `--` separator before the server
204
+ command + args. The `http` subcommand takes `--url`, `--header` (repeatable
205
+ `KEY=VAL`), and `--timeout`.
206
+
207
+ ### Out of scope
208
+
209
+ - MCP server implementation
210
+ - Long-lived event-loop reuse across calls (each call creates a new
211
+ one; see trade-off above)
212
+ - Streaming / subscription primitives
213
+ - Server-side protocol: `mcpsync` is a client only
214
+ - Any protocol version below what the bundled `mcp` SDK supports
215
+
216
+ ### Limitations
217
+
218
+ - **One shot per call.** Each `SyncMCPClient` use opens the server
219
+ process, runs the request, and closes. If you need a long-lived
220
+ connection, keep the `with` block open and make many calls inside.
221
+ - **HTTP transport** requires the `httpx2` runtime dep (re-exported
222
+ by `mcp` for convenience).
223
+ - **Close path** is bounded by a wall-clock fuse to avoid a known
224
+ deadlock in the `mcp` SDK's stdio teardown.
225
+
226
+ ## Tests
227
+
228
+ ```bash
229
+ pytest tests/ -q
230
+ ```
231
+
232
+ 129 tests across 1 file (split into 16 test classes by behavior).
233
+ Coverage spans every spec acceptance criterion plus angular sweep
234
+ (empty/None inputs, unicode, large payloads, malformed JSON, CLI
235
+ end-to-end, concurrent `@sync` calls, parameter adversarial
236
+ inputs, and regression guards for the close-path deadlock).
237
+
238
+ ## License
239
+
240
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,213 @@
1
+ # mcpsync
2
+
3
+ [![PyPI](https://img.shields.io/badge/version-0.1.0-blue)](https://pypi.org/project/mcpsync)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
5
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](https://www.python.org/)
6
+ [![Tests](https://img.shields.io/badge/tests-129%20passing-brightgreen.svg)](tests/)
7
+ [![Min runtime deps](https://img.shields.io/badge/dependencies-mcp-blue)](pyproject.toml)
8
+
9
+ > **Synchronous Python API for MCP servers — stdio and HTTP transports, plus a CLI for ad-hoc inspection.**
10
+
11
+ `mcpsync` wraps the official `mcp` Python SDK's async client behind a
12
+ blocking `SyncMCPClient` so you can call MCP tools, list resources, and
13
+ read resources from synchronous code (CLI tools, Django views, Flask
14
+ handlers, scripts). It also ships a CLI for ad-hoc server inspection
15
+ from the shell.
16
+
17
+ ## Quick Start
18
+
19
+ Install from source:
20
+
21
+ ```bash
22
+ pip install git+https://github.com/prasad-a-abhishek/mcpsync.git
23
+ ```
24
+
25
+ Connect to a stdio MCP server and call a tool:
26
+
27
+ ```python
28
+ from mcpsync import SyncMCPClient, StdioServerParameters
29
+
30
+ params = StdioServerParameters(
31
+ command="python", args=("-m", "my_mcp_server"),
32
+ env={"DEBUG": "1"},
33
+ cwd="/srv/myapp",
34
+ )
35
+
36
+ with SyncMCPClient(params) as client:
37
+ tools = client.list_tools()
38
+ for tool in tools:
39
+ print(f"- {tool.name}: {tool.description}")
40
+
41
+ result = client.call_tool("add", {"a": 2, "b": 3})
42
+ for block in result.content:
43
+ print(block.text)
44
+ ```
45
+
46
+ Connect to an HTTP MCP server:
47
+
48
+ ```python
49
+ from mcpsync import SyncMCPClient, HttpServerParameters
50
+
51
+ params = HttpServerParameters(
52
+ url="https://mcp.example.com/api",
53
+ headers={"Authorization": "Bearer ..."},
54
+ timeout=10.0,
55
+ )
56
+
57
+ with SyncMCPClient(params) as client:
58
+ resources = client.list_resources()
59
+ contents = client.read_resource(resources[0].uri)
60
+ ```
61
+
62
+ Turn any async MCP helper into a sync function with the `@sync`
63
+ decorator:
64
+
65
+ ```python
66
+ from mcpsync import sync
67
+
68
+ @sync
69
+ async def load_schema(server_url: str) -> dict:
70
+ async with some_async_helper(server_url) as helper:
71
+ return await helper.fetch_schema()
72
+ ```
73
+
74
+ ## ⚡ Performance & Benchmarks
75
+
76
+ We publish `benchmarks/BENCHMARK.md` per Invariant 14 — including
77
+ the methodology caveat. The honest summary: mcpsync uses
78
+ `asyncio.Runner` per call, which actually benchmarks ~15% faster
79
+ than the SDK's `asyncio.run` pattern in this setup (interpreter
80
+ boot dominates; relative comparison is meaningful). Reproduce
81
+ locally:
82
+
83
+ ```bash
84
+ python benchmarks/run_benchmark.py
85
+ ```
86
+
87
+ The full results table, methodology, and the trade-off
88
+ transparency statement are in
89
+ [benchmarks/BENCHMARK.md](benchmarks/BENCHMARK.md).
90
+
91
+ ## Why `mcpsync`? (Problem & Trade-Off Statement)
92
+
93
+ The MCP Python SDK ships an **async-only** client. Synchronous callers
94
+ (Django views, CLI tools, scripts) hit the same wall: every
95
+ `asyncio.run()` from sync code re-creates an event loop, leaks
96
+ resources, and breaks under `asyncio.run()` recursion if the
97
+ surrounding runtime already runs a loop.
98
+
99
+ Two GitHub issues document the gap:
100
+
101
+ - [`modelcontextprotocol/python-sdk#1223`](https://github.com/modelcontextprotocol/python-sdk/issues/1223) — "Sync client API" (open, multiple reactions)
102
+ - `modelcontextprotocol/python-sdk` — the SDK explicitly documents
103
+ the `client.session.stdio` use pattern as **async only**.
104
+
105
+ **What mcpsync is:** a thin wrapper that gives you `SyncMCPClient` +
106
+ a `@sync` decorator, plus a CLI for ad-hoc inspection. It uses the
107
+ official `mcp` SDK under the hood and never re-implements the JSON-RPC
108
+ protocol or transport.
109
+
110
+ **What mcpsync is NOT:**
111
+ - Not a replacement for the `mcp` SDK — it depends on it.
112
+ - Not an MCP server implementation. It's a client.
113
+ - Not magic. Each `SyncMCPClient.list_tools()` call creates a
114
+ one-shot event loop on a worker thread. If you need 100k calls/sec,
115
+ use the SDK's async client directly.
116
+
117
+ **Trade-offs you accept by using mcpsync:**
118
+ - Each `SyncMCPClient` call creates a fresh `asyncio.Runner` on a
119
+ worker thread. The bench shows this is ~15% faster than the SDK
120
+ baseline, but for a true long-lived async loop you should use
121
+ the SDK's async client directly.
122
+ - The close path needs a wall-clock fuse to escape a known deadlock
123
+ in the `mcp` SDK's `stdio_client.__aexit__` shielded cancel scope.
124
+ Without the fuse the caller's process hangs. See
125
+ `src/mcpsync/client.py::_run_bounded` and the docstring on
126
+ `SyncMCPClient.close()`.
127
+ - HTTP transport requires the `mcp` SDK's optional `httpx2` dep,
128
+ which is re-exported by `mcp` for convenience.
129
+
130
+ ## Key Features & Complete API / CLI Reference
131
+
132
+ ### Library API
133
+
134
+ | Name | Returns | Notes |
135
+ |---|---|---|
136
+ | `StdioServerParameters(command, args, env, cwd)` | dataclass | spawn the server as a subprocess |
137
+ | `HttpServerParameters(url, headers, timeout)` | dataclass | connect to a streamable-HTTP MCP server |
138
+ | `SyncMCPClient(params)` | context manager | open the session; use as `with` block |
139
+ | `client.list_tools()` | `list[Tool]` | tool descriptors from `mcp.types.Tool` |
140
+ | `client.list_resources()` | `list[Resource]` | resource descriptors from `mcp.types.Resource` |
141
+ | `client.call_tool(name, arguments)` | `CallToolResult` | invoke a tool; `result.content` is a list of typed blocks |
142
+ | `client.read_resource(uri)` | `ReadResourceResult` | fetch a resource by URI |
143
+ | `@sync` decorator | wraps an async function into a sync one | uses a per-call event loop |
144
+ | `MCPError` | exception | re-exported from `mcp.shared.exceptions` |
145
+
146
+ All public types are fully type-hinted. A `py.typed` marker ships in
147
+ `src/mcpsync/` for PEP 561.
148
+
149
+ ### CLI
150
+
151
+ ```text
152
+ $ mcpsync --help
153
+ usage: mcpsync [-h] [--version] {stdio,http} ...
154
+
155
+ Synchronous client for MCP servers.
156
+
157
+ $ mcpsync stdio list-tools -- python -m my_server
158
+ [
159
+ {"name": "echo", "description": "Echo the input message back", "inputSchema": {...}},
160
+ ...
161
+ ]
162
+
163
+ $ mcpsync stdio call-tool add '{"a":2,"b":3}' -- python -m my_server
164
+ {"content":[{"type":"text","text":"5"}]}
165
+
166
+ $ mcpsync stdio list-resources -- python -m my_server
167
+ [{"uri": "file:///greeting.txt", "name": "greeting", "mimeType": "text/plain"}, ...]
168
+
169
+ $ mcpsync stdio call-resource 'file:///greeting.txt' -- python -m my_server
170
+ {"contents":[{"uri": "file:///greeting.txt", "text": "Hello!", "mimeType": "text/plain"}]}
171
+
172
+ $ mcpsync http list-tools --url https://mcp.example.com/api
173
+ $ mcpsync http call-tool add '{"a":2}' --url https://mcp.example.com/api
174
+ ```
175
+
176
+ The `stdio` subcommand takes a `--` separator before the server
177
+ command + args. The `http` subcommand takes `--url`, `--header` (repeatable
178
+ `KEY=VAL`), and `--timeout`.
179
+
180
+ ### Out of scope
181
+
182
+ - MCP server implementation
183
+ - Long-lived event-loop reuse across calls (each call creates a new
184
+ one; see trade-off above)
185
+ - Streaming / subscription primitives
186
+ - Server-side protocol: `mcpsync` is a client only
187
+ - Any protocol version below what the bundled `mcp` SDK supports
188
+
189
+ ### Limitations
190
+
191
+ - **One shot per call.** Each `SyncMCPClient` use opens the server
192
+ process, runs the request, and closes. If you need a long-lived
193
+ connection, keep the `with` block open and make many calls inside.
194
+ - **HTTP transport** requires the `httpx2` runtime dep (re-exported
195
+ by `mcp` for convenience).
196
+ - **Close path** is bounded by a wall-clock fuse to avoid a known
197
+ deadlock in the `mcp` SDK's stdio teardown.
198
+
199
+ ## Tests
200
+
201
+ ```bash
202
+ pytest tests/ -q
203
+ ```
204
+
205
+ 129 tests across 1 file (split into 16 test classes by behavior).
206
+ Coverage spans every spec acceptance criterion plus angular sweep
207
+ (empty/None inputs, unicode, large payloads, malformed JSON, CLI
208
+ end-to-end, concurrent `@sync` calls, parameter adversarial
209
+ inputs, and regression guards for the close-path deadlock).
210
+
211
+ ## License
212
+
213
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,63 @@
1
+ [project]
2
+ name = "mcpsync-cli"
3
+ version = "0.1.0"
4
+ description = "Synchronous MCP Python client — call MCP servers from sync Python without async/await"
5
+ readme = "README.md"
6
+ license = {text = "MIT"}
7
+ requires-python = ">=3.11"
8
+ authors = [
9
+ {name = "Repo Factory", email = "noreply@example.com"},
10
+ ]
11
+ keywords = [
12
+ "mcp",
13
+ "model-context-protocol",
14
+ "synchronous",
15
+ "sync",
16
+ "client",
17
+ "stdio",
18
+ "http",
19
+ "streamable-http",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 4 - Beta",
23
+ "Intended Audience :: Developers",
24
+ "License :: OSI Approved :: MIT License",
25
+ "Operating System :: OS Independent",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Topic :: Software Development :: Libraries :: Python Modules",
30
+ "Typing :: Typed",
31
+ ]
32
+ # mcpsync wraps the official `mcp` SDK. It introduces NO deps beyond `mcp` + stdlib.
33
+ # See spec.md acceptance criterion #14: no third-party imports outside stdlib.
34
+ dependencies = [
35
+ "mcp>=1.0",
36
+ ]
37
+
38
+ [project.optional-dependencies]
39
+ dev = [
40
+ "pytest>=7.0",
41
+ ]
42
+
43
+ [project.scripts]
44
+ mcpsync = "mcpsync.cli:main"
45
+
46
+ [project.urls]
47
+ Homepage = "https://github.com/prasad-a-abhishek/mcpsync"
48
+ Repository = "https://github.com/prasad-a-abhishek/mcpsync"
49
+ Issues = "https://github.com/prasad-a-abhishek/mcpsync/issues"
50
+
51
+ [build-system]
52
+ requires = ["setuptools>=61.0"]
53
+ build-backend = "setuptools.build_meta"
54
+
55
+ [tool.setuptools.packages.find]
56
+ where = ["src"]
57
+
58
+ [tool.setuptools.package-data]
59
+ mcpsync = ["py.typed", "*.pyi"]
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+ addopts = "-q"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,31 @@
1
+ """``mcpsync`` — Synchronous MCP Python client.
2
+
3
+ Call MCP servers from sync Python without ``async``/``await``. Wraps the
4
+ official `mcp` SDK's async ``ClientSession`` inside a dedicated
5
+ ``asyncio.Runner`` per session, and exposes ``call_tool``, ``list_tools``,
6
+ ``call_resource``, and ``list_resources`` as plain blocking methods.
7
+
8
+ Example::
9
+
10
+ from mcpsync import SyncMCPClient, StdioServerParameters
11
+
12
+ params = StdioServerParameters(command="python", args=["-m", "my_server"])
13
+ with SyncMCPClient(params) as client:
14
+ tools = client.list_tools()
15
+ result = client.call_tool("git_log", {"n": 5})
16
+ """
17
+
18
+ from mcpsync.client import SyncMCPClient
19
+ from mcpsync.errors import MCPError
20
+ from mcpsync.params import HttpServerParameters, StdioServerParameters
21
+ from mcpsync.sync import sync
22
+
23
+ __all__ = [
24
+ "HttpServerParameters",
25
+ "MCPError",
26
+ "StdioServerParameters",
27
+ "SyncMCPClient",
28
+ "sync",
29
+ ]
30
+
31
+ __version__ = "0.1.0"
@@ -0,0 +1,64 @@
1
+ """Type stubs for the ``mcpsync`` public API."""
2
+
3
+ from collections.abc import Callable, Mapping
4
+ from types import TracebackType
5
+ from typing import Any, ParamSpec, TypeVar
6
+
7
+ from typing_extensions import Self
8
+
9
+ P = ParamSpec("P")
10
+ R = TypeVar("R")
11
+
12
+ class StdioServerParameters:
13
+ command: str
14
+ args: tuple[str, ...]
15
+ env: Mapping[str, str] | None
16
+ cwd: str | None
17
+ def __init__(
18
+ self,
19
+ command: str,
20
+ args: tuple[str, ...] | list[str] = (),
21
+ env: Mapping[str, str] | None = None,
22
+ cwd: str | None = None,
23
+ ) -> None: ...
24
+ def to_argv(self) -> list[str]: ...
25
+
26
+ class HttpServerParameters:
27
+ url: str
28
+ headers: Mapping[str, str]
29
+ timeout: float
30
+ def __init__(
31
+ self,
32
+ url: str,
33
+ headers: Mapping[str, str] | None = None,
34
+ timeout: float = 30.0,
35
+ ) -> None: ...
36
+
37
+ class MCPError(Exception):
38
+ code: int
39
+ message: str
40
+ data: Any
41
+ def __init__(self, code: int, message: str, data: Any = None) -> None: ...
42
+
43
+ class SyncMCPClient:
44
+ def __init__(
45
+ self,
46
+ params: StdioServerParameters | HttpServerParameters,
47
+ *,
48
+ timeout: float | None = None,
49
+ ) -> None: ...
50
+ def __enter__(self) -> Self: ...
51
+ def __exit__(
52
+ self,
53
+ exc_type: type[BaseException] | None,
54
+ exc: BaseException | None,
55
+ tb: TracebackType | None,
56
+ ) -> None: ...
57
+ def list_tools(self) -> list[Any]: ...
58
+ def call_tool(self, name: str, arguments: dict[str, Any] | None = None) -> Any: ...
59
+ def list_resources(self) -> list[Any]: ...
60
+ def call_resource(self, uri: str) -> Any: ...
61
+ def read_resource(self, uri: str) -> Any: ...
62
+ def close(self) -> None: ...
63
+
64
+ def sync(fn: Callable[P, Any]) -> Callable[P, Any]: ...
@@ -0,0 +1,5 @@
1
+ """Allow ``python -m mcpsync`` to invoke the CLI."""
2
+
3
+ from mcpsync.cli import main
4
+
5
+ raise SystemExit(main())