View on GitHub

Matimo - AI Tools Ecosystem

Define tools once in YAML, use them everywhere

Download this project as a .zip file Download this project as a tar.gz file

Tool Development Workflow

Complete step-by-step process for creating and submitting a new Matimo tool.


Overview

1. Create Tool Directory
        ↓
2. Write definition.yaml
        ↓
3. Validate Syntax
        ↓
4. Implement Tool Logic (if needed)
        ↓
5. Write Tests
        ↓
6. Add Examples
        ↓
7. Validate Coverage
        ↓
8. Submit PR

Total time: 30 minutes (simple tool) to 2 hours (full provider package)


Step 1: Create Tool Structure

For a Simple Tool (in core)

# Create directory
mkdir -p packages/core/tools/{tool-name}

# Create YAML definition
touch packages/core/tools/{tool-name}/definition.yaml

For a Provider Package (npm)

# Create provider structure
mkdir -p packages/{provider}/tools/{tool-name}

# Create YAML definition
touch packages/{provider}/tools/{tool-name}/definition.yaml

Directory structure:

packages/core-or-provider/
├── tools/
│   └── my-tool/
│       ├── definition.yaml          # Tool definition (YAML)
│       ├── index.ts                 # Implementation (optional)
│       └── README.md                # Usage docs (optional)

Step 2: Write definition.yaml

See full specification: TOOL_SPECIFICATION.md

Minimal example (command-based):

name: my-tool
description: Brief description of what the tool does
version: '1.0.0'

parameters:
  input:
    type: string
    required: true
    description: Input parameter

execution:
  type: command
  command: node
  args:
    - -e
    - |
      console.log(JSON.stringify({ result: process.argv[1].toUpperCase() }));
    - '{input}'

output_schema:
  type: object
  properties:
    result:
      type: string
  required: [result]

For HTTP tools:

name: api-tool
execution:
  type: http
  method: POST
  url: https://api.example.com/endpoint
  headers:
    Authorization: 'Bearer {API_TOKEN}'
  body:
    param: '{param_value}'

Step 3: Validate Syntax

Before writing tests, validate your YAML:

# Validate all tools
pnpm validate-tools

# What it checks:
# ✅ Valid YAML syntax
# ✅ Matches ToolDefinition schema
# ✅ All required fields present
# ✅ Parameter types are valid
# ✅ Output schema is valid

Expected output:

✅ Validating tools...
  ✅ packages/core/tools/my-tool/definition.yaml
  ✅ packages/slack/tools/slack-send-message/definition.yaml
  ...
✅ All tools valid!

If validation fails:

❌ Error in packages/core/tools/my-tool/definition.yaml:
   Missing required field: "name"

❌ Error in packages/slack/tools/slack-api/definition.yaml:
   Unknown execution type: "webhook" (valid: command, http, function)

Fix and re-validate immediately.


Step 4: Implement Tool Logic (If Needed)

For Command-Based Tools

If your tool type is command, you may need an executable file.

Simple implementation (inline in YAML):

execution:
  type: command
  command: node
  args:
    - -e
    - |
      // JS code inline
      const result = process.argv[1].toUpperCase();
      console.log(JSON.stringify({ result }));
    - '{input}'

Complex implementation (separate file):

Create packages/{provider}/tools/{tool-name}/index.ts:

// Receives args from YAML, outputs JSON to stdout
const input = process.argv[1];
const result = await processInput(input);
console.log(JSON.stringify(result));

async function processInput(data: string) {
  // Your logic here
  return { result: data.toUpperCase() };
}

Then reference in YAML:

execution:
  type: command
  command: tsx
  args:
    - packages/provider/tools/my-tool/index.ts
    - '{input}'

For HTTP Tools

No implementation needed — just define the HTTP request in YAML.

For Function-Based Tools

Not yet supported, but coming in Phase 2.


Step 5: Write Tests

Create Test Fixture

Tests use YAML fixtures to validate tools. Create:

File: packages/core/test/fixtures/{tool-category}/{tool-name}-fixture.yaml

# Copy of your tool definition or a test variant
name: my-tool
description: My test tool
version: '1.0.0'

parameters:
  input:
    type: string
    required: true
    description: Test input

execution:
  type: command
  command: node
  args:
    - -e
    - |
      console.log(JSON.stringify({ result: process.argv[1].toUpperCase() }));
    - '{input}'

output_schema:
  type: object
  properties:
    result:
      type: string
  required: [result]

Write Unit Tests

File: packages/core/test/unit/tools/{tool-name}.test.ts

import { MatimoInstance } from '../../src/matimo-instance';

describe('MyTool', () => {
  let matimo: MatimoInstance;

  beforeAll(async () => {
    matimo = await MatimoInstance.init('./packages/core/tools');
  });

  it('should execute with valid parameters', async () => {
    const result = await matimo.execute('my-tool', {
      input: 'test',
    });
    expect(result).toHaveProperty('result');
    expect(result.result).toBe('TEST');
  });

  it('should fail with missing required parameter', async () => {
    await expect(matimo.execute('my-tool', {})).rejects.toThrow();
  });

  it('should validate output schema', async () => {
    const result = await matimo.execute('my-tool', {
      input: 'hello',
    });
    // Output validation happens automatically
    expect(typeof result.result).toBe('string');
  });
});

