Compare commits

..
Author SHA1 Message Date
thanhnv 202925e6ed feat(mcp): scaffold three project context tools
CI / test (pull_request) Canceled after 0s
2026-08-20 20:50:37 +07:00
4 changed files with 28 additions and 6 deletions
+6 -5
View File
@@ -21,12 +21,13 @@ cần đổi, mở một PR nhỏ riêng và để cả ba người rebase sau k
## Bắt đầu trong 5 phút ## Bắt đầu trong 5 phút
1. Tạo branch từ commit template chứa tài liệu này sau khi PR template merge. 1. Chạy `python --version` và xác nhận Python 3.11+ như baseline trong `requirements.txt`.
2. Đọc input/output model trong module tool được giao; không thêm field riêng của Gitea/Jira/Redmine. 2. Tạo branch từ commit template chứa tài liệu này sau khi PR template merge.
3. Implement provider read-only trong module `providers/<tool>.py`; credential chỉ lấy sau policy ALLOW. 3. Đọc input/output model trong module tool được giao; không thêm field riêng của Gitea/Jira/Redmine.
4. Thêm test happy, invalid, not-found, timeout, DENIED với `resolver.calls == 0`, output sai schema, 4. Implement provider read-only trong module `providers/<tool>.py`; credential chỉ lấy sau policy ALLOW.
5. Thêm test happy, invalid, not-found, timeout, DENIED với `resolver.calls == 0`, output sai schema,
truncation/cursor và source mở được có `revision`. truncation/cursor và source mở được có `revision`.
5. Chạy: 6. Chạy:
```bash ```bash
python -m pytest tests/test_project_context_mcp_template.py tests/test_project_context_<tool>.py -q python -m pytest tests/test_project_context_mcp_template.py tests/test_project_context_<tool>.py -q
+14
View File
@@ -3,6 +3,7 @@
from __future__ import annotations from __future__ import annotations
import os import os
import sys
from collections.abc import Callable from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
@@ -12,6 +13,18 @@ from .providers.change import build_provider as build_change_provider
from .providers.issue import build_provider as build_issue_provider from .providers.issue import build_provider as build_issue_provider
from .providers.knowledge import build_provider as build_knowledge_provider from .providers.knowledge import build_provider as build_knowledge_provider
MINIMUM_PYTHON = (3, 11)
def require_supported_python(version_info: tuple[int, ...] | None = None) -> None:
"""Fail with an actionable message before the MCP server starts."""
current = version_info or tuple(sys.version_info[:3])
if current[:2] < MINIMUM_PYTHON:
raise RuntimeError(
"Project Context MCP requires Python 3.11 or newer; "
f"current runtime is {current[0]}.{current[1]}"
)
@dataclass(frozen=True) @dataclass(frozen=True)
class ProjectScopePolicy: class ProjectScopePolicy:
@@ -46,6 +59,7 @@ def _required_environment(name: str) -> str:
def default_runtime() -> ProjectContextRuntime: def default_runtime() -> ProjectContextRuntime:
"""Build immutable runtime state; missing identity configuration fails at boot.""" """Build immutable runtime state; missing identity configuration fails at boot."""
require_supported_python()
identity = IdentityContext( identity = IdentityContext(
actor_id=_required_environment("COWORK_MCP_ACTOR_ID"), actor_id=_required_environment("COWORK_MCP_ACTOR_ID"),
org_unit=_required_environment("COWORK_MCP_ORG_UNIT"), org_unit=_required_environment("COWORK_MCP_ORG_UNIT"),
+2 -1
View File
@@ -17,7 +17,7 @@ from .foundation import (
error_result, error_result,
) )
from .registry import TOOLS_BY_NAME, tool_declarations from .registry import TOOLS_BY_NAME, tool_declarations
from .runtime import default_runtime from .runtime import default_runtime, require_supported_python
def dispatch( def dispatch(
@@ -102,6 +102,7 @@ def build_server(runtime: Optional[ProjectContextRuntime] = None):
from mcp import types from mcp import types
from mcp.server.lowlevel import Server from mcp.server.lowlevel import Server
require_supported_python()
app_runtime = runtime or default_runtime() app_runtime = runtime or default_runtime()
app = Server("project_context") app = Server("project_context")
@@ -12,6 +12,7 @@ from cowork_local.mcp_servers.project_context.registry import (
TOOL_NAMES, TOOL_NAMES,
tool_declarations, tool_declarations,
) )
from cowork_local.mcp_servers.project_context.runtime import require_supported_python
from cowork_local.mcp_servers.project_context.server import dispatch from cowork_local.mcp_servers.project_context.server import dispatch
from mcp import types from mcp import types
@@ -95,6 +96,11 @@ def test_template_exposes_exactly_three_provider_neutral_tools() -> None:
assert all(types.Tool(**item).name in EXPECTED_TOOLS for item in declarations) assert all(types.Tool(**item).name in EXPECTED_TOOLS for item in declarations)
def test_runtime_fails_fast_below_python_311() -> None:
with pytest.raises(RuntimeError, match="requires Python 3.11"):
require_supported_python((3, 9, 0))
def test_denied_request_never_resolves_credentials_or_calls_provider( def test_denied_request_never_resolves_credentials_or_calls_provider(
identity: IdentityContext, identity: IdentityContext,
) -> None: ) -> None: