@vornrun/mcp 0.7.3 → 0.7.5
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/dist/index.js +172 -8
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -589,6 +589,7 @@ function createSchema() {
|
|
|
589
589
|
CREATE INDEX IF NOT EXISTS idx_connector_inbox_connection
|
|
590
590
|
ON connector_inbox(connection_id, created_at);
|
|
591
591
|
`);
|
|
592
|
+
d.exec(ARTIFACT_DDL);
|
|
592
593
|
migrateSchema(d);
|
|
593
594
|
verifySchema(d);
|
|
594
595
|
seedLegacyConnectorPollState(d);
|
|
@@ -1135,7 +1136,59 @@ function migrateSchema(d) {
|
|
|
1135
1136
|
})();
|
|
1136
1137
|
logger_default.info("[database] migrated schema to version 24 (the text a reviewer edited at a gate)");
|
|
1137
1138
|
}
|
|
1139
|
+
if (version < 25) {
|
|
1140
|
+
d.transaction(() => {
|
|
1141
|
+
d.exec(ARTIFACT_DDL);
|
|
1142
|
+
d.prepare(
|
|
1143
|
+
"INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '25')"
|
|
1144
|
+
).run();
|
|
1145
|
+
})();
|
|
1146
|
+
logger_default.info("[database] migrated schema to version 25 (published artifacts and their comments)");
|
|
1147
|
+
}
|
|
1138
1148
|
}
|
|
1149
|
+
var ARTIFACT_DDL = `
|
|
1150
|
+
CREATE TABLE IF NOT EXISTS artifacts (
|
|
1151
|
+
id TEXT PRIMARY KEY,
|
|
1152
|
+
kind TEXT NOT NULL,
|
|
1153
|
+
title TEXT NOT NULL,
|
|
1154
|
+
session_id TEXT,
|
|
1155
|
+
project_name TEXT,
|
|
1156
|
+
token TEXT NOT NULL,
|
|
1157
|
+
latest_version INTEGER NOT NULL DEFAULT 0,
|
|
1158
|
+
gate_run_id TEXT,
|
|
1159
|
+
gate_node_id TEXT,
|
|
1160
|
+
created_at TEXT NOT NULL,
|
|
1161
|
+
updated_at TEXT NOT NULL
|
|
1162
|
+
);
|
|
1163
|
+
|
|
1164
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_session ON artifacts(session_id, updated_at DESC);
|
|
1165
|
+
CREATE INDEX IF NOT EXISTS idx_artifacts_project ON artifacts(project_name, updated_at DESC);
|
|
1166
|
+
|
|
1167
|
+
CREATE TABLE IF NOT EXISTS artifact_versions (
|
|
1168
|
+
artifact_id TEXT NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE,
|
|
1169
|
+
version INTEGER NOT NULL,
|
|
1170
|
+
author TEXT NOT NULL,
|
|
1171
|
+
answers_batch_id TEXT,
|
|
1172
|
+
created_at TEXT NOT NULL,
|
|
1173
|
+
PRIMARY KEY (artifact_id, version)
|
|
1174
|
+
);
|
|
1175
|
+
|
|
1176
|
+
CREATE TABLE IF NOT EXISTS artifact_comments (
|
|
1177
|
+
id TEXT PRIMARY KEY,
|
|
1178
|
+
artifact_id TEXT NOT NULL REFERENCES artifacts(id) ON DELETE CASCADE,
|
|
1179
|
+
version INTEGER NOT NULL,
|
|
1180
|
+
anchor TEXT,
|
|
1181
|
+
body TEXT NOT NULL,
|
|
1182
|
+
state TEXT NOT NULL DEFAULT 'draft',
|
|
1183
|
+
batch_id TEXT,
|
|
1184
|
+
created_at TEXT NOT NULL,
|
|
1185
|
+
updated_at TEXT NOT NULL,
|
|
1186
|
+
sent_at TEXT
|
|
1187
|
+
);
|
|
1188
|
+
|
|
1189
|
+
CREATE INDEX IF NOT EXISTS idx_artifact_comments_artifact
|
|
1190
|
+
ON artifact_comments(artifact_id, created_at);
|
|
1191
|
+
`;
|
|
1139
1192
|
var GATE_COLUMNS = [
|
|
1140
1193
|
["message", "TEXT"],
|
|
1141
1194
|
["view_token", "TEXT"],
|
|
@@ -1258,7 +1311,7 @@ function verifySchema(d) {
|
|
|
1258
1311
|
},
|
|
1259
1312
|
// Which pass of a loop produced this row.
|
|
1260
1313
|
{ column: "iteration", ddl: "ALTER TABLE workflow_run_nodes ADD COLUMN iteration INTEGER" },
|
|
1261
|
-
...GATE_COLUMNS.map(([column, type]) => ({
|
|
1314
|
+
...[...GATE_COLUMNS, ...GATE_EDIT_COLUMNS].map(([column, type]) => ({
|
|
1262
1315
|
column,
|
|
1263
1316
|
ddl: `ALTER TABLE workflow_run_nodes ADD COLUMN ${column} ${type}`
|
|
1264
1317
|
}))
|
|
@@ -4215,9 +4268,18 @@ The run is stopped by the instance holding it, so confirm with list_workflow_run
|
|
|
4215
4268
|
edited: z6.string().max(2e5).optional().describe(
|
|
4216
4269
|
"Your rewrite of the gate's editable text, when it has one. Kept beside the original, and read by later steps as {{steps.<gate>.text}}. Ignored on reject."
|
|
4217
4270
|
),
|
|
4271
|
+
comments: z6.array(
|
|
4272
|
+
z6.object({
|
|
4273
|
+
quote: z6.string().max(2e3).optional().describe("The words on the review page it is about"),
|
|
4274
|
+
comment: z6.string().min(1).max(4e3)
|
|
4275
|
+
})
|
|
4276
|
+
).max(100).optional().describe(
|
|
4277
|
+
"Comments on the review page, each on the words it quotes. Carried back with changes and read by the redone steps as {{steps.<gate>.comments}}; ignored on approve and reject."
|
|
4278
|
+
),
|
|
4218
4279
|
node_id: V.id.optional().describe("The waiting node, when a run has more than one gate open")
|
|
4219
4280
|
},
|
|
4220
4281
|
async (args) => {
|
|
4282
|
+
const comments = (args.comments ?? []).filter((c) => c.comment.trim());
|
|
4221
4283
|
const run = await runById(args.run_id);
|
|
4222
4284
|
if (!run) {
|
|
4223
4285
|
return {
|
|
@@ -4247,7 +4309,7 @@ The run is stopped by the instance holding it, so confirm with list_workflow_run
|
|
|
4247
4309
|
isError: true
|
|
4248
4310
|
};
|
|
4249
4311
|
}
|
|
4250
|
-
if (args.decision === "changes" && !args.comment?.trim()) {
|
|
4312
|
+
if (args.decision === "changes" && !args.comment?.trim() && comments.length === 0) {
|
|
4251
4313
|
return {
|
|
4252
4314
|
content: [
|
|
4253
4315
|
{ type: "text", text: "Error: changes needs a comment saying what to change." }
|
|
@@ -4266,7 +4328,8 @@ The run is stopped by the instance holding it, so confirm with list_workflow_run
|
|
|
4266
4328
|
nodeId: target.nodeId,
|
|
4267
4329
|
decision: args.decision,
|
|
4268
4330
|
...args.comment?.trim() && { comment: args.comment.trim() },
|
|
4269
|
-
...args.edited?.trim() && { edited: args.edited.trim() }
|
|
4331
|
+
...args.edited?.trim() && { edited: args.edited.trim() },
|
|
4332
|
+
...args.decision === "changes" && comments.length > 0 && { comments }
|
|
4270
4333
|
}
|
|
4271
4334
|
);
|
|
4272
4335
|
if (answer?.accepted === false) {
|
|
@@ -4610,8 +4673,12 @@ var DOCS = {
|
|
|
4610
4673
|
field: "items",
|
|
4611
4674
|
description: "When the editable text is a JSON list of records: the rows the reviewer kept, as a list, ready for a forEach loop"
|
|
4612
4675
|
},
|
|
4613
|
-
{
|
|
4614
|
-
|
|
4676
|
+
{
|
|
4677
|
+
field: "comments",
|
|
4678
|
+
description: "Comments pinned to the review page in the latest round, as a JSON list of {quote, comment, anchored}"
|
|
4679
|
+
},
|
|
4680
|
+
{ field: "feedback", description: "The general note from the latest round" },
|
|
4681
|
+
{ field: "feedbackAll", description: "Every note, one line per round" },
|
|
4615
4682
|
{ field: "round", description: "Which time the gate asked, from 1" },
|
|
4616
4683
|
...COMMON_OUTPUTS
|
|
4617
4684
|
],
|
|
@@ -5648,9 +5715,11 @@ function registerDeviceTools(server) {
|
|
|
5648
5715
|
);
|
|
5649
5716
|
server.tool(
|
|
5650
5717
|
"device_interact",
|
|
5651
|
-
'Act on your claimed simulator: tap, swipe, type, press a hardware button,
|
|
5718
|
+
'Act on your claimed simulator: tap, swipe, type, press a hardware button, long-press, or rotate it. Address the target by "ref" from read_screen where possible. Coordinates are in POINTS. A ref from before an earlier interaction is refused rather than guessed at \u2014 read the screen again. A swipe starting at the very edge of the screen is refused too: iOS claims those as system gestures and swallows them, which looks to you like nothing happened.',
|
|
5652
5719
|
{
|
|
5653
|
-
action: z11.enum(["tap", "swipe", "type", "button", "press"]).describe(
|
|
5720
|
+
action: z11.enum(["tap", "swipe", "type", "button", "press", "rotate"]).describe(
|
|
5721
|
+
'"press" is a long press; "button" takes a name in `text`; "rotate" takes `orientation`'
|
|
5722
|
+
),
|
|
5654
5723
|
ref: required("Element ref from read_screen or device_find").optional(),
|
|
5655
5724
|
x: z11.number().optional().describe("Screen x in points, when no ref is available"),
|
|
5656
5725
|
y: z11.number().optional().describe("Screen y in points, when no ref is available"),
|
|
@@ -5659,6 +5728,7 @@ function registerDeviceTools(server) {
|
|
|
5659
5728
|
text: V.description.optional().describe(
|
|
5660
5729
|
'Text for "type", or the button name for "button" (HOME, LOCK, SIRI, SIDE_BUTTON)'
|
|
5661
5730
|
),
|
|
5731
|
+
orientation: z11.enum(["portrait", "portrait-upside-down", "landscape-left", "landscape-right"]).optional().describe('Which way up to turn the device, for "rotate"'),
|
|
5662
5732
|
duration: z11.number().min(0).max(30).optional().describe('Seconds to hold, for "press"'),
|
|
5663
5733
|
system_gesture: z11.boolean().optional().describe("Allow a stroke starting in the bezel band, when a system gesture is the intent")
|
|
5664
5734
|
},
|
|
@@ -5669,6 +5739,7 @@ function registerDeviceTools(server) {
|
|
|
5669
5739
|
target: toDeviceTarget(args),
|
|
5670
5740
|
to: typeof args.to_x === "number" && typeof args.to_y === "number" ? { x: args.to_x, y: args.to_y } : void 0,
|
|
5671
5741
|
text: args.text,
|
|
5742
|
+
orientation: args.orientation,
|
|
5672
5743
|
duration: args.duration,
|
|
5673
5744
|
systemGesture: args.system_gesture
|
|
5674
5745
|
});
|
|
@@ -5763,6 +5834,98 @@ Screen is ${r.screen.width}x${r.screen.height} points. Image pixels are ${r.scal
|
|
|
5763
5834
|
);
|
|
5764
5835
|
}
|
|
5765
5836
|
|
|
5837
|
+
// src/tools/artifacts.ts
|
|
5838
|
+
import { z as z12 } from "zod";
|
|
5839
|
+
var MAX_CONTENT = 5 * 1024 * 1024;
|
|
5840
|
+
var text = (value) => ({ content: [{ type: "text", text: value }] });
|
|
5841
|
+
function registerArtifactTools(server) {
|
|
5842
|
+
server.tool(
|
|
5843
|
+
"publish_artifact",
|
|
5844
|
+
"Publish a page, doc or design for the person to review. It opens in your session browser pane, where they can comment on the exact words and send their notes back to you as one message. Pass artifactId to publish the next version of an artifact after addressing comments. A page or design is self-contained HTML (no network: inline styles, scripts and images as data: URIs); a doc is Markdown.",
|
|
5845
|
+
{
|
|
5846
|
+
kind: z12.enum(["page", "doc", "design"]).describe("page: any HTML; doc: Markdown; design: HTML with a design manifest"),
|
|
5847
|
+
title: V.shortText.min(1).describe("What the artifact is called in the pane"),
|
|
5848
|
+
file: z12.string().min(1).max(1e3).optional().describe("A .html or .md file inside your project or worktree; give this or content"),
|
|
5849
|
+
content: z12.string().max(MAX_CONTENT).optional().describe("The HTML or Markdown itself"),
|
|
5850
|
+
artifactId: V.id.optional().describe("Publish the next version of this artifact"),
|
|
5851
|
+
open: z12.boolean().optional().describe("Open it in the browser pane (default true)")
|
|
5852
|
+
},
|
|
5853
|
+
async (args) => withSession(async (id) => {
|
|
5854
|
+
const result = await rpcCall("artifact:publish", { sessionId: id, ...args });
|
|
5855
|
+
const lines = [
|
|
5856
|
+
`Published ${result.artifact.kind} "${result.artifact.title}" v${result.version.version} (artifactId ${result.artifact.id}).`,
|
|
5857
|
+
result.opened ? "It is open in your browser pane." : `Not opened in a pane. Served at ${result.url}`
|
|
5858
|
+
];
|
|
5859
|
+
if (result.answered > 0) lines.push(`This version answers ${result.answered} comments.`);
|
|
5860
|
+
return text(lines.join("\n"));
|
|
5861
|
+
})
|
|
5862
|
+
);
|
|
5863
|
+
server.tool(
|
|
5864
|
+
"list_artifacts",
|
|
5865
|
+
"List the artifacts published by your session or in your project, newest first.",
|
|
5866
|
+
{ limit: z12.number().int().min(1).max(100).optional() },
|
|
5867
|
+
async (args) => withSession(async (id) => {
|
|
5868
|
+
const artifacts = await rpcCall("artifact:list", {
|
|
5869
|
+
sessionId: id,
|
|
5870
|
+
limit: args.limit
|
|
5871
|
+
});
|
|
5872
|
+
if (artifacts.length === 0) return text("No artifacts yet.");
|
|
5873
|
+
return text(
|
|
5874
|
+
artifacts.map(
|
|
5875
|
+
(a) => `${a.id} ${a.kind} v${a.latestVersion} "${a.title}" updated ${a.updatedAt}`
|
|
5876
|
+
).join("\n")
|
|
5877
|
+
);
|
|
5878
|
+
})
|
|
5879
|
+
);
|
|
5880
|
+
server.tool(
|
|
5881
|
+
"read_artifact_comments",
|
|
5882
|
+
"Read the comments the person left on an artifact: the quoted words or point each one is about, the comment, which version it was written on, and whether it was sent to you.",
|
|
5883
|
+
{
|
|
5884
|
+
artifactId: V.id,
|
|
5885
|
+
version: z12.number().int().min(1).optional().describe("Only comments written on this version")
|
|
5886
|
+
},
|
|
5887
|
+
async (args) => withSession(async (id) => {
|
|
5888
|
+
const comments = await rpcCall("artifact:readComments", {
|
|
5889
|
+
sessionId: id,
|
|
5890
|
+
artifactId: args.artifactId,
|
|
5891
|
+
version: args.version
|
|
5892
|
+
});
|
|
5893
|
+
return pageResult(
|
|
5894
|
+
comments.map((c) => ({
|
|
5895
|
+
version: c.version,
|
|
5896
|
+
state: c.state,
|
|
5897
|
+
anchor: c.anchor,
|
|
5898
|
+
comment: c.body
|
|
5899
|
+
})),
|
|
5900
|
+
"ARTIFACT COMMENTS ON WEB PAGE CONTENT"
|
|
5901
|
+
);
|
|
5902
|
+
})
|
|
5903
|
+
);
|
|
5904
|
+
server.tool(
|
|
5905
|
+
"read_artifact",
|
|
5906
|
+
"Read the source of an artifact version: Markdown for a doc, HTML for a page or design. Use it before publishing the next version when the person saved edits of their own.",
|
|
5907
|
+
{
|
|
5908
|
+
artifactId: V.id,
|
|
5909
|
+
version: z12.number().int().min(1).optional().describe("Defaults to the latest version")
|
|
5910
|
+
},
|
|
5911
|
+
async (args) => withSession(async (id) => {
|
|
5912
|
+
const found = await rpcCall(
|
|
5913
|
+
"artifact:readSource",
|
|
5914
|
+
{ sessionId: id, artifactId: args.artifactId, version: args.version }
|
|
5915
|
+
);
|
|
5916
|
+
if (!found) return text(`No such version of artifact ${args.artifactId}.`);
|
|
5917
|
+
return pageResult(
|
|
5918
|
+
{
|
|
5919
|
+
version: found.version.version,
|
|
5920
|
+
author: found.version.author === "user" ? "the person" : "an agent",
|
|
5921
|
+
source: found.body
|
|
5922
|
+
},
|
|
5923
|
+
"WEB PAGE CONTENT: ARTIFACT SOURCE"
|
|
5924
|
+
);
|
|
5925
|
+
})
|
|
5926
|
+
);
|
|
5927
|
+
}
|
|
5928
|
+
|
|
5766
5929
|
// src/server.ts
|
|
5767
5930
|
function createMcpServer(version) {
|
|
5768
5931
|
const server = new McpServer({ name: "vorn", version }, { capabilities: { tools: {} } });
|
|
@@ -5771,6 +5934,7 @@ function createMcpServer(version) {
|
|
|
5771
5934
|
registerConnectorTools(server);
|
|
5772
5935
|
registerBrowserTools(server);
|
|
5773
5936
|
registerDeviceTools(server);
|
|
5937
|
+
registerArtifactTools(server);
|
|
5774
5938
|
registerProjectTools(server);
|
|
5775
5939
|
registerTaskTools(server);
|
|
5776
5940
|
registerWorkflowTools(server);
|
|
@@ -5788,7 +5952,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
5788
5952
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
5789
5953
|
async function main() {
|
|
5790
5954
|
configManager.init();
|
|
5791
|
-
const version = true ? "0.7.
|
|
5955
|
+
const version = true ? "0.7.5" : createRequire(import.meta.url)("../package.json").version;
|
|
5792
5956
|
const server = createMcpServer(version);
|
|
5793
5957
|
const transport = new StdioServerTransport();
|
|
5794
5958
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vornrun/mcp",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Vorn MCP server — task management, git, and workflow tools for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"zod": "^4.5.4"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@vornrun/server": "0.7.
|
|
42
|
-
"@vornrun/shared": "0.7.
|
|
41
|
+
"@vornrun/server": "0.7.5",
|
|
42
|
+
"@vornrun/shared": "0.7.5",
|
|
43
43
|
"tsup": "^8.5.1",
|
|
44
44
|
"tsx": "^4.23.1",
|
|
45
45
|
"typescript": "^6.0.3"
|