Skip to content

Commit 056e15d

Browse files
fix: explicitly set encoding to utf-8
1 parent 4324faf commit 056e15d

File tree

12 files changed

+76
-55
lines changed

12 files changed

+76
-55
lines changed

README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ MCPM is an open source service and a CLI package management tool for MCP servers
2727
2828
## 🚀 Quick Installation
2929

30-
Choose your preferred installation method:
30+
### 🔄 Shell Script (One-liner)
31+
32+
```bash
33+
curl -sSL https://mcpm.sh/install | bash
34+
```
35+
36+
Or choose your preferred installation method:
3137

3238
### 🍺 Homebrew
3339

@@ -41,6 +47,14 @@ brew install mcpm
4147
pipx install mcpm
4248
```
4349

50+
### 🪄 uv tool
51+
52+
```bash
53+
uv tool install mcpm
54+
```
55+
56+
## More Installation Methods
57+
4458
### 🐍 pip
4559

4660
```bash
@@ -55,11 +69,6 @@ If you are a user of [x-cmd](https://x-cmd.com), you can run:
5569
x install mcpm.sh
5670
```
5771

58-
### 🔄 Shell Script (One-liner)
59-
60-
```bash
61-
curl -sSL https://mcpm.sh/install | bash
62-
```
6372

6473
## 🔎 Overview
6574

README.zh-CN.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ MCPM 是一个开源的服务和命令行界面(CLI),用于管理模型上下
2727
2828
## 🚀 快速安装
2929

30-
选择您喜欢的安装方式:
30+
### 🔄 Shell 脚本(一行命令)
31+
32+
```bash
33+
curl -sSL https://mcpm.sh/install | bash
34+
```
35+
36+
或选择您喜欢的安装方式:
3137

3238
### 🍺 Homebrew
3339

@@ -41,6 +47,14 @@ brew install mcpm
4147
pipx install mcpm
4248
```
4349

50+
### 🪄 uv tool
51+
52+
```bash
53+
uv tool install mcpm
54+
```
55+
56+
## 其他安装方式
57+
4458
### 🐍 pip
4559

4660
```bash
@@ -55,12 +69,6 @@ pip install mcpm
5569
x install mcpm.sh
5670
```
5771

58-
### 🔄 Shell 脚本(一行命令)
59-
60-
```bash
61-
curl -sSL https://mcpm.sh/install | bash
62-
```
63-
6472
## 🔎 概述
6573

6674
MCPM 简化了 MCP 服务器的安装、配置和管理,以及它们在不同应用程序(客户端)中的配置。主要功能包括:

scripts/prepare.py

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ def load_manifest(manifest_path: Path) -> Dict[str, Any]:
5454
except jsonschema.exceptions.ValidationError:
5555
# If validation fails, we continue but log a warning
5656
# This allows the site to build even with some schema issues
57-
print(
58-
f"⚠️ Warning: {manifest_path} does not fully conform to the schema")
57+
print(f"⚠️ Warning: {manifest_path} does not fully conform to the schema")
5958

6059
return manifest
6160
except json.JSONDecodeError as e:
@@ -92,9 +91,12 @@ def extract_github_repos(server_manifests: List[Path]) -> Dict[str, str]:
9291
# Handle both string and dictionary repository formats
9392
if isinstance(repo_url, str) and repo_url.startswith("https://github.com/"):
9493
github_repos[server_name] = repo_url
95-
elif (isinstance(repo_url, dict) and "url" in repo_url and
96-
isinstance(repo_url["url"], str) and
97-
repo_url["url"].startswith("https://github.com/")):
94+
elif (
95+
isinstance(repo_url, dict)
96+
and "url" in repo_url
97+
and isinstance(repo_url["url"], str)
98+
and repo_url["url"].startswith("https://github.com/")
99+
):
98100
github_repos[server_name] = repo_url["url"]
99101

100102
return github_repos
@@ -130,7 +132,7 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
130132

131133
# Process repositories in batches
132134
for batch_start in range(0, len(repos), BATCH_SIZE):
133-
batch = repos[batch_start:batch_start + BATCH_SIZE]
135+
batch = repos[batch_start : batch_start + BATCH_SIZE]
134136

135137
# Construct GraphQL query
136138
query_parts = []
@@ -147,8 +149,7 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
147149
variables[f"repo{i}"] = repo
148150

149151
# Join the query parts with proper line length
150-
variable_defs = ", ".join(f"$owner{i}: String!, $repo{i}: String!"
151-
for i in range(len(batch)))
152+
variable_defs = ", ".join(f"$owner{i}: String!, $repo{i}: String!" for i in range(len(batch)))
152153
query_body = " ".join(query_parts)
153154

154155
query = f"""query ({variable_defs}) {{
@@ -157,23 +158,16 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
157158

158159
# Send GraphQL request
159160
try:
160-
response = requests.post(
161-
GITHUB_API_URL,
162-
headers=headers,
163-
json={"query": query, "variables": variables}
164-
)
161+
response = requests.post(GITHUB_API_URL, headers=headers, json={"query": query, "variables": variables})
165162

166163
# Check for errors
167164
if response.status_code != 200:
168165
if response.status_code == 401:
169-
print(
170-
"⚠️ GitHub API authentication failed. Set GITHUB_TOKEN for higher rate limits.")
166+
print("⚠️ GitHub API authentication failed. Set GITHUB_TOKEN for higher rate limits.")
171167
elif response.status_code == 403:
172-
print(
173-
"⚠️ GitHub API rate limit exceeded. Set GITHUB_TOKEN for higher rate limits.")
168+
print("⚠️ GitHub API rate limit exceeded. Set GITHUB_TOKEN for higher rate limits.")
174169
else:
175-
print(
176-
f"⚠️ GitHub API request failed: status {response.status_code}")
170+
print(f"⚠️ GitHub API request failed: status {response.status_code}")
177171
continue
178172

179173
data = response.json()
@@ -191,16 +185,13 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
191185
star_count = data["data"][repo_key]["stargazerCount"]
192186
stars[url] = star_count
193187
if url.startswith("https://github.com/"):
194-
returned_parts = url.replace(
195-
"https://github.com/", "").split("/")
188+
returned_parts = url.replace("https://github.com/", "").split("/")
196189
if len(returned_parts) >= 2:
197190
returned_owner, returned_repo = returned_parts[0], returned_parts[1]
198191
if owner != returned_owner:
199-
print(
200-
f"⚠️owner mismatch:: {owner} != {returned_owner}")
192+
print(f"⚠️owner mismatch:: {owner} != {returned_owner}")
201193
if repo != returned_repo:
202-
print(
203-
f"⚠️repo mismatch:: {repo} != {returned_repo}")
194+
print(f"⚠️repo mismatch:: {repo} != {returned_repo}")
204195

205196
except Exception as e:
206197
print(f"⚠️ Error fetching GitHub stars for batch: {e}")
@@ -249,7 +240,7 @@ def generate_servers_json(server_manifests: List[Path], output_path: Path) -> Di
249240
servers_data[server_name] = manifest
250241

251242
# Write servers.json
252-
with open(output_path, "w") as f:
243+
with open(output_path, "w", encoding="utf-8") as f:
253244
json.dump(servers_data, f, indent=2)
254245

255246
return servers_data
@@ -267,8 +258,7 @@ def generate_stars_json(stars: Dict[str, int], output_path: Path) -> None:
267258
def main() -> None:
268259
"""Main function to prepare site data"""
269260
if len(sys.argv) < 3:
270-
error_exit(
271-
"Usage: prepare.py <source_dir> <target_dir> [--skip-stars]")
261+
error_exit("Usage: prepare.py <source_dir> <target_dir> [--skip-stars]")
272262

273263
source_dir = Path(sys.argv[1])
274264
target_dir = Path(sys.argv[2])

src/mcpm/clients/base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ def _load_config(self) -> Dict[str, Any]:
218218
return empty_config
219219

220220
try:
221-
with open(self.config_path, "r") as f:
221+
with open(self.config_path, "r", encoding="utf-8") as f:
222222
config = json.load(f)
223223
# Ensure mcpServers section exists
224224
if self.configure_key_name not in config:
@@ -252,7 +252,7 @@ def _save_config(self, config: Dict[str, Any]) -> bool:
252252
# Create directory if it doesn't exist
253253
os.makedirs(os.path.dirname(self.config_path), exist_ok=True)
254254

255-
with open(self.config_path, "w") as f:
255+
with open(self.config_path, "w", encoding="utf-8") as f:
256256
json.dump(config, f, indent=2)
257257
return True
258258
except Exception as e:

src/mcpm/clients/managers/fiveire.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def _load_config(self) -> Dict[str, Any]:
6262
return empty_config
6363

6464
try:
65-
with open(self.config_path, "r") as f:
65+
with open(self.config_path, "r", encoding="utf-8") as f:
6666
config = json.load(f)
6767
# Ensure servers section exists
6868
if self.configure_key_name not in config:

src/mcpm/commands/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def edit_client():
176176

177177
# Write the template to file
178178
try:
179-
with open(config_path, "w") as f:
179+
with open(config_path, "w", encoding="utf-8") as f:
180180
json.dump(basic_config, f, indent=2)
181181
console.print("[green]Successfully created config file![/]\n")
182182
config_exists = True
@@ -187,7 +187,7 @@ def edit_client():
187187
# Show the current configuration if it exists
188188
if config_exists:
189189
try:
190-
with open(config_path, "r") as f:
190+
with open(config_path, "r", encoding="utf-8") as f:
191191
config_content = f.read()
192192

193193
# Display the content

src/mcpm/commands/server_operations/add.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def add(server_name, force=False, alias=None, target: str | None = None):
262262
# Save metadata to server directory
263263
progress.add_task("Saving server metadata...", total=None)
264264
metadata_path = os.path.join(server_dir, "metadata.json")
265-
with open(metadata_path, "w") as f:
265+
with open(metadata_path, "w", encoding="utf-8") as f:
266266
json.dump(server_metadata, f, indent=2)
267267

268268
# Configure the server

src/mcpm/monitor/duckdb.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,18 @@
88
from datetime import datetime
99
from typing import Any, Dict, Optional, Union
1010

11-
import duckdb
11+
try:
12+
import duckdb
13+
except ImportError as e:
14+
duckdb = None
15+
if "DLL load failed while importing duckdb" in str(e):
16+
print("The DuckDB Python package requires the Microsoft Visual C++ Redistributable. ")
17+
print(
18+
"Please install it from: https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170"
19+
)
20+
print("See https://duckdb.org/docs/installation/?version=stable&environment=python for more information.")
21+
else:
22+
raise
1223

1324
from mcpm.monitor.base import AccessEventType, AccessMonitor, MCPEvent, Pagination, QueryEventResponse
1425
from mcpm.utils.config import ConfigManager
@@ -57,6 +68,9 @@ async def initialize_storage(self) -> bool:
5768

5869
def _initialize_storage_impl(self) -> bool:
5970
"""Internal implementation of storage initialization."""
71+
if duckdb is None:
72+
print("DuckDB is not available.")
73+
return False
6074
try:
6175
# Create the directory if it doesn't exist
6276
os.makedirs(os.path.dirname(self.db_path), exist_ok=True)

src/mcpm/profile/profile_config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def __init__(self, profile_path: str = DEFAULT_PROFILE_PATH):
1717
def _load_profiles(self) -> Dict[str, list[ServerConfig]]:
1818
if not os.path.exists(self.profile_path):
1919
return {}
20-
with open(self.profile_path, "r") as f:
20+
with open(self.profile_path, "r", encoding="utf-8") as f:
2121
profiles = json.load(f) or {}
2222
return {
2323
name: [TypeAdapter(ServerConfig).validate_python(config) for config in configs]
@@ -26,7 +26,7 @@ def _load_profiles(self) -> Dict[str, list[ServerConfig]]:
2626

2727
def _save_profiles(self) -> None:
2828
profile_info = {name: [config.model_dump() for config in configs] for name, configs in self._profiles.items()}
29-
with open(self.profile_path, "w") as f:
29+
with open(self.profile_path, "w", encoding="utf-8") as f:
3030
json.dump(profile_info, f, indent=2)
3131

3232
def new_profile(self, profile_name: str) -> bool:

src/mcpm/router/watcher.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async def _reload(self):
5252

5353
def _validate_config(self):
5454
try:
55-
with open(self.config_path, "r") as f:
55+
with open(self.config_path, "r", encoding="utf-8") as f:
5656
_ = json.load(f)
5757
except json.JSONDecodeError:
5858
logger.error(f"Error parsing config file: {self.config_path}")

0 commit comments

Comments
 (0)