mcp-4-llm
](https://www.npmjs.com/package/@pqai/mcp-4-llm) 
一个CLI工具,可生成LLM就绪的MCP(模型上下文协议)服务器项目,具有干净的架构、全面的linting和自主开发护栏。
快速开始
npx @pqai/mcp-4-llm my-service
cd my-service
npm run dev为什么要为LLM开发提供干净的架构?
清洁架构不仅仅是关于代码组织——它是关于创建LLM可以理解、遵循和一致生成的可预测模式。
核心观点:法学硕士需要模式,而不是自由
当LLM处理您的代码库时,它会根据读取的代码构建一个心智模型。你的模式越一致和明确,LLM就越能:
- 理解现有代码 -根据文件的位置和命名识别每个文件的功能
- 生成新代码 -创建新功能时遵循既定模式
- 做出正确的决定 -知道把东西放在哪里,进口什么
- 抓住错误 -确定什么时候违反了既定的模式
非结构化代码库的问题
如果没有明确的架构,LLM将面临不断的模糊性:
| 问题 | LLM后果 |
|---|---|
| 业务逻辑与HTTP处理程序混合 | LLM将逻辑放在错误的地方 |
| 错误处理不一致 | LLM生成混合 throw、返回代码和结果类型 |
| 无导入约定 | LLM创建循环依赖关系 |
| 域逻辑中的数据库调用 | LLM将业务规则耦合到基础架构 |
| 各种测试方法 | LLM生成不一致的测试风格 |
这导致了 漂移--LLM生成的每个更改都会使代码库稍微不那么一致,随着时间的推移而复合。
清洁架构如何解决这个问题
清洁架构提供 明确的、机器可读的规则 限制LLM输出:
1.层分离=明确放置规则
src/
├── domain/ # Pure business logic, no imports from other layers
├── application/ # Use cases, orchestration, ports (interfaces)
├── infrastructure/ # External adapters (DB, HTTP, files)
├── mcp/ # MCP protocol layer (tools, server)
└── di/ # Dependency injection wiring当LLM需要添加“验证电子邮件”功能时,它知道:
- 纯验证逻辑→
domain/value-objects/ - 协调多个验证→
application/use-cases/ - 调用电子邮件验证API→
infrastructure/services/
没有歧义。无需讨论。
2.依赖规则=可预测的导入
| 图层 | 可以导入 | 无法导入 |
|---|---|---|
| 域 | 仅域 | 其他所有 |
| 应用程序 | 应用程序,域 | 基础设施,mcp,di |
| 基础设施 | 基础设施、应用程序、域 | mcp、di |
| mcp | mcp,应用程序,di | 域,基础设施 |
应用,基础设施,MCP
这些规则是 由ESLint在编译时强制执行LLM不能意外创建:
- 导入的域代码
express - 直接调用数据库的用例
- 层之间的循环依赖关系
3.桶出口=一致的进口模式
每个目录都有一个 index.ts 导出其公共API:
// LLM always writes this:
import { User, Email } from '../domain/index.js';
// Never this:
import { User } from '../domain/entities/user.entity.js';
import { Email } from '../domain/value-objects/email.vo.js';这给LLMs一个 单一、可预测的导入模式 跟随。
4.结构化错误=丰富的错误上下文
每个错误都必须有:
{
code: 'USER_NOT_FOUND', // Machine-readable identifier
message: 'User not found', // Human-readable description
suggestedFix: 'Check the user ID', // Actionable guidance
isRetryable: false, // Can the operation be retried?
category: 'not_found' // Error classification
}当LLM生成错误处理时,它会生成 一致的、信息性错误 这有助于人类和其他LLM了解出了什么问题。
5.用例模式=可预测流
每个用例都遵循相同的结构:
class CreateUserUseCase {
async execute(input: unknown): Promise {
// 1. Validate input with Zod
const validated = CreateUserSchema.parse(input);
// 2. Execute business logic
const user = User.create(validated);
// 3. Persist via port
await this.userRepository.save(user);
// 4. Return result
return user;
}
}LLMs可以 可靠地生成新的用例 因为模式是明确的和强制的。
复合效应
每个约束都会使其他约束的有效性倍增:
- 图层规则+桶导出=无导入混淆
- 结构化错误+用例模式=一致的错误传播
- 域隔离+端口/适配器=易于添加新集成
- BDD功能+用例=符合业务需求的测试
结果:LLM生成的代码无缝地融入你的代码库,即使在数千次人工智能辅助的更改中也能保持一致性。
生成什么
my-service/
├── src/
│ ├── domain/ # Pure business logic (no external deps)
│ │ ├── entities/ # Core business objects
│ │ ├── value-objects/# Immutable, validated types
│ │ └── errors/ # Domain-specific errors
│ ├── application/ # Use cases and port interfaces
│ │ ├── use-cases/ # Business operations
│ │ ├── ports/ # Interface definitions
│ │ └── schemas/ # Zod validation schemas
│ ├── infrastructure/ # External adapters
│ ├── mcp/ # MCP server and tools
│ │ ├── server.ts # MCP server setup
│ │ └── tools/ # MCP tool implementations
│ ├── di/ # Dependency injection
│ └── index.ts # Entry point
├── tests/
│ ├── unit/ # Unit tests
│ ├── step-definitions/ # BDD step implementations
│ └── mocks/ # Test doubles
├── features/ # BDD feature files
├── scripts/
│ └── check-code-quality.sh
├── CLAUDE.md # LLM development guide
├── AGENTS.md # LLM development guide (identical)
└── [config files]提交前质量检查
生成的项目包括 41次质量检查 该块提交包含不完整或不兼容的代码。所有检查均为 错误,而不是警告.
哲学:左转,快速失败
每个检查都有特定的原因:
- 在问题恶化之前抓住问题 -今天的TODO明天就会变成技术债务
- 保持LLM上下文质量 -不一致的代码混淆了未来的LLM交互
- 强制建筑边界 -预防违规比修复违规更容易
- 确保生产准备就绪 -生产中没有占位符代码
______________________________________________________________________
检查1:工作标记不完整
为什么? 不完整的工作应该在问题中跟踪,而不是隐藏在代码中。流入生产的TODO和存根变成了看不见的技术债务。
| 检查 | 模式 | 基本原理 |
|---|---|---|
| 1a | TODO, FIXME, XXX, HACK, BUG | 工作项属于问题跟踪器,而不是代码注释 |
| 1b | not implemented, placeholder | Stub代码表示未完成的工作 |
| 1c | mock, fake, dummy, stub | 测试公用设施不得泄漏到生产中 |
| 1c-2 | MockService, FakeRepository (CamelCase) | Catches测试加倍类样式命名 |
| 1d | .only(, .skip( | 集中/跳过测试会破坏CI并隐藏失败 |
LLM福利: LLM不会学习生成占位符代码,因为代码库中不存在占位符代码。
______________________________________________________________________
检查2:类型安全和代码质量
为什么? 类型安全旁路和棉绒抑制隐藏了真正的问题。他们还教法学硕士坏习惯。
| 检查 | 模式 | 基本原理 |
|---|---|---|
| 2a | as any | 类型断言绕过TypeScript的安全保证 |
| 2b | @ts-ignore, @ts-expect-error | 错误抑制隐藏了真正的类型问题 |
| 2c | eslint-disable | Lint绕过了隐藏代码质量问题 |
| 2d | TODO/FIXME 在测试中 | 测试代码应与生产代码一样完整 |
| 2e | throw new Error('not implemented') | Stub实现表示未完成的工作 |
| 2f | console.log | MCP使用stdout作为协议;使用 console.error 用于日志记录 |
| 2g | throw new Error() 在域/应用程序中 | 通用错误缺乏结构;使用 DomainError |
| 2小时 | reflect-metadata 不首先导入 | tsyrange装饰器需要先加载元数据polyfill |
LLM福利: LLM学会使用正确的类型和结构化错误,而不是快捷方式。
______________________________________________________________________
检查3:桶出口
为什么? 一致的导入模式降低了人类和LLM的认知负荷。桶导出为每个模块创建一个明确的公共API。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 3a | 层 index.ts 存在 | 每个层必须公开一个公共API |
| 3b | 子目录 index.ts exists | 嵌套模块(实体、模式)也需要桶 |
| 3c | 无直接文件导入 | 使用 ../schemas/index.js 不 ../schemas/user.schema.js |
LLM福利: LLM总是生成相同的导入模式: from '../layer/index.js'.
______________________________________________________________________
检查4:Zod验证
为什么? 所有外部输入都必须经过验证。Zod通过TypeScript类型推断提供运行时验证。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 4 | 用例调用 .parse() 或 .safeParse() | 每个用例都必须验证其输入 |
LLM福利: LLM了解到验证是强制性的,而不是可选的。
______________________________________________________________________
检查5:域错误结构
为什么? 结构化错误支持一致的错误处理、有用的错误消息和重试逻辑。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 5a | base.error.ts 具有抽象属性 | 基类定义错误契约 |
| 5b | 错误 code, suggestedFix, isRetryable, category | 每个错误都必须提供可操作的信息 |
LLM福利: LLM会生成所有必填字段的错误,从而在整个系统中实现丰富的错误处理。
______________________________________________________________________
检查6:BDD功能覆盖率
为什么? BDD功能充当可执行文档。他们确保业务需求得到测试,并为LLM提供示例。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 6a | features/ 目录存在 | BDD是核心架构要求 |
| 6b | 功能文件存在 | 至少一个 .feature 需要文件 |
| 6c | 功能有场景 | 空的功能文件没有价值 |
| 6d | 存在步骤定义 | 功能需要实现才能执行 |
| 6e | 功能涵盖的用例 | 业务逻辑应涵盖BDD |
| 6f | 最小场景数 | 每个用例推荐≥2个场景 |
LLM福利: LLM可以在生成代码之前阅读功能文件以了解业务需求。
______________________________________________________________________
检查7:值对象错误
为什么? 值对象是验证的第一行。他们必须抛出结构化错误,而不是通用错误。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 7 | 值对象抛出 DomainError | 通用 Error 缺乏适当处理的结构 |
LLM福利: LLM学会使用域错误,即使是在最简单的验证代码中。
______________________________________________________________________
检查8:MCP工具错误处理
为什么? MCP工具是外部接口。他们必须优雅地处理错误并返回结构化的响应。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 8a | 工具有try-catch | 每个工具都必须处理错误 |
| 8b | 工具返回结构化错误 | 返回 {isError: true, code, message, suggestedFix} |
LLM福利: LLM生成健壮的工具实现,不会在意外输入时崩溃。
______________________________________________________________________
检查9:MCP工具注册
为什么? 工具必须连接好才能调用。未注册的工具是死代码。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 9 | 在中注册的工具 server.ts | 工具必须从容器中导入和解析 |
LLM福利: LLMs学习完整的模式:创建工具→ 桶装出口→ 在服务器中注册。
______________________________________________________________________
检查10:用例暴露
为什么? 业务逻辑应可通过MCP访问。未公开的用例表明集成不完整。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 10 | 通过MCP工具公开的用例 | 每个用例都应该可以通过MCP调用 |
LLM福利: LLM明白用例需要相应的MCP工具。
______________________________________________________________________
检查11:桶出口使用情况
为什么? 服务器应该从桶中导入工具,而不是直接从文件中导入。
| 检查 | 内容 | 基本原理 |
|---|---|---|
| 11 | 从工具桶导入服务器 | 与桶导出模式一致 |
LLM福利: LLM在整个代码库中看到一致的导入模式。
______________________________________________________________________
测试覆盖要求
承诺前执行 80%覆盖率 在所有指标上:
| 度量 | 阈值 | 基本原理 |
|---|---|---|
| 语句 | 80% | 应测试大多数代码路径 |
| 分支 | 80% | 应涵盖条件逻辑 |
| 函数 | 80% | 应测试公共API |
| 行数 | 80% | 总体代码覆盖率 |
LLM福利: 高覆盖率意味着LLM在生成新测试时有更多的测试示例可供学习。
ESLint规则(所有错误)
代码质量规则的执行方式如下 错误,而不是警告。警告被忽略;错误得到修复。
| 规则 | 设置 | 基本原理 |
|---|---|---|
complexity | max 10 | 复杂的函数很难被人类和LLM理解 |
max-depth | max 4 | 深度嵌套表示需要重构的代码 |
max-lines | 最大750 | 大文件应拆分为重点模块 |
max-lines-per-function | max 100 | 函数应该做好一件事 |
max-params | max 4 | 许多参数表明需要一个options对象 |
no-console | error(allow-warn,error) | stdout保留用于MCP协议通信 |
@typescript-eslint/no-explicit-any | 错误 | 类型安全不是可选的 |
LLM福利: LLM生成符合这些约束的代码,默认情况下生成可维护的代码。
建筑边界
ESLint边界规则在编译时强制执行层依赖关系:
| 图层 | 可以导入 | 无法导入 | 为什么 |
|---|---|---|---|
| 域 | 域 | 其他所有 | 域必须是纯的,没有外部依赖关系 |
| 应用程序 | 应用程序、领域 | 基础设施、mcp、di | 用例编排但不了解基础设施 |
| 基础设施 | 基础设施、应用程序、域 | mcp、di | 适配器实现端口,不了解mcp |
| mcp | mcp,应用程序,di | 域,基础设施 | mcp层通过di使用用例,而不是直接使用 |
应用,基础设施,MCP,域,将一切连接在一起。
为什么这对LLMs很重要: 当LLM试图导入 express 在域文件中,ESLint立即失败。LLM从错误反馈中学习边界。
用法
npx @pqai/mcp-4-llm
或者以交互方式运行:
npx @pqai/mcp-4-llm
# Prompts for project name生成的项目命令
| 命令 | 目的 |
|---|---|
npm run dev | 从热重新加载开始 |
npm run build | 编译TypeScript |
npm run start | 运行生产构建 |
npm run test | 运行所有测试 |
npm run test:unit | 仅限单元测试 |
npm run test:features | 仅BDD测试 |
npm run test:coverage | 覆盖测试 |
npm run lint | 检查问题 |
npm run lint:fix | 自动修复问题 |
npm run pre-commit | 全质量闸门 |
预提交流程
npm run pre-commit
│
├── check:code-quality # Shell script checks (1-11)
├── lint # ESLint with boundaries
├── format:check # Prettier formatting
├── typecheck # TypeScript compilation
├── build # Production build
├── test:coverage # Unit tests + 80% threshold
└── test:features # BDD/Cucumber tests开发流程
生成的项目执行严格 红绿重构 TDD/BDD实践。每个功能都必须经历这个循环:
第1阶段:红色-写入失败的功能测试
步骤1.1:写入特征文件
# features/create-thing.feature
Feature: Create Thing
As a user
I want to create a thing
So that I can track my things
Scenario: Successfully create a thing
Given I have valid thing data
When I create the thing
Then the thing should be created
And I should receive the thing ID步骤1.2:执行步骤定义
// tests/step-definitions/create-thing.steps.ts
import { Given, When, Then } from '@cucumber/cucumber';
import { expect } from 'chai';
Given('I have valid thing data', function () {
this.input = { name: 'Test Thing' };
});
When('I create the thing', async function () {
const useCase = this.container.resolve(CreateThingUseCase);
this.result = await useCase.execute(this.input);
});
Then('the thing should be created', function () {
expect(this.result).to.exist;
});步骤1.3:验证功能测试失败(红色)
npm run test:features
# Expected: Tests should FAIL because the feature is not implemented yet
# This confirms your tests are actually testing something关键的:如果测试在这个阶段通过,你的测试就不是在测试正确的东西!
第2阶段:红色-写入失败的单元测试
步骤2.1:为模板编写单元测试
// tests/unit/templates/new-template.test.ts
import { describe, it, expect } from 'vitest';
import { getNewTemplate } from '../../../templates/new-template';
describe('new-template', () => {
it('should generate valid output', () => {
const result = getNewTemplate('test-project');
expect(result).toContain('test-project');
});
});步骤2.2:验证单元测试失败(红色)
npm run test:unit
# Expected: Tests should FAIL because the template doesn't exist yet关键的:在继续之前,BDD测试和单元测试都必须为红色!
第3阶段:绿色-实施以通过测试
步骤3.1:实现模板/生成器代码
现在编写通过测试所需的最小代码:
// templates/new-template.ts
export function getNewTemplate(name: string): string {
return `// Generated for ${name}`;
}步骤3.2:验证单元测试通过(绿色)
npm run test:unit
# Expected: Unit tests should now PASS步骤3.3:验证功能测试通过(绿色)
npm run test:features
# Expected: BDD tests should now PASS步骤3.4:验证所有质量门是否通过
npm run pre-commit
# Expected: All checks pass (lint, typecheck, coverage, etc.)第四阶段:REFACTOR-绿色清洁
步骤4.1:提高代码质量
- 提取辅助函数
- 改进命名
- 添加文档
- 优化性能
步骤4.2:验证测试是否仍然通过
npm run test
npm run pre-commit
# Expected: All tests still pass after refactoring总结:红绿重构周期
┌─────────────────────────────────────────────────────────────────┐
│ RED-GREEN-REFACTOR CYCLE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ ┌───────────┐ │
│ │ RED │────▶│ GREEN │────▶│ REFACTOR │──┐ │
│ └─────────┘ └─────────┘ └───────────┘ │ │
│ ▲ │ │
│ └──────────────────────────────────────────┘ │
│ │
│ RED: 1. Write feature file │
│ 2. Write step definitions │
│ 3. Run tests → MUST FAIL │
│ 4. Write unit tests │
│ 5. Run tests → MUST FAIL │
│ │
│ GREEN: 6. Implement minimal code │
│ 7. Run tests → MUST PASS │
│ 8. Run pre-commit → MUST PASS │
│ │
│ REFACTOR: 9. Improve code quality │
│ 10. Run tests → MUST STILL PASS │
│ │
└─────────────────────────────────────────────────────────────────┘由内而外实施
从核心向外构建:
- 领域 -实体、值对象、域错误
- 应用 -用例、端口、模式
- 基础设施 -存储库实施、外部服务
- 主控程序 -公开用例的工具
LLM发展指南
生成的 CLAUDE.md 和 AGENTS.md 文件包含LLM的全面指导:
- 关键规则 -从不做/总是做清单
- 架构图 -视觉层次结构
- 代码模式 -每种模式的示例
- 错误处理 -如何创建和处理错误
- 测试模式 -单元和BDD测试示例
- 常见错误 -错误及其修复
这些文件由Claude Code和兼容的AI编码助手自动加载。
需求
- Node.js 18+
- npm 9+
许可证
麻省理工学院
