Skip to main content
Glama

Collaboration

collaboration

Query collaboration data in OfficeRnD including events, tickets, and posts with filtering options. List entities with pagination or retrieve specific items by ID.

Instructions

Query collaboration 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:

  • events: location, startAfter, startBefore (ISO dates)

  • tickets: status, member, location

  • posts: (pagination only)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform
entityYesEntity type to query
idNoEntity ID (required for action=get)
statusNoFilter by status (tickets only)
memberNoFilter by member ID (tickets only)
locationNoFilter by location ID (events, tickets)
startAfterNoEvents starting on/after this ISO date
startBeforeNoEvents starting before this ISO date
cursorNextNoCursor token for next page of results
limitNoResults per page (max 50, default 50)

Implementation Reference

  • The handler function logic for the collaboration tool, which executes list or get actions based on the provided entity and filters.
        async ({ action, entity, id, status, member, location, startAfter, startBefore, cursorNext, limit }) => {
          try {
            const cfg = ENTITIES[entity];
    
            if (action === "get") {
              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;
    
            switch (entity) {
              case "events":
                if (location) params["location"] = location;
                if (startAfter) params["start[$gte]"] = startAfter;
                if (startBefore) params["start[$lt]"] = startBefore;
                break;
              case "tickets":
                if (status) params["status"] = status;
                if (member) params["member"] = member;
                if (location) params["location"] = location;
                break;
            }
    
            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,
            };
          }
        }
      );
    }
  • The input schema definition using Zod for the collaboration tool.
    inputSchema: {
      action: z.enum(["list", "get"]).describe("Action to perform"),
      entity: z
        .enum(["events", "tickets", "posts"])
        .describe("Entity type to query"),
      id: z
        .string()
        .optional()
        .describe("Entity ID (required for action=get)"),
      status: z
        .string()
        .optional()
        .describe("Filter by status (tickets only)"),
      member: z
        .string()
        .optional()
        .describe("Filter by member ID (tickets only)"),
      location: z
        .string()
        .optional()
        .describe("Filter by location ID (events, tickets)"),
      startAfter: z
        .string()
        .optional()
        .describe("Events starting on/after this ISO date"),
      startBefore: z
        .string()
        .optional()
        .describe("Events starting before this ISO date"),
      cursorNext: z
        .string()
        .optional()
        .describe("Cursor token for next page of results"),
      limit: z
        .string()
        .optional()
        .describe("Results per page (max 50, default 50)"),
    },
  • Tool registration function for the collaboration tool.
    export function registerCollaborationTool(server: McpServer): void {
      server.registerTool(
        "collaboration",
        {
          title: "Collaboration",
          description: `Query collaboration 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:
    - events: location, startAfter, startBefore (ISO dates)
    - tickets: status, member, location
    - posts: (pagination only)`,
          inputSchema: {
            action: z.enum(["list", "get"]).describe("Action to perform"),
            entity: z
              .enum(["events", "tickets", "posts"])
              .describe("Entity type to query"),
            id: z
              .string()
              .optional()
              .describe("Entity ID (required for action=get)"),
            status: z
              .string()
              .optional()
              .describe("Filter by status (tickets only)"),
            member: z
              .string()
              .optional()
              .describe("Filter by member ID (tickets only)"),
            location: z
              .string()
              .optional()
              .describe("Filter by location ID (events, tickets)"),
            startAfter: z
              .string()
              .optional()
              .describe("Events starting on/after this ISO date"),
            startBefore: z
              .string()
              .optional()
              .describe("Events starting before this ISO date"),
            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, member, location, startAfter, startBefore, cursorNext, limit }) => {
          try {
            const cfg = ENTITIES[entity];
    
            if (action === "get") {
              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;
    
            switch (entity) {
              case "events":
                if (location) params["location"] = location;
                if (startAfter) params["start[$gte]"] = startAfter;
                if (startBefore) params["start[$lt]"] = startBefore;
                break;
              case "tickets":
                if (status) params["status"] = status;
                if (member) params["member"] = member;
                if (location) params["location"] = location;
                break;
            }
    
            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,
            };
          }
        }
      );
    }
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses pagination limits (max 50) and date formats (ISO), plus entity-specific filter constraints. However, it lacks safety disclosures (read-only vs destructive), error behavior, or rate limiting context that would be essential for a tool without annotation coverage.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Well-structured with clear logical flow: purpose statement → action definitions → entity-specific filter reference. Information density is high with no redundant sentences. Bullet formatting (implied by hyphens) aids scanability for the complex filter matrix.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Adequately covers input parameters and action selection for 10-parameter complexity, but lacks output schema description (return format, structure of events/tickets/posts objects). Without output schema or return value documentation, agents cannot predict result structure.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, establishing baseline 3. The description organizes entity-specific filter constraints centrally (events vs tickets vs posts), but individual parameter descriptions in the schema already capture most of this specificity. Adds minimal semantic value beyond schema structure.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States specific verb ('Query') + resource ('collaboration data in OfficeRnD') and distinguishes scope from siblings via entity types (events, tickets, posts). However, 'collaboration data' is slightly abstract without the entity list, and it doesn't explicitly contrast with sibling tools like 'community' or 'space'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides clear internal guidance distinguishing when to use 'list' (filtered pagination) vs 'get' (single entity by ID), including entity-specific filter mappings. However, it lacks external guidance on when to select this tool over siblings (billing, community, settings, space).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MrBoor/officernd-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server