Skip to content

Release 0.15.0

Latest

Choose a tag to compare

@tercel tercel released this 29 May 06:17

Audit-driven consistency work from /apcore-skills:audit --scope mcp. Eight per-repo fixes land here; the docs/spec repo (apcore-mcp/) remains at 0.15.0 because no spec contracts changed, so SDK versions also stay at 0.15.0 pending an explicit release decision. The entries below describe changes already committed on main.

Changed

  • Upgraded required runtime to apcore 0.22.0 and apcore-toolkit 0.8.0 (pyproject.toml: apcore>=0.22.0, apcore-toolkit>=0.8.0). Adopts the apcore 0.22.0 Context.create() signature unification (D-24): the cancel token registered for each MCP tool call is now passed as the first-class cancel_token= parameter at both ExecutionRouter._dispatch context-creation sites, replacing the prior post-hoc context.cancel_token = … assignment that the apcore 0.22.0 changelog explicitly flagged in apcore-mcp-python. No public API change; full suite green (807 passed).

Breaking Changes

  • OpenAIConverter.convert_descriptor(strict=...) default flipped from False to True ([D11-5] / OC-1). Cross-SDK parity with apcore-mcp-typescript 0.14.0+, which already defaults to strict mode. Callers that previously relied on the lax default (additionalProperties allowed, original required ordering preserved) must now pass strict=False explicitly. Strict mode injects additionalProperties: false, hoists all properties into required (sorted alphabetically, with optionals widened to nullable), and emits "strict": true on the function definition — the format OpenAI Structured Outputs expects. Note: the top-level to_openai_tools() and APCoreMCP.to_openai_tools() wrappers (and OpenAIConverter.convert_registry) still default to strict=False and pass that through; this change only affects callers using convert_descriptor directly with no strict argument.

Fixed

  • [D10-001] ErrorMapper.to_mcp_error emits canonical "Schema validation failed" for SCHEMA_VALIDATION_ERROR even when details is None. Previously Python's if code == SCHEMA_VALIDATION_ERROR and details is not None guard fell through to passthrough and emitted the raw error.message, while Rust+TS unconditionally emitted "Schema validation failed". Cross-SDK callers grouping logs or doing i18n on the canonical string no longer silently miss Python.
  • [D10-002] MCPServerFactory.create_server(name) now validates non-empty + max 255 chars per spec. Previously the spec-declared ValueError was silently absent; empty / oversized names propagated to the underlying MCP server constructor. Fix applied across all three SDKs.
  • [D11-6] Router-fallback async-bridge dispatch now extracts _meta.traceparent and forwards transport_session_var.get() as session_key. Previously the factory layer (register_handlers) extracted both, but the defensive router-layer fallback path (_dispatch) silently lost W3C trace propagation + session mass-cancel indexing. Direct-router consumers (custom test harnesses) now get parity with the factory path.

Refactored

  • [D9-001] Collapsed parallel serve() / async_serve() / to_openai_tools() pipelines into a single canonical implementation on APCoreMCP. Prior to 0.16.0 the same pipeline was assembled twice — once in the module-level functions in apcore_mcp/__init__.py and once in APCoreMCP, which then delegated back to the module-level functions. Every new feature had to be wired in two places, which had already produced latent bugs in extra_routes typing (list[Mount] vs. list[Route | Mount]) and metrics_collector type narrowing. Post-refactor:
  • APCoreMCP owns Config Bus loading (via the relocated _load_config_bus_overrides helper), observability auto-wiring, async-task bridge construction, explorer routes, auth middleware, and transport selection.
  • Module-level serve(), async_serve(), and to_openai_tools() are now thin delegators that construct an APCoreMCP and forward to its instance methods. Their public signatures are unchanged.
  • APCoreMCP.__init__ gained five new kwargs (strategy, redact_output, trace, dynamic, plus internal _load_pipeline_from_config) so it can absorb every option the legacy serve() signature exposed.
  • APCoreMCP._build_server_components now resolves MCPServerFactory / ExecutionRouter via the apcore_mcp package namespace so that existing tests patching apcore_mcp.MCPServerFactory / apcore_mcp.ExecutionRouter intercept both legacy and class-based entry points.
  • Net source LOC reduction: ~180 lines deleted across the two files. The buggy code paths around metrics_collector narrowing (formerly at __init__.py:442/444/450/557/564/568) and extra_routes typing (formerly at __init__.py:743/745/751) are gone, taking the nine pre-existing pyright errors with them.
  • [D9-004] Removed apcore_mcp.to_mcp_error_any free function. The body was effectively del error; return internal_error_response() — a no-op delegator that was asymmetric with TypeScript / Rust (method-only on ErrorMapper). Callers should use internal_error_response() directly (identical observable behavior) or ErrorMapper().to_mcp_error_any(error) for the typed-error path.
  • [D9-007] Deleted apcore_mcp.inspector stub package. A 7-line placeholder with module docstring describing future F-039 scope; zero importers. Will be re-created when the F-039 implementation begins.

