mcp-ado-browser 1.2.1
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/LICENSE +21 -0
- package/README.md +247 -0
- package/dist/src/ado/archive.js +69 -0
- package/dist/src/ado/client.js +478 -0
- package/dist/src/ado/hosts.js +36 -0
- package/dist/src/ado/schemas.js +178 -0
- package/dist/src/ado/versions.js +52 -0
- package/dist/src/browser/auth-detect.js +47 -0
- package/dist/src/browser/session.js +201 -0
- package/dist/src/cache/sqlite-cache.js +66 -0
- package/dist/src/cache/types.js +1 -0
- package/dist/src/cli.js +50 -0
- package/dist/src/config.js +70 -0
- package/dist/src/errors.js +88 -0
- package/dist/src/index.js +73 -0
- package/dist/src/logger.js +22 -0
- package/dist/src/mock/mock-ado-server.js +108 -0
- package/dist/src/runtime.js +155 -0
- package/dist/src/scrub.js +38 -0
- package/dist/src/server.js +51 -0
- package/dist/src/tools/defs.js +128 -0
- package/dist/src/tools/errors.js +7 -0
- package/dist/src/transport/mock-transport.js +73 -0
- package/dist/src/transport/types.js +8 -0
- package/package.json +70 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockTransport: executes canonical ADO URLs against a local MockAdoServer.
|
|
3
|
+
*
|
|
4
|
+
* It records the *canonical* (real-host) URL in calledUrls — so cache and live
|
|
5
|
+
* gates assert on what the client intended to fetch — while physically routing
|
|
6
|
+
* the request to the mock base. The real host is forwarded in `x-ado-real-host`
|
|
7
|
+
* so the mock can key fixtures across the 5 ADO hosts on a single port.
|
|
8
|
+
*/
|
|
9
|
+
import { AuthRequiredError, HttpError, NotFoundError } from "../errors.js";
|
|
10
|
+
import { mandatoryHeaders } from "./types.js";
|
|
11
|
+
export class MockTransport {
|
|
12
|
+
mockBase;
|
|
13
|
+
kind = "mock";
|
|
14
|
+
calledUrls = [];
|
|
15
|
+
fetchCount = 0;
|
|
16
|
+
lastHeaders = {};
|
|
17
|
+
constructor(mockBase) {
|
|
18
|
+
this.mockBase = mockBase;
|
|
19
|
+
}
|
|
20
|
+
resetCounters() {
|
|
21
|
+
this.calledUrls.length = 0;
|
|
22
|
+
this.fetchCount = 0;
|
|
23
|
+
}
|
|
24
|
+
rewrite(url) {
|
|
25
|
+
const u = new URL(url);
|
|
26
|
+
const realHost = u.host;
|
|
27
|
+
const exec = `${this.mockBase}${u.pathname}${u.search}`;
|
|
28
|
+
return { exec, realHost };
|
|
29
|
+
}
|
|
30
|
+
async raw(url, init) {
|
|
31
|
+
this.calledUrls.push(url);
|
|
32
|
+
this.fetchCount++;
|
|
33
|
+
const { exec, realHost } = this.rewrite(url);
|
|
34
|
+
const res = await fetch(exec, {
|
|
35
|
+
method: init?.method ?? "GET",
|
|
36
|
+
headers: mandatoryHeaders({ ...(init?.headers ?? {}), "x-ado-real-host": realHost }),
|
|
37
|
+
body: init?.body,
|
|
38
|
+
});
|
|
39
|
+
if (res.status === 401)
|
|
40
|
+
throw new AuthRequiredError(url);
|
|
41
|
+
if (res.status === 404)
|
|
42
|
+
throw new NotFoundError("resource", url, url);
|
|
43
|
+
if (!res.ok) {
|
|
44
|
+
const body = await res.text().catch(() => "");
|
|
45
|
+
throw new HttpError(res.status, url, body);
|
|
46
|
+
}
|
|
47
|
+
return res;
|
|
48
|
+
}
|
|
49
|
+
async fetchJson(url, init) {
|
|
50
|
+
const res = await this.raw(url, init);
|
|
51
|
+
const data = (await res.json());
|
|
52
|
+
this.lastHeaders = headerObj(res.headers);
|
|
53
|
+
return { data, headers: this.lastHeaders };
|
|
54
|
+
}
|
|
55
|
+
async fetchBuffer(url, init) {
|
|
56
|
+
const res = await this.raw(url, init);
|
|
57
|
+
const ab = await res.arrayBuffer();
|
|
58
|
+
const data = Buffer.from(ab);
|
|
59
|
+
const cl = res.headers.get("content-length");
|
|
60
|
+
this.lastHeaders = headerObj(res.headers);
|
|
61
|
+
return {
|
|
62
|
+
data,
|
|
63
|
+
contentLength: cl != null ? Number(cl) : null,
|
|
64
|
+
contentType: res.headers.get("content-type"),
|
|
65
|
+
headers: this.lastHeaders,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function headerObj(h) {
|
|
70
|
+
const o = {};
|
|
71
|
+
h.forEach((v, k) => (o[k.toLowerCase()] = v));
|
|
72
|
+
return o;
|
|
73
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mcp-ado-browser",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "Read-only Azure DevOps for MCP clients using only your existing browser session — no PAT, no Azure CLI. Browse work items, pull requests, comments, attachments and Artifacts feeds across every project, repo and feed you can access.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-ado-browser": "dist/src/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/src/index.js",
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=22.5"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "VMargan (https://github.com/VMargan)",
|
|
15
|
+
"homepage": "https://github.com/VMargan/mcp-ado-browser#readme",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/VMargan/mcp-ado-browser.git"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/VMargan/mcp-ado-browser/issues"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"azure-devops",
|
|
27
|
+
"ado",
|
|
28
|
+
"playwright",
|
|
29
|
+
"browser-session",
|
|
30
|
+
"no-pat",
|
|
31
|
+
"work-items",
|
|
32
|
+
"pull-requests",
|
|
33
|
+
"artifacts"
|
|
34
|
+
],
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"dist/src",
|
|
40
|
+
"README.md",
|
|
41
|
+
"LICENSE"
|
|
42
|
+
],
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsc -p tsconfig.json",
|
|
45
|
+
"start": "node dist/src/index.js",
|
|
46
|
+
"verify": "tsc -p tsconfig.json && node dist/test/harness/verify.js",
|
|
47
|
+
"verify:live": "tsc -p tsconfig.json && ADO_LIVE=1 node dist/test/harness/verify.js",
|
|
48
|
+
"authenticate": "tsc -p tsconfig.json && node dist/src/index.js authenticate",
|
|
49
|
+
"demo:live": "tsc -p tsconfig.json && node scripts/demo-live.mjs",
|
|
50
|
+
"scan:secrets": "node scripts/scan-secrets.mjs",
|
|
51
|
+
"prepare": "npm run build",
|
|
52
|
+
"prepublishOnly": "npm run build"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
56
|
+
"playwright-core": "^1.49.0",
|
|
57
|
+
"zod": "^3.24.0"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
61
|
+
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
62
|
+
"@semantic-release/git": "^10.0.1",
|
|
63
|
+
"@semantic-release/github": "^12.0.8",
|
|
64
|
+
"@semantic-release/npm": "^13.1.5",
|
|
65
|
+
"@semantic-release/release-notes-generator": "^14.1.1",
|
|
66
|
+
"@types/node": "^22.10.0",
|
|
67
|
+
"semantic-release": "^25.0.5",
|
|
68
|
+
"typescript": "^5.7.0"
|
|
69
|
+
}
|
|
70
|
+
}
|