@unbrained/pm-cli 2026.8.25 → 2026.8.26
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/.claude-plugin/marketplace.json +2 -2
- package/CHANGELOG.md +33 -0
- package/dist/cli-bundle/bundle-manifest.json +133 -133
- package/dist/cli-bundle/chunks/chunk-E2GCFJSU.js +35 -0
- package/dist/cli-bundle/chunks/{chunk-QKGMHGEI.js → chunk-ES25LX3D.js} +51 -51
- package/dist/cli-bundle/chunks/{chunk-TPQIBSL2.js → chunk-FRDWWB6R.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-65MHLHAA.js → chunk-ICQ3RVIY.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-IV64RJVE.js +13 -0
- package/dist/cli-bundle/chunks/{chunk-QLUORNIB.js → chunk-KBFP3E4E.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-T2ENPRXF.js → chunk-MVYLQ67M.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-5I5RWIJC.js → chunk-S4U76VZF.js} +2 -2
- package/dist/cli-bundle/chunks/{chunk-OWHNAR2B.js → chunk-UKBCRPA2.js} +2 -2
- package/dist/cli-bundle/chunks/chunk-WRHJ3MB6.js +8 -0
- package/dist/cli-bundle/chunks/{chunk-244MI4GS.js → chunk-ZNRLJ54C.js} +60 -60
- package/dist/cli-bundle/chunks/{register-list-query-XVN2ZLI7.js → register-list-query-EMCPMICY.js} +2 -2
- package/dist/cli-bundle/chunks/{register-mutation-QCKAEGIJ.js → register-mutation-OJ67ABCB.js} +2 -2
- package/dist/cli-bundle/chunks/{register-operations-SDEAXE7E.js → register-operations-H2GLP7LT.js} +2 -2
- package/dist/cli-bundle/chunks/{register-setup-LXVBRCJ3.js → register-setup-GLZAHLVI.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-NOOZGIXP.js → chunk-4K2II4TV.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-IBZZZGK3.js → chunk-4XNH2HM7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-VXWATRFL.js → chunk-57XY346D.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-GQR3WH3F.js → chunk-66VGB23P.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-42S3GGZ7.js → chunk-72T6JGAE.js} +45 -45
- package/dist/cli-bundle/focused-chunks/{chunk-RAFKLNZX.js → chunk-FXDLT6FL.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-H5JZEIQV.js → chunk-IBHXMFE7.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-N7W67YIG.js → chunk-LV5N3LK5.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-2UIWOP3O.js → chunk-MMXUPDDJ.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-ONYQCALA.js → chunk-P2E6LDAE.js} +6 -6
- package/dist/cli-bundle/focused-chunks/{chunk-XUQPEKRN.js → chunk-TMJDFHVD.js} +2 -2
- package/dist/cli-bundle/focused-chunks/{chunk-OVJL6NZE.js → chunk-UYBA57GY.js} +4 -4
- package/dist/cli-bundle/main.js +2 -2
- package/dist/cli-bundle/sdk-authoring.js +1 -1
- package/dist/cli-bundle/sdk-contracts.js +1 -1
- package/dist/cli-bundle/sdk-core.js +31 -31
- package/dist/cli-bundle/sdk-governance.js +1 -1
- package/dist/cli-bundle/sdk-graph.js +1 -1
- package/dist/cli-bundle/sdk-merge.js +18 -18
- package/dist/cli-bundle/sdk-query.js +1 -1
- package/dist/cli-bundle/sdk-runtime.js +1 -1
- package/dist/cli-bundle/sdk-testing.js +1 -1
- package/dist/cli-bundle/sdk.js +7 -6
- package/dist/core/extensions/manifest-schema.d.ts +20 -0
- package/dist/core/extensions/manifest-schema.js +28 -10
- package/dist/mcp/http-server.d.ts +60 -0
- package/dist/mcp/http-server.js +451 -0
- package/dist/mcp/legacy-adapter.d.ts +50 -0
- package/dist/mcp/legacy-adapter.js +64 -0
- package/dist/mcp/server.d.ts +42 -9
- package/dist/mcp/server.js +457 -58
- package/dist/sdk/agent/refusal-closure-census.d.ts +6 -2
- package/dist/sdk/agent/refusal-closure-census.js +16 -8
- package/dist/sdk/compose.d.ts +4 -1
- package/dist/sdk/compose.js +23 -36
- package/dist/sdk/extension/author-manifest.d.ts +22 -0
- package/dist/sdk/extension/author-manifest.js +93 -0
- package/dist/sdk/extension.js +6 -3
- package/dist/sdk/generated/generated-error-code-catalog-part-1.js +20 -5
- package/dist/sdk/generated/generated-error-code-catalog-part-2.js +6 -3
- package/dist/sdk/governance/health.js +7 -2
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.js +8 -2
- package/dist/sdk/mcp/authorization.d.ts +134 -0
- package/dist/sdk/mcp/authorization.js +405 -0
- package/dist/sdk/mcp/interactions.d.ts +118 -0
- package/dist/sdk/mcp/interactions.js +337 -0
- package/dist/sdk/mcp/protocol.d.ts +142 -0
- package/dist/sdk/mcp/protocol.js +174 -0
- package/dist/sdk/mcp/subscriptions.d.ts +65 -0
- package/dist/sdk/mcp/subscriptions.js +212 -0
- package/dist/sdk/mcp/tasks.d.ts +107 -0
- package/dist/sdk/mcp/tasks.js +431 -0
- package/dist/sdk/mcp/transport.d.ts +30 -0
- package/dist/sdk/mcp/transport.js +261 -0
- package/dist/sdk/merge/receipts.d.ts +16 -0
- package/dist/sdk/merge/receipts.js +9 -8
- package/dist/sdk/runtime-primitives.d.ts +1 -1
- package/dist/sdk/runtime-primitives.js +3 -3
- package/dist/sdk/test/execution.d.ts +6 -0
- package/dist/sdk/test/execution.js +32 -3
- package/docs/AGENT_PROVENANCE_ADR.md +6 -4
- package/docs/AGENT_RUNTIME_PRIMITIVES.md +6 -5
- package/docs/CLAUDE_CODE_PLUGIN.md +12 -5
- package/docs/DIAGNOSTIC_OUTPUT_CONTRACTS.md +8 -0
- package/docs/EXTENSIONS.md +3 -4
- package/docs/MCP_2026_07_28.md +138 -0
- package/docs/MCP_2026_07_28_CONFORMANCE.md +30 -0
- package/docs/MCP_REMOTE_TRANSPORT_SECURITY.md +180 -0
- package/docs/README.md +4 -0
- package/docs/RELEASING.md +3 -2
- package/docs/SDK.md +22 -1
- package/docs/SDK_AGENT_SESSION_CONTEXT.md +18 -13
- package/docs/SDK_MCP_INTERACTIONS.md +227 -0
- package/docs/TESTING.md +4 -0
- package/docs/generated/REFUSAL_CLOSURE_CENSUS.md +9 -8
- package/marketplace.json +2 -2
- package/package.json +7 -5
- package/packages/pm-beads/package.json +1 -1
- package/packages/pm-calendar/package.json +1 -1
- package/packages/pm-command-kit/package.json +1 -1
- package/packages/pm-digital-twin/package.json +1 -1
- package/packages/pm-governance-audit/package.json +1 -1
- package/packages/pm-guide-shell/package.json +1 -1
- package/packages/pm-kanban/package.json +1 -1
- package/packages/pm-lifecycle-hooks/package.json +1 -1
- package/packages/pm-linked-test-adapters/package.json +1 -1
- package/packages/pm-search-advanced/package.json +1 -1
- package/packages/pm-templates/package.json +1 -1
- package/packages/pm-todos/package.json +1 -1
- package/packages/pm-vcs/package.json +1 -1
- package/plugins/pm-claude/.claude-plugin/plugin.json +1 -1
- package/plugins/pm-codex/.codex-plugin/plugin.json +1 -1
- package/scripts/finalize-build.mjs +1 -0
- package/sdk/public-surface.json +628 -2
- package/dist/cli-bundle/chunks/chunk-2F3LUFMW.js +0 -8
- package/dist/cli-bundle/chunks/chunk-6C7GIMIL.js +0 -13
- package/dist/cli-bundle/chunks/chunk-YQMYF3YD.js +0 -35
|
@@ -14,7 +14,27 @@ export interface ExtensionManifestSchemaInspection {
|
|
|
14
14
|
/** Whether neither canonical pm compatibility bound is present. */
|
|
15
15
|
missingVersionBounds: boolean;
|
|
16
16
|
}
|
|
17
|
+
/** Stable machine-readable schema finding emitted by author-time lint. */
|
|
18
|
+
export interface ExtensionManifestSchemaFinding {
|
|
19
|
+
/** Schema condition an author must review. */
|
|
20
|
+
code: "manifest_unknown_key" | "no_version_bounds_declared";
|
|
21
|
+
/** JSON-style manifest location responsible for the finding. */
|
|
22
|
+
path: string;
|
|
23
|
+
/** Canonical key to use when the mistaken or missing field has one. */
|
|
24
|
+
suggested_key?: "pm_min_version" | "pm_max_version";
|
|
25
|
+
/** Actionable explanation of the ignored or missing declaration. */
|
|
26
|
+
message: string;
|
|
27
|
+
}
|
|
28
|
+
/** Pure schema-lint result for one raw extension manifest. */
|
|
29
|
+
export interface ExtensionManifestSchemaLintResult {
|
|
30
|
+
/** False when at least one author-facing schema finding is present. */
|
|
31
|
+
ok: boolean;
|
|
32
|
+
/** Deterministically ordered schema findings. */
|
|
33
|
+
findings: ExtensionManifestSchemaFinding[];
|
|
34
|
+
}
|
|
17
35
|
/** Inspect raw manifest keys without parsing, loading, or mutating an extension. */
|
|
18
36
|
export declare function inspectExtensionManifestSchema(manifest: Readonly<Record<string, unknown>>): ExtensionManifestSchemaInspection;
|
|
37
|
+
/** Lint raw extension-manifest keys without loading code or evaluating versions. */
|
|
38
|
+
export declare function lintExtensionManifestSchema(manifest: Readonly<Record<string, unknown>>): ExtensionManifestSchemaLintResult;
|
|
19
39
|
/** Format runtime discovery warnings for unknown keys and absent canonical compatibility bounds. */
|
|
20
40
|
export declare function formatExtensionManifestSchemaWarnings(layer: ExtensionLayer, manifest: ExtensionManifest, rawManifest: Readonly<Record<string, unknown>>): string[];
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
/** Canonical top-level keys accepted by an extension `manifest.json`. */
|
|
8
8
|
|
|
9
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
9
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="49cea84c-1d1b-523b-8c98-7ac16c69ae82")}catch(e){}}();
|
|
10
10
|
export const KNOWN_EXTENSION_MANIFEST_KEYS = new Set([
|
|
11
11
|
"name",
|
|
12
12
|
"version",
|
|
@@ -37,16 +37,34 @@ export function inspectExtensionManifestSchema(manifest) {
|
|
|
37
37
|
manifest.pm_max_version === undefined,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
export function
|
|
42
|
-
const inspection = inspectExtensionManifestSchema(
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
:
|
|
40
|
+
/** Lint raw extension-manifest keys without loading code or evaluating versions. */
|
|
41
|
+
export function lintExtensionManifestSchema(manifest) {
|
|
42
|
+
const inspection = inspectExtensionManifestSchema(manifest);
|
|
43
|
+
const findings = inspection.unknownKeys.map((key) => ({
|
|
44
|
+
code: "manifest_unknown_key",
|
|
45
|
+
path: key,
|
|
46
|
+
...(key === "compatibility"
|
|
47
|
+
? { suggested_key: "pm_min_version" }
|
|
48
|
+
: {}),
|
|
49
|
+
message: key === "compatibility"
|
|
50
|
+
? 'Manifest key "compatibility" is ignored; declare the canonical pm_min_version and optional pm_max_version fields instead.'
|
|
51
|
+
: `Manifest key "${key}" is not part of the extension manifest contract and will be ignored.`,
|
|
52
|
+
}));
|
|
46
53
|
if (inspection.missingVersionBounds) {
|
|
47
|
-
|
|
54
|
+
findings.push({
|
|
55
|
+
code: "no_version_bounds_declared",
|
|
56
|
+
path: "$",
|
|
57
|
+
suggested_key: "pm_min_version",
|
|
58
|
+
message: "No canonical pm version bounds are declared; add pm_min_version so compatibility intent is explicit.",
|
|
59
|
+
});
|
|
48
60
|
}
|
|
49
|
-
return
|
|
61
|
+
return { ok: findings.length === 0, findings };
|
|
62
|
+
}
|
|
63
|
+
/** Format runtime discovery warnings for unknown keys and absent canonical compatibility bounds. */
|
|
64
|
+
export function formatExtensionManifestSchemaWarnings(layer, manifest, rawManifest) {
|
|
65
|
+
return lintExtensionManifestSchema(rawManifest).findings.map((finding) => finding.code === "manifest_unknown_key"
|
|
66
|
+
? `extension_manifest_unknown_key:${layer}:${manifest.name}:key=${finding.path}${finding.suggested_key ? `:suggested=${finding.suggested_key}` : ""}`
|
|
67
|
+
: `extension_manifest_no_version_bounds:${layer}:${manifest.name}:suggested=pm_min_version`);
|
|
50
68
|
}
|
|
51
69
|
//# sourceMappingURL=manifest-schema.js.map
|
|
52
|
-
//# debugId=
|
|
70
|
+
//# debugId=49cea84c-1d1b-523b-8c98-7ac16c69ae82
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @module mcp/http-server
|
|
4
|
+
*
|
|
5
|
+
* Serves the pm MCP dispatcher through the sessionless 2026-07-28 Streamable
|
|
6
|
+
* HTTP POST binding with request-scoped JSON/SSE responses.
|
|
7
|
+
*/
|
|
8
|
+
import { type IncomingMessage, type Server, type ServerResponse } from "node:http";
|
|
9
|
+
import { type PmMcpHttpAuthorizationPolicy, type PmMcpProtectedResourceMetadata } from "../sdk/mcp/authorization.js";
|
|
10
|
+
import { type JsonRpcRequest } from "./server.js";
|
|
11
|
+
/** Runtime options for one real Streamable HTTP server. */
|
|
12
|
+
export interface PmMcpHttpServerOptions {
|
|
13
|
+
/** Local bind host. Defaults to the DNS-rebinding-safe loopback address. */
|
|
14
|
+
host?: string;
|
|
15
|
+
/** TCP port. Zero requests an ephemeral port. */
|
|
16
|
+
port?: number;
|
|
17
|
+
/** Single modern MCP POST endpoint. */
|
|
18
|
+
endpointPath?: string;
|
|
19
|
+
/** Exact browser origins permitted when an Origin header is present. */
|
|
20
|
+
allowedOrigins?: readonly string[];
|
|
21
|
+
/** Optional RFC 9728/OAuth resource-server policy. */
|
|
22
|
+
authorization?: PmMcpHttpAuthorizationPolicy;
|
|
23
|
+
/** Metadata served at RFC 9728 well-known paths. */
|
|
24
|
+
protectedResourceMetadata?: PmMcpProtectedResourceMetadata;
|
|
25
|
+
/** Maximum accepted JSON request bytes. */
|
|
26
|
+
maximumBodyBytes?: number;
|
|
27
|
+
/** SSE comment interval; zero disables keep-alives. */
|
|
28
|
+
keepAliveMs?: number;
|
|
29
|
+
/** Optional embedding dispatcher; defaults to the canonical pm MCP handler. */
|
|
30
|
+
requestHandler?: (request: JsonRpcRequest) => unknown | Promise<unknown>;
|
|
31
|
+
}
|
|
32
|
+
/** Write one SSE event and wait for transport backpressure to clear. */
|
|
33
|
+
export declare function writePmMcpSseEvent(response: ServerResponse, payload: unknown): Promise<void>;
|
|
34
|
+
declare function writeMcpHttpDispatchError(response: ServerResponse, rpcRequest: JsonRpcRequest | undefined, error: unknown): void;
|
|
35
|
+
declare function destroyFailedMcpHttpResponse(response: ServerResponse, error: unknown): void;
|
|
36
|
+
/** Create a configured HTTP server without binding it. */
|
|
37
|
+
export declare function createPmMcpHttpServer(options?: PmMcpHttpServerOptions): Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
38
|
+
/** Gracefully close subscriptions before stopping a Streamable HTTP server. */
|
|
39
|
+
export declare function closePmMcpHttpServer(server: Server): Promise<void>;
|
|
40
|
+
/** Bind the configured Streamable HTTP server and return its actual address. */
|
|
41
|
+
export declare function startPmMcpHttpServer(options?: PmMcpHttpServerOptions): Promise<ReturnType<typeof createPmMcpHttpServer>>;
|
|
42
|
+
/** Resolve loopback-safe bind defaults without opening a socket. */
|
|
43
|
+
export declare function resolvePmMcpHttpListenAddress(options?: Pick<PmMcpHttpServerOptions, "host" | "port">): {
|
|
44
|
+
host: string;
|
|
45
|
+
port: number;
|
|
46
|
+
};
|
|
47
|
+
/** Resolve and harden executable HTTP options from an environment map. */
|
|
48
|
+
export declare function resolvePmMcpHttpServerOptionsFromEnvironment(processEnvironment?: NodeJS.ProcessEnv): PmMcpHttpServerOptions;
|
|
49
|
+
/** Internal HTTP transport seams used only by direct integration tests. */
|
|
50
|
+
export declare const _testOnly: {
|
|
51
|
+
/** Return the first active request-scoped transport key for one server. */
|
|
52
|
+
activeSubscriptionKey: (server: Server) => symbol | undefined;
|
|
53
|
+
/** Open a stream against one server runtime without a network socket. */
|
|
54
|
+
openHttpSubscription: (server: Server, request: JsonRpcRequest, response: ServerResponse) => Promise<void>;
|
|
55
|
+
/** Exercise committed-response handling without a network race. */
|
|
56
|
+
writeMcpHttpDispatchError: typeof writeMcpHttpDispatchError;
|
|
57
|
+
/** Exercise the terminal request rejection handler without a live socket. */
|
|
58
|
+
destroyFailedMcpHttpResponse: typeof destroyFailedMcpHttpResponse;
|
|
59
|
+
};
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @module mcp/http-server
|
|
4
|
+
*
|
|
5
|
+
* Serves the pm MCP dispatcher through the sessionless 2026-07-28 Streamable
|
|
6
|
+
* HTTP POST binding with request-scoped JSON/SSE responses.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="7c5d2b34-e2ab-52a9-80a9-c5a28ce09dc7")}catch(e){}}();
|
|
10
|
+
import { createServer, } from "node:http";
|
|
11
|
+
import { PM_MCP_ERROR_CODES, PmMcpProtocolError } from "../sdk/mcp/protocol.js";
|
|
12
|
+
import { PmMcpAuthorizationError, authorizeMcpHttpRequest, buildMcpProtectedResourceMetadata, createMcpStaticBearerVerifier, } from "../sdk/mcp/authorization.js";
|
|
13
|
+
import { validateMcpHttpRequestHeaders } from "../sdk/mcp/transport.js";
|
|
14
|
+
import { buildMcpToolCallErrorResult, closeMcpSubscription, handleRequest, isInvokedAsMcpMainModule, openMcpSubscription, resolveMcpToolSchemaForRequest, } from "./server.js";
|
|
15
|
+
const DEFAULT_MAXIMUM_BODY_BYTES = 1024 * 1024;
|
|
16
|
+
const DEFAULT_KEEP_ALIVE_MS = 15_000;
|
|
17
|
+
const PM_MCP_HTTP_RUNTIMES = new WeakMap();
|
|
18
|
+
/** Write one SSE event and wait for transport backpressure to clear. */
|
|
19
|
+
export function writePmMcpSseEvent(response, payload) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
let settled = false;
|
|
22
|
+
const cleanup = () => {
|
|
23
|
+
response.off("drain", onDrain);
|
|
24
|
+
response.off("close", onClose);
|
|
25
|
+
};
|
|
26
|
+
const onDrain = () => {
|
|
27
|
+
settled = true;
|
|
28
|
+
cleanup();
|
|
29
|
+
resolve();
|
|
30
|
+
};
|
|
31
|
+
const onClose = () => {
|
|
32
|
+
settled = true;
|
|
33
|
+
cleanup();
|
|
34
|
+
reject(new Error("MCP response stream closed during backpressure"));
|
|
35
|
+
};
|
|
36
|
+
response.once("drain", onDrain);
|
|
37
|
+
response.once("close", onClose);
|
|
38
|
+
try {
|
|
39
|
+
if (response.write(`data: ${JSON.stringify(payload)}\n\n`) && !settled) {
|
|
40
|
+
settled = true;
|
|
41
|
+
cleanup();
|
|
42
|
+
resolve();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
cleanup();
|
|
47
|
+
reject(error);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function writeJson(response, status, payload, headers = {}) {
|
|
52
|
+
response.writeHead(status, {
|
|
53
|
+
"Cache-Control": "no-store",
|
|
54
|
+
"Content-Type": "application/json",
|
|
55
|
+
...headers,
|
|
56
|
+
});
|
|
57
|
+
response.end(`${JSON.stringify(payload)}\n`);
|
|
58
|
+
}
|
|
59
|
+
function writeJsonRpcError(response, id, error) {
|
|
60
|
+
const authorizationError = error instanceof PmMcpAuthorizationError ? error : undefined;
|
|
61
|
+
const protocolError = error instanceof PmMcpProtocolError ? error : undefined;
|
|
62
|
+
const code = protocolError?.code ?? -32603;
|
|
63
|
+
const clientErrorCodes = new Set([
|
|
64
|
+
-32700,
|
|
65
|
+
-32600,
|
|
66
|
+
PM_MCP_ERROR_CODES.headerMismatch,
|
|
67
|
+
PM_MCP_ERROR_CODES.unsupportedProtocolVersion,
|
|
68
|
+
PM_MCP_ERROR_CODES.invalidParams,
|
|
69
|
+
]);
|
|
70
|
+
const status = authorizationError?.httpStatus ??
|
|
71
|
+
(code === -32601 ? 404 : clientErrorCodes.has(code) ? 400 : 500);
|
|
72
|
+
writeJson(response, status, {
|
|
73
|
+
jsonrpc: "2.0",
|
|
74
|
+
id: id ?? null,
|
|
75
|
+
error: {
|
|
76
|
+
code,
|
|
77
|
+
message: error instanceof Error ? error.message : "Internal MCP error",
|
|
78
|
+
...(protocolError ? { data: protocolError.data } : {}),
|
|
79
|
+
},
|
|
80
|
+
}, authorizationError?.challenge
|
|
81
|
+
? { "WWW-Authenticate": authorizationError.challenge }
|
|
82
|
+
: {});
|
|
83
|
+
}
|
|
84
|
+
function finishCommittedMcpResponse(response) {
|
|
85
|
+
if (!response.headersSent && !response.writableEnded && !response.destroyed) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (!response.writableEnded && !response.destroyed)
|
|
89
|
+
response.end();
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
function writeMcpHttpDispatchError(response, rpcRequest, error) {
|
|
93
|
+
if (finishCommittedMcpResponse(response))
|
|
94
|
+
return;
|
|
95
|
+
const toolError = rpcRequest
|
|
96
|
+
? buildMcpToolCallErrorResult(rpcRequest, error)
|
|
97
|
+
: undefined;
|
|
98
|
+
if (toolError && rpcRequest) {
|
|
99
|
+
writeJson(response, 200, {
|
|
100
|
+
jsonrpc: "2.0",
|
|
101
|
+
id: rpcRequest.id ?? null,
|
|
102
|
+
result: toolError,
|
|
103
|
+
});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
writeJsonRpcError(response, rpcRequest?.id, error);
|
|
107
|
+
}
|
|
108
|
+
function destroyFailedMcpHttpResponse(response, error) {
|
|
109
|
+
response.destroy(new Error("Unhandled MCP HTTP request failure", { cause: error }));
|
|
110
|
+
}
|
|
111
|
+
async function readRequestBody(request, maximumBytes) {
|
|
112
|
+
const chunks = [];
|
|
113
|
+
let observed = 0;
|
|
114
|
+
for await (const chunk of request) {
|
|
115
|
+
const buffer = Buffer.from(chunk);
|
|
116
|
+
observed += buffer.length;
|
|
117
|
+
if (observed > maximumBytes) {
|
|
118
|
+
throw new PmMcpProtocolError("MCP HTTP request body exceeds the configured limit", PM_MCP_ERROR_CODES.invalidParams, { maximumBytes });
|
|
119
|
+
}
|
|
120
|
+
chunks.push(buffer);
|
|
121
|
+
}
|
|
122
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
123
|
+
}
|
|
124
|
+
function parseJsonRpcRequest(source) {
|
|
125
|
+
let value;
|
|
126
|
+
try {
|
|
127
|
+
value = JSON.parse(source);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
throw new PmMcpProtocolError("Invalid JSON-RPC request body", -32700);
|
|
131
|
+
}
|
|
132
|
+
if (typeof value !== "object" ||
|
|
133
|
+
value === null ||
|
|
134
|
+
Array.isArray(value) ||
|
|
135
|
+
("jsonrpc" in value && value.jsonrpc !== "2.0") ||
|
|
136
|
+
("method" in value && typeof value.method !== "string")) {
|
|
137
|
+
throw new PmMcpProtocolError("Invalid JSON-RPC request", -32600);
|
|
138
|
+
}
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
function protectedResourcePaths(endpointPath) {
|
|
142
|
+
return new Set([
|
|
143
|
+
"/.well-known/oauth-protected-resource",
|
|
144
|
+
`/.well-known/oauth-protected-resource${endpointPath}`,
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
function routeMcpHttpBoundary(request, response, requestUrl, runtime) {
|
|
148
|
+
if (runtime.options.protectedResourceMetadata &&
|
|
149
|
+
request.method === "GET" &&
|
|
150
|
+
protectedResourcePaths(runtime.endpointPath).has(requestUrl.pathname)) {
|
|
151
|
+
writeJson(response, 200, { ...runtime.options.protectedResourceMetadata });
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
if (requestUrl.pathname !== runtime.endpointPath) {
|
|
155
|
+
writeJson(response, 404, { error: "Not found" });
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
if (request.method !== "POST") {
|
|
159
|
+
response.setHeader("Allow", "POST");
|
|
160
|
+
writeJson(response, 405, { error: "Method not allowed" });
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
const origin = request.headers.origin;
|
|
164
|
+
if (origin !== undefined && !runtime.allowedOrigins.has(origin)) {
|
|
165
|
+
writeJson(response, 403, { error: "Origin is not allowed" });
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
const accept = request.headers.accept;
|
|
169
|
+
const contentType = request.headers["content-type"];
|
|
170
|
+
if (typeof accept !== "string" ||
|
|
171
|
+
!accept.includes("application/json") ||
|
|
172
|
+
!accept.includes("text/event-stream") ||
|
|
173
|
+
typeof contentType !== "string" ||
|
|
174
|
+
!contentType.startsWith("application/json")) {
|
|
175
|
+
writeJson(response, 406, {
|
|
176
|
+
error: "MCP requires JSON and SSE content negotiation",
|
|
177
|
+
});
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
async function openHttpSubscription(input) {
|
|
183
|
+
if (typeof input.request.id !== "string" &&
|
|
184
|
+
typeof input.request.id !== "number") {
|
|
185
|
+
throw new PmMcpProtocolError("subscriptions/listen requires a JSON-RPC id", PM_MCP_ERROR_CODES.invalidParams);
|
|
186
|
+
}
|
|
187
|
+
const subscriptionId = input.request.id;
|
|
188
|
+
const subscriptionKey = Symbol("pm-mcp-http-subscription");
|
|
189
|
+
const active = {
|
|
190
|
+
id: subscriptionId,
|
|
191
|
+
key: subscriptionKey,
|
|
192
|
+
response: input.response,
|
|
193
|
+
};
|
|
194
|
+
let disconnected = input.response.destroyed || input.response.writableEnded;
|
|
195
|
+
let subscriptionOpened = false;
|
|
196
|
+
const closeActiveSubscription = () => {
|
|
197
|
+
disconnected = true;
|
|
198
|
+
if (active.timer)
|
|
199
|
+
clearInterval(active.timer);
|
|
200
|
+
input.runtime.activeSubscriptions.delete(subscriptionKey);
|
|
201
|
+
if (subscriptionOpened) {
|
|
202
|
+
closeMcpSubscription(subscriptionId, subscriptionKey);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
input.response.once("close", closeActiveSubscription);
|
|
206
|
+
let responseStarted = false;
|
|
207
|
+
try {
|
|
208
|
+
await openMcpSubscription({
|
|
209
|
+
request: input.request,
|
|
210
|
+
key: subscriptionKey,
|
|
211
|
+
sink: (notification) => {
|
|
212
|
+
if (input.response.destroyed || input.response.writableEnded) {
|
|
213
|
+
throw new Error("MCP response stream closed before acknowledgment");
|
|
214
|
+
}
|
|
215
|
+
if (!responseStarted) {
|
|
216
|
+
responseStarted = true;
|
|
217
|
+
input.response.setHeader("Cache-Control", "no-cache, no-store");
|
|
218
|
+
input.response.setHeader("Connection", "keep-alive");
|
|
219
|
+
input.response.setHeader("Content-Type", "text/event-stream");
|
|
220
|
+
input.response.setHeader("X-Accel-Buffering", "no");
|
|
221
|
+
}
|
|
222
|
+
return writePmMcpSseEvent(input.response, notification);
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
subscriptionOpened = true;
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
input.response.off("close", closeActiveSubscription);
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
if (disconnected ||
|
|
232
|
+
input.response.destroyed ||
|
|
233
|
+
input.response.writableEnded) {
|
|
234
|
+
closeActiveSubscription();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (input.runtime.keepAliveMs > 0) {
|
|
238
|
+
active.timer = setInterval(() => input.response.write(":\r\n\r\n"), input.runtime.keepAliveMs);
|
|
239
|
+
active.timer.unref();
|
|
240
|
+
}
|
|
241
|
+
input.runtime.activeSubscriptions.set(subscriptionKey, active);
|
|
242
|
+
}
|
|
243
|
+
async function dispatchMcpHttpRpc(input) {
|
|
244
|
+
let rpcRequest;
|
|
245
|
+
try {
|
|
246
|
+
if (input.runtime.options.authorization) {
|
|
247
|
+
await authorizeMcpHttpRequest({
|
|
248
|
+
headers: input.request.headers,
|
|
249
|
+
requestUrl: input.requestUrl.href,
|
|
250
|
+
policy: input.runtime.options.authorization,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
rpcRequest = parseJsonRpcRequest(await readRequestBody(input.request, input.runtime.maximumBodyBytes));
|
|
254
|
+
validateMcpHttpRequestHeaders({
|
|
255
|
+
headers: input.request.headers,
|
|
256
|
+
request: rpcRequest,
|
|
257
|
+
toolSchema: await resolveMcpToolSchemaForRequest(rpcRequest),
|
|
258
|
+
});
|
|
259
|
+
if (rpcRequest.method === "subscriptions/listen") {
|
|
260
|
+
await openHttpSubscription({
|
|
261
|
+
request: rpcRequest,
|
|
262
|
+
response: input.response,
|
|
263
|
+
runtime: input.runtime,
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const result = await (input.runtime.options.requestHandler ?? handleRequest)(rpcRequest);
|
|
268
|
+
if (!Object.prototype.hasOwnProperty.call(rpcRequest, "id")) {
|
|
269
|
+
input.response.writeHead(202);
|
|
270
|
+
input.response.end();
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
writeJson(input.response, 200, {
|
|
274
|
+
jsonrpc: "2.0",
|
|
275
|
+
id: rpcRequest.id ?? null,
|
|
276
|
+
result: result ?? {},
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
writeMcpHttpDispatchError(input.response, rpcRequest, error);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function handleMcpHttpRequest(request, response, runtime) {
|
|
284
|
+
const requestUrl = new URL(request.url, "http://localhost");
|
|
285
|
+
if (routeMcpHttpBoundary(request, response, requestUrl, runtime))
|
|
286
|
+
return;
|
|
287
|
+
await dispatchMcpHttpRpc({ request, response, requestUrl, runtime });
|
|
288
|
+
}
|
|
289
|
+
/** Create a configured HTTP server without binding it. */
|
|
290
|
+
export function createPmMcpHttpServer(options = {}) {
|
|
291
|
+
const runtime = {
|
|
292
|
+
activeSubscriptions: new Map(),
|
|
293
|
+
allowedOrigins: new Set(options.allowedOrigins ?? []),
|
|
294
|
+
endpointPath: options.endpointPath ?? "/mcp",
|
|
295
|
+
keepAliveMs: options.keepAliveMs ?? DEFAULT_KEEP_ALIVE_MS,
|
|
296
|
+
maximumBodyBytes: options.maximumBodyBytes ?? DEFAULT_MAXIMUM_BODY_BYTES,
|
|
297
|
+
options,
|
|
298
|
+
};
|
|
299
|
+
const server = createServer((request, response) => {
|
|
300
|
+
void handleMcpHttpRequest(request, response, runtime).catch(destroyFailedMcpHttpResponse.bind(undefined, response));
|
|
301
|
+
});
|
|
302
|
+
PM_MCP_HTTP_RUNTIMES.set(server, runtime);
|
|
303
|
+
server.on("close", () => {
|
|
304
|
+
closeActiveHttpSubscriptions(runtime);
|
|
305
|
+
PM_MCP_HTTP_RUNTIMES.delete(server);
|
|
306
|
+
});
|
|
307
|
+
return server;
|
|
308
|
+
}
|
|
309
|
+
function closeActiveHttpSubscriptions(runtime) {
|
|
310
|
+
for (const [key, active] of runtime.activeSubscriptions) {
|
|
311
|
+
if (active.timer)
|
|
312
|
+
clearInterval(active.timer);
|
|
313
|
+
const result = closeMcpSubscription(active.id, key);
|
|
314
|
+
if (result) {
|
|
315
|
+
active.response.write(`data: ${JSON.stringify({ jsonrpc: "2.0", id: active.id, result })}\n\n`);
|
|
316
|
+
}
|
|
317
|
+
active.response.end();
|
|
318
|
+
}
|
|
319
|
+
runtime.activeSubscriptions.clear();
|
|
320
|
+
}
|
|
321
|
+
/** Gracefully close subscriptions before stopping a Streamable HTTP server. */
|
|
322
|
+
export async function closePmMcpHttpServer(server) {
|
|
323
|
+
const runtime = PM_MCP_HTTP_RUNTIMES.get(server);
|
|
324
|
+
if (runtime)
|
|
325
|
+
closeActiveHttpSubscriptions(runtime);
|
|
326
|
+
await new Promise((resolve, reject) => {
|
|
327
|
+
server.close((error) => (error ? reject(error) : resolve()));
|
|
328
|
+
});
|
|
329
|
+
server.closeAllConnections();
|
|
330
|
+
}
|
|
331
|
+
/** Bind the configured Streamable HTTP server and return its actual address. */
|
|
332
|
+
export async function startPmMcpHttpServer(options = {}) {
|
|
333
|
+
const server = createPmMcpHttpServer(options);
|
|
334
|
+
const address = resolvePmMcpHttpListenAddress(options);
|
|
335
|
+
await new Promise((resolve, reject) => {
|
|
336
|
+
server.once("error", reject);
|
|
337
|
+
server.listen(address.port, address.host, () => {
|
|
338
|
+
server.off("error", reject);
|
|
339
|
+
resolve();
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
return server;
|
|
343
|
+
}
|
|
344
|
+
/** Resolve loopback-safe bind defaults without opening a socket. */
|
|
345
|
+
export function resolvePmMcpHttpListenAddress(options = {}) {
|
|
346
|
+
return {
|
|
347
|
+
host: options.host ?? "127.0.0.1",
|
|
348
|
+
port: options.port ?? 3000,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function readMcpHttpEnvironment(environment) {
|
|
352
|
+
const configuredPort = environment.PM_MCP_HTTP_PORT?.trim();
|
|
353
|
+
const port = Number(configuredPort || "3000");
|
|
354
|
+
if (!Number.isInteger(port) || port < 0 || port > 65_535) {
|
|
355
|
+
throw new Error("PM_MCP_HTTP_PORT must be an integer from 0 through 65535");
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
host: environment.PM_MCP_HTTP_HOST?.trim() || "127.0.0.1",
|
|
359
|
+
port,
|
|
360
|
+
allowedOrigins: (environment.PM_MCP_HTTP_ALLOWED_ORIGINS ?? "")
|
|
361
|
+
.split(",")
|
|
362
|
+
.map((entry) => entry.trim())
|
|
363
|
+
.filter(Boolean),
|
|
364
|
+
token: environment.PM_MCP_HTTP_BEARER_TOKEN?.trim() || undefined,
|
|
365
|
+
issuer: environment.PM_MCP_HTTP_AUTH_ISSUER?.trim() || undefined,
|
|
366
|
+
resource: environment.PM_MCP_HTTP_RESOURCE?.trim() || undefined,
|
|
367
|
+
scopes: (environment.PM_MCP_HTTP_SCOPES ?? "pm:read pm:write")
|
|
368
|
+
.split(/\s+/u)
|
|
369
|
+
.filter(Boolean),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function buildMcpHttpAuthorizationOptions(environment) {
|
|
373
|
+
const metadata = buildMcpProtectedResourceMetadata({
|
|
374
|
+
resource: environment.resource,
|
|
375
|
+
authorizationServers: [environment.issuer],
|
|
376
|
+
scopes: environment.scopes,
|
|
377
|
+
});
|
|
378
|
+
const acceptedIssuer = environment.issuer;
|
|
379
|
+
return {
|
|
380
|
+
protectedResourceMetadata: metadata,
|
|
381
|
+
authorization: {
|
|
382
|
+
issuer: acceptedIssuer,
|
|
383
|
+
resource: metadata.resource,
|
|
384
|
+
requiredScopes: environment.scopes,
|
|
385
|
+
resourceMetadataUrl: new URL("/.well-known/oauth-protected-resource", metadata.resource).href,
|
|
386
|
+
verifyAccessToken: createMcpStaticBearerVerifier({
|
|
387
|
+
token: environment.token,
|
|
388
|
+
claims: {
|
|
389
|
+
principal: "configured-remote-client",
|
|
390
|
+
issuer: acceptedIssuer,
|
|
391
|
+
audience: metadata.resource,
|
|
392
|
+
scopes: environment.scopes,
|
|
393
|
+
},
|
|
394
|
+
}),
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function hasCompleteMcpHttpAuthorization(environment) {
|
|
399
|
+
return Boolean(environment.token && environment.issuer && environment.resource);
|
|
400
|
+
}
|
|
401
|
+
/** Resolve and harden executable HTTP options from an environment map. */
|
|
402
|
+
export function resolvePmMcpHttpServerOptionsFromEnvironment(processEnvironment = process.env) {
|
|
403
|
+
const environment = readMcpHttpEnvironment(processEnvironment);
|
|
404
|
+
const hasAuthorization = hasCompleteMcpHttpAuthorization(environment);
|
|
405
|
+
if (environment.host !== "127.0.0.1" &&
|
|
406
|
+
environment.host !== "localhost" &&
|
|
407
|
+
!hasAuthorization) {
|
|
408
|
+
throw new Error("Non-loopback MCP HTTP requires PM_MCP_HTTP_BEARER_TOKEN, PM_MCP_HTTP_AUTH_ISSUER, and PM_MCP_HTTP_RESOURCE");
|
|
409
|
+
}
|
|
410
|
+
const base = {
|
|
411
|
+
host: environment.host,
|
|
412
|
+
port: environment.port,
|
|
413
|
+
allowedOrigins: environment.allowedOrigins,
|
|
414
|
+
};
|
|
415
|
+
return hasAuthorization
|
|
416
|
+
? {
|
|
417
|
+
...base,
|
|
418
|
+
...buildMcpHttpAuthorizationOptions({
|
|
419
|
+
issuer: environment.issuer,
|
|
420
|
+
resource: environment.resource,
|
|
421
|
+
token: environment.token,
|
|
422
|
+
scopes: environment.scopes,
|
|
423
|
+
}),
|
|
424
|
+
}
|
|
425
|
+
: base;
|
|
426
|
+
}
|
|
427
|
+
/** Internal HTTP transport seams used only by direct integration tests. */
|
|
428
|
+
export const _testOnly = {
|
|
429
|
+
/** Return the first active request-scoped transport key for one server. */
|
|
430
|
+
activeSubscriptionKey: (server) => PM_MCP_HTTP_RUNTIMES.get(server).activeSubscriptions.keys().next().value,
|
|
431
|
+
/** Open a stream against one server runtime without a network socket. */
|
|
432
|
+
openHttpSubscription: (server, request, response) => openHttpSubscription({
|
|
433
|
+
request,
|
|
434
|
+
response,
|
|
435
|
+
runtime: PM_MCP_HTTP_RUNTIMES.get(server),
|
|
436
|
+
}),
|
|
437
|
+
/** Exercise committed-response handling without a network race. */
|
|
438
|
+
writeMcpHttpDispatchError,
|
|
439
|
+
/** Exercise the terminal request rejection handler without a live socket. */
|
|
440
|
+
destroyFailedMcpHttpResponse,
|
|
441
|
+
};
|
|
442
|
+
/* c8 ignore start -- executable guard and process lifecycle are integration-tested */
|
|
443
|
+
if (isInvokedAsMcpMainModule(process.argv[1], import.meta.url)) {
|
|
444
|
+
startPmMcpHttpServer(resolvePmMcpHttpServerOptionsFromEnvironment()).catch((error) => {
|
|
445
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
446
|
+
process.exit(1);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
/* c8 ignore stop */
|
|
450
|
+
//# sourceMappingURL=http-server.js.map
|
|
451
|
+
//# debugId=7c5d2b34-e2ab-52a9-80a9-c5a28ce09dc7
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module mcp/legacy-adapter
|
|
3
|
+
*
|
|
4
|
+
* Isolates the bounded MCP 2025-06-18 stdio compatibility surface from the
|
|
5
|
+
* canonical stateless dispatcher. No modern transport imports policy from it.
|
|
6
|
+
*/
|
|
7
|
+
import { type AgentClientInfo } from "../sdk/runtime-primitives.js";
|
|
8
|
+
import { type PmMcpImplementation, type PmMcpServerCapabilities } from "../sdk/mcp/protocol.js";
|
|
9
|
+
/** Minimal request shape consumed by the compatibility adapter. */
|
|
10
|
+
export interface LegacyMcpRequest {
|
|
11
|
+
/** Legacy method name. */
|
|
12
|
+
method?: string;
|
|
13
|
+
/** Legacy method parameters. */
|
|
14
|
+
params?: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
/** Host callbacks keep this adapter separate from canonical SDK policy. */
|
|
17
|
+
export interface LegacyMcpAdapterOptions {
|
|
18
|
+
/** Immutable server identity. */
|
|
19
|
+
serverInfo: PmMcpImplementation;
|
|
20
|
+
/** Compatibility-era capabilities. */
|
|
21
|
+
capabilities: PmMcpServerCapabilities;
|
|
22
|
+
/** Model-facing usage instructions. */
|
|
23
|
+
instructions: string;
|
|
24
|
+
/** Parse bounded historical client identity. */
|
|
25
|
+
parseClientInfo: (value: unknown) => AgentClientInfo | undefined;
|
|
26
|
+
/** Resolve legacy tool definitions. */
|
|
27
|
+
listTools: (params: Record<string, unknown> | undefined) => Promise<Record<string, unknown>>;
|
|
28
|
+
/** Execute one legacy tool call. */
|
|
29
|
+
callTool: (params: Record<string, unknown> | undefined, clientInfo: AgentClientInfo | undefined) => Promise<Record<string, unknown>>;
|
|
30
|
+
/** List legacy resources. */
|
|
31
|
+
listResources: () => Record<string, unknown>;
|
|
32
|
+
/** Read one legacy resource. */
|
|
33
|
+
readResource: (params: Record<string, unknown> | undefined) => Promise<Record<string, unknown>>;
|
|
34
|
+
/** List legacy prompts. */
|
|
35
|
+
listPrompts: () => Record<string, unknown>;
|
|
36
|
+
/** Render one legacy prompt. */
|
|
37
|
+
getPrompt: (params: Record<string, unknown> | undefined) => Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/** Explicit compatibility adapter with no cross-process or modern-session role. */
|
|
40
|
+
export declare class LegacyMcpAdapter {
|
|
41
|
+
#private;
|
|
42
|
+
/** Create one process-local legacy stdio adapter. */
|
|
43
|
+
constructor(options: LegacyMcpAdapterOptions);
|
|
44
|
+
/** Read the bounded identity most recently supplied to legacy initialize. */
|
|
45
|
+
getClientInfo(): AgentClientInfo | undefined;
|
|
46
|
+
/** Validate and answer the historical initialize handshake. */
|
|
47
|
+
initialize(params: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
48
|
+
/** Dispatch one unversioned request through the finite legacy method set. */
|
|
49
|
+
dispatch(request: LegacyMcpRequest): Promise<Record<string, unknown>>;
|
|
50
|
+
}
|