Skip to content
🐍 Looking for Python? See Python Decorators | Looking for Java? See Java Decorators

MCP Mesh Functions (TypeScript)

Core functions for building distributed agent systems

Overview

MCP Mesh provides core functions that transform regular TypeScript functions into mesh-aware distributed services. These functions handle registration, dependency injection, and communication automatically.

Function Purpose
mesh() Create mesh agent wrapping FastMCP
agent.addTool() Register capability with DI
mesh.llm() Enable LLM-powered tools
mesh.llmProvider() Create LLM provider (zero-code)
mesh.route() Express route with mesh DI

mesh() Function

Creates a MeshAgent that wraps a FastMCP server with mesh capabilities.

import { FastMCP, mesh } from "@mcpmesh/sdk";

const server = new FastMCP({
  name: "My Service",
  version: "1.0.0",
});

const agent = mesh(server, {
  name: "my-service",           // Required: unique agent identifier
  version: "1.0.0",             // Semantic version
  description: "Service desc",  // Human-readable description
  httpPort: 8080,                   // HTTP server port (0 = auto-assign)
  host: "localhost",            // Host announced to registry
  namespace: "default",         // Namespace for isolation
  heartbeatInterval: 30,        // Heartbeat interval in seconds
  healthCheck: myHealthCheck,   // Optional: decides whether this agent keeps traffic
  healthCheckTtl: 30,           // How often it re-runs (default 15s)
});

healthCheck is what lets a provider take itself out of rotation. While it returns { status: "unhealthy" } (or false) the agent stops heartbeating, the registry withdraws it, and consumers resolve to another provider — restored automatically when it passes again, with no restart. A check that throws is recorded as degraded and keeps heartbeating, so a bug in the check cannot remove a working agent. The verdict also drives the probe endpoints: /ready and /health answer 200 only while it reports healthy. MCP_MESH_HEALTH_CHECK_TTL overrides healthCheckTtl. See Health & Discovery.

Import FastMCP from @mcpmesh/sdk, which re-exports it — not directly from "fastmcp". A direct "fastmcp" import can trigger duplicate class-identity type errors under tsc against the SDK's own bundled copy.

agent.addTool()

Registers a function as a mesh capability with dependency injection.

import { z } from "zod";

agent.addTool({
  name: "greet",
  capability: "greeting",              // Capability name for discovery
  description: "Greets users",         // Human-readable description
  version: "1.0.0",                    // Capability version
  tags: ["greeting", "utility"],       // Tags for filtering
  dependencies: ["date_service"],      // Required capabilities
  parameters: z.object({
    name: z.string(),
  }),
  execute: async (
    { name },                                  // Input parameters
    date_service: McpMeshTool | null = null,   // Injected positionally (nullable)
  ) => {
    if (date_service) {
      const today = await date_service({});
      return `Hello ${name}! Today is ${today}`;
    }
    return `Hello ${name}!`;  // Graceful degradation
  },
});

Note: Dependencies are injected by position as McpMeshTool | null parameters after the first args parameter, in declaration order; parameter names are never consulted, so they are yours to choose. See the Dependency Injection guide for the full pairing rule.

Dependency Injection Types

Type Use Case
McpMeshTool Tool calls via proxy
null Dependency unavailable

mesh.serviceView()

