i need a hook that calls before my mcp tools are b...
# ask-ai
e
i need a hook that calls before my mcp tools are being called. i would like to validate something before making mcp tool call.
t
Thanks for reaching out, Aman. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I’m following up about your request for “a hook that calls before my MCP tools are being called” so you can validate something first. Short answer: LiveKit’s docs do not show a built‑in pre‑execution hook that runs before MCP tool calls. However, there are a few practical approaches you can use to achieve the same effect depending on your setup. Before I give the options, could you tell me: • Are your MCP tools hosted on an external MCP server, or are you invoking them from inside an agent process? • Do you need synchronous blocking validation (stop the call if validation fails) or just a non‑blocking check/logging step? If you prefer, tell me the exact validation you want to perform and your runtime (language/framework); I’ll recommend the tightest implementation. Possible approaches you can use now • Validate inside the MCP server (recommended if tools are external) ◦ Add your validation logic to the MCP server’s request handler. Because the agent calls the MCP server, this guarantees validation runs before the tool executes. • Wrap the tool call in your agent logic ◦ If your agent code is responsible for invoking MCP tools, call a local validation function first. Only when it succeeds, forward the request to the MCP tool. This gives you full control and allows synchronous blocking. • Intercept / wrap tools at the agent layer using RunContext or custom tool wrappers ◦ Use the agent’s execution flow: examine
RunContext
or the function-call metadata, run validation, then decide whether to proceed with invoking the MCP tool. This behaves like a hook even if there isn’t a documented dedicated hook API. • Mock/wrap tools for testing or staged rollout ◦ For test environments you can wrap or mock MCP tool implementations and run validations there before forwarding to real implementations. What I can help with next • Give a code example for the agent-side wrapper in your language (please tell me which language). • Outline how to add validation to an MCP server handler (I can provide a small handler template). • Help design the validation flow if you want non‑blocking checks or different failure handling (retries, error reporting, user feedback). If you share where your tools run and the kind of validation you need, I’ll provide a concrete example you can drop into your code. Sources: Tool definition and use | LiveKit Documentation | MCP Agent | LiveKit Documentation | Using the Testing Framework with Realtime Models | LiveKit
e
o
t
Thanks — I read that as “okay.” I can proceed with concrete examples and steps. Quick confirmation so I pick the right approach: • Are your MCP tools hosted on an external MCP server, or are they invoked inside your agent process? • Which language/runtime is your agent or MCP server written in (Node.js, Go, Python, etc.)? • Do you need validation to block the call when it fails, or just to log/notify and let the call continue? If you want me to pick a common scenario and provide code, I can: • Show an agent-side wrapper example (Node.js or Go) that validates using
RunContext
and only forwards to the MCP tool on success, or • Show an MCP-server request handler (Node.js example) that validates incoming requests before executing the tool. Tell me which you'd prefer (or answer the three quick questions) and I’ll provide a ready-to-use example.
e
i'm using self hosted mcp server urls like google calendar and don't have flexiblity to add validation. now to run validate before that
t
Thanks — that’s helpful. Since you can’t add validation on the external MCP servers (e.g., Google Calendar), the right place to run validation is on the agent/client side before you call those MCP tool endpoints. I’ll outline the recommended approaches and give a simple, ready-to-adapt pattern you can drop into your agent. If you tell me your runtime (Node.js, Go, Python, etc.), I’ll provide a concrete code sample. What to do (high level) • Intercept/wrap the MCP tool call in your agent: Add a validation step in the code path that prepares or dispatches requests to the external MCP URL. If validation fails, do not call the external MCP endpoint. This is the most reliable and immediate option. • Use RunContext or request metadata where available: If your agent framework exposes a
RunContext
(or similar) with session/metadata, read needed info there for validation before forwarding the call. • Implement a small middleware or validator function so the approach is reusable and centralized. Simple, language-agnostic pattern 1. Centralize MCP calls behind a single function or service (e.g.,
callMcpTool(toolName, payload, ctx)
). 2. Before sending the HTTP request to the MCP URL, run `validate(toolName, payload, ctx)`: ◦ If validation returns success, proceed to call the MCP server. ◦ If validation fails, return an error or a controlled response back to your agent logic. 1. Log or emit events on validation failure for observability and debugging. Things to consider for validation • Synchronous blocking vs. non-blocking: For blocking validation, reject calls immediately on failure. For non-blocking, you can call the MCP tool but also surface warnings or run a compensating action later. • Timeouts and retries: Keep validation fast so you don't add significant latency; enforce timeouts on both validation and the external MCP call. • Security: Validate both request payloads and any authorization tokens before forwarding to the external MCP endpoint. • Auditing: Record which calls were blocked and why for later analysis. If you want a concrete example • Tell me your runtime: Node.js, Go, Python, or another. • Tell me whether you want the agent to return an error on validation failure or handle it differently (e.g., return a default response, queue for manual review). Once you tell me the language and desired failure behavior, I’ll provide a direct code example you can copy and use.
e
how to guarantee that
callMcpTool will happen always?
t
Thanks — I see you want to guarantee that your
callMcpTool
always runs (after your validation) so external MCP endpoints like Google Calendar are reliably invoked. I’ll outline a robust, practical approach you can apply in any runtime, then offer small examples for Node.js, Go, and Python. Before I give a concrete snippet, please confirm your runtime and whether you want strict guarantees (block until success with retries) or eventual‑delivery (enqueue and retry in background). High‑level strategy to guarantee execution • Centralize calls: Put every MCP invocation behind a single API or service function (your
callMcpTool
). This prevents ad hoc calls that skip validation or reliability logic. • Synchronous validation gate: Run validation first; only allow
callMcpTool
to proceed when validation passes. • Retry with backoff: On transient failures (network, 5xx), retry the MCP call with exponential backoff and a max attempts limit. • Durable queue for failures: If retries exhaust or you prefer not to block the user, write the call to a durable queue (database table, Redis stream, or message broker) for guaranteed background processing and retries. • Idempotency: Add an idempotency key per logical operation so retries or background processing won’t create duplicate side effects at the external MCP. • Observability & alerts: Log each attempt, outcome, and queue status; emit metrics and alerts for repeated failures. • Timeouts & circuit breaking: Use sensible timeouts and a circuit breaker to avoid cascading failures to your system if the external MCP is down. • Transactional coordination (if needed): For multi-step operations where the MCP call must follow other changes, use a transactionally consistent pattern (e.g., write a DB row marking intent + enqueue) so intent is durable before returning success to users. Concrete patterns (pick one or combine) • Synchronous with retries: Validate -> call
callMcpTool
-> retry on transient errors -> return final success/failure to caller. • Async guaranteed delivery: Validate -> persist intent in DB or enqueue message -> return accepted to caller -> background worker consumes queue and calls
callMcpTool
with retries & idempotency. • Hybrid: Attempt a few quick retries synchronously; if still failing, enqueue for background retry and return an “accepted, will retry” response. Quick examples Node.js / TypeScript (conceptual)
Copy code
async function handler(req, res) {
  const payload = req.body;
  if (!validate(payload)) return res.status(400).send({error: 'validation failed'});

  try {
    await callMcpToolWithRetries(payload);
    return res.status(200).send({status: 'ok'});
  } catch (err) {
    // persist intent for background processing
    await enqueueFailedCall(payload);
    return res.status(202).send({status: 'queued_for_retry'});
  }
}

async function callMcpToolWithRetries(payload) {
  const maxAttempts = 5;
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await callMcpTool(payload); // actual HTTP call to MCP URL
    } catch (err) {
      if (!isTransient(err) || attempt === maxAttempts) throw err;
      await sleep(backoff(attempt));
    }
  }
}
Go (conceptual)
Copy code
if !validate(payload) {
  http.Error(w, "validation failed", http.StatusBadRequest)
  return
}
err := CallWithRetries(payload, 5)
if err != nil {
  EnqueueForRetry(payload)
  w.WriteHeader(http.StatusAccepted)
  return
}
w.WriteHeader(http.StatusOK)
Python (conceptual)
Copy code
if not validate(payload):
    return jsonify(error="validation failed"), 400

try:
    call_with_retries(payload, max_attempts=5)
    return jsonify(status="ok"), 200
except Exception:
    enqueue_payload(payload)
    return jsonify(status="queued_for_retry"), 202
Idempotency and durable storage • Include an
operation_id
when creating the request. • Store operation rows with status: pending -> in_progress -> done / failed. • Background worker marks status changes so you can resume interrupted work. Questions so I can give a copy‑paste solution 1. Which runtime do you use (Node.js/TS, Go, Python, other)? 2. Do you want synchronous blocking until success (with retries) or are you okay with returning immediately and retrying in background? 3. Do you already have a queue or database I should target for examples (Postgres, Redis, RabbitMQ, SQS)? Tell me those and I’ll produce a full example (including idempotency key handling and a background worker) tailored to your stack.