EvoX ドキュメント
  • 製品
  • 料金
  • ドキュメント
  • マーケット
EvoMap に戻る
ログインサインアップ
Overview
Practice cases
Features
Configuration
Developers
Development workflows
Develop in a projectCode review and changesIntegrated terminalGit worktrees
Extension development
Build skillsConnect MCP toolsHooks
Experience and protocol integration
GEP schemasDistill Genes, Skills, and RecipesRecipes and experience reuseEvolver Proxy and MCPSync, import, and exportA2A and task network integration
Standalone Evolver
Installation and first runReview and validationResident operation and task queuesIdentity and connectionEvolution strategy and boundariesAsset publishing and creditsStorage, containers, and CI
Architecture and source repositories
Security
Administration
Overview
Practice cases
Features
Configuration
Developers
Development workflows
Develop in a projectCode review and changesIntegrated terminalGit worktrees
Extension development
Build skillsConnect MCP toolsHooks
Experience and protocol integration
GEP schemasDistill Genes, Skills, and RecipesRecipes and experience reuseEvolver Proxy and MCPSync, import, and exportA2A and task network integration
Standalone Evolver
Installation and first runReview and validationResident operation and task queuesIdentity and connectionEvolution strategy and boundariesAsset publishing and creditsStorage, containers, and CI
Architecture and source repositories
Security
Administration
EvoX ドキュメント/Build with EvoX/Connect MCP tools

Connect MCP tools

Expose your tools to EvoX through an MCP server. This guide implements a read-only integration: an agent reads npm scripts from a fixed project while you verify discovery, invocation, and errors. Connection management and credentials belong in MCP servers. To expose Evolver experience tools to other agents, see Evolver Proxy and MCP.

Define tool scope

Run this local example only in a project directory and parent directories controlled by the runtime account. Other users must not replace these directories or modify the file during a call. The server rejects symbolic links, checks the opened file identity, and reads at most 64 KiB plus one overflow byte from that descriptor; it does not isolate a hostile shared filesystem or provide an OS sandbox.

The example read_project_scripts reads scripts from the startup project's package.json. It does not execute scripts, write files, or access the network. The server's startup argument selects the project; the agent cannot supply arbitrary file paths.

ContractThis example
InputEmpty object {}; additional fields rejected
OutputJSON text containing scripts
ResourceA regular package.json in a fixed project, limited to 64 KiB
ErrorsProtocol errors for invalid arguments; isError: true for file failures
Side effectsNo file writes or command execution

Create a stdio server

Save the following as read-project-scripts.mjs outside the source project. It uses Node.js built-ins and writes only MCP messages to stdout. A deployed service also needs session management, resource limits, and access controls appropriate to its environment.

javascript
import { createInterface } from "node:readline";
import { realpathSync, lstatSync, openSync, fstatSync, readSync, closeSync, constants } from "node:fs";
import { join } from "node:path";

const root = realpathSync(process.argv[2]);
const file = join(root, "package.json");
const tool = {
  name: "read_project_scripts",
  description: "Read npm scripts from the configured project without executing them.",
  inputSchema: { type: "object", properties: {}, additionalProperties: false }
};
const send = (id, result) => process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, result }) + "\n");
const fail = (id, code, message) => process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, error: { code, message } }) + "\n");
const protocolVersion = "2024-11-05";
let phase = "new";
const input = createInterface({ input: process.stdin });
input.on("line", (line) => {
  let request;
  try { request = JSON.parse(line); }
  catch { fail(null, -32700, "Invalid JSON"); return; }
  if (!request || typeof request !== "object" || Array.isArray(request)) {
    fail(null, -32600, "Invalid request"); return;
  }
  const { id, method } = request;
  if (id !== undefined && typeof id !== "string" && !(typeof id === "number" && Number.isFinite(id))) {
    fail(null, -32600, "Invalid request ID"); return;
  }
  if (request.jsonrpc !== "2.0" || typeof method !== "string") {
    fail(id ?? null, -32600, "Invalid request"); return;
  }
  if (request.params !== undefined && (!request.params || typeof request.params !== "object" || Array.isArray(request.params))) {
    if (id !== undefined) fail(id, -32602, "Invalid params");
    return;
  }
  const params = request.params ?? {};
  if (id === undefined) {
    if (method === "notifications/initialized" && phase === "negotiated") phase = "ready";
    return;
  }
  if (method === "initialize") {
    const object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
    if (phase !== "new" || typeof params.protocolVersion !== "string" || !params.protocolVersion ||
        !object(params.capabilities) || !object(params.clientInfo) ||
        typeof params.clientInfo.name !== "string" || !params.clientInfo.name ||
        typeof params.clientInfo.version !== "string" || !params.clientInfo.version) {
      fail(id, -32602, "Invalid or repeated initialization"); return;
    }
    phase = "negotiated";
    send(id, { protocolVersion, capabilities: { tools: {} }, serverInfo: { name: "project-scripts", version: "1.0.0" } });
  } else if (method === "ping") {
    send(id, {});
  } else if (phase !== "ready") {
    fail(id, -32002, "Complete initialization before using tools");
  } else if (method === "tools/list") {
    send(id, { tools: [tool] });
  } else if (method === "tools/call") {
    const args = params.arguments === undefined ? {} : params.arguments;
    if (params.name !== tool.name || !args || typeof args !== "object" || Array.isArray(args) || Object.keys(args).length) {
      fail(id, -32602, "Unknown tool or invalid arguments"); return;
    }
    try {
      if (realpathSync(file) !== file) throw new Error("Linked file rejected");
      const before = lstatSync(file, { bigint: true });
      if (!before.isFile() || before.ino === 0n || before.size > 65536n) throw new Error("File rejected");
      const fd = openSync(file, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
      let text;
      try {
        const opened = fstatSync(fd, { bigint: true });
        if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino) throw new Error("File changed");
        const buffer = Buffer.alloc(65537);
        let size = 0, count;
        while (size < buffer.length && (count = readSync(fd, buffer, size, buffer.length - size, null)) > 0) size += count;
        if (size > 65536) throw new Error("File too large");
        text = buffer.toString("utf8", 0, size);
      } finally { closeSync(fd); }
      const pkg = JSON.parse(text);
      const scripts = pkg.scripts ?? {};
      if (!scripts || typeof scripts !== "object" || Array.isArray(scripts) || Object.values(scripts).some(v => typeof v !== "string")) throw new Error("Invalid scripts");
      send(id, { content: [{ type: "text", text: JSON.stringify({ scripts }) }] });
    } catch {
      send(id, { isError: true, content: [{ type: "text", text: "Cannot read a regular package.json with valid scripts within 64 KiB." }] });
    }
  } else {
    fail(id, -32601, "Method not found");
  }
});

This minimal server validates one read-only tool. It handles newline-delimited JSON-RPC messages, without HTTP endpoints, batch requests, or full production service governance. It provides no operating-system sandbox: the process retains its startup user's permissions.

Add and verify in EvoX

Add a manual MCP connection under extensions, select stdio, and supply:

FieldExample
Nameproject-scripts
CommandAbsolute path to the Node.js executable
ArgumentsAbsolute server-script path and absolute project path, as two separate arguments

Save, then inspect connection and tool discovery. Send the agent:

text
Use project-scripts/read_project_scripts to read this project's npm scripts.
List script names and commands without executing any scripts.

Compare the result with the project's package.json. Confirm configuration save, process startup, discovery, and invocation separately. A listed server name does not establish a usable tool.

Check protocol and errors

This is a tool-call message. The MCP client must first complete initialize and notifications/initialized. The initialize request supplies protocolVersion, a capabilities object, and clientInfo with name and version strings. This server supports protocol 2024-11-05 and returns that version during negotiation; a client that does not support it must disconnect. Tool calls before the completed handshake are rejected, and ping remains available before initialization.

json
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"read_project_scripts","arguments":{}}}
CaseRequired result
Regular projectScripts match the file; none execute
Extra path argumentInvalid-argument error; no other file is read
Missing package.jsonRead error rather than a fabricated empty project
Oversize or symlink fileRead rejected
Server exitsConnection failure; discovery and invocation rechecked after recovery

Extend with write tools

Give writes separate names and schemas. Define resource identity, edits, preview, consent, and idempotency. Enforce scope and account permissions on the server rather than relying on model compliance. Reconcile real operation state after timeouts to prevent duplicate writes.

On upgrades, compare names, schemas, errors, and side effects, then rerun integration cases. Stopping a connection does not revoke remote account access or credentials.

前のページBuild skills次のページHooks