community
Query OfficeRnD community data including members, companies, check-ins, and visits using filters and pagination to retrieve specific information.
Instructions
Query community/people data in OfficeRnD.
action=list: List entities with optional filters and pagination (max 50 per page). action=get: Get a single entity by ID.
Entity-specific filters when listing:
members: status, email, name, company, location
companies: name, status, location
memberships: member, company, status
checkins: member, location, startAfter, startBefore (ISO dates — use these to get today's check-ins)
contracts: member, company, status
visits: location, startAfter, startBefore (ISO dates — use these to get today's visits)
visitors: (pagination only)
opportunities: status, member, company
opportunity_statuses: (pagination only, no get)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform | |
| entity | Yes | Entity type to query | |
| id | No | Entity ID (required for action=get) | |
| status | No | Filter by status (members, companies, memberships, contracts, visits, opportunities) | |
| No | Filter by email (members only) | ||
| name | No | Filter by exact full name, e.g. 'Yoan Reimers' not just 'Yoan' (members, companies) | |
| member | No | Filter by member ID (memberships, checkins, contracts, opportunities) | |
| company | No | Filter by company ID (members, memberships, contracts, opportunities) | |
| location | No | Filter by location ID (members, companies, checkins, visits) | |
| startAfter | No | Filter visits/checkins starting on or after this ISO date (e.g. 2026-03-11T00:00:00.000Z) | |
| startBefore | No | Filter visits/checkins starting before this ISO date (e.g. 2026-03-12T00:00:00.000Z) | |
| cursorNext | No | Cursor token for next page of results | |
| limit | No | Results per page (max 50, default 50) |
Implementation Reference
- src/tools/community.ts:318-381 (handler)The asynchronous handler function for the 'community' tool that processes 'list' and 'get' actions.
async ({ action, entity, id, status, email, name, member, company, location, startAfter, startBefore, cursorNext, limit }) => { try { const cfg = ENTITIES[entity]; if (action === "get") { if (!cfg.getPath) { return { content: [{ type: "text" as const, text: `Entity "${entity}" does not support get by ID.` }], isError: true, }; } if (!id) { return { content: [{ type: "text" as const, text: "id is required for action=get." }], isError: true, }; } const item = await apiGet<Record<string, unknown>>(`${cfg.getPath}/${id}`); return { content: [{ type: "text" as const, text: cfg.formatter(item) }] }; } // list const params: Record<string, string> = {}; if (cursorNext) params["$cursorNext"] = cursorNext; if (limit) params["$limit"] = limit; if (status) params["status"] = status; if (email) params["email"] = email; if (name) params["name"] = name; if (member) params["member"] = member; if (company) params["company"] = company; if (location) params["location"] = location; if (entity === "visits" || entity === "checkins") { if (startAfter) params["start[$gte]"] = startAfter; if (startBefore) params["start[$lt]"] = startBefore; } const data = await apiGet<PaginatedResponse<Record<string, unknown>>>(cfg.listPath, params); if (data.results.length === 0) { return { content: [{ type: "text" as const, text: `No ${cfg.label} found.` }] }; } const text = data.results.map(cfg.formatter).join("\n---\n"); let result = `Found ${data.results.length} ${cfg.label} (range ${data.rangeStart}-${data.rangeEnd}):\n\n${text}`; if (data.cursorNext) { result += `\n\n[More results available — use cursorNext: "${data.cursorNext}"]`; } return { content: [{ type: "text" as const, text: result }] }; } catch (error) { return { content: [ { type: "text" as const, text: `Error querying ${entity}: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } ); - src/tools/community.ts:237-382 (registration)The registration function that defines the 'community' tool on the MCP server.
export function registerCommunityTool(server: McpServer): void { server.registerTool( "community", { title: "Community", description: `Query community/people data in OfficeRnD. action=list: List entities with optional filters and pagination (max 50 per page). action=get: Get a single entity by ID. Entity-specific filters when listing: - members: status, email, name, company, location - companies: name, status, location - memberships: member, company, status - checkins: member, location, startAfter, startBefore (ISO dates — use these to get today's check-ins) - contracts: member, company, status - visits: location, startAfter, startBefore (ISO dates — use these to get today's visits) - visitors: (pagination only) - opportunities: status, member, company - opportunity_statuses: (pagination only, no get)`, inputSchema: { action: z.enum(["list", "get"]).describe("Action to perform"), entity: z .enum([ "members", "companies", "memberships", "checkins", "contracts", "visits", "visitors", "opportunities", "opportunity_statuses", ]) .describe("Entity type to query"), id: z .string() .optional() .describe("Entity ID (required for action=get)"), status: z .string() .optional() .describe("Filter by status (members, companies, memberships, contracts, visits, opportunities)"), email: z .string() .optional() .describe("Filter by email (members only)"), name: z .string() .optional() .describe("Filter by exact full name, e.g. 'Yoan Reimers' not just 'Yoan' (members, companies)"), member: z .string() .optional() .describe("Filter by member ID (memberships, checkins, contracts, opportunities)"), company: z .string() .optional() .describe("Filter by company ID (members, memberships, contracts, opportunities)"), location: z .string() .optional() .describe("Filter by location ID (members, companies, checkins, visits)"), startAfter: z .string() .optional() .describe("Filter visits/checkins starting on or after this ISO date (e.g. 2026-03-11T00:00:00.000Z)"), startBefore: z .string() .optional() .describe("Filter visits/checkins starting before this ISO date (e.g. 2026-03-12T00:00:00.000Z)"), cursorNext: z .string() .optional() .describe("Cursor token for next page of results"), limit: z .string() .optional() .describe("Results per page (max 50, default 50)"), }, }, async ({ action, entity, id, status, email, name, member, company, location, startAfter, startBefore, cursorNext, limit }) => { try { const cfg = ENTITIES[entity]; if (action === "get") { if (!cfg.getPath) { return { content: [{ type: "text" as const, text: `Entity "${entity}" does not support get by ID.` }], isError: true, }; } if (!id) { return { content: [{ type: "text" as const, text: "id is required for action=get." }], isError: true, }; } const item = await apiGet<Record<string, unknown>>(`${cfg.getPath}/${id}`); return { content: [{ type: "text" as const, text: cfg.formatter(item) }] }; } // list const params: Record<string, string> = {}; if (cursorNext) params["$cursorNext"] = cursorNext; if (limit) params["$limit"] = limit; if (status) params["status"] = status; if (email) params["email"] = email; if (name) params["name"] = name; if (member) params["member"] = member; if (company) params["company"] = company; if (location) params["location"] = location; if (entity === "visits" || entity === "checkins") { if (startAfter) params["start[$gte]"] = startAfter; if (startBefore) params["start[$lt]"] = startBefore; } const data = await apiGet<PaginatedResponse<Record<string, unknown>>>(cfg.listPath, params); if (data.results.length === 0) { return { content: [{ type: "text" as const, text: `No ${cfg.label} found.` }] }; } const text = data.results.map(cfg.formatter).join("\n---\n"); let result = `Found ${data.results.length} ${cfg.label} (range ${data.rangeStart}-${data.rangeEnd}):\n\n${text}`; if (data.cursorNext) { result += `\n\n[More results available — use cursorNext: "${data.cursorNext}"]`; } return { content: [{ type: "text" as const, text: result }] }; } catch (error) { return { content: [ { type: "text" as const, text: `Error querying ${entity}: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } ); }