Skip to content

Commit 3e6b693

Browse files
committed
working on client tooling
1 parent ac8ddfc commit 3e6b693

File tree

5 files changed

+80
-13
lines changed

5 files changed

+80
-13
lines changed

mcp-run-python/src/main.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ response.text
8787
`
8888
if (callbacks) {
8989
toolDescription += `
90-
The following functions are globally available to call:
90+
The following functions are already defined and available to call:
9191
9292
\`\`\`python
9393
${callbacks}

mcp-run-python/src/prepare_env.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def _json_fallback(value: Any) -> Any:
139139
elif module == 'pyodide.ffi':
140140
return value.to_py()
141141
else:
142-
return repr(value)
142+
return str(value)
143143

144144

145145
def _add_extra_dependencies(dependencies: list[str]) -> list[str]:

mcp-run-python/test_mcp_servers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
DENO_ARGS = [
2020
'run',
2121
'-N',
22+
'--node-modules-dir=auto',
2223
'-R=mcp-run-python/node_modules',
2324
'-W=mcp-run-python/node_modules',
24-
'--node-modules-dir=auto',
2525
'mcp-run-python/src/main.ts',
2626
]
2727

mcp-run-python/uprev.py

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11
import re
22
import sys
3-
43
from pathlib import Path
54

65
if len(sys.argv) != 2:
7-
print("Usage: python uprev.py <new_version>")
6+
print('Usage: python uprev.py <new_version>')
87
sys.exit(1)
98

109
new_version = sys.argv[1]
1110
this_dir = Path(__file__).parent
11+
root_dir = (this_dir / '..').resolve()
1212

1313
path_regexes = [
1414
(this_dir / 'deno.jsonc', r'^\s+"version": "(.+?)"'),
1515
(this_dir / 'src/main.ts', "^const VERSION = '(.+?)'"),
16-
(this_dir / '../pydantic_ai_slim/pydantic_ai/mcp_run_python.py', "^MCP_RUN_PYTHON_VERSION = '(.+?)'")
16+
(root_dir / 'pydantic_ai_slim/pydantic_ai/mcp_run_python.py', "^MCP_RUN_PYTHON_VERSION = '(.+?)'"),
1717
]
1818

19-
def replace_version(m: re.Match[str]) -> str:
20-
version = m.group(1)
21-
return m.group(0).replace(version, new_version)
2219

23-
if __name__ == "__main__":
20+
if __name__ == '__main__':
2421
for path, regex in path_regexes:
25-
path = path.resolve()
22+
path_pretty = path.relative_to(root_dir)
23+
24+
def replace_version(m: re.Match[str]) -> str:
25+
version = m.group(1)
26+
print(f'Updated version from {version} to {new_version} in {path_pretty}')
27+
return m.group(0).replace(version, new_version)
28+
2629
content = path.read_text()
2730
content, count = re.subn(regex, replace_version, content, count=1, flags=re.M)
2831
if count != 1:
29-
raise ValueError(f"Failed to update version in {path}")
32+
raise ValueError(f'Failed to update version in {path}')
3033
path.write_text(content)
31-
print(f"Updated version to {new_version} in {path}")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import ast
2+
import inspect
3+
from collections.abc import Awaitable, Sequence
4+
from typing import Any, Callable, Literal
5+
6+
from .mcp import MCPServerStdio
7+
8+
__all__ = ('mcp_run_python_stdio',)
9+
10+
MCP_RUN_PYTHON_VERSION = '0.0.13'
11+
Callback = Callable[..., Awaitable[Any]]
12+
13+
14+
def mcp_run_python_stdio(callbacks: Sequence[Callback] = (), *, local_code: bool = False) -> MCPServerStdio:
15+
"""Prepare a server server connection using `'stdio'` transport.
16+
17+
Args:
18+
callbacks: A sequence of callback functions to be register on the server.
19+
local_code: Whether to run local `mcp-run-python` code.
20+
21+
Returns:
22+
A server connection definition.
23+
"""
24+
return MCPServerStdio('deno', args=_deno_args('stdio', callbacks, local_code))
25+
26+
27+
def _deno_args(mode: Literal['stdio', 'sse'], callbacks: Sequence[Callback], local_code: bool) -> list[str]:
28+
path_prefix = 'mcp-run-python/' if local_code else ''
29+
args = [
30+
'run',
31+
'-N',
32+
f'-R={path_prefix}node_modules',
33+
f'-W={path_prefix}node_modules',
34+
'--node-modules-dir=auto',
35+
'mcp-run-python/src/main.ts' if local_code else f'jsr:@pydantic/mcp-run-python@{MCP_RUN_PYTHON_VERSION}',
36+
mode,
37+
]
38+
39+
if callbacks:
40+
sigs = '\n\n'.join(_callback_signature(cb) for cb in callbacks)
41+
args += ['--callbacks', sigs]
42+
return args
43+
44+
45+
def _callback_signature(func: Callback) -> str:
46+
"""Extract the signature of a function.
47+
48+
This simply means getting the source code of the function, and removing the body of the function while keeping the docstring.
49+
"""
50+
source = inspect.getsource(func)
51+
ast_mod = ast.parse(source)
52+
assert isinstance(ast_mod, ast.Module), f'Expected Module, got {type(ast_mod)}'
53+
assert len(ast_mod.body) == 1, f'Expected single function definition, got {len(ast_mod.body)}'
54+
f = ast_mod.body[0]
55+
assert isinstance(f, ast.AsyncFunctionDef), f'Expected an async function, got {type(func)}'
56+
lines = lines = source.splitlines()
57+
e = f.body[0]
58+
# if the first expression is a docstring, keep it and no need for an ellipsis as the body
59+
if isinstance(e, ast.Expr) and isinstance(e.value, ast.Constant) and isinstance(e.value.value, str):
60+
e = f.body[1]
61+
lines = lines[: e.lineno - 1]
62+
else:
63+
lines = lines[: e.lineno - 1]
64+
lines.append(e.col_offset * ' ' + '...')
65+
return '\n'.join(lines)

0 commit comments

Comments
 (0)