MeetStream Guide: Custom Function Tools for MIA Agents
MeetStream Guide: Custom Function Tools for MIA Agents
Custom functions let a MIA agent call any HTTPS endpoint you host as a tool — no MCP server required. This guide walks you end-to-end: registering a function, writing a receiving endpoint, authenticating the call, and wiring response values back into the agent’s prompt.
Applies to: Google Meet, Zoom, Microsoft Teams Support: docs.meetstream.ai · API: api.meetstream.ai
New to MIA? Start with the Create Agent Guide. This guide assumes you already have an agent and just want to add a custom-function tool to it.
1. When to use custom functions vs MCP
You can combine both on the same agent — add MCP servers for multi-tool integrations and custom functions for everything else.
2. Quick start — full API example
Register a custom function on an agent via the API. The LLM sees lookup_order in its function-calling schema and can call it when the conversation warrants. The endpoint is authenticated with a webhook-specific bearer token.
For the full list of fields and constraints, see agent.custom_functions[] in the API reference.
3. Wire format
When the LLM calls your function, MeetStream sends an HTTPS request to the URL you registered.
Request body
By default the body is a JSON envelope so you can distinguish arguments from bot context:
For POST, PUT, and PATCH, this envelope is the default. GET and DELETE requests have no body by default.
If you prefer a flat body containing only args, set "payload_args_only": true:
Request headers
MeetStream always adds X-Bot-ID unless your configuration explicitly supplies that exact header name. X-Agent-ID and bot.agent_id are optional and may be absent, so use X-Bot-ID as the reliable session identifier. dynamic_vars contains values extracted from earlier custom-function responses; it does not include initial agent_config_params.
Any other configured headers are forwarded, including {{variable}} substitutions. Environment expansion works only when the entire header value is env:VAR_NAME and that variable has been provisioned in the managed agent runtime; "Bearer env:VAR_NAME" is sent literally.
Response body
Your endpoint should respond with application/json. Anything 2xx is treated as success. Non-2xx responses are handed back to the LLM verbatim (capped at response_cap_chars) so the agent can recover gracefully.
Example success response:
With response_variables: [{ "name": "latest_order_status", "path": "data.order.status" }] configured, the value "shipped" becomes available as {{latest_order_status}} for the rest of the session.
4. Authenticating the call
MeetStream forwards the headers you configure and adds the bot context described above. Use a dedicated, narrowly scoped bearer token for each endpoint.
Configure the function
Configured values are sent verbatim after {{variable}} resolution. A whole value such as "Authorization": "env:CUSTOM_FUNCTION_AUTH_HEADER" can reference a variable provisioned by MeetStream in the managed runtime.
Header values are persisted in the agent configuration and returned to callers who can read that configuration. Use a function-specific token with minimal permissions, rotate it regularly, and never reuse a broad account credential.
Verify the token in your endpoint
Python (Flask)
Node.js (Express)
Verification checklist
- Use a constant-time comparison when matching tokens (
hmac.compare_digestin Python,crypto.timingSafeEqualin Node) for defence against timing attacks. - Rotate tokens periodically — update the receiving service and agent configuration together.
- Scope tokens narrowly — give each agent its own token so a leak can be contained without affecting other tenants.
5. Response variables — making the agent remember
Setting response_variables on a function extracts values from a successful JSON response into in-memory session variables. They are immediately available to later custom-function URL, header, query, and body templates. They are also available when the stored agent prompt template is rebuilt through update_mia.
Agent config (excerpt):
To use captured values in the active prompt, include their placeholders in the saved model.system_prompt, then trigger a template rebuild with update_mia. For example, the saved prompt can contain:
After the function has populated those variables:
Values persist for the lifetime of the session and override matching AgentConfigParams during a template rebuild. A literal system_prompt supplied to update_mia is not template-resolved, so placeholders inside that field remain unchanged.
Path rules
- Dot-notation only:
data.user.city,meta.total,result. - Array access is not supported — stick to object navigation.
- A missing path is silently skipped (no error).
- Non-scalar values are JSON-encoded (e.g.
{"city": "SF"}→{"city":"SF"}).
6. Speak-during / speak-after behavior
Filler utterance while the HTTP call runs
If your endpoint takes more than a couple of hundred milliseconds, the meeting can feel like it’s stalling. Turn on speak_during_execution to have the agent emit a short filler while the HTTP call is in flight.
Leave speak_during_prompt empty to use the default filler (“One moment please.”).
Silent return for pure side effects
When your function exists purely to do something (create a ticket, update a CRM), you often don’t want the agent to summarise the result out loud. Set:
The agent’s turn ends silently once the tool returns. The next turn begins when the user speaks again.
7. Errors and retries
- 5xx / network errors: MeetStream retries up to
retriestimes with exponential backoff (0.5s, 1s, 2s, 4s, capped at 8s). - 4xx: not retried. The response body is capped and handed back to the LLM so it can adjust and retry with different arguments, or apologise to the caller.
- Timeouts (
timeout_s): count as failures and are retried up toretriestimes. - Response size: anything beyond
response_cap_charsis truncated with a…[truncated at N chars]marker.
Use HTTP status codes meaningfully in your endpoint — a clear 4xx error message (“Order not found”) teaches the LLM to correct itself or explain to the caller.
8. Security & best practices
- Always require an
Authorizationheader — without it, anyone who learns your URL can hit your endpoint. - Idempotency: retries mean the same call can arrive multiple times. Use an idempotency key (e.g. derive one from
bot_id + args) for state-mutating endpoints. - Don’t leak secrets in descriptions — descriptions are shown to the LLM. Put endpoint credentials only in headers and scope them specifically to the custom function.
- Keep responses small — the LLM reads the full capped response every turn it’s referenced; small, structured responses cost less and reason better.
- Use
speak_after_execution=falsefor write ops — saves tokens and keeps the meeting flowing.
