author-mcp-tool
Sub-skill of `compose-domain`. Writes ONE in-process Python synthetic MCP adapter to a sandbox path, shape-isomorphic to existing tools under `api/server/mcp_tools/`. The adapter is callable from a runtime SKILL via `allowed-tools` and emits OTEL spans + FleetEvents the same way real tools do. Returns deterministic synthetic data — no real backend. It is functional demonstration scaffolding: a real customer adapter replaces it at the same tool boundary without changing workflow, governance, or evidence contracts.
- License 许可
- MIT
- 最近更新
- 2026-09-28
- Stars
- ★ 1
- Forks
- 2
01 前言 Front Matter
- name
- author-mcp-tool
- audience
- design-time-only
- description
- Sub-skill of `compose-domain`. Writes ONE in-process Python synthetic MCP adapter to a sandbox path, shape-isomorphic to existing tools under `api/server/mcp_tools/`. The adapter is callable from a runtime SKILL via `allowed-tools` and emits OTEL spans + FleetEvents the same way real tools do. Returns deterministic synthetic data — no real backend. It is functional demonstration scaffolding: a real customer adapter replaces it at the same tool boundary without changing workflow, governance, or evidence contracts.
- forbidden-runtime
- true
03 SKILL.md 正文
author-mcp-tool
You write one Python file per invocation: an in-process synthetic MCP
adapter.
The adapter mirrors the shape of api/server/mcp_tools/claim_lookup.py and
api/server/mcp_tools/policy_search.py exactly. It is callable from a
runtime SKILL via the GHCP SDK's tools=[…] channel and produces OTEL
spans the same way real tools do — so the Observatory animates correctly
without anyone having to plug in a real backend.
Semantic boundary. This adapter is functional demonstration scaffolding, not an unfinished implementation. It returns deterministic synthetic data so reference runs require no customer system. When a customer connects their estate, they replace this adapter at the same tool boundary — the workflow, governance evidence contracts, and HITL policies do not change.
Inputs you require from the caller
output_path— absolute path inside the sandbox, e.g.tools/scratch/compose-domain/<run-id>/api/server/mcp_tools/<tool>.py.tool_brief— the relevantexternal_systems[]entry from the YAML brief:id: <snake_case> mcp_tool: <snake_case Python module name> operations: [<snake_case function name>, ...]canonical_example_path— one absolute path. v1 canonical isapi/server/mcp_tools/claim_lookup.py(HTTP-backed pattern with the in-memory escape hatch). The three v1 graduated adapters (workday_hr_employee.py,concur_travel_policy.py,concur_travel_search.py) are working examples of the in-memory shape this skill produces.
If any input is missing, stop and ask.
Note on standalone smoke testing
Importing any module under api/server/mcp_tools/ triggers
api/server/mcp_tools/__init__.py, which transitively imports
api/server/state.py → BlobStore, which connects to Azurite at
localhost:10000 on construction. Standalone import-smoke of a generated
tool therefore requires Azurite running:
azurite --silent --location azurite-data \
--blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 &
Without Azurite, python -c 'import api.server.mcp_tools.<tool>' hangs
in retry loops until SIGINT. This is a property of the existing harness,
not of the generated adapter.
Procedure
Step 1 — Read both canonical examples
Read canonical_example_path end-to-end. Note:
- the module docstring style (purpose + how it's exposed + a one-line note about synthetic data vs production-grade).
- the
from copilot.tools import ToolResult, define_toolimport. - the
from ._otel import traced_toolimport. - the Pydantic params class convention (
_FooParams(BaseModel)). - the
@define_tool(name=..., description=...)registration pattern. - the
@traced_tool("<dotted.name>")wrapper on the underlying function.
You will mirror this shape. The three v1 graduated adapters
(workday_hr_employee.py, concur_travel_policy.py,
concur_travel_search.py) are good targets to compare against once
you've drafted yours — they are deterministic, self-contained, and
passed CHECKLIST §4.6–4.7.
Step 2 — Decide: HTTP-backed or in-memory?
For v1 we always pick in-memory deterministic synthetic MCP adapter. Reasons:
- The point of the generated domain is to animate in the Observatory and exercise the orchestrator harness — not to integrate with a real upstream system.
- HTTP-backed adds a Node mock server, port assignment, env var, and a health check. All real engineering, all out of v1 scope.
If the operator/caller insists on HTTP-backed, stop. Tell them an HTTP-
backed tool is a separate workflow that lives outside this skill: it
requires authoring a Node mock server under mocks/<name>-mcp/, picking a
port, adding it to docker-compose.yml, etc. They should write that by
hand, then point a future revision of the in-memory adapter at it.
Step 3 — Build the body
Use the template at docs/superpowers/skills/compose-domain/templates/mcp_tool.py.tmpl
as the skeleton.
The shape:
"""<tool>.<operation> MCP tool — <one-line purpose>.
Synthetic MCP adapter generated by compose-domain. Returns deterministic
synthetic data keyed on the input(s). No real upstream call. Replace this
adapter at the same tool boundary when connecting the customer estate.
"""
from __future__ import annotations
import hashlib
import json
from copilot.tools import ToolResult, define_tool
from opentelemetry import trace
from pydantic import BaseModel, Field
from ._otel import traced_tool
@traced_tool("<tool>.<operation>")
def <operation>(<typed-args>) -> dict:
"""<One-line purpose> using deterministic synthetic data."""
span = trace.get_current_span()
span.set_attribute("zava.<tool>.<arg>", str(<arg>))
return _synth(<arg>)
def _synth(<arg>) -> dict:
"""Deterministic synthesis. Same input → same output across runs."""
seed = int(hashlib.sha256(str(<arg>).encode()).hexdigest()[:8], 16)
# …deterministic dict construction keyed on `seed`…
return {…}
class _<Operation>Params(BaseModel):
<arg>: <type> = Field(description="<one line>")
@define_tool(
name="<tool>_<operation>",
description=(
"<One sentence describing what this tool does and when an agent would call it. "
"Synthetic adapter: deterministic demonstration data.>"
),
)
def <tool>_<operation>_tool(params: _<Operation>Params) -> ToolResult:
try:
result = <operation>(params.<arg>)
return ToolResult(text_result_for_llm=json.dumps(result, ensure_ascii=False))
except Exception as ex:
return ToolResult(text_result_for_llm="", result_type="failure", error=str(ex))
SDK contract.
ToolResult.__init__takestext_result_for_llm: strandresult_type: "success" | "failure". It does NOT acceptsuccess=/content=(an older draft API). Always JSON-serialise the dict result intotext_result_for_llm; the SDK feeds that string back to the model as the tool output. Returning the wrong shape silently breaks every tool call — the model sees aTypeErrorstring and falls back to invented prose.
One Python module = one mcp_tool from the brief = one or more
operations. If the brief lists multiple operations for the same
mcp_tool, generate one <operation> function and one
<operation>_tool registration per operation, all in the same module.
Step 4 — Determinism
Every operation must be pure: same input → byte-identical output. Use
hashlib.sha256(str(input).encode()) to seed any synthetic structure
(employee names, calendar busy windows, leave balances). No time.time(),
no random.random(), no environment reads.
This matters because the meta-skill's quality bar is "two runs against the same brief diff to nothing". Non-deterministic synthetic MCP adapters would break that even if the SKILL.md generator is perfectly stable.
Step 5 — Synthetic-data realism
Make the synthesis good enough that the agent skill's allowed-tools
calls return something the agent can plausibly reason over.
For example, for a workday_hr_leave_balance.get_leave_balance(employee_id)
operation:
{
"employee_id": "EMP-0042",
"year": 2026,
"annual_entitlement_days": 25,
"taken_days": 12,
"booked_days": 5,
"remaining_days": 8,
"carry_over_from_prior_year": 0,
}
— numbers consistent (taken + booked + remaining = entitlement + carry_over),
keyed off the seed so the same employee_id always returns the same record.
Do not include real-looking PII. Synthesised employee names should follow
the existing repo convention (EMP-NNNN, fixture employees only).
Step 6 — Write the file
Write the assembled Python module to output_path. Create parent
directories if needed.
Step 7 — Self-check
Before returning, verify:
- Module imports resolve textually against the canonical examples
(
copilot.tools,opentelemetry.trace,pydantic,._otel). - Every
operationfrom the brief has both an<operation>function and an<operation>_toolregistration. - Every
<operation>function is@traced_tool(…)-wrapped. - The synth function is pure (no
time,random,os.environ, network). - No
TODOplaceholder remains. - The output JSON shape is internally consistent (cross-field invariants hold).
If any check fails, fix in place. If a check fails because the brief asks for something you can't deterministically synthesise, stop and tell the caller.
Anti-patterns
- Reading from
data/synthetic/to "make it more realistic". The real synthetic corpus is for the existing two domains. New domains synthesise from a seed. - Importing
httpxand pointing at a non-existent localhost port. The whole point of the in-memory synthetic MCP adapter is no port management. - Inventing OTEL attribute names. Mirror what
claim_lookup.pydoes (zava.<tool>.<arg>). - Returning lists where the canonical examples return dicts. Stay isomorphic.
- Adding multiple
mcp_toolfiles in one invocation. One file per call. The caller decides how many calls.
04 同仓库更多 Skills
proactive-customer-care
Proactively notifies and, where warranted, extends a small bounded goodwill gesture to a customer whose itinerary materially changed.
payment-exception
Resolves a failed balance payment by retrying it to complete the booking, or releasing the booking's inventory when retry is not viable.
hotel-supplier-recovery
Restores accommodation for a shortfall-hit hotel allotment by moving contracted rooms from a donor allotment with headroom, resolving the reported supplier shortfall.
holiday-sales-booking
暂无简介
flight-disruption-recovery
暂无简介
destination-operations
Dispatches a replacement resort transfer when an arrival is at risk, rebinding any travellers on the at-risk transfer to the replacement.