@absolutejs/mcp 0.17.4 → 0.17.6
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 +16 -0
- package/UPSTREAM_ISSUES.md +19 -0
- package/canary/vscode/README.md +33 -0
- package/canary/vscode/check-mount.ts +75 -0
- package/canary/vscode/webview-mount-race.patch +22 -0
- package/changelog.json +28 -0
- package/dist/apps.js +3 -1
- package/dist/index.js +4 -2
- package/dist/src/apps.d.ts +3 -1
- package/docs/host-canaries.md +10 -0
- package/package.json +4 -3
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,22 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.17.6 — 2026-09-11
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Allow verified patched hosts through the version fallback with optional Apps configuration** (`clientSupportsMcpApps`, `McpAppsConfig`)
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **Use text and structured report results on affected VS Code builds while their upstream webview startup race remains unresolved**
|
|
18
|
+
|
|
19
|
+
## 0.17.5 — 2026-09-11
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Distribute a verified VS Code webview startup-race source patch and actual-method regression checker, with explicit upstream delivery requirements**
|
|
24
|
+
|
|
9
25
|
## 0.17.4 — 2026-09-11
|
|
10
26
|
|
|
11
27
|
### Added
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Upstream issues
|
|
2
|
+
|
|
3
|
+
## VS Code webviews can go blank after a remount
|
|
4
|
+
|
|
5
|
+
Tracking: [microsoft/vscode#335908](https://github.com/microsoft/vscode/issues/335908). An issue was submitted; no pull request was submitted.
|
|
6
|
+
|
|
7
|
+
An earlier asynchronous origin-hash completion can replace the iframe after a newer mount connects. In native Windows VS Code 1.135.0, delaying the first hash reproduced a blank report. A current-promise guard passed three rounds across balance, usage and receipts in the isolated test process. The same unguarded method was found in the local 1.136.1 bundle; that version was source-inspected, not runtime-tested. See [the source patch and regression checker](canary/vscode/README.md).
|
|
8
|
+
|
|
9
|
+
### Shared workaround in MCP 0.17.6
|
|
10
|
+
|
|
11
|
+
New sessions identifying themselves as `Visual Studio Code` version `1.135.0` or `1.136.1` receive text and structured results without Apps metadata or HTML resources. Report tools remain available. Authorization, billing and other host negotiations are unchanged. This exact-version rule is a presentation workaround, not a security boundary or a certification of other versions.
|
|
12
|
+
|
|
13
|
+
For an existing VS Code connection, run **MCP: Reset Cached Tools**, then **Developer: Reload Window**, reconnect/start the MCP server and open a new chat. Reconnection alone reused old UI metadata in our test. Reconnect the MCP server after upgrading: the negotiated Apps capability is stored in the session, so existing sessions retain their old decision. Consumers get the workaround through the package; they do not need a project-specific renderer or an editor patch.
|
|
14
|
+
|
|
15
|
+
`apps.allowKnownBrokenHosts: true` bypasses the version exclusion for controlled testing of a verified patched host. It still requires the host to advertise the Apps MIME capability. Do not enable it on ordinary affected installations. The diagnostic in-memory VS Code patch does not survive restart and is not the deployed workaround.
|
|
16
|
+
|
|
17
|
+
### Removal
|
|
18
|
+
|
|
19
|
+
Keep the fallback for affected builds until they can reliably render Apps. When an upstream fix ships, verify fresh conversations, repeated mounts, refresh and pagination in the released host before certifying rich views there. Remove the override when a patched test build is retired. Keep the source regression and issue history so a successful retry is not mistaken for a fix.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# VS Code webview mount race
|
|
2
|
+
|
|
3
|
+
A host lifecycle race can leave an MCP App blank before its inner document loads. `mountTo` computes the parent-origin hash asynchronously. A subsequent mount can finish first; without a current-startup check, the older completion can overwrite the origin and reset the iframe after the newer connection is ready.
|
|
4
|
+
|
|
5
|
+
`webview-mount-race.patch` fixes the host's source implementation by applying a completion only when its captured promise is still the current mount promise. It does not change MCP or billing code, disable sandboxing, retry tools, or add an arbitrary delay.
|
|
6
|
+
|
|
7
|
+
The baseline is VS Code 1.135.0, commit `08d4889f9ec4a1685d257b9b95de036c8e1ce1e5`. The same mount method was present on upstream main when checked September 11, 2026. [Upstream source](https://github.com/microsoft/vscode/blob/08d4889f9ec4a1685d257b9b95de036c8e1ce1e5/src/vs/workbench/contrib/webview/browser/webviewElement.ts).
|
|
8
|
+
|
|
9
|
+
## Reproduce and check the source fix
|
|
10
|
+
|
|
11
|
+
Use a trusted VS Code checkout and the package's Bun checker. The checker extracts and executes the actual `mountTo` method; it does not substitute a second implementation. Other DOM/service hooks are stubbed, and hash completion order is controlled.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
bun /path/to/node_modules/@absolutejs/mcp/canary/vscode/check-mount.ts src/vs/workbench/contrib/webview/browser/webviewElement.ts
|
|
15
|
+
# Original: exits 1 for both obsolete-startup checks.
|
|
16
|
+
git apply /path/to/node_modules/@absolutejs/mcp/canary/vscode/webview-mount-race.patch
|
|
17
|
+
bun /path/to/node_modules/@absolutejs/mcp/canary/vscode/check-mount.ts src/vs/workbench/contrib/webview/browser/webviewElement.ts
|
|
18
|
+
# Patched: all four checks pass; exits 0.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The checker executes code from the supplied source file. Use only a trusted checkout. It needs Bun and is a targeted lifecycle regression, not the complete VS Code test suite.
|
|
22
|
+
|
|
23
|
+
## Native-host verification
|
|
24
|
+
|
|
25
|
+
In the isolated, signed-in native Windows VS Code 1.135.0 test window, completing an earlier origin hash 350ms after a newer startup reproduced a blank report on the unmodified host. With the equivalent current-promise guard applied only in the test process, three repeated rounds across balance, usage and receipts rendered all views without reloading. Both source-level completion orders now pass, and disposed views remain excluded. No paid tools or customer data were used.
|
|
26
|
+
|
|
27
|
+
Some unmodified first-load runs passed too: this is a timing-sensitive failure, and ordinary successful retries do not prove it fixed. The controlled race provides the regression. Additional host defects remain possible; this patch addresses the demonstrated obsolete-startup race.
|
|
28
|
+
|
|
29
|
+
## Delivery status
|
|
30
|
+
|
|
31
|
+
The bug is reported in [microsoft/vscode#335908](https://github.com/microsoft/vscode/issues/335908); no PR was submitted. This package distributes the source patch and checker for review, but does not modify installed editors. The test-process patch disappears on restart.
|
|
32
|
+
|
|
33
|
+
MCP 0.17.6 instead defaults affected versions to text and structured reports. Reconnect after upgrading to negotiate the fallback. See [UPSTREAM_ISSUES.md](../../UPSTREAM_ISSUES.md) for the exact version scope, diagnostic override and removal conditions. Rich first-load support remains unverified in an unmodified released host.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/** Run against a trusted VS Code webviewElement.ts checkout; executes its real mountTo method. */
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
const file = process.argv[2];
|
|
4
|
+
if (!file) throw Error("Usage: bun check-mount.ts /path/to/webviewElement.ts");
|
|
5
|
+
const source = await readFile(file, "utf8");
|
|
6
|
+
const start = source.indexOf("\tpublic mountTo(");
|
|
7
|
+
const end = source.indexOf("\n\tprivate _registerMessageHandler(", start);
|
|
8
|
+
if (start < 0 || end < 0) throw Error("Unsupported source layout");
|
|
9
|
+
const method = source.slice(start, end);
|
|
10
|
+
const javascript = new Bun.Transpiler({ loader: "ts" }).transformSync(
|
|
11
|
+
`class MountProbe { ${method} }`,
|
|
12
|
+
);
|
|
13
|
+
type Probe = {
|
|
14
|
+
mountTo: (element: unknown, window: unknown) => void;
|
|
15
|
+
_encodedWebviewOrigin?: string;
|
|
16
|
+
_disposed: boolean;
|
|
17
|
+
};
|
|
18
|
+
const results: { name: string; passed: boolean }[] = [];
|
|
19
|
+
for (const order of [
|
|
20
|
+
[1, 0],
|
|
21
|
+
[0, 1],
|
|
22
|
+
]) {
|
|
23
|
+
const pending: ((origin: string) => void)[] = [];
|
|
24
|
+
const mounted: string[] = [];
|
|
25
|
+
const create = new Function(
|
|
26
|
+
"parentOriginHash",
|
|
27
|
+
"EventType",
|
|
28
|
+
"addDisposableListener",
|
|
29
|
+
`${javascript};return MountProbe;`,
|
|
30
|
+
);
|
|
31
|
+
const Constructor = create(
|
|
32
|
+
() => new Promise<string>((resolve) => pending.push(resolve)),
|
|
33
|
+
{},
|
|
34
|
+
() => ({}),
|
|
35
|
+
) as new () => Probe;
|
|
36
|
+
const probe = new Constructor();
|
|
37
|
+
Object.assign(probe, {
|
|
38
|
+
element: {},
|
|
39
|
+
origin: "test",
|
|
40
|
+
_disposed: false,
|
|
41
|
+
_registerMessageHandler: () => {},
|
|
42
|
+
_register: () => {},
|
|
43
|
+
perfMark: () => {},
|
|
44
|
+
_initElement: (origin: string) => mounted.push(origin),
|
|
45
|
+
});
|
|
46
|
+
const container = { appendChild: () => {} };
|
|
47
|
+
probe.mountTo(container, { origin: "first-window", vscodeWindowId: 1 });
|
|
48
|
+
probe.mountTo(container, { origin: "second-window", vscodeWindowId: 2 });
|
|
49
|
+
for (const index of order) {
|
|
50
|
+
pending[index]!(index === 0 ? "old-origin" : "new-origin");
|
|
51
|
+
await new Promise<void>((resolve) => queueMicrotask(resolve));
|
|
52
|
+
}
|
|
53
|
+
results.push({
|
|
54
|
+
name:
|
|
55
|
+
order[0] === 1
|
|
56
|
+
? "late-old-startup-ignored"
|
|
57
|
+
: "early-obsolete-startup-ignored",
|
|
58
|
+
passed:
|
|
59
|
+
mounted.length === 1 &&
|
|
60
|
+
mounted[0] === "new-origin" &&
|
|
61
|
+
probe._encodedWebviewOrigin === "new-origin",
|
|
62
|
+
});
|
|
63
|
+
mounted.length = 0;
|
|
64
|
+
probe.mountTo(container, { origin: "disposed-window", vscodeWindowId: 3 });
|
|
65
|
+
probe._disposed = true;
|
|
66
|
+
pending[2]!("disposed-origin");
|
|
67
|
+
await new Promise<void>((resolve) => queueMicrotask(resolve));
|
|
68
|
+
results.push({
|
|
69
|
+
name: "disposed-view-not-mounted",
|
|
70
|
+
passed: mounted.length === 0,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const passed = results.every((result) => result.passed);
|
|
74
|
+
console.log(JSON.stringify({ passed, results }, null, 2));
|
|
75
|
+
if (!passed) process.exitCode = 1;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
--- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts
|
|
2
|
+
+++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts
|
|
3
|
+
@@ -477,9 +477,16 @@
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
this._windowId = targetWindow.vscodeWindowId;
|
|
7
|
+
- this._encodedWebviewOriginPromise = parentOriginHash(targetWindow.origin, this.origin).then(id => this._encodedWebviewOrigin = id);
|
|
8
|
+
- this._encodedWebviewOriginPromise.then(encodedWebviewOrigin => {
|
|
9
|
+
- if (!this._disposed) {
|
|
10
|
+
+ const encodedWebviewOriginPromise = parentOriginHash(targetWindow.origin, this.origin).then(id => {
|
|
11
|
+
+ if (this._encodedWebviewOriginPromise === encodedWebviewOriginPromise) {
|
|
12
|
+
+ this._encodedWebviewOrigin = id;
|
|
13
|
+
+ }
|
|
14
|
+
+ return id;
|
|
15
|
+
+ });
|
|
16
|
+
+ this._encodedWebviewOriginPromise = encodedWebviewOriginPromise;
|
|
17
|
+
+ encodedWebviewOriginPromise.then(encodedWebviewOrigin => {
|
|
18
|
+
+ // A remount may supersede this startup before its origin hash resolves.
|
|
19
|
+
+ if (!this._disposed && this._encodedWebviewOriginPromise === encodedWebviewOriginPromise) {
|
|
20
|
+
this._initElement(encodedWebviewOrigin, this.extension, this._options, targetWindow);
|
|
21
|
+
}
|
|
22
|
+
});
|
package/changelog.json
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
"contract": 1,
|
|
3
3
|
"name": "@absolutejs/mcp",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"version": "0.17.6",
|
|
7
|
+
"date": "2026-09-11",
|
|
8
|
+
"changes": [
|
|
9
|
+
{
|
|
10
|
+
"kind": "fixed",
|
|
11
|
+
"summary": "Use text and structured report results on affected VS Code builds while their upstream webview startup race remains unresolved"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "added",
|
|
15
|
+
"summary": "Allow verified patched hosts through the version fallback with optional Apps configuration",
|
|
16
|
+
"symbols": [
|
|
17
|
+
"clientSupportsMcpApps",
|
|
18
|
+
"McpAppsConfig"
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"version": "0.17.5",
|
|
25
|
+
"date": "2026-09-11",
|
|
26
|
+
"changes": [
|
|
27
|
+
{
|
|
28
|
+
"kind": "added",
|
|
29
|
+
"summary": "Distribute a verified VS Code webview startup-race source patch and actual-method regression checker, with explicit upstream delivery requirements"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
5
33
|
{
|
|
6
34
|
"version": "0.17.4",
|
|
7
35
|
"date": "2026-09-11",
|
package/dist/apps.js
CHANGED
|
@@ -49,9 +49,11 @@ var createBillingApps = () => {
|
|
|
49
49
|
|
|
50
50
|
// src/apps.ts
|
|
51
51
|
var MCP_APP_MIME = "text/html;profile=mcp-app";
|
|
52
|
-
var clientSupportsMcpApps = (params) => {
|
|
52
|
+
var clientSupportsMcpApps = (params, options = {}) => {
|
|
53
53
|
if (!isRecord(params) || !isRecord(params.capabilities) || !isRecord(params.capabilities.extensions))
|
|
54
54
|
return false;
|
|
55
|
+
if (!options.allowKnownBrokenHosts && isRecord(params.clientInfo) && params.clientInfo.name === "Visual Studio Code" && (params.clientInfo.version === "1.135.0" || params.clientInfo.version === "1.136.1"))
|
|
56
|
+
return false;
|
|
55
57
|
const ui = params.capabilities.extensions["io.modelcontextprotocol/ui"];
|
|
56
58
|
return isRecord(ui) && Array.isArray(ui.mimeTypes) && ui.mimeTypes.includes(MCP_APP_MIME);
|
|
57
59
|
};
|
package/dist/index.js
CHANGED
|
@@ -49,9 +49,11 @@ var createBillingApps = () => {
|
|
|
49
49
|
|
|
50
50
|
// src/apps.ts
|
|
51
51
|
var MCP_APP_MIME = "text/html;profile=mcp-app";
|
|
52
|
-
var clientSupportsMcpApps = (params) => {
|
|
52
|
+
var clientSupportsMcpApps = (params, options = {}) => {
|
|
53
53
|
if (!isRecord(params) || !isRecord(params.capabilities) || !isRecord(params.capabilities.extensions))
|
|
54
54
|
return false;
|
|
55
|
+
if (!options.allowKnownBrokenHosts && isRecord(params.clientInfo) && params.clientInfo.name === "Visual Studio Code" && (params.clientInfo.version === "1.135.0" || params.clientInfo.version === "1.136.1"))
|
|
56
|
+
return false;
|
|
55
57
|
const ui = params.capabilities.extensions["io.modelcontextprotocol/ui"];
|
|
56
58
|
return isRecord(ui) && Array.isArray(ui.mimeTypes) && ui.mimeTypes.includes(MCP_APP_MIME);
|
|
57
59
|
};
|
|
@@ -1379,7 +1381,7 @@ var initialize = async (config, id, params, context) => {
|
|
|
1379
1381
|
if (!config.elicitation?.enabled && !config.apps || !context.sessions)
|
|
1380
1382
|
return response;
|
|
1381
1383
|
const elicitation = clientElicitation(params);
|
|
1382
|
-
const sessionId = await context.sessions.create(elicitation.form || elicitation.url, elicitation.url, Boolean(config.apps) && clientSupportsMcpApps(params));
|
|
1384
|
+
const sessionId = await context.sessions.create(elicitation.form || elicitation.url, elicitation.url, Boolean(config.apps) && clientSupportsMcpApps(params, config.apps));
|
|
1383
1385
|
response.headers.set("Mcp-Session-Id", sessionId);
|
|
1384
1386
|
return response;
|
|
1385
1387
|
};
|
package/dist/src/apps.d.ts
CHANGED
|
@@ -5,11 +5,13 @@ export type McpAppResource = {
|
|
|
5
5
|
html: string;
|
|
6
6
|
};
|
|
7
7
|
export type McpAppsConfig = {
|
|
8
|
+
/** Only enable for a host build whose upstream rendering fix has been verified. */
|
|
9
|
+
allowKnownBrokenHosts?: boolean;
|
|
8
10
|
resources: Record<string, McpAppResource>;
|
|
9
11
|
store?: McpSessionStore;
|
|
10
12
|
};
|
|
11
13
|
/** Capability is presentation only; never a commerce or authorization decision. */
|
|
12
|
-
export declare const clientSupportsMcpApps: (params: unknown) => boolean;
|
|
14
|
+
export declare const clientSupportsMcpApps: (params: unknown, options?: Pick<McpAppsConfig, "allowKnownBrokenHosts">) => boolean;
|
|
13
15
|
export declare const withMcpApp: (tool: McpTool, resourceUri: string) => McpTool;
|
|
14
16
|
/** Offline templates: all data comes through authenticated tool results and the
|
|
15
17
|
* official host bridge. Network, nested frames and privileged permissions are not requested. */
|
package/docs/host-canaries.md
CHANGED
|
@@ -93,3 +93,13 @@ const report = await runAuthenticatedCanary({
|
|
|
93
93
|
Choose two accounts with known distinct data. The canary refuses identical results as inconclusive and verifies repeated reads remain stable. It checks both directions of session-ID substitution: rejecting the foreign session or returning the currently authenticated account's result is acceptable. It also checks missing/invalid credentials, unauthorized DELETE, authorized deletion, terminated-session 404, and fresh initialization/read. It creates and cleans up only its own MCP sessions, never purchases or modifies account data. Read-only annotations are a prerequisite, not a substitute for the operator selecting a known non-billable read tool. The current harness requires JSON RPC responses; SSE-only servers are not certified by it.
|
|
94
94
|
|
|
95
95
|
This verifies the selected read path, not every tenant resource, token revocation, automatic host reconnect, or billing permissions. Session termination is explicit DELETE; timed TTL expiry needs a separate store test. Use dedicated test clients and remove their grants after verification. Do not publish tokens, transcripts, fingerprints, or raw account data.
|
|
96
|
+
|
|
97
|
+
## VS Code first-load race identified
|
|
98
|
+
|
|
99
|
+
MCP 0.17.5 includes `canary/vscode/README.md`, a VS Code source patch and an executable actual-method regression. A controlled out-of-order origin-hash completion reproduces a blank view in native VS Code 1.135.0. The candidate current-promise guard passed three rounds across all three report views and both source-level completion orders. This is a host fix, not an MCP renderer change. The package distributes the patch for review; it does not modify installed editors or certify an upstream release. First-load activation remains blocked until a supported host containing the fix is verified.
|
|
100
|
+
|
|
101
|
+
## Temporary VS Code fallback (0.17.6)
|
|
102
|
+
|
|
103
|
+
The issue is tracked in [microsoft/vscode#335908](https://github.com/microsoft/vscode/issues/335908). Known affected versions now receive text and structured reports on new sessions, keeping tools usable without invoking the faulty webview path. Other hosts retain their negotiated presentation. See [UPSTREAM_ISSUES.md](../UPSTREAM_ISSUES.md) for scope and removal conditions. This workaround does not certify rich rendering on affected editors.
|
|
104
|
+
|
|
105
|
+
Native Windows VS Code 1.135.0 verification: after removing the in-memory editor patch, reconnecting alone left cached UI metadata and produced resource errors. Running **MCP: Reset Cached Tools**, reloading the window and opening a new chat cleared it. The final run returned all three reports as visible text, with three successful tool calls, no resource reads and no App frames. This is a verified text fallback, not rich rendering evidence.
|
package/package.json
CHANGED
|
@@ -72,7 +72,8 @@
|
|
|
72
72
|
"docs/mcp-apps.md",
|
|
73
73
|
"docs/third-party",
|
|
74
74
|
"canary",
|
|
75
|
-
"docs/host-canaries.md"
|
|
75
|
+
"docs/host-canaries.md",
|
|
76
|
+
"UPSTREAM_ISSUES.md"
|
|
76
77
|
],
|
|
77
78
|
"scripts": {
|
|
78
79
|
"build": "bun run build:apps && rm -rf dist && bun build src/index.ts src/manifest.ts src/commerce.ts src/apps.ts --outdir dist --root ./src --target=bun --external @absolutejs/agency --external '@absolutejs/agency/*' --external elysia && tsc --emitDeclarationOnly --project tsconfig.json && absolute-manifest emit",
|
|
@@ -84,8 +85,8 @@
|
|
|
84
85
|
"prepublishOnly": "bun run check:package",
|
|
85
86
|
"build:apps": "bun scripts/build-apps.ts",
|
|
86
87
|
"canary": "bun canary/server.ts",
|
|
87
|
-
"check:canary": "tsc --noEmit --strict --skipLibCheck --moduleResolution bundler --module esnext --target esnext --types bun canary/server.ts canary/authenticated.ts"
|
|
88
|
+
"check:canary": "tsc --noEmit --strict --skipLibCheck --moduleResolution bundler --module esnext --target esnext --types bun canary/server.ts canary/authenticated.ts canary/vscode/check-mount.ts"
|
|
88
89
|
},
|
|
89
90
|
"types": "./dist/src/index.d.ts",
|
|
90
|
-
"version": "0.17.
|
|
91
|
+
"version": "0.17.6"
|
|
91
92
|
}
|