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
|
```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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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."
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 .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
@@ -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.",
|
||||||
|
|||||||
@@ -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."
|
||||||
|
)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user