more prompts for the LLM user

This commit is contained in:
lda
2026-05-18 17:24:37 +07:00 Verified
parent 4d90101e4b
commit 40f4d53352
9 changed files with 172 additions and 7 deletions
+22 -1
View File
@@ -20,7 +20,7 @@ connections into that model rather than owning the model itself.
```text ```text
CapabilitySource CapabilitySource
id: "wf.std" | "wf.mcp" | "wf.admin" | "<server>.<account>" id: "wf.std" | "wf.docs" | "wf.mcp" | "wf.admin" | "<server>.<account>"
kind: "system" | "connection" kind: "system" | "connection"
enabled: bool enabled: bool
visibility: visibility:
@@ -49,6 +49,27 @@ future saved wrapper artifacts, see
## Canonical Sources ## Canonical Sources
### `wf.docs`
Local platform documentation.
- Planner-visible: no.
- MCP-client-visible: yes.
- Admin-dashboard-visible: yes.
- MCP tools: none.
- Workflow safety: not applicable; it owns docs, not workflow nodes.
Current capabilities:
- `prompts`: `wf.docs.operator_guide`, `wf.docs.workflow_authoring_guide`,
`wf.docs.troubleshooting_guide`.
- `resources`: `wf://docs/operator-manual`,
`wf://docs/end-to-end-runbook`, `wf://docs/troubleshooting`.
The documentation resource model lives in `wf_platform`, not in the MCP
projection layer. That lets the same manuals feed MCP resources now and other
surfaces such as a future CLI or UI later.
### `wf.std` ### `wf.std`
Workflow standard library. Workflow standard library.
+22 -1
View File
@@ -27,7 +27,7 @@ with each other just because MCP transports them all.
| Noun | Meaning | Example | | Noun | Meaning | Example |
| --- | --- | --- | | --- | --- | --- |
| Connection | A configured upstream MCP account/profile with auth and transport settings. | `everything.default` | | Connection | A configured upstream MCP account/profile with auth and transport settings. | `everything.default` |
| Source | A named owner of capabilities. A source may be local or backed by a connection. | `wf.std`, `wf.mcp`, `everything.default` | | Source | A named owner of capabilities. A source may be local or backed by a connection. | `wf.std`, `wf.docs`, `wf.mcp`, `everything.default` |
| Catalog | A discovered snapshot of backend MCP capabilities. | tools/resources/prompts loaded from `everything.default` | | Catalog | A discovered snapshot of backend MCP capabilities. | tools/resources/prompts loaded from `everything.default` |
| Workflow capability | A workflow-ready `NodeSpec` contract that graphs can consume. | `wf.std.runtime_error`, `everything.default.echo` | | Workflow capability | A workflow-ready `NodeSpec` contract that graphs can consume. | `wf.std.runtime_error`, `everything.default.echo` |
| Artifact | An immutable saved workflow definition or saved wrapper workflow. | `codex_echo_probe` version `2` | | Artifact | An immutable saved workflow definition or saved wrapper workflow. | `codex_echo_probe` version `2` |
@@ -92,6 +92,26 @@ Typical tools:
- `wf.workflow.validate_deployment` - `wf.workflow.validate_deployment`
- `wf.workflow.run_deployment` - `wf.workflow.run_deployment`
### `wf.docs`
Local documentation source.
It owns stable documentation resources such as:
- `wf://docs/operator-manual`
- `wf://docs/end-to-end-runbook`
- `wf://docs/troubleshooting`
It also owns short guide prompts such as:
- `wf.docs.operator_guide`
- `wf.docs.workflow_authoring_guide`
- `wf.docs.troubleshooting_guide`
This source exists so manuals are discoverable through the same capability model
as everything else. The docs themselves are provider-neutral platform
resources; MCP is only one projection of them.
### Proxied Upstream Tools ### Proxied Upstream Tools
These are the upstream MCP tools themselves, projected under connection/source These are the upstream MCP tools themselves, projected under connection/source
@@ -257,6 +277,7 @@ connection.
Examples: Examples:
- `everything.default`: both a connection and a source - `everything.default`: both a connection and a source
- `wf.docs`: a local documentation source, not a connection
- `wf.std`: a local source, not a connection - `wf.std`: a local source, not a connection
- `wf.admin`: a privileged local source, not a connection - `wf.admin`: a privileged local source, not a connection
+42 -2
View File
@@ -2,7 +2,12 @@ from __future__ import annotations
from pathlib import Path from pathlib import Path
from wf_platform import CapabilitySource, DocumentationResource, build_documentation_source from wf_platform import (
CapabilitySource,
DocumentationPrompt,
DocumentationResource,
build_documentation_source,
)
def build_local_documentation_source(repo_root: Path) -> CapabilitySource: def build_local_documentation_source(repo_root: Path) -> CapabilitySource:
@@ -31,7 +36,42 @@ def build_local_documentation_source(repo_root: Path) -> CapabilitySource:
title="wf_mcp Troubleshooting", title="wf_mcp Troubleshooting",
description="Failure-oriented guide for discovery and deployment issues.", description="Failure-oriented guide for discovery and deployment issues.",
), ),
] ],
prompts=[
DocumentationPrompt(
name="wf.docs.operator_guide",
title="wf_mcp Operator Guide",
description="Choose the right wf_mcp manual for the task at hand.",
text=(
"Use wf://docs/operator-manual for the platform mental model. "
"Use wf://docs/end-to-end-runbook for the normal connection-to-run "
"flow. Use wf://docs/troubleshooting when a source, capability, "
"or deployment is missing or unrunnable."
),
),
DocumentationPrompt(
name="wf.docs.workflow_authoring_guide",
title="Workflow Authoring Guide",
description="Guide an authoring client through safe capability discovery.",
text=(
"Start with wf.admin.list_sources, then use "
"wf.workflow.list_capabilities and "
"wf.workflow.inspect_capability. Test a small reusable piece with "
"wf.workflow.call_capability before saving a larger artifact. "
"Read wf://docs/end-to-end-runbook for the full sequence."
),
),
DocumentationPrompt(
name="wf.docs.troubleshooting_guide",
title="wf_mcp Troubleshooting Guide",
description="Point a client to the failure-oriented manual.",
text=(
"When a source, capability, or deployment is missing, inspect the "
"smallest layer first and read wf://docs/troubleshooting for the "
"diagnostic ladder."
),
),
],
) )
+2
View File
@@ -14,6 +14,7 @@ from ..documentation import build_local_documentation_source
from ..models import BrokerConfig from ..models import BrokerConfig
from ..transparent_proxy.runtime import ProxyRuntime from ..transparent_proxy.runtime import ProxyRuntime
from ..workflow_surface import register_workflow_tools from ..workflow_surface import register_workflow_tools
from .prompts import register_documentation_prompts
from .resources import register_documentation_resources from .resources import register_documentation_resources
@@ -46,6 +47,7 @@ def create_server(
register_workflow_tools(runtime.server, service) register_workflow_tools(runtime.server, service)
docs_source = build_local_documentation_source(_repo_root()) docs_source = build_local_documentation_source(_repo_root())
service.capability_sources[docs_source.id] = docs_source service.capability_sources[docs_source.id] = docs_source
register_documentation_prompts(runtime.server, docs_source)
register_documentation_resources(runtime.server, docs_source) register_documentation_resources(runtime.server, docs_source)
return runtime.server return runtime.server
+33
View File
@@ -0,0 +1,33 @@
from __future__ import annotations
from typing import Any
from fastmcp import FastMCP
from wf_platform import CapabilitySource, DocumentationPrompt
def register_documentation_prompts(
server: FastMCP[Any],
source: CapabilitySource,
) -> None:
"""Project provider-neutral documentation prompts through MCP."""
for prompt in source.capabilities.prompts.values():
if not isinstance(prompt, DocumentationPrompt):
continue
_register_documentation_prompt(server, prompt)
def _register_documentation_prompt(
server: FastMCP[Any],
prompt: DocumentationPrompt,
) -> None:
"""Bind one curated docs guide prompt to its short text."""
@server.prompt(
name=prompt.name,
title=prompt.title,
description=prompt.description,
)
def documentation_prompt() -> str:
return prompt.text
+2 -1
View File
@@ -1,4 +1,4 @@
from .docs import DocumentationResource, build_documentation_source from .docs import DocumentationPrompt, DocumentationResource, build_documentation_source
from .refs import CapabilityRef, SourceRef from .refs import CapabilityRef, SourceRef
from .paging import Page, page_items from .paging import Page, page_items
from .schema_hashes import hash_json_schema from .schema_hashes import hash_json_schema
@@ -21,6 +21,7 @@ __all__ = [
"CapabilityBuckets", "CapabilityBuckets",
"CapabilitySource", "CapabilitySource",
"CapabilityRef", "CapabilityRef",
"DocumentationPrompt",
"DocumentationResource", "DocumentationResource",
"NodeSpecInventory", "NodeSpecInventory",
"Page", "Page",
+14 -1
View File
@@ -17,15 +17,28 @@ class DocumentationResource:
text: str text: str
@dataclass(frozen=True, slots=True)
class DocumentationPrompt:
"""Provider-neutral prompt that guides a client toward documentation."""
name: str
title: str
description: str
text: str
def build_documentation_source( def build_documentation_source(
resources: list[DocumentationResource], resources: list[DocumentationResource],
*,
prompts: list[DocumentationPrompt] | None = None,
) -> CapabilitySource: ) -> CapabilitySource:
"""Build the local documentation source without depending on MCP transport.""" """Build the local documentation source without depending on MCP transport."""
return CapabilitySource( return CapabilitySource(
id="wf.docs", id="wf.docs",
kind="system", kind="system",
capabilities=CapabilityBuckets( capabilities=CapabilityBuckets(
resources={resource.name: resource for resource in resources} resources={resource.name: resource for resource in resources},
prompts={prompt.name: prompt for prompt in prompts or []},
), ),
visibility=SourceVisibility(mcp_client=True), visibility=SourceVisibility(mcp_client=True),
description="Local operator and workflow documentation.", description="Local operator and workflow documentation.",
+13 -1
View File
@@ -3,6 +3,7 @@ from __future__ import annotations
from wf_platform import ( from wf_platform import (
CapabilityBuckets, CapabilityBuckets,
CapabilitySource, CapabilitySource,
DocumentationPrompt,
DocumentationResource, DocumentationResource,
SourceInventory, SourceInventory,
SourcePermissions, SourcePermissions,
@@ -66,7 +67,15 @@ def test_documentation_source_owns_provider_neutral_resources() -> None:
mime_type="text/markdown", mime_type="text/markdown",
text="# Operator Manual", text="# Operator Manual",
) )
] ],
prompts=[
DocumentationPrompt(
name="wf.docs.operator_guide",
title="Operator Guide",
description="Guide a human operator to the right manuals.",
text="Read wf://docs/operator-manual first.",
)
],
) )
resource = source.capabilities.resources["wf.docs.operator_manual"] resource = source.capabilities.resources["wf.docs.operator_manual"]
@@ -75,3 +84,6 @@ def test_documentation_source_owns_provider_neutral_resources() -> None:
assert source.visibility.mcp_client is True assert source.visibility.mcp_client is True
assert resource.uri == "wf://docs/operator-manual" assert resource.uri == "wf://docs/operator-manual"
assert resource.text == "# Operator Manual" assert resource.text == "# Operator Manual"
assert source.capabilities.prompts["wf.docs.operator_guide"].text == (
"Read wf://docs/operator-manual first."
)
+22
View File
@@ -82,6 +82,7 @@ def test_server_exposes_upstream_admin_and_workflow_tools() -> None:
for source in _structured(sources_result)["sources"] for source in _structured(sources_result)["sources"]
} }
assert "wf.admin" in source_ids assert "wf.admin" in source_ids
assert "wf.docs" in source_ids
assert "wf.mcp" in source_ids assert "wf.mcp" in source_ids
assert "wf.std" in source_ids assert "wf.std" in source_ids
@@ -203,6 +204,27 @@ def test_server_exposes_platform_documentation_resources() -> None:
asyncio.run(run_proxy()) asyncio.run(run_proxy())
def test_server_exposes_platform_documentation_prompts() -> None:
config = BrokerConfig(
store_root=local_temp_root() / "unified_docs_prompt_store",
connections=[],
)
async def run_proxy() -> None:
client = create_server_client(config, admin_tools=False)
async with client:
prompts = await client.list_prompts()
names = [prompt.name for prompt in prompts]
assert "wf.docs.operator_guide" in names
result = await client.get_prompt("wf.docs.operator_guide")
content = result.messages[0].content
assert isinstance(content, mcp_types.TextContent)
assert "wf://docs/operator-manual" in content.text
asyncio.run(run_proxy())
def test_admin_tools_have_human_metadata() -> None: def test_admin_tools_have_human_metadata() -> None:
config = BrokerConfig( config = BrokerConfig(
store_root=local_temp_root() / "unified_admin_metadata_store", store_root=local_temp_root() / "unified_admin_metadata_store",