@topline-build/harness-mcp 0.5.1 → 0.5.2
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 +6 -0
- package/README.md +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -1
- package/docs/generated/build-mcp-manifest.json +7 -2
- package/docs/generated/build-mcp-reference.md +2 -1
- package/package.json +1 -1
- package/scripts/certify-live.mjs +15 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable customer-visible Build MCP changes are recorded here. A source chang
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.5.2 - 2026-09-26
|
|
8
|
+
|
|
9
|
+
- Report rejected bearer credentials as `authorization_required`, with scoped cache-clearing and browser reconnect guidance instead of input-correction advice.
|
|
10
|
+
- Guide connection-specific cache cleanup and recovery of existing deployment progress before a write is retried.
|
|
11
|
+
- Report native client certification timeouts as failures even when partial output mentions authentication; a timed-out process is not proof of a missing provider session.
|
|
12
|
+
|
|
7
13
|
## 0.5.1 - 2026-09-18
|
|
8
14
|
|
|
9
15
|
- Harden the bundled Claude certification script with strict tool isolation, effective-runtime proof, and noninteractive JSONL validation.
|
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ npx -y @topline-build/harness-mcp
|
|
|
22
22
|
|
|
23
23
|
Run `npx -y @topline-build/harness-mcp --help` for every supported flag and environment variable. `TOPLINE_MCP_TOKEN` is an advanced manual-token fallback; browser-approved OAuth is preferred.
|
|
24
24
|
|
|
25
|
+
If a tool returns `authorization_required`, clear only that connection's OAuth cache and reconnect through browser sign-in. Replace an expired or revoked manual token if using the fallback. Inspect existing artifact/deployment progress before retrying a write; the helper does not silently replay the rejected request.
|
|
26
|
+
|
|
25
27
|
## Typical workflow
|
|
26
28
|
|
|
27
29
|
1. Use `platform_init_artifact_app` to create a starter, or point at an existing artifact app.
|
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface PackedArtifactAppBundle {
|
|
|
32
32
|
type ToolProgressReporter = (progress: number, message: string) => void | Promise<void>;
|
|
33
33
|
export declare const DEFAULT_MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
34
34
|
export declare const BUILD_MCP_SERVER_NAME = "topline-build-stdio";
|
|
35
|
-
export declare const HARNESS_MCP_VERSION = "0.5.
|
|
35
|
+
export declare const HARNESS_MCP_VERSION = "0.5.2";
|
|
36
36
|
export declare const DEFAULT_OAUTH_SCOPES: readonly string[];
|
|
37
37
|
export interface BuildMcpSetupOption {
|
|
38
38
|
readonly flag: string;
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } fr
|
|
|
13
13
|
export const DEFAULT_MCP_PROTOCOL_VERSION = "2025-11-25";
|
|
14
14
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
15
15
|
export const BUILD_MCP_SERVER_NAME = "topline-build-stdio";
|
|
16
|
-
export const HARNESS_MCP_VERSION = "0.5.
|
|
16
|
+
export const HARNESS_MCP_VERSION = "0.5.2";
|
|
17
17
|
export const DEFAULT_OAUTH_SCOPES = Object.freeze(["harness:artifact:read", "harness:artifact:write", "harness:artifact:deploy", "harness:artifact:delete"]);
|
|
18
18
|
export const BUILD_MCP_SETUP_OPTIONS = Object.freeze([
|
|
19
19
|
{ flag: "--url", env: "TOPLINE_API_URL", aliases: ["PLATFORM_API_URL", "OVERSITE_API_URL"], required: true, description: "Topline tenant base URL. HTTPS is required except for loopback development." },
|
|
@@ -1063,6 +1063,7 @@ function toolError(message) {
|
|
|
1063
1063
|
return structuredToolError(message);
|
|
1064
1064
|
}
|
|
1065
1065
|
const BUILD_MCP_RECOVERY_ERROR_VALUES = {
|
|
1066
|
+
authorization_required: { retryable: false, recoveryAction: "Clear only this connection's cached OAuth credentials, then reconnect the Build MCP through browser sign-in. If using a manual bearer token, replace the expired or revoked token. Recover existing artifact/deployment progress before retrying a write." },
|
|
1066
1067
|
platform_harness_timeout: { retryable: true, recoveryAction: "Retry the call. For a deployment, use platform_list_artifact_drafts to recover the artifact and platform_wait_artifact_deploy to resume progress." },
|
|
1067
1068
|
platform_mcp_tool_error: { retryable: false, recoveryAction: "Read the returned error code, correct the request or OAuth scopes, then retry." },
|
|
1068
1069
|
artifact_deploy_wait_timeout: { retryable: true, recoveryAction: "Call platform_wait_artifact_deploy again with the same artifactId and operationId." },
|
|
@@ -1087,6 +1088,8 @@ function structuredToolError(message) {
|
|
|
1087
1088
|
};
|
|
1088
1089
|
}
|
|
1089
1090
|
function recoveryErrorCode(message) {
|
|
1091
|
+
if (/^platform_harness_http_error:401(?::|$)/.test(message))
|
|
1092
|
+
return "authorization_required";
|
|
1090
1093
|
const known = [
|
|
1091
1094
|
"artifact_delete_plan_stale",
|
|
1092
1095
|
"artifact_deploy_wait_timeout",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"package": {
|
|
4
4
|
"name": "@topline-build/harness-mcp",
|
|
5
|
-
"version": "0.5.
|
|
5
|
+
"version": "0.5.2",
|
|
6
6
|
"bin": {
|
|
7
7
|
"topline-mcp": "dist/cli.js"
|
|
8
8
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"defaultVersion": "2025-11-25",
|
|
13
13
|
"server": {
|
|
14
14
|
"name": "topline-build-stdio",
|
|
15
|
-
"version": "0.5.
|
|
15
|
+
"version": "0.5.2"
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"generatedFrom": {
|
|
@@ -171,6 +171,11 @@
|
|
|
171
171
|
"recoveryAction": "Inspect the code and message, correct the input if needed, then retry the same tool."
|
|
172
172
|
},
|
|
173
173
|
"catalog": [
|
|
174
|
+
{
|
|
175
|
+
"code": "authorization_required",
|
|
176
|
+
"retryable": false,
|
|
177
|
+
"recoveryAction": "Clear only this connection's cached OAuth credentials, then reconnect the Build MCP through browser sign-in. If using a manual bearer token, replace the expired or revoked token. Recover existing artifact/deployment progress before retrying a write."
|
|
178
|
+
},
|
|
174
179
|
{
|
|
175
180
|
"code": "platform_harness_timeout",
|
|
176
181
|
"retryable": true,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Generated from `src/index.ts#BUILD_MCP_TOOLS`. Do not edit this file by hand.
|
|
4
4
|
|
|
5
|
-
Package: `@topline-build/harness-mcp@0.5.
|
|
5
|
+
Package: `@topline-build/harness-mcp@0.5.2`
|
|
6
6
|
Transport: `stdio`
|
|
7
7
|
Default protocol: `2025-11-25`
|
|
8
8
|
Canonical guide: https://topline.build/docs/build-mcp-guide
|
|
@@ -32,6 +32,7 @@ The first authenticated tool call normally opens browser approval. Set `TOPLINE_
|
|
|
32
32
|
|
|
33
33
|
Tool failures set `isError: true` and return `structuredContent.error` with `code`, `message`, `retryable`, and `recoveryAction`.
|
|
34
34
|
|
|
35
|
+
- `authorization_required` (not automatically retryable): Clear only this connection's cached OAuth credentials, then reconnect the Build MCP through browser sign-in. If using a manual bearer token, replace the expired or revoked token. Recover existing artifact/deployment progress before retrying a write.
|
|
35
36
|
- `platform_harness_timeout` (retryable): Retry the call. For a deployment, use platform_list_artifact_drafts to recover the artifact and platform_wait_artifact_deploy to resume progress.
|
|
36
37
|
- `platform_mcp_tool_error` (not automatically retryable): Read the returned error code, correct the request or OAuth scopes, then retry.
|
|
37
38
|
- `artifact_deploy_wait_timeout` (retryable): Call platform_wait_artifact_deploy again with the same artifactId and operationId.
|
package/package.json
CHANGED
package/scripts/certify-live.mjs
CHANGED
|
@@ -218,9 +218,7 @@ async function agentMediatedRead(name, command, scratch, apiUrl, appDir, workspa
|
|
|
218
218
|
"-p", "--verbose", "--output-format", "stream-json", prompt
|
|
219
219
|
], scratch, 180000, strictAgentEnvironment(process.env));
|
|
220
220
|
if (!result.ok) {
|
|
221
|
-
return
|
|
222
|
-
? skipped("provider_credentials_or_client_session_unavailable")
|
|
223
|
-
: failed("client_mediated_read_failed", result.exitCode);
|
|
221
|
+
return clientMediatedFailure(result);
|
|
224
222
|
}
|
|
225
223
|
const usedTool = hasToolCallEvidence(result.output, "platform_inspect_artifact_app");
|
|
226
224
|
const certified = hasAssistantCertificationEvidence(result.output);
|
|
@@ -344,6 +342,12 @@ async function certifyProtocol({ apiUrl, token, appDir, workspaceRoot }) {
|
|
|
344
342
|
}
|
|
345
343
|
|
|
346
344
|
function selfTestReport() {
|
|
345
|
+
const timedOutProbe = run({ executable: process.execPath, prefix: [] }, ["-e", "console.log('api key authentication required'); setInterval(() => {}, 1000)"], process.cwd(), 1000);
|
|
346
|
+
const timedOutFailure = clientMediatedFailure(timedOutProbe);
|
|
347
|
+
if (!timedOutProbe.timedOut || !authUnavailable(timedOutProbe.output) || timedOutFailure.status !== "failed" || timedOutFailure.reason !== "client_mediated_read_timed_out"
|
|
348
|
+
|| clientMediatedFailure({ timedOut: false, output: "not logged in", exitCode: 1 }).status !== "skipped") {
|
|
349
|
+
throw new Error("client_timeout_classification_self_test_failed");
|
|
350
|
+
}
|
|
347
351
|
const toolName = "platform_inspect_artifact_app";
|
|
348
352
|
const promptOnly = JSON.stringify({ type: "user", content: `Call ${toolName} and print TOPLINE_CLIENT_READ_CERTIFIED.` });
|
|
349
353
|
const toolEvent = JSON.stringify({ type: "item.completed", item: { type: "mcp_tool_call", tool: toolName } });
|
|
@@ -477,12 +481,20 @@ function run(command, commandArgs, cwd, timeout = 30000, env = clientProbeEnviro
|
|
|
477
481
|
return {
|
|
478
482
|
ok: result.status === 0,
|
|
479
483
|
missing: result.error?.code === "ENOENT",
|
|
484
|
+
timedOut: result.error?.code === "ETIMEDOUT",
|
|
480
485
|
exitCode: result.status,
|
|
481
486
|
stdout: result.stdout || "",
|
|
482
487
|
output: `${result.stdout || ""}\n${result.stderr || ""}`
|
|
483
488
|
};
|
|
484
489
|
}
|
|
485
490
|
|
|
491
|
+
function clientMediatedFailure(result) {
|
|
492
|
+
if (result.timedOut) return failed("client_mediated_read_timed_out", result.exitCode);
|
|
493
|
+
return authUnavailable(result.output)
|
|
494
|
+
? skipped("provider_credentials_or_client_session_unavailable")
|
|
495
|
+
: failed("client_mediated_read_failed", result.exitCode);
|
|
496
|
+
}
|
|
497
|
+
|
|
486
498
|
function authUnavailable(output) {
|
|
487
499
|
return /not logged in|not authenticated|authentication required|api key|login required|please login|sign in|unable to locate credentials|security token|AccessDeniedException|UnrecognizedClientException/i.test(output);
|
|
488
500
|
}
|