@rockhopper-co/mcp-server 0.8.0 → 0.10.0
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 +46 -0
- package/README.md +8 -0
- package/dist/api-client.d.ts +72 -3
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +115 -3
- package/dist/api-client.js.map +1 -1
- package/dist/cli.js +7 -1
- package/dist/cli.js.map +1 -1
- package/dist/not-ready.d.ts +95 -0
- package/dist/not-ready.d.ts.map +1 -0
- package/dist/not-ready.js +151 -0
- package/dist/not-ready.js.map +1 -0
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +8 -0
- package/dist/prompts/index.js.map +1 -1
- package/dist/resources/changes.d.ts.map +1 -1
- package/dist/resources/changes.js +6 -0
- package/dist/resources/changes.js.map +1 -1
- package/dist/resources/orchestration-guide.md +23 -3
- package/dist/resources/teams.d.ts.map +1 -1
- package/dist/resources/teams.js +7 -1
- package/dist/resources/teams.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +4 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/get-cell-history.d.ts.map +1 -1
- package/dist/tools/get-cell-history.js +28 -4
- package/dist/tools/get-cell-history.js.map +1 -1
- package/dist/tools/search.d.ts.map +1 -1
- package/dist/tools/search.js +13 -1
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/write-reviews.d.ts.map +1 -1
- package/dist/tools/write-reviews.js +58 -4
- package/dist/tools/write-reviews.js.map +1 -1
- package/dist/types.d.ts +58 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/zod-schemas.d.ts +24 -0
- package/dist/zod-schemas.d.ts.map +1 -1
- package/dist/zod-schemas.js +24 -0
- package/dist/zod-schemas.js.map +1 -1
- package/package.json +17 -7
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan 02 ruling 5 (David, 2026-08-04) — STRICT no-partial, on the machine
|
|
3
|
+
* surfaces.
|
|
4
|
+
*
|
|
5
|
+
* The rule David landed is "nothing serves change history until it is
|
|
6
|
+
* complete", and he chose STRICT specifically because THIS server is a
|
|
7
|
+
* consumer: rows leave here for Claude Desktop / Cursor, which get no banner,
|
|
8
|
+
* no colour and no human in the loop, and will narrate whatever arrives as
|
|
9
|
+
* fact. An empty list is the dangerous answer — "there are no changes" is a
|
|
10
|
+
* factual claim, and it is the claim an assistant makes when handed zero rows.
|
|
11
|
+
*
|
|
12
|
+
* So a not-ready answer here is a REFUSAL, not a value: `isError: true`, a
|
|
13
|
+
* shouting marker, an explicit "this is not an empty result", and a JSON
|
|
14
|
+
* object an assistant can branch on. A resource or prompt THROWS for the same
|
|
15
|
+
* reason a tool cannot return rows — a protocol error cannot be summarised as
|
|
16
|
+
* data.
|
|
17
|
+
*
|
|
18
|
+
* Vocabulary is deliberately the backend's, not a second one: the reasons
|
|
19
|
+
* mirror the SP02 `ParsedOutputNotReadyReason` family
|
|
20
|
+
* (`backend/src/common/parsed-output/parsed-output-not-ready.error.ts`), and
|
|
21
|
+
* `isNotReady` matches both the typed error and a wrapper carrying it as
|
|
22
|
+
* `cause`, exactly like the backend's `isParsedOutputNotReady`.
|
|
23
|
+
*/
|
|
24
|
+
/** Grep marker; also the first token of every refusal an assistant sees. */
|
|
25
|
+
export const NOT_READY_MARKER = 'CHANGE_HISTORY_NOT_READY';
|
|
26
|
+
/** Poll hint when the backend gave none — mirrors the backend's own default. */
|
|
27
|
+
export const DEFAULT_RETRY_AFTER_SECONDS = 15;
|
|
28
|
+
export class ChangeHistoryNotReadyError extends Error {
|
|
29
|
+
/** Structural marker, so a wrapper can be recognised without importing. */
|
|
30
|
+
notReady = true;
|
|
31
|
+
reason;
|
|
32
|
+
retryAfterSeconds;
|
|
33
|
+
fileMsId;
|
|
34
|
+
constructor(ctx) {
|
|
35
|
+
super(`${NOT_READY_MARKER}: reason=${ctx.reason} ` +
|
|
36
|
+
`retryAfterSeconds=${ctx.retryAfterSeconds ?? DEFAULT_RETRY_AFTER_SECONDS}` +
|
|
37
|
+
(ctx.detail ? ` — ${ctx.detail}` : '') +
|
|
38
|
+
' — Rockhopper has not finished computing this change history. ' +
|
|
39
|
+
'This is NOT an empty result: no rows can be served yet, and nothing ' +
|
|
40
|
+
'may be inferred about whether the file changed.');
|
|
41
|
+
this.name = 'ChangeHistoryNotReadyError';
|
|
42
|
+
this.reason = ctx.reason;
|
|
43
|
+
this.retryAfterSeconds =
|
|
44
|
+
ctx.retryAfterSeconds ?? DEFAULT_RETRY_AFTER_SECONDS;
|
|
45
|
+
this.fileMsId = ctx.fileMsId ?? null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** Matches the typed error AND any error carrying one as `cause`. */
|
|
49
|
+
export function isNotReady(err) {
|
|
50
|
+
if (err instanceof ChangeHistoryNotReadyError)
|
|
51
|
+
return true;
|
|
52
|
+
const cause = err?.cause;
|
|
53
|
+
return cause instanceof ChangeHistoryNotReadyError;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* HTTP statuses that answer the question DEFINITIVELY. A permanently-refused
|
|
57
|
+
* request is not "still producing", and the two must not share an answer: a
|
|
58
|
+
* probe that 404s (no such file) or 403s (no access) has told us something
|
|
59
|
+
* true, and dressing it up as a capacity signal would send an assistant into a
|
|
60
|
+
* retry loop against a wall. Matched structurally (`err.status`) so this module
|
|
61
|
+
* stays free of an import cycle with the API client.
|
|
62
|
+
*/
|
|
63
|
+
const DEFINITIVE_HTTP_STATUSES = new Set([400, 401, 403, 404, 405, 410, 422]);
|
|
64
|
+
function isDefinitiveRejection(err) {
|
|
65
|
+
const status = err?.status;
|
|
66
|
+
return typeof status === 'number' && DEFINITIVE_HTTP_STATUSES.has(status);
|
|
67
|
+
}
|
|
68
|
+
/** Unwrap to the typed error (accepts the `cause` shape). */
|
|
69
|
+
function unwrap(err) {
|
|
70
|
+
if (err instanceof ChangeHistoryNotReadyError)
|
|
71
|
+
return err;
|
|
72
|
+
const cause = err.cause;
|
|
73
|
+
if (cause instanceof ChangeHistoryNotReadyError)
|
|
74
|
+
return cause;
|
|
75
|
+
return new ChangeHistoryNotReadyError({ reason: 'completeness_unknown' });
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The tool answer for a refusal. Three defences, because one is not enough
|
|
79
|
+
* against a model that wants to be helpful: `isError`, prose that names the
|
|
80
|
+
* wrong inference explicitly, and a JSON object to branch on.
|
|
81
|
+
*/
|
|
82
|
+
export function notReadyToolResult(err) {
|
|
83
|
+
const e = unwrap(err);
|
|
84
|
+
const payload = {
|
|
85
|
+
status: 'not_ready',
|
|
86
|
+
reason: e.reason,
|
|
87
|
+
retryAfterSeconds: e.retryAfterSeconds,
|
|
88
|
+
fileMsId: e.fileMsId,
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
content: [
|
|
92
|
+
{
|
|
93
|
+
type: 'text',
|
|
94
|
+
text: `${NOT_READY_MARKER} — this is NOT a result and NOT an empty result.\n` +
|
|
95
|
+
`Rockhopper has not finished computing this file's change history, ` +
|
|
96
|
+
`so no rows can be served.\n` +
|
|
97
|
+
`Do NOT say there are no changes, that nothing changed, or that the ` +
|
|
98
|
+
`history is empty — none of that is known.\n` +
|
|
99
|
+
`Retry in ${e.retryAfterSeconds} seconds.\n` +
|
|
100
|
+
JSON.stringify(payload),
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
isError: true,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Throws {@link ChangeHistoryNotReadyError} unless the file's change history is
|
|
108
|
+
* complete.
|
|
109
|
+
*
|
|
110
|
+
* `foldPending` is the backend's own authoritative queue read
|
|
111
|
+
* (`GET /file-versions/file/:fileMsId/fold-status`, KI-1399) — while it is
|
|
112
|
+
* true a commit-diff fold is queued, retrying or running, and the change-log
|
|
113
|
+
* window is mid-rewrite. That is precisely the incomplete state, and after
|
|
114
|
+
* plan 02's write-path decoupling (David Q3, 2026-08-03: save the version row
|
|
115
|
+
* first, defer the fold) it is the NORMAL state immediately after any write.
|
|
116
|
+
*
|
|
117
|
+
* A probe that cannot answer refuses. The backend's own probe fails OPEN to
|
|
118
|
+
* not-pending on purpose — a UI lock that can hang is worse than a stale row —
|
|
119
|
+
* but that trade does not transfer here: for a machine consumer a wrong
|
|
120
|
+
* "complete" is a fabricated fact, so the client-side default is the opposite.
|
|
121
|
+
*/
|
|
122
|
+
export async function assertChangeHistoryComplete(api, fileMsId) {
|
|
123
|
+
let status;
|
|
124
|
+
try {
|
|
125
|
+
status = await api.getFoldStatus(fileMsId);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
if (isNotReady(err))
|
|
129
|
+
throw unwrap(err);
|
|
130
|
+
// A definitive rejection is the caller's real answer — let it through so
|
|
131
|
+
// the tool reports "not found" / "no access" instead of "retry in 15s".
|
|
132
|
+
if (isDefinitiveRejection(err))
|
|
133
|
+
throw err;
|
|
134
|
+
throw new ChangeHistoryNotReadyError({
|
|
135
|
+
reason: 'completeness_unknown',
|
|
136
|
+
fileMsId,
|
|
137
|
+
detail: `fold-status probe failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (status.foldPending) {
|
|
141
|
+
throw new ChangeHistoryNotReadyError({
|
|
142
|
+
reason: 'change_history_incomplete',
|
|
143
|
+
fileMsId,
|
|
144
|
+
detail: `a commit-diff fold is still pending` +
|
|
145
|
+
(status.foldTargetVersionId == null
|
|
146
|
+
? ''
|
|
147
|
+
: ` for version ${status.foldTargetVersionId}`),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=not-ready.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"not-ready.js","sourceRoot":"","sources":["../src/not-ready.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI,CAgOvE"}
|
package/dist/prompts/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { assertChangeHistoryComplete } from '../not-ready.js';
|
|
2
3
|
export function registerPrompts(server, api) {
|
|
3
4
|
server.registerPrompt('summarize-file-changes', {
|
|
4
5
|
title: 'Summarize Recent Changes',
|
|
@@ -7,6 +8,10 @@ export function registerPrompts(server, api) {
|
|
|
7
8
|
fileMsId: z.string().describe('Platform ID of the enrolled file'),
|
|
8
9
|
},
|
|
9
10
|
}, async ({ fileMsId }) => {
|
|
11
|
+
// Plan 02 ruling 5 (STRICT) — a prompt is the highest-risk surface: its
|
|
12
|
+
// whole product is a model narrating these rows as fact. Refuse before
|
|
13
|
+
// assembling anything; a prompt has no error channel but its own throw.
|
|
14
|
+
await assertChangeHistoryComplete(api, fileMsId);
|
|
10
15
|
const [file, versions, changesPage] = await Promise.all([
|
|
11
16
|
api.getEnrolledFile(fileMsId),
|
|
12
17
|
api.getFileVersions(fileMsId),
|
|
@@ -118,6 +123,9 @@ export function registerPrompts(server, api) {
|
|
|
118
123
|
fileMsId: z.string().describe('Platform ID of the enrolled file'),
|
|
119
124
|
},
|
|
120
125
|
}, async ({ fileMsId }) => {
|
|
126
|
+
// Plan 02 ruling 5 (STRICT) — this prompt reports a change COUNT, which
|
|
127
|
+
// is the same factual claim in one number instead of many rows.
|
|
128
|
+
await assertChangeHistoryComplete(api, fileMsId);
|
|
121
129
|
const [file, versions, comments, changesPage] = await Promise.all([
|
|
122
130
|
api.getEnrolledFile(fileMsId),
|
|
123
131
|
api.getFileVersions(fileMsId),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/resources/changes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../../src/resources/changes.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGlD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,GACb,IAAI,CA0CN"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { assertChangeHistoryComplete } from '../not-ready.js';
|
|
2
3
|
export function registerChangeResources(server, api) {
|
|
3
4
|
// KI-078 (ENG-1381): template only, no per-file expansion into resources/list.
|
|
4
5
|
// Previously this enumerated only files with `hasUncommittedChanges === true` —
|
|
@@ -16,6 +17,11 @@ export function registerChangeResources(server, api) {
|
|
|
16
17
|
// different cursor — though most clients will treat this as a
|
|
17
18
|
// single read. Resource shape is now the paginated envelope, not
|
|
18
19
|
// a bare array.
|
|
20
|
+
// Plan 02 ruling 5 (STRICT) — a resource read has no `isError` channel,
|
|
21
|
+
// so an incomplete window must THROW. The SDK renders that as a protocol
|
|
22
|
+
// error, which is the only shape here that cannot be mistaken for an
|
|
23
|
+
// empty change set.
|
|
24
|
+
await assertChangeHistoryComplete(api, fileMsId);
|
|
19
25
|
const page = await api.getUnattributedChangesPaginated(fileMsId);
|
|
20
26
|
return {
|
|
21
27
|
contents: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/resources/changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"changes.js","sourceRoot":"","sources":["../../src/resources/changes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAG3E,OAAO,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AAE9D,MAAM,UAAU,uBAAuB,CACrC,MAAiB,EACjB,GAAc;IAEd,+EAA+E;IAC/E,gFAAgF;IAChF,+DAA+D;IAC/D,MAAM,CAAC,gBAAgB,CACrB,sBAAsB,EACtB,IAAI,gBAAgB,CAAC,uCAAuC,EAAE;QAC5D,IAAI,EAAE,SAAS;KAChB,CAAC,EACF;QACE,KAAK,EAAE,sBAAsB;QAC7B,WAAW,EACT,uEAAuE;QACzE,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC1B,mEAAmE;QACnE,uEAAuE;QACvE,8DAA8D;QAC9D,8DAA8D;QAC9D,iEAAiE;QACjE,gBAAgB;QAChB,wEAAwE;QACxE,yEAAyE;QACzE,qEAAqE;QACrE,oBAAoB;QACpB,MAAM,2BAA2B,CAAC,GAAG,EAAE,QAAkB,CAAC,CAAC;QAE3D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,+BAA+B,CACpD,QAAkB,CACnB,CAAC;QACF,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,QAAQ,EAAE,kBAAkB;oBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;iBACpC;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -4,19 +4,27 @@ This guide explains how to combine Rockhopper's MCP tools and resources correctl
|
|
|
4
4
|
|
|
5
5
|
## 1. Identity and IDs
|
|
6
6
|
|
|
7
|
-
Rockhopper uses
|
|
7
|
+
Rockhopper uses four distinct identifiers. Mixing them is the most common cause of tool-call errors.
|
|
8
8
|
|
|
9
9
|
| ID | Type | Where it comes from | Where it goes |
|
|
10
10
|
|----|------|---------------------|---------------|
|
|
11
11
|
| `fileMsId` | string | `list_files` response, `rockhopper://files` resource | Tools that act on a file: `get_file_versions`, `get_file_comments`, `add_comment`, `create_version`, `discard_changes`, `get_cell_history`, `get_unattributed_changes`, `rename_file` |
|
|
12
12
|
| `versionId` | number | `get_file_versions` response (`internalId` field), `create_version` response | Tools that act on a specific version snapshot: `get_reviews`, `create_review_request`, `approve_review`, `cancel_review` |
|
|
13
13
|
| `versionInternalId` | number | `get_file_comments` response (comments scope to a version) | Comment-thread tools: `add_comment`, `reply_to_comment`, `resolve_comment` |
|
|
14
|
+
| user / team id | uuid string **or** number | `rockhopper://teams/{teamId}` resource — each record carries an `id` (uuid) and an `internalId` (number) | `reviewerIds` on `create_review_request`; the `{teamId}` in `rockhopper://teams/{teamId}` |
|
|
14
15
|
|
|
15
16
|
Decision tree:
|
|
16
17
|
|
|
17
18
|
- **Reading file metadata** → `fileMsId`.
|
|
18
19
|
- **Writing a comment** → `fileMsId` (the server auto-resolves the latest version) OR `versionInternalId` if commenting on a historical version.
|
|
19
20
|
- **Acting on a review** → `versionId`.
|
|
21
|
+
- **Naming a person or a team** → their `id` (uuid).
|
|
22
|
+
|
|
23
|
+
### Users and teams take two spellings; send the uuid
|
|
24
|
+
|
|
25
|
+
A user or team can be named by its `id` (a uuid, e.g. `0198f3a1-2b4c-7d8e-9f01-23456789abcd`) or by its legacy `internalId` (a number). Both are accepted and both name the same record, and the two can be mixed inside one `reviewerIds` array.
|
|
26
|
+
|
|
27
|
+
**Send the uuid.** The numeric form is accepted until **2027-09-14** and is removed after that date. Nothing else in this guide takes a uuid — `versionId`, `versionInternalId` and comment ids stay numeric.
|
|
20
28
|
|
|
21
29
|
If a tool returns an error like "expected versionId but received fileMsId", you used the wrong identifier — check the tool description for which type it accepts.
|
|
22
30
|
|
|
@@ -32,6 +40,18 @@ Standard sequence to inspect a file:
|
|
|
32
40
|
- Reviews → `get_reviews(versionId)`. Pass a `versionId` from step 2.
|
|
33
41
|
- Uncommitted changes → `get_unattributed_changes(fileMsId)` (only useful if `hasUncommittedChanges === true`).
|
|
34
42
|
|
|
43
|
+
### `CHANGE_HISTORY_NOT_READY` — never report it as "no changes"
|
|
44
|
+
|
|
45
|
+
`get_cell_history`, `get_unattributed_changes`, the `rockhopper://files/{id}/changes`
|
|
46
|
+
resource and the change prompts all serve change history strictly: while Rockhopper is
|
|
47
|
+
still computing a file's history they refuse, and the refusal carries
|
|
48
|
+
`{"status":"not_ready", "reason": …, "retryAfterSeconds": N}` with `isError: true`
|
|
49
|
+
(the resource and prompts throw).
|
|
50
|
+
|
|
51
|
+
A refusal means **nothing is known yet**. It is not an empty history, not zero changes,
|
|
52
|
+
and not evidence that the file is unmodified. Wait `retryAfterSeconds` and ask again;
|
|
53
|
+
never answer a user's question about what changed from a not-ready response.
|
|
54
|
+
|
|
35
55
|
## 3. Commenting workflow
|
|
36
56
|
|
|
37
57
|
Comments are scoped to a version. The default version is the latest **committed** one.
|
|
@@ -49,7 +69,7 @@ Comments on historical versions:
|
|
|
49
69
|
|
|
50
70
|
Reviews follow a strict state machine: **`pending` → `approved`** or **`pending` → `cancelled`**. No other transitions are valid.
|
|
51
71
|
|
|
52
|
-
1. `create_review_request({ versionId,
|
|
72
|
+
1. `create_review_request({ versionId, reviewerIds, subject, description? })` — creates a `pending` review. Only the file owner / workspace member with edit rights can create. `reviewerIds` is an array of user ids, **not** email addresses; read them from `rockhopper://teams/{teamId}` and send each member's `id` (uuid).
|
|
53
73
|
2. `approve_review({ reviewId })` — moves `pending` → `approved`. **Only** assigned reviewers can approve.
|
|
54
74
|
3. `cancel_review({ reviewId })` — moves `pending` → `cancelled`. **Only** the requester can cancel.
|
|
55
75
|
|
|
@@ -74,7 +94,7 @@ Two distinct concepts. Tools work with one or the other; do not mix.
|
|
|
74
94
|
- **Committed history** — version snapshots created by `create_version`. Surfaced by `get_file_versions`, `get_cell_history`. Immutable.
|
|
75
95
|
- **Uncommitted changes** — cell edits the user has made since the last `create_version`. Surfaced by `get_unattributed_changes`. Mutable; consumed by the next `create_version` or `discard_changes` call.
|
|
76
96
|
|
|
77
|
-
|
|
97
|
+
Note: on files served from the change ledger (most Microsoft files), `get_cell_history` also includes live edits not yet captured by a committed version — those entries carry the literal versionId `uncommitted`. On files still served from the legacy store, only committed values appear; use `get_unattributed_changes` for pending edits there.
|
|
78
98
|
|
|
79
99
|
## 7. Cross-cloud differences
|
|
80
100
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../src/resources/teams.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,GACb,IAAI,
|
|
1
|
+
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../src/resources/teams.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,GACb,IAAI,CAgCN"}
|
package/dist/resources/teams.js
CHANGED
|
@@ -7,7 +7,13 @@ export function registerTeamResources(server, api) {
|
|
|
7
7
|
description: 'Details for a specific team including members and roles',
|
|
8
8
|
mimeType: 'application/json',
|
|
9
9
|
}, async (uri, { teamId }) => {
|
|
10
|
-
|
|
10
|
+
// ENG-2230: NOT `Number(teamId)`. A team is keyed on a version-7 uuid
|
|
11
|
+
// (ENG-1966) and `Number('0198f3a1-…')` is `NaN`, which would request
|
|
12
|
+
// `/teams/NaN` — a uuid refused on the customer's machine, the same
|
|
13
|
+
// defect class as the reviewerIds schema. The raw path variable is
|
|
14
|
+
// interpolated straight into the URL, so a numeric id produces a
|
|
15
|
+
// byte-identical request to what `Number()` produced before.
|
|
16
|
+
const team = await api.getTeam(Array.isArray(teamId) ? teamId[0] : teamId);
|
|
11
17
|
return {
|
|
12
18
|
contents: [
|
|
13
19
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../src/resources/teams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAI3E,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,GAAc;IAEd,MAAM,CAAC,gBAAgB,CACrB,aAAa,EACb,IAAI,gBAAgB,CAAC,6BAA6B,EAAE;QAClD,IAAI,EAAE,SAAS;KAChB,CAAC,EACF;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,yDAAyD;QACtE,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QACxB,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../src/resources/teams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAI3E,MAAM,UAAU,qBAAqB,CACnC,MAAiB,EACjB,GAAc;IAEd,MAAM,CAAC,gBAAgB,CACrB,aAAa,EACb,IAAI,gBAAgB,CAAC,6BAA6B,EAAE;QAClD,IAAI,EAAE,SAAS;KAChB,CAAC,EACF;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,yDAAyD;QACtE,QAAQ,EAAE,kBAAkB;KAC7B,EACD,KAAK,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;QACxB,sEAAsE;QACtE,sEAAsE;QACtE,oEAAoE;QACpE,mEAAmE;QACnE,iEAAiE;QACjE,6DAA6D;QAC7D,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,OAAO,CAC5B,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAC3C,CAAC;QACF,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,QAAQ,EAAE,kBAAkB;oBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;iBACpC;aACF;SACF,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAqD5E,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,SAAS,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAqD5E,wBAAgB,YAAY,CAC1B,SAAS,EAAE,SAAS,EACpB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,SAAS,CAiCX"}
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { runWithCorrelationId } from './correlation.js';
|
|
3
|
-
import { log } from './logger.js';
|
|
3
|
+
import { log, serviceVersion } from './logger.js';
|
|
4
4
|
import { registerPrompts } from './prompts/index.js';
|
|
5
5
|
import { registerResources } from './resources/index.js';
|
|
6
6
|
import { registerTools } from './tools/index.js';
|
|
@@ -42,7 +42,9 @@ export function createServer(apiClient, options) {
|
|
|
42
42
|
const readOnly = options?.scope === 'read-only';
|
|
43
43
|
const server = new McpServer({
|
|
44
44
|
name: 'rockhopper',
|
|
45
|
-
|
|
45
|
+
// ENG-1955: read from package.json (see logger.ts) so the version a
|
|
46
|
+
// client sees in `initialize` is the version it is actually running.
|
|
47
|
+
version: serviceVersion,
|
|
46
48
|
}, {
|
|
47
49
|
instructions: readOnly
|
|
48
50
|
? 'Rockhopper MCP server for reading Excel file metadata. ' +
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,aAAa,EAA6B,MAAM,kBAAkB,CAAC;AAE5E;;;;;;;;;;;;;;GAcG;AACH,SAAS,uBAAuB,CAAC,MAAiB;IAChD,MAAM,gBAAgB,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,CAAC,YAAY,GAAG,CAAC,CACrB,IAAY,EACZ,MAAe,EACf,EAAW,EAC0B,EAAE;QACvC,MAAM,OAAO,GAAG,EAAqC,CAAC;QACtD,MAAM,OAAO,GAAG,CAAC,GAAG,IAAe,EAAW,EAAE,CAC9C,oBAAoB,CAAC,KAAK,IAAI,EAAE;YAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACzB,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;gBACtC,GAAG,CAAC,IAAI,CACN,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EACjF,WAAW,CACZ,CAAC;gBACF,OAAO,MAAM,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,GAAG,CAAC,KAAK,CACP,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,GAAG,EAAE,EAC9E,kBAAkB,CACnB,CAAC;gBACF,MAAM,GAAG,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QACL,OACE,gBAKD,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3B,CAAC,CAA+B,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,SAAoB,EACpB,OAA8B;IAE9B,MAAM,QAAQ,GAAG,OAAO,EAAE,KAAK,KAAK,WAAW,CAAC;IAEhD,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,YAAY;QAClB,oEAAoE;QACpE,qEAAqE;QACrE,OAAO,EAAE,cAAc;KACxB,EACD;QACE,YAAY,EAAE,QAAQ;YACpB,CAAC,CAAC,yDAAyD;gBACzD,oEAAoE;gBACpE,gDAAgD;gBAChD,gEAAgE;gBAChE,kEAAkE;YACpE,CAAC,CAAC,0DAA0D;gBAC1D,oEAAoE;gBACpE,iEAAiE;gBACjE,0EAA0E;gBAC1E,kEAAkE;gBAClE,kDAAkD;gBAClD,kEAAkE;KACvE,CACF,CAAC;IAEF,uBAAuB,CAAC,MAAM,CAAC,CAAC;IAChC,iBAAiB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACrC,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1C,eAAe,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAEnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-cell-history.d.ts","sourceRoot":"","sources":["../../src/tools/get-cell-history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"get-cell-history.d.ts","sourceRoot":"","sources":["../../src/tools/get-cell-history.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAOlD,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,GACb,IAAI,CAkFN"}
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { assertChangeHistoryComplete, isNotReady, notReadyToolResult, } from '../not-ready.js';
|
|
2
3
|
export function registerGetCellHistoryTool(server, api) {
|
|
3
4
|
server.registerTool('get_cell_history', {
|
|
4
5
|
title: 'Get Cell History',
|
|
5
6
|
description: 'Get the change history for a specific cell in an enrolled file. ' +
|
|
6
|
-
'Shows how the cell value changed across versions.'
|
|
7
|
+
'Shows how the cell value changed across versions. ' +
|
|
8
|
+
// Plan 02 ruling 5 — the contract belongs in the description, not only
|
|
9
|
+
// in the payload: a model deciding "nothing changed" reads the tool
|
|
10
|
+
// doc, not the error envelope.
|
|
11
|
+
'Answers CHANGE_HISTORY_NOT_READY (isError) while Rockhopper is still ' +
|
|
12
|
+
'computing this history. That is NOT an empty history — nothing is ' +
|
|
13
|
+
'known yet; retry after the stated interval and never report an ' +
|
|
14
|
+
'absence of changes from it.',
|
|
7
15
|
inputSchema: {
|
|
8
16
|
fileMsId: z.string().describe('Platform ID of the enrolled file'),
|
|
9
17
|
sheetName: z.string().describe('Name of the worksheet'),
|
|
@@ -17,11 +25,22 @@ export function registerGetCellHistoryTool(server, api) {
|
|
|
17
25
|
},
|
|
18
26
|
}, async ({ fileMsId, sheetName, cellAddress }) => {
|
|
19
27
|
try {
|
|
28
|
+
// Plan 02 ruling 5 (STRICT): completeness FIRST. A pending fold means
|
|
29
|
+
// the change-log window is mid-rewrite, so rows served now are a
|
|
30
|
+
// partial view an assistant would summarise as the whole truth.
|
|
31
|
+
await assertChangeHistoryComplete(api, fileMsId);
|
|
20
32
|
const history = await api.getCellHistory(fileMsId, sheetName, cellAddress);
|
|
33
|
+
// ENG-1638 (P3-2): a ledger-served entry carries a backend-rendered
|
|
34
|
+
// `formatted` line — 'vX.Y.Z: <value> — <provenance> (driven by
|
|
35
|
+
// <human>) — <ts>' — print it verbatim. The legacy normalized
|
|
36
|
+
// fallback (not-eligible file / Google / old backend) has only the
|
|
37
|
+
// four core fields; keep the original rendering for it.
|
|
21
38
|
const summary = history
|
|
22
|
-
.map((h) =>
|
|
23
|
-
|
|
24
|
-
|
|
39
|
+
.map((h) => h.formatted
|
|
40
|
+
? `- ${h.formatted}`
|
|
41
|
+
: `- Version ${h.versionId}: **${JSON.stringify(h.value)}**` +
|
|
42
|
+
(h.changedBy ? ` — by ${h.changedBy}` : '') +
|
|
43
|
+
` — ${h.changedAt}`)
|
|
25
44
|
.join('\n');
|
|
26
45
|
return {
|
|
27
46
|
content: [
|
|
@@ -35,6 +54,11 @@ export function registerGetCellHistoryTool(server, api) {
|
|
|
35
54
|
};
|
|
36
55
|
}
|
|
37
56
|
catch (error) {
|
|
57
|
+
// A not-ready answer is a refusal, never an error string: the generic
|
|
58
|
+
// branch below hands the model prose it may read as "the tool is
|
|
59
|
+
// broken, answer from what I already have".
|
|
60
|
+
if (isNotReady(error))
|
|
61
|
+
return notReadyToolResult(error);
|
|
38
62
|
return {
|
|
39
63
|
content: [
|
|
40
64
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-cell-history.js","sourceRoot":"","sources":["../../src/tools/get-cell-history.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"get-cell-history.js","sourceRoot":"","sources":["../../src/tools/get-cell-history.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EACL,2BAA2B,EAC3B,UAAU,EACV,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,UAAU,0BAA0B,CACxC,MAAiB,EACjB,GAAc;IAEd,MAAM,CAAC,YAAY,CACjB,kBAAkB,EAClB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,kEAAkE;YAClE,oDAAoD;YACpD,uEAAuE;YACvE,oEAAoE;YACpE,+BAA+B;YAC/B,uEAAuE;YACvE,oEAAoE;YACpE,iEAAiE;YACjE,6BAA6B;QAC/B,WAAW,EAAE;YACX,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kCAAkC,CAAC;YACjE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;YACvD,WAAW,EAAE,CAAC;iBACX,MAAM,EAAE;iBACR,QAAQ,CAAC,8CAA8C,CAAC;SAC5D;QACD,WAAW,EAAE;YACX,YAAY,EAAE,IAAI;YAClB,aAAa,EAAE,KAAK;SACrB;KACF,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE,EAAE;QAC7C,IAAI,CAAC;YACH,sEAAsE;YACtE,iEAAiE;YACjE,gEAAgE;YAChE,MAAM,2BAA2B,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;YAEjD,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,cAAc,CACtC,QAAQ,EACR,SAAS,EACT,WAAW,CACZ,CAAC;YAEF,oEAAoE;YACpE,gEAAgE;YAChE,8DAA8D;YAC9D,mEAAmE;YACnE,wDAAwD;YACxD,MAAM,OAAO,GAAG,OAAO;iBACpB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACT,CAAC,CAAC,SAAS;gBACT,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE;gBACpB,CAAC,CAAC,aAAa,CAAC,CAAC,SAAS,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI;oBAC1D,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC3C,MAAM,CAAC,CAAC,SAAS,EAAE,CACxB;iBACA,IAAI,CAAC,IAAI,CAAC,CAAC;YAEd,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,OAAO,CAAC,MAAM;4BAClB,CAAC,CAAC,QAAQ,WAAW,QAAQ,SAAS,OAAO,OAAO,CAAC,MAAM,kBAAkB,OAAO,EAAE;4BACtF,CAAC,CAAC,wBAAwB,WAAW,QAAQ,SAAS,IAAI;qBAC7D;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,sEAAsE;YACtE,iEAAiE;YACjE,4CAA4C;YAC5C,IAAI,UAAU,CAAC,KAAK,CAAC;gBAAE,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;YACxD,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,+BAA+B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;qBAC9F;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAOlD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,GACb,IAAI,CAuNN"}
|
package/dist/tools/search.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { assertChangeHistoryComplete, isNotReady, notReadyToolResult, } from '../not-ready.js';
|
|
2
3
|
export function registerSearchTool(server, api) {
|
|
3
4
|
server.registerTool('search_files', {
|
|
4
5
|
title: 'Search Files',
|
|
@@ -58,7 +59,12 @@ export function registerSearchTool(server, api) {
|
|
|
58
59
|
'every change on a single worksheet (no pagination — bounded by ' +
|
|
59
60
|
'sheet size); (2) omit `sheetName` for the file-wide cursor-paginated ' +
|
|
60
61
|
'view (rows are capped per response with a summary line; pass `cursor` ' +
|
|
61
|
-
'returned by the previous call to fetch the next page).'
|
|
62
|
+
'returned by the previous call to fetch the next page). ' +
|
|
63
|
+
// Plan 02 ruling 5 — "No unattributed changes" is a factual claim, and
|
|
64
|
+
// it is only made after completeness is proven.
|
|
65
|
+
'Answers CHANGE_HISTORY_NOT_READY (isError) while Rockhopper is still ' +
|
|
66
|
+
"computing this file's changes. That is NOT \"no changes\" — retry " +
|
|
67
|
+
'after the stated interval instead of reporting an absence.',
|
|
62
68
|
inputSchema: {
|
|
63
69
|
fileMsId: z.string().describe('Platform ID of the enrolled file'),
|
|
64
70
|
sheetName: z
|
|
@@ -81,6 +87,10 @@ export function registerSearchTool(server, api) {
|
|
|
81
87
|
},
|
|
82
88
|
}, async ({ fileMsId, sheetName, cursor }) => {
|
|
83
89
|
try {
|
|
90
|
+
// Plan 02 ruling 5 (STRICT): gate BOTH modes. The commit-diff fold
|
|
91
|
+
// retracts and rewrites the uncommitted window, so a pending fold
|
|
92
|
+
// means this list is mid-rewrite in either shape.
|
|
93
|
+
await assertChangeHistoryComplete(api, fileMsId);
|
|
84
94
|
if (sheetName) {
|
|
85
95
|
// Sheet-filtered mode: bounded by sheet size, no pagination needed.
|
|
86
96
|
// Audit (2026-05-23) measured: `Summary` sheet on a 12 MB file
|
|
@@ -155,6 +165,8 @@ export function registerSearchTool(server, api) {
|
|
|
155
165
|
};
|
|
156
166
|
}
|
|
157
167
|
catch (error) {
|
|
168
|
+
if (isNotReady(error))
|
|
169
|
+
return notReadyToolResult(error);
|
|
158
170
|
return {
|
|
159
171
|
content: [
|
|
160
172
|
{
|
package/dist/tools/search.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-reviews.d.ts","sourceRoot":"","sources":["../../src/tools/write-reviews.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"write-reviews.d.ts","sourceRoot":"","sources":["../../src/tools/write-reviews.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAyDlD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,GAAG,EAAE,SAAS,GACb,IAAI,CA2LN"}
|
|
@@ -1,11 +1,62 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* ENG-2230 — a reviewer id in EITHER spelling: the version-7 uuid, or the
|
|
4
|
+
* legacy numeric internal id.
|
|
5
|
+
*
|
|
6
|
+
* This schema runs on the CUSTOMER'S machine, inside the published tool
|
|
7
|
+
* definition. That is the whole reason this ticket exists: the previous
|
|
8
|
+
* `z.array(z.number().int().positive())` refused a uuid before any request
|
|
9
|
+
* left the process, in all 10 versions published since 0.2.0, so no backend
|
|
10
|
+
* change could rescue it. Both branches are load-bearing — dropping the
|
|
11
|
+
* numeric one would break every customer still on an older version, which is
|
|
12
|
+
* exactly the population the 400-day window exists to protect.
|
|
13
|
+
*
|
|
14
|
+
* The uuid branch deliberately mirrors the BACKEND predicate
|
|
15
|
+
* (`src/common/identifiers/resource-identifier.ts:37`) character for
|
|
16
|
+
* character rather than using zod's `z.uuid()`. Zod refuses a uuid whose
|
|
17
|
+
* version or variant nibble is non-RFC; the backend accepts it on purpose,
|
|
18
|
+
* because "a predicate that refuses on the VERSION nibble makes a row
|
|
19
|
+
* unreachable by its own primary key the moment one value disagrees". A
|
|
20
|
+
* client-side validator stricter than the server is this ticket's own defect
|
|
21
|
+
* in a narrower form — a row the backend would resolve, refused on a machine
|
|
22
|
+
* we cannot redeploy.
|
|
23
|
+
*
|
|
24
|
+
* Existence is settled by the backend lookup, which is authoritative. Shape
|
|
25
|
+
* is settled here, which is not.
|
|
26
|
+
*
|
|
27
|
+
* Both cases are accepted because PostgreSQL normalises uuid case, so `018F…`
|
|
28
|
+
* and `018f…` are the same row. The class is spelled `[0-9a-fA-F]` rather than
|
|
29
|
+
* `[0-9a-f]` with the `/i` flag on purpose: this schema is converted to JSON
|
|
30
|
+
* Schema and advertised in `tools/list`, and JSON Schema `pattern` carries no
|
|
31
|
+
* case-insensitivity flag —
|
|
32
|
+
* an `/i` regex emits a lowercase-only pattern, so a client that pre-validates
|
|
33
|
+
* against the advertised schema would refuse an uppercase uuid that the zod
|
|
34
|
+
* schema and the backend both accept. Writing the class out keeps the
|
|
35
|
+
* advertised contract and the enforced one identical.
|
|
36
|
+
*/
|
|
37
|
+
const UUID_TEXT = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
38
|
+
/**
|
|
39
|
+
* The refusal text an AI client reads before deciding what to retry with. Zod's
|
|
40
|
+
* default for the string branch is "must match pattern /^[0-9a-f]{8}…/", which
|
|
41
|
+
* names only the branch that failed and hides that a number is equally fine —
|
|
42
|
+
* so it is set on both the branch and the union.
|
|
43
|
+
*/
|
|
44
|
+
const REVIEWER_ID_ERROR = 'must be a user id — either a uuid (preferred) or a positive integer internal id';
|
|
45
|
+
const reviewerIdSchema = z.union([
|
|
46
|
+
z.number().int().positive(),
|
|
47
|
+
z.string().regex(UUID_TEXT, REVIEWER_ID_ERROR),
|
|
48
|
+
], { error: REVIEWER_ID_ERROR });
|
|
2
49
|
export function registerWriteReviewTools(server, api) {
|
|
3
50
|
server.registerTool('create_review_request', {
|
|
4
51
|
title: 'Create Review Request',
|
|
5
52
|
description: 'Request a review for a specific file version. Assigns reviewers who ' +
|
|
6
53
|
'will be notified to approve or comment on the version. ' +
|
|
7
|
-
'
|
|
8
|
-
'
|
|
54
|
+
'A reviewer ID is either the user\'s uuid (preferred) or the legacy ' +
|
|
55
|
+
'numeric internal ID — both are accepted. Read either one from the ' +
|
|
56
|
+
'rockhopper://teams/{teamId} resource, which lists each member\'s user ' +
|
|
57
|
+
'record with both an "id" (uuid) and an "internalId" (number); use it to ' +
|
|
58
|
+
'resolve platform IDs (msId / googleId) to a reviewer ID first. ' +
|
|
59
|
+
'The numeric form is accepted until 2027-09-14 and removed after — send the uuid.',
|
|
9
60
|
inputSchema: {
|
|
10
61
|
versionId: z
|
|
11
62
|
.number()
|
|
@@ -21,9 +72,12 @@ export function registerWriteReviewTools(server, api) {
|
|
|
21
72
|
.optional()
|
|
22
73
|
.describe('Optional description of what to review'),
|
|
23
74
|
reviewerIds: z
|
|
24
|
-
.array(
|
|
75
|
+
.array(reviewerIdSchema)
|
|
25
76
|
.min(1)
|
|
26
|
-
.describe('
|
|
77
|
+
.describe('User IDs of the reviewers to assign. Each entry is either the ' +
|
|
78
|
+
"user's uuid (preferred) or the legacy numeric internal ID; the " +
|
|
79
|
+
'two spellings can be mixed in one array. The numeric form is ' +
|
|
80
|
+
'accepted until 2027-09-14 and removed after.'),
|
|
27
81
|
},
|
|
28
82
|
annotations: {
|
|
29
83
|
readOnlyHint: false,
|