Claude Code Agent SDK 接入自研插件

Claude Code Agent SDK 接入自研插件

Claude Code Agent SDK

前言

Claude Code Agent SDK 是 Anthropic 推出的强大工具,允许开发者创建自定义的 AI 助手来处理各种编程任务。通过接入自研插件,我们可以扩展 Claude Code 的功能,使其能够处理特定领域的任务。本文将详细介绍如何使用 Claude Code Agent SDK 开发自研插件。

Claude Code Agent SDK 简介

Claude Code Agent SDK 是一个基于 TypeScript/JavaScript 的开发框架,提供了以下核心功能:

  • 多模态交互:支持文本、图像等多种输入格式
  • 工具调用系统:可以调用各种外部工具和 API
  • 上下文管理:自动管理对话历史和上下文
  • 插件架构:支持插件化扩展功能

SDK 核心组件

  1. Agent:主代理类,负责处理用户请求和协调插件
  2. Plugin:插件基类,定义了插件的标准接口
  3. Tool:工具类,封装具体的操作功能
  4. Context:上下文管理器,维护对话状态

开发自研插件

1. 环境准备

首先,确保你的开发环境满足以下要求:

# Node.js 版本需要 >= 16.0.0
node --version

# 安装 Claude Code Agent SDK
npm install @anthropic-ai/claude-code-sdk

2. 创建插件项目

# 创建插件目录
mkdir my-claude-plugin
cd my-claude-plugin

# 初始化项目
npm init -y

# 安装依赖
npm install @anthropic-ai/claude-code-sdk typescript @types/node
npm install -D @types/typescript ts-node nodemon

3. 插件基础结构

创建一个基础插件类:

// src/MyPlugin.ts
import { Plugin, PluginContext, Tool } from '@anthropic-ai/claude-code-sdk';

export class MyPlugin extends Plugin {
  name = 'my-plugin';
  version = '1.0.0';
  description = '我的自定义 Claude Code 插件';

  async initialize(context: PluginContext): Promise<void> {
    // 插件初始化逻辑
    console.log('插件初始化成功');
  }

  getTools(): Tool[] {
    return [
      new MyCustomTool(),
      // 可以添加更多工具
    ];
  }
}

4. 实现自定义工具

// src/MyCustomTool.ts
import { Tool, ToolInput, ToolResult } from '@anthropic-ai/claude-code-sdk';

interface MyToolInput extends ToolInput {
  message: string;
  options?: {
    language?: string;
    style?: 'formal' | 'casual';
  };
}

export class MyCustomTool extends Tool {
  name = 'my-custom-tool';
  description = '执行自定义操作的工具';

  async execute(input: MyToolInput): Promise<ToolResult> {
    try {
      // 实现你的工具逻辑
      const result = await this.processMessage(
        input.message,
        input.options
      );

      return {
        success: true,
        data: result,
        message: '操作成功完成'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message,
        message: '操作失败'
      };
    }
  }

  private async processMessage(
    message: string,
    options?: MyToolInput['options']
  ): Promise<any> {
    // 实现具体的处理逻辑
    // 例如:调用外部 API、处理数据等

    const language = options?.language || 'zh-CN';
    const style = options?.style || 'formal';

    // 这里添加你的业务逻辑
    return {
      originalMessage: message,
      processedMessage: `处理后的消息 (${language}, ${style})`,
      timestamp: new Date().toISOString()
    };
  }
}

插件配置与注册

1. 创建配置文件

// config/plugin-config.ts
export interface PluginConfig {
  name: string;
  version: string;
  enabled: boolean;
  settings: {
    apiKey?: string;
    endpoint?: string;
    timeout?: number;
    retries?: number;
  };
}

export const pluginConfig: PluginConfig = {
  name: 'my-plugin',
  version: '1.0.0',
  enabled: true,
  settings: {
    timeout: 30000,
    retries: 3
  }
};

2. 注册插件

// src/index.ts
import { ClaudeCodeAgent } from '@anthropic-ai/claude-code-sdk';
import { MyPlugin } from './MyPlugin';
import { pluginConfig } from '../config/plugin-config';

async function main() {
  // 创建 Claude Code Agent 实例
  const agent = new ClaudeCodeAgent({
    apiKey: process.env.ANTHROPIC_API_KEY,
    model: 'claude-3-opus-20240229'
  });

  // 创建并注册插件
  const myPlugin = new MyPlugin();
  await agent.registerPlugin(myPlugin, pluginConfig);

  // 启动 agent
  await agent.start();

  console.log('Claude Code Agent 已启动,插件已注册');
}

main().catch(console.error);

实战示例:代码分析插件

让我们创建一个实际的代码分析插件示例:

