| name | tool-creation |
| description | Create tools for the Matimo SDK. Understand YAML tool definitions, execution types, authentication, parameter validation, and quality standards. Apply this skill when implementing Matimo tools. |
| metadata | {"category":"Tool Development","difficulty":"intermediate","user-invokable":"true"} |
Tool Creation for Matimo SDK
This skill teaches you how to properly create, configure, and validate tools for the Matimo SDKโa configuration-driven framework where tools are defined once in YAML and executed everywhere (factory pattern, decorators, LangChain).
Two audiences โ know which one you are:
| You are an Agent at runtime | You are an SDK developer |
|---|
Creating tools dynamically via matimo_create_tool | Adding tools to the SDK codebase |
Tools go to ./matimo-tools/{tool-name}/definition.yaml (configured by developer) | Tools go to packages/{provider}/tools/{tool-name}/definition.yaml |
Validate with matimo_doctor (meta-tool) | Validate with pnpm validate-tools (CLI) |
Approval needed: matimo_approve_tool โ matimo_reload_tools | No approval flow โ merged via git |
If you are an agent using meta-tools, follow the "Agent at runtime" column throughout this skill.
Matimo Architecture Overview
Tool Execution Flow
Tool Definition (YAML)
โ
ToolLoader (parse & validate)
โ
ToolRegistry (in-memory store)
โ
MatimoInstance.execute(name, params)
โ
Select Executor Based on execution.type
โ
CommandExecutor | HttpExecutor | FunctionExecutor
โ
Validate output against output_schema
โ
Return structured result
Key Concepts
- Tools are YAML-first: Define once in YAML, execute anywhere
- Always include
requires_approval: true: Required by policy for all agent-created tools
- Default execution type is
http: command and function types are blocked by policy by default โ only use http unless the developer has explicitly enabled the others
- Parameter templating: Use
{paramName} syntax for dynamic values
- Authentication: Provider-agnostic (API key, bearer, basic, OAuth2)
- Output validation: All responses validated against Zod schemas
- Error handling: Structured errors with retry/backoff policies
โ ๏ธ Policy defaults block command and function tools. Always use type: http unless you have confirmed the developer has set allowCommandTools: true or allowFunctionTools: true in the policy. Creating a command or function tool without explicit policy permission will fail validation.
File Structure
Agent at runtime (using matimo_create_tool):
{target_dir}/{tool-name}/ โ target_dir is configured by the developer
โโโ definition.yaml โ Tool YAML โ written by meta-tool
Default target_dir is ./matimo-tools unless overridden. The approved tool stays in this path permanently โ it is NOT cleared on restart. The ApprovalManifest (.matimo-approvals.json) is stored in the same directory.
SDK developer (adding to codebase):
packages/{provider}/tools/{tool-name}/
โโโ definition.yaml โ Tool configuration
โโโ index.ts โ Executor script (if type: command)
Agent Tool Lifecycle (runtime creation)
matimo_doctor(yaml_content) โ 1. Validate YAML + policy FIRST
โ { valid: true }
matimo_create_tool(name, yaml_content, target_dir)
โ writes {target_dir}/{name}/definition.yaml
โ status: "draft", approvalState: "pending"
matimo_approve_tool(name, tool_dir) โ 3. Human approves
โ status updated to "approved" in same file
โ .matimo-approvals.json written to target_dir
matimo_reload_tools() โ 4. Hot-reload registry
โ approved tool is now live and executable
matimo.execute(tool-name, params) โ 5. Use the tool
โ
Approved tools are stored permanently in target_dir on disk. They survive restarts as long as target_dir is included in Matimo's toolPaths config. They are NOT stored in temp directories โ that is only used in demo examples.
When Creating Tools
Apply this skill when:
- An agent needs to create a new tool at runtime (use meta-tools workflow above)
- Defining tool YAML structure (applies to both agents and SDK developers)
- Configuring authentication, parameter templating, or output validation
- An SDK developer is adding a new provider package (e.g., GitHub, AWS, Notion)
Tool Definition Structure
Minimal Valid Tool (HTTP)
name: tool_name
description: What this tool does
version: '1.0.0'
requires_approval: true
parameters:
required_param:
type: string
required: true
description: What this parameter does
execution:
type: http
method: POST
url: 'https://api.example.com/v1/resource'
headers:
Authorization: 'Bearer {AUTH_TOKEN}'
body:
field: '{required_param}'
authentication:
type: api_key
location: header
name: Authorization
output_schema:
type: object
properties:
success:
type: boolean
required:
- success
examples:
- name: "Basic usage"
params:
required_param: "value"
Execution Types
Policy Rule: By default, command and function execution types are blocked (allowCommandTools: false, allowFunctionTools: false). The safe default is type: http. Before using command or function, always run matimo_doctor to check if the policy in your environment allows it.
Type: HTTP โ
Always Allowed
For REST API integrations (Slack, GitHub, AWS, etc.):
execution:
type: http
method: POST
url: 'https://api.example.com/endpoint'
timeout_ms: 30000
headers:
Authorization: 'Bearer {API_TOKEN}'
Content-Type: application/json
query_params:
filter: '{paramName}'
body:
channel: '{channel}'
message: '{text}'
Key patterns:
- Templating:
{paramName} replaced with actual parameter values
- Auth template:
{API_TOKEN} replaced with MATIMO_{TOOL_NAME}_API_KEY env var
- Headers/query/body all support templating
- Timeouts default to 30000ms (30 seconds)
Type: Command โ Blocked by Default
Policy: allowCommandTools: false by default. Creating a command tool will fail matimo_doctor unless the developer has explicitly set allowCommandTools: true. Do not attempt to create command tools unless you have confirmed this is allowed.
If allowed by policy:
execution:
type: command
command: 'tsx'
timeout_ms: 30000
args:
- 'packages/provider/tool/executor.ts'
- '--param'
- '{paramValue}'
Key patterns:
- Command executor receives templated args
- Executor must output JSON to stdout
- Parse args with Node's
parseArgs utility
- Use
getGlobalMatimoLogger() for logging, never bare console.log
Type: Function โ Blocked by Default
Policy: allowFunctionTools: false by default. Creating a function tool will fail matimo_doctor unless the developer has explicitly set allowFunctionTools: true. Do not attempt to create function tools unless you have confirmed this is allowed.
If allowed by policy:
execution:
type: function
handler: 'execute'
code: |
export async function execute(params) {
return { success: true, data: params };
}
Key patterns:
- Handler function receives
params object
- Must return object matching
output_schema
- Can be async
- No file I/O or CLI access
Authentication Configuration
โ NEVER write placeholder text in YAML โ strings like YOUR_API_KEY, API_KEY_HERE, or replace_me are invalid. They will be sent to the API as-is and cause authentication failures.
โ
Always use {VARIABLE_NAME} template syntax. The system replaces these at execution time from environment variables.
API Key (Header)
authentication:
type: api_key
location: header
name: Authorization
Environment variable: MATIMO_{TOOL_NAME}_API_KEY
When executing, the system injects: Authorization: Bearer <key-from-env>
API Key (Query Parameter)
Some APIs (e.g., weatherapi.com, OpenWeatherMap) require the key in the URL query string, not a header:
execution:
type: http
method: GET
url: 'https://api.weatherapi.com/v1/current.json'
query_params:
key: '{WEATHER_API_KEY}'
q: '{city}'
authentication:
type: api_key
location: query
name: key
Set the key via environment variable before running: WEATHER_API_KEY=your_actual_key
โ ๏ธ Never embed the key directly in the URL string like ?key=abc123 โ use the query_params map with {VAR_NAME} templating.
Bearer Token
authentication:
type: bearer
Environment variable: MATIMO_{TOOL_NAME}_BEARER_TOKEN
Injected as: Authorization: Bearer <token>
Basic Auth
authentication:
type: basic
username_env: MATIMO_{TOOL_NAME}_USERNAME
password_env: MATIMO_{TOOL_NAME}_PASSWORD
Encodes as Authorization: Basic base64(username:password)
OAuth2
authentication:
type: oauth2
provider: google
scopes:
- 'https://www.googleapis.com/auth/drive'
OAuth configuration defined in provider's definition.yaml. System handles authorization code โ token exchange.
Parameter Definition
Basic Parameters
parameters:
name:
type: string
required: true
description: Parameter description
With Constraints
parameters:
email:
type: string
required: true
pattern: '^[^\s@]+@[^\s@]+\.[^\s@]+$'
priority:
type: string
enum:
- low
- medium
- high
timeout:
type: number
min: 1
max: 300
default: 30
port:
type: number
min: 1
max: 65535
count:
type: number
min_length: 0
max_length: 100
Parameter Templating in Execution
execution:
type: http
body:
filter: '{priority}'
count: '{count}'
tags:
- '{tag1}'
- '{tag2}'
When executing with { priority: 'high', count: 42, tag1: 'urgent', tag2: 'system' }:
{priority} โ "high"
{count} โ "42"
{tag1}, {tag2} โ array elements
Output Schema Validation
All tools must define output_schema. Responses are validated with Zod:
output_schema:
type: object
properties:
success:
type: boolean
description: Operation success
data:
type: object
description: Response data
properties:
id:
type: string
timestamp:
type: string
count:
type: number
error:
type: object
description: Error details (if failed)
required:
- success
Execution behavior:
- Tool executes (HTTP, command, or function)
- Response parsed and validated against schema
- If validation fails โ
MatimoError(INVALID_SCHEMA, ...)
- If validation succeeds โ result returned to caller
Error Handling & Retry Policies
error_handling:
retry: 3
backoff_type: exponential
initial_delay_ms: 500
max_delay_ms: 30000
retry_on_status:
- 429
- 500
- 503
Retry behavior:
- Delay =
initial_delay_ms * (backoff_exponent ^ attempt_number)
- Capped at
max_delay_ms
- Only retries on specified HTTP status codes
- Non-2xx responses still validated against schema
Command Executor Implementation
โ ๏ธ Only applicable if allowCommandTools: true is set in policy. If you are unsure, run matimo_doctor on your YAML first โ it will fail immediately if command tools are blocked.
For type: command tools, create an executor at packages/{provider}/tools/{tool-name}/index.ts:
import { parseArgs } from 'util';
import { getGlobalMatimoLogger } from '@matimo/core';
interface ExecutorParams {
channel: string;
text?: string;
}
async function main() {
const logger = getGlobalMatimoLogger();
try {
const { values } = parseArgs({
options: {
channel: { type: 'string' },
text: { type: 'string' }
}
});
if (!values.channel) {
throw new Error('channel is required');
}
const params: ExecutorParams = {
channel: values.channel as string,
text: values.text
};
const result = await executeLogic(params);
console.log(JSON.stringify(result));
} catch (error) {
logger.error('Executor failed', {
error: error instanceof Error ? error.message : String(error)
});
console.log(JSON.stringify({
success: false,
message: error instanceof Error ? error.message : String(error)
}));
process.exit(1);
}
}
async function executeLogic(params: ExecutorParams) {
return {
success: true,
message: 'Operation completed',
data: { channel: params.channel }
};
}
main();
Key guidelines:
- Always use
getGlobalMatimoLogger(), never bare console.log in core code
- Validate all parameters
- Output JSON with structure matching
output_schema
- Exit with code 1 on error
- Never log secrets
Code Quality Standards
Zod Validation
All parameter parsing must use Zod:
import { z } from 'zod';
const ParamSchema = z.object({
channel: z.string().min(1),
text: z.string().optional(),
timeout: z.number().min(1).max(300).default(30)
});
const params = ParamSchema.parse(input);
Error Handling
Use MatimoError with structured error codes:
import { MatimoError, ErrorCode } from '@matimo/core';
throw new MatimoError(
'API request failed',
ErrorCode.EXECUTION_FAILED,
{
toolName: 'slack_send',
statusCode: 500,
details: { message: 'Server error' }
}
);
Error codes:
INVALID_SCHEMA โ Parameter or response validation failed
EXECUTION_FAILED โ Tool execution failed
AUTH_FAILED โ Authentication missing or invalid
TOOL_NOT_FOUND โ Tool definition not found
Type Safety
- Use strict TypeScript (no
any type)
- Export types alongside implementations
- Use discriminated unions:
{ type: 'command', command: string }
- Never trust user input without validation
Strict Rules & Enforcement Standards
These rules are non-negotiable for all Matimo tools. Violations block tool approval.
1. Naming Conventions (STRICT)
Tool Names:
- โ
MUST use
snake_case (e.g., slack_send_message, github_create_issue)
- โ
MUST be globally unique across all providers
- โ
MUST describe the action clearly (no abbreviations like
slack_msg)
- โ NO camelCase, PascalCase, or kebab-case
- โ NO generic names like
execute or run
Parameters (YAML):
- โ
MUST use
camelCase (e.g., channelId, messageText, retryCount)
- โ
MUST be descriptive and match API conventions
- โ NO snake_case in parameter names
- โ NO single-letter parameters (except standard
a, b in math examples)
Parameters (TypeScript/JavaScript):
- โ
MUST use
camelCase for variables (e.g., const channelId = ...)
- โ
Class/function names MUST use
PascalCase (e.g., SlackClient, parseResponse())
- โ
Constants MUST use
UPPER_SNAKE_CASE (e.g., MAX_RETRIES, API_TIMEOUT_MS)
- โ
Private methods MUST use
# prefix (e.g., #validateResponse())
- โ NO inconsistent casing within a single tool
Provider Packages:
- โ
MUST use
snake_case (e.g., @matimo/slack, @matimo/github)
- โ
Directory path:
packages/{provider}/tools/{tool_name}/
- โ NO uppercase letters in directory names
Example (Correct):
name: slack_send_message
parameters:
channelId:
type: string
required: true
messageText:
type: string
2. Tool Completeness (CRITICAL)
All tools MUST be fully functional:
โ
Required Elements:
- โ๏ธ Tool definition YAML must be complete and valid
- โ๏ธ All parameters in YAML must be documented with type, description, required
- โ๏ธ All examples in YAML must match the defined parameters (no extra/missing params)
- โ๏ธ All referenced environment variables MUST be documented in description
- โ๏ธ Output schema MUST match actual API response structure
- โ๏ธ Error handling MUST cover all documented failure modes
- โ๏ธ Executor code (if
type: command) must be fully implemented
- โ๏ธ Authentication setup must be tested and working
- โ๏ธ Tool must execute without errors in all example scenarios
โ Never Allow:
- Stub implementations with TODO comments
- @TODO, FIXME, XXX, HACK comments in production code
- @ts-ignore, eslint-disable comments (fix the actual issue)
- Placeholder values in examples
- "Coming soon" features
- Incomplete authentication configuration
Completeness Checklist:
Before submitting a tool:
[ ] Tool YAML parses without errors (pnpm validate-tools)
[ ] All parameters in YAML have type + description + required
[ ] All examples match parameter definitions exactly
[ ] Output schema matches real API responses
[ ] Error handling uses MatimoError with ErrorCode
[ ] Tests pass with 80%+ coverage (pnpm test:coverage)
[ ] No console.log in main code (use logger)
[ ] No env var references without documentation
[ ] Tool executes successfully in all examples
[ ] No TODO/FIXME/XXX/HACK comments
[ ] No @ts-ignore, eslint-disable comments
3. Parameter Validation (STRICT)
Parameter Definition Requirements:
- โ
EVERY parameter MUST have
type, description, required
- โ
type must be one of: string, number, boolean, array, object
- โ
description must explain WHAT and WHY (e.g., "Slack channel ID (starts with C)")
- โ
Constraints MUST be specific:
- For strings:
minLength, maxLength, pattern (regex)
- For numbers:
minimum, maximum, step
- For arrays:
minItems, maxItems, items (type)
- For enums:
enum: [value1, value2]
- โ
Default values MUST match their type
- โ NO
type: object without properties defined
- โ NO vague descriptions like "The text parameter"
Valid Parameter Example:
parameters:
channelId:
type: string
required: true
description: "Slack channel ID (format: C0123456789, starts with C)"
pattern: "^C[A-Z0-9]{10,}$"
messageText:
type: string
required: false
description: "Plain text message to send. Supports markdown. Max 4000 chars."
maxLength: 4000
threadTimestamp:
type: number
required: false
description: "Parent message timestamp for threading (Unix epoch, decimal)"
minimum: 0
tags:
type: array
required: false
description: "Optional tags to categorize the message"
items:
type: string
minItems: 1
maxItems: 5
4. Authentication (MANDATORY for HTTP tools)
Every HTTP tool MUST have authentication:
- โ
authentication block MUST specify type and location
- โ
Supported types:
api_key, bearer, basic, oauth2
- โ
Environment variable naming:
MATIMO_{TOOL_NAME}_{AUTH_TYPE} (uppercase)
- โ
Authentication setup MUST be documented (which provider supports which auth)
- โ
Token refresh/expiration MUST be handled if applicable
- โ NO tools with
type: http and no authentication
- โ NO hardcoded credentials or tokens
Example (Correct):
execution:
type: http
method: POST
url: 'https://slack.com/api/chat.postMessage'
headers:
Authorization: 'Bearer {SLACK_BOT_TOKEN}'
authentication:
type: api_key
location: header
name: Authorization
5. Output Schema Validation (REQUIRED)
Output schema MUST match actual API response:
- โ
Schema MUST be present for every tool
- โ
Schema MUST use Zod-compatible JSON schema
- โ
Schema MUST match real API success response
- โ
Response validation MUST fail if schema doesn't match
- โ NO generic
{ type: object } schemas
- โ NO schemas that don't match actual responses
Example (Correct):
output_schema:
type: object
properties:
ok:
type: boolean
description: "Request succeeded"
message:
type: object
properties:
type:
type: string
ts:
type: string
channel:
type: string
required: [ok, message]
6. Error Handling (MANDATORY)
All errors MUST use MatimoError:
- โ
MUST import and use
MatimoError from @matimo/core
- โ
MUST use correct
ErrorCode (INVALID_SCHEMA, EXECUTION_FAILED, AUTH_FAILED, TOOL_NOT_FOUND)
- โ
Error messages MUST be clear and actionable
- โ
Error details MUST include context (but NO secrets)
- โ
Setup
error_handling.retry if operation is idempotent
- โ NO custom error classes
- โ NO throwing plain Error or Error subclasses
Example (Correct):
import { MatimoError, ErrorCode } from '@matimo/core';
if (!channelId) {
throw new MatimoError(
'Channel ID is required',
ErrorCode.INVALID_SCHEMA,
{ expectedFormat: 'C0123456789' }
);
}
try {
const response = await fetch(url);
if (!response.ok) {
throw new MatimoError(
'Slack API request failed',
ErrorCode.EXECUTION_FAILED,
{ statusCode: response.status }
);
}
} catch (error) {
throw new MatimoError(
'Network error',
ErrorCode.EXECUTION_FAILED,
{ message: error instanceof Error ? error.message : 'Unknown error' }
);
}
7. Logging (NO console.log in Core)
Log Usage Rules:
- โ
Use
getGlobalMatimoLogger() from @matimo/core
- โ
Log at appropriate levels:
error, warn, info, debug
- โ
Log important validation failures, auth issues, retries
- โ
Never log secrets, credentials, or sensitive data
- โ NO
console.log, console.error, console.warn in package code
- โ NO logging secrets, API keys, tokens, passwords
- โ NO over-logging (avoid logs for normal happy paths)
Example (Correct):
import { getGlobalMatimoLogger } from '@matimo/core';
const logger = getGlobalMatimoLogger();
try {
logger.info('Sending message to Slack', { channel: channelId });
} catch (error) {
logger.error('Slack API call failed', { statusCode, message });
throw new MatimoError('Failed', ErrorCode.EXECUTION_FAILED, { statusCode });
}
10. Enforcement Gates (Agent Checklist)
When reviewing a tool, the agent MUST verify:
NAMING CONVENTIONS GATE
โ Tool name is snake_case (slack_send_message)
โ requires_approval: true is present
โ Parameters are camelCase (channelId, messageText)
โ Tool name does NOT start with matimo_ (reserved namespace)
โ TypeScript uses PascalCase for classes
POLICY GATE (run matimo_doctor first โ fail fast)
โ execution.type is http (command/function blocked by default)
โ URL domain is in allowedDomains list
โ HTTP method is GET or POST (default allowed methods)
โ No SSRF risk (no internal IPs or metadata endpoints)
โ Credential name is in allowedCredentials list
โ matimo_doctor returns { valid: true } before create
COMPLETENESS GATE
โ YAML parses without errors
โ All parameters have type, description, required
โ All examples match parameter definitions
โ No TODO/FIXME/HACK comments
QUALITY GATE
โ Output schema matches real API response
โ Error handling uses MatimoError + ErrorCode
โ Authentication configured (if HTTP)
โ Env vars documented and referenced
DELIVERY GATE
โ All gates passed
โ Tool ready for production use
โ Can be merged without caveats
Workflow: Creating a New Tool
Agent at Runtime (using meta-tools)
Step 1: Write YAML in memory
Compose the full tool definition YAML string. Required fields:
name, description, version
requires_approval: true โ mandatory โ policy will reject without it
parameters (with types and descriptions)
execution โ use type: http (command/function blocked by default)
authentication (mandatory for HTTP tools)
output_schema
Step 2: Validate with matimo_doctor FIRST
matimo_doctor(yaml_content: "<full YAML string>")
- โ
{ valid: true } โ proceed to Step 3
- โ
{ valid: false, policyViolations: [...] } โ fix the YAML and re-validate
Common failures and fixes:
| Failure | Fix |
|---|
command tools blocked | Change type: command โ type: http |
function tools blocked | Change type: function โ type: http |
domain not allowed | Use a domain in the allowedDomains list |
HTTP method not allowed | Use GET or POST |
reserved namespace | Rename โ don't start with matimo_ |
requires_approval missing | Add requires_approval: true |
forced-approval | Same โ add requires_approval: true |
Step 3: Create on disk with matimo_create_tool
matimo_create_tool(
name: "my_tool_name",
yaml_content: "<validated YAML>",
target_dir: "<path the developer configured, e.g. ./matimo-tools>"
)
Result:
- Tool written to
{target_dir}/{name}/definition.yaml
status: "draft", approvalState: "pending" (or "auto-approved" for low-risk GET tools)
- This path is permanent โ approved tools stay here and survive restarts
Step 4: Get human approval
matimo_approve_tool(name: "my_tool_name", tool_dir: "<same target_dir>")
- Updates
status to "approved" in the same definition.yaml
- Writes approval hash to
{target_dir}/.matimo-approvals.json
Step 5: Reload and use
matimo_reload_tools() โ Hot-reloads registry from disk
matimo.execute("my_tool_name", params) โ Tool is now live
SDK Developer (adding to codebase)
Step 1: Create directory
mkdir -p packages/{provider}/tools/{tool-name}
Step 2: Write definition.yaml
Same YAML structure as above. No requires_approval needed for SDK-shipped tools.
Step 3: Add test fixture
cp packages/{provider}/tools/{tool-name}/definition.yaml \
packages/core/test/fixtures/{provider}/{tool-name}-fixture.yaml
Step 4: Validate & test
pnpm validate-tools
pnpm test
pnpm test:coverage
pnpm lint
Validation & Quality Checks
Agent: Validate with matimo_doctor
Run before every matimo_create_tool call:
โ
YAML syntax is valid
โ
Policy compliance (domains, methods, execution type, namespace)
โ
Schema fields present (name, parameters, execution, output_schema)
โ
requires_approval: true present
SDK Developer: Validate with CLI
pnpm validate-tools
โ
YAML syntax is valid
โ
Schema matches ToolDefinition contract
โ
All required fields present
โ
Parameter types are consistent
Code Quality
pnpm lint
pnpm format
pnpm test
pnpm test:coverage
Security Review
- No hardcoded secrets in definitions
- No sensitive data in error messages
- Auth env vars properly documented
- No
console.log in production code
Common Patterns
Pattern 1: Parameter Templating
execution:
type: http
url: 'https://api.example.com/users/{user_id}'
body:
name: '{name}'
email: '{email}'
Parameters are replaced before execution. Type conversion handled automatically (numbers โ strings).
Pattern 2: Environment Variable Injection
execution:
type: http
headers:
Authorization: 'Bearer {API_TOKEN}'
Template syntax: {UPPERCASE_NAME} โ process.env.MATIMO_{TOOL_NAME}_UPPERCASE_NAME
Pattern 3: Optional Parameters with Defaults
parameters:
timeout:
type: number
required: false
default: 30
execution:
args:
- '--timeout'
- '{timeout}'
Pattern 4: Enum Validation
parameters:
status:
type: string
enum:
- draft
- published
- archived
execution:
query_params:
status: '{status}'
Real Example: Slack Send Message
definition.yaml
name: slack_send_message
description: Send a text message to a Slack channel
version: '1.0.0'
parameters:
channel:
type: string
required: true
description: Channel ID or name (e.g.,
text:
type: string
required: true
description: Message text to send
thread_ts:
type: string
required: false
description: Parent message timestamp for thread replies
execution:
type: http
method: POST
url: 'https://slack.com/api/chat.postMessage'
headers:
Authorization: 'Bearer {SLACK_BOT_TOKEN}'
Content-Type: application/json
body:
channel: '{channel}'
text: '{text}'
thread_ts: '{thread_ts}'
authentication:
type: api_key
location: header
name: Authorization
output_schema:
type: object
properties:
ok:
type: boolean
ts:
type: string
channel:
type: string
required:
- ok
- ts
error_handling:
retry: 2
backoff_type: exponential
initial_delay_ms: 500
retry_on_status:
- 429
- 500
- 503
examples:
- name: "Send to channel"
description: Post a message to
params:
channel: "#general"
text: "Hello team!"
- name: "Reply in thread"
description: Reply to a message thread
params:
channel: "#general"
text: "Thanks for the update"
thread_ts: "1234567890.123456"
Key Decisions
- Type: http because Slack API is REST-based
- Auth: api_key in header (standard for Slack)
- Parameters: Optional
thread_ts for thread replies
- Output: Validates
ok and ts fields
- Retry: Handles rate limiting (429) and server errors
Troubleshooting
Tool Definition Won't Validate
Run pnpm validate-tools --verbose to see:
- Missing required fields (name, parameters, execution, output_schema)
- Parameter type mismatches
- Invalid execution config for selected type
- Schema validation errors
Execution Fails with Auth Error
Check:
- Env variable exists:
MATIMO_{TOOL_NAME}_API_KEY
- Template references correct field:
{API_TOKEN} not {api_token}
- Authentication.location matches where secret is used (header, query, body)
Command Executor Not Found
Verify:
index.ts exists at correct path
command field in execution matches executable
- Args array is properly formatted
- Script outputs valid JSON to stdout
Parameter Templating Doesn't Work
Ensure:
- Parameter name in definition matches template:
{paramName}
- Template syntax uses exact name from parameters section
- Parameter is defined as
required: true or has default value
- Type matches: numbers auto-converted to strings in templates
References
- Tool lifecycle: See
meta-tools-lifecycle skill
- Complete tool creation: See
tool-creation skill
- Tool discovery: See
tool-discovery skill