more prompts for the LLM user
This commit is contained in:
@@ -20,7 +20,7 @@ connections into that model rather than owning the model itself.
|
||||
|
||||
```text
|
||||
CapabilitySource
|
||||
id: "wf.std" | "wf.mcp" | "wf.admin" | "<server>.<account>"
|
||||
id: "wf.std" | "wf.docs" | "wf.mcp" | "wf.admin" | "<server>.<account>"
|
||||
kind: "system" | "connection"
|
||||
enabled: bool
|
||||
visibility:
|
||||
@@ -49,6 +49,27 @@ future saved wrapper artifacts, see
|
||||
|
||||
## 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`
|
||||
|
||||
Workflow standard library.
|
||||
|
||||
@@ -27,7 +27,7 @@ with each other just because MCP transports them all.
|
||||
| Noun | Meaning | Example |
|
||||
| --- | --- | --- |
|
||||
| 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` |
|
||||
| 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` |
|
||||
@@ -92,6 +92,26 @@ Typical tools:
|
||||
- `wf.workflow.validate_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
|
||||
|
||||
These are the upstream MCP tools themselves, projected under connection/source
|
||||
@@ -257,6 +277,7 @@ connection.
|
||||
Examples:
|
||||
|
||||
- `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.admin`: a privileged local source, not a connection
|
||||
|
||||
|
||||
@@ -2,7 +2,12 @@ from __future__ import annotations
|
||||
|
||||
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:
|
||||
@@ -31,7 +36,42 @@ def build_local_documentation_source(repo_root: Path) -> CapabilitySource:
|
||||
title="wf_mcp Troubleshooting",
|
||||
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."
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ from ..documentation import build_local_documentation_source
|
||||
from ..models import BrokerConfig
|
||||
from ..transparent_proxy.runtime import ProxyRuntime
|
||||
from ..workflow_surface import register_workflow_tools
|
||||
from .prompts import register_documentation_prompts
|
||||
from .resources import register_documentation_resources
|
||||
|
||||
|
||||
@@ -46,6 +47,7 @@ def create_server(
|
||||
register_workflow_tools(runtime.server, service)
|
||||
docs_source = build_local_documentation_source(_repo_root())
|
||||
service.capability_sources[docs_source.id] = docs_source
|
||||
register_documentation_prompts(runtime.server, docs_source)
|
||||
register_documentation_resources(runtime.server, docs_source)
|
||||
return runtime.server
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 .paging import Page, page_items
|
||||
from .schema_hashes import hash_json_schema
|
||||
@@ -21,6 +21,7 @@ __all__ = [
|
||||
"CapabilityBuckets",
|
||||
"CapabilitySource",
|
||||
"CapabilityRef",
|
||||
"DocumentationPrompt",
|
||||
"DocumentationResource",
|
||||
"NodeSpecInventory",
|
||||
"Page",
|
||||
|
||||
+14
-1
@@ -17,15 +17,28 @@ class DocumentationResource:
|
||||
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(
|
||||
resources: list[DocumentationResource],
|
||||
*,
|
||||
prompts: list[DocumentationPrompt] | None = None,
|
||||
) -> CapabilitySource:
|
||||
"""Build the local documentation source without depending on MCP transport."""
|
||||
return CapabilitySource(
|
||||
id="wf.docs",
|
||||
kind="system",
|
||||
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),
|
||||
description="Local operator and workflow documentation.",
|
||||
|
||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
from wf_platform import (
|
||||
CapabilityBuckets,
|
||||
CapabilitySource,
|
||||
DocumentationPrompt,
|
||||
DocumentationResource,
|
||||
SourceInventory,
|
||||
SourcePermissions,
|
||||
@@ -66,7 +67,15 @@ def test_documentation_source_owns_provider_neutral_resources() -> None:
|
||||
mime_type="text/markdown",
|
||||
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"]
|
||||
@@ -75,3 +84,6 @@ def test_documentation_source_owns_provider_neutral_resources() -> None:
|
||||
assert source.visibility.mcp_client is True
|
||||
assert resource.uri == "wf://docs/operator-manual"
|
||||
assert resource.text == "# Operator Manual"
|
||||
assert source.capabilities.prompts["wf.docs.operator_guide"].text == (
|
||||
"Read wf://docs/operator-manual first."
|
||||
)
|
||||
|
||||
@@ -82,6 +82,7 @@ def test_server_exposes_upstream_admin_and_workflow_tools() -> None:
|
||||
for source in _structured(sources_result)["sources"]
|
||||
}
|
||||
assert "wf.admin" in source_ids
|
||||
assert "wf.docs" in source_ids
|
||||
assert "wf.mcp" 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())
|
||||
|
||||
|
||||
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:
|
||||
config = BrokerConfig(
|
||||
store_root=local_temp_root() / "unified_admin_metadata_store",
|
||||
|
||||
Reference in New Issue
Block a user