add-provider
Add a new storage or service provider to the core package. Use when implementing a new backend for StorageService (e.g., a new database) or a new service provider (e.g., a new LLM backend).
来源信息
- 仓库
- cyanheads/mcp-ts-core
- 最近来源活动
- 2026年9月13日 17:08
- 检测到的 SKILL.md 语言
- 英语
- 星标
- 151
- 分支
- 30
安装方式
默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。
检查来源文件
决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。
正在显示 SKILL.md
SKILL.md
来源说明 · 只读预览- name
- add-provider
- description
- Add a new storage or service provider to the core package. Use when implementing a new backend for StorageService (e.g., a new database) or a new service provider (e.g., a new LLM backend).
- metadata
- {"author":"cyanheads","version":"1.1","audience":"internal","type":"reference"}
## Context
Providers implement interfaces defined in core. They are selected at runtime via config
(e.g., `STORAGE_PROVIDER_TYPE`). Tier 3 providers lazy-load their dependencies to keep the
core bundle small.
Providers live inside the package source tree — import the interface via relative path
(e.g., `import type { IStorageProvider } from '../core/IStorageProvider.js'`), not via the
package subpath exports (those are for consumers).
## Provider interfaces
| Domain | Interface file |
|:--------|:--------------------------------------------------|
| Storage | `src/storage/core/IStorageProvider.ts` |
| LLM | `src/services/llm/core/ILlmProvider.ts` |
| Speech | `src/services/speech/core/ISpeechProvider.ts` |
Read the relevant interface fully before implementing — each has distinct required members.
`ISpeechProvider` in particular requires `readonly name`, `readonly supportsTTS`,
`readonly supportsSTT`, and `healthCheck()` in addition to the capability methods;
these flags drive routing in `SpeechService`.
## File conventions
Provider file location and naming differ by domain:
- **Storage** — nested subdirectory, camelCase directory name, PascalCase-suffixed provider file.
Each provider gets its own subdirectory for the provider file plus any co-located types:
`src/storage/providers/{{providerName}}/{{providerName}}Provider.ts`
(e.g., `src/storage/providers/inMemory/inMemoryProvider.ts`,
`src/storage/providers/supabase/supabaseProvider.ts` + `supabase.types.ts`)
- **LLM / Speech** — flat directory, kebab-case with `.provider.ts` suffix:
`src/services/llm/providers/{{provider-name}}.provider.ts`
`src/services/speech/providers/{{provider-name}}.provider.ts`
(e.g., `src/services/llm/providers/openrouter.provider.ts`,
`src/services/speech/providers/elevenlabs.provider.ts`)
## Steps
1. **Identify the provider interface** — read the interface file for the target domain
(see table above).
2. **Create the provider file** following the file convention for its domain (see above).
3. **Implement the interface** — all methods must be implemented. Storage providers build
on `src/storage/core/providerHelpers.ts` rather than re-deriving what the existing
providers share:
- `getManyViaGet` / `setManyViaSet` / `deleteManyViaDelete` — the batch methods as a
parallel fan-out over the single-key methods, for backends with no native batch API.
- `encodeEnvelope` / `decodeEnvelope` — the TTL envelope for backends with no TTL of their
own (R2, filesystem). `decodeEnvelope` returns `{ kind: 'expired' }` so the provider can
delete on read, returns pre-envelope JSON as a plain value, and throws `SyntaxError` on
invalid JSON so the provider can attach the key to the error it raises.
- `paginateSortedKeys` — one `list()` page over an already-sorted key set, with the cursor
for the page that follows.
- `escapeLikePattern` — escapes `%`, `_`, and `\` in a prefix before a SQL `LIKE`.
4. **Lazy-load dependencies** if Tier 3:
```typescript
let _client: SomeClient | undefined;
async function getClient(): Promise<SomeClient> {
if (!_client) {
const { SomeClient } = await import('some-package');
_client = new SomeClient(/* config */);
}
return _client;
}
```
5. **Register the provider** — the registration point differs by domain:
- **Storage** — two changes required:
1. Add the new provider string to the `z.enum` for `STORAGE_PROVIDER_TYPE` in
`src/config/index.ts` — without this, the config schema rejects the env var at runtime.
2. Add a `case` to the `switch` in `src/storage/core/storageFactory.ts`
inside `createStorageProvider()`. Import the new provider class at the top of that file.
- **Speech** — two changes required:
1. Add the new provider string literal to the `provider` union in
`SpeechProviderConfig` (`src/services/speech/types.ts`, field `provider`).
2. Add a `case` to the `switch` in `createSpeechProvider()`
(`src/services/speech/core/SpeechService.ts`). Import the new provider class at
the top of that file.
- **LLM** — currently only one provider exists (`OpenRouterProvider`); it is
instantiated directly in `src/core/app.ts` rather than through a factory switch.
There is no factory pattern yet — adding a second provider requires introducing
one (a selector env var, a factory function, and a conditional in `app.ts`).
Read `src/core/app.ts` to understand the current instantiation site before
designing the wiring.
6. **Update the Worker-compatible provider list** if the new storage provider runs in
Cloudflare Workers. The list is an inline array in `storageFactory.ts` at the
`isServerless()` guard:
```typescript
// src/storage/core/storageFactory.ts
!['in-memory', 'cloudflare-r2', 'cloudflare-kv', 'cloudflare-d1'].includes(providerType)
```
Add the new provider string to this array. Non-storage providers have no equivalent
gate.
7. **Add the dependency** if Tier 3: add to both `peerDependencies` and
`peerDependenciesMeta` (with `{ "optional": true }`) in `package.json`.
Without the `peerDependenciesMeta` entry, the dep appears required rather than optional.
8. **Run `bun run rebuild`** — since this is package source, verify the build output compiles.
9. **Run `bun run devcheck`** to verify.
## Checklist
- [ ] Provider file created with JSDoc `@fileoverview` + `@module` header
- [ ] Interface fully implemented (including `name`, `supportsTTS`/`supportsSTT` for speech)
- [ ] Tier 3 dependencies lazy-loaded (not top-level imports)
- [ ] Registered in the correct factory for the domain (see Step 5)
- [ ] Storage: provider string added to `z.enum` in `src/config/index.ts`
- [ ] Storage: Worker-compatible array in `storageFactory.ts` updated if applicable
- [ ] Storage: batch, TTL envelope, paging, and `LIKE` escaping come from `providerHelpers.ts`, not a local copy
- [ ] Speech: `provider` literal added to `SpeechProviderConfig` union in `types.ts`
- [ ] LLM: `src/core/app.ts` instantiation logic updated if adding a second LLM provider
- [ ] Optional peer dependency added to both `peerDependencies` and `peerDependenciesMeta` in `package.json` if Tier 3
- [ ] `bun run rebuild` succeeds
- [ ] `bun run devcheck` passes
- [ ] Tests added under `tests/unit/storage/providers/{{providerName}}/` (storage) or `tests/unit/services/{{domain}}/providers/{{provider-name}}.provider.test.ts` (LLM / speech), and `bun run test` passes
在 GitHub 查看