Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

## Table of Contents

- [What's new (2026-06-19) — MCP Structured Output](#whats-new-2026-06-19--mcp-structured-output)
- [What's new (2026-06-19) — Tweened Drag](#whats-new-2026-06-19--tweened-drag)
- [What's new (2026-06-19) — Process-Doc (SOP) Generator](#whats-new-2026-06-19--process-doc-sop-generator)
- [What's new (2026-06-19) — Heal Analytics & Secret Scan](#whats-new-2026-06-19--heal-analytics--secret-scan)
Expand Down Expand Up @@ -82,6 +83,12 @@

---

## What's new (2026-06-19) — MCP Structured Output

MCP 2025-06-18 structured tool output. Full reference: [`docs/source/Eng/doc/new_features/v30_features_doc.rst`](docs/source/Eng/doc/new_features/v30_features_doc.rst).

- **`MCPTool(output_schema=...)`** — a tool may declare an `outputSchema`; its dict result is returned as `structuredContent` in the `tools/call` response so clients/LLMs consume a typed, schema-validated object instead of re-parsing text. `to_descriptor()` advertises it in `tools/list`; non-dict results and schema-less tools are unchanged. `ac_validate_rows` is the first built-in to adopt it.

## What's new (2026-06-19) — Tweened Drag

Deterministic eased drags. Full reference: [`docs/source/Eng/doc/new_features/v29_features_doc.rst`](docs/source/Eng/doc/new_features/v29_features_doc.rst).
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目录

- [本次更新 (2026-06-19) — MCP 结构化输出](#本次更新-2026-06-19--mcp-结构化输出)
- [本次更新 (2026-06-19) — 缓动拖拽](#本次更新-2026-06-19--缓动拖拽)
- [本次更新 (2026-06-19) — 流程文档(SOP)生成器](#本次更新-2026-06-19--流程文档sop生成器)
- [本次更新 (2026-06-19) — 修复分析与机密扫描](#本次更新-2026-06-19--修复分析与机密扫描)
Expand Down Expand Up @@ -81,6 +82,12 @@

---

## 本次更新 (2026-06-19) — MCP 结构化输出

MCP 2025-06-18 结构化工具输出。完整参考:[`docs/source/Zh/doc/new_features/v30_features_doc.rst`](../docs/source/Zh/doc/new_features/v30_features_doc.rst)。

- **`MCPTool(output_schema=...)`** — 工具可声明 `outputSchema`;其 dict 结果会在 `tools/call` 响应以 `structuredContent` 返回,让客户端/LLM 消费类型化、经 schema 验证的对象而非重新解析文本。`to_descriptor()` 会在 `tools/list` 公告;非 dict 结果与未声明 schema 的工具行为不变。`ac_validate_rows` 为首个采用。

## 本次更新 (2026-06-19) — 缓动拖拽

确定性的缓动拖拽。完整参考:[`docs/source/Zh/doc/new_features/v29_features_doc.rst`](../docs/source/Zh/doc/new_features/v29_features_doc.rst)。
Expand Down
7 changes: 7 additions & 0 deletions README/README_zh-TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

## 目錄

- [本次更新 (2026-06-19) — MCP 結構化輸出](#本次更新-2026-06-19--mcp-結構化輸出)
- [本次更新 (2026-06-19) — 緩動拖曳](#本次更新-2026-06-19--緩動拖曳)
- [本次更新 (2026-06-19) — 流程文件(SOP)產生器](#本次更新-2026-06-19--流程文件sop產生器)
- [本次更新 (2026-06-19) — 修復分析與機密掃描](#本次更新-2026-06-19--修復分析與機密掃描)
Expand Down Expand Up @@ -81,6 +82,12 @@

---

## 本次更新 (2026-06-19) — MCP 結構化輸出

MCP 2025-06-18 結構化工具輸出。完整參考:[`docs/source/Zh/doc/new_features/v30_features_doc.rst`](../docs/source/Zh/doc/new_features/v30_features_doc.rst)。

- **`MCPTool(output_schema=...)`** — 工具可宣告 `outputSchema`;其 dict 結果會在 `tools/call` 回應以 `structuredContent` 回傳,讓用戶端/LLM 消費型別化、經 schema 驗證的物件而非重新解析文字。`to_descriptor()` 會在 `tools/list` 公告;非 dict 結果與未宣告 schema 的工具行為不變。`ac_validate_rows` 為首個採用。

## 本次更新 (2026-06-19) — 緩動拖曳

決定性的緩動拖曳。完整參考:[`docs/source/Zh/doc/new_features/v29_features_doc.rst`](../docs/source/Zh/doc/new_features/v29_features_doc.rst)。
Expand Down
40 changes: 40 additions & 0 deletions docs/source/Eng/doc/new_features/v30_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
==================================================
New Features (2026-06-19) — MCP Structured Tool Output
==================================================

The MCP server now supports the 2025-06-18 **structured tool output** spec
feature: a tool may declare an ``outputSchema``, and its dict result is
returned as ``structuredContent`` in the ``tools/call`` response (alongside
the usual text ``content``). MCP clients and LLMs can then consume a typed,
schema-validated object instead of re-parsing text — saving tokens and
errors. Pure standard library; an MCP-framework enhancement (no new
``AC_*`` command).

.. contents::
:local:
:depth: 2


Declaring an output schema
=========================

::

from je_auto_control.utils.mcp_server.tools import MCPTool

MCPTool(
name="ac_validate_rows", description="...",
input_schema=..., handler=validate_rows,
output_schema={"type": "object", "properties": {
"ok": {"type": "boolean"}, "errors": {"type": "array"}}},
)

``to_descriptor()`` then includes ``outputSchema`` in ``tools/list``, and a
``tools/call`` whose handler returns a ``dict`` includes
``structuredContent`` in the result. Tools without an ``outputSchema`` are
unchanged; non-dict results never produce ``structuredContent``.

The first built-in tool to adopt this is ``ac_validate_rows`` (data-quality
validation), whose ``{ok, total, valid_count, invalid_count, errors, ...}``
result is now schema-typed; more tools can opt in by adding an
``output_schema``.
1 change: 1 addition & 0 deletions docs/source/Eng/eng_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Comprehensive guides for all AutoControl features.
doc/new_features/v27_features_doc
doc/new_features/v28_features_doc
doc/new_features/v29_features_doc
doc/new_features/v30_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
37 changes: 37 additions & 0 deletions docs/source/Zh/doc/new_features/v30_features_doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
==================================================
新功能 (2026-06-19) — MCP 結構化工具輸出
==================================================

MCP 伺服器現在支援 2025-06-18 的**結構化工具輸出**規範:工具可宣告
``outputSchema``,其 dict 結果會在 ``tools/call`` 回應中以
``structuredContent`` 回傳(與一般的文字 ``content`` 並列)。MCP 用戶端
與 LLM 即可消費經 schema 驗證的型別化物件,而非重新解析文字——省 token、
少出錯。純標準庫;屬 MCP 框架增強(不新增 ``AC_*`` 指令)。

.. contents::
:local:
:depth: 2


宣告輸出 schema
===============

::

from je_auto_control.utils.mcp_server.tools import MCPTool

MCPTool(
name="ac_validate_rows", description="...",
input_schema=..., handler=validate_rows,
output_schema={"type": "object", "properties": {
"ok": {"type": "boolean"}, "errors": {"type": "array"}}},
)

``to_descriptor()`` 會在 ``tools/list`` 中包含 ``outputSchema``;當
``tools/call`` 的 handler 回傳 ``dict`` 時,結果會包含 ``structuredContent``。
未宣告 ``outputSchema`` 的工具行為不變;非 dict 結果不會產生
``structuredContent``。

首個採用的內建工具是 ``ac_validate_rows``(資料品質驗證),其
``{ok, total, valid_count, invalid_count, errors, ...}`` 結果現在已型別化;
其他工具可透過加上 ``output_schema`` 逐步採用。
1 change: 1 addition & 0 deletions docs/source/Zh/zh_index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ AutoControl 所有功能的完整使用指南。
doc/new_features/v27_features_doc
doc/new_features/v28_features_doc
doc/new_features/v29_features_doc
doc/new_features/v30_features_doc
doc/ocr_backends/ocr_backends_doc
doc/observability/observability_doc
doc/operations_layer/operations_layer_doc
Expand Down
7 changes: 6 additions & 1 deletion je_auto_control/utils/mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,15 @@ def _handle_tools_call(self, msg_id: Any,
tool=name, arguments=arguments, status="ok",
duration_seconds=time.monotonic() - started_at,
)
return {
response: Dict[str, Any] = {
"content": _to_content_blocks(result),
"isError": False,
}
# 2025-06-18 spec: tools with an outputSchema return their dict result
# as structuredContent for typed, token-cheap client consumption.
if tool.output_schema is not None and isinstance(result, dict):
response["structuredContent"] = result
return response

def request_elicitation(self, message: str,
requested_schema: Optional[Dict[str, Any]] = None,
Expand Down
6 changes: 5 additions & 1 deletion je_auto_control/utils/mcp_server/tools/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,19 @@ class MCPTool:
input_schema: Dict[str, Any]
handler: Callable[..., Any]
annotations: MCPToolAnnotations = MCPToolAnnotations()
output_schema: Optional[Dict[str, Any]] = None

def to_descriptor(self) -> Dict[str, Any]:
"""Return the dict shape MCP clients expect from ``tools/list``."""
return {
descriptor: Dict[str, Any] = {
"name": self.name,
"description": self.description,
"inputSchema": self.input_schema,
"annotations": self.annotations.to_dict(),
}
if self.output_schema is not None:
descriptor["outputSchema"] = self.output_schema
return descriptor

def invoke(self, arguments: Dict[str, Any], ctx: Any = None) -> Any:
"""Call the underlying handler with keyword arguments.
Expand Down
9 changes: 9 additions & 0 deletions je_auto_control/utils/mcp_server/tools/_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -2193,6 +2193,15 @@ def data_quality_tools() -> List[MCPTool]:
"rows": {"type": "array", "items": {"type": "object"}},
"schema": {"type": "object"},
}, required=["rows", "schema"]),
output_schema=schema({
"ok": {"type": "boolean"},
"total": {"type": "integer"},
"valid_count": {"type": "integer"},
"invalid_count": {"type": "integer"},
"valid": {"type": "array"},
"invalid": {"type": "array"},
"errors": {"type": "array", "items": {"type": "object"}},
}, required=["ok", "errors"]),
handler=h.validate_rows,
annotations=READ_ONLY,
),
Expand Down
48 changes: 48 additions & 0 deletions test/unit_test/headless/test_mcp_structured_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Headless tests for MCP structured tool output (2025-06-18 spec):
optional outputSchema on a tool + structuredContent in the tools/call
result. Pure stdlib; no Qt imports."""
from je_auto_control.utils.mcp_server.server import MCPServer
from je_auto_control.utils.mcp_server.tools import (
MCPTool, build_default_tool_registry)

_EMPTY_INPUT = {"type": "object", "properties": {}}
_OUT = {"type": "object", "properties": {"value": {"type": "integer"}}}


def test_output_schema_in_descriptor():
typed = MCPTool(name="t", description="d", input_schema=_EMPTY_INPUT,
handler=lambda: {"value": 1}, output_schema=_OUT)
plain = MCPTool(name="p", description="d", input_schema=_EMPTY_INPUT,
handler=lambda: {"value": 1})
assert typed.to_descriptor()["outputSchema"] == _OUT
assert "outputSchema" not in plain.to_descriptor()


def test_structured_content_only_with_output_schema():
typed = MCPTool(name="typed", description="d", input_schema=_EMPTY_INPUT,
handler=lambda: {"value": 42}, output_schema=_OUT)
plain = MCPTool(name="plain", description="d", input_schema=_EMPTY_INPUT,
handler=lambda: {"value": 7})
server = MCPServer(tools=[typed, plain])
typed_result = server._handle_tools_call(2, {"name": "typed",
"arguments": {}})
plain_result = server._handle_tools_call(3, {"name": "plain",
"arguments": {}})
assert typed_result["isError"] is False
assert typed_result["structuredContent"] == {"value": 42}
assert "structuredContent" not in plain_result


def test_non_dict_result_has_no_structured_content():
listy = MCPTool(name="listy", description="d", input_schema=_EMPTY_INPUT,
handler=lambda: [1, 2, 3], output_schema=_OUT)
server = MCPServer(tools=[listy])
result = server._handle_tools_call(2, {"name": "listy", "arguments": {}})
assert "structuredContent" not in result # only dict results qualify


def test_default_registry_tool_declares_output_schema():
tool = {t.name: t for t in build_default_tool_registry()}["ac_validate_rows"]
assert tool.output_schema is not None
assert "ok" in tool.output_schema["properties"]
assert "outputSchema" in tool.to_descriptor()
Loading