41ce810cff
- Native (non-mcp://) resource URIs now pass through byte-for-byte via rawHref; slash elision applies only to the legacy mcp:// wrapper, so catalog://root/ style URIs match exact-equality server lookups. - resources/templates/list failure no longer discards a successful resources/list (Promise.allSettled; templates retried later). - Opaque RFC 3986 URIs (urn:doc, custom:item) are recognized by both the router and read-cli discovery gates, with drive-path and read-selector false positives guarded. - Review follow-up for PR #6790.
88 lines
2.7 KiB
TypeScript
88 lines
2.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
import * as fs from "node:fs/promises";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { removeWithRetries } from "@oh-my-pi/pi-utils";
|
|
|
|
const CLI_ENTRY = path.join(import.meta.dir, "..", "src", "cli.ts");
|
|
const FIXTURE_PATH = path.join(import.meta.dir, "fixtures", "resources-no-templates-mcp.ts");
|
|
|
|
describe("omp read MCP resources", () => {
|
|
let root: string;
|
|
let projectDir: string;
|
|
let agentDir: string;
|
|
|
|
beforeEach(async () => {
|
|
root = await fs.mkdtemp(path.join(os.tmpdir(), "omp-read-mcp-"));
|
|
projectDir = path.join(root, "project");
|
|
agentDir = path.join(root, "agent");
|
|
await Promise.all([fs.mkdir(projectDir), fs.mkdir(agentDir)]);
|
|
await Bun.write(
|
|
path.join(projectDir, ".mcp.json"),
|
|
JSON.stringify({
|
|
mcpServers: {
|
|
fixture: {
|
|
type: "stdio",
|
|
command: process.execPath,
|
|
args: [FIXTURE_PATH],
|
|
},
|
|
},
|
|
}),
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await removeWithRetries(root);
|
|
});
|
|
|
|
async function runRead(resourceUri: string): Promise<{ exitCode: number; output: string; error: string }> {
|
|
const proc = Bun.spawn([process.execPath, CLI_ENTRY, "read", resourceUri], {
|
|
cwd: projectDir,
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
env: {
|
|
...process.env,
|
|
HOME: root,
|
|
NO_COLOR: "1",
|
|
PI_CODING_AGENT_DIR: agentDir,
|
|
},
|
|
});
|
|
const stdout = new Response(proc.stdout).text();
|
|
const stderr = new Response(proc.stderr).text();
|
|
const [exitCode, output, error] = await Promise.all([proc.exited, stdout, stderr]);
|
|
return { exitCode, output, error };
|
|
}
|
|
|
|
it("discovers MCP before reading a server-advertised native URI", async () => {
|
|
const { exitCode, output, error } = await runRead("test://alpha");
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(error).toBe("");
|
|
expect(output).toContain("fixture content for test://alpha");
|
|
}, 30_000);
|
|
|
|
it("discovers MCP before reading a server-advertised opaque URI", async () => {
|
|
const { exitCode, output, error } = await runRead("urn:fixture:gamma");
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(error).toBe("");
|
|
expect(output).toContain("fixture content for urn:fixture:gamma");
|
|
}, 30_000);
|
|
|
|
it("keeps the mcp:// wrapper working in the standalone CLI", async () => {
|
|
const { exitCode, output, error } = await runRead("mcp://test://beta");
|
|
|
|
expect(exitCode).toBe(0);
|
|
expect(error).toBe("");
|
|
expect(output).toContain("fixture content for test://beta");
|
|
}, 30_000);
|
|
|
|
it("exits after an MCP resource read error", async () => {
|
|
const { exitCode, output, error } = await runRead("test://missing");
|
|
|
|
expect(exitCode).toBe(1);
|
|
expect(output).toBe("");
|
|
expect(error).toContain('No MCP server has resource "test://missing"');
|
|
}, 30_000);
|
|
});
|