Changed

  • [D5-002] Migrated to apcore.observability.context_logger.ObsLoggingMiddleware. The legacy LoggingMiddleware emits a DeprecationWarning targeting removal in apcore 1.0.0. Public surface (log_inputs / log_outputs constructor parameters) is preserved. Suite warnings dropped from 9 to 0.

Leverages apcore 0.21.0 + apcore-toolkit 0.7.0. Promotes three new
upstream capabilities into MCP-facing surface area: Module.preview()
(PROTOCOL_SPEC §5.6), CircuitBreakerOpenError (sync alignment A-001),
and apcore_toolkit.format_module(style="markdown"). Cross-SDK byte-
equivalent with apcore-mcp-typescript and apcore-mcp-rust 0.15.0.

Changed

  • Dependency bump: apcore >= 0.21.0 (was >= 0.19.0); apcore-toolkit >= 0.7.0 (was >= 0.5.0, optional [markdown] extra).

Added

  • Built-in output format support: Added --output-format (json, csv, jsonl) to CLI and output_format parameter to serve(). Leverages apcore-toolkit 0.7 for standard tabular formatting.
  • __apcore_module_preview meta-tool (apcore 0.21 PROTOCOL_SPEC §5.6 / §12.8) — fifth reserved meta-tool alongside the four __apcore_task_* ones. Drives executor.validate(module_id, inputs, context) and returns a structured {valid, requires_approval, predicted_changes, checks} envelope WITHOUT executing the module. Lets AI orchestrators answer "what would change in the world if I called this?" before invoking destructive or stateful modules. arguments: null is preserved verbatim — the calling business decides whether null is a valid input. Structurally-impossible shapes (arrays, scalars) return a typed validation error.
  • AsyncTaskBridge(executor=...) constructor kwarg — explicit Executor reference for the preview meta-tool. When omitted, falls back to the manager's bound executor. The with_limits factory wires this automatically.
  • MCPServerFactory(rich_description=True) and OpenAIConverter.convert_descriptor(rich_description=True) / convert_registry(rich_description=True) — render Tool.description / OpenAI function.description as canonical apcore-toolkit Markdown (format_module(style="markdown")) instead of the plain one-line description. Includes title, description, parameters list, returns list, behavior table (only fields differing from defaults — toolkit 0.6 alignment), tags, and examples. LLMs select tools primarily from this string; Markdown packs more decision-relevant signal per token. Display-overlay mcp.description overrides still win first. One-shot WARN log when apcore-toolkit is not installed (recommend pip install 'apcore-mcp[markdown]').
  • apcore_mcp.markdown module — public helpers: is_available(), descriptor_to_scanned_module(descriptor), render_module_markdown(descriptor, *, display=True). The descriptor adapter is forwards-compatible across toolkit minor versions (introspects dataclasses.fields(ScannedModule) to drop unknown kwargs).
  • CIRCUIT_BREAKER_OPEN error mapping (apcore 0.20 sync alignment A-001) — ErrorMapper.to_mcp_error now dispatches apcore.errors.CircuitBreakerOpenError to a retryable=True envelope with the per-module aiGuidance mirrored from the apcore error class. New constant ERROR_CODES["CIRCUIT_BREAKER_OPEN"]. Best-effort import shim keeps the mapper compatible with pre-0.20 apcore builds.

Fixed

  • AsyncTaskBridge async-API alignment with apcore 0.20+ — adapts to apcore's AsyncTaskManager.{submit,cancel,shutdown} becoming async (D10-003 / D10-004). Bridge methods now await upstream calls; sync transport-layer cancel handlers route through tokio-style fire-and-forget patterns where applicable.
  • __apcore_task_status redactor try/except symmetry — wraps the redactor call so a buggy redactor does not bring down the meta-tool; falls back to the unredacted result with a DEBUG log.

Tests

  • +9 new tests covering __apcore_module_preview (basic predict, missing module_id, arguments: null preserved, missing arguments preserved, array rejection, meta-tool registration), CIRCUIT_BREAKER_OPEN mapping (retryable + aiGuidance), and rich_description (Markdown rendering, display-overlay override, toolkit-missing fallback, convert_registry propagation, factory build_tool integration).
  • Total suite: 771 passed (was 758).