praxis-agent 0.51.0 → 0.52.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 +7 -3
- package/dist/mcp/claude-mcp-tools.js +22 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -216,9 +216,13 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
|
|
|
216
216
|
tool selection defers `mcp__*` schemas behind a turn-scoped `ToolSearch`;
|
|
217
217
|
each query activates at most eight deterministic matches for the next model
|
|
218
218
|
request, and published MCP tool descriptions are capped at 2,048 Unicode
|
|
219
|
-
code points.
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
code points. Text-only MCP results above 100,000 UTF-8 bytes are redacted
|
|
220
|
+
into mode-`0600` `.txt` files under the session-scoped `tool-results`
|
|
221
|
+
directory; providers and transcripts receive only a bounded instruction with
|
|
222
|
+
the absolute file path. Results at or below the limit remain inline, while
|
|
223
|
+
structured, mixed-media, and binary-resource handling is unchanged. Explicit
|
|
224
|
+
concrete `--tools` selections load selected tools directly, while
|
|
225
|
+
`--disallowedTools ToolSearch` restores the complete tool list.
|
|
222
226
|
- **Provider-neutral models** — native Provider Registry/Vault routing, API
|
|
223
227
|
adapters, an experimental Codex OAuth adapter, explicit capability checks,
|
|
224
228
|
separate per-attempt connect, byte-idle, and absolute-total timeouts, typed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
2
|
import { mkdir, mkdtemp, open, rm } from 'node:fs/promises';
|
|
3
3
|
import { homedir, tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
4
|
+
import { join, resolve as resolvePath } from 'node:path';
|
|
5
5
|
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
6
6
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
7
7
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
@@ -26,6 +26,7 @@ function capMcpToolDescription(value) {
|
|
|
26
26
|
return [...value].slice(0, MAX_MCP_TOOL_DESCRIPTION_CODE_POINTS).join('');
|
|
27
27
|
}
|
|
28
28
|
const MAX_RESOURCE_BYTES = 25 * 1024 * 1024;
|
|
29
|
+
const MAX_INLINE_MCP_TEXT_RESULT_BYTES = 100_000;
|
|
29
30
|
const NO_MCP_RESOURCES = 'No resources found. MCP servers may still provide tools even if they have no resources.';
|
|
30
31
|
const MCP_RESOURCE_TOOL_DEFINITIONS = [
|
|
31
32
|
{
|
|
@@ -458,11 +459,21 @@ async function mcpBinaryText(options, sensitiveValues, createdFiles) {
|
|
|
458
459
|
: `Resource from ${options.serverName} at ${options.uri}`;
|
|
459
460
|
return mcpTextBlock(`[${origin}] Binary content (${options.mimeType}, ${options.bytes.length} bytes) saved to ${filePath}`, sensitiveValues);
|
|
460
461
|
}
|
|
462
|
+
async function externalizeMcpTextResult(serverName, content, context, sensitiveValues, createdFiles) {
|
|
463
|
+
if (!context.toolResultDirectory) {
|
|
464
|
+
throw new Error('MCP large text tool result output directory is unavailable');
|
|
465
|
+
}
|
|
466
|
+
const bytes = Buffer.from(content, 'utf8');
|
|
467
|
+
const filePath = await writeToolBlob(resolvePath(context.cwd, context.toolResultDirectory), serverName, bytes, '.txt');
|
|
468
|
+
createdFiles.push(filePath);
|
|
469
|
+
return mcpTextBlock(`[MCP tool result from ${serverName}] Text content (${bytes.length} bytes) saved to ${filePath}`, sensitiveValues);
|
|
470
|
+
}
|
|
461
471
|
async function mcpToolResultUnchecked(serverName, result, context, sensitiveValues, createdFiles) {
|
|
462
472
|
if (!Array.isArray(result.content)) {
|
|
463
473
|
throw new Error('MCP tool result content must be an array');
|
|
464
474
|
}
|
|
465
475
|
const blocks = [];
|
|
476
|
+
let textOnly = result.structuredContent === undefined;
|
|
466
477
|
let resultBytes = 0;
|
|
467
478
|
const consumeBytes = (bytes) => {
|
|
468
479
|
resultBytes += bytes;
|
|
@@ -487,6 +498,7 @@ async function mcpToolResultUnchecked(serverName, result, context, sensitiveValu
|
|
|
487
498
|
}
|
|
488
499
|
continue;
|
|
489
500
|
}
|
|
501
|
+
textOnly = false;
|
|
490
502
|
if (item.type === 'image') {
|
|
491
503
|
if (typeof item.mimeType !== 'string' ||
|
|
492
504
|
!MCP_IMAGE_MEDIA_TYPES.has(item.mimeType) ||
|
|
@@ -573,6 +585,15 @@ async function mcpToolResultUnchecked(serverName, result, context, sensitiveValu
|
|
|
573
585
|
if (Buffer.byteLength(content) > MAX_RESOURCE_BYTES) {
|
|
574
586
|
throw new Error(`MCP tool result exceeded ${MAX_RESOURCE_BYTES} bytes`);
|
|
575
587
|
}
|
|
588
|
+
if (textOnly &&
|
|
589
|
+
Buffer.byteLength(content) > MAX_INLINE_MCP_TEXT_RESULT_BYTES) {
|
|
590
|
+
const pointerBlock = await externalizeMcpTextResult(serverName, content, context, sensitiveValues, createdFiles);
|
|
591
|
+
return {
|
|
592
|
+
content: pointerBlock.text,
|
|
593
|
+
contentBlocks: [pointerBlock],
|
|
594
|
+
isError: result.isError === true,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
576
597
|
const images = blocks.filter((block) => block.type === 'image');
|
|
577
598
|
return {
|
|
578
599
|
content,
|