Skip to content

Contributing โ€‹

Help improve Go Bananas! for everyone.

Welcome Contributors! โ€‹

Go Bananas! is an open-source project and we welcome contributions of all kinds:

  • ๐Ÿ› Bug reports and fixes
  • โœจ New features
  • ๐Ÿ“– Documentation improvements
  • ๐Ÿงช Test coverage
  • ๐Ÿ’ก Ideas and feedback

Getting Started โ€‹

Prerequisites โ€‹

  • Node.js 24
  • npm or pnpm
  • Cloudflare account (for testing deployments)
  • Git

Development Setup โ€‹

bash
# Clone the repository
git clone https://github.com/davendra/go-bananas-app.git
cd go-bananas-app

# Install dependencies
npm install

# Copy environment template
cp .env.example .env

# Start development server
npm run dev

Running Tests โ€‹

bash
# Type checking
npm run typecheck

# Unit tests
npm test

# All tests
npm run test:all

Contribution Workflow โ€‹

1. Find or Create an Issue โ€‹

  • Check existing issues
  • For new features, open a discussion first
  • For bugs, include steps to reproduce

2. Fork and Branch โ€‹

bash
# Fork on GitHub, then clone
git clone https://github.com/your-username/go-bananas.git

# Create feature branch
git checkout -b feature/your-feature-name

3. Make Changes โ€‹

  • Follow the code style guide
  • Add tests for new functionality
  • Update documentation as needed

4. Test Locally โ€‹

bash
npm run typecheck
npm test
npm run dev  # Manual testing

5. Commit and Push โ€‹

bash
# Commit with conventional commit format
git commit -m "feat: add new feature description"

# Push to your fork
git push origin feature/your-feature-name

6. Open Pull Request โ€‹

  • Use the PR template
  • Link related issues
  • Wait for review

Code Style โ€‹

TypeScript โ€‹

  • Use TypeScript strict mode
  • Prefer interface over type for objects
  • Use explicit return types for functions
  • Avoid any - use unknown if type is uncertain
typescript
// Good
interface ImageMetadata {
  id: number;
  width: number;
  height: number;
}

async function processImage(data: unknown): Promise<ImageMetadata> {
  // Implementation
}

// Avoid
async function processImage(data: any) {
  // Implementation
}

Formatting โ€‹

We use Prettier with default settings:

bash
# Format all files
npx prettier --write .

# Check formatting
npx prettier --check .

Naming Conventions โ€‹

TypeConventionExample
Fileskebab-caseimage-storage.ts
ClassesPascalCaseImageStorageService
FunctionscamelCaseprocessImage
ConstantsUPPER_SNAKEMAX_IMAGE_SIZE
InterfacesPascalCaseImageMetadata

Project Structure โ€‹

go-bananas/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ index.ts           # Worker entry point
โ”‚   โ”œโ”€โ”€ mcp/
โ”‚   โ”‚   โ””โ”€โ”€ agent.ts       # MCP Durable Object
โ”‚   โ”œโ”€โ”€ tools/             # MCP tool implementations
โ”‚   โ”œโ”€โ”€ api/               # REST API endpoints
โ”‚   โ”œโ”€โ”€ auth/              # Authentication
โ”‚   โ”œโ”€โ”€ services/          # Business logic
โ”‚   โ””โ”€โ”€ types/             # TypeScript definitions
โ”œโ”€โ”€ frontend/              # React console
โ”œโ”€โ”€ migrations/            # Database migrations
โ”œโ”€โ”€ tests/                 # Test files
โ”œโ”€โ”€ doc-site/              # This documentation
โ””โ”€โ”€ scripts/               # Utility scripts

Adding a New MCP Tool โ€‹

1. Create Tool File โ€‹

typescript
// src/tools/my-new-tool.ts
import { z } from 'zod';
import { trackUsage } from '../services/usage';
import { Env } from '../types';

// 1. Define input schema
export const MyNewToolInputSchema = z.object({
  param1: z.string().min(1).describe('First parameter'),
  param2: z.number().optional().describe('Optional parameter'),
});

export type MyNewToolInput = z.infer<typeof MyNewToolInputSchema>;

// 2. Define result type
export interface MyNewToolResult {
  success: boolean;
  data: any;
}