// src/CodeAnalysisPlugin.ts
import { Plugin, PluginContext, Tool, ToolInput, ToolResult } from '@anthropic-ai/claude-code-sdk';
import * as fs from 'fs/promises';
import * as path from 'path';

interface AnalyzeCodeInput extends ToolInput {
  filePath: string;
  analysisType: 'complexity' | 'security' | 'performance' | 'style';
}

export class CodeAnalysisTool extends Tool {
  name = 'code-analysis';
  description = '分析代码质量、安全性、性能等';

  async execute(input: AnalyzeCodeInput): Promise<ToolResult> {
    try {
      const code = await fs.readFile(input.filePath, 'utf-8');
      const analysis = await this.analyzeCode(code, input.analysisType);

      return {
        success: true,
        data: analysis,
        message: '代码分析完成'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message,
        message: '代码分析失败'
      };
    }
  }

  private async analyzeCode(code: string, type: string): Promise<any> {
    switch (type) {
      case 'complexity':
        return this.analyzeComplexity(code);
      case 'security':
        return this.analyzeSecurity(code);
      case 'performance':
        return this.analyzePerformance(code);
      case 'style':
        return this.analyzeStyle(code);
      default:
        throw new Error(`未知的分析类型: ${type}`);
    }
  }

  private analyzeComplexity(code: string) {
    // 计算圈复杂度、认知复杂度等
    const lines = code.split('\n');
    const complexity = {
      totalLines: lines.length,
      cyclomaticComplexity: this.calculateCyclomaticComplexity(code),
      cognitiveComplexity: this.calculateCognitiveComplexity(code)
    };
    return { type: 'complexity', ...complexity };
  }

  private analyzeSecurity(code: string) {
    // 检查常见的安全问题
    const securityIssues = [];

    // 检查 SQL 注入风险
    if (code.includes('SELECT') && code.includes('+')) {
      securityIssues.push({
        type: 'sql_injection',
        severity: 'high',
        line: this.findLineNumber(code, 'SELECT'),
        description: '可能存在 SQL 注入风险'
      });
    }

    // 检查硬编码密钥
    if (code.includes('password') || code.includes('secret')) {
      securityIssues.push({
        type: 'hardcoded_secret',
        severity: 'critical',
        line: this.findLineNumber(code, 'password'),
        description: '检测到硬编码的密码或密钥'
      });
    }

    return { type: 'security', issues: securityIssues };
  }

  private analyzePerformance(code: string) {
    // 性能分析
    const performanceTips = [];

    // 检查循环嵌套
    const nestedLoops = this.checkNestedLoops(code);
    if (nestedLoops > 2) {
      performanceTips.push({
        type: 'nested_loops',
        severity: 'medium',
        description: `发现 ${nestedLoops} 层循环嵌套,可能影响性能`
      });
    }

    return { type: 'performance', tips: performanceTips };
  }

  private analyzeStyle(code: string) {
    // 代码风格分析
    const styleIssues = [];

    // 检查缩进
    const indentIssues = this.checkIndentation(code);
    if (indentIssues.length > 0) {
      styleIssues.push(...indentIssues);
    }

    return { type: 'style', issues: styleIssues };
  }

  // 辅助方法实现
  private calculateCyclomaticComplexity(code: string): number {
    // 简化的圈复杂度计算
    const decisionKeywords = ['if', 'else', 'while', 'for', 'case', 'catch', 'switch'];
    let complexity = 1;

    decisionKeywords.forEach(keyword => {
      const regex = new RegExp(`\\b${keyword}\\b`, 'g');
      const matches = code.match(regex);
      if (matches) {
        complexity += matches.length;
      }
    });

    return complexity;
  }

  private calculateCognitiveComplexity(code: string): number {
    // 认知复杂度计算
    // 实现略...
    return 0;
  }

  private findLineNumber(code: string, keyword: string): number {
    const lines = code.split('\n');
    for (let i = 0; i < lines.length; i++) {
      if (lines[i].includes(keyword)) {
        return i + 1;
      }
    }
    return -1;
  }

  private checkNestedLoops(code: string): number {
    // 检查循环嵌套层数
    // 实现略...
    return 0;
  }

  private checkIndentation(code: string): any[] {
    // 检查缩进问题
    // 实现略...
    return [];
  }
}

export class CodeAnalysisPlugin extends Plugin {
  name = 'code-analysis-plugin';
  version = '1.0.0';
  description = '代码分析插件';

  async initialize(context: PluginContext): Promise<void> {
    console.log('代码分析插件初始化');
  }

  getTools(): Tool[] {
    return [new CodeAnalysisTool()];
  }
}

最佳实践

1. 错误处理

