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.0Context.create()signature unification (D-24): the cancel token registered for each MCP tool call is now passed as the first-classcancel_token=parameter at bothExecutionRouter._dispatchcontext-creation sites, replacing the prior post-hoccontext.cancel_token = …assignment that the apcore 0.22.0 changelog explicitly flagged inapcore-mcp-python. No public API change; full suite green (807 passed).
Breaking Changes
OpenAIConverter.convert_descriptor(strict=...)default flipped fromFalsetoTrue([D11-5] / OC-1). Cross-SDK parity withapcore-mcp-typescript0.14.0+, which already defaults to strict mode. Callers that previously relied on the lax default (additionalPropertiesallowed, originalrequiredordering preserved) must now passstrict=Falseexplicitly. Strict mode injectsadditionalProperties: false, hoists all properties intorequired(sorted alphabetically, with optionals widened to nullable), and emits"strict": trueon the function definition — the format OpenAI Structured Outputs expects. Note: the top-levelto_openai_tools()andAPCoreMCP.to_openai_tools()wrappers (andOpenAIConverter.convert_registry) still default tostrict=Falseand pass that through; this change only affects callers usingconvert_descriptordirectly with nostrictargument.
Fixed
- [D10-001]
ErrorMapper.to_mcp_erroremits canonical"Schema validation failed"forSCHEMA_VALIDATION_ERROReven whendetailsisNone. Previously Python'sif code == SCHEMA_VALIDATION_ERROR and details is not Noneguard fell through to passthrough and emitted the rawerror.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-declaredValueErrorwas 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.traceparentand forwardstransport_session_var.get()assession_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 onAPCoreMCP. Prior to 0.16.0 the same pipeline was assembled twice — once in the module-level functions inapcore_mcp/__init__.pyand once inAPCoreMCP, 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 inextra_routestyping (list[Mount]vs.list[Route | Mount]) andmetrics_collectortype narrowing. Post-refactor: APCoreMCPowns Config Bus loading (via the relocated_load_config_bus_overrideshelper), observability auto-wiring, async-task bridge construction, explorer routes, auth middleware, and transport selection.- Module-level
serve(),async_serve(), andto_openai_tools()are now thin delegators that construct anAPCoreMCPand 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 legacyserve()signature exposed.APCoreMCP._build_server_componentsnow resolvesMCPServerFactory/ExecutionRoutervia theapcore_mcppackage namespace so that existing tests patchingapcore_mcp.MCPServerFactory/apcore_mcp.ExecutionRouterintercept both legacy and class-based entry points.- Net source LOC reduction: ~180 lines deleted across the two files. The buggy code paths around
metrics_collectornarrowing (formerly at__init__.py:442/444/450/557/564/568) andextra_routestyping (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_anyfree function. The body was effectivelydel error; return internal_error_response()— a no-op delegator that was asymmetric with TypeScript / Rust (method-only onErrorMapper). Callers should useinternal_error_response()directly (identical observable behavior) orErrorMapper().to_mcp_error_any(error)for the typed-error path. - [D9-007] Deleted
apcore_mcp.inspectorstub 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 legacyLoggingMiddlewareemits aDeprecationWarningtargeting removal in apcore 1.0.0. Public surface (log_inputs/log_outputsconstructor 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 andoutput_formatparameter toserve(). Leveragesapcore-toolkit0.7 for standard tabular formatting. __apcore_module_previewmeta-tool (apcore 0.21 PROTOCOL_SPEC §5.6 / §12.8) — fifth reserved meta-tool alongside the four__apcore_task_*ones. Drivesexecutor.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: nullis 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. Thewith_limitsfactory wires this automatically.MCPServerFactory(rich_description=True)andOpenAIConverter.convert_descriptor(rich_description=True)/convert_registry(rich_description=True)— renderTool.description/ OpenAIfunction.descriptionas 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-overlaymcp.descriptionoverrides still win first. One-shot WARN log whenapcore-toolkitis not installed (recommendpip install 'apcore-mcp[markdown]').apcore_mcp.markdownmodule — 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 (introspectsdataclasses.fields(ScannedModule)to drop unknown kwargs).CIRCUIT_BREAKER_OPENerror mapping (apcore 0.20 sync alignment A-001) —ErrorMapper.to_mcp_errornow dispatchesapcore.errors.CircuitBreakerOpenErrorto a retryable=True envelope with the per-moduleaiGuidancemirrored from the apcore error class. New constantERROR_CODES["CIRCUIT_BREAKER_OPEN"]. Best-effort import shim keeps the mapper compatible with pre-0.20 apcore builds.
Fixed
AsyncTaskBridgeasync-API alignment with apcore 0.20+ — adapts to apcore'sAsyncTaskManager.{submit,cancel,shutdown}becoming async (D10-003 / D10-004). Bridge methods nowawaitupstream calls; sync transport-layer cancel handlers route throughtokio-style fire-and-forget patterns where applicable.__apcore_task_statusredactor 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: nullpreserved, missing arguments preserved, array rejection, meta-tool registration),CIRCUIT_BREAKER_OPENmapping (retryable + aiGuidance), andrich_description(Markdown rendering, display-overlay override, toolkit-missing fallback,convert_registrypropagation, factory build_tool integration). - Total suite: 771 passed (was 758).