Skip to content

Latest commit

 

History

History
294 lines (234 loc) · 11.1 KB

File metadata and controls

294 lines (234 loc) · 11.1 KB

Claude Agent SDK Ruby Implementation

This document provides an overview of the Ruby implementation of the Claude Agent SDK, based on the Python version.

Implementation Summary

The Ruby SDK has been fully implemented with the following components:

Core Components (~1,500+ lines of code)

  1. Transport Layer (411 lines)

    • transport.rb - Abstract transport interface (44 lines)
    • subprocess_cli_transport.rb - CLI subprocess implementation (367 lines)
    • Features: Process management, stderr handling, version checking, command building
  2. Query Class (424 lines)

    • query.rb - Full control protocol implementation
    • Features:
      • Bidirectional control request/response routing
      • Hook callback execution
      • Permission callback handling
      • Message streaming with async queues
      • Initialization handshake
      • Full SDK MCP server support
      • Control operations: interrupt, set_permission_mode, set_model
  3. SDK MCP Server (~150 lines)

    • sdk_mcp_server.rb - In-process MCP server implementation
    • Features:
      • SdkMcpServer class for managing tools
      • create_tool helper for defining tools
      • create_sdk_mcp_server function for server creation
      • JSON schema generation from Ruby types
      • Tool execution with error handling
      • Full MCP protocol support (initialize, tools/list, tools/call)
  4. Type System (358 lines)

    • types.rb - Complete type definitions
    • Message types: UserMessage, AssistantMessage, SystemMessage, ResultMessage, StreamEvent
    • Content blocks: TextBlock, ThinkingBlock, ToolUseBlock, ToolResultBlock
    • Configuration: ClaudeAgentOptions with all major settings
    • MCP server configs: McpStdioServerConfig, McpSSEServerConfig, McpHttpServerConfig, McpSdkServerConfig
    • Permission types: PermissionResultAllow, PermissionResultDeny, PermissionUpdate
    • Hook types: HookMatcher, HookCallback
  5. Message Parser (103 lines)

    • message_parser.rb - JSON message parsing
    • Handles all message types with proper error handling
  6. Error Handling (53 lines)

    • errors.rb - Comprehensive error classes
    • ClaudeSDKError, CLIConnectionError, CLINotFoundError, ProcessError, CLIJSONDecodeError, MessageParseError
  7. Main Library (256 lines)

    • lib/claude_agent_sdk.rb - Entry point with query() and Client class
    • Features:
      • Simple query() function for one-shot queries
      • Full-featured Client class for bidirectional conversations
      • Hooks support
      • Permission callbacks support
      • Advanced features: interrupt, set_permission_mode, set_model, server_info

Examples (~450 lines)

  1. quick_start.rb - Basic usage examples with query()
  2. client_example.rb - Interactive client usage
  3. mcp_calculator.rb - Custom tools with SDK MCP servers ✨
  4. hooks_example.rb - Hook callback examples
  5. permission_callback_example.rb - Permission callback examples

Documentation

  • README.md - Overview, install, comparison table, quick start, and minimal API examples
  • docs/ - Topic subpages (client, MCP servers, hooks, configuration, sessions, observability, Rails, types, errors) linked from the README
  • CHANGELOG.md - Version history
  • IMPLEMENTATION.md - This file

Architecture

The Ruby SDK follows the Python SDK's architecture closely:

┌─────────────────────────────────────────┐
│           User Application              │
│  (query() or Client with callbacks)     │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│         Query (Control Protocol)        │
│  - Hook execution                       │
│  - Permission callbacks                 │
│  - Message routing                      │
│  - Control requests/responses           │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│     SubprocessCLITransport              │
│  - Process management                   │
│  - stdin/stdout/stderr handling         │
│  - Command building                     │
└─────────────────┬───────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────┐
│         Claude Code CLI                 │
│  (External Node.js process)             │
└─────────────────────────────────────────┘

