mcpField
A
Agent Mesh

Agent Mesh

本地服务
github.com/google其他1.2k今天更新

关于 Agent Mesh

Agent Mesh 是供 Node.js 和浏览器 Agent 加入网状网络的 JavaScript SDK,也是 stdio MCP 服务。经过身份验证的成员可发现并调用远程服务,同时遵循网络策略;该项目本身不发布服务。

主要功能

  1. 01以经过验证的成员身份加入网络
  2. 02发现服务和远程工具
  3. 03描述并调用远程工具
  4. 04响应发往 Agent 的 A2A 请求
  5. 05保存身份以供后续会话使用

适用场景

  1. 01让 MCP 客户端访问 Agent Mesh 上的工具
  2. 02构建调用网络服务的 Node.js Agent
  3. 03使 Agent 能够响应 A2A 请求

MCP 相关

`agentmesh` 可执行程序通过 stdio 暴露五个 MCP 工具,用于获取网络信息、发现服务和工具、描述工具及发起远程调用。MCP 客户端负责启动和停止进程;服务提供方通过 `agentmesh-node` 发布服务。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"google-agentmesh": {
"command": "npx",
"args": [
"-y",
"@agentmesh-p2p/sdk"
],
"env": {
"AGENTMESH_CONTROL_PLANE_URL": "<AGENTMESH_CONTROL_PLANE_URL>",
"AGENTMESH_BOOTSTRAP_TOKEN_PATH": "<AGENTMESH_BOOTSTRAP_TOKEN_PATH>",
"AGENTMESH_JWT_PATH": "<AGENTMESH_JWT_PATH>",
"AGENTMESH_STATE_DIR": "<AGENTMESH_STATE_DIR>",
"AGENTMESH_INSECURE_CONTROL_PLANE": "<AGENTMESH_INSECURE_CONTROL_PLANE>"
}
}
}
}
启动命令npx -y @agentmesh-p2p/sdk
需要配置的环境变量
AGENTMESH_CONTROL_PLANE_URLControl plane URL supplied by your mesh operator. HTTPS is required beyond loopback unless explicitly permitted below.
AGENTMESH_BOOTSTRAP_TOKEN_PATH密钥Absolute path to a bootstrap token file for first enrollment. Set this OR AGENTMESH_JWT_PATH; leave both empty only when resuming saved credentials.
AGENTMESH_JWT_PATH密钥Absolute path to an OIDC/workload token file. Alternative to the bootstrap token; reread during credential refresh.
AGENTMESH_STATE_DIRAbsolute directory for this client identity. Use a distinct directory per client and mesh. If omitted, uses ~/.config/agentmesh/mcp.
AGENTMESH_INSECURE_CONTROL_PLANEAllow plaintext HTTP beyond loopback. Development only: exposes enrollment traffic to the network.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@agentmesh-p2p/sdk0.2.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

@agentmesh-p2p/sdk

用于在 Agent 进程内部加入 Agent Mesh 的原生 JavaScript SDK。对于使用 Node.js 编写或在浏览器页面中运行的 Agent,它可以替代 agentmesh-node 边车: Agent 会向控制平面注册,通过路由器加入网格,发现并调用服务,为 Agent 自身处理 A2A 请求,并在运行期间遵循控制平面的密钥、封禁和策略。它不会发布服务;需要按名称被发现的工具或模型应运行在 agentmesh-node 后面。

指南:google.github.io/agentmesh/docs/guides/native-sdks,从空白机器开始,直至两个程序加入网格。 源码:github.com/google/agentmesh/tree/main/sdk/js。

安装

npm install @agentmesh-p2p/sdk @modelcontextprotocol/sdk zod

需要 Node.js 22.18 或更高版本。在浏览器中,请将其与页面打包(该包的 browser 字段会选择浏览器文件);指南中的在浏览器中部分提供了详细说明和示例页面。

通过 stdio 使用 MCP 客户端

该包的 agentmesh 可执行文件会以独立网格成员的身份加入,并提供五个 MCP 工具:get_mesh_info、discover_remote_services、find_remote_tools、describe_remote_tool 和 call_remote_tool。支持 stdio 的 MCP 客户端负责启动和停止进程;此调用方无需 node 守护进程。提供方仍通过 agentmesh-node 发布其服务。

要从源码检出目录试用该可执行文件:

cd sdk/js
npm ci
npm run build

在 MCP 客户端的配置中使用绝对路径:

