Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
# OpenSpec Change: enterprise-mcp
- **Change ID**: enterprise-mcp
- **Package**: `@suaveplan/mcp`
- **Category**: devtools / tooling
- **Target path**: `packages/devtools/tooling/mcp`
- **Status**: PROPOSED
- **Date**: 2026-05-25
- **Reference spec**: `/mnt/s/web/suaveplan/enterprise/mcp.md`
- **Design**: ./design.md
- **Spec delta**: ./specs/mcp/spec.md
---
## Summary
Deliver `@suaveplan/mcp` on the genesis branch from scratch at enterprise quality. The package provides a Model Context Protocol (MCP) server and client implementation. `MCPServer` lets host apps register named tools, URI-addressed resources, and prompt templates; `MCPClient` mirrors the surface from the consumer side. Genesis ships the existing scaffold (42 tests, high coverage, Zod configs) plus: real transport implementations (stdio, SSE, WebSocket), `AbortSignal` cancellation, streaming tool results (`invokeToolStream` / `callToolStream`), a tool/resource ACL hook, all DTOs derived from Zod schemas, OTel spans per invocation, and an E2E test wiring server + client over stdio. `"private": true` is removed; the package is published to Verdaccio.
---
## Current-state audit (genesis)
Does not exist on genesis — fresh build (the main-branch package was a non-published scaffold; genesis rewrites it cleanly per the enterprise spec).
---
## Available workspace deps on genesis
| Package | Role |
|---|---|
| `@suaveplan/error` (`workspace:^`) | `MCPError` / `MCPToolError` / `MCPTransportError` hierarchy |
| `@suaveplan/types` (`workspace:^`) | `LoggerLike` |
| `@suaveplan/events` (`workspace:^`) | Resource-change notifications |
| `@suaveplan/logger` (`workspace:^`) | Structured logs |
| `zod` (`catalog:forms`) | Single source of truth for all wire types |
| `@opentelemetry/api` (`catalog:observability`) | `mcp.*` spans per invocation |
| `tslib` (`catalog:build`) | Runtime helpers |
---
## Public API surface (from main + genesis additions)
```ts
// Server
class MCPServer {
constructor(config: MCPServerConfig)
registerTool(definition: MCPToolDefinition): void
unregisterTool(name: string): void
registerResource(definition: MCPResourceDefinition): void
unregisterResource(uri: string): void
registerPrompt(definition: MCPPromptDefinition): void
unregisterPrompt(name: string): void
invokeTool(name: string, args: unknown, signal?: AbortSignal): Promise<MCPToolResult>
invokeToolStream(name: string, args: unknown, signal?: AbortSignal): AsyncIterable<MCPContentItem>
readResource(uri: string, signal?: AbortSignal): Promise<MCPResourceContent>
getPromptMessages(name: string, args?: Record<string, string>, signal?: AbortSignal): Promise<MCPPromptMessage[]>
getInfo(): MCPServerInfo
start(transport: Transport): Promise<void>
stop(): Promise<void>
setAuthorizer(fn: (request: MCPAuthRequest) => Promise<boolean>): void
}
// Client
class MCPClient {
constructor(config: MCPClientConfig)
connect(transport: Transport): Promise<void>
disconnect(): Promise<void>
refreshTools(): Promise<void>
refreshResources(): Promise<void>
refreshPrompts(): Promise<void>
callTool(name: string, args: unknown, signal?: AbortSignal): Promise<MCPToolResult>
callToolStream(name: string, args: unknown, signal?: AbortSignal): AsyncIterable<MCPContentItem>
readResource(uri: string, signal?: AbortSignal): Promise<MCPResourceContent>
getPrompt(name: string, args?: Record<string, string>, signal?: AbortSignal): Promise<MCPPromptMessage[]>
tools(): RemoteTool[]
resources(): RemoteResource[]
prompts(): RemotePrompt[]
}
// Transport interface
interface Transport {
send(message: MCPMessage): Promise<void>
onMessage(handler: (message: MCPMessage) => void): () => void
close(): Promise<void>
}
// Concrete transports (sub-path exports)
function createStdioTransport(): Transport // Node process.stdin/stdout
function createSseTransport(url: string): Transport
function createWebSocketTransport(url: string): Transport
// Zod schemas (all wire types)
const MCPServerConfigSchema: z.ZodSchema<MCPServerConfig>
const MCPClientConfigSchema: z.ZodSchema<MCPClientConfig>
const MCPToolDefinitionSchema: z.ZodSchema<MCPToolDefinition>
const MCPResourceDefinitionSchema: z.ZodSchema<MCPResourceDefinition>
const MCPPromptDefinitionSchema: z.ZodSchema<MCPPromptDefinition>
const MCPToolResultSchema: z.ZodSchema<MCPToolResult>
const MCPContentItemSchema: z.ZodSchema<MCPContentItem>
const MCPResourceContentSchema: z.ZodSchema<MCPResourceContent>
const MCPPromptMessageSchema: z.ZodSchema<MCPPromptMessage>
// Errors
class MCPError extends SuaveplanError { }
class MCPToolError extends MCPError { }
class MCPTransportError extends MCPError { }
function registerMCPErrorCodes(): void
```
---
## File layout
```text
packages/devtools/tooling/mcp/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
types.ts # Zod schemas + z.infer types for all wire types
constants/index.ts
server/
mcp-server/
mcp-server.ts / .md / .types.ts / .test.ts / index.ts
client/
mcp-client/
mcp-client.ts / .md / .types.ts / .test.ts / index.ts
transport/
transport/
transport.ts / transport.md / transport.types.ts / index.ts
stdio/
stdio.ts / stdio.md / stdio.test.ts / index.ts
sse/
sse.ts / sse.md / sse.test.ts / index.ts
websocket/
websocket.ts / websocket.md / websocket.test.ts / index.ts
tests/
e2e/
stdio-handshake.test.ts
```
---
## Impact
Downstream: AI agent integration, tool registration for LLM workflows. The package is the MCP foundation for the entire platform.
**Prerequisites on Verdaccio**: `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/opentelemetry`.
### Affected dependents
None. `@suaveplan/mcp` is currently unreferenced by every other genesis package (per `MANIFEST.md`).
### Deviations from upstream
1. Upstream is a scaffold with no JSON-RPC framing, no real transport, no abort, no progress, no authorize, no observability. Genesis ships a full implementation of every MCP 2024-11-05 host-side method.
2. Upstream's wire DTOs are hand-rolled interfaces; only the two config objects are Zod. Genesis derives every wire type from a Zod schema (`MCPServerConfigSchema`, `JsonRpcRequestSchema`, `MCPToolCallParamsSchema`, etc.) and `z.infer`s the TypeScript type.
3. Upstream declared a `stdio | sse | websocket` transport union but shipped no transport. Genesis ships a `Transport` interface plus three concrete adapters: `InMemoryTransport.pair()` (universal, used by tests and in-process bridges), `createStdioTransport({ readable, writable })` (universal — accepts injectable byte streams so the same code runs on Node, Bun, Deno, Edge, browser), `createHttpSseTransport({ endpoint, fetchImpl, eventSourceImpl })` (universal — accepts injectable `fetch` + `EventSource` constructor).
4. WebSocket transport is intentionally deferred to a sibling change once `@suaveplan/network-websockets` lands. The `Transport` interface is shaped so a WebSocket adapter is drop-in.
5. The package gains a stable `MCP_CODES` registry built via `defineCodes("MCP", […])` and a `MCPError` / `MCPProtocolError` / `MCPTransportError` hierarchy.
6. Every public method on `MCPServer` / `MCPClient` accepts an `AbortSignal`; cancellation surfaces over the wire as `notifications/cancelled`.
7. Progress notifications: `MCPServer.invokeTool` invokes the handler with a `progress(token, value, total?)` callback that forwards `notifications/progress` to the client.
8. Authorize hook: `MCPServer.setAuthorizer(fn)` gates every incoming method; rejection yields a JSON-RPC `-32001` error mapped to `MCP.UNAUTHORIZED`.
9. Resource subscriptions: server pushes `notifications/resources/updated` to every subscribed client when a resource is marked dirty.
### Out of scope (explicitly deferred to follow-up changes, not silently dropped)
- **WebSocket transport** — requires `@suaveplan/network-websockets`, not yet migrated.
- **React renderers for `MCPContentItem`** — belong in a separate `react-mcp` package under `packages/ui/react/`.
- **`suaveplan-mcp` CLI binary** — belongs in a `tooling/mcp-cli` sibling.
Each deferral is justified above; this is not silent omission per AGENTS §2b.