Initial package implementation: operations registry, call protocol, and adapters
Extracted from alkhub_ts packages/core/operations/ and packages/core/mcp/. - Runtime-agnostic (injected fs/env deps, no Deno globals) - Direct @logtape/logtape import instead of logger wrapper - PendingRequestMap with pubsub-wired call protocol - Peer-dep isolation for MCP adapter (sub-path export) - Schema const naming convention (XSchema + X type alias) - 68 tests passing, build + lint + test all green
This commit is contained in:
54
docs/architecture/decisions/003-peer-dep-adapters.md
Normal file
54
docs/architecture/decisions/003-peer-dep-adapters.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# ADR-003: Peer Dependencies for Adapter Isolation
|
||||
|
||||
**Status**: Accepted
|
||||
**Date**: 2026-04-30
|
||||
|
||||
## Context
|
||||
|
||||
The MCP adapter (`from_mcp.ts`) depends on `@modelcontextprotocol/sdk` for `Client`, `StdioClientTransport`, and `StreamableHTTPClientTransport`. This dependency is heavy (transitive deps) and only needed by consumers that connect to MCP servers. Other adapters may have similar heavy dependencies in the future (e.g., gRPC, GraphQL).
|
||||
|
||||
Two approaches:
|
||||
|
||||
1. **Regular dependency** — list `@modelcontextprotocol/sdk` as a direct dependency. All consumers install it.
|
||||
2. **Optional peer dependency + sub-path export** — list it as an optional peer dependency, import dynamically, and expose via a separate `./from-mcp` sub-path export.
|
||||
|
||||
## Decision
|
||||
|
||||
Use optional peer dependency with sub-path export.
|
||||
|
||||
```json
|
||||
{
|
||||
"peerDependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@modelcontextprotocol/sdk": { "optional": true }
|
||||
},
|
||||
"exports": {
|
||||
".": { ... },
|
||||
"./from-mcp": { ... }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rationale
|
||||
|
||||
1. **Zero-cost for non-MCP consumers** — `npm install @alkdev/operations` does not install `@modelcontextprotocol/sdk`. Only consumers that `import { createMCPClient } from "@alkdev/operations/from-mcp"` need to install it.
|
||||
|
||||
2. **Dynamic import** — `from_mcp.ts` uses `await import("@modelcontextprotocol/sdk/client/index.js")` and `await import("@modelcontextprotocol/sdk/client/stdio.js")`. The MCP SDK is loaded only when `createMCPClient` is actually called, not at module parse time.
|
||||
|
||||
3. **Explicit dependency declaration** — the sub-path import makes it clear at the import site that this code needs the MCP SDK. A barrel-only import doesn't communicate this.
|
||||
|
||||
4. **No bundler reliance** — sub-path exports don't depend on the consumer's bundler correctly tree-shaking. Not all consumers use bundlers (Deno, Node with `--experimental-strip-types`).
|
||||
|
||||
5. **Follows established pattern** — `@alkdev/pubsub` uses the same approach for its Redis adapter (sub-path export with optional ioredis peer dep).
|
||||
|
||||
6. **Incremental** — future adapters (gRPC, GraphQL) will follow the same pattern. Each adds one peer dep entry and one sub-path export.
|
||||
|
||||
## Consequences
|
||||
|
||||
- `package.json` has a peer dep entry for each adapter's external dependency
|
||||
- Both barrel and sub-path work — barrel re-exports everything for convenience, sub-path for explicitness
|
||||
- `tsup` must list each adapter as a separate entry point
|
||||
- Consumer docs should recommend sub-path imports for adapter-specific code
|
||||
- The `from_mcp.ts` module also imports from `from_schema.ts` and `types.ts` (core), which are bundled into the sub-path output by tsup's code splitting
|
||||
Reference in New Issue
Block a user