feat: serve workspace MCP over the web app HTTP endpoint
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { expect, test } from "../fixtures";
|
||||
|
||||
test("HTTP MCP persists decks visible in Research without browser credentials", async ({
|
||||
playwright,
|
||||
baseURL,
|
||||
page,
|
||||
}) => {
|
||||
const client = await playwright.request.newContext({
|
||||
baseURL,
|
||||
extraHTTPHeaders: {},
|
||||
storageState: { cookies: [], origins: [] },
|
||||
});
|
||||
async function rpc(method: string, params: unknown = {}) {
|
||||
const response = await client.post("/mcp", {
|
||||
headers: { Accept: "application/json, text/event-stream" },
|
||||
data: { jsonrpc: "2.0", id: 1, method, params },
|
||||
});
|
||||
expect(response.status()).toBe(200);
|
||||
const text = await response.text();
|
||||
const data = text.startsWith("event:")
|
||||
? text
|
||||
.split("\n")
|
||||
.find((line) => line.startsWith("data: "))
|
||||
?.slice(6)
|
||||
: text;
|
||||
return JSON.parse(data ?? "null").result;
|
||||
}
|
||||
async function tool(name: string, args: unknown = {}) {
|
||||
const result = await rpc("tools/call", { name, arguments: args });
|
||||
return JSON.parse(result.content[0].text);
|
||||
}
|
||||
try {
|
||||
// Browser routes still require their existing owner identity and session.
|
||||
expect((await client.get("/deck", { maxRedirects: 0 })).status()).toBe(403);
|
||||
const initialized = await rpc("initialize", {
|
||||
protocolVersion: "2025-03-26",
|
||||
capabilities: {},
|
||||
clientInfo: { name: "e2e", version: "1" },
|
||||
});
|
||||
expect(initialized.serverInfo.name).toBe("personal-workspace");
|
||||
const { connections } = await tool("list_connections");
|
||||
const connection = connections.find(
|
||||
(entry: { displayName: string }) => entry.displayName === "e2e",
|
||||
);
|
||||
const created = await tool("create_deck", {
|
||||
title: "HTTP MCP research",
|
||||
columns: [
|
||||
{
|
||||
title: "HTTP posts",
|
||||
connectionId: connection.id,
|
||||
source: { kind: "search", query: "WebMCP" },
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(created.ok).toBe(true);
|
||||
const fetched = await tool("fetch_column_posts", {
|
||||
deckId: created.deck.id,
|
||||
columnId: created.deck.columns[0].id,
|
||||
});
|
||||
expect(fetched.ok).toBe(true);
|
||||
expect(fetched.posts.length).toBeGreaterThan(0);
|
||||
await page.goto(`/deck?deck=${created.deck.id}`);
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText("HTTP MCP research");
|
||||
const updated = await tool("replace_deck", {
|
||||
deckId: created.deck.id,
|
||||
expectedRevision: 1,
|
||||
title: "Updated via HTTP",
|
||||
columns: created.deck.columns,
|
||||
});
|
||||
expect(updated.deck.revision).toBe(2);
|
||||
expect(
|
||||
await tool("delete_deck", { deckId: created.deck.id, expectedRevision: 1 }),
|
||||
).toMatchObject({ ok: false, error: { code: "conflict" } });
|
||||
await page.reload();
|
||||
await expect(page.getByRole("heading", { level: 1 })).toHaveText("Updated via HTTP");
|
||||
expect(
|
||||
await tool("delete_deck", { deckId: created.deck.id, expectedRevision: 2 }),
|
||||
).toMatchObject({ ok: true });
|
||||
expect(await tool("get_deck", { deckId: created.deck.id })).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "not-found" },
|
||||
});
|
||||
} finally {
|
||||
await client.dispose();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user