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

HTTP Parameter Embedding — Core Matimo Design

Core Principle: “Define once in YAML, embed correctly at execution time”

This guide explains how Matimo’s HTTP executor handles different parameter types when embedding them in request bodies.

Overview

When you define a tool with HTTP execution, parameters can be different data types (string, number, boolean, object, array). The HTTP executor needs to know how to embed each type into the request.

Matimo uses the parameter type from your YAML definition to determine how values should be embedded:

Parameter Type How It’s Embedded Example
string String templating "Hello World"
number Number conversion 42
boolean Boolean conversion true
object Direct JSON object {"id": "123", "type": "db"}
array Direct JSON array [{"text": "item1"}, {"text": "item2"}]

Key Principle: Type Information Drives Embedding

The HTTP executor looks at the parameter’s type in your YAML definition to decide how to embed it. This enables:

How It Works

Step 1: Define Parameters in YAML

parameters:
  parent:
    type: object
    description: Parent database or page
    required: true
  items:
    type: array
    description: Array of items
    required: false
  title:
    type: string
    description: Page title
    required: true

Step 2: Define HTTP Body Template

execution:
  type: http
  method: POST
  url: https://api.notion.com/v1/pages
  body:
    parent: '{parent}' # Placeholder for object param
    children: '{items}' # Placeholder for array param
    title: '{title}' # Placeholder for string param

Step 3: Execute from JavaScript

const result = await matimo.execute('notion_create_page', {
  parent: { database_id: 'abc123', type: 'database' }, // JavaScript object
  items: [{ type: 'text', text: 'Content' }], // JavaScript array
  title: 'Create This Page', // String
});

Step 4: HTTP Executor Embeds Values

The HTTP executor:

  1. Sees parent: "{parent}" in the body
  2. Checks parameter definition: parent: { type: 'object' }
  3. Since type is object, embeds the value directly as JSON (not stringified!)
  4. Sends to API:
{
  "parent": { "database_id": "abc123", "type": "database" },
  "children": [{ "type": "text", "text": "Content" }],
  "title": "Create This Page"
}

What NOT to Do ❌

Anti-pattern 1: Stringifying objects manually

// DON'T do this:
await matimo.execute('notion_create_page', {
  parent: JSON.stringify({ database_id: 'abc123' }), // ❌ Already stringified
  items: JSON.stringify([{ type: 'text' }]), // ❌ Already stringified
});

Why: The HTTP executor will try to template a string that’s already stringified, resulting in double-encoding.

Anti-pattern 2: Defining objects as type:string in YAML

parameters:
  parent:
    type: string # ❌ Wrong type for an object
    description: Parent database

Why: The executor will treat it as a string and won’t preserve JSON structure. You’ll get "[object Object]" instead of proper JSON.

Anti-pattern 3: Passing JSON strings from decorators

class NotionManager {
  @tool('notion_create_page')
  async createPage(parent: string, title: string) {
    // ❌ Wrong signature
    throw new Error('Should not be called');
  }
}

// This will fail:
await manager.createPage('{"database_id": "123"}', 'Title'); // ❌ String, not object

Why: The decorator doesn’t know you meant this to be an object. Always pass actual objects/arrays.

Complete Example: Notion Create Page

YAML Definition

name: notion_create_page
description: Create a new page in a Notion database
version: "1.0.0"

parameters:
  parent:
    type: object
    required: true
    description: |
      Where to create the page:
      - { "database_id": "..." }
      - { "page_id": "..." }

  icon:
    type: object
    required: false
    description: Page icon - { type: "emoji", emoji: "🎯" }

  children:
    type: array
    required: false
    description: Array of block objects to add to the page

  markdown:
    type: string
    required: false
    description: Page content in Markdown format

execution:
  type: http
  method: POST
  url: "https://api.notion.com/v1/pages"
  headers:
    Authorization: "Bearer {NOTION_API_KEY}"
    "Notion-Version": "2024-11-28"
    Content-Type: application/json
  body:
    parent: "{parent}"          # ← Object embedded directly as JSON
    icon: "{icon}"              # ← Object embedded directly as JSON
    children: "{children}"      # ← Array embedded directly as JSON
    markdown: "{markdown}"      # ← String templated normally

authentication:
  type: bearer
  location: header
  name: Authorization

output_schema:
  type: object
  properties:
    id:
      type: string
    url:
      type: string

JavaScript Usage (Factory Pattern)

const matimo = await MatimoInstance.init('./tools');

