better vscode integration

This commit is contained in:
John Lancaster
2026-08-08 00:05:58 -05:00
parent a157489634
commit 9be7c27410
7 changed files with 120 additions and 17 deletions
+42 -11
View File
@@ -16,7 +16,32 @@ Copilot interacts with MCP servers through independently exposed lanes:
2. resources attached as read-only context 2. resources attached as read-only context
3. server-provided prompts 3. server-provided prompts
This server publishes skills as native `skill://` resources and prompts as native MCP prompt objects. This server publishes skills as native `skill://` resources and prompts as native MCP prompt objects. It also exposes generic `list_resources` and `read_resource` tools for agents whose tool catalog does not include direct MCP resource operations.
## VS Code Feature Coverage
The server uses every FastMCP feature that applies to its read-only guidance workload:
| Feature | Usage |
| --- | --- |
| Server identity | The initialize response includes a stable name, usage instructions, and a self-contained icon for VS Code's MCP server UI. |
| Tools | Compatibility tools have display titles, structured output schemas, and read-only, idempotent, closed-world annotations. FastMCP's default schema dereferencing remains enabled for clients such as VS Code that require flat schemas. |
| Resources | Documentation and skills use native resources and wildcard resource templates with explicit Markdown MIME types. |
| Prompts | Declarative workflows use native prompt objects with descriptions, display titles, typed arguments, and slash-command access. |
| Argument completion | Prompt arguments with authored `choices` are returned through `completion/complete` as the user types. |
[FastMCP server identity](https://gofastmcp.com/servers/server), [component icons](https://gofastmcp.com/servers/icons), [tool metadata](https://gofastmcp.com/servers/tools), and [argument completion](https://gofastmcp.com/servers/completions) define the implementation details. [VS Code's MCP documentation](https://code.visualstudio.com/docs/agent-customization/mcp-servers) describes how tools, resources, prompts, and MCP Apps appear in the client.
The following capabilities are conditional rather than useful by default:
1. MCP Apps require an interactive tool result such as a form or visualization; this server returns guidance and structured resource data only.
2. Sampling is appropriate only when server-side work must ask VS Code to run an LLM. The current server retrieves authored content and does not generate it.
3. Elicitation is appropriate only when a running operation needs additional user input. Prompt arguments already collect all required input before execution.
4. Progress, client logging, and background tasks require long-running operations. Current reads and prompt rendering are bounded local operations.
5. Client roots matter only when server behavior depends on client filesystem roots. This server reads packaged content and never traverses a client workspace.
6. `website_url` requires a canonical public deployment URL. None is configured, so the server does not advertise a guessed address.
Add one of these capabilities when a concrete workflow needs it, then cover its negotiated capability and protocol response in the HTTP MCP smoke tests. See the [FastMCP Apps overview](https://gofastmcp.com/apps/overview), [sampling](https://gofastmcp.com/servers/sampling), [elicitation](https://gofastmcp.com/servers/elicitation), [progress reporting](https://gofastmcp.com/servers/progress), and [MCP context](https://gofastmcp.com/servers/context) for the activation criteria.
## Native Skill Resources ## Native Skill Resources
@@ -26,7 +51,7 @@ For every skill, Copilot can discover:
2. `skill://<name>/_manifest` 2. `skill://<name>/_manifest`
3. `skill://<name>/{path*}` supporting-file template 3. `skill://<name>/{path*}` supporting-file template
The main resource description comes from `SKILL.md`. The manifest discloses supporting paths, sizes, and SHA256 hashes. This is the only skill discovery contract; there is no parallel skill catalog. The main resource description comes from `SKILL.md`. The manifest discloses supporting paths, sizes, and SHA256 hashes. Native resources remain the only skill content and discovery contract; the generic tools delegate to that same resource surface rather than maintaining a parallel catalog.
## Resource Picker Availability ## Resource Picker Availability
@@ -39,10 +64,15 @@ A successful `resources/list` response does not guarantee the picker appears in
## Recommended Workflow ## Recommended Workflow
1. browse the server's resources For autonomous agents:
2. attach one relevant `skill://<name>/SKILL.md`
3. attach `_manifest` only if supporting detail may be needed 1. call `list_resources`
4. attach only selected supporting files 2. compare main skill names and descriptions
3. call `read_resource` for one relevant `skill://<name>/SKILL.md`
4. read `_manifest` only if supporting detail may be needed
5. read only selected supporting files
For manual context attachment, browse the server's resources and attach the same bounded set of files.
## Prompt Examples ## Prompt Examples
@@ -72,13 +102,13 @@ A repo-level instruction should name the native retrieval order and context budg
When a task matches a personal-mcp skill: When a task matches a personal-mcp skill:
1. Prefer an already attached native skill resource. 1. Prefer an already attached native skill resource.
2. Otherwise browse MCP resources and select one `skill://<name>/SKILL.md` resource by description. 2. Otherwise call `list_resources` and select one `skill://<name>/SKILL.md` resource by description.
3. Read `_manifest` only when supporting material is needed. 3. Call `read_resource` for the selected skill and read `_manifest` only when supporting material is needed.
4. Load at most two candidate main files and only the relevant supporting paths. 4. Load at most two candidate main files and only the relevant supporting paths.
5. Reconcile guidance with the current repository before editing. 5. Reconcile guidance with the current repository before editing.
``` ```
Instructions steer behavior but do not force VS Code to attach resources automatically. Instructions steer behavior but do not force VS Code to attach resources automatically. The generic tools provide an agent-callable fallback when direct resource operations are absent from the deferred-tool catalog.
## Prompt Objects ## Prompt Objects
@@ -88,8 +118,9 @@ Prompts remain separate from skills. When the client supports MCP prompt APIs, u
1. Use `MCP: List Servers` to confirm the server is enabled. 1. Use `MCP: List Servers` to confirm the server is enabled.
2. Use `MCP: Browse Resources` to confirm native skill resources exist. 2. Use `MCP: Browse Resources` to confirm native skill resources exist.
3. Restart the MCP server after changing skill files because production uses `reload=False`. 3. Confirm `list_resources` and `read_resource` appear in the chat tool picker when autonomous retrieval is required.
4. Reload the VS Code window if the server is healthy but the resource picker remains stale. 4. Restart the MCP server after changing skill files because production uses `reload=False`.
5. Reload the VS Code window if the server is healthy but the resource or tool picker remains stale.
## Further Reading ## Further Reading
+1 -1
View File
@@ -25,7 +25,7 @@ uv sync
Run the app locally with the static docs rebuilt first, using [Uvicorn factory mode](https://www.uvicorn.org/settings/#application): Run the app locally with the static docs rebuilt first, using [Uvicorn factory mode](https://www.uvicorn.org/settings/#application):
```bash ```bash
uv run zensical build && uv run uvicorn personal_mcp.main:create_app --factory --host 127.0.0.1 --port 8765 uv run zensical build && uv run uvicorn personal_mcp.web.app:create_app --factory --host 127.0.0.1 --port 8765
``` ```
Build and run the Docker image with the same exposed port: Build and run the Docker image with the same exposed port:
@@ -7,6 +7,8 @@ Use this page for MCP client setup, operational tools, and integration reference
!!! info "VS Code MCP docs" !!! info "VS Code MCP docs"
- [VS Code MCP servers overview](https://code.visualstudio.com/docs/agent-customization/mcp-servers) - [VS Code MCP servers overview](https://code.visualstudio.com/docs/agent-customization/mcp-servers)
- [VS Code MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration) - [VS Code MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration)
- [VS Code MCP developer guide](https://code.visualstudio.com/docs/agents/guides/mcp-developer-guide)
- [VS Code MCP Apps support](https://code.visualstudio.com/blogs/2026/01/26/mcp-apps-support)
- [VS Code Copilot customization overview](https://code.visualstudio.com/docs/copilot/customization/overview) - [VS Code Copilot customization overview](https://code.visualstudio.com/docs/copilot/customization/overview)
## Debugging and Inspection ## Debugging and Inspection
@@ -13,6 +13,13 @@ Use this page for implementation-oriented links across MCP SDKs and FastMCP.
!!! info "FastMCP sources" !!! info "FastMCP sources"
- [FastMCP project documentation](https://gofastmcp.com/) - [FastMCP project documentation](https://gofastmcp.com/)
- [FastMCP server identity and behavior](https://gofastmcp.com/servers/server)
- [FastMCP tools and annotations](https://gofastmcp.com/servers/tools)
- [FastMCP resources and templates](https://gofastmcp.com/servers/resources)
- [FastMCP prompts](https://gofastmcp.com/servers/prompts)
- [FastMCP argument completion](https://gofastmcp.com/servers/completions)
- [FastMCP component icons](https://gofastmcp.com/servers/icons)
- [FastMCP Apps](https://gofastmcp.com/apps/overview)
- [FastMCP GitHub repository](https://github.com/jlowin/fastmcp) - [FastMCP GitHub repository](https://github.com/jlowin/fastmcp)
- [FastMCP examples directory](https://github.com/jlowin/fastmcp/tree/main/examples) - [FastMCP examples directory](https://github.com/jlowin/fastmcp/tree/main/examples)
- [FastMCP PyPI package](https://pypi.org/project/fastmcp/) - [FastMCP PyPI package](https://pypi.org/project/fastmcp/)
+4 -3
View File
@@ -53,14 +53,15 @@ These utilities operate directly on the native `skill://` contract and require n
In VS Code, skills can arrive through: In VS Code, skills can arrive through:
1. explicit attachment from `Add Context > MCP Resources` or `MCP: Browse Resources` 1. explicit attachment from `Add Context > MCP Resources` or `MCP: Browse Resources`
2. a slash-command prompt that names a specific native skill URI 2. the generic `list_resources` and `read_resource` tools for autonomous agents
3. a slash-command prompt that names a specific native skill URI
Instructions can steer retrieval, but they do not guarantee automatic resource attachment in every chat surface. Instructions can steer retrieval, but they do not guarantee automatic resource attachment in every chat surface. When the deferred-tool catalog omits direct MCP resource operations, use the generic tools; they delegate to the native providers and do not duplicate skill metadata or content.
A reliable prompt is: A reliable prompt is:
```text ```text
Browse personal-mcp resources and select the best matching skill://.../SKILL.md resource. Read one selected skill, inspect its _manifest only if supporting detail is needed, and reconcile the guidance with this workspace. Call list_resources and select the best matching skill://.../SKILL.md resource. Use read_resource for one selected skill, inspect its _manifest only if supporting detail is needed, and reconcile the guidance with this workspace.
``` ```
## Thin Shim Pattern ## Thin Shim Pattern
+63 -2
View File
@@ -1,24 +1,53 @@
from __future__ import annotations from __future__ import annotations
from fastmcp import FastMCP from fastmcp import FastMCP
from mcp.types import CompletionArgument
from mcp.types import CompletionContext
from mcp.types import Icon
from mcp.types import PromptReference
from personal_mcp.prompts import create_prompts_provider from personal_mcp.prompts import create_prompts_provider
from personal_mcp.prompts.models import MarkdownPrompt
from personal_mcp.prompts.provider import MarkdownPromptsProvider
from personal_mcp.registry.load import get_docs_registry from personal_mcp.registry.load import get_docs_registry
from personal_mcp.registry.load import read_docs_markdown_path from personal_mcp.registry.load import read_docs_markdown_path
from personal_mcp.registry.models import DocsRegistry from personal_mcp.registry.models import DocsRegistry
from personal_mcp.skills import create_skills_provider from personal_mcp.skills import create_skills_provider
_SERVER_INSTRUCTIONS = """Personal development guidance exposed as native MCP resources and prompts.
Use prompts for parameterized workflows. For task-specific guidance, list resources, select one
skill://<name>/SKILL.md by description, and read its manifest only when supporting detail is needed.
Read-only compatibility tools expose the same resource catalog to clients without native resource access.
"""
_SERVER_ICON = Icon(
src=(
"data:image/svg+xml;base64,"
"PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NCA2NCI+"
"PHJlY3Qgd2lkdGg9IjY0IiBoZWlnaHQ9IjY0IiByeD0iOCIgZmlsbD0iIzE2N0Q4RCIvPjxwYXRoIGQ9"
"Ik0xOCAxNmgyMGw4IDh2MjRIMTh6IiBmaWxsPSJub25lIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lk"
"dGg9IjQiLz48cGF0aCBkPSJNMzggMTZ2MTBoOE0yNSAzM2gxNE0yNSA0MGgxNCIgZmlsbD0ibm9uZSIg"
"c3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSI0Ii8+PC9zdmc+"
),
mime_type="image/svg+xml",
sizes=["64x64"],
)
def _ro_annotations() -> dict[str, bool]: def _ro_annotations() -> dict[str, bool]:
return { return {
"readOnlyHint": True, "readOnlyHint": True,
"idempotentHint": True, "idempotentHint": True,
"openWorldHint": False,
} }
def _register_components(mcp: FastMCP, registry: DocsRegistry) -> None: def _register_components(mcp: FastMCP, registry: DocsRegistry) -> None:
@mcp.resource( @mcp.resource(
"resource://docs/{path*}", "resource://docs/{path*}",
name="docs_markdown",
title="Documentation Markdown",
description="Read a packaged documentation page by its path relative to the docs root.",
mime_type="text/markdown", mime_type="text/markdown",
tags={"docs"}, tags={"docs"},
annotations=_ro_annotations(), annotations=_ro_annotations(),
@@ -30,6 +59,7 @@ def _register_components(mcp: FastMCP, registry: DocsRegistry) -> None:
def _register_resource_tools(mcp: FastMCP) -> None: def _register_resource_tools(mcp: FastMCP) -> None:
@mcp.tool( @mcp.tool(
name="list_resources", name="list_resources",
title="List Resources",
description=( description=(
"List available MCP resources and URI templates. Use before read_resource to discover skill guidance." "List available MCP resources and URI templates. Use before read_resource to discover skill guidance."
), ),
@@ -61,6 +91,7 @@ def _register_resource_tools(mcp: FastMCP) -> None:
@mcp.tool( @mcp.tool(
name="read_resource", name="read_resource",
title="Read Resource",
description="Read a resource URI returned by list_resources, including skill files, manifests, and references.", description="Read a resource URI returned by list_resources, including skill files, manifests, and references.",
annotations=_ro_annotations(), annotations=_ro_annotations(),
) )
@@ -69,11 +100,41 @@ def _register_resource_tools(mcp: FastMCP) -> None:
return result.model_dump(mode="json", exclude_none=True) return result.model_dump(mode="json", exclude_none=True)
def _register_prompt_completions(mcp: FastMCP, provider: MarkdownPromptsProvider) -> None:
@mcp.completion
async def complete_prompt_argument(
ref: object,
argument: CompletionArgument,
context: CompletionContext | None,
) -> list[str] | None:
del context
if not isinstance(ref, PromptReference):
return None
prompt = await provider.get_prompt(ref.name)
if not isinstance(prompt, MarkdownPrompt):
return None
definition = prompt.definitions.get(argument.name)
if definition is None or definition.choices is None:
return None
prefix = argument.value.casefold()
return [choice for choice in definition.choices if choice.casefold().startswith(prefix)]
def create_mcp() -> FastMCP: def create_mcp() -> FastMCP:
registry = get_docs_registry() registry = get_docs_registry()
mcp = FastMCP("personal-mcp", on_duplicate="error") mcp = FastMCP(
"personal-mcp",
instructions=_SERVER_INSTRUCTIONS,
icons=[_SERVER_ICON],
on_duplicate="error",
)
_register_components(mcp, registry) _register_components(mcp, registry)
mcp.add_provider(create_prompts_provider()) prompts_provider = create_prompts_provider()
mcp.add_provider(prompts_provider)
mcp.add_provider(create_skills_provider()) mcp.add_provider(create_skills_provider())
_register_resource_tools(mcp) _register_resource_tools(mcp)
_register_prompt_completions(mcp, prompts_provider)
return mcp return mcp
+1
View File
@@ -65,6 +65,7 @@ class MarkdownPrompt(Prompt):
metadata = definition.metadata metadata = definition.metadata
return cls( return cls(
name=definition.prompt_id, name=definition.prompt_id,
title=definition.prompt_id.replace("-", " ").title(),
version=metadata.version, version=metadata.version,
description=metadata.description, description=metadata.description,
tags=set(metadata.tags), tags=set(metadata.tags),