99 lines
3.0 KiB
TypeScript
99 lines
3.0 KiB
TypeScript
/**
|
|
* MCP tools for rSpace space discovery.
|
|
*
|
|
* Tools: list_spaces, list_modules
|
|
* Resource: rspace://spaces/{slug}
|
|
*/
|
|
|
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
import { z } from "zod";
|
|
import { listCommunities, loadCommunity, getDocumentData, normalizeVisibility } from "../community-store";
|
|
import { getModuleInfoList } from "../../shared/module";
|
|
import { resolveAccess } from "./_auth";
|
|
|
|
export function registerSpacesTools(server: McpServer) {
|
|
server.tool(
|
|
"list_spaces",
|
|
"List all rSpace spaces with metadata (name, visibility, owner, members, enabled modules)",
|
|
{
|
|
token: z.string().optional().describe("JWT auth token (shows private/permissioned spaces you have access to)"),
|
|
},
|
|
async ({ token }) => {
|
|
const slugs = await listCommunities();
|
|
const spaces = [];
|
|
for (const slug of slugs) {
|
|
await loadCommunity(slug);
|
|
const data = getDocumentData(slug);
|
|
if (!data) continue;
|
|
|
|
const visibility = normalizeVisibility(data.meta.visibility || "private");
|
|
|
|
// Filter out spaces the caller can't see
|
|
if (visibility !== "public") {
|
|
const access = await resolveAccess(token, slug, false);
|
|
if (!access.allowed) continue;
|
|
}
|
|
|
|
spaces.push({
|
|
slug,
|
|
name: data.meta.name,
|
|
visibility,
|
|
ownerDID: data.meta.ownerDID,
|
|
description: data.meta.description || null,
|
|
enabledModules: data.meta.enabledModules || null,
|
|
memberCount: Object.keys(data.members || {}).length,
|
|
shapeCount: Object.keys(data.shapes || {}).length,
|
|
createdAt: data.meta.createdAt,
|
|
});
|
|
}
|
|
return {
|
|
content: [{ type: "text", text: JSON.stringify(spaces, null, 2) }],
|
|
};
|
|
},
|
|
);
|
|
|
|
server.tool(
|
|
"list_modules",
|
|
"List all available rSpace modules (rApps) with metadata",
|
|
{},
|
|
async () => {
|
|
const modules = getModuleInfoList();
|
|
return {
|
|
content: [{ type: "text", text: JSON.stringify(modules, null, 2) }],
|
|
};
|
|
},
|
|
);
|
|
|
|
server.resource(
|
|
"space_metadata",
|
|
"rspace://spaces/{slug}",
|
|
{ description: "Get detailed metadata for a specific rSpace space" },
|
|
async (uri) => {
|
|
const slug = uri.pathname.split("/").pop();
|
|
if (!slug) {
|
|
return { contents: [{ uri: uri.href, mimeType: "application/json", text: '{"error":"Missing slug"}' }] };
|
|
}
|
|
await loadCommunity(slug);
|
|
const data = getDocumentData(slug);
|
|
if (!data) {
|
|
return { contents: [{ uri: uri.href, mimeType: "application/json", text: '{"error":"Space not found"}' }] };
|
|
}
|
|
const result = {
|
|
slug,
|
|
name: data.meta.name,
|
|
visibility: data.meta.visibility,
|
|
ownerDID: data.meta.ownerDID,
|
|
description: data.meta.description || null,
|
|
enabledModules: data.meta.enabledModules || null,
|
|
members: Object.values(data.members || {}),
|
|
shapeCount: Object.keys(data.shapes || {}).length,
|
|
nestedSpaceCount: Object.keys(data.nestedSpaces || {}).length,
|
|
createdAt: data.meta.createdAt,
|
|
};
|
|
return {
|
|
contents: [{ uri: uri.href, mimeType: "application/json", text: JSON.stringify(result, null, 2) }],
|
|
};
|
|
},
|
|
);
|
|
}
|