Key Design Decisions

  1. Async Runtime: Uses the async gem (Ruby's async/await runtime) instead of Python's anyio
  2. Process Management: Uses Ruby's built-in Open3 for subprocess management (no external process gems needed)
  3. Message Passing: Uses Async::Queue for message streaming instead of memory object streams
  4. Synchronization: Uses Async::Condition for control request/response coordination
  5. Threading: Uses standard Ruby threads for stderr handling
  6. Naming: Follows Ruby conventions (snake_case) while maintaining API similarity
  7. Callbacks: Uses Ruby procs/lambdas for hooks and permission callbacks

Features Implemented

✅ Completed

  • Basic query() function for simple queries
  • Full Client class for interactive conversations
  • Transport abstraction with subprocess implementation using Open3
  • Complete message type system
  • Message parsing with error handling
  • Control protocol (bidirectional communication)
  • Hook support (PreToolUse, PostToolUse, etc.)
  • Permission callback support
  • Interrupt capability
  • Dynamic permission mode changes
  • Dynamic model changes
  • Server info retrieval
  • Comprehensive error handling
  • SDK MCP server support (in-process custom tools) ✨
  • Full MCP protocol (initialize, tools/list, tools/call) ✨
  • Working examples for all features
  • Comprehensive RSpec test suite
  • Session forking support
  • Agent definitions support
  • Setting sources control
  • Partial messages streaming support
  • Streaming input support (Enumerator-based prompts) ✨
  • MCP resource support (Data sources for SDK servers) ✨
  • MCP prompt support (Reusable prompt templates) ✨

⏱️ Not Yet Implemented

All major features have been implemented! The SDK has complete feature parity with the Python SDK.

Comparison with Python SDK

Similarities

  • Same overall architecture
  • Same message types and structures
  • Same control protocol
  • Same hook and permission callback concepts
  • Similar API design

Differences

Feature Python Ruby
Async runtime anyio async gem
Process management Async subprocess Open3 (built-in)
Message queue MemoryObjectStream Async::Queue
Synchronization anyio.Event Async::Condition
Type hints Yes (TypedDict, dataclass) No (uses regular classes)
MCP integration Full with mcp package Full SDK MCP support
Async iterators Built-in Uses blocks/enumerators

Usage Comparison

Python

import anyio
from claude_agent_sdk import query, ClaudeAgentOptions

async def main():
    async for message in query(prompt="Hello"):
        print(message)

anyio.run(main)

Ruby

require 'claude_agent_sdk'
require 'async'

Async do
  ClaudeAgentSDK.query(prompt: "Hello") do |message|
    puts message
  end
end.wait

Dependencies

  • async (~2.0) - Async I/O runtime for concurrent operations
  • Ruby 3.2+ required
  • No external process management gems needed (uses built-in Open3)

Testing

The SDK includes a comprehensive RSpec test suite (unit + integration). See spec/README.md for how to run and extend it.

Run tests with:

bundle exec rspec                    # Run all tests
bundle exec rspec --format documentation  # Detailed output
PROFILE=1 bundle exec rspec         # Show slowest tests

Future Enhancements

  1. Additional MCP Features

    • MCP server lifecycle management (start/stop/restart)
    • Resource subscriptions (watch for changes)
    • Sampling support
  2. Additional Features

    • Connection pooling for multiple queries
    • Session persistence and replay
  3. Performance

    • Optimize message parsing
    • Better async task management
    • Lazy loading of optional components
  4. Developer Experience

    • Debug logging support
    • Type documentation (YARD)
    • More examples and tutorials
    • Performance profiling tools

SDK MCP Server Implementation

The Ruby SDK now includes full support for in-process MCP servers, allowing developers to define custom tools that run directly in their Ruby applications.

Example Usage

# Define a tool
add_tool = ClaudeAgentSDK.create_tool('add', 'Add numbers', { a: :number, b: :number }) do |args|
  result = args[:a] + args[:b]
  { content: [{ type: 'text', text: "Result: #{result}" }] }
end

# Create server
server = ClaudeAgentSDK.create_sdk_mcp_server(name: 'calc', tools: [add_tool])

# Use with Claude
options = ClaudeAgentOptions.new(
  mcp_servers: { calc: server },
  allowed_tools: ['mcp__calc__add']
)

Architecture

The SDK MCP implementation consists of:

  1. SdkMcpServer - Manages tool registry and execution
  2. create_tool - Helper for defining tools with schemas
  3. Query integration - Routes MCP messages to server instances
  4. JSON schema conversion - Converts Ruby types to JSON schemas

Conclusion

The Ruby SDK successfully implements complete feature parity with the Python SDK, including the advanced SDK MCP server functionality. The implementation prioritizes correctness and maintainability while following Ruby idioms and conventions.

Project Statistics

  • Core implementation: ~1,700 lines of production code
  • Test suite: RSpec coverage for major components
  • Examples: Multiple runnable scripts under examples/
  • Documentation: Slim README + 9 topic subpages in docs/, CHANGELOG, and implementation guide
  • Dependencies: Minimal (only async gem + Ruby stdlib)
  • Ruby version: 3.2+ required

Key Achievements

✅ Full bidirectional communication with Claude Code CLI ✅ Complete SDK MCP server support for in-process tools ✅ Comprehensive hook and permission callback system ✅ Production-ready with a comprehensive test suite ✅ Zero external dependencies for subprocess management (uses Open3) ✅ Clean, idiomatic Ruby code following community conventions

The SDK is production-ready and actively maintained as an unofficial, community-driven project.