const result = await matimo.execute('notion_create_page', {
  parent: { database_id: 'a1d8501e-1ac1-43e9-a6bd-ea9fe6c8822b' },
  icon: { type: 'emoji', emoji: '🔧' },
  children: [
    {
      object: 'block',
      type: 'paragraph',
      paragraph: {
        rich_text: [
          {
            type: 'text',
            text: { content: 'Page content here' },
          },
        ],
      },
    },
  ],
  markdown: '# My Page\n\nSome content',
});

console.log('Created page:', result.data.id);

TypeScript with Decorator Pattern

import { MatimoInstance, setGlobalMatimoInstance, tool } from '@matimo/core';

const matimo = await MatimoInstance.init('./tools');
setGlobalMatimoInstance(matimo);

class NotionManager {
  @tool('notion_create_page')
  async createPage(
    parent: Record<string, string>, // Object type
    markdown?: string, // String type
    icon?: Record<string, string> // Object type
  ): Promise<any> {
    throw new Error('Should not be called');
  }
}

const manager = new NotionManager();
const result = await manager.createPage(
  { database_id: 'a1d8501e-1ac1-43e9-a6bd-ea9fe6c8822b' },
  '# My Page',
  { type: 'emoji', emoji: '🔧' }
);

console.log('Created page:', result.id);

Deep Dive: How the HTTP Executor Works

The Embedding Algorithm

When the HTTP executor processes a body with placeholders:

// Input body from YAML:
body: {
  parent: "{parent}",
  children: "{children}",
  title: "{title}"
}

// Input parameters from JavaScript:
params: {
  parent: { database_id: "123" },
  children: [{ type: "text" }],
  title: "My Page"
}

// Parameter definitions from YAML:
paramDefinitions: {
  parent: { type: "object" },
  children: { type: "array" },
  title: { type: "string" }
}

// Processing:
1. Find key "parent" with value "{parent}"
2. Extract placeholder: parent
3. Get param value: { database_id: "123" }
4. Check paramDefinitions["parent"].type === "object"
5. Since it's object/array: EMBED DIRECTLY (don't stringify)
6. Result: { parent: { database_id: "123" } }

7. Find key "children" with value "{children}"
8. Extract placeholder: children
9. Get param value: [{ type: "text" }]
10. Check paramDefinitions["children"].type === "array"
11. Since it's array: EMBED DIRECTLY
12. Result: { children: [{ type: "text" }] }

13. Find key "title" with value "{title}"
14. Extract placeholder: title
15. Get param value: "My Page"
16. Check paramDefinitions["title"].type === "string"
17. Since it's string: Do normal templating
18. Result: { title: "My Page" }

Why This Matters

Without type information, the executor would fall back to string templating for all values:

// Without using type information:
result = {
  parent: '[object Object]', // ❌ Stringified!
  children: '[object Object]', // ❌ Stringified!
  title: 'My Page',
};

With type information, everything works correctly:

// With type information:
result = {
  parent: { database_id: '123' }, // ✅ Proper JSON
  children: [{ type: 'text' }], // ✅ Proper JSON
  title: 'My Page', // ✅ String
};

Best Practices

✅ DO

❌ DON’T

Troubleshooting

I’m getting “[object Object]” in my API request

Cause: Parameter type is wrong or not set.

Solution: Check your YAML parameter definition:

parameters:
  parent:
    type: object  # ✅ Correct
    type: string  # ❌ Wrong!

My API is receiving nested objects as strings

Cause: You manually stringified before passing to execute().

Solution: Pass objects directly:

// ❌ Wrong
await matimo.execute('my_tool', {
  data: JSON.stringify({ nested: { value: 123 } }),
});

// ✅ Correct
await matimo.execute('my_tool', {
  data: { nested: { value: 123 } },
});

The parameter embedding isn’t working for my custom tool

Cause: The YAML might not have paramDefinitions passed to the HTTP executor.

Solution: Ensure the tool definition includes the parameters section with proper types.

Real-World APIs

Here are common APIs that need structured parameters:

API Parameter Type Example
Notion Objects and arrays { database_id: "..." }, [{ type: "..." }]
GitHub Objects { owner: "...", repo: "..." }
Slack Objects { channel_id: "...", user_id: "..." }
MongoDB Objects and arrays { filter: { ... } }, [{ $set: { ... } }]
OpenAI Objects and arrays { messages: [...] }, { tools: [...] }

See Also