{
  "mcpServers": {
    "agentmesh": {
      "command": "node",
      "args": ["/absolute/path/to/agentmesh/sdk/js/dist/cli.js"],
      "env": {
        "AGENTMESH_CONTROL_PLANE_URL": "https://your-control-plane.example",
        "AGENTMESH_BOOTSTRAP_TOKEN_PATH": "/absolute/path/to/join-token",
        "AGENTMESH_STATE_DIR": "/absolute/path/to/this-client-identity"
      }
    }
  }
}

请向网格运营方或 agentmesh-one 获取控制平面 URL 和令牌文件。也可以将 AGENTMESH_JWT_PATH 设置为工作负载/OIDC 令牌文件;不要同时设置两个令牌路径。凭据从文件读取,绝不会使用命令行参数值。

首次运行时会注册并保存身份;后续运行会从状态目录恢复,无需引导令牌。JWT 注册会在刷新时重新读取令牌文件。每个客户端身份和网格都应使用独立的状态目录;不要让多个进程共用同一目录。默认目录为 ~/.config/agentmesh/mcp。除非明确设置 AGENTMESH_INSECURE_CONTROL_PLANE=true 允许在其他地址使用纯 HTTP,否则纯 HTTP 仅允许用于环回地址。

标准输出仅包含 MCP 消息;诊断信息会输出到标准错误。关闭 stdin 或发送 SIGINT/SIGTERM 会关闭网格会话。工具调用使用成员现有的经过身份验证的流和策略,包括提供方标签检查。远程工具结果会保留文本、非文本内容、结构化输出和错误信息。发现过程会遵循工具分页,拒绝循环目录,并将每次搜索限制为最多 64 个提供方、每个提供方 256 个服务、每个服务 256 个工具,以及 1024 个结果。请求时限为 10 秒;若搜索范围过大,请按对等方或服务缩小范围。intent 参数会被接受但忽略,与 node 当前工具的行为一致。

此成员不会发布服务,因此不提供 list_local_services 工具。推理和 A2A 发现会返回元数据,但此可执行文件不提供 HTTP 代理、/v1 端点、本地套接字或 local_proxy_url。如需调用这些协议,请使用 SDK 客户端或 agentmesh-node HTTP 代理。

使用

下面两个程序都位于 examples/,并会在仓库的测试中连接真实的网状网络。它们从 AGENTMESH_CONTROL_PLANE_URL 读取网状网络地址,并从 AGENTMESH_BOOTSTRAP_TOKEN_PATH 读取注册令牌;指南介绍了如何从 agentmesh-one 获取这两项信息,或向现有网状网络的运营者获取。

查找服务并调用它:

// Calls something on the mesh: a tool of an MCP service or a path of an
// inference or A2A service someone published, found by name, or an agent that
// published nothing, reached by its peer ID.
//
//   node call.js mcp://everything echo '{"message": "hi"}'
//   node call.js inference://ollama /v1/models
//   node call.js 12D3KooW... a2a://agent /card
//
// AGENTMESH_CONTROL_PLANE_URL names the mesh. The first run enrolls with the file
// AGENTMESH_BOOTSTRAP_TOKEN_PATH (a token the mesh operator gave you) or
// AGENTMESH_JWT_PATH (a workload identity token your platform issues, such as a
// Kubernetes projected service account token), and keeps the identity and
// credential in AGENTMESH_STATE_DIR; later runs resume from there without it.
import { homedir } from "node:os";
import { AgentMesh, type DiscoveredProvider } from "@agentmesh-p2p/sdk";

let argv = process.argv.slice(2);
// A first argument that is not a service target is the peer ID of an agent.
let peerId: string | undefined;
if (argv[0] !== undefined && !argv[0].includes("://")) {
  [peerId, ...argv] = argv as [string, ...string[]];
}
const [service = peerId !== undefined ? "a2a://agent" : "mcp://everything", toolOrPath = peerId !== undefined ? "/card" : "echo", args = '{"message": "hi"}'] = argv;

const mesh = await AgentMesh.enroll({
  controlPlaneUrl: process.env.AGENTMESH_CONTROL_PLANE_URL ?? "https://mesh.example.com",
  bootstrapTokenPath: process.env.AGENTMESH_BOOTSTRAP_TOKEN_PATH,
  jwtPath: process.env.AGENTMESH_JWT_PATH,
  stateDir: process.env.AGENTMESH_STATE_DIR ?? `${homedir()}/.config/agentmesh/caller`,
  // A plaintext http:// control plane is otherwise accepted only on loopback.
  allowInsecure: process.env.AGENTMESH_INSECURE_CONTROL_PLANE === "true",
});
const session = await mesh.join();
console.log(`on the mesh as ${session.peerId}`);

// The peer to call: an agent named by ID, or a provider of a published service.
let provider: DiscoveredProvider = { peerId: peerId ?? "", addrs: [] };
if (peerId !== undefined) {
  // An agent is not in the discovery table; the SDK finds the path to its
  // peer ID through the routers.
  await session.connect(peerId);
} else {
  const providers = await session.discover(service);
  if (providers.length === 0) {
    throw new Error(`no member of the mesh serves ${service}`);
  }
  // A provider record can outlive its member; the first that answers is used.
  let reached = false;
  for (const candidate of providers) {
    try {
      await session.connect(candidate);
      provider = candidate;
      reached = true;
      break;
    } catch (err) {
      console.error(`${candidate.peerId}: ${(err as Error).message}`);
    }
  }
  if (!reached) {
    throw new Error(`no provider of ${service} is reachable`);
  }
}
console.log(`${service} is served by ${provider.peerId}`);

if (service.startsWith("mcp://")) {
  const tools = await session.listTools(provider, service);
  console.log(`tools: ${tools.map((t) => t.name).join(", ")}`);
  const result = await session.callTool(provider, service, toolOrPath, JSON.parse(args));
  console.log(result.text.join("\n"));
} else {
  const response = await session.request(provider, service, toolOrPath);
  console.log(response.status, response.text());
}

await session.close();

成为其他人可以调用的 Agent。不会发布任何内容;调用方通过路由器使用 Agent 的对等端 ID(也就是它打印出来的 ID)访问它:

// An agent on the mesh: joins, then answers A2A requests from other members
// until stopped. It publishes nothing. There is no service name to look up; a
// caller reaches the agent by its peer ID, through a router, as a2a://agent.
// The mesh policy decides which members may call; the SDK turns the others
// away before anything reaches this code.
//
//   node agent.js                        # answered by the handler below
//   node agent.js http://127.0.0.1:9999  # forwarded to an A2A server beside it
//
// AGENTMESH_CONTROL_PLANE_URL names the mesh. The first run enrolls with the file
// AGENTMESH_BOOTSTRAP_TOKEN_PATH (a token the mesh operator gave you) or
// AGENTMESH_JWT_PATH (a workload identity token your platform issues, such as a
// Kubernetes projected service account token), and keeps the identity and
// credential in AGENTMESH_STATE_DIR; later runs resume from there, as the same
// peer, without it.
import { homedir } from "node:os";
import { AgentMesh } from "@agentmesh-p2p/sdk";

const [backendURL] = process.argv.slice(2);

const mesh = await AgentMesh.enroll({
  controlPlaneUrl: process.env.AGENTMESH_CONTROL_PLANE_URL ?? "https://mesh.example.com",
  bootstrapTokenPath: process.env.AGENTMESH_BOOTSTRAP_TOKEN_PATH,
  jwtPath: process.env.AGENTMESH_JWT_PATH,
  stateDir: process.env.AGENTMESH_STATE_DIR ?? `${homedir()}/.config/agentmesh/agent`,
  // A plaintext http:// control plane is otherwise accepted only on loopback.
  allowInsecure: process.env.AGENTMESH_INSECURE_CONTROL_PLANE === "true",
});
const session = await mesh.join();

// Answers every path with who was asked and who asked; a real agent runs an
// A2A server here, or beside this process at backendURL.
const target = await session.acceptA2A(
  backendURL !== undefined
    ? { url: backendURL }
    : { handler: (request, caller) => Response.json({ name: "agent", path: new URL(request.url).pathname, caller: caller.peerId }) },
);
console.log(`accepting ${target} as ${session.peerId}`);

const stop = () => void session.close().then(() => process.exit(0));
process.on("SIGINT", stop);
process.on("SIGTERM", stop);

如果保存在 stateDir 中的身份和凭据对该控制平面仍然有效,enroll 会复用它们;否则,必须且只能提供 bootstrapTokenPath、bootstrapToken、jwtPath 或 jwt 中的一项(令牌字符串,或在注册时及每次刷新时调用的 () => string | Promise<string> 回调)。从文件、回调或环境变量中读取令牌;不要将令牌放在命令行中。labels 会在注册时进行证明;join({ egressRequireLabels }) 则是会话调用的每个对等端都必须证明的最低要求,所有这些标签都必须满足,并且在整个会话期间持续有效。

仅在回环地址上接受明文 http:// 控制平面。对于你信任的网络,请传入 allowInsecure: true。

许可证

Apache-2.0。请前往 github.com/google/agentmesh 提交问题和贡献。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。