codex-plugin-doctor 1.69.1 → 1.70.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/README.md +17 -4
- package/dist/core/mcp-discovery.d.ts +19 -0
- package/dist/core/mcp-discovery.js +24 -5
- package/dist/core/mcp-stdio-tool-catalog.d.ts +31 -0
- package/dist/core/mcp-stdio-tool-catalog.js +595 -0
- package/dist/core/mcp-tool-catalog.d.ts +36 -4
- package/dist/core/mcp-tool-catalog.js +90 -69
- package/dist/core/output-contract.js +20 -0
- package/dist/core/runtime-probe.d.ts +3 -1
- package/dist/core/runtime-probe.js +2 -2
- package/dist/reporting/render-mcp-tool-catalog-report.js +7 -0
- package/dist/run-cli.d.ts +2 -0
- package/dist/run-cli.js +109 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -127,7 +127,7 @@ compatibility.
|
|
|
127
127
|
See [Offline MCP Tool Diff](docs/guides/offline-tool-diff.md) for matching rules,
|
|
128
128
|
privacy, limits, and exit codes.
|
|
129
129
|
|
|
130
|
-
###
|
|
130
|
+
### MCP Tool Catalog
|
|
131
131
|
|
|
132
132
|
Inspect a modern HTTP MCP server's tool definitions without invoking them:
|
|
133
133
|
|
|
@@ -148,7 +148,20 @@ approved loopback endpoint; it is never printed or saved. The GitHub Action can
|
|
|
148
148
|
live list with `tools-capture-url` (and `tools-capture-token-env`), and skip the
|
|
149
149
|
plugin package check with `check: "false"` in repositories that only ship an MCP
|
|
150
150
|
server.
|
|
151
|
-
|
|
151
|
+
|
|
152
|
+
Local stdio servers from a package's MCP configuration can be cataloged too.
|
|
153
|
+
`--runtime` is required because this starts package code; the server is stopped
|
|
154
|
+
after its tool list is read:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
codex-plugin-doctor doctor tools ./my-plugin --server my-server --runtime --save-response ./tools-current.json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Doctor tries `server/discover` (`2026-07-28`) first and falls back to the
|
|
161
|
+
`initialize` handshake for older stdio servers, so the saved list works with
|
|
162
|
+
`tools-file` and `tools-diff` either way. `--sandbox docker` and
|
|
163
|
+
`--require-runtime-approval` work as they do for `check --runtime`.
|
|
164
|
+
See [MCP Tool Catalog](docs/guides/mcp-tool-catalog.md).
|
|
152
165
|
|
|
153
166
|
### Remote MCP Readiness
|
|
154
167
|
|
|
@@ -568,9 +581,9 @@ jobs:
|
|
|
568
581
|
runs-on: ubuntu-latest
|
|
569
582
|
steps:
|
|
570
583
|
- uses: actions/checkout@v5
|
|
571
|
-
- uses: Esquetta/CodexPluginDoctor@v1.
|
|
584
|
+
- uses: Esquetta/CodexPluginDoctor@v1.70.0
|
|
572
585
|
with:
|
|
573
|
-
version: "1.
|
|
586
|
+
version: "1.70.0"
|
|
574
587
|
path: .
|
|
575
588
|
runtime: "true"
|
|
576
589
|
policy: codex-publish
|
|
@@ -26,4 +26,23 @@ export interface McpDiscoveryReport {
|
|
|
26
26
|
export declare function findMcpSseResponse(body: Buffer, requestId?: string): McpJsonObject | null;
|
|
27
27
|
export declare function parseMcpResponse(response: BoundedHttpResponse, requestId?: string): McpJsonObject | null;
|
|
28
28
|
export declare function discoverMcpCapabilities(response: BoundedHttpResponse, requestId?: string): McpJsonObject | null;
|
|
29
|
+
export declare function serializeMcpDiscoverRequest(requestId?: string): string;
|
|
30
|
+
export type McpDiscoverOutcome = {
|
|
31
|
+
kind: "discovered";
|
|
32
|
+
supportedVersions: string[];
|
|
33
|
+
capabilities: McpJsonObject;
|
|
34
|
+
} | {
|
|
35
|
+
kind: "version-not-listed";
|
|
36
|
+
supportedVersions: string[];
|
|
37
|
+
} | {
|
|
38
|
+
kind: "version-unsupported";
|
|
39
|
+
supportedVersions: string[];
|
|
40
|
+
} | {
|
|
41
|
+
kind: "result-invalid";
|
|
42
|
+
} | {
|
|
43
|
+
kind: "envelope-invalid";
|
|
44
|
+
} | {
|
|
45
|
+
kind: "other-error";
|
|
46
|
+
};
|
|
47
|
+
export declare function classifyMcpDiscoverMessage(message: McpJsonObject, requestId?: string): McpDiscoverOutcome;
|
|
29
48
|
export declare function discoverMcpServer(rawUrl: string, options?: McpDiscoveryOptions): Promise<McpDiscoveryReport>;
|
|
@@ -102,6 +102,29 @@ export function discoverMcpCapabilities(response, requestId = REQUEST_ID) {
|
|
|
102
102
|
return null;
|
|
103
103
|
return result.supportedVersions.includes(REQUESTED_VERSION) ? capabilities : null;
|
|
104
104
|
}
|
|
105
|
+
export function serializeMcpDiscoverRequest(requestId = REQUEST_ID) {
|
|
106
|
+
return JSON.stringify({ jsonrpc: "2.0", id: requestId, method: "server/discover", params: { _meta: {
|
|
107
|
+
"io.modelcontextprotocol/protocolVersion": REQUESTED_VERSION,
|
|
108
|
+
"io.modelcontextprotocol/clientInfo": { name: "Codex Plugin Doctor", version: packageVersion },
|
|
109
|
+
"io.modelcontextprotocol/clientCapabilities": {}
|
|
110
|
+
} } });
|
|
111
|
+
}
|
|
112
|
+
// Classifies a transport-neutral server/discover reply. Only a valid result or a valid
|
|
113
|
+
// UnsupportedProtocolVersionError identifies a modern server; other errors do not.
|
|
114
|
+
export function classifyMcpDiscoverMessage(message, requestId = REQUEST_ID) {
|
|
115
|
+
if (!hasMatchingResponseEnvelope(message, requestId))
|
|
116
|
+
return { kind: "envelope-invalid" };
|
|
117
|
+
if (Object.hasOwn(message, "error")) {
|
|
118
|
+
const versions = unsupportedVersions(message.error);
|
|
119
|
+
return versions === null ? { kind: "other-error" } : { kind: "version-unsupported", supportedVersions: versions };
|
|
120
|
+
}
|
|
121
|
+
const result = message.result;
|
|
122
|
+
if (!validDiscoverResult(result))
|
|
123
|
+
return { kind: "result-invalid" };
|
|
124
|
+
return result.supportedVersions.includes(REQUESTED_VERSION)
|
|
125
|
+
? { kind: "discovered", supportedVersions: result.supportedVersions, capabilities: result.capabilities }
|
|
126
|
+
: { kind: "version-not-listed", supportedVersions: result.supportedVersions };
|
|
127
|
+
}
|
|
105
128
|
function failureReport(id, message) {
|
|
106
129
|
return report("failed", "fail", [finding(id, "fail", message, "The discovery response cannot safely establish modern MCP discovery support.", "Verify the endpoint returns a valid server/discover response for the requested protocol version.")]);
|
|
107
130
|
}
|
|
@@ -116,11 +139,7 @@ export async function discoverMcpServer(rawUrl, options = {}) {
|
|
|
116
139
|
return report("blocked", "skipped", [finding("plugin.discovery.url.invalid", "warn", "The MCP discovery endpoint is unsafe or unsupported.", "Unsafe endpoint URLs can bypass network controls or expose credentials.", "Use an absolute HTTP or HTTPS endpoint without credentials, query parameters, fragments, or IP literals.")]);
|
|
117
140
|
}
|
|
118
141
|
const request = options.request ?? requestBoundedHttp;
|
|
119
|
-
const body =
|
|
120
|
-
"io.modelcontextprotocol/protocolVersion": REQUESTED_VERSION,
|
|
121
|
-
"io.modelcontextprotocol/clientInfo": { name: "Codex Plugin Doctor", version: packageVersion },
|
|
122
|
-
"io.modelcontextprotocol/clientCapabilities": {}
|
|
123
|
-
} } });
|
|
142
|
+
const body = serializeMcpDiscoverRequest();
|
|
124
143
|
let response;
|
|
125
144
|
try {
|
|
126
145
|
response = await request(rawUrl, {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import type { RuntimeSandboxMode } from "../domain/types.js";
|
|
3
|
+
import { type McpToolCatalogSnapshot } from "./mcp-tool-catalog.js";
|
|
4
|
+
export interface McpStdioServer {
|
|
5
|
+
packageRoot: string;
|
|
6
|
+
command: string;
|
|
7
|
+
args: string[];
|
|
8
|
+
cwd: string;
|
|
9
|
+
source: "manifest" | "mcp-json";
|
|
10
|
+
}
|
|
11
|
+
export type McpStdioServerResolution = {
|
|
12
|
+
ok: true;
|
|
13
|
+
server: McpStdioServer;
|
|
14
|
+
} | {
|
|
15
|
+
ok: false;
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
export interface McpStdioToolCatalogOptions {
|
|
19
|
+
sandbox?: RuntimeSandboxMode;
|
|
20
|
+
now?: () => number;
|
|
21
|
+
spawnImpl?: typeof spawn;
|
|
22
|
+
removeContainerImpl?: (containerName: string) => Promise<boolean>;
|
|
23
|
+
timeouts?: {
|
|
24
|
+
startupMs?: number;
|
|
25
|
+
requestMs?: number;
|
|
26
|
+
totalMs?: number;
|
|
27
|
+
shutdownMs?: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function resolveMcpStdioServer(targetPath: string, serverName: string): Promise<McpStdioServerResolution>;
|
|
31
|
+
export declare function captureStdioMcpToolCatalog(server: McpStdioServer, options?: McpStdioToolCatalogOptions): Promise<McpToolCatalogSnapshot>;
|