Test template:

describe('ToolName', () => {
  // Test basic execution
  // Test with invalid params
  // Test with edge cases
  // Test output validation
  // Test error handling (if HTTP, test error codes)
});

Step 6: Add Examples

Add your tool to examples/tools/ if it’s a provider tool.

Example: examples/tools/{provider}/{tool-name}.ts

import { MatimoInstance } from '@matimo/core';

async function main() {
  const matimo = await MatimoInstance.init({ autoDiscover: true });

  console.log('Executing my-tool...');
  const result = await matimo.execute('my-tool', {
    input: 'Hello World',
  });

  console.log('Result:', result);
}

main().catch(console.error);

Step 7: Validate Coverage

Before submitting PR, ensure tests pass:

# Run tests
pnpm test

# Check coverage (target: 95%+)
pnpm test:coverage

# View coverage report
open coverage/lcov-report/index.html

For your tool, aim for:


Full Checklist Before PR

Before submitting a pull request, verify:

YAML & Validation

Code Quality

Testing

Documentation

Security

Logging


Step 8: Submit PR

Branch & Commit

# Create feature branch
git checkout -b feat/my-tool-description

# Stage changes
git add packages/core/tools/my-tool/

# Commit with conventional message
git commit -m "feat(core): add my-tool for X functionality"

# Push
git push origin feat/my-tool-description

PR Description Template

## Description

What does this tool do? Why is it useful?

## Changes

- Added my-tool definition
- Implemented core logic
- Added comprehensive tests
- Created usage examples

## Tool Details

- **Name:** my-tool
- **Type:** command/http/function
- **Authentication:** none/api_key/oauth2
- **Parameters:** [list them]

## Testing

- [x] All tests pass (`pnpm test`)
- [x] Coverage 95%+ (`pnpm test:coverage`)
- [x] YAML validates (`pnpm validate-tools`)
- [x] Lint passes (`pnpm lint`)

## Example Usage

```typescript
const result = await matimo.execute('my-tool', {
  param: 'value',
});
```

Closes #123

Checklist


### What to Expect

1. **Automated checks run:**
   - Tests (must all pass)
   - Linting (auto-fixed if needed)
   - Coverage (must be 95%+)

2. **Maintainer review:**
   - Code quality feedback
   - Documentation suggestions
   - Security considerations

3. **Merge:**
   - Once approved, tool is merged
   - Published with next release

---

## Troubleshooting

### "pnpm validate-tools fails"

**Check:**
```bash
# See actual error
pnpm validate-tools 2>&1

# Common issues:
# - Missing required field (name, execution, output_schema)
# - Invalid YAML syntax
# - Parameter type mismatched

Fix:


“Tests don’t find my tool”

Check:

# Verify tool loads
pnpm test -- --testPathPattern="tool-loader"

# Tool must be in:
# packages/core/tools/{name}/definition.yaml
# packages/{provider}/tools/{name}/definition.yaml

“Coverage too low”

Add tests for:

// Example: Test error scenario
it('should handle network errors gracefully', async () => {
  // If HTTP tool, mock failed request
  const result = await matimo.execute('my-tool', { bad: 'param' });
  // Should throw MatimoError with ERROR_CODE
});

“Lint/Format issues”

Auto-fix:

pnpm lint:fix
pnpm format
git add .
git commit --amend

Real Example: Adding git-clone Tool

# 1. Create structure
mkdir -p packages/github/tools/github-clone-repo

# 2. Write definition.yaml
cat > packages/github/tools/github-clone-repo/definition.yaml << 'EOF'
name: github-clone-repo
description: Clone a GitHub repository to a local directory
version: '1.0.0'

parameters:
  owner:
    type: string
    required: true
    description: Repository owner (username or org)
  repo:
    type: string
    required: true
    description: Repository name
  path:
    type: string
    required: false
    description: Local directory to clone into

execution:
  type: command
  command: bash
  args:
    - -c
    - |
      if [ -z "{path}" ]; then
        git clone https://github.com/{owner}/{repo}.git
      else
        git clone https://github.com/{owner}/{repo}.git {path}
      fi
      echo '{"status":"cloned"}'

output_schema:
  type: object
  properties:
    status:
      type: string
  required: [status]
EOF

# 3. Validate
pnpm validate-tools

# 4. Write tests
cat > packages/core/test/unit/tools/github-clone.test.ts << 'EOF'
describe('GitHubClone', () => {
  it('should clone repository', async () => {
    // Mock git command in test
  });
});
EOF

# 5. Run tests
pnpm test

# 6. Check coverage
pnpm test:coverage

# 7. Submit PR
git add packages/github/tools/github-clone-repo/
git commit -m "feat(github): add github-clone-repo tool"

Next Steps

Questions?