-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcpp_style_server.py
More file actions
400 lines (314 loc) · 13.7 KB
/
cpp_style_server.py
File metadata and controls
400 lines (314 loc) · 13.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
"""
C++ 编码规范 MCP 服务器
提供 C++ 代码规范检查、最佳实践建议和代码审查支持。
"""
import os
from pathlib import Path
from typing import Literal
from mcp.server.fastmcp import FastMCP
from mcp.types import ToolAnnotations
from starlette.requests import Request
from starlette.responses import FileResponse, JSONResponse, RedirectResponse
# 导入工具模块
from cpp_style.tools.naming_checker import get_checker as get_naming_checker
from cpp_style.tools.include_guard_checker import get_checker as get_include_guard_checker
from cpp_style.tools.memory_safety import get_analyzer as get_memory_analyzer
from cpp_style.tools.modern_cpp import get_suggester as get_modern_cpp_suggester
from cpp_style.tools.const_checker import get_checker as get_const_checker
# 导入资源模块
from cpp_style.resources.naming_rules import get_resource as get_naming_resource
from cpp_style.resources.best_practices import get_resource as get_practices_resource
from cpp_style.resources.cpp_standards import get_resource as get_standards_resource
from cpp_style.resources.design_patterns import get_resource as get_patterns_resource
# 导入提示模块
from cpp_style.prompts.code_review import get_prompt as get_code_review_prompt
from cpp_style.prompts.refactor_suggestion import get_prompt as get_refactor_prompt
# ==================== OAuth 配置 ====================
# GitHub OAuth 环境变量(在 Railway 中配置)
_GITHUB_CLIENT_ID = os.environ.get("GITHUB_CLIENT_ID", "")
_GITHUB_CLIENT_SECRET = os.environ.get("GITHUB_CLIENT_SECRET", "")
# MCP 服务器公开 URL(Railway 部署地址或自定义域名)
_MCP_SERVER_URL = os.environ.get(
"MCP_SERVER_URL",
"https://cpp-style-guide-mcp.fly.dev",
)
_oauth_provider = None
_auth_settings = None
if _GITHUB_CLIENT_ID and _GITHUB_CLIENT_SECRET:
from mcp.server.auth.settings import AuthSettings, ClientRegistrationOptions
from mcp.server.auth.provider import ProviderTokenVerifier
from cpp_style.auth.github_provider import GitHubOAuthProvider
_oauth_provider = GitHubOAuthProvider(
github_client_id=_GITHUB_CLIENT_ID,
github_client_secret=_GITHUB_CLIENT_SECRET,
mcp_server_url=_MCP_SERVER_URL,
)
_auth_settings = AuthSettings(
issuer_url=_MCP_SERVER_URL, # type: ignore[arg-type]
resource_server_url=_MCP_SERVER_URL, # type: ignore[arg-type]
client_registration_options=ClientRegistrationOptions(
enabled=True,
valid_scopes=["mcp"],
default_scopes=["mcp"],
),
)
# 创建 MCP 服务器实例
# stateless_http=True:每个请求独立处理,无需 session ID
# 使 Smithery 扫描器可以直接查询 tools/list 等端点
mcp = FastMCP(
"C++ Style Guide Server",
auth_server_provider=_oauth_provider,
token_verifier=ProviderTokenVerifier(_oauth_provider) if _oauth_provider else None,
auth=_auth_settings,
stateless_http=True,
)
# ==================== Custom Routes ====================
@mcp.custom_route("/health", methods=["GET"])
async def health_check(request: Request) -> JSONResponse:
"""健康检查端点(供 Railway 等平台使用)"""
auth_enabled = _oauth_provider is not None
return JSONResponse({
"status": "ok",
"service": "cpp-style-guide-mcp",
"auth": "github" if auth_enabled else "disabled",
})
@mcp.custom_route("/.well-known/mcp/server-card.json", methods=["GET"])
async def server_card(request: Request) -> FileResponse | JSONResponse:
"""Smithery 服务器元数据(用于 Smithery 平台展示)"""
card_path = Path(__file__).parent / "static" / ".well-known" / "mcp" / "server-card.json"
if card_path.exists():
return FileResponse(str(card_path), media_type="application/json")
return JSONResponse({"error": "server-card.json 未找到"}, status_code=404)
@mcp.custom_route("/icon.svg", methods=["GET"])
async def server_icon(request: Request) -> FileResponse | JSONResponse:
"""服务器图标"""
icon_path = Path(__file__).parent / "static" / "icon.svg"
if icon_path.exists():
return FileResponse(str(icon_path), media_type="image/svg+xml")
return JSONResponse({"error": "icon.svg 未找到"}, status_code=404)
@mcp.custom_route("/oauth/callback", methods=["GET"])
async def oauth_callback(request: Request) -> RedirectResponse | JSONResponse:
"""GitHub OAuth 回调处理(GitHub 授权后重定向到此路由)"""
if _oauth_provider is None:
return JSONResponse({"error": "OAuth 未启用"}, status_code=400)
code = request.query_params.get("code")
state = request.query_params.get("state")
if not code or not state:
return JSONResponse({"error": "缺少 code 或 state 参数"}, status_code=400)
try:
redirect_url = await _oauth_provider.handle_github_callback(code, state)
return RedirectResponse(url=redirect_url, status_code=302)
except ValueError as e:
return JSONResponse({"error": str(e)}, status_code=400)
# ==================== Tools ====================
_READ_ONLY = ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False)
@mcp.tool(
description="Check whether a C++ identifier follows naming conventions. Validates variables, constants, functions, classes, namespaces, member variables, template parameters, and file names against established C++ style guidelines. Returns whether the identifier is valid, a detailed explanation, and suggested alternatives if it violates the rules.",
annotations=_READ_ONLY,
)
def check_naming(
identifier: str,
category: Literal["variable", "constant", "function", "class", "namespace", "member_variable", "template_parameter", "file_naming"],
) -> str:
"""
检查 C++ 标识符命名是否符合规范
参数:
identifier: 要检查的标识符名称
category: 标识符类别(variable/constant/function/class/namespace/member_variable/template_parameter/file_naming)
返回:
检查结果,包含是否符合规范、详细说明和建议
"""
checker = get_naming_checker()
is_valid, details, suggestions = checker.check_naming(identifier, category)
result = details
if suggestions:
result += f"\n推荐使用:\n"
for sug in suggestions:
result += f" • {sug}\n"
return result
@mcp.tool(
description="Check whether a C++ header file has correct include guards or #pragma once directives. Detects missing guards, malformed macro names, mismatched #endif comments, and suggests correctly formatted guard macros based on the file path.",
annotations=_READ_ONLY,
)
def check_include_guard(code: str, file_path: str = "") -> str:
"""
检查 C++ 头文件的包含保护是否正确
参数:
code: 头文件的完整代码
file_path: 可选的文件路径,用于生成建议的保护宏名
返回:
检查结果,包含是否符合规范、详细说明和建议
"""
checker = get_include_guard_checker()
file_path_param = file_path if file_path else None
is_valid, details, suggestions = checker.check_include_guard(code, file_path_param)
result = details
if suggestions and not is_valid:
result += f"\n建议的保护宏名:\n"
for sug in suggestions:
result += f" • {sug}\n"
return result
@mcp.tool(
description="Analyze C++ code for memory safety issues including memory leaks, dangling pointers, double-free errors, use of raw owning pointers, missing RAII patterns, and unsafe array operations. Returns a detailed report with line-level findings and recommendations to use modern C++ alternatives like smart pointers.",
annotations=_READ_ONLY,
)
def analyze_memory_safety(code: str) -> str:
"""
分析 C++ 代码中的内存安全问题
参数:
code: 要分析的 C++ 代码
返回:
内存安全分析报告,包括潜在的内存泄漏、悬空指针、不安全操作等
"""
analyzer = get_memory_analyzer()
issues, report = analyzer.analyze_memory_safety(code)
return report
@mcp.tool(
description="Suggest how to modernize C++ code to use features from a target standard (C++11 through C++23). Identifies outdated patterns such as raw loops replaceable by range-for, C-style casts, manual memory management, pre-C++11 type aliases, and suggests concrete rewrites using modern idioms like auto, structured bindings, std::optional, concepts, and ranges.",
annotations=_READ_ONLY,
)
def suggest_modern_cpp(
code: str,
target_standard: Literal["cpp11", "cpp14", "cpp17", "cpp20", "cpp23"] = "cpp17",
) -> str:
"""
建议将代码升级为现代 C++ 写法
参数:
code: 要分析的 C++ 代码
target_standard: 目标 C++ 标准(cpp11/cpp14/cpp17/cpp20/cpp23,默认 cpp17)
返回:
现代化建议报告,包括可以使用的新特性和重写示例
"""
suggester = get_modern_cpp_suggester()
suggestions, report = suggester.suggest_modern_cpp(code, target_standard)
return report
@mcp.tool(
description="Check C++ code for const correctness issues. Identifies member functions that do not modify state but are missing the const qualifier, non-const references where const references would suffice, and variables that are never modified but not declared const. Returns a report with specific locations and suggested fixes.",
annotations=_READ_ONLY,
)
def check_const_correctness(code: str) -> str:
"""
检查 C++ 代码中的 const 正确性
参数:
code: 要检查的 C++ 代码
返回:
const 正确性检查报告,包括缺少 const 的地方和改进建议
"""
checker = get_const_checker()
issues, report = checker.check_const_correctness(code)
return report
# ==================== Resources ====================
@mcp.resource("cpp-style://naming/{category}")
def get_naming_convention(category: str) -> str:
"""
获取 C++ 命名规范文档
可用的类别:
- variable: 变量命名
- constant: 常量命名
- function: 函数命名
- class: 类命名
- namespace: 命名空间命名
- member_variable: 成员变量命名
- template_parameter: 模板参数命名
- file_naming: 文件命名
- all: 查看所有类别
"""
resource = get_naming_resource()
if category == "all":
return resource.get_all_categories()
return resource.get_naming_rule(category)
@mcp.resource("cpp-style://best-practices/{topic}")
def get_best_practice(topic: str) -> str:
"""
获取 C++ 最佳实践指南
可用的主题:
- memory: 内存管理
- exceptions: 异常处理
- templates: 模板编程
- concurrency: 并发编程
- performance: 性能优化
- modern_cpp: 现代 C++ 特性
- all: 查看所有主题
"""
resource = get_practices_resource()
if topic == "all":
return resource.get_all_topics()
return resource.get_best_practice(topic)
@mcp.resource("cpp-style://standard/{version}")
def get_cpp_standard(version: str) -> str:
"""
获取 C++ 标准特性文档
可用的版本:
- cpp11: C++11 特性
- cpp14: C++14 特性
- cpp17: C++17 特性
- cpp20: C++20 特性
- cpp23: C++23 特性
- all: 查看所有标准概览
"""
resource = get_standards_resource()
if version == "all":
return resource.get_all_standards()
return resource.get_standard_features(version)
@mcp.resource("cpp-style://examples/{pattern}")
def get_design_pattern(pattern: str) -> str:
"""
获取 C++ 设计模式示例
可用的模式:
- singleton: 单例模式
- factory: 工厂模式
- observer: 观察者模式
- raii: RAII 惯用法
- pimpl: Pimpl 惯用法
- strategy: 策略模式
- all: 查看所有模式
"""
resource = get_patterns_resource()
if pattern == "all":
return resource.get_all_patterns()
return resource.get_pattern_example(pattern)
# ==================== Prompts ====================
@mcp.prompt()
def code_review(focus: str = "general") -> str:
"""
生成 C++ 代码审查提示模板
参数:
focus: 审查重点,可选值:
- general: 综合审查(默认)
- performance: 性能优化
- safety: 内存和类型安全
- readability: 可读性和维护性
- modern: 现代 C++ 特性使用
"""
prompt_generator = get_code_review_prompt()
return prompt_generator.generate(focus)
@mcp.prompt()
def refactor_suggestion(target_standard: str = "cpp17") -> str:
"""
生成代码重构建议提示模板
参数:
target_standard: 目标 C++ 标准
- cpp11: C++11
- cpp14: C++14
- cpp17: C++17
- cpp20: C++20
- cpp23: C++23
"""
prompt_generator = get_refactor_prompt()
return prompt_generator.generate(target_standard)
# 启动服务器(仅在直接运行时)
if __name__ == "__main__":
# 从环境变量检测运行模式,默认为 stdio
# Smithery 部署时使用 streamable-http,本地开发使用 stdio
transport = os.environ.get("MCP_TRANSPORT", "stdio")
if transport == "streamable-http":
# HTTP 模式:使用 uvicorn 手动启动以支持 PORT 环境变量
import uvicorn
port = int(os.environ.get("PORT", "8000"))
# 获取 FastMCP 的 streamable HTTP 应用(已包含 /health 自定义路由)
app = mcp.streamable_http_app()
# 启动服务器
uvicorn.run(app, host="0.0.0.0", port=port)
else:
# stdio 模式:使用标准方式启动
mcp.run(transport=transport)