Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

mcp-resources.test.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

mcp-resources.test.tsBlame79 lines · 1 contributor
3a15219ccantynz-alt1/**
2 * MCP resources: initialize must advertise the `resources` capability when a
3 * provider is wired, and resources/list + resources/read must route to it.
4 *
5 * Why this matters: Claude Desktop only SHOWS things a server exposes as
6 * resources. Before this, the server advertised only `tools`, so repos never
7 * appeared in the app's picker and users thought the repo "wasn't there".
8 * These tests lock the wiring with a stub provider (no DB needed).
9 */
10import { describe, it, expect } from "bun:test";
11import { routeMcpRequest } from "../lib/mcp";
12
13const ctx = { userId: "u1", scopes: ["repo"] };
14const tools = {};
15
16const stubResources = {
17 list: async () => ({
18 resources: [
19 { uri: "gluecron://repo/ccantynz/Vapron", name: "ccantynz/Vapron" },
20 ],
21 }),
22 read: async (uri: string) => ({
23 contents: [{ uri, mimeType: "text/markdown", text: "# hello" }],
24 }),
25};
26
27function rpc(method: string, params?: unknown) {
28 return { jsonrpc: "2.0" as const, id: 1, method, params };
29}
30
31describe("MCP resources capability", () => {
32 it("initialize advertises resources when a provider is wired", async () => {
33 const res: any = await routeMcpRequest(rpc("initialize"), {
34 ctx,
35 tools,
36 resources: stubResources,
37 });
38 expect(res.result.capabilities.resources).toBeDefined();
39 expect(res.result.capabilities.tools).toBeDefined();
40 });
41
42 it("initialize omits resources when NO provider is wired", async () => {
43 const res: any = await routeMcpRequest(rpc("initialize"), { ctx, tools });
44 expect(res.result.capabilities.resources).toBeUndefined();
45 });
46
47 it("resources/list routes to the provider", async () => {
48 const res: any = await routeMcpRequest(rpc("resources/list"), {
49 ctx,
50 tools,
51 resources: stubResources,
52 });
53 expect(res.result.resources).toHaveLength(1);
54 expect(res.result.resources[0].uri).toBe("gluecron://repo/ccantynz/Vapron");
55 });
56
57 it("resources/read passes the uri through and returns contents", async () => {
58 const res: any = await routeMcpRequest(
59 rpc("resources/read", { uri: "gluecron://repo/ccantynz/Vapron" }),
60 { ctx, tools, resources: stubResources }
61 );
62 expect(res.result.contents[0].text).toBe("# hello");
63 });
64
65 it("resources/read without a uri is an invalid-params error", async () => {
66 const res: any = await routeMcpRequest(rpc("resources/read", {}), {
67 ctx,
68 tools,
69 resources: stubResources,
70 });
71 expect(res.error).toBeDefined();
72 expect(res.error.code).toBe(-32602);
73 });
74
75 it("resources/list with no provider returns an empty list (no crash)", async () => {
76 const res: any = await routeMcpRequest(rpc("resources/list"), { ctx, tools });
77 expect(res.result.resources).toEqual([]);
78 });
79});