Adding Tools to Matimo
Tools in Matimo are organized as independent npm packages in the packages/ directory. This guide walks you through creating and publishing a new tool provider.
Overview
Section titled “Overview”Each tool provider:
- Is published to npm as
@matimo/{provider}(e.g.,@matimo/slack,@matimo/github) - Can be installed via CLI:
matimo install {provider} - Is auto-discovered when installed in
node_modules/@matimo/* - Contains YAML tool definitions that work across all patterns
Tool Naming Convention
Matimo supports bothkebab-caseandsnake_casetool names for backward compatibility:
- Legacy tools (GitHub, Gmail, older Slack):
kebab-case(e.g.,gmail-send-email,github-create-issue)- Modern tools (Bruno, meta-tools, newer providers):
snake_case(e.g.,bruno_run_request,matimo_create_tool)- Recommended for new tools:
snake_casefollowing the pattern{provider}_{action}(e.g.,stripe_create_payment,notion_create_page)- Both naming styles work identically in all SDK patterns, filters need to check both:
.startsWith('slack-') || .startsWith('slack_')
Step 1: Create a New Tool Provider Package
Section titled “Step 1: Create a New Tool Provider Package”# Create provider directorymkdir packages/githubcd packages/github
# Create package.jsoncat > package.json << 'EOF'{ "name": "@matimo/github", "version": "0.1.0", "description": "GitHub repository and issue management tools for Matimo", "license": "MIT", "type": "module", "files": ["tools", "skills", "README.md", "definition.yaml"], "peerDependencies": { "matimo": "workspace:*" }, "devDependencies": { "@matimo/core": "workspace:*" }}EOF
# Create tool directoriesmkdir -p tools/create-issuemkdir -p tools/list-repositoriesStep 2: Define Your Tools as YAML
Section titled “Step 2: Define Your Tools as YAML”Each tool gets its own directory with a definition.yaml file.
Example: GitHub Create Issue
Section titled “Example: GitHub Create Issue”Create packages/github/tools/create-issue/definition.yaml:
name: github-create-issuedescription: Create a new issue in a GitHub repositoryversion: '1.0.0'
parameters: owner: type: string required: true description: GitHub repository owner repo: type: string required: true description: GitHub repository name title: type: string required: true description: Issue title body: type: string required: false description: Issue description
execution: type: http method: POST url: https://api.github.com/repos/{owner}/{repo}/issues headers: Authorization: 'Bearer {GITHUB_TOKEN}' Accept: application/vnd.github.v3+json body: title: '{title}' body: '{body}'
authentication: type: api_key location: header name: Authorization
output_schema: type: object properties: id: type: number description: Issue ID number: type: number description: Issue number title: type: string description: Issue title url: type: string description: Issue URL required: - id - number - titleTool Definition Structure
Section titled “Tool Definition Structure”Required fields:
name- Unique tool identifier (lowercase; see naming)description- What the tool doesversion- Semantic versionparameters- Input schema with types, required flagsexecution- How to execute (type, method, url/command, etc.)
Optional fields:
authentication- Auth method (api_key, bearer, oauth2, basic)output_schema- Response validation schemaerror_handling- Retry policy. Accepted and stored, but not applied by the executors yetexamples- Usage examples
See Tool Specification for complete schema.
Step 3: Add Provider Configuration (OAuth2 only)
Section titled “Step 3: Add Provider Configuration (OAuth2 only)”A definition.yaml at the package root is a provider manifest. It is not a tool and it does not list your tools (Matimo finds them in tools/). Add one only if your tools use OAuth2 and you want OAuth2Handler to know the authorization and token URLs. This is the shape @matimo/github ships:
name: github-providertype: providerversion: '1.0.0'description: GitHub OAuth2 provider configuration
provider: name: github displayName: GitHub endpoints: authorizationUrl: https://github.com/login/oauth/authorize tokenUrl: https://github.com/login/oauth/access_tokenA tool refers to it with authentication: { type: oauth2, provider: github }. Tools that take an API key or token in a header (see Tool Specification) don’t need a manifest.
Step 4: Test & Validate
Section titled “Step 4: Test & Validate”From typescript/ (the pnpm workspace; Python: make validate-tools from python/):
# Validate all tool definitions (YAML syntax + schema + governance rules)pnpm validate-tools
# Run testspnpm test
# Build the packagepnpm buildValidation Checks:
Section titled “Validation Checks:”- YAML syntax is valid
- All required fields present (
name,description,version,execution) - Parameter types are valid (string, number, boolean, etc.)
- Execution config is complete
- HTTP
DELETEtools declarerequires_approval: true - Function tools declare
risk: low | medium | high | critical
The validator does not call the API, so it cannot tell whether output_schema matches real responses, cover that in tests.
Step 5: Publish to npm
Section titled “Step 5: Publish to npm”# Build everythingpnpm build
# Publish the provider packagenpm publish packages/github --access public
# Or for pre-release versionsnpm publish packages/github --access public --tag alphaPublishing checklist:
- Package name starts with
@matimo/ - Version number in
package.jsonupdated - All YAML files validate
- Tests pass
- Documentation in provider README
- CHANGELOG updated
Step 6: Users Install and Auto-Discover
Section titled “Step 6: Users Install and Auto-Discover”Installation
Section titled “Installation”# Install via CLImatimo install github
# Or directly with npmnpm install @matimo/githubAuto-Discovery
Section titled “Auto-Discovery”Users can auto-discover all installed tool packages:
import { MatimoInstance } from 'matimo';
// Auto-discover finds all @matimo/* packages in node_modulesconst matimo = await MatimoInstance.init({ autoDiscover: true });
// github-create-issue is now available!const result = await matimo.execute('github-create-issue', { owner: 'tallclub', repo: 'matimo', title: 'Amazing new feature', body: 'This would be a great addition!',});
const issue = (result as { data: { number: number; html_url: string } }).data;console.log(`Issue #${issue.number} created: ${issue.html_url}`);Auto-Discovery Mechanism
Section titled “Auto-Discovery Mechanism”When autoDiscover: true:
- Scans
node_modules/@matimo/for installed packages - Discovers
tools/directories in each package - Loads all YAML definitions from those directories
- Validates each definition against the tool schema
- Registers tools in the tool registry
- Exposes them via
matimo.listTools(),matimo.getTool(),matimo.execute()
const matimo = await MatimoInstance.init({ autoDiscover: true });
// Finds and registers:// node_modules/@matimo/slack/tools/* → Slack tools// node_modules/@matimo/github/tools/* → GitHub tools// node_modules/@matimo/notion/tools/* → Notion tools// ... any other @matimo/* packages
console.log(matimo.listTools()); // All tools from all packagesTool Discovery Features
Section titled “Tool Discovery Features”List Installed Packages
Section titled “List Installed Packages”matimo listOutput:
Installed Matimo Packages:
@matimo/slack Slack workspace tools Tools: slack_send_channel_message, slack-list-channels, slack_get_channel_history, ...
@matimo/github GitHub repository and issue management tools Tools: github-create-issue, github-list-repositories, github-get-issue, ...
Total: 2 packages installedSearch for Tools
Section titled “Search for Tools”matimo search emailOutput:
Results: @matimo/gmail Gmail email tools Tools: gmail-send-email, gmail-list-messages, gmail-create-draft, ...Install Multiple Packages
Section titled “Install Multiple Packages”matimo install twilio notion hubspotDirectory Structure
Section titled “Directory Structure”packages/github/├── tools/│ ├── create-issue/│ │ ├── definition.yaml # Tool definition│ │ └── (index.ts - optional) # Custom logic if needed│ ├── list-repositories/│ │ └── definition.yaml│ └── get-issue/│ └── definition.yaml├── definition.yaml # Provider manifest (OAuth2 endpoints), optional├── package.json # npm package config├── tsconfig.json # TypeScript config (if needed)└── README.md # Provider documentationExecution Types
Section titled “Execution Types”Command Execution
Section titled “Command Execution”For shell commands with parameter templating:
execution: type: command command: node args: - scripts/process.js - '{param1}' - '{param2}' timeout: 30000Only args are templated; command must be a fixed executable. Every call to a command tool asks for approval unless the YAML says requires_approval: false. The process gets the parent environment plus per-call credentials (an execution.env map works in Python only).
HTTP Execution
Section titled “HTTP Execution”For REST APIs with authentication:
execution: type: http method: POST url: https://api.example.com/items headers: Authorization: 'Bearer {API_TOKEN}' Content-Type: application/json body: name: '{name}' description: '{description}' timeout: 15000Function Execution
Section titled “Function Execution”Matimo supports two patterns for function execution:
1. Recommended: External TypeScript/JavaScript Files (Trusted)
Section titled “1. Recommended: External TypeScript/JavaScript Files (Trusted)”execution: type: function code: ./handler.ts # Path to external .ts or .js file timeout: 10000The referenced file should export a default async function. Its optional second argument holds the call’s credentials and the caller’s policy context ({ credentials?, policyContext? }), set by the host, never by the agent:
import type { FunctionToolContext } from '@matimo/core';
export default async function handler(params: Record<string, unknown>, context?: FunctionToolContext) { const { query } = params; // context?.policyContext?.agentId, roles, environment return { result: 'success' };}Declare risk: on every function tool, the validator rejects one without it. The Python SDK runs handler.py next to the YAML: def run(params, context=None) (sync or async). See Tool Specification.
Security: Recommended. Code is version-controlled, reviewable, and cannot be injected via YAML.
2. Legacy: Embedded Code (Disabled by Default)
Section titled “2. Legacy: Embedded Code (Disabled by Default)”execution: type: function code: | async (params) => { return { result: params.value }; } timeout: 10000SECURITY WARNING: Embedded code execution is disabled by default because it:
- Runs arbitrary JavaScript with access to
fs,path,axiosmodules - Creates an RCE (Remote Code Execution) vector if YAML comes from untrusted sources
- Cannot be audited without parsing the YAML
How it works:
// Logic in function-executor.tsconst embeddedCodeDisabled = process.env.MATIMO_ALLOW_EMBEDDED_CODE !== 'true';
// If MATIMO_ALLOW_EMBEDDED_CODE is NOT set to 'true' → disabled (default)// If MATIMO_ALLOW_EMBEDDED_CODE = 'true' → enabled (opt-in)if (embeddedCodeDisabled) { throw new MatimoError('Embedded code execution is disabled by default...');}To enable (NOT recommended):
export MATIMO_ALLOW_EMBEDDED_CODE=trueOnly enable if you fully trust all tool YAML sources. Never enable in production without careful review.
Authentication
Section titled “Authentication”Tools support multiple authentication methods:
API Key
Section titled “API Key”authentication: type: api_key location: header name: Authorization # Loads from env: MATIMO_GITHUB_API_KEY or GITHUB_API_KEYBearer Token
Section titled “Bearer Token”authentication: type: bearer location: header # Loads from env: MATIMO_GITHUB_TOKEN or GITHUB_TOKENOAuth2
Section titled “OAuth2”authentication: type: oauth2 provider: github scopes: - repo - issuesBasic Auth
Section titled “Basic Auth”authentication: type: basic # Loads from env: MATIMO_GITHUB_USERNAME, MATIMO_GITHUB_PASSWORDBest Practices
Section titled “Best Practices”Naming
Section titled “Naming”- Package name:
@matimo/{provider}(e.g.,@matimo/slack) - Tool name:
{provider}-{action}(e.g.,slack-send-message) - Parameter names: lowercase, hyphens for multi-word
Parameters
Section titled “Parameters”parameters: channel_id: type: string required: true description: Slack channel ID (e.g., C123456) enum: [] # Optional: restrict to specific valuesError Handling
Section titled “Error Handling”You can declare retry settings so the intent is recorded with the tool, but neither SDK applies them yet: a failed call is not retried. Handle RATE_LIMIT_EXCEEDED and retryable errors in the caller (see Errors).
error_handling: retry: 2 # accepted, not yet applied backoff_type: exponential # exponential, linear or fixed initial_delay_ms: 1000Output Schema
Section titled “Output Schema”Validate responses to catch API changes:
output_schema: type: object properties: id: type: number created_at: type: string format: date-time required: - idExamples in Repository
Section titled “Examples in Repository”See these working examples:
- Slack tools:
packages/slack/tools/ - Gmail tools:
packages/gmail/tools/ - Calculator tool:
packages/core/tools/
Publishing Your Tools
Section titled “Publishing Your Tools”Steps:
Section titled “Steps:”- Ensure tests pass:
pnpm test - Validate tools:
pnpm validate-tools - Update version in
package.json - Build:
pnpm build - Publish:
npm publish packages/github --access public
Naming Convention:
Section titled “Naming Convention:”Always use @matimo/{provider} scope for consistency and discoverability.
Getting Help
Section titled “Getting Help”- See Tool Specification for complete schema
- Open an issue on GitHub
- Check Architecture Overview for system design
Contributing Your Tools
Section titled “Contributing Your Tools”We’d love to include your tools in the ecosystem! Submit a PR with:
- Tool definitions in
packages/{provider}/ - Tests for your tools
- Documentation in provider README
- Examples showing how to use them
See Contributing Guidelines for details.