feat(mcp): separate direct order queries from wiki tools
Add an opt-in business group with schema, template, guarded SQL, and status tools. Reuse existing order statistics through read-only SQLite connections without OpenCode or wiki retrieval. Add tools set for switching enabled groups with one restart. Gate file diagnostics with file-assets and document business-only operation while retaining the independent Kingdee sync task. Validation: 217 unit tests passed; business tool tests rerun after read-only annotations. Live authenticated MCP schema/status/template/SQL calls passed, ask_wiki was unavailable, uploads returned 404, and unauthenticated requests returned 401.
This commit is contained in:
@@ -53,7 +53,8 @@ DEFAULT_RUNTIME_CLEANUP_DAYS = 30
|
||||
TOOL_FILE_ASSETS = "file-assets"
|
||||
TOOL_INGEST = "ingest"
|
||||
TOOL_QUERY = "query"
|
||||
AVAILABLE_TOOL_GROUPS = (TOOL_FILE_ASSETS, TOOL_INGEST, TOOL_QUERY)
|
||||
TOOL_BUSINESS = "business"
|
||||
AVAILABLE_TOOL_GROUPS = (TOOL_FILE_ASSETS, TOOL_INGEST, TOOL_QUERY, TOOL_BUSINESS)
|
||||
DEFAULT_ENABLED_TOOLS = (TOOL_FILE_ASSETS,)
|
||||
TERMINAL_JOB_STATES = {"succeeded", "failed", "cancelled"}
|
||||
PROXY_ENV_NAMES = ("http_proxy", "https_proxy", "all_proxy", "no_proxy", "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY", "NO_PROXY")
|
||||
@@ -71,6 +72,8 @@ def normalize_tool_name(name: str) -> str:
|
||||
"upload": TOOL_INGEST,
|
||||
"uploads": TOOL_INGEST,
|
||||
"opencode-ingest": TOOL_INGEST,
|
||||
"business": TOOL_BUSINESS,
|
||||
"orders": TOOL_BUSINESS,
|
||||
"query": TOOL_QUERY,
|
||||
"wiki-query": TOOL_QUERY,
|
||||
"ask": TOOL_QUERY,
|
||||
@@ -642,6 +645,12 @@ def render_remote_config(
|
||||
"Remote agents should upload with create_ingest_upload_ticket and X-Obsidian-Wiki-Upload-Token.\n"
|
||||
"Bearer upload is for local/admin use. POST multipart/form-data with one or more `files` fields.\n"
|
||||
)
|
||||
business_section = ""
|
||||
if enabled_tools is not None and TOOL_BUSINESS in normalize_enabled_tools(enabled_tools):
|
||||
business_section = (
|
||||
"\nBusiness query tools (direct SQLite, no OpenCode):\n"
|
||||
"Use get_business_schema, query_business_template, execute_business_sql, and get_business_status.\n"
|
||||
)
|
||||
query_section = ""
|
||||
if enabled_tools is not None and TOOL_QUERY in normalize_enabled_tools(enabled_tools):
|
||||
query_section = (
|
||||
@@ -661,6 +670,7 @@ def render_remote_config(
|
||||
f"{enabled_section}"
|
||||
f"{ingest_upload_section}"
|
||||
f"{query_section}"
|
||||
f"{business_section}"
|
||||
"\n"
|
||||
"Use this header:\n"
|
||||
f"Authorization: Bearer {token}\n"
|
||||
@@ -1000,6 +1010,17 @@ def set_tool_enabled(project_root: Path, tool: str, enabled: bool) -> str:
|
||||
else:
|
||||
current.discard(tool_name)
|
||||
updated_tools = tuple(item for item in AVAILABLE_TOOL_GROUPS if item in current)
|
||||
return _save_tool_groups(paths, config, updated_tools)
|
||||
|
||||
|
||||
def set_enabled_tools(project_root: Path, tools: Sequence[str]) -> str:
|
||||
"""Replace the whole group set with one config write and one service restart."""
|
||||
updated_tools = normalize_enabled_tools(tools)
|
||||
paths, config = ensure_service_config(project_root)
|
||||
return _save_tool_groups(paths, config, updated_tools)
|
||||
|
||||
|
||||
def _save_tool_groups(paths: ServicePaths, config: ServiceConfig, updated_tools: tuple[str, ...]) -> str:
|
||||
opencode_path = config.opencode_path
|
||||
if ({TOOL_INGEST, TOOL_QUERY} & set(updated_tools)) and not opencode_path:
|
||||
opencode_path = resolve_opencode_path()
|
||||
@@ -1257,6 +1278,10 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
tools_disable_parser.add_argument("tool", choices=AVAILABLE_TOOL_GROUPS)
|
||||
tools_disable_parser.add_argument("--project-root", default=Path(__file__).resolve().parent.parent, type=Path)
|
||||
|
||||
tools_set_parser = tools_subparsers.add_parser("set", help="Replace enabled groups and restart once; e.g. tools set business.")
|
||||
tools_set_parser.add_argument("tools", nargs="+", choices=AVAILABLE_TOOL_GROUPS)
|
||||
tools_set_parser.add_argument("--project-root", default=Path(__file__).resolve().parent.parent, type=Path)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.command == "new-token":
|
||||
@@ -1309,6 +1334,9 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
print(cleanup_runtime(args.project_root, max_age_days=args.max_age_days), end="")
|
||||
return 0
|
||||
if args.command == "tools":
|
||||
if args.tools_command == "set":
|
||||
print(set_enabled_tools(args.project_root, args.tools), end="")
|
||||
return 0
|
||||
if args.tools_command == "list":
|
||||
print(list_tools(args.project_root), end="")
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user