// 健壮的错误处理
async execute(input: ToolInput): Promise<ToolResult> {
  try {
    // 参数验证
    if (!input.requiredParam) {
      return {
        success: false,
        error: '缺少必需参数',
        code: 'MISSING_PARAMETER'
      };
    }

    // 执行逻辑
    const result = await this.doWork(input);

    return {
      success: true,
      data: result
    };
  } catch (error) {
    // 记录错误日志
    console.error('工具执行错误:', error);

    // 返回友好的错误信息
    return {
      success: false,
      error: process.env.NODE_ENV === 'development'
        ? error.message
        : '内部错误,请稍后重试',
      code: 'INTERNAL_ERROR'
    };
  }
}

2. 性能优化

// 使用缓存优化性能
class CachedTool extends Tool {
  private cache = new Map<string, any>();
  private cacheTimeout = 5 * 60 * 1000; // 5分钟

  async execute(input: ToolInput): Promise<ToolResult> {
    const cacheKey = this.generateCacheKey(input);
    const cached = this.cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      return cached.result;
    }

    // 执行实际操作
    const result = await this.performOperation(input);

    // 缓存结果
    this.cache.set(cacheKey, {
      result,
      timestamp: Date.now()
    });

    return result;
  }

  private generateCacheKey(input: ToolInput): string {
    return JSON.stringify(input);
  }
}

3. 安全考虑

// 输入清理和验证
class SecureTool extends Tool {
  async execute(input: ToolInput): Promise<ToolResult> {
    // 清理输入
    const sanitizedInput = this.sanitizeInput(input);

    // 验证权限
    if (!await this.checkPermission(sanitizedInput)) {
      return {
        success: false,
        error: '权限不足',
        code: 'PERMISSION_DENIED'
      };
    }

    // 执行操作
    return await this.executeSecurely(sanitizedInput);
  }

  private sanitizeInput(input: ToolInput): ToolInput {
    // 移除潜在危险的字符
    if (typeof input === 'string') {
      return input.replace(/[<>]/g, '');
    }
    return input;
  }

  private async checkPermission(input: ToolInput): Promise<boolean> {
    // 实现权限检查逻辑
    return true;
  }
}

4. 测试策略

// src/__tests__/MyTool.test.ts
import { MyCustomTool } from '../MyTool';

describe('MyCustomTool', () => {
  let tool: MyCustomTool;

  beforeEach(() => {
    tool = new MyCustomTool();
  });

  test('should process message correctly', async () => {
    const input = {
      message: 'Hello, World!',
      options: {
        language: 'zh-CN',
        style: 'formal'
      }
    };

    const result = await tool.execute(input);

    expect(result.success).toBe(true);
    expect(result.data.originalMessage).toBe('Hello, World!');
    expect(result.data.processedMessage).toContain('zh-CN');
  });

  test('should handle errors gracefully', async () => {
    const invalidInput = {
      message: null
    };

    const result = await tool.execute(invalidInput);

    expect(result.success).toBe(false);
    expect(result.error).toBeDefined();
  });
});

部署与发布

1. 构建插件

// package.json
{
  "scripts": {
    "build": "tsc",
    "dev": "nodemon src/index.ts",
    "test": "jest",
    "lint": "eslint src/**/*.ts"
  }
}

2. 发布到 npm

# 构建项目
npm run build

# 发布到 npm
npm publish

3. 在 Claude Code 中使用

// 在 Claude Code 配置中加载插件
const agent = new ClaudeCodeAgent({
  plugins: [
    '@your-org/my-claude-plugin'
  ]
});

常见问题与解决方案

Q1: 插件加载失败怎么办?

A: 检查以下几点:

  1. 确保 TypeScript 编译无错误
  2. 检查插件导出是否正确
  3. 验证插件配置文件格式
  4. 查看控制台错误日志

Q2: 如何处理异步操作?

A: 使用 async/await 模式:

async execute(input: ToolInput): Promise<ToolResult> {
  const result = await this.asyncOperation(input);
  return { success: true, data: result };
}

Q3: 插件之间如何通信?

A: 通过事件系统或共享存储:

// 事件方式
context.emit('plugin-event', data);
context.on('plugin-event', (data) => {
  // 处理事件
});

// 共享存储方式
context.shared.set('key', value);
const value = context.shared.get('key');

总结

通过 Claude Code Agent SDK,我们可以轻松开发功能强大的自定义插件。本文介绍了:

  1. SDK 的核心概念和架构
  2. 插件开发的基础步骤
  3. 实战代码分析插件示例
  4. 最佳实践和安全考虑
  5. 部署和发布流程

随着 AI 技术的发展,Claude Code Agent SDK 将为开发者提供更多可能性。希望本文能帮助你开始开发自己的 Claude Code 插件!

参考资料


文章作者: 寒霜
发布时间: 2024-12-16
更新时间: 2024-12-16
许可协议: 本文采用 CC BY-SA 4.0 协议