chore: old docs updates
This commit is contained in:
158
MCP_PLAN.md
158
MCP_PLAN.md
@@ -1,158 +0,0 @@
|
||||
# Chapter 3: MCP Server — Agent-Assisted Content Creation
|
||||
|
||||
## Summary
|
||||
|
||||
Run a standalone MCP server on its own port (default 4124). External AI agents connect via Streamable HTTP at `/mcp`. Read access is open; writes go through user-reviewable proposals with MCP App review UIs powered by `@modelcontextprotocol/ext-apps`.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `@modelcontextprotocol/sdk` (v1.27+) — `McpServer`, `StreamableHTTPServerTransport`, `createMcpExpressApp`
|
||||
- `@modelcontextprotocol/ext-apps` (v1.1+) — `registerAppTool`, `registerAppResource`, `RESOURCE_MIME_TYPE` for interactive review UIs rendered inline in compliant hosts (Claude, ChatGPT, VS Code, etc.)
|
||||
- `express` / `cors` — pulled in transitively by the SDK; used by `createMcpExpressApp`
|
||||
|
||||
zod v4 already in project satisfies peer dep requirements.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
PreviewServer (HTTP on 127.0.0.1:4123) ← unchanged
|
||||
└── /* → Blog preview routes
|
||||
|
||||
MCPServer (HTTP on 127.0.0.1:4124) ← NEW, standalone
|
||||
└── /mcp → Streamable HTTP (POST + GET/DELETE)
|
||||
├── tools, resources, prompts
|
||||
└── ui:// resources → MCP App review Views (via ext-apps)
|
||||
```
|
||||
|
||||
The MCP SDK provides `StreamableHTTPServerTransport` for HTTP handling. We use Node's `http.createServer` directly with stateless mode (new `McpServer` per request) to avoid session management complexity and the Express dependency.
|
||||
|
||||
`MCPServer` is a new engine class that owns the `McpServer` factory, tool/resource/prompt registration, and the `ProposalStore`. It runs independently of PreviewServer.
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Install deps
|
||||
```
|
||||
npm install @modelcontextprotocol/sdk @modelcontextprotocol/ext-apps
|
||||
```
|
||||
|
||||
### Step 2: ProposalStore (`src/main/engine/ProposalStore.ts`)
|
||||
- `Map<string, Proposal>` with TTL (30 min default)
|
||||
- `create(type, data) → id`, `get(id)`, `remove(id)`, `cleanup()`
|
||||
- Draft posts tracked by mapping proposalId → 'draftPost' (actual data in DB)
|
||||
- Periodic cleanup via `setInterval`
|
||||
|
||||
### Step 3: MCPServer engine (`src/main/engine/MCPServer.ts`)
|
||||
- Constructor: inject engine getters (PostEngine, MediaEngine, ScriptEngine, TemplateEngine, MetaEngine, PostMediaEngine, TagEngine)
|
||||
- `createServer()` → factory that instantiates a fresh `McpServer` and registers all tools/resources/prompts (stateless mode — one per request)
|
||||
- `start(port)` → uses `http.createServer` + `StreamableHTTPServerTransport` in stateless mode, listens on `127.0.0.1:port`, validates Origin header
|
||||
- `stop()` → close HTTP server
|
||||
- `cleanup()` → discard proposals, stop intervals, stop server
|
||||
- Singleton pattern with `getMCPServer()` getter
|
||||
|
||||
### Step 4: Resources (read-only data)
|
||||
Register MCP resources mapping to existing engine methods:
|
||||
|
||||
| Resource URI | Engine Method |
|
||||
|---|---|
|
||||
| `bds://posts` | PostEngine.getAllPosts() |
|
||||
| `bds://posts/{id}` | PostEngine.getPost(id) |
|
||||
| `bds://media` | MediaEngine.getAllMedia() |
|
||||
| `bds://media/{id}` | MediaEngine.getMedia(id) |
|
||||
| `bds://tags` | PostEngine.getTagsWithCounts() |
|
||||
| `bds://categories` | PostEngine.getCategoriesWithCounts() |
|
||||
| `bds://stats` | PostEngine.getBlogStats() + MediaEngine |
|
||||
| `bds://posts/{id}/backlinks` | PostEngine.getLinkedBy(id) |
|
||||
| `bds://posts/{id}/outlinks` | PostEngine.getLinksTo(id) |
|
||||
| `bds://posts/{id}/media` | PostMediaEngine.getLinkedMediaDataForPost(id) |
|
||||
| `bds://media/{id}/posts` | PostMediaEngine.getLinkedPostsForMedia(id) |
|
||||
| `bds://media/{id}/image` | MediaEngine.getThumbnailDataUrl(id, 'medium') |
|
||||
|
||||
### Step 5: Read tools
|
||||
- `search_posts` — annotations: `{ readOnlyHint: true, openWorldHint: false }`
|
||||
- Wraps PostEngine.searchPosts() with filter args (query, category, tags, year/month, offset, limit)
|
||||
|
||||
### Step 6: Proposal tools (with MCP App Views)
|
||||
Each proposal tool uses `registerAppTool` from `@modelcontextprotocol/ext-apps/server` to link the tool to a `ui://` resource. The host renders the review View inline.
|
||||
|
||||
Each review View is a bundled HTML page (built with Vite `vite-plugin-singlefile` into a single self-contained HTML) that uses the `App` class from `@modelcontextprotocol/ext-apps` for bidirectional communication with the host.
|
||||
|
||||
| Tool | Action | `ui://` Resource | Accept | Discard |
|
||||
|---|---|---|---|---|
|
||||
| `draft_post` | PostEngine.createPost(draft) | `ui://bds/review-post` | publishPost() | deletePost() |
|
||||
| `propose_script` | Store in ProposalStore | `ui://bds/review-script` | ScriptEngine.createScript() | remove from store |
|
||||
| `propose_template` | Store in ProposalStore | `ui://bds/review-template` | TemplateEngine.createTemplate() | remove from store |
|
||||
| `propose_media_metadata` | Store diff in ProposalStore | `ui://bds/review-metadata` | MediaEngine.updateMedia() | remove from store |
|
||||
| `propose_post_metadata` | Store diff in ProposalStore | `ui://bds/review-metadata` | PostEngine.updatePost() | remove from store |
|
||||
|
||||
Resource registration uses `registerAppResource` from `@modelcontextprotocol/ext-apps/server`. Each resource returns a bundled HTML string with `mimeType: RESOURCE_MIME_TYPE`.
|
||||
|
||||
### Step 7: Accept/discard tools
|
||||
- `accept_proposal({ proposalId })` — dispatch by type, commit change
|
||||
- `discard_proposal({ proposalId })` — dispatch by type, clean up
|
||||
- Registered via `registerAppTool` with `visibility: ["app"]` (app-only, hidden from agent LLM)
|
||||
- Annotations: idempotentHint: true
|
||||
|
||||
### Step 8: MCP Prompts
|
||||
- `draft-blog-post(topic?, category?)` — structured prompt guiding agent to read context and draft
|
||||
- `improve-media-metadata(scope)` — guide agent to review media and propose alt/caption
|
||||
- `content-audit(category?)` — guide agent to review posts for quality
|
||||
|
||||
### Step 9: MCP App Review Views (`src/main/mcp-apps/`)
|
||||
Four View HTML pages, each using vanilla JS + `App` class from `@modelcontextprotocol/ext-apps`:
|
||||
|
||||
- `review-post.html` — title, metadata, rendered markdown, word count; accept/discard buttons call `app.callServerTool()` → `accept_proposal`/`discard_proposal`
|
||||
- `review-script.html` — title, syntax-highlighted Python, validation status
|
||||
- `review-template.html` — title, kind badge, syntax-highlighted Liquid, validation
|
||||
- `review-metadata.html` — side-by-side diff (current vs proposed)
|
||||
|
||||
Each View:
|
||||
1. Receives tool result data from host via `app.ontoolresult`
|
||||
2. Renders focused review interface
|
||||
3. On accept/discard: calls `app.callServerTool({ name: 'accept_proposal' | 'discard_proposal', arguments: { proposalId } })`
|
||||
4. Updates UI to show outcome
|
||||
|
||||
Build step: Review Views are inline HTML template strings in `mcp-views.ts` — no separate build step needed.
|
||||
|
||||
### Step 10: Lifecycle in main.ts
|
||||
- MCPServer starts independently alongside PreviewServer during app init
|
||||
- On `before-quit`: call `MCPServer.cleanup()` (stops server + discards proposals)
|
||||
|
||||
### Step 11: Update TODO.md
|
||||
- Remove stdio references per user request
|
||||
- Mark standalone server approach in the doc
|
||||
|
||||
### Step 12: Tests (TDD per CLAUDE.md)
|
||||
Each step above is preceded by failing tests:
|
||||
|
||||
- **ProposalStore**: create/get/remove, TTL expiry, cleanup
|
||||
- **MCPServer tools**: tool definitions (names, schemas, annotations)
|
||||
- **MCPServer resources**: URI resolution, data returned
|
||||
- **MCPServer prompts**: message structure, arguments
|
||||
- **Accept/discard**: draft_post→accept publishes, propose_script→discard removes
|
||||
- **Integration**: HTTP POST to `http://127.0.0.1:{port}/mcp`, tool call, response format
|
||||
- **Review Views**: `registerAppResource` returns valid HTML with `RESOURCE_MIME_TYPE`
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `src/main/engine/ProposalStore.ts` | NEW — in-memory proposal storage |
|
||||
| `src/main/engine/MCPServer.ts` | NEW — standalone MCP server engine |
|
||||
| `src/main/engine/mcp-views.ts` | NEW — inline review View HTML templates |
|
||||
| `tests/engine/mcp-views.test.ts` | NEW |
|
||||
| `src/main/main.ts` | MODIFY — start/cleanup MCPServer |
|
||||
| `tests/engine/ProposalStore.test.ts` | NEW |
|
||||
| `tests/engine/MCPServer.test.ts` | NEW |
|
||||
| `tests/engine/MCPServer.integration.test.ts` | NEW |
|
||||
| `TODO.md` | MODIFY — remove stdio, update transport section |
|
||||
|
||||
## Verification
|
||||
|
||||
1. `npm test` — all tests pass (new + existing)
|
||||
2. `npm run build` — clean build
|
||||
3. Manual: start app, connect Claude Code or curl to `http://127.0.0.1:4124/mcp` with MCP protocol, verify tool listing and resource reading
|
||||
4. Manual: call `draft_post` tool, verify draft created, review View renders inline in host, accept publishes post
|
||||
|
||||
## Unresolved Questions
|
||||
|
||||
None — scope is clear: standalone HTTP server on port 4124, MCP App Views via `@modelcontextprotocol/ext-apps`, no settings UI.
|
||||
Reference in New Issue
Block a user