Resource Gating
Resources use the same middleware and visibility system as tools. Hidden resources are excluded from resources/list and reject resources/read.
guard() on a Resource
import { createMCPServer } from "@lynq/lynq";
import { guard } from "@lynq/lynq/guard";
const server = createMCPServer({ name: "docs", version: "1.0.0" });
// Public resource -- always visible
server.resource(
"config://public",
{ name: "Public Config" },
async () => ({ text: '{"theme":"light"}' }),
);
// Protected resource -- hidden until guard() is authorized
server.resource(
"config://secrets",
guard(),
{ name: "Secret Config" },
async () => ({ text: '{"apiKey":"sk-..."}' }),
);authorize() Reveals Both Tools and Resources
A single c.session.authorize("guard") call reveals every tool and every resource guarded by the "guard" middleware. Both tools/list_changed and resources/list_changed notifications fire automatically.
server.tool(
"login",
{ description: "Log in" },
async (args, c) => {
c.session.set("user", args.user);
c.session.authorize("guard");
// Both "admin-panel" tool and "config://secrets" resource appear
return c.text("Logged in");
},
);
server.tool(
"admin-panel",
guard(),
{ description: "Admin operations" },
async (_args, c) => c.text("Admin panel data"),
);Individual Resource Control
Use enableResources() / disableResources() for fine-grained control independent of middleware authorization.
import { type ToolMiddleware } from "@lynq/lynq";
function hidden(name: string): ToolMiddleware {
return { name, onRegister: () => false };
}
server.resource(
"report://daily",
hidden("report-gate"),
{ name: "Daily Report", mimeType: "text/plain" },
async () => ({ text: "Today's report..." }),
);
server.resource(
"report://weekly",
hidden("report-gate"),
{ name: "Weekly Report", mimeType: "text/plain" },
async () => ({ text: "This week's report..." }),
);
server.tool(
"unlock_daily",
{ description: "Unlock only the daily report" },
async (_args, c) => {
c.session.enableResources("report://daily");
return c.text("Daily report unlocked");
},
);Global Middleware Applies to Resources
Global middleware registered with server.use() applies to tools, resources, and tasks. A single guard() gates everything:
server.use(guard()); // applies to all tools, resources, and tasks
// This resource IS protected by the global guard() above.
// No need to pass guard() again per-resource.
server.resource(
"config://settings",
{ name: "Settings" },
async () => ({ text: "{}" }),
);Under the hood
Resources use the same internal visibility map as tools. authorize("guard") fires both notifications/tools/list_changed and notifications/resources/list_changed. enableResources() / disableResources() set per-session overrides for specific resource URIs.