// 3. Implement handler
export async function myNewTool(
  env: Env,
  tenantId: string,
  sessionId: string,
  geminiApiKey: string,
  params: MyNewToolInput
): Promise<MyNewToolResult> {
  const startTime = Date.now();

  try {
    // Validate input
    const input = MyNewToolInputSchema.parse(params);

    // Implement logic
    const result = await doSomething(input);

    // Track usage
    await trackUsage(env.DB, {
      tenantId,
      sessionId,
      operation: 'my_new_tool',
      imagesGenerated: 0,
      durationMs: Date.now() - startTime,
    });

    return { success: true, data: result };
  } catch (error) {
    await trackUsage(env.DB, {
      tenantId,
      sessionId,
      operation: 'my_new_tool_failed',
      durationMs: Date.now() - startTime,
    });
    throw error;
  }
}

// 4. Export tool definition
export function getMyNewToolDefinition() {
  return {
    name: 'my_new_tool',
    description: 'What this tool does',
    inputSchema: MyNewToolInputSchema,
  };
}

2. Register in Agent โ€‹

typescript
// src/mcp/agent.ts
import { myNewTool, MyNewToolInputSchema } from '../tools/my-new-tool';

// In constructor
this.registerMyNewTool();

// Add registration method
private registerMyNewTool() {
  this.server.tool(
    'my_new_tool',
    'What this tool does',
    MyNewToolInputSchema.shape,
    async (params) => this.handleMyNewTool(params)
  );
}

// Add handler
private async handleMyNewTool(params: unknown) {
  const result = await myNewTool(
    this.env,
    this.tenant.tenantId,
    this.sessionId,
    this.tenant.geminiApiKey,
    params as MyNewToolInput
  );
  return {
    content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
  };
}

3. Add Tests โ€‹

typescript
// tests/my-new-tool.test.ts
import { describe, it, expect } from 'vitest';
import { MyNewToolInputSchema, myNewTool } from '../src/tools/my-new-tool';

describe('myNewTool', () => {
  it('validates input correctly', () => {
    const valid = MyNewToolInputSchema.parse({ param1: 'test' });
    expect(valid.param1).toBe('test');
  });

  it('rejects invalid input', () => {
    expect(() => MyNewToolInputSchema.parse({})).toThrow();
  });
});

4. Update Documentation โ€‹

  • Add to MCP tools documentation
  • Update tool count in CLAUDE.md
  • Add examples in README

Testing Guidelines โ€‹

Unit Tests โ€‹

Test individual functions in isolation:

typescript
import { describe, it, expect, vi } from 'vitest';

describe('functionName', () => {
  it('should handle normal case', () => {
    const result = functionName(input);
    expect(result).toBe(expected);
  });

  it('should handle edge case', () => {
    // Test edge cases
  });

  it('should throw on invalid input', () => {
    expect(() => functionName(invalid)).toThrow();
  });
});

Integration Tests โ€‹

Test API endpoints and database interactions:

typescript
import { unstable_dev } from 'wrangler';

describe('API Integration', () => {
  let worker: UnstableDevWorker;

  beforeAll(async () => {
    worker = await unstable_dev('src/index.ts', {
      experimental: { disableExperimentalWarning: true },
    });
  });

  afterAll(async () => {
    await worker.stop();
  });

  it('returns images list', async () => {
    const resp = await worker.fetch('/api/images', {
      headers: { 'X-API-Key': 'sk_test_xxx' },
    });
    expect(resp.status).toBe(200);
  });
});

Documentation โ€‹

Writing Documentation โ€‹

  • Use VitePress markdown
  • Include code examples
  • Add Mermaid diagrams for complex flows
  • Use admonitions for important notes
markdown
::: tip
This is a helpful tip.
:::

::: warning
This is a warning message.
:::

::: danger
This is a dangerous operation.
:::

Testing Documentation โ€‹

bash
cd doc-site
npm install
npm run dev

Pull Request Guidelines โ€‹

PR Title Format โ€‹

Use conventional commits:

  • feat: - New feature
  • fix: - Bug fix
  • docs: - Documentation only
  • test: - Adding tests
  • refactor: - Code refactoring
  • chore: - Maintenance tasks

PR Checklist โ€‹

  • [ ] Tests added/updated
  • [ ] Documentation updated
  • [ ] TypeScript types checked
  • [ ] No console.log statements
  • [ ] Follows code style

Review Process โ€‹

  1. Automated checks must pass
  2. At least one maintainer review
  3. All comments addressed
  4. Squash and merge

Community โ€‹

Communication โ€‹

  • GitHub Issues - Bug reports and features
  • Discussions - Questions and ideas
  • Pull Requests - Code contributions

Code of Conduct โ€‹

Be respectful and inclusive. We follow the Contributor Covenant.

Next Steps โ€‹

Released under the MIT License.