@visulima/vis-mcp 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/bin.js +2 -2
- package/dist/index.d.ts +89 -104
- package/dist/index.js +1 -1
- package/dist/packem_shared/{createMcpServer-DOAejOGe.js → createMcpServer-BYF8yGpu.js} +1 -1
- package/dist/packem_shared/isSafeOptionValue-BLs7zyGy.js +1 -0
- package/dist/packem_shared/orchestrator-shared-DzTSMbhK.js +4 -0
- package/dist/packem_shared/registerAdvisoryStatus-Ctq9WTp7.js +1 -0
- package/dist/packem_shared/registerAudit-Tg-VQen8.js +1 -0
- package/dist/packem_shared/registerCacheHash-kLMq5p_z.js +1 -0
- package/dist/packem_shared/registerCacheWhy-kAMZHbMk.js +1 -0
- package/dist/packem_shared/registerDescribeTemplate-DFk88tgP.js +1 -0
- package/dist/packem_shared/registerFmt-BdNw7Btl.js +1 -0
- package/dist/packem_shared/registerGetRunLogs-BNGK67zZ.js +1 -0
- package/dist/packem_shared/registerLint-BTPVj4G7.js +1 -0
- package/dist/packem_shared/registerListProjects-RaMh3FYt.js +1 -0
- package/dist/packem_shared/registerListRuns-BYGb5LoE.js +1 -0
- package/package.json +4 -4
- package/dist/packem_shared/isSafePositional-CJq1jfAS.js +0 -1
- package/dist/packem_shared/registerAdvisoryStatus-BHihSIOB.js +0 -1
- package/dist/packem_shared/registerAudit-DTsSWedQ.js +0 -1
- package/dist/packem_shared/registerCacheHash-gEdqaBTi.js +0 -1
- package/dist/packem_shared/registerCacheWhy-DSj2LYlR.js +0 -1
- package/dist/packem_shared/registerDescribeTemplate-6RCmmfQs.js +0 -1
- package/dist/packem_shared/registerFmt-Dh0z0vq3.js +0 -4
- package/dist/packem_shared/registerGetRunLogs-CNGR-EcE.js +0 -1
- package/dist/packem_shared/registerLint-kA0hcRUz.js +0 -4
- package/dist/packem_shared/registerListProjects-DYz2st87.js +0 -1
- package/dist/packem_shared/registerListRuns-BPqPwFUQ.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
## @visulima/vis-mcp [1.0.11](https://github.com/visulima/visulima/compare/%40visulima%2Fvis-mcp%401.0.10...%40visulima%2Fvis-mcp%401.0.11) (2026-08-02)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Dependencies
|
|
5
|
+
|
|
6
|
+
* **@visulima/fs:** upgraded to 5.1.0
|
|
7
|
+
* **@visulima/path:** upgraded to 3.1.0
|
|
8
|
+
* **@visulima/vis:** upgraded to 2.0.2
|
|
9
|
+
|
|
10
|
+
## @visulima/vis-mcp [1.0.10](https://github.com/visulima/visulima/compare/%40visulima%2Fvis-mcp%401.0.9...%40visulima%2Fvis-mcp%401.0.10) (2026-07-27)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
|
|
15
|
+
* **@visulima/fs:** upgraded to 5.0.11
|
|
16
|
+
* **@visulima/vis:** upgraded to 2.0.1
|
|
17
|
+
|
|
1
18
|
## @visulima/vis-mcp [1.0.9](https://github.com/visulima/visulima/compare/%40visulima%2Fvis-mcp%401.0.8...%40visulima%2Fvis-mcp%401.0.9) (2026-07-27)
|
|
2
19
|
|
|
3
20
|
|
package/dist/bin.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
3
|
-
`),process.exit(1)}},
|
|
2
|
+
import{realpathSync as t}from"node:fs";import{argv as a}from"node:process";import{fileURLToPath as e}from"node:url";import{startMcpServer as o}from"./packem_shared/createMcpServer-BYF8yGpu.js";const i=async()=>{try{await o()}catch(r){process.stderr.write(`[vis-mcp] failed to start: ${r instanceof Error?r.message:String(r)}
|
|
3
|
+
`),process.exit(1)}},s=(()=>{const r=a[1];if(r===void 0)return!1;try{return t(r)===t(e(import.meta.url))}catch{return!1}})();s&&await i();export{i as main};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
/**
|
|
3
|
-
* Spawn a `vis` subprocess and capture stdout/stderr without piping to the
|
|
4
|
-
* parent's stdio. The MCP server must keep its own stdout pristine — anything
|
|
5
|
-
* other than JSON-RPC frames corrupts the protocol stream.
|
|
6
|
-
*
|
|
7
|
-
* `visBin` is the resolved path to the user's installed vis CLI; injected so
|
|
8
|
-
* tests can point at a fixture instead of the real CLI.
|
|
9
|
-
*/
|
|
3
|
+
* Spawn a `vis` subprocess and capture stdout/stderr without piping to the
|
|
4
|
+
* parent's stdio. The MCP server must keep its own stdout pristine — anything
|
|
5
|
+
* other than JSON-RPC frames corrupts the protocol stream.
|
|
6
|
+
*
|
|
7
|
+
* `visBin` is the resolved path to the user's installed vis CLI; injected so
|
|
8
|
+
* tests can point at a fixture instead of the real CLI.
|
|
9
|
+
*/
|
|
10
10
|
declare const execVis: (visBin: string, args: ReadonlyArray<string>, options?: ExecOptions) => Promise<ExecResult>;
|
|
11
11
|
/**
|
|
12
|
-
* Run `vis` and parse stdout as JSON. Throws when the subprocess fails or
|
|
13
|
-
* stdout isn't valid JSON — callers map this into an `isError: true` MCP
|
|
14
|
-
* response.
|
|
15
|
-
*/
|
|
12
|
+
* Run `vis` and parse stdout as JSON. Throws when the subprocess fails or
|
|
13
|
+
* stdout isn't valid JSON — callers map this into an `isError: true` MCP
|
|
14
|
+
* response.
|
|
15
|
+
*/
|
|
16
16
|
declare const execVisJson: <T>(visBin: string, args: ReadonlyArray<string>, options?: ExecOptions) => Promise<T>;
|
|
17
17
|
interface ExecResult {
|
|
18
18
|
exitCode: number;
|
|
@@ -26,29 +26,29 @@ interface ExecOptions {
|
|
|
26
26
|
cwd?: string;
|
|
27
27
|
env?: Record<string, string | undefined>;
|
|
28
28
|
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
* Maximum combined stdout+stderr (in code units, an over-approximation of
|
|
30
|
+
* bytes) before the child is killed. Defaults to 64 MiB.
|
|
31
|
+
*/
|
|
32
32
|
maxBufferBytes?: number;
|
|
33
33
|
/** Hard ceiling. The default 120s suits cache lookups and listings. */
|
|
34
34
|
timeoutMs?: number;
|
|
35
35
|
}
|
|
36
36
|
/**
|
|
37
|
-
* Run `vis list ...` and parse stdout as JSON, served from a short-TTL memo
|
|
38
|
-
* keyed on `workspaceRoot` + `args`. A rejected lookup is never cached, so a
|
|
39
|
-
* transient CLI failure does not poison subsequent calls.
|
|
40
|
-
*/
|
|
37
|
+
* Run `vis list ...` and parse stdout as JSON, served from a short-TTL memo
|
|
38
|
+
* keyed on `workspaceRoot` + `args`. A rejected lookup is never cached, so a
|
|
39
|
+
* transient CLI failure does not poison subsequent calls.
|
|
40
|
+
*/
|
|
41
41
|
declare const listVisJson: <T>(visBin: string, workspaceRoot: string, args: ReadonlyArray<string>, ttlMs?: number) => Promise<T>;
|
|
42
42
|
/** Clear the memo. Exposed for tests and long-running embeds that need a reset. */
|
|
43
43
|
declare const clearListCache: () => void;
|
|
44
44
|
/**
|
|
45
|
-
* MCP tool response shape used across all `vis` tools. Kept in one place so
|
|
46
|
-
* adding a new tool doesn't require re-deriving the type from the SDK.
|
|
47
|
-
*
|
|
48
|
-
* The index signature matches the SDK's `CallToolResult` shape — the SDK
|
|
49
|
-
* extends the JSON-RPC result with an open `_meta` bag and refuses anything
|
|
50
|
-
* stricter at the assignment site.
|
|
51
|
-
*/
|
|
45
|
+
* MCP tool response shape used across all `vis` tools. Kept in one place so
|
|
46
|
+
* adding a new tool doesn't require re-deriving the type from the SDK.
|
|
47
|
+
*
|
|
48
|
+
* The index signature matches the SDK's `CallToolResult` shape — the SDK
|
|
49
|
+
* extends the JSON-RPC result with an open `_meta` bag and refuses anything
|
|
50
|
+
* stricter at the assignment site.
|
|
51
|
+
*/
|
|
52
52
|
interface McpToolResponse {
|
|
53
53
|
[key: string]: unknown;
|
|
54
54
|
content: {
|
|
@@ -57,19 +57,19 @@ interface McpToolResponse {
|
|
|
57
57
|
}[];
|
|
58
58
|
isError?: boolean;
|
|
59
59
|
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
* Typed payload mirrored from `content`. Present only for tools that declare
|
|
61
|
+
* an `outputSchema`; lets MCP clients validate and render the result instead
|
|
62
|
+
* of re-parsing the JSON string in the text block.
|
|
63
|
+
*/
|
|
64
64
|
structuredContent?: Record<string, unknown>;
|
|
65
65
|
}
|
|
66
66
|
declare const okResponse: (payload: unknown) => McpToolResponse;
|
|
67
67
|
/**
|
|
68
|
-
* Like {@link okResponse} but also attaches `structuredContent` so clients of a
|
|
69
|
-
* tool that declared an `outputSchema` get a validated, typed payload alongside
|
|
70
|
-
* the JSON text block. Only object payloads are valid structured content per the
|
|
71
|
-
* MCP spec, so the caller must pass a record.
|
|
72
|
-
*/
|
|
68
|
+
* Like {@link okResponse} but also attaches `structuredContent` so clients of a
|
|
69
|
+
* tool that declared an `outputSchema` get a validated, typed payload alongside
|
|
70
|
+
* the JSON text block. Only object payloads are valid structured content per the
|
|
71
|
+
* MCP spec, so the caller must pass a record.
|
|
72
|
+
*/
|
|
73
73
|
declare const okStructuredResponse: (payload: Record<string, unknown>) => McpToolResponse;
|
|
74
74
|
declare const errorResponse: (error: unknown) => McpToolResponse;
|
|
75
75
|
interface ToolContext {
|
|
@@ -79,90 +79,75 @@ interface ToolContext {
|
|
|
79
79
|
workspaceRoot: string;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
* Dependencies a tool needs from the MCP SDK. Threaded through so each tool
|
|
83
|
-
* stays a plain registration function and tests can swap in a fake `server`
|
|
84
|
-
* without touching the real SDK.
|
|
85
|
-
*/
|
|
82
|
+
* Dependencies a tool needs from the MCP SDK. Threaded through so each tool
|
|
83
|
+
* stays a plain registration function and tests can swap in a fake `server`
|
|
84
|
+
* without touching the real SDK.
|
|
85
|
+
*/
|
|
86
86
|
interface ToolDeps {
|
|
87
87
|
server: McpServer;
|
|
88
88
|
}
|
|
89
89
|
/**
|
|
90
|
-
* Register every tool against an already-built MCP server. Pure wiring — no
|
|
91
|
-
* I/O — so tests can drive it against an `InMemoryTransport` without forking
|
|
92
|
-
* a subprocess.
|
|
93
|
-
*/
|
|
90
|
+
* Register every tool against an already-built MCP server. Pure wiring — no
|
|
91
|
+
* I/O — so tests can drive it against an `InMemoryTransport` without forking
|
|
92
|
+
* a subprocess.
|
|
93
|
+
*/
|
|
94
94
|
declare const registerAllTools: (deps: ToolDeps, context: ToolContext) => void;
|
|
95
95
|
/**
|
|
96
|
-
* Build a fully-configured MCP server with all vis tools registered. Returns
|
|
97
|
-
* the server and deps so callers can connect any transport. Tests use this
|
|
98
|
-
* with `InMemoryTransport.createLinkedPair()`.
|
|
99
|
-
*/
|
|
96
|
+
* Build a fully-configured MCP server with all vis tools registered. Returns
|
|
97
|
+
* the server and deps so callers can connect any transport. Tests use this
|
|
98
|
+
* with `InMemoryTransport.createLinkedPair()`.
|
|
99
|
+
*/
|
|
100
100
|
declare const createMcpServer: (context: ToolContext) => Promise<{
|
|
101
101
|
deps: ToolDeps;
|
|
102
102
|
server: McpServer;
|
|
103
103
|
}>;
|
|
104
104
|
/**
|
|
105
|
-
* Boot the vis MCP server over stdio.
|
|
106
|
-
*
|
|
107
|
-
* Critical invariant: nothing in this process may write to stdout outside
|
|
108
|
-
* the MCP transport. Any console.log in tool handlers will frame-corrupt
|
|
109
|
-
* the JSON-RPC stream. Logs go to stderr only.
|
|
110
|
-
*/
|
|
105
|
+
* Boot the vis MCP server over stdio.
|
|
106
|
+
*
|
|
107
|
+
* Critical invariant: nothing in this process may write to stdout outside
|
|
108
|
+
* the MCP transport. Any console.log in tool handlers will frame-corrupt
|
|
109
|
+
* the JSON-RPC stream. Logs go to stderr only.
|
|
110
|
+
*/
|
|
111
111
|
declare const startMcpServer: () => Promise<void>;
|
|
112
|
-
declare const registerAdvisoryStatus: ({
|
|
113
|
-
|
|
114
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
115
|
-
declare const
|
|
116
|
-
|
|
117
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
118
|
-
declare const
|
|
119
|
-
|
|
120
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
121
|
-
declare const
|
|
122
|
-
|
|
123
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
124
|
-
declare const
|
|
125
|
-
server
|
|
126
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
127
|
-
declare const registerDescribeTemplate: ({
|
|
128
|
-
server
|
|
129
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
130
|
-
declare const registerFmt: ({
|
|
131
|
-
server
|
|
132
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
133
|
-
declare const registerGetRunLogs: ({
|
|
134
|
-
server
|
|
135
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
136
|
-
declare const registerLint: ({
|
|
137
|
-
server
|
|
138
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
139
|
-
declare const registerListProjects: ({
|
|
140
|
-
server
|
|
141
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
142
|
-
declare const registerListRuns: ({
|
|
143
|
-
server
|
|
144
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
145
|
-
declare const registerListTargets: ({
|
|
146
|
-
server
|
|
147
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
148
|
-
declare const registerListTemplates: ({
|
|
149
|
-
server
|
|
150
|
-
}: ToolDeps, context: ToolContext) => void;
|
|
112
|
+
declare const registerAdvisoryStatus: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
113
|
+
declare const registerAudit: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
114
|
+
declare const registerCacheHash: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
115
|
+
declare const registerCacheWhy: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
116
|
+
declare const registerDescribeProject: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
117
|
+
declare const registerDescribeTemplate: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
118
|
+
declare const registerFmt: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
119
|
+
declare const registerGetRunLogs: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
120
|
+
declare const registerLint: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
121
|
+
declare const registerListProjects: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
122
|
+
declare const registerListRuns: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
123
|
+
declare const registerListTargets: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
124
|
+
declare const registerListTemplates: ({ server }: ToolDeps, context: ToolContext) => void;
|
|
151
125
|
declare const isValidRunId: (value: string) => boolean;
|
|
152
126
|
/**
|
|
153
|
-
* Task IDs are `<project>:<target>`; both halves are package or npm-scope
|
|
154
|
-
* shaped. The exec layer uses argv-form spawn so shell metacharacters are
|
|
155
|
-
* inert, but a leading `-` would be parsed as a CLI flag by the vis CLI.
|
|
156
|
-
*/
|
|
127
|
+
* Task IDs are `<project>:<target>`; both halves are package or npm-scope
|
|
128
|
+
* shaped. The exec layer uses argv-form spawn so shell metacharacters are
|
|
129
|
+
* inert, but a leading `-` would be parsed as a CLI flag by the vis CLI.
|
|
130
|
+
*/
|
|
157
131
|
declare const isValidTaskId: (value: string) => boolean;
|
|
158
132
|
/**
|
|
159
|
-
* Guard for free-form positional arguments forwarded to the vis CLI (template
|
|
160
|
-
* names, file paths). Spawn is argv-form so shell metacharacters are inert, but
|
|
161
|
-
* a leading `-` would be parsed as a CLI flag — turning an LLM-supplied
|
|
162
|
-
* `files: ["--fix"]` into a write operation that violates the `readOnlyHint`
|
|
163
|
-
* contract clients rely on for auto-approval. Callers must additionally insert
|
|
164
|
-
* a literal `--` separator before any positionals so a value that *becomes*
|
|
165
|
-
* flag-shaped through some other path can never be reinterpreted as an option.
|
|
166
|
-
*/
|
|
133
|
+
* Guard for free-form positional arguments forwarded to the vis CLI (template
|
|
134
|
+
* names, file paths). Spawn is argv-form so shell metacharacters are inert, but
|
|
135
|
+
* a leading `-` would be parsed as a CLI flag — turning an LLM-supplied
|
|
136
|
+
* `files: ["--fix"]` into a write operation that violates the `readOnlyHint`
|
|
137
|
+
* contract clients rely on for auto-approval. Callers must additionally insert
|
|
138
|
+
* a literal `--` separator before any positionals so a value that *becomes*
|
|
139
|
+
* flag-shaped through some other path can never be reinterpreted as an option.
|
|
140
|
+
*/
|
|
167
141
|
declare const isSafePositional: (value: string) => boolean;
|
|
168
|
-
|
|
142
|
+
/**
|
|
143
|
+
* Guard for free-form values forwarded as the argument of a CLI option (e.g.
|
|
144
|
+
* `--since <ref>`, `--query <expr>`, `--ecosystem <list>`, `--db <path>`). The
|
|
145
|
+
* option name is a fixed literal, but the value itself is LLM-supplied: the
|
|
146
|
+
* `@visulima/command-line-args` tokenizer classifies any `--x`/`-x` token as an
|
|
147
|
+
* option rather than the preceding option's value, so a flag-shaped value like
|
|
148
|
+
* `since: "--fix"` is re-read as a real flag — flipping a `readOnlyHint: true`
|
|
149
|
+
* tool into a write. Reject the leading `-` at the tool boundary before pushing
|
|
150
|
+
* the value onto argv.
|
|
151
|
+
*/
|
|
152
|
+
declare const isSafeOptionValue: (value: string) => boolean;
|
|
153
|
+
export { type ExecOptions, type ExecResult, type McpToolResponse, type ToolContext, type ToolDeps, clearListCache, createMcpServer, errorResponse, execVis, execVisJson, isSafeOptionValue, isSafePositional, isValidRunId, isValidTaskId, listVisJson, okResponse, okStructuredResponse, registerAdvisoryStatus, registerAllTools, registerAudit, registerCacheHash, registerCacheWhy, registerDescribeProject, registerDescribeTemplate, registerFmt, registerGetRunLogs, registerLint, registerListProjects, registerListRuns, registerListTargets, registerListTemplates, startMcpServer };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{execVis as t,execVisJson as o}from"./packem_shared/execVis-CMeUcwPz.js";import{clearListCache as i,listVisJson as p}from"./packem_shared/clearListCache-4BxipCYS.js";import{errorResponse as
|
|
1
|
+
import{execVis as t,execVisJson as o}from"./packem_shared/execVis-CMeUcwPz.js";import{clearListCache as i,listVisJson as p}from"./packem_shared/clearListCache-4BxipCYS.js";import{errorResponse as f,okResponse as x,okStructuredResponse as a}from"./packem_shared/errorResponse-CVaJrOoB.js";import{createMcpServer as c,registerAllTools as l,startMcpServer as n}from"./packem_shared/createMcpServer-BYF8yGpu.js";import{registerAdvisoryStatus as d}from"./packem_shared/registerAdvisoryStatus-Ctq9WTp7.js";import{registerAudit as R}from"./packem_shared/registerAudit-Tg-VQen8.js";import{registerCacheHash as V}from"./packem_shared/registerCacheHash-kLMq5p_z.js";import{registerCacheWhy as T}from"./packem_shared/registerCacheWhy-kAMZHbMk.js";import{registerDescribeProject as v}from"./packem_shared/registerDescribeProject-HYlvD0tm.js";import{registerDescribeTemplate as C}from"./packem_shared/registerDescribeTemplate-DFk88tgP.js";import{registerFmt as b}from"./packem_shared/registerFmt-BdNw7Btl.js";import{registerGetRunLogs as y}from"./packem_shared/registerGetRunLogs-BNGK67zZ.js";import{registerLint as I}from"./packem_shared/registerLint-BTPVj4G7.js";import{registerListProjects as M}from"./packem_shared/registerListProjects-RaMh3FYt.js";import{registerListRuns as G}from"./packem_shared/registerListRuns-BYGb5LoE.js";import{registerListTargets as O}from"./packem_shared/registerListTargets-IWnJkyzy.js";import{registerListTemplates as q}from"./packem_shared/registerListTemplates-CSsN3JAE.js";import{isSafeOptionValue as z,isSafePositional as B,isValidRunId as E,isValidTaskId as K}from"./packem_shared/isSafeOptionValue-BLs7zyGy.js";export{i as clearListCache,c as createMcpServer,f as errorResponse,t as execVis,o as execVisJson,z as isSafeOptionValue,B as isSafePositional,E as isValidRunId,K as isValidTaskId,p as listVisJson,x as okResponse,a as okStructuredResponse,d as registerAdvisoryStatus,l as registerAllTools,R as registerAudit,V as registerCacheHash,T as registerCacheWhy,v as registerDescribeProject,C as registerDescribeTemplate,b as registerFmt,y as registerGetRunLogs,I as registerLint,M as registerListProjects,G as registerListRuns,O as registerListTargets,q as registerListTemplates,n as startMcpServer};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{readFile as g}from"node:fs/promises";import{createRequire as l}from"node:module";import{resolve as u,dirname as f}from"node:path";import{pathToFileURL as d,fileURLToPath as h}from"node:url";import{McpServer as w}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as b}from"@modelcontextprotocol/sdk/server/stdio.js";import{findUp as I}from"@visulima/fs";import{registerAdvisoryStatus as S}from"./registerAdvisoryStatus-
|
|
1
|
+
import{readFile as g}from"node:fs/promises";import{createRequire as l}from"node:module";import{resolve as u,dirname as f}from"node:path";import{pathToFileURL as d,fileURLToPath as h}from"node:url";import{McpServer as w}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as b}from"@modelcontextprotocol/sdk/server/stdio.js";import{findUp as I}from"@visulima/fs";import{registerAdvisoryStatus as S}from"./registerAdvisoryStatus-Ctq9WTp7.js";import{registerAudit as N}from"./registerAudit-Tg-VQen8.js";import{registerCacheHash as y}from"./registerCacheHash-kLMq5p_z.js";import{registerCacheWhy as _}from"./registerCacheWhy-kAMZHbMk.js";import{registerDescribeProject as k}from"./registerDescribeProject-HYlvD0tm.js";import{registerDescribeTemplate as P}from"./registerDescribeTemplate-DFk88tgP.js";import{registerFmt as R}from"./registerFmt-BdNw7Btl.js";import{registerGetRunLogs as C}from"./registerGetRunLogs-BNGK67zZ.js";import{registerLint as L}from"./registerLint-BTPVj4G7.js";import{registerListProjects as T}from"./registerListProjects-RaMh3FYt.js";import{registerListRuns as V}from"./registerListRuns-BYGb5LoE.js";import{registerListTargets as j}from"./registerListTargets-IWnJkyzy.js";import{registerListTemplates as M}from"./registerListTemplates-CSsN3JAE.js";const p="1.0.0-alpha.35",v=r=>{const[e,s=""]=r.split("-",2);return{core:(e??"").split(".").map(t=>Number.parseInt(t,10)||0),pre:s.length>0?s.split("."):[]}},$=(r,e)=>{if(r===void 0)return e===void 0?0:-1;if(e===void 0)return 1;const s=Number.parseInt(r,10),t=Number.parseInt(e,10);return!Number.isNaN(s)&&!Number.isNaN(t)?s-t:r.localeCompare(e)},O=(r,e)=>{if(r.length===0&&e.length>0)return 1;if(r.length>0&&e.length===0)return-1;const s=Math.max(r.length,e.length);for(let t=0;t<s;t+=1){const i=$(r[t],e[t]);if(i!==0)return i}return 0},q=(r,e)=>{const s=v(r),t=v(e);for(let i=0;i<3;i+=1){const o=(s.core[i]??0)-(t.core[i]??0);if(o!==0)return o}return O(s.pre,t.pre)},E=r=>{!r||q(r,p)>=0||process.stderr.write(`[vis-mcp] warning: @visulima/vis ${r} is older than the required ${p}; lint/fmt/audit tools may fail with opaque errors. Upgrade @visulima/vis to >= ${p}.
|
|
2
2
|
`)},A=r=>{const e=process.env.VIS_MCP_VIS_BIN;if(e)return{binPath:u(e)};const s=[{label:r,require:l(d(`${r}/package.json`))},{label:"vis-mcp install location",require:l(import.meta.url)}],t=[];let i,o;for(const{label:n,require:a}of s)try{i=a.resolve("@visulima/vis/package.json"),o=a("@visulima/vis/package.json");break}catch(m){t.push({error:m,label:n})}if(!i||!o){const n=t.at(-1)?.error,a=t.map(m=>m.label).join(", ");throw new Error(`Cannot resolve \`@visulima/vis\` (tried: ${a}). Install it as a workspace dependency, or set VIS_MCP_VIS_BIN to a vis CLI path.`,{cause:n})}const c=typeof o.bin=="string"?o.bin:o.bin?.vis;if(!c)throw new Error("`@visulima/vis` is installed but does not declare a `vis` bin entry — install a current version.");return{binPath:u(f(i),c),version:o.version}},U=async()=>{try{const r=await I("package.json",{cwd:f(h(import.meta.url))});return r?JSON.parse(await g(r,"utf8")).version??"0.0.0":"0.0.0"}catch(r){return process.stderr.write(`[vis-mcp] failed to read package.json: ${r instanceof Error?r.message:String(r)}
|
|
3
3
|
`),"0.0.0"}},B=(r,e)=>{T(r,e),k(r,e),j(r,e),M(r,e),P(r,e),C(r,e),V(r,e),_(r,e),y(r,e),N(r,e),S(r,e),L(r,e),R(r,e)},D=async r=>{const e=new w({name:"vis",version:await U()}),s={server:e};return B(s,r),{deps:s,server:e}},mr=async()=>{const r=process.env.VIS_MCP_WORKSPACE_ROOT??process.cwd(),{binPath:e,version:s}=A(r);E(s);const t={visBin:e,workspaceRoot:r},{server:i}=await D(t);await i.connect(new b),process.stderr.write(`[vis-mcp] ready (workspace: ${t.workspaceRoot})
|
|
4
4
|
`)};export{p as MIN_VIS_VERSION,q as compareVersions,D as createMcpServer,B as registerAllTools,mr as startMcpServer,E as warnIfVisTooOld};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=/^[\w.-]+$/,n=i=>e.test(i),d=i=>i.length>0&&!i.startsWith("-")&&i.includes(":"),l=i=>i.length>0&&!i.startsWith("-"),o=i=>!i.startsWith("-"),r=(i,s)=>{if(!s||s.length===0)return;const t=s.find(a=>!l(a));if(t!==void 0)return`Invalid file path "${t}". A leading "-" would be parsed as a CLI flag.`;i.push("--",...s)};export{r as appendPositionalFiles,o as isSafeOptionValue,l as isSafePositional,n as isValidRunId,d as isValidTaskId};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import*as r from"zod";import{execVis as l}from"./execVis-CMeUcwPz.js";import{errorResponse as t,okStructuredResponse as m}from"./errorResponse-CVaJrOoB.js";const g=r.object({adapter:r.string(),column:r.number().optional(),endColumn:r.number().optional(),endLine:r.number().optional(),file:r.string(),fixable:r.boolean().optional(),line:r.number().optional(),message:r.string(),ruleId:r.string().optional(),severity:r.enum(["error","info","warning"])}).catchall(r.unknown()),w=r.object({adapter:r.string(),durationMs:r.number(),exitCode:r.number(),findingCount:r.number()}).catchall(r.unknown()),b=async(i,o,a,u,c)=>{try{const e=await l(i.visBin,a,{cwd:i.workspaceRoot});if(e.timedOut)return t(new Error(`vis ${o} timed out`));if(e.overflowed)return t(new Error(`vis ${o} produced more output than the buffer ceiling allows and was killed`));if(e.stdout.trim().length===0){const n=e.stderr.trim().split(`
|
|
2
|
+
`).slice(-5).join(`
|
|
3
|
+
`);return t(new Error(`vis ${o} exited with code ${String(e.exitCode)} and no JSON output${n?`
|
|
4
|
+
${n}`:""}`))}let s;try{s=JSON.parse(e.stdout)}catch(n){return t(new Error(`vis ${a.join(" ")} did not emit valid JSON: ${n instanceof Error?n.message:String(n)}`,{cause:n}))}const d=u.parse(s);return m(c(d,e.exitCode))}catch(e){return t(e)}};export{b as a,g as f,w as r};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"zod";import{execVisJson as d}from"./execVis-CMeUcwPz.js";import{errorResponse as a,okStructuredResponse as u}from"./errorResponse-CVaJrOoB.js";import{isSafeOptionValue as l}from"./isSafeOptionValue-BLs7zyGy.js";const m=e.object({advisoryCount:e.number(),lastSyncIso:e.string(),manifestEtag:e.string().nullable(),name:e.string()}),o=e.object({dbPath:e.string(),ecosystems:e.array(m),exists:e.boolean(),schemaVersion:e.number(),sizeBytes:e.number()}).catchall(e.unknown()),p=o.shape,v=({server:i},r)=>{i.registerTool("advisory_status",{annotations:{readOnlyHint:!0},description:"Report the local OSV advisory DB freshness via `vis advisories status --format json`: DB path, schema version, per-ecosystem advisory counts, last sync timestamp, and ETag. Use this to detect whether the offline cache is stale before running an `audit` in offline mode.",inputSchema:{db:e.string().min(1).optional().describe("Override the cache DB path (default: <cache>/vis/advisories/db.sqlite).")},outputSchema:p},async s=>{try{const t=["advisories","status","--format","json"];if(s.db!==void 0){if(!l(s.db))return a(new Error(`Invalid --db value "${s.db}". A leading "-" would be parsed as a CLI flag.`));t.push("--db",s.db)}const n=await d(r.visBin,t,{cwd:r.workspaceRoot}),c=o.parse(n);return u(c)}catch(t){return a(t)}})};export{v as registerAdvisoryStatus};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"zod";import{execVisJson as l}from"./execVis-CMeUcwPz.js";import{errorResponse as o,okStructuredResponse as p}from"./errorResponse-CVaJrOoB.js";import{isSafeOptionValue as u}from"./isSafeOptionValue-BLs7zyGy.js";const d=e.object({fixedVersions:e.array(e.string()).optional(),id:e.string(),severity:e.string(),summary:e.string().optional()}),m=e.object({acceptedRisk:e.unknown().optional(),name:e.string(),socketAlerts:e.array(e.unknown()).optional(),socketScore:e.number().nullable().optional(),version:e.string(),vulnerabilities:e.array(d)}),a=e.object({duplicates:e.array(e.object({name:e.string(),versionCount:e.number(),versions:e.array(e.string())})),packages:e.number(),results:e.array(m),summary:e.object({accepted:e.number(),duplicatePackages:e.number(),issues:e.number(),total:e.number()})}).catchall(e.unknown()),y=a.shape,h=["low","medium","high","critical"],v=({server:i},r)=>{i.registerTool("audit",{annotations:{readOnlyHint:!0},description:"Audit installed packages for known vulnerabilities and supply-chain risk via `vis audit --format json`. Defaults to the local offline OSV cache when one exists, otherwise falls back to the live OSV API. Returns the parsed audit payload (results, duplicates, summary).",inputSchema:{ecosystem:e.string().optional().describe("Comma-separated OSV ecosystems to scan. Defaults to 'npm' (the only matcher wired today)."),offline:e.boolean().optional().describe("Force the local OSV cache. Errors if the cache is missing — run `vis advisories sync` first."),prodOnly:e.boolean().optional().describe("Skip devDependencies — scan the production graph only."),severity:e.enum(h).optional().describe("Minimum severity to report. Defaults to 'low'."),showAccepted:e.boolean().optional().describe("Include findings already on the workspace's accepted-risk list."),usage:e.boolean().optional().describe("Apply the reachability filter — only report vulnerabilities in statically-imported packages.")},outputSchema:y},async s=>{try{const t=["audit","--format","json"];if(s.severity&&t.push("--severity",s.severity),s.offline&&t.push("--offline"),s.prodOnly&&t.push("--prod-only"),s.usage&&t.push("--usage"),s.ecosystem){if(!u(s.ecosystem))return o(new Error(`Invalid --ecosystem value "${s.ecosystem}". A leading "-" would be parsed as a CLI flag.`));t.push("--ecosystem",s.ecosystem)}s.showAccepted&&t.push("--show-accepted");const n=await l(r.visBin,t,{cwd:r.workspaceRoot}),c=a.parse(n);return p(c)}catch(t){return o(t)}})};export{v as registerAudit};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as n from"zod";import{execVisJson as d}from"./execVis-CMeUcwPz.js";import{errorResponse as s,okResponse as c}from"./errorResponse-CVaJrOoB.js";import{isValidTaskId as p,isValidRunId as u}from"./isSafeOptionValue-BLs7zyGy.js";const I=({server:a},i)=>{a.registerTool("cache_hash",{annotations:{readOnlyHint:!0},description:"Print the recorded hash and per-input hash details for a task — the contributing inputs (command, nodes, runtime, implicitDeps) that produced it.",inputSchema:{runId:n.string().optional().describe("Run ID to inspect (default: latest)"),taskId:n.string().describe('Task ID like "@my/app:build"')}},async({runId:t,taskId:e})=>{if(!p(e))return s(new Error(`Invalid taskId "${e}". Expected "<project>:<target>".`));if(t!==void 0&&!u(t))return s(new Error(`Invalid runId "${t}". Expected filesystem-safe slug ([A-Za-z0-9_.-]).`));try{const r=["cache","hash",e,"--format=json"];t&&r.push("--run",t);const o=await d(i.visBin,r,{cwd:i.workspaceRoot});return c(o)}catch(r){return s(r)}})};export{I as registerCacheHash};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as n from"zod";import{execVisJson as c}from"./execVis-CMeUcwPz.js";import{errorResponse as s,okResponse as d}from"./errorResponse-CVaJrOoB.js";import{isValidTaskId as p,isValidRunId as u}from"./isSafeOptionValue-BLs7zyGy.js";const f=({server:i},a)=>{i.registerTool("cache_why",{annotations:{readOnlyHint:!0},description:"Explain why a task's cache hash rotated between runs. Diffs the task's hashDetails (command, nodes, runtime, implicitDeps) against the previous run to pinpoint what changed.",inputSchema:{runId:n.string().optional().describe("Run ID to inspect (default: latest)"),taskId:n.string().describe('Task ID like "@my/app:build"')}},async({runId:t,taskId:r})=>{if(!p(r))return s(new Error(`Invalid taskId "${r}". Expected "<project>:<target>".`));if(t!==void 0&&!u(t))return s(new Error(`Invalid runId "${t}". Expected filesystem-safe slug ([A-Za-z0-9_.-]).`));try{const e=["cache","why",r,"--format=json"];t&&e.push("--run",t);const o=await c(a.visBin,e,{cwd:a.workspaceRoot});return d(o)}catch(e){return s(e)}})};export{f as registerCacheWhy};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as s from"zod";import{execVisJson as o}from"./execVis-CMeUcwPz.js";import{errorResponse as r,okResponse as i}from"./errorResponse-CVaJrOoB.js";import{isSafePositional as m}from"./isSafeOptionValue-BLs7zyGy.js";const d=({server:n},a)=>{n.registerTool("describe_template",{annotations:{readOnlyHint:!0},description:"Return full metadata for a scaffolding template: description, default destination, and the variable schema (name, type, default, required, prompt, enum values). Use this before suggesting a `vis generate` command so option values can be filled correctly.",inputSchema:{name:s.string().describe('Template name (e.g. "package", "component"). Use list_templates to see available names.')}},async({name:e})=>{if(!m(e))return r(new Error(`Invalid template name "${e}". A leading "-" would be parsed as a CLI flag.`));try{const t=await o(a.visBin,["generate","--describe","--json","--",e],{cwd:a.workspaceRoot});return i(t)}catch(t){return r(t)}})};export{d as registerDescribeTemplate};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"zod";import{errorResponse as o}from"./errorResponse-CVaJrOoB.js";import{isSafeOptionValue as l,appendPositionalFiles as m}from"./isSafeOptionValue-BLs7zyGy.js";import{r as s,f as a,a as h}from"./orchestrator-shared-DzTSMbhK.js";const p=e.object({findings:e.array(a),mode:e.enum(["check","fix"]).optional(),runs:e.array(s)}).catchall(e.unknown()),u={exitCode:e.number(),findings:e.array(a),mode:e.enum(["check","fix"]),runs:e.array(s)},y=({server:c},d)=>{c.registerTool("fmt",{annotations:{readOnlyHint:!0},description:"Run the vis fmt orchestrator in --check mode via `vis fmt --check --format json`. Auto-detects every installed formatter (oxfmt, biome, dprint, prettier, ruff-fmt, deno-fmt) and returns the merged { findings, runs } payload — each finding represents a file that would change. This tool never writes to disk; invoke `vis fmt` directly to apply the formatting.",inputSchema:{files:e.array(e.string()).optional().describe("Restrict the check to these files (relative to the workspace root). Empty/omitted = workspace-wide."),quiet:e.boolean().optional().describe("Suppress per-file logs (findings still flow through)."),since:e.string().optional().describe("Forward `--since <ref>` so only files changed vs `<ref>` are checked."),staged:e.boolean().optional().describe("Forward `--staged` so only files in the git index are checked.")},outputSchema:u},async t=>{const r=["fmt","--check","--format","json"];if(t.quiet&&r.push("--quiet"),t.staged)r.push("--staged");else if(t.since!==void 0&&t.since.length>0){if(!l(t.since))return o(new Error(`Invalid --since value "${t.since}". A leading "-" would be parsed as a CLI flag.`));r.push("--since",t.since)}const i=m(r,t.files);return i!==void 0?o(new Error(i)):h(d,"fmt",r,p,(n,f)=>({...n,exitCode:f,mode:n.mode??"check"}))})};export{y as registerFmt};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readFile as c}from"node:fs/promises";import{join as u}from"@visulima/path";import*as d from"zod";import{errorResponse as e,okResponse as l}from"./errorResponse-CVaJrOoB.js";import{isValidRunId as f}from"./isSafeOptionValue-BLs7zyGy.js";const R=({server:p},o)=>{p.registerTool("get_run_logs",{annotations:{readOnlyHint:!0},description:"Read a vis run summary from .task-runner/. Returns the most recent run by default, or a specific run when `runId` is set. Optionally filter to a single task with `taskId`.",inputSchema:{runId:d.string().optional().describe("Run ID (default: latest)"),taskId:d.string().optional().describe('Filter to a single task ID like "@my/app:build"')}},async({runId:r,taskId:s})=>{if(r!==void 0&&!f(r))return e(new Error(`Invalid runId "${r}". Expected filesystem-safe slug ([A-Za-z0-9_.-]).`));const a=r?u(o.workspaceRoot,".task-runner","runs",`${r}.json`):u(o.workspaceRoot,".task-runner","last-summary.json");try{const t=await c(a,"utf8"),n=JSON.parse(t);if(s){const i=n.tasks?.find(m=>m.taskId===s);return i?l({runId:n.runId,task:i}):e(new Error(`Task "${s}" not found in run "${n.runId??"(unknown)"}".`))}return l(n)}catch(t){return t.code==="ENOENT"?e(new Error(`No run summary at ${a}. Run a task first to populate it.`)):e(t)}})};export{R as registerGetRunLogs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"zod";import{errorResponse as t}from"./errorResponse-CVaJrOoB.js";import{isSafeOptionValue as p,appendPositionalFiles as u}from"./isSafeOptionValue-BLs7zyGy.js";import{r as s,f as o,a as f}from"./orchestrator-shared-DzTSMbhK.js";const g=e.object({findings:e.array(o),runs:e.array(s)}).catchall(e.unknown()),h={exitCode:e.number(),findings:e.array(o),runs:e.array(s)},w=({server:a},l)=>{a.registerTool("lint",{annotations:{readOnlyHint:!0},description:"Run the vis lint orchestrator in check-only mode via `vis lint --format json`. Auto-detects every installed linter (oxlint, biome, eslint, stylelint, ruff, markdownlint, shellcheck, deno-lint) and returns the merged { findings, runs } payload. This tool never applies fixes — invoke `vis lint --fix` directly for that.",inputSchema:{files:e.array(e.string()).optional().describe("Restrict the run to these files (relative to the workspace root). Empty/omitted = workspace-wide."),maxWarnings:e.int().nonnegative().optional().describe("Forward `--max-warnings N` to every adapter that supports it."),quiet:e.boolean().optional().describe("Suppress warning-severity findings (errors still flow through)."),since:e.string().optional().describe("Forward `--since <ref>` so only files changed vs `<ref>` are linted."),staged:e.boolean().optional().describe("Forward `--staged` so only files in the git index are linted.")},outputSchema:h},async i=>{const r=["lint","--format","json"];if(i.quiet&&r.push("--quiet"),typeof i.maxWarnings=="number"&&r.push("--max-warnings",String(i.maxWarnings)),i.staged)r.push("--staged");else if(i.since!==void 0&&i.since.length>0){if(!p(i.since))return t(new Error(`Invalid --since value "${i.since}". A leading "-" would be parsed as a CLI flag.`));r.push("--since",i.since)}const n=u(r,i.files);return n!==void 0?t(new Error(n)):f(l,"lint",r,g,(d,c)=>({...d,exitCode:c}))})};export{w as registerLint};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as a from"zod";import{execVisJson as n}from"./execVis-CMeUcwPz.js";import{errorResponse as i,okResponse as p}from"./errorResponse-CVaJrOoB.js";import{isSafeOptionValue as l}from"./isSafeOptionValue-BLs7zyGy.js";const y=({server:s},t)=>{s.registerTool("list_projects",{annotations:{readOnlyHint:!0},description:'List all workspace projects with their language, type, layer, tags, and target metadata. Optionally filter with a vis query string (e.g. "tag=frontend", "type=application").',inputSchema:{query:a.string().optional().describe('Filter projects by vis query string (e.g. "tag=frontend")')}},async({query:e})=>{try{const r=["list","--json"];if(e){if(!l(e))return i(new Error(`Invalid --query value "${e}". A leading "-" would be parsed as a CLI flag.`));r.push("--query",e)}const o=await n(t.visBin,r,{cwd:t.workspaceRoot});return p({count:o.length,projects:o})}catch(r){return i(r)}})};export{y as registerListProjects};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{readdir as k,stat as y,readFile as g}from"node:fs/promises";import{join as d}from"@visulima/path";import*as w from"zod";import{okResponse as f,errorResponse as m}from"./errorResponse-CVaJrOoB.js";const v=/\.json$/,M=a=>{if(typeof a.status=="string")return a.status;if(a.tasks)return a.tasks.some(i=>i.status==="failure"||i.status==="failed")?"failure":"success"},A=({server:a},i)=>{a.registerTool("list_runs",{annotations:{readOnlyHint:!0},description:'List recorded vis runs from `.task-runner/runs/`, most recent first. Each entry includes the run ID, start timestamp, derived status, task count, and failed-task count — use it to discover run IDs for `get_run_logs`, `cache_hash`, or `cache_why` (e.g. "compare this run to the previous one").',inputSchema:{limit:w.int().positive().max(100).optional().describe("Maximum runs to return (default: 20, newest first).")}},async({limit:p})=>{const o=d(i.workspaceRoot,".task-runner","runs");let c;try{c=(await k(o)).filter(s=>s.endsWith(".json"))}catch(s){return s.code==="ENOENT"?f({count:0,runs:[]}):m(s)}try{const s=(await Promise.all(c.map(async t=>{try{const e=await y(d(o,t));return{file:t,modifiedMs:e.mtimeMs}}catch{return}}))).filter(t=>t!==void 0).toSorted((t,e)=>e.modifiedMs-t.modifiedMs).slice(0,p??20),l=(await Promise.all(s.map(async({file:t,modifiedMs:e})=>{try{const u=await g(d(o,t),"utf8"),r=JSON.parse(u);return{failedTasks:r.tasks?.filter(n=>n.status==="failure"||n.status==="failed").length,modifiedMs:e,runId:r.runId??t.replace(v,""),startedAt:r.startedAt,status:M(r),taskCount:r.tasks?.length}}catch{return}}))).filter(t=>t!==void 0).map(t=>{const{failedTasks:e,runId:u,startedAt:r,status:n,taskCount:h}=t;return{failedTasks:e,runId:u,startedAt:r,status:n,taskCount:h}});return f({count:l.length,runs:l})}catch(s){return m(s)}})};export{A as registerListRuns};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@visulima/vis-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server for @visulima/vis — exposes vis tooling to AI agents over stdio",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -59,12 +59,12 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
62
|
-
"@visulima/fs": "5.0
|
|
63
|
-
"@visulima/path": "3.
|
|
62
|
+
"@visulima/fs": "5.1.0",
|
|
63
|
+
"@visulima/path": "3.1.0",
|
|
64
64
|
"zod": "4.4.3"
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
|
-
"@visulima/vis": "2.0.
|
|
67
|
+
"@visulima/vis": "2.0.2"
|
|
68
68
|
},
|
|
69
69
|
"engines": {
|
|
70
70
|
"node": "^22.14.0 || >=24.10.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const e=/^[\w.-]+$/,n=i=>e.test(i),d=i=>i.length>0&&!i.startsWith("-")&&i.includes(":"),l=i=>i.length>0&&!i.startsWith("-"),o=(i,s)=>{if(!s||s.length===0)return;const t=s.find(a=>!l(a));if(t!==void 0)return`Invalid file path "${t}". A leading "-" would be parsed as a CLI flag.`;i.push("--",...s)};export{o as appendPositionalFiles,l as isSafePositional,n as isValidRunId,d as isValidTaskId};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as s from"zod";import{execVisJson as c}from"./execVis-CMeUcwPz.js";import{okStructuredResponse as d,errorResponse as u}from"./errorResponse-CVaJrOoB.js";const m=s.object({advisoryCount:s.number(),lastSyncIso:s.string(),manifestEtag:s.string().nullable(),name:s.string()}),o=s.object({dbPath:s.string(),ecosystems:s.array(m),exists:s.boolean(),schemaVersion:s.number(),sizeBytes:s.number()}).catchall(s.unknown()),h=o.shape,v=({server:a},t)=>{a.registerTool("advisory_status",{annotations:{readOnlyHint:!0},description:"Report the local OSV advisory DB freshness via `vis advisories status --format json`: DB path, schema version, per-ecosystem advisory counts, last sync timestamp, and ETag. Use this to detect whether the offline cache is stale before running an `audit` in offline mode.",inputSchema:{db:s.string().min(1).optional().describe("Override the cache DB path (default: <cache>/vis/advisories/db.sqlite).")},outputSchema:h},async r=>{try{const e=["advisories","status","--format","json"];r.db!==void 0&&e.push("--db",r.db);const i=await c(t.visBin,e,{cwd:t.workspaceRoot}),n=o.parse(i);return d(n)}catch(e){return u(e)}})};export{v as registerAdvisoryStatus};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as e from"zod";import{execVisJson as c}from"./execVis-CMeUcwPz.js";import{okStructuredResponse as l,errorResponse as p}from"./errorResponse-CVaJrOoB.js";const u=e.object({fixedVersions:e.array(e.string()).optional(),id:e.string(),severity:e.string(),summary:e.string().optional()}),d=e.object({acceptedRisk:e.unknown().optional(),name:e.string(),socketAlerts:e.array(e.unknown()).optional(),socketScore:e.number().nullable().optional(),version:e.string(),vulnerabilities:e.array(u)}),r=e.object({duplicates:e.array(e.object({name:e.string(),versionCount:e.number(),versions:e.array(e.string())})),packages:e.number(),results:e.array(d),summary:e.object({accepted:e.number(),duplicatePackages:e.number(),issues:e.number(),total:e.number()})}).catchall(e.unknown()),m=r.shape,y=["low","medium","high","critical"],f=({server:i},s)=>{i.registerTool("audit",{annotations:{readOnlyHint:!0},description:"Audit installed packages for known vulnerabilities and supply-chain risk via `vis audit --format json`. Defaults to the local offline OSV cache when one exists, otherwise falls back to the live OSV API. Returns the parsed audit payload (results, duplicates, summary).",inputSchema:{ecosystem:e.string().optional().describe("Comma-separated OSV ecosystems to scan. Defaults to 'npm' (the only matcher wired today)."),offline:e.boolean().optional().describe("Force the local OSV cache. Errors if the cache is missing — run `vis advisories sync` first."),prodOnly:e.boolean().optional().describe("Skip devDependencies — scan the production graph only."),severity:e.enum(y).optional().describe("Minimum severity to report. Defaults to 'low'."),showAccepted:e.boolean().optional().describe("Include findings already on the workspace's accepted-risk list."),usage:e.boolean().optional().describe("Apply the reachability filter — only report vulnerabilities in statically-imported packages.")},outputSchema:m},async o=>{try{const t=["audit","--format","json"];o.severity&&t.push("--severity",o.severity),o.offline&&t.push("--offline"),o.prodOnly&&t.push("--prod-only"),o.usage&&t.push("--usage"),o.ecosystem&&t.push("--ecosystem",o.ecosystem),o.showAccepted&&t.push("--show-accepted");const a=await c(s.visBin,t,{cwd:s.workspaceRoot}),n=r.parse(a);return l(n)}catch(t){return p(t)}})};export{f as registerAudit};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as n from"zod";import{execVisJson as d}from"./execVis-CMeUcwPz.js";import{errorResponse as s,okResponse as c}from"./errorResponse-CVaJrOoB.js";import{isValidTaskId as p,isValidRunId as u}from"./isSafePositional-CJq1jfAS.js";const I=({server:a},i)=>{a.registerTool("cache_hash",{annotations:{readOnlyHint:!0},description:"Print the recorded hash and per-input hash details for a task — the contributing inputs (command, nodes, runtime, implicitDeps) that produced it.",inputSchema:{runId:n.string().optional().describe("Run ID to inspect (default: latest)"),taskId:n.string().describe('Task ID like "@my/app:build"')}},async({runId:t,taskId:e})=>{if(!p(e))return s(new Error(`Invalid taskId "${e}". Expected "<project>:<target>".`));if(t!==void 0&&!u(t))return s(new Error(`Invalid runId "${t}". Expected filesystem-safe slug ([A-Za-z0-9_.-]).`));try{const r=["cache","hash",e,"--format=json"];t&&r.push("--run",t);const o=await d(i.visBin,r,{cwd:i.workspaceRoot});return c(o)}catch(r){return s(r)}})};export{I as registerCacheHash};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as n from"zod";import{execVisJson as c}from"./execVis-CMeUcwPz.js";import{errorResponse as s,okResponse as d}from"./errorResponse-CVaJrOoB.js";import{isValidTaskId as p,isValidRunId as u}from"./isSafePositional-CJq1jfAS.js";const f=({server:i},a)=>{i.registerTool("cache_why",{annotations:{readOnlyHint:!0},description:"Explain why a task's cache hash rotated between runs. Diffs the task's hashDetails (command, nodes, runtime, implicitDeps) against the previous run to pinpoint what changed.",inputSchema:{runId:n.string().optional().describe("Run ID to inspect (default: latest)"),taskId:n.string().describe('Task ID like "@my/app:build"')}},async({runId:t,taskId:r})=>{if(!p(r))return s(new Error(`Invalid taskId "${r}". Expected "<project>:<target>".`));if(t!==void 0&&!u(t))return s(new Error(`Invalid runId "${t}". Expected filesystem-safe slug ([A-Za-z0-9_.-]).`));try{const e=["cache","why",r,"--format=json"];t&&e.push("--run",t);const o=await c(a.visBin,e,{cwd:a.workspaceRoot});return d(o)}catch(e){return s(e)}})};export{f as registerCacheWhy};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as s from"zod";import{execVisJson as o}from"./execVis-CMeUcwPz.js";import{errorResponse as r,okResponse as i}from"./errorResponse-CVaJrOoB.js";import{isSafePositional as m}from"./isSafePositional-CJq1jfAS.js";const d=({server:n},a)=>{n.registerTool("describe_template",{annotations:{readOnlyHint:!0},description:"Return full metadata for a scaffolding template: description, default destination, and the variable schema (name, type, default, required, prompt, enum values). Use this before suggesting a `vis generate` command so option values can be filled correctly.",inputSchema:{name:s.string().describe('Template name (e.g. "package", "component"). Use list_templates to see available names.')}},async({name:e})=>{if(!m(e))return r(new Error(`Invalid template name "${e}". A leading "-" would be parsed as a CLI flag.`));try{const t=await o(a.visBin,["generate","--describe","--json","--",e],{cwd:a.workspaceRoot});return i(t)}catch(t){return r(t)}})};export{d as registerDescribeTemplate};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import*as e from"zod";import{execVis as f}from"./execVis-CMeUcwPz.js";import{errorResponse as n,okStructuredResponse as p}from"./errorResponse-CVaJrOoB.js";import{appendPositionalFiles as h}from"./isSafePositional-CJq1jfAS.js";const l=e.object({adapter:e.string(),column:e.number().optional(),file:e.string(),fixable:e.boolean().optional(),line:e.number().optional(),message:e.string(),ruleId:e.string().optional(),severity:e.enum(["error","info","warning"])}).catchall(e.unknown()),u=e.object({adapter:e.string(),durationMs:e.number(),exitCode:e.number(),findingCount:e.number()}).catchall(e.unknown()),g=e.object({findings:e.array(l),mode:e.enum(["check","fix"]).optional(),runs:e.array(u)}).catchall(e.unknown()),w={exitCode:e.number(),findings:e.array(l),mode:e.enum(["check","fix"]),runs:e.array(u)},y=({server:m},s)=>{m.registerTool("fmt",{annotations:{readOnlyHint:!0},description:"Run the vis fmt orchestrator in --check mode via `vis fmt --check --format json`. Auto-detects every installed formatter (oxfmt, biome, dprint, prettier, ruff-fmt, deno-fmt) and returns the merged { findings, runs } payload — each finding represents a file that would change. This tool never writes to disk; invoke `vis fmt` directly to apply the formatting.",inputSchema:{files:e.array(e.string()).optional().describe("Restrict the check to these files (relative to the workspace root). Empty/omitted = workspace-wide."),quiet:e.boolean().optional().describe("Suppress per-file logs (findings still flow through)."),since:e.string().optional().describe("Forward `--since <ref>` so only files changed vs `<ref>` are checked."),staged:e.boolean().optional().describe("Forward `--staged` so only files in the git index are checked.")},outputSchema:w},async i=>{try{const t=["fmt","--check","--format","json"];i.quiet&&t.push("--quiet"),i.staged?t.push("--staged"):i.since!==void 0&&i.since.length>0&&t.push("--since",i.since);const a=h(t,i.files);if(a!==void 0)return n(new Error(a));const r=await f(s.visBin,t,{cwd:s.workspaceRoot});if(r.timedOut)return n(new Error("vis fmt timed out"));if(r.overflowed)return n(new Error("vis fmt produced more output than the buffer ceiling allows and was killed"));if(r.stdout.trim().length===0){const o=r.stderr.trim().split(`
|
|
2
|
-
`).slice(-5).join(`
|
|
3
|
-
`);return n(new Error(`vis fmt exited with code ${String(r.exitCode)} and no JSON output${o?`
|
|
4
|
-
${o}`:""}`))}let c;try{c=JSON.parse(r.stdout)}catch(o){return n(new Error(`vis ${t.join(" ")} did not emit valid JSON: ${o instanceof Error?o.message:String(o)}`,{cause:o}))}const d=g.parse(c);return p({...d,exitCode:r.exitCode,mode:d.mode??"check"})}catch(t){return n(t)}})};export{y as registerFmt};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{readFile as c}from"node:fs/promises";import{join as u}from"@visulima/path";import*as d from"zod";import{errorResponse as e,okResponse as l}from"./errorResponse-CVaJrOoB.js";import{isValidRunId as f}from"./isSafePositional-CJq1jfAS.js";const R=({server:p},o)=>{p.registerTool("get_run_logs",{annotations:{readOnlyHint:!0},description:"Read a vis run summary from .task-runner/. Returns the most recent run by default, or a specific run when `runId` is set. Optionally filter to a single task with `taskId`.",inputSchema:{runId:d.string().optional().describe("Run ID (default: latest)"),taskId:d.string().optional().describe('Filter to a single task ID like "@my/app:build"')}},async({runId:r,taskId:s})=>{if(r!==void 0&&!f(r))return e(new Error(`Invalid runId "${r}". Expected filesystem-safe slug ([A-Za-z0-9_.-]).`));const a=r?u(o.workspaceRoot,".task-runner","runs",`${r}.json`):u(o.workspaceRoot,".task-runner","last-summary.json");try{const t=await c(a,"utf8"),n=JSON.parse(t);if(s){const i=n.tasks?.find(m=>m.taskId===s);return i?l({runId:n.runId,task:i}):e(new Error(`Task "${s}" not found in run "${n.runId??"(unknown)"}".`))}return l(n)}catch(t){return t.code==="ENOENT"?e(new Error(`No run summary at ${a}. Run a task first to populate it.`)):e(t)}})};export{R as registerGetRunLogs};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import*as e from"zod";import{execVis as m}from"./execVis-CMeUcwPz.js";import{errorResponse as o,okStructuredResponse as g}from"./errorResponse-CVaJrOoB.js";import{appendPositionalFiles as f}from"./isSafePositional-CJq1jfAS.js";const d=e.object({adapter:e.string(),column:e.number().optional(),endColumn:e.number().optional(),endLine:e.number().optional(),file:e.string(),fixable:e.boolean().optional(),line:e.number().optional(),message:e.string(),ruleId:e.string().optional(),severity:e.enum(["error","info","warning"])}).catchall(e.unknown()),u=e.object({adapter:e.string(),durationMs:e.number(),exitCode:e.number(),findingCount:e.number()}).catchall(e.unknown()),h=e.object({findings:e.array(d),runs:e.array(u)}).catchall(e.unknown()),w={exitCode:e.number(),findings:e.array(d),runs:e.array(u)},x=({server:c},s)=>{c.registerTool("lint",{annotations:{readOnlyHint:!0},description:"Run the vis lint orchestrator in check-only mode via `vis lint --format json`. Auto-detects every installed linter (oxlint, biome, eslint, stylelint, ruff, markdownlint, shellcheck, deno-lint) and returns the merged { findings, runs } payload. This tool never applies fixes — invoke `vis lint --fix` directly for that.",inputSchema:{files:e.array(e.string()).optional().describe("Restrict the run to these files (relative to the workspace root). Empty/omitted = workspace-wide."),maxWarnings:e.int().nonnegative().optional().describe("Forward `--max-warnings N` to every adapter that supports it."),quiet:e.boolean().optional().describe("Suppress warning-severity findings (errors still flow through)."),since:e.string().optional().describe("Forward `--since <ref>` so only files changed vs `<ref>` are linted."),staged:e.boolean().optional().describe("Forward `--staged` so only files in the git index are linted.")},outputSchema:w},async t=>{try{const n=["lint","--format","json"];t.quiet&&n.push("--quiet"),typeof t.maxWarnings=="number"&&n.push("--max-warnings",String(t.maxWarnings)),t.staged?n.push("--staged"):t.since!==void 0&&t.since.length>0&&n.push("--since",t.since);const a=f(n,t.files);if(a!==void 0)return o(new Error(a));const i=await m(s.visBin,n,{cwd:s.workspaceRoot});if(i.timedOut)return o(new Error("vis lint timed out"));if(i.overflowed)return o(new Error("vis lint produced more output than the buffer ceiling allows and was killed"));if(i.stdout.trim().length===0){const r=i.stderr.trim().split(`
|
|
2
|
-
`).slice(-5).join(`
|
|
3
|
-
`);return o(new Error(`vis lint exited with code ${String(i.exitCode)} and no JSON output${r?`
|
|
4
|
-
${r}`:""}`))}let l;try{l=JSON.parse(i.stdout)}catch(r){return o(new Error(`vis ${n.join(" ")} did not emit valid JSON: ${r instanceof Error?r.message:String(r)}`,{cause:r}))}const p=h.parse(l);return g({...p,exitCode:i.exitCode})}catch(n){return o(n)}})};export{x as registerLint};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import*as i from"zod";import{execVisJson as n}from"./execVis-CMeUcwPz.js";import{okResponse as a,errorResponse as p}from"./errorResponse-CVaJrOoB.js";const g=({server:o},e)=>{o.registerTool("list_projects",{annotations:{readOnlyHint:!0},description:'List all workspace projects with their language, type, layer, tags, and target metadata. Optionally filter with a vis query string (e.g. "tag=frontend", "type=application").',inputSchema:{query:i.string().optional().describe('Filter projects by vis query string (e.g. "tag=frontend")')}},async({query:r})=>{try{const t=["list","--json"];r&&t.push("--query",r);const s=await n(e.visBin,t,{cwd:e.workspaceRoot});return a({count:s.length,projects:s})}catch(t){return p(t)}})};export{g as registerListProjects};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{readdir as h,stat as k,readFile as g}from"node:fs/promises";import{join as l}from"@visulima/path";import*as y from"zod";import{okResponse as m,errorResponse as f}from"./errorResponse-CVaJrOoB.js";const v=/\.json$/,w=r=>{if(typeof r.status=="string")return r.status;if(r.tasks)return r.tasks.some(n=>n.status==="failure"||n.status==="failed")?"failure":"success"},R=({server:r},n)=>{r.registerTool("list_runs",{annotations:{readOnlyHint:!0},description:'List recorded vis runs from `.task-runner/runs/`, most recent first. Each entry includes the run ID, start timestamp, derived status, task count, and failed-task count — use it to discover run IDs for `get_run_logs`, `cache_hash`, or `cache_why` (e.g. "compare this run to the previous one").',inputSchema:{limit:y.int().positive().max(100).optional().describe("Maximum runs to return (default: 20, newest first).")}},async({limit:p})=>{const d=l(n.workspaceRoot,".task-runner","runs");let c;try{c=(await h(d)).filter(t=>t.endsWith(".json"))}catch(t){return t.code==="ENOENT"?m({count:0,runs:[]}):f(t)}try{const t=(await Promise.all(c.map(async s=>{const a=l(d,s);try{const[o,u]=await Promise.all([k(a),g(a,"utf8")]),e=JSON.parse(u);return{failedTasks:e.tasks?.filter(i=>i.status==="failure"||i.status==="failed").length,modifiedMs:o.mtimeMs,runId:e.runId??s.replace(v,""),startedAt:e.startedAt,status:w(e),taskCount:e.tasks?.length}}catch{return}}))).filter(s=>s!==void 0).toSorted((s,a)=>a.modifiedMs-s.modifiedMs).slice(0,p??20).map(s=>{const{failedTasks:a,runId:o,startedAt:u,status:e,taskCount:i}=s;return{failedTasks:a,runId:o,startedAt:u,status:e,taskCount:i}});return m({count:t.length,runs:t})}catch(t){return f(t)}})};export{R as registerListRuns};
|