@fitchmultz/pi-mcp-adapter 8.0.11 → 8.0.13
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 +27 -0
- package/dist/sampling-handler.d.ts +1 -1
- package/dist/sampling-handler.js +34 -28
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## 8.0.13 - 2026-11-03
|
|
11
|
+
|
|
12
|
+
<!-- npm-release:unreleased:start -->
|
|
13
|
+
<!-- npm-release:unreleased:end -->
|
|
14
|
+
|
|
15
|
+
- Enforce strict lint on MCP sampling bridge (#166) ([bb87eb1](https://github.com/fitchmultz/pi-mcp-adapter/commit/bb87eb14405d9231fb404dc8c50743b3c3757620))
|
|
16
|
+
- Enforce strict lint on native manager configuration tests (#168) ([998be45](https://github.com/fitchmultz/pi-mcp-adapter/commit/998be4523d16239614f4c0f0ca55006660488c23))
|
|
17
|
+
|
|
18
|
+
### What's Changed
|
|
19
|
+
|
|
20
|
+
- Enforce strict lint on MCP sampling bridge by @fitchmultz in https://github.com/fitchmultz/pi-mcp-adapter/pull/166
|
|
21
|
+
- Enforce strict lint on native manager configuration tests by @fitchmultz in https://github.com/fitchmultz/pi-mcp-adapter/pull/168
|
|
22
|
+
|
|
23
|
+
**Full Changelog**: https://github.com/fitchmultz/pi-mcp-adapter/compare/v8.0.12...v8.0.13
|
|
24
|
+
|
|
25
|
+
## 8.0.12 - 2026-11-03
|
|
26
|
+
|
|
27
|
+
<!-- npm-release:unreleased:start -->
|
|
28
|
+
<!-- npm-release:unreleased:end -->
|
|
29
|
+
|
|
30
|
+
### What's Changed
|
|
31
|
+
|
|
32
|
+
- Enforce strict lint on HTTP callback lifetimes by @fitchmultz in https://github.com/fitchmultz/pi-mcp-adapter/pull/163
|
|
33
|
+
- Enforce strict lint on native command panels by @fitchmultz in https://github.com/fitchmultz/pi-mcp-adapter/pull/165
|
|
34
|
+
|
|
35
|
+
**Full Changelog**: https://github.com/fitchmultz/pi-mcp-adapter/compare/v8.0.11...v8.0.12
|
|
36
|
+
|
|
10
37
|
## 8.0.11 - 2026-11-03
|
|
11
38
|
|
|
12
39
|
<!-- npm-release:unreleased:start -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Api, Model } from "@earendil-works/pi-ai";
|
|
2
2
|
import type { ExtensionUIContext, ModelRegistry } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import {
|
|
3
|
+
import type { Client, CreateMessageRequest, CreateMessageResult } from "@modelcontextprotocol/client";
|
|
4
4
|
export interface SamplingHandlerOptions {
|
|
5
5
|
serverName: string;
|
|
6
6
|
autoApprove: boolean;
|
package/dist/sampling-handler.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { truncateAtWord } from "./utils.js";
|
|
2
2
|
import { throwIfAborted } from "./abort.js";
|
|
3
|
-
import {} from "@modelcontextprotocol/client";
|
|
4
3
|
export function registerSamplingHandler(client, options, own = (run) => run()) {
|
|
5
4
|
// Own the handler's real promise, not the SDK caller's cancellable response facade.
|
|
6
5
|
client.setRequestHandler("sampling/createMessage", (request) => own(() => handleSamplingRequest(options, request)));
|
|
@@ -9,51 +8,57 @@ export async function handleSamplingRequest(options, request) {
|
|
|
9
8
|
const params = request.params;
|
|
10
9
|
const signal = options.getSignal();
|
|
11
10
|
throwIfAborted(signal);
|
|
11
|
+
validateSamplingFeatures(params);
|
|
12
|
+
const messages = params.messages.map(convertSamplingMessage);
|
|
13
|
+
const model = await resolveSamplingModel(options, params.modelPreferences);
|
|
14
|
+
throwIfAborted(signal);
|
|
15
|
+
await confirmSampling(options, "Approve MCP sampling request", formatRequestApproval(options.serverName, `${model.provider}/${model.id}`, params.systemPrompt, messages));
|
|
16
|
+
throwIfAborted(signal);
|
|
17
|
+
const result = await options.modelRegistry.complete(model, {
|
|
18
|
+
...(params.systemPrompt !== undefined ? { systemPrompt: params.systemPrompt } : {}),
|
|
19
|
+
messages,
|
|
20
|
+
}, samplingCompletionOptions(model, params, signal));
|
|
21
|
+
const converted = convertAssistantResult(result);
|
|
22
|
+
throwIfAborted(signal);
|
|
23
|
+
await confirmSampling(options, "Return MCP sampling response", formatResponseApproval(options.serverName, converted));
|
|
24
|
+
return converted;
|
|
25
|
+
}
|
|
26
|
+
function validateSamplingFeatures(params) {
|
|
12
27
|
if ("task" in params && params.task) {
|
|
13
28
|
throw new Error("MCP sampling tasks are not supported");
|
|
14
29
|
}
|
|
15
|
-
if (params.includeContext && params.includeContext !== "none") {
|
|
30
|
+
if (params.includeContext !== undefined && params.includeContext !== "none") {
|
|
16
31
|
throw new Error("MCP sampling context inclusion is not supported");
|
|
17
32
|
}
|
|
18
|
-
if (params.tools
|
|
33
|
+
if (params.tools && params.tools.length > 0) {
|
|
19
34
|
throw new Error("MCP sampling tool use is not supported");
|
|
20
35
|
}
|
|
21
36
|
if (params.toolChoice) {
|
|
22
37
|
throw new Error("MCP sampling tool choice is not supported");
|
|
23
38
|
}
|
|
24
|
-
if (params.stopSequences
|
|
39
|
+
if (params.stopSequences && params.stopSequences.length > 0) {
|
|
25
40
|
throw new Error("MCP sampling stop sequences are not supported");
|
|
26
41
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
throwIfAborted(signal);
|
|
30
|
-
await confirmSampling(options, "Approve MCP sampling request", formatRequestApproval(options.serverName, `${model.provider}/${model.id}`, params.systemPrompt, messages));
|
|
31
|
-
throwIfAborted(signal);
|
|
42
|
+
}
|
|
43
|
+
function samplingCompletionOptions(model, params, signal) {
|
|
32
44
|
const supportsTemperature = model.compat && "supportsTemperature" in model.compat
|
|
33
45
|
? model.compat.supportsTemperature !== false
|
|
34
46
|
: !(model.id === "gpt-6-astra" &&
|
|
35
47
|
(model.api === "openai-responses" || model.api === "openai-codex-responses"));
|
|
36
|
-
|
|
37
|
-
...(params.systemPrompt !== undefined ? { systemPrompt: params.systemPrompt } : {}),
|
|
38
|
-
messages,
|
|
39
|
-
}, {
|
|
48
|
+
return {
|
|
40
49
|
maxTokens: params.maxTokens,
|
|
41
50
|
...(supportsTemperature && params.temperature !== undefined
|
|
42
51
|
? { temperature: params.temperature }
|
|
43
52
|
: {}),
|
|
44
53
|
...(params.metadata !== undefined ? { metadata: params.metadata } : {}),
|
|
45
54
|
...(signal ? { signal } : {}),
|
|
46
|
-
}
|
|
47
|
-
const converted = convertAssistantResult(result);
|
|
48
|
-
throwIfAborted(signal);
|
|
49
|
-
await confirmSampling(options, "Return MCP sampling response", formatResponseApproval(options.serverName, converted));
|
|
50
|
-
return converted;
|
|
55
|
+
};
|
|
51
56
|
}
|
|
52
57
|
function formatRequestApproval(serverName, modelName, systemPrompt, messages) {
|
|
53
58
|
const lines = [
|
|
54
59
|
`${serverName} wants to sample ${messages.length} message${messages.length === 1 ? "" : "s"} with ${modelName}.`,
|
|
55
60
|
];
|
|
56
|
-
if (systemPrompt) {
|
|
61
|
+
if (systemPrompt !== undefined && systemPrompt !== "") {
|
|
57
62
|
lines.push(`System: ${truncateAtWord(systemPrompt, 400)}`);
|
|
58
63
|
}
|
|
59
64
|
for (const [index, message] of messages.entries()) {
|
|
@@ -80,19 +85,16 @@ function messageText(message) {
|
|
|
80
85
|
if (block.type === "thinking") {
|
|
81
86
|
return "[thinking]";
|
|
82
87
|
}
|
|
83
|
-
|
|
84
|
-
return `[tool call: ${block.name}]`;
|
|
85
|
-
}
|
|
86
|
-
return "[content]";
|
|
88
|
+
return `[tool call: ${block.name}]`;
|
|
87
89
|
})
|
|
88
90
|
.join("\n");
|
|
89
91
|
}
|
|
90
|
-
|
|
92
|
+
function samplingCandidates(options, modelPreferences) {
|
|
91
93
|
const candidates = [];
|
|
92
94
|
const availableModels = options.modelRegistry.getAvailable();
|
|
93
95
|
for (const hint of modelPreferences?.hints ?? []) {
|
|
94
|
-
const normalizedHint = hint.name
|
|
95
|
-
if (
|
|
96
|
+
const normalizedHint = (hint.name ?? "").trim().toLowerCase();
|
|
97
|
+
if (normalizedHint === "") {
|
|
96
98
|
continue;
|
|
97
99
|
}
|
|
98
100
|
for (const model of availableModels) {
|
|
@@ -109,6 +111,10 @@ async function resolveSamplingModel(options, modelPreferences) {
|
|
|
109
111
|
for (const model of availableModels) {
|
|
110
112
|
addSamplingCandidate(candidates, model);
|
|
111
113
|
}
|
|
114
|
+
return candidates;
|
|
115
|
+
}
|
|
116
|
+
async function resolveSamplingModel(options, modelPreferences) {
|
|
117
|
+
const candidates = samplingCandidates(options, modelPreferences);
|
|
112
118
|
const errors = [];
|
|
113
119
|
const signal = options.getSignal();
|
|
114
120
|
for (const model of candidates) {
|
|
@@ -188,14 +194,14 @@ function convertAssistantResult(message) {
|
|
|
188
194
|
return block.text;
|
|
189
195
|
}
|
|
190
196
|
if (block.type === "thinking") {
|
|
191
|
-
return
|
|
197
|
+
return;
|
|
192
198
|
}
|
|
193
199
|
throw new Error(`MCP sampling result ${block.type} content is not supported`);
|
|
194
200
|
})
|
|
195
201
|
.filter((value) => value !== undefined)
|
|
196
202
|
.join("\n\n")
|
|
197
203
|
.trim();
|
|
198
|
-
if (
|
|
204
|
+
if (text === "") {
|
|
199
205
|
throw new Error("MCP sampling result did not contain text content");
|
|
200
206
|
}
|
|
201
207
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitchmultz/pi-mcp-adapter",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.13",
|
|
4
4
|
"description": "Token-efficient MCP adapter for the Pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"lint:prompts": "oxlint --deny-warnings __tests__/prompts.test.ts __tests__/prompts-sdk-integration.test.ts __tests__/test-state.ts __tests__/test-extension-context.ts",
|
|
46
46
|
"lint:runtime-tests": "oxlint --deny-warnings __tests__/runtime",
|
|
47
47
|
"lint:tracing": "oxlint --deny-warnings mcp-trace.ts",
|
|
48
|
+
"lint:sampling": "oxlint --deny-warnings sampling-handler.ts",
|
|
48
49
|
"lint:sdk-http": "oxlint --deny-warnings __tests__/sdk-v2-http.test.ts __tests__/fixtures/native-capture-extension.ts",
|
|
49
50
|
"lint:lifecycle-tests": "oxlint --deny-warnings __tests__/index-lifecycle.test.ts __tests__/fixtures/extension-lifecycle.ts __tests__/test-extension-context.ts",
|
|
50
51
|
"lint:oauth-recovery": "oxlint --deny-warnings __tests__/oauth-recovery-integration.test.ts __tests__/fixtures/oauth-recovery-http.ts __tests__/fixtures/oauth-recovery.ts __tests__/fixtures/panel-host.ts",
|