Compare commits

...
2 Commits
Author SHA1 Message Date
John Lancaster 9eb4ccbc6e doc updates 2026-08-30 11:49:58 -05:00
John Lancaster bbaa84720c prompts and resources as tools 2026-08-30 11:42:26 -05:00
11 changed files with 225 additions and 655 deletions
+22 -84
View File
@@ -4,102 +4,40 @@ icon: lucide/library
# Architecture # Architecture
## Overview Personal MCP is a small publishing service. Markdown is written once and made available in two ways:
The application combines a FastMCP server with a pre-built Zensical documentation site. Markdown under `src/personal_mcp/docs/` is the single authored content tree, while native FastMCP providers own skill and prompt discovery. 1. as an MCP server for AI clients
2. as a documentation website for people
The runtime has four content paths: ## How It Fits Together
1. `SkillsDirectoryProvider` publishes native `skill://` resources from packaged skill directories.
2. A custom prompt provider loads declarative prompt definitions from packaged Markdown.
3. The general docs registry publishes non-skill Markdown through `resource://docs/{path*}`.
4. FastAPI serves the pre-built `site/` directory.
There is no custom skill catalog, prompt catalog, or per-prompt Python module.
## Source Ownership
### Skills
Each skill owns one directory:
1. `src/personal_mcp/docs/skills/<skill-id>/SKILL.md`
2. `src/personal_mcp/docs/skills/<skill-id>/<supporting-path>`
`SkillsDirectoryProvider` publishes:
1. `skill://<name>/SKILL.md`
2. `skill://<name>/_manifest`
3. `skill://<name>/{path*}`
The provider parses standard skill frontmatter and generates the manifest. The general docs registry excludes `skills/**`, so only the native provider owns this namespace.
### Prompts
Each prompt has one source: `src/personal_mcp/docs/prompts/<prompt-id>/PROMPT.md`. Its nested `prompt` frontmatter owns runtime metadata and argument declarations, while its body owns canonical prose.
The custom provider reads packaged Markdown with `importlib.resources`, validates metadata and exact placeholder-to-argument equality, and creates native FastMCP prompt objects. It rescans on each list and get request, so an editable deployment observes file additions, edits, and deletions without a restart.
FastMCP exposes prompts through native `prompts/list` and `prompts/get` operations.
### General Docs
The docs registry indexes packaged Markdown for `resource://docs/{path*}`. It rejects `skills/**` because skills are provider-owned. Prompt Markdown can remain visible as general documentation, but prompt invocation is owned by the native prompt provider.
## Runtime Composition
```mermaid ```mermaid
flowchart TD flowchart LR
A[Packaged Skill Directories] --> B[SkillsDirectoryProvider] A[Markdown in src/personal_mcp/docs] --> B[MCP resources and prompts]
C[Packaged Prompt Markdown] --> D[Markdown Prompt Provider] A --> C[Documentation website]
F[General Markdown] --> G[Docs Registry] B --> D[AI clients]
B --> H[FastMCP Server] C --> E[Human readers]
D --> H
G --> H
H --> K[MCP Transport]
L[Zensical Site Output] --> M[FastAPI Static Mount]
K --> M
``` ```
Server construction is lazy with respect to package import. Each application process creates its providers and docs snapshot when the server factory runs. Skills use startup discovery, while prompts are reloaded when a client lists or gets prompts. The running application has two routes:
## Packaging - `/mcp` is the MCP endpoint.
- `/docs/` is the pre-built documentation site.
The regular directory `src/personal_mcp/docs/` is the only authored Markdown source. The `uv_build` backend includes it as package data beneath `personal_mcp/docs/` in built distributions. The root URL redirects to the website.
Runtime reads are package-relative: ## Content Types
1. Prompt content and general docs use `importlib.resources` and `Traversable` APIs. The server publishes three kinds of Markdown content:
2. `SkillsDirectoryProvider` receives the packaged `personal_mcp/docs/skills` filesystem path.
3. No runtime content lookup depends on the current working directory.
## Public Contracts - **Skills** are reusable guidance that clients read as `skill://` resources.
- **Prompts** are parameterized workflows that clients invoke as MCP prompts.
- **Documentation** is available to clients through `resource://docs/...` and to people on the website.
The machine-facing surfaces are: FastMCP provides the MCP behavior. FastAPI hosts that server beside the static site, and Zensical builds the site from the same Markdown files.
1. Native skill resources under `skill://<name>/...`. ## Source Of Truth
2. Native MCP prompt list and get operations.
3. `resource://docs/{path*}` for general Markdown.
Canonical contracts are documented in: All authored content lives under `src/personal_mcp/docs/`. The generated `src/personal_mcp/site/` directory is build output and should not be edited by hand.
1. [Prompt Contract](./contracts/prompt.md) For exact file formats and URI rules, see the [content contracts](./contracts/index.md). For everyday changes, start with the [Authoring Guide](./authoring.md).
2. [Skill Contract](./contracts/skill_contract.md)
3. [Frontmatter Contract](./contracts/frontmatter.md)
4. [URI Contract](./contracts/uris.md)
Only these canonical provider and protocol surfaces are registered.
## Static Documentation
Zensical builds `src/personal_mcp/docs/` into `src/personal_mcp/site/` before deployment. FastAPI mounts that immutable output in the same process that hosts FastMCP. Generated site files are deployment assets and are never an authored source.
## Validation
Changes are accepted only after:
1. focused provider and protocol tests
2. Ruff and ty checks
3. a Zensical build
4. the full pytest suite
5. an installed-wheel smoke test when packaging or provider paths change
+19 -90
View File
@@ -4,112 +4,48 @@ icon: lucide/pencil
# Authoring Guide # Authoring Guide
This page defines the practical workflow for maintaining skills, prompts, and project documentation in the package-native `src/personal_mcp/docs` source tree. All authored content lives under `src/personal_mcp/docs/`. The same files feed the MCP server and the documentation website.
Primary references:
1. [Skill Contract](./contracts/skill_contract.md)
2. [Prompt Contract](./contracts/prompt.md)
3. [Frontmatter Contract](./contracts/frontmatter.md)
4. [URI Contract](./contracts/uris.md)
5. [Zensical documentation skill](./skills/zensical-docs/SKILL.md)
## Source Tree Ownership ## Source Tree Ownership
Edit content only under `src/personal_mcp/docs`. This directory is the canonical authored source for both MCP content and the documentation site.
The `uv_build` backend packages this tree under `personal_mcp/docs/`. The installed package therefore gives runtime providers package-relative content, while Zensical builds the human site directly from `src/personal_mcp/docs` as configured by `docs_dir` in the repository's `zensical.toml`.
Generated `src/personal_mcp/site/` content is a build artifact and must not be edited by hand.
## Content Layout
```text ```text
src/personal_mcp/docs/ src/personal_mcp/docs/
*.md *.md # General documentation
contracts/
prompts/<prompt-id>/ prompts/<prompt-id>/
PROMPT.md PROMPT.md # One MCP prompt
references/
skills/<skill-name>/ skills/<skill-name>/
SKILL.md SKILL.md # Main skill guidance
references/ references/ # Optional supporting material
``` ```
Keep skill and prompt files inside their owning directories. Relative links may cross sections, but content ownership should remain clear. Do not edit `src/personal_mcp/site/` by hand. It is generated by Zensical.
## Documentation Pages
Add general documentation as Markdown under `src/personal_mcp/docs/`. Use relative links between pages. Top-level pages need an `icon` in their frontmatter, and navigation changes belong in `zensical.toml`.
## Skill Authoring ## Skill Authoring
A skill is discovered when a direct child of `src/personal_mcp/docs/skills/` contains `SKILL.md`. A skill is a directory containing `SKILL.md`:
Required frontmatter:
```yaml ```yaml
--- ---
name: <skill-name> name: <skill-name>
description: <what the skill does and when to use it> description: <what this skill covers and when to use it>
--- ---
``` ```
Rules: Use lowercase kebab-case, and keep the directory name and frontmatter `name` identical. Put optional references beneath the same directory and link to them from `SKILL.md` when readers need to know they exist.
1. Use lowercase kebab-case for the directory and `name`.
2. Keep `name` exactly equal to the directory name.
3. Write a specific description because clients use it for discovery.
4. Do not add `x-personal-mcp`, versions, tags, capabilities, or reference mappings.
5. Put supporting material anywhere beneath the skill directory, normally under `references/`.
6. Link supporting files from `SKILL.md` so humans and agents understand when to load them.
FastMCP recursively scans every skill file and generates `skill://<name>/_manifest`. Supporting-resource identity is the real relative path, not a synthetic reference id.
Recommended sequence:
1. Draft or revise `SKILL.md` routing guidance.
2. Add focused supporting files.
3. Verify relative links.
4. Run the provider tests and docs build.
5. Restart running servers because production uses `reload=False`.
## Prompt Authoring ## Prompt Authoring
A prompt is one self-describing `src/personal_mcp/docs/prompts/<prompt-id>/PROMPT.md` file: A prompt is one `PROMPT.md` file under `src/personal_mcp/docs/prompts/<prompt-id>/`. Its `prompt` frontmatter describes the workflow and arguments; its Markdown body contains the instructions.
1. Create a lowercase kebab-case directory beneath `src/personal_mcp/docs/prompts/`. Use each declared argument as a `{{placeholder}}` in the body. The server validates prompt metadata and placeholders when the prompt is discovered.
2. Add a nested `prompt` frontmatter mapping with version, description, tags, and ordered arguments.
3. Give every argument a description and explicit required flag.
4. Add `choices` only when a string argument accepts a fixed set of values.
5. Use each argument exactly once or more as a `{{argument_name}}` placeholder in the body.
6. Do not add a Python component, name field, metadata sidecar, or central catalog entry.
The custom provider rescans prompt documents during every native list and get request. Changes in an editable checkout are therefore visible on the next request without a process restart. Invalid metadata or placeholder drift fails that request with a configuration error. The [Prompt Contract](./contracts/prompt.md) and [Frontmatter Contract](./contracts/frontmatter.md) contain the exact schema.
## Frontmatter Safety ## Validate Changes
1. Quote scalar values containing `:`.
2. Quote values with reserved YAML characters such as `#`, `{}`, `[]`, or leading `*`.
3. Use block scalars for punctuation-heavy multiline text.
4. Keep fields within the applicable skill or documentation contract.
## Writing Quality
1. Prefer focused sections and descriptive headings.
2. Link feature-level claims to authoritative sources.
3. Use relative links for internal pages.
4. Keep code examples minimal and actionable.
5. Avoid bare URLs in prose.
6. Load only supporting material relevant to the immediate task.
## Copilot Routing
Active instructions should point directly to native main resources:
1. `skill://zensical-docs/SKILL.md`
2. `skill://pytesting/SKILL.md`
3. `skill://vscode-configuration/SKILL.md`
When deeper guidance is needed, read the selected skill's `_manifest` and fetch supporting files by their listed path.
## Validation Checklist
```bash ```bash
uv run zensical build uv run zensical build
@@ -118,13 +54,6 @@ uv run ty check
uv run pytest uv run pytest
``` ```
For packaging changes, also build and inspect an installed wheel so provider path resolution is verified outside the editable checkout. Restart a running server after changing content so every surface reads the latest package data.
## Navigation For detailed rules, see the [content contracts](./contracts/index.md). For writing and site features, use the [Zensical documentation skill](./skills/zensical-docs/SKILL.md).
When adding or moving pages:
1. update `zensical.toml`
2. keep top-level page icons in frontmatter
3. rebuild the site
4. verify internal links and navigation labels
+28 -101
View File
@@ -2,129 +2,56 @@
icon: lucide/bot icon: lucide/bot
--- ---
# Copilot MCP Mechanics # Using With GitHub Copilot
## Purpose Once Personal MCP is configured as a VS Code MCP server, Copilot can use its resources, prompts, and read-only tools.
This page explains how GitHub Copilot in VS Code consumes native skill resources and prompts from `personal-mcp`. For general connection details, see [Using Personal MCP](./usage.md). For VS Code setup options, see [Add and manage MCP servers](https://code.visualstudio.com/docs/agent-customization/mcp-servers).
## Capability Lanes ## Skills And Documentation
Copilot interacts with MCP servers through independently exposed lanes: Use **MCP: Browse Resources** to inspect the server's resources. Skills appear as `skill://<name>/SKILL.md`; general pages appear under `resource://docs/...`.
1. tools invoked during execution For a task that needs guidance:
2. resources attached as read-only context
3. server-provided prompts
This server publishes skills as native `skill://` resources, general docs as `resource://docs/{path*}` resources, and workflows as native MCP prompt objects. It intentionally publishes no compatibility tools that mirror resources or prompts. 1. choose the skill whose description best matches the task
2. read its main `SKILL.md`
3. read `_manifest` only when supporting material is needed
4. attach or read only the relevant supporting files
## VS Code Feature Coverage When the current chat surface supports MCP resource attachments, the same resources are available from **Add Context**.
The server uses every FastMCP feature that applies to its read-only guidance workload: ## Prompts
| Feature | Usage | Personal MCP prompts appear as `/<server>.<prompt>` chat commands. Select a command and fill in its arguments to start the workflow. Arguments with a fixed list of choices offer completion as you type.
| --- | --- |
| Server identity | The initialize response includes a stable name, usage instructions, and a self-contained icon for VS Code's MCP server UI. |
| Tools | No tools are published for this documentation-only server surface. Resource and prompt operations stay on native MCP capabilities. |
| 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. ## Automatic Use
The following capabilities are conditional rather than useful by default: Copilot can use four fallback tools when the chat surface does not expose resources or prompts directly:
1. MCP Apps require an interactive tool result such as a form or visualization; this server returns guidance and structured resource data only. - `list_resources` and `read_resource`
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. - `list_prompts` and `get_prompt`
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. These tools access the same content as the native features. A repository instruction can guide Copilot toward the intended order:
## Native Skill Resources
For every skill, Copilot can discover:
1. `skill://<name>/SKILL.md`
2. `skill://<name>/_manifest`
3. `skill://<name>/{path*}` supporting-file template
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 tools search or delegate to that same resource surface rather than maintaining a parallel catalog.
## Resource Picker Availability
`MCP Resources...` in Add Context requires both:
1. a connected server advertising resource capability
2. a chat surface that exposes MCP resource attachment
A successful `resources/list` response does not guarantee the picker appears in every session type. Use `MCP: Browse Resources` to distinguish server availability from chat UI availability.
## Recommended Workflow
For autonomous agents:
1. browse native resources and compare skill descriptions
2. read one relevant `skill://<name>/SKILL.md`
3. read `_manifest` only if supporting detail may be needed
4. read only selected supporting files
For manual context attachment, browse the server's resources and attach the same bounded set of files.
## Prompt Examples
Resource attachment:
```text ```text
Use the attached personal-mcp skill as guidance, then reconcile it with the repository before proposing changes.
```
Direct loading:
```text
Read skill://async-fastapi-sqlmodel/SKILL.md and apply only the sections relevant to this repository.
```
Supporting material:
```text
Read skill://pytesting/_manifest, select the one reference relevant to async test lifecycle, and use that file with the main skill instructions.
```
## Repository Instruction Pattern
A repo-level instruction should name the native retrieval order and context budget:
```md
When a task matches a personal-mcp skill: When a task matches a personal-mcp skill:
1. Prefer an attached skill resource, or browse resources and choose one by description.
1. Prefer an already attached native skill resource. 2. Read its main file and load supporting material only when needed.
2. Otherwise browse MCP resources and select one `skill://<name>/SKILL.md` by description. 3. Reconcile the guidance with the current repository before editing.
3. Read 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.
5. Reconcile guidance with the current repository before editing.
``` ```
Instructions steer behavior but do not force VS Code to attach resources automatically. Instructions guide resource use but do not force VS Code to attach resources automatically.
## Prompt Objects
Prompts remain separate from skills. When the client supports MCP prompt APIs, use prompt listing and `get_prompt` for parameterized workflows. Each authored `PROMPT.md` is the complete source of truth for its metadata, arguments, and prose; changes are loaded on the next prompt request.
## Troubleshooting ## Troubleshooting
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 resources are available.
3. Confirm `Add Context > MCP Resources` lists server resources in the active chat surface. 3. Restart the MCP server after changing its content.
4. Restart the MCP server after changing skill files because production uses `reload=False`. 4. Reload the VS Code window if the server is healthy but the resource or tool list remains stale.
5. Reload the VS Code window if the server is healthy but the resource or tool picker remains stale.
## Further Reading ## Further Reading
1. [FastMCP Skills Provider](https://gofastmcp.com/servers/providers/skills) 1. [VS Code MCP configuration](https://code.visualstudio.com/docs/agents/reference/mcp-configuration)
2. [Add and manage MCP servers](https://code.visualstudio.com/docs/agent-customization/mcp-servers) 2. [Managing context in VS Code](https://code.visualstudio.com/docs/chat/copilot-chat-context)
3. [MCP configuration reference](https://code.visualstudio.com/docs/agents/reference/mcp-configuration) 3. [Using Personal MCP](./usage.md)
4. [Manage context for AI](https://code.visualstudio.com/docs/chat/copilot-chat-context)
5. [Skill Usage Mechanics](./usage.md)
+24 -23
View File
@@ -4,45 +4,46 @@ icon: lucide/rocket
# Personal MCP # Personal MCP
This project is a document library of software patterns, best practices, and structured references to external documentation. The same markdown files are published through two equivalent surfaces, so human-readable docs and MCP resources stay aligned. Personal MCP is a library of software development guidance for people and AI assistants. Content is written once in Markdown and published as both a documentation website and an MCP server.
## MCP Server ## What It Provides
An [MCP server](https://modelcontextprotocol.io/docs/getting-started/intro) at `/mcp` provides context for AI systems. The markdown files are exposed as [resources](https://modelcontextprotocol.io/docs/learn/server-concepts#resources) and are structured to be easily consumed by [MCP clients](https://modelcontextprotocol.io/docs/learn/client-concepts), such as VS Code. - **Skills** provide focused guidance for development tasks.
- **Prompts** provide reusable workflows with named inputs.
- **Documentation** makes the same material easy to browse and maintain.
## Docs The HTTP service exposes the [MCP](https://modelcontextprotocol.io/docs/getting-started/intro) endpoint at `/mcp` and the website at `/docs/`.
A website at `/docs` for humans to read and review. ## Quick Start
## Quick start Install dependencies, build the website, and start the server:
Install dependencies first:
```bash ```bash
uv sync uv sync
uv run zensical build
uv run personal-mcp --host 127.0.0.1 --port 8765
``` ```
Run the app locally with the static docs rebuilt first, using [Uvicorn factory mode](https://www.uvicorn.org/settings/#application): Then open `http://127.0.0.1:8765/docs/` or connect an MCP client to `http://127.0.0.1:8765/mcp`.
The MCP server can also run over standard input and output:
```bash ```bash
uv run zensical build && uv run uvicorn personal_mcp.web.app:create_app --factory --host 127.0.0.1 --port 8765 uv run mcp-stdio
``` ```
Build and run the Docker image with the same exposed port: For Docker:
```bash ```bash
docker build -t personal-mcp . && docker run --rm -p 8765:8765 personal-mcp docker compose up --build
``` ```
When the server is running, the health check is available at `/healthz` and the generated docs are available at `/docs/`. ## Read Next
## Architecture - [Using Personal MCP](./usage.md)
- [Authoring Guide](./authoring.md)
- [Resource-First Pattern Module Architecture](./architecture.md) - [Architecture](./architecture.md)
- [Contracts](./contracts/index.md) - [Running the Server](./mcp_layout.md)
- [Content Contract](./contracts/index.md#content-contract) - [Testing](./testing.md)
- [Frontmatter Contract](./contracts/frontmatter.md) - [Security](./securing.md)
- [URI Contract](./contracts/uris.md) - [Content Contracts](./contracts/index.md)
- [Static Docs Hosting Pattern](./mcp_layout.md)
- [Skill Usage Mechanics](./usage.md)
- [Copilot MCP Mechanics](./copilot.md)
+25 -74
View File
@@ -2,92 +2,43 @@
icon: lucide/server icon: lucide/server
--- ---
# Runtime And Static Docs Layout # Running The Server
## Purpose Personal MCP can run as an HTTP service or as a local stdio process.
The project serves native MCP content and a pre-built documentation site from one FastAPI process. Markdown is authored once under `src/personal_mcp/docs/`; runtime providers and Zensical consume that same package-owned tree for different purposes. ## Local HTTP Server
## Repository Layout Build the website before starting the application:
```mermaid ```bash
--- uv sync
config: uv run zensical build
treeView: uv run personal-mcp --host 127.0.0.1 --port 8765
rowIndent: 32
lineThickness: 2
---
treeView-beta
"project-root"
"src/personal_mcp"
"docs"
"prompts/<prompt-id>/PROMPT.md"
"skills/<skill-id>/SKILL.md"
"skills/<skill-id>/<supporting-files>"
"<general-pages>.md"
"site"
"static build output"
"app.py"
"mcp.py"
"skills.py"
"prompts/"
"registry/"
``` ```
Ownership rules: The server then provides:
1. `src/personal_mcp/docs/skills/` is owned exclusively by `SkillsDirectoryProvider` at runtime. - `http://127.0.0.1:8765/docs/` for the website
2. Each file under `src/personal_mcp/docs/prompts/` owns its prompt metadata, argument schema, and prose. - `http://127.0.0.1:8765/mcp` for MCP clients
3. The docs registry owns only general Markdown resources and explicitly excludes skills.
4. `src/personal_mcp/site/` is generated output.
5. The deleted custom `catalog/` package is not part of the runtime.
## Runtime Composition The host, port, log level, debug mode, and reload behavior can be set with command-line options or `PERSONAL_MCP_` environment variables.
```mermaid ## Local Stdio Server
flowchart TD
A[Packaged Skills] --> B[SkillsDirectoryProvider] For clients that manage the server process themselves:
C[Packaged Prompt Markdown] --> D[Markdown Prompt Provider]
E[Packaged Markdown] --> F[Docs Registry] ```bash
B --> G[FastMCP] uv run mcp-stdio
D --> G
F --> G
G --> H[MCP Transport]
H --> K[FastAPI Application]
L[Pre-built site] --> M[Static /docs Mount]
K --> M
``` ```
Runtime guarantees: This mode provides MCP only; it does not host the website.
1. Providers are installed before serving requests. ## Docker
2. Prompt discovery rescans authored files on each list and get request.
3. Duplicate components fail according to FastMCP's configured duplicate policy.
4. Skills and prompts use native FastMCP component surfaces.
5. General docs path parsing rejects traversal, backslashes, non-Markdown paths, and the skill namespace.
## Build And Publish Flow The included Compose configuration builds the website into the image and publishes the service on port `8765`:
1. Author prompt definitions and prose under `src/personal_mcp/docs/prompts/`. ```bash
2. Run `uv run zensical build` to produce `src/personal_mcp/site/`. docker compose up --build
3. Build the wheel, which packages the authored docs under `personal_mcp/docs/`. ```
4. Start the app and serve MCP plus the static site.
No runtime Markdown-to-HTML conversion occurs. For a remote deployment, place the service behind a reverse proxy and review the [security guidance](./securing.md).
## Machine-Facing Mapping
1. `src/personal_mcp/docs/skills/<skill-id>/SKILL.md` maps to `skill://<skill-id>/SKILL.md`.
2. Skill supporting files map to `skill://<skill-id>/<path>`.
3. Declarative prompt documents map to native MCP prompt names.
4. General `src/personal_mcp/docs/<path>.md` maps to `resource://docs/{path*}`.
The server publishes no tool projections of resources or prompts.
## Public Surface Policy
Canonical provider and protocol surfaces are the only public interfaces.
## Static Mount Expectations
The FastAPI app mounts the Zensical output, serves index and asset files, and returns a clear unavailable response when the static output is absent. The site directory is immutable for a given build and remains separate from packaged authored Markdown.
+22 -119
View File
@@ -2,138 +2,41 @@
icon: lucide/shield-check icon: lucide/shield-check
--- ---
# Securing Remote Access # Security
## Context ## Public By Design
This project exposes two related surfaces from the same runtime: The application does not implement authentication. Its current purpose is to publish read-only guidance, so everything exposed through the server must be safe to make public.
1. a static documentation site under `/docs` This includes:
2. a Streamable HTTP MCP endpoint under `/mcp`
The same Markdown content backs both surfaces. For the current project shape, the MCP server is resource-first and primarily exposes public skill and documentation text. It is not intended to expose secrets, private data, shell access, filesystem access, or tools with side effects. - the website under `/docs/`
- resources and prompts under `/mcp`
- the four read-only fallback tools
The expected deployment path is: Do not add secrets, private notes, credentials, or sensitive environment details to the authored content.
## Remote Deployment
Place the service behind a reverse proxy or tunnel rather than exposing the container directly. The edge can provide TLS, rate limiting, access logs, and optional authentication without adding those concerns to this small application.
A simple deployment is:
```text ```text
Public internet Internet -> reverse proxy or tunnel -> personal-mcp
-> Cloudflare Tunnel
-> Caddy
-> personal-mcp container
``` ```
## Decision The website and MCP endpoint can remain public while they contain only public, read-only content. Use edge authentication if access should be limited.
For the current use case, heavy application-level authentication is not required. ## When Authentication Becomes Required
The recommended posture is: Protect `/mcp` before adding any capability that can:
1. Keep the service behind Cloudflare Tunnel and Caddy.
2. Do not expose the container port directly to the public internet.
3. Treat everything exposed through MCP as publishable public documentation.
4. Add stronger authentication only if the MCP surface later includes sensitive content or tools with meaningful side effects.
This keeps the deployment simple while preserving a clear upgrade path.
## Tradeoffs
### Leaving `/mcp` Public
This is acceptable if `/mcp` exposes only the same public Markdown already available through `/docs`.
Benefits:
1. lowest operational friction
2. fewer compatibility issues with MCP clients
3. no need to implement OAuth, mTLS, JWT validation, or custom auth middleware
4. consistent with the project assumption that documentation content is public
Risks:
1. random scraping, probing, or fuzzing of a machine endpoint
2. possible bandwidth or CPU nuisance traffic
3. accidental future exposure if new tools or private resources are added
4. less control over who can use the MCP endpoint
### Protecting `/mcp` With Cloudflare Access
Cloudflare Access can add a lightweight gate using GitHub, Google, one-time PIN, or service tokens.
Benefits:
1. reduces random internet traffic
2. requires little app code
3. works well for a small trusted team
4. provides logs and centralized access control
Costs:
1. browser-based login may not work with all MCP clients
2. non-browser MCP clients may need Cloudflare Access service tokens
3. adds operational configuration for a low-sensitivity endpoint
### Using mTLS
mTLS is useful when both client and server environments are tightly controlled.
Benefits:
1. strong client identity
2. good fit for service-to-service or private infrastructure
3. can be used between Cloudflare, Caddy, and the backend if desired
Costs:
1. harder certificate provisioning and rotation
2. weaker compatibility with normal MCP clients
3. unnecessary for public documentation-only content
For this project, mTLS is not the primary recommendation.
## Practical Recommendation
Use a simple public-docs posture unless the endpoint changes.
Recommended current setup:
```text
/docs public
/mcp public or lightly protected
```
If `/mcp` remains public, add only basic operational safeguards:
1. keep Cloudflare Tunnel and Caddy in front
2. avoid publishing `8765` directly
3. enable Cloudflare or Caddy rate limiting if traffic becomes noisy
4. monitor logs for unusual request volume
5. document that MCP resources must remain safe to publish
A slightly stricter setup is also reasonable:
```text
/docs public
/mcp Cloudflare Access or service token
```
This is the best option if the team wants to reduce drive-by MCP traffic without adding auth code to the application.
## Upgrade Trigger
Add real authentication before introducing any MCP capability that can:
1. read non-public files 1. read non-public files
2. access private notes or credentials 2. access private data or credentials
3. call upstream APIs 3. call authenticated services
4. mutate data 4. mutate data
5. run commands 5. run commands
6. expose environment details 6. perform expensive work
7. perform expensive computation
At that point, prefer edge-level authentication first, such as Cloudflare Access, and consider proper OAuth 2.1 resource-server behavior only if broad public MCP client interoperability becomes a goal. At that point, choose authentication based on the clients that need to connect. Edge authentication is the simplest option for a small trusted audience; standards-based MCP authorization is more appropriate when broad client interoperability is required.
## Security Invariant
Everything exposed by the MCP server must be safe to publish publicly.
If that invariant stops being true, `/mcp` should be protected before the new capability is deployed.
+19 -71
View File
@@ -4,96 +4,44 @@ icon: lucide/flask-conical
# Testing # Testing
This page describes the current test layout and execution model for this repository. The test suite checks that the same public MCP behavior works over HTTP and stdio.
Primary guidance sources:
- [Pytest scaffolding skill](./skills/pytesting/SKILL.md)
- [Pytest docs reference](./skills/pytesting/references/pytest-docs.md)
- [FastAPI + uv + Docker skill](./skills/fastapi-uv-docker/SKILL.md)
## Goals
1. Keep local feedback fast with deterministic tests.
2. Mirror source modules with focused test groups.
3. Keep endpoint and MCP surface checks explicit.
4. Make marker usage strict and intentional.
## Current Test Layout ## Current Test Layout
Current tree:
```text ```text
tests/ tests/
__init__.py
conftest.py conftest.py
registry/ server_contract.py
test_read.py test_http.py
ingest/ test_stdio.py
test_current_docs.py
test_document.py
models/
test_document_validation.py
prompts/
test_content_renderer.py
test_filesystem_provider.py
skills/
test_provider.py
web/
conftest.py
test_endpoint_connections.py
test_mcp_prompts.py
test_mcp_skills.py
``` ```
Source-to-test alignment today: `server_contract.py` contains the shared expectations. Both transport tests verify the resources, prompts, fallback tools, and representative reads against that contract.
- `src/personal_mcp/registry/ingest/` -> `tests/registry/ingest/`
- `src/personal_mcp/registry/models/` -> `tests/registry/models/`
- `src/personal_mcp/prompts/` -> `tests/prompts/`
- `src/personal_mcp/skills/provider.py` -> `tests/skills/test_provider.py`
- `src/personal_mcp/web/` and MCP HTTP surface -> `tests/web/`
## Markers And Strictness ## Run Tests
Configured markers in `pyproject.toml`: Run the full suite with:
- `unit`: fast deterministic tests with no external dependencies
- `integration`: framework or component integration tests
- `smoke`: thin critical-path checks
Pytest runs with `--strict-markers`, so any unregistered marker fails the test run.
## Fixture Layering
Fixture placement follows test scope:
1. `tests/conftest.py` for cross-suite defaults.
2. `tests/web/conftest.py` for web and endpoint client setup.
Prefer adding fixtures at the narrowest scope that serves more than one test.
## Command Baseline
Canonical invocation:
```bash ```bash
uv run pytest uv run pytest
``` ```
Useful filtered runs: Run one transport while working on a focused change:
```bash ```bash
uv run pytest --collect-only -q uv run pytest tests/test_http.py -q
uv run pytest -m unit -q uv run pytest tests/test_stdio.py -q
uv run pytest -m integration -q
uv run pytest -m smoke -q
``` ```
## Adding New Tests The repository uses strict pytest markers. Register any new marker in `pyproject.toml` before using it.
When adding coverage: ## Full Validation
1. Place tests under the nearest existing module subtree (`prompts/`, `registry/`, `skills/`, or `web/`).
2. Mirror the source path where practical.
3. Reuse existing `conftest.py` files before adding new fixture layers.
4. Add markers only when they convey execution intent, and register new markers in `pyproject.toml` first.
This keeps the suite aligned with the current architecture while preserving a fast local test loop. ```bash
uv run zensical build
uv run ruff check .
uv run ty check
uv run pytest
```
Prefer durable boundaries over implementation details: provider discovery, prompt rendering, traversal rejection, protocol behavior, and installed-package path resolution. Do not test deleted catalog projections, Pydantic immutability internals, or helper delegation. See the [Pytesting skill](./skills/pytesting/SKILL.md) when adding or restructuring tests.
+41 -87
View File
@@ -2,105 +2,59 @@
icon: lucide/workflow icon: lucide/workflow
--- ---
# Skill Usage Mechanics # Using Personal MCP
## Purpose Personal MCP gives clients access to skills, prompts, and general documentation. Use the smallest piece of content that matches the task instead of loading the whole library.
This page describes how clients discover and load `personal-mcp` skills published by the [FastMCP Skills Provider](https://gofastmcp.com/servers/providers/skills). ## Connect
Skills are MCP resources. The client remains responsible for selecting guidance, loading only useful supporting material, and applying it to the current workspace. The HTTP endpoint is `/mcp`. For a local server on port `8765`, connect to:
## Published Skill Surface
Each directory beneath `src/personal_mcp/docs/skills/` publishes:
1. `skill://<name>/SKILL.md` for primary instructions
2. `skill://<name>/_manifest` for file discovery and integrity metadata
3. `skill://<name>/{path*}` for supporting files
The server uses `supporting_files="template"`. Main files and manifests appear in `resources/list`; supporting files stay behind per-skill wildcard templates so the resource list remains compact.
The manifest contains every skill-relative path, byte size, and SHA256 hash. References do not have synthetic ids or a separate catalog record.
Prompts are available through native MCP prompt discovery and rendering.
## Discovery Workflow
Use this bounded sequence:
1. List resources or call FastMCP `list_skills()`.
2. Compare skill names and descriptions.
3. Read one selected `skill://<name>/SKILL.md`.
4. Read `skill://<name>/_manifest` only when supporting material may be useful.
5. Fetch the minimum supporting paths needed for the task.
6. Reconcile the guidance with the actual repository code before making changes.
Do not load every skill or every supporting file up front.
## FastMCP Client Utilities
FastMCP provides native utilities in `fastmcp.utilities.skills`:
1. `list_skills(client)` discovers main skill resources.
2. `get_skill_manifest(client, name)` parses a generated manifest.
3. `download_skill(client, name, target_dir)` downloads one skill.
4. `sync_skills(client, target_dir)` downloads all advertised skills.
These utilities operate directly on the native `skill://` contract and require no repository-specific adapter.
## Copilot Invocation
In VS Code, skills can arrive through:
1. explicit attachment from `Add Context > MCP Resources` or `MCP: Browse Resources`
2. direct resource reads on selected `skill://<name>/SKILL.md` entries
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. Use `MCP: Browse Resources` to confirm server-side availability, then attach only the minimum skill resources needed for the current task.
A reliable prompt is:
```text ```text
Browse MCP resources, select the best matching skill://.../SKILL.md entry by description, inspect its _manifest only if supporting detail is needed, and reconcile the guidance with this workspace. http://127.0.0.1:8765/mcp
``` ```
## Thin Shim Pattern Clients that launch servers as subprocesses can use:
Consumer repositories can bind file scopes to native skill resources with short `.github/instructions/*.instructions.md` files. ```bash
uv run mcp-stdio
| `applyTo` scope | Companion docs | Primary skill resource |
| --- | --- | --- |
| `**/*.md` | [Authoring Guide](./authoring.md) | `skill://zensical-docs/SKILL.md` |
| `tests/**` | [Testing](./testing.md) | `skill://pytesting/SKILL.md` |
| `.vscode/**` | [VS Code Configuration](./skills/vscode-configuration/SKILL.md) | `skill://vscode-configuration/SKILL.md` |
Minimal shape:
```md
---
name: <scope name>
description: Route <path scope> edits to a personal-mcp skill.
applyTo: '<glob>'
---
Load `skill://<skill-name>/SKILL.md` first. Read `_manifest` and supporting files only when the task needs deeper detail. Apply the guidance to the current repository rather than treating it as generated output.
``` ```
## Failure Recovery ## Use A Skill
When no skill is an obvious match: Skills are MCP resources. A client should:
1. compare the available main-resource descriptions again 1. browse the available `skill://<name>/SKILL.md` resources
2. select at most two candidates 2. choose one by its name and description
3. read their main files, not all supporting files 3. read the main `SKILL.md`
4. ask one clarifying question if the choice remains ambiguous 4. read its `_manifest` only when extra reference material is needed
5. load only the relevant supporting files
When a supporting path fails, refresh `_manifest`; file paths are the public supporting-resource identifiers. The guidance should then be checked against the code and conventions in the current workspace.
## Runtime Checklist ## Use A Prompt
1. Confirm MCP connectivity. Prompts are reusable workflows with named arguments. Browse the server's prompts, select one by its description, and supply the requested values when invoking it.
2. Confirm at least one `skill://<name>/SKILL.md` resource is listed.
3. Read its `_manifest` and verify `SKILL.md` appears with a SHA256 hash. In VS Code, MCP prompts appear as chat slash commands. Resources can be opened from **MCP: Browse Resources** and attached as context when the active chat surface supports it.
4. Read one supporting file through its manifest path.
5. Keep loaded context bounded to the selected skill and relevant files. ## Tool Fallbacks
Some clients can call tools but cannot browse MCP resources or prompts directly. For those clients, the server exposes four read-only tools:
- `list_resources`
- `read_resource`
- `list_prompts`
- `get_prompt`
They provide access to the same underlying content. Clients with native resource and prompt support should use those native features.
## Example Request
```text
Browse the available Personal MCP skills, choose the best match for this task,
read its main SKILL.md, and load supporting files only if they are needed.
Apply the guidance to this repository rather than treating it as generated output.
```
For the exact resource and prompt formats, see the [content contracts](./contracts/index.md).
+9 -2
View File
@@ -1,6 +1,8 @@
from __future__ import annotations from __future__ import annotations
from fastmcp import FastMCP from fastmcp import FastMCP
from fastmcp.server.transforms import PromptsAsTools
from fastmcp.server.transforms import ResourcesAsTools
from mcp_types import Icon from mcp_types import Icon
from .prompts.provider import prompt_lifespan from .prompts.provider import prompt_lifespan
@@ -10,8 +12,9 @@ from .skills import skill_lifespan
_SERVER_INSTRUCTIONS = """Personal development guidance exposed as native MCP resources and prompts. _SERVER_INSTRUCTIONS = """Personal development guidance exposed as native MCP resources and prompts.
Use prompts for parameterized workflows. For task-specific guidance, browse native skill resources, Use prompts for parameterized workflows; tool-only agents can discover and render them with
select one skill://<name>/SKILL.md resource, and read its manifest only when supporting detail is needed. list_prompts and get_prompt. For task-specific guidance, browse native skill resources, select one
skill://<name>/SKILL.md resource, and read its manifest only when supporting detail is needed.
""" """
_SERVER_ICON = Icon( _SERVER_ICON = Icon(
src=( src=(
@@ -59,6 +62,10 @@ def create_mcp() -> FastMCP:
def docs_markdown(path: str) -> dict[str, str]: def docs_markdown(path: str) -> dict[str, str]:
return read_docs_markdown_path(registry, path) return read_docs_markdown_path(registry, path)
# Bridges tool-only clients that cannot browse native resources or prompts directly.
mcp.add_transform(ResourcesAsTools(mcp))
mcp.add_transform(PromptsAsTools(mcp))
return mcp return mcp
+13 -1
View File
@@ -1,4 +1,5 @@
from fastmcp import Client from fastmcp import Client
from mcp_types import TextContent
from mcp_types import TextResourceContents from mcp_types import TextResourceContents
@@ -6,8 +7,9 @@ async def assert_server_contract(client: Client) -> None:
resources = {str(resource.uri) for resource in await client.list_resources()} resources = {str(resource.uri) for resource in await client.list_resources()}
templates = {str(template.uri_template) for template in await client.list_resource_templates()} templates = {str(template.uri_template) for template in await client.list_resource_templates()}
prompts = {prompt.name for prompt in await client.list_prompts()} prompts = {prompt.name for prompt in await client.list_prompts()}
tools = {tool.name for tool in await client.list_tools()}
assert await client.list_tools() == [] assert tools == {"get_prompt", "list_prompts", "list_resources", "read_resource"}
assert "skill://pytesting/SKILL.md" in resources assert "skill://pytesting/SKILL.md" in resources
assert "resource://docs/{path*}" in templates assert "resource://docs/{path*}" in templates
assert "skill://pytesting/{path*}" in templates assert "skill://pytesting/{path*}" in templates
@@ -20,3 +22,13 @@ async def assert_server_contract(client: Client) -> None:
assert "# Pytesting" in skill_content[0].text assert "# Pytesting" in skill_content[0].text
assert isinstance(docs_content[0], TextResourceContents) assert isinstance(docs_content[0], TextResourceContents)
assert '"format": "markdown"' in docs_content[0].text assert '"format": "markdown"' in docs_content[0].text
listed = await client.call_tool("list_resources")
listed_content = listed.content[0]
assert isinstance(listed_content, TextContent)
assert "skill://pytesting/SKILL.md" in listed_content.text
read = await client.call_tool("read_resource", {"uri": "skill://pytesting/SKILL.md"})
read_content = read.content[0]
assert isinstance(read_content, TextContent)
assert "# Pytesting" in read_content.text
+3 -3
View File
@@ -11,7 +11,7 @@
# The site_name is shown in the page header and the browser window title # The site_name is shown in the page header and the browser window title
# #
# Read more: https://zensical.org/docs/setup/basics/#site_name # Read more: https://zensical.org/docs/setup/basics/#site_name
site_name = "Documentation" site_name = "Personal MCP"
site_dir = "src/personal_mcp/site" site_dir = "src/personal_mcp/site"
docs_dir = "src/personal_mcp/docs" docs_dir = "src/personal_mcp/docs"
@@ -20,12 +20,12 @@ docs_dir = "src/personal_mcp/docs"
# meaningful description of the site content for use by search engines. # meaningful description of the site content for use by search engines.
# #
# Read more: https://zensical.org/docs/setup/basics/#site_description # Read more: https://zensical.org/docs/setup/basics/#site_description
site_description = "A new project generated from the default template project." site_description = "Software development guidance published as MCP resources, prompts, and human-readable documentation."
# The site_author attribute. This is used in the HTML head element. # The site_author attribute. This is used in the HTML head element.
# #
# Read more: https://zensical.org/docs/setup/basics/#site_author # Read more: https://zensical.org/docs/setup/basics/#site_author
site_author = "<your name here>" site_author = "Personal MCP"
# The site_url is the canonical URL for your site. When building online # The site_url is the canonical URL for your site. When building online
# documentation you should set this. # documentation you should set this.