mesh.serviceView({ methods }) aggregates several dotted capabilities behind one typed consumer facade (RFC #1280) — one dependencies entry expanding to N edges, injected as a facade argument:

const Media = mesh.serviceView({
  methods: {
    caption: { capability: "media.caption", required: true },
    thumbnail: "media.thumbnail",
  },
});

agent.addTool({
  name: "process_media",
  dependencies: [Media],
  execute: async (args, media) =>
    // dep params are inferred; cast the view slot to type its methods
    (media as MeshServiceFacade<typeof Media>).caption({ text: args.text }),
});

The capabilities a view binds are ordinary dotted tools — each declared explicitly on its provider with a dot-namespaced capability:

agent.addTool({
  name: "caption",
  capability: "media.caption",
  parameters: z.object({ text: z.string() }),
  execute: async ({ text }) => ({ caption: `a scene: ${text}` }),
});

required view edges get the pre-invoke dependency_unavailable refusal (a UserError); minAvailable (consumer-only) adds a floor; a view forces inline execution and is rejected in mesh.route(...) / mesh.a2a.mount(...). For the full semantics see the Dependency Injection guide.

mesh.llm()

Creates an LLM-powered tool with automatic tool discovery.

import { z } from "zod";

agent.addTool({
  name: "assist",
  ...mesh.llm({
    provider: { capability: "llm", tags: ["+claude"] },  // LLM provider selector
    maxIterations: 5,                    // Max agentic loop iterations
    systemPrompt: "file://prompts/agent.hbs",  // Handlebars template
    contextParam: "ctx",                 // Parameter name for context
    filter: [{ tags: ["tools"] }],       // Tool filter for discovery
    filterMode: "all",                   // "all", "best_match", or "*"
    responseModel: AnalystOutput,        // Optional: schema the LLM must emit (drives structured output)
    returns: RunDailyResult,             // Optional: schema for what execute returns to callers
  }),
  capability: "smart_assistant",
  description: "LLM-powered assistant",
  parameters: z.object({
    ctx: z.object({
      query: z.string(),
    }),
  }),
  execute: async ({ ctx }, { llm }) => {
    return llm("Help the user with their request");
  },
});

responseModel is the schema the LLM is required to emit and is validated against (and types the injected llm callable); returns types what execute returns to callers. When responseModel is omitted, the LLM schema falls back to returns. See meshctl man llm --typescript for a combined-fields example.

Filter Modes

Mode Description
all Include all tools matching any filter
best_match One tool per capability (best tag match)
* All available tools (wildcard)

mesh.llmProvider()

Creates a zero-code LLM provider wrapping LiteLLM-compatible APIs.

agent.addTool({
  name: "claude_chat",
  ...mesh.llmProvider({
    model: "anthropic/claude-sonnet-4-5",  // LiteLLM model string
    capability: "llm",                      // Capability name
    tags: ["llm", "claude", "provider"],    // Discovery tags
    version: "1.0.0",                       // Provider version
  }),
});

mesh.route()

Enables mesh dependency injection in Express route handlers. Use this when building REST APIs that consume mesh capabilities.

import express from "express";
import { mesh } from "@mcpmesh/sdk";

const app = express();
app.use(express.json());

app.post("/chat", mesh.route(
  [{ capability: "avatar_chat" }],  // Dependencies, in binding order
  async (req, res, [avatarChat]) => {
    if (!avatarChat) {
      res.status(503).json({ error: "Service unavailable" });
      return;
    }
    const result = (await avatarChat({
      message: req.body.message,
      user_email: "user@example.com",
    })) as { message?: string };
    res.json({ response: result.message });
  }
));

app.listen(3000);

Note: the handler's third argument is a positional array — deps[i] is the i-th declared dependency, null when unresolved. (Changed in 3.4.0; it used to be an object keyed by capability. See Migrating to positional DI.)

Note: mesh.route() is for Express backends that consume mesh capabilities. Use agent.addTool() for MCP agents that provide capabilities.

See meshctl man express for complete Express integration guide.

Environment Variable Overrides

All configuration can be overridden via environment variables:

export MCP_MESH_AGENT_NAME=custom-name
export MCP_MESH_HTTP_PORT=9090
export MCP_MESH_NAMESPACE=production
export MCP_MESH_REGISTRY_URL=http://registry:8000

Complete Example

import { FastMCP, mesh } from "@mcpmesh/sdk";
import { z } from "zod";

const server = new FastMCP({
  name: "Calculator Service",
  version: "1.0.0",
});

const agent = mesh(server, {
  name: "calculator",
  httpPort: 8080,
});

// Basic tool
agent.addTool({
  name: "add",
  capability: "calculator_add",
  description: "Add two numbers",
  tags: ["math", "calculator"],
  parameters: z.object({
    a: z.number(),
    b: z.number(),
  }),
  execute: async ({ a, b }) => String(a + b),
});

// Tool with dependency
agent.addTool({
  name: "calculate_with_logging",
  capability: "calculator_logged",
  description: "Calculate with audit logging",
  dependencies: ["audit_log"],
  parameters: z.object({
    operation: z.string(),
    a: z.number(),
    b: z.number(),
  }),
  execute: async ({ operation, a, b }, audit_log: McpMeshTool | null = null) => {
    const result = operation === "add" ? a + b : a - b;
    if (audit_log) {
      await audit_log({ operation, a, b, result });
    }
    return String(result);
  },
});

// Agent auto-starts - no explicit run() call needed!

See Also

  • meshctl man dependency-injection - DI details
  • meshctl man llm --typescript - LLM integration guide
  • meshctl man tags - Tag matching system
  • meshctl man capabilities - Capabilities system
  • meshctl man express - Express integration with mesh.route()