AI Integration

Drive vis from Claude, Cursor, Zed, ChatGPT, and other AI clients via the bundled MCP server and Claude Skill

AI Integration

vis ships an embedded Model Context Protocol server (vis-mcp) and a companion Claude Skill. Together they let an AI assistant inspect your workspace, plan target runs and scaffolds, and diagnose cache rotations — all through the same task graph the CLI uses.

  • MCP server is the wire format. It works with any MCP-aware client: Claude Code, Claude Desktop, Cursor, Zed, ChatGPT (Pro+), Continue, and more.
  • Claude Skill is workflow guidance layered on top. It teaches Claude when to call which tool, and reinforces the "agent prepares, human executes" pattern: every tool is read-only, so the assistant builds the right vis run / vis generate command and a person actually runs it.

The MCP SDK and zod are declared as optional peer dependencies. They are only required when you actually launch vis-mcp — running plain vis does not pull them in.

Installation

Install the optional peers alongside vis:

pnpm add @modelcontextprotocol/sdk zod
# or
npm install @modelcontextprotocol/sdk zod

Verify the bin is wired:

pnpm exec vis-mcp
# [vis-mcp] ready (workspace: /path/to/repo)

The server speaks JSON-RPC over stdio and never writes to stdout outside the protocol — all logs go to stderr.

Client setup

Claude Code

claude mcp add vis -- pnpm exec vis-mcp

Once added, the eight vis tools are available in any Claude Code session opened in this repo.

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
    "mcpServers": {
        "vis": {
            "command": "pnpm",
            "args": ["exec", "vis-mcp"],
            "cwd": "/absolute/path/to/your/repo"
        }
    }
}

Restart Claude Desktop. The vis server will appear in the tools panel.

Cursor

Add to .cursor/mcp.json at the repo root (project-scoped) or ~/.cursor/mcp.json (global):

{
    "mcpServers": {
        "vis": {
            "command": "pnpm",
            "args": ["exec", "vis-mcp"]
        }
    }
}

Zed

Add to your settings:

{
    "context_servers": {
        "vis": {
            "command": "pnpm",
            "args": ["exec", "vis-mcp"]
        }
    }
}

ChatGPT (Developer Mode)

Add the server in Settings → Connectors → Add MCP server:

  • Name: vis
  • Command: pnpm exec vis-mcp
  • Working directory: your repo path

Workspace override

By default vis-mcp operates against process.cwd(). Override it from the client config:

{
    "mcpServers": {
        "vis": {
            "command": "pnpm",
            "args": ["exec", "vis-mcp"],
            "env": { "VIS_MCP_WORKSPACE_ROOT": "/absolute/path/to/repo" }
        }
    }
}

Claude Skill

The package also exports a Claude Skill at node_modules/@visulima/vis/skills/vis/SKILL.md. The skill encodes opinionated workflow guidance — when to use each tool, the safety story for run_task, and how to investigate cache misses.

To activate it project-wide, symlink the skill into your repo:

mkdir -p .claude/skills
ln -s ../../node_modules/@visulima/vis/skills/vis .claude/skills/vis

Or copy it directly if you prefer to keep skills version-controlled:

cp -R node_modules/@visulima/vis/skills/vis .claude/skills/vis

Skills and the MCP server are independent — you can use either, or both. The MCP server alone gives Claude tool access; the skill alone gives Claude workflow guidance for the tools it can already see.

Tool reference

The MCP server exposes eight read-only tools.

list_projects

List all workspace projects with their language, type, layer, tags, and target metadata.

ArgumentTypeDescription
querystring?vis query string (e.g. tag=frontend, type=application)

describe_project

Return full metadata for a single project.

ArgumentTypeDescription
namestringProject name (e.g. @visulima/cerebro)

list_targets

List per-target rows across the workspace. Optionally narrow to one project.

ArgumentTypeDescription
projectstring?Limit to one project

list_templates

List scaffolding templates discovered in .vis/templates/, .moon/templates/, vis.config.ts generator.templates, and the builtins shipped with @visulima/vis (e.g. buildkite-ci).

Takes no arguments. Returns each template's name, source (native | moon | config | builtin | remote), on-disk path, and one-line description. The agent uses source to tell the user when a template is the bundled preset vs. their own copy — and that vendoring .vis/templates/<builtin-name>/ is the supported way to override a builtin.

describe_template

Return the full schema for a single template — useful before suggesting a vis generate invocation.

ArgumentTypeDescription
namestringTemplate name (e.g. package, component)

Returns: description, destination (default target dir), and variables[] with name, type, required, default, prompt, and (for enums) values. The agent uses this to build a fully-qualified vis generate <name> -- --foo=bar command for the user to run.

get_run_logs

Read the run summary from .vis/. Defaults to last-summary.json.

ArgumentTypeDescription
runIdstring?Specific run ID (defaults to latest)
taskIdstring?Filter to one task

cache_why

Diff a task's cache hash against the previous run to pinpoint what changed.

ArgumentTypeDescription
taskIdstringTask ID like @my/app:build
runIdstring?Specific run (defaults to latest)

cache_hash

Print the recorded hash and per-input hash details (command, nodes, runtime, implicitDeps).

ArgumentTypeDescription
taskIdstringTask ID like @my/app:build
runIdstring?Specific run (defaults to latest)

Safety: agent prepares, human executes

vis-mcp deliberately ships no side-effecting tools. There is no run_task and no template-applying tool — the agent's job is to investigate (list_* / describe_* / get_run_logs / cache_*) and then propose the exact vis run or vis generate command for a human to execute. This mirrors the Nx MCP design and removes a whole class of failure modes (runaway builds, accidental writes, long timeouts) that come with letting an LLM drive the runner directly.

If you want the assistant to actually run something for you, paste the suggested command into your terminal — or wire it through a separate tool you trust (e.g. Claude Code's bash tool with explicit per-call approval).

Self-healing CI

vis ai heal (command reference) closes the loop on a failed CI run: it reads the latest failed task from .vis/runs/, asks the configured AI provider for a structured patch, applies it, validates the fix by re-running the failing target with --no-cache --summarize --fail-fast, and — when validation passes — posts the proposal back to the PR/MR.

CI surface detection lives in src/ai/ci-context.ts:

  • GitHub Actions (GITHUB_ACTIONS=true): resolves the PR number from GITHUB_REF (refs/pull/<n>/merge) or the GITHUB_EVENT_PATH payload, captures the head SHA from the event, and reads GITHUB_TOKEN for REST fallback.
  • GitLab CI (GITLAB_CI=true): reads CI_MERGE_REQUEST_IID, CI_API_V4_URL, CI_PROJECT_ID (with CI_PROJECT_PATH fallback), and GITLAB_TOKEN / CI_TOKEN (the auto-injected CI_JOB_TOKEN cannot post MR notes — vis ai heal skips it deliberately).
  • Buildkite (BUILDKITE=true): captures BUILDKITE_BUILD_ID (annotation dedup key), BUILDKITE_BUILD_NUMBER, BUILDKITE_ORGANIZATION_SLUG, BUILDKITE_PIPELINE_SLUG, the literal BUILDKITE_PULL_REQUEST value ("false" for non-PR builds), and BUILDKITE_API_BASE_URL for self-hosted Enterprise. There is no PR-comment surface to listen to — heal acceptance flows through a block step instead.
  • Anything else: provider is reported as unknown and the comment step is skipped — the patch is still applied and validated locally.

Comment posting in src/ai/pr-comment.ts dispatches on the resolved provider:

  • GitHub: tries gh pr comment --body-file - first (uses the runner's bundled gh and auto-injected token, falls back to --repo when the local clone lacks origin). On failure, falls back to POST /repos/:owner/:repo/issues/:n/comments with GITHUB_TOKEN.
  • GitLab: posts directly to POST /projects/:id/merge_requests/:iid/notes via the REST API. glab is not preinstalled on shared runners and project tokens are the standard auth shape, so vis skips the CLI path entirely.
  • Buildkite: posts a build annotation via buildkite-agent annotate with --context vis-ai-heal-${BUILDKITE_BUILD_ID} so reruns update the same annotation in place. Falls back to the Buildkite REST API (POST /v2/.../builds/{n}/annotations, requires BUILDKITE_API_TOKEN, honours BUILDKITE_API_BASE_URL) when the agent CLI isn't available.
  • Push-event runs: report posted: false, method: "skipped" — there's no PR/MR to comment on.

Pass --dry-run to inspect the proposal without applying or commenting; pass --validation-timeout <seconds> to cap how long the re-run is allowed to take (default 30 min). The PR/MR comment (or Buildkite annotation) is bounded at ~60 KB; oversized patch sets drop the inline diff body and leave a pointer to vis ai fix … --apply.

Acceptance: vis ai heal accept

vis ai heal accept (command reference) closes the loop: it re-validates the latest heal proposal, then commits the patch to the PR/MR branch via the upstream provider. The acceptance signal is provider-specific and lives in src/commands/ai/heal-accept.ts:

  • GitHub Actions / GitLab CI: an allow-listed maintainer comments /vis heal accept on the PR/MR.
  • Buildkite: an allow-listed maintainer unblocks a block step. BUILDKITE_UNBLOCKER_EMAIL (preferred) or BUILDKITE_UNBLOCKER is the actor checked against the allow-list — there is no comment phrase. Because Buildkite has no commit API of its own, vis derives the upstream provider from BUILDKITE_REPO (*github* → GitHub, *gitlab* → GitLab) and commits through whichever GITHUB_TOKEN / GITLAB_TOKEN is set. Both HTTPS and SSH BUILDKITE_REPO shapes are recognised, and non-default ports on self-hosted GitLab (e.g. :8443) are preserved in the synthesised API base URL.

When the trigger actor is missing from the allow-list, the error message is provider-aware so the operator can fix the right env var: GitHub/GitLab entries are platform usernames (no leading @), Buildkite entries are unblocker emails or Buildkite usernames. Acceptance is also refused when the change is on a forked repository — the CI token can't push to forks.

Troubleshooting

Cannot find module '@modelcontextprotocol/sdk' — install the optional peer: pnpm add @modelcontextprotocol/sdk zod.

Claude doesn't see the tools — check the client logs. Most clients write MCP startup errors to a debug log; in Claude Desktop it's ~/Library/Logs/Claude/mcp.log.

vis-mcp reports the wrong workspace — set VIS_MCP_WORKSPACE_ROOT in the MCP server's env block. The default process.cwd() depends on where the parent client launches the process.

JSON-RPC framing errors — something in your vis config wrote to stdout. The server is a stdio transport, so any console.log in a config plugin or task hook will corrupt the protocol stream. Move logs to console.error.

Support

Contribute to our work and keep us going

Community is the heart of open source. The success of our packages wouldn't be possible without the incredible contributions of users, testers, and developers who collaborate with us every day.Want to get involved? Here are some tips on how you can make a meaningful impact on our open source projects.

Ready to help us out?

Be sure to check out the package's contribution guidelines first. They'll walk you through the process on how to properly submit an issue or pull request to our repositories.

Submit a pull request

Found something to improve? Fork the repo, make your changes, and open a PR. We review every contribution and provide feedback to help you get merged.

Good first issues

Simple issues suited for people new to open source development, and often a good place to start working on a package.
View good first issues