Skip to main content
Glama
TCSoftInc

TestCollab MCP Server

by TCSoftInc

get_project_context

Retrieve essential project metadata to resolve human-readable names to numeric IDs and understand project structure before using other TestCollab tools.

Instructions

Get project context including project name, description, application type, suite tree, tags, test_case_custom_fields, test_plan_custom_fields, requirements, test plan folders, releases, and project users. Returns the metadata needed to resolve human-readable names (e.g. suite titles, tag names, folder titles, release titles, user names) to numeric IDs used by other tools. Also returns the project description and app_type (web_app, mobile_app, api, desktop_app, other) which should inform the style of test steps you generate.

IMPORTANT: Call this tool at the start of every conversation before using any other TestCollab tool. This avoids errors from unresolved suite names, tag names, or custom field references.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_idNoProject ID (optional — uses default project if omitted)

Implementation Reference

  • The handler function `handleProjectContext` in `src/resources/project-context.ts` implements the core logic for the "get_project_context" tool. It fetches project metadata (suites, tags, custom fields, etc.) and returns them as a JSON response.
    export async function handleProjectContext(
      projectId: number
    ): Promise<{ contents: Array<{ uri: string; mimeType: string; text: string }> }> {
      const now = Date.now();
      const cacheKey = cacheTtlMs > 0 ? getCacheKey(projectId) : undefined;
    
      if (cacheKey) {
        const cached = contextCache.get(cacheKey);
        if (cached && cached.expiresAt > now) {
          if (!hasEntityScopedCustomFields(cached.payload)) {
            console.log(
              `${logPrefix} Project context cache invalidated for project ${projectId} due to legacy payload shape`
            );
            contextCache.delete(cacheKey);
          } else {
            console.log(
              `${logPrefix} Project context cache hit for project ${projectId}`
            );
            return {
              contents: [
                {
                  uri: `testcollab://project/${projectId}/context`,
                  mimeType: "application/json",
                  text: JSON.stringify(cached.payload, null, 2),
                },
              ],
            };
          }
        }
      }
    
      const startTime = Date.now();
      console.log(`${logPrefix} Building project context for ${projectId}`);
    
      try {
        const client = getApiClient();
    
        let companyId: number | undefined;
        let projectName: string | undefined;
        let projectDescription: string | undefined;
        let appType: string | undefined;
        let appTypeOther: string | undefined;
        try {
          console.log(
            `${apiLogPrefix} GET /projects/{id} params: ${JSON.stringify({
              projectId,
            })}`
          );
          const project = await client.getProject(projectId);
          companyId = getCompanyIdFromProject(project);
          projectName = normalizeString(getField<string>(project, "name"));
          projectDescription = normalizeString(getField<string>(project, "description"));
          appType = normalizeString(getField<string>(project, "app_type"));
          appTypeOther = normalizeString(getField<string>(project, "app_type_other"));
        } catch (error) {
          console.warn(
            `${logPrefix} Failed to fetch project ${projectId} for company ID`,
            error
          );
        }
    
        console.log(
          `${apiLogPrefix} GET /suites params: ${JSON.stringify({
            projectId,
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /tags params: ${JSON.stringify({
            projectId,
            // companyId,
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /requirements params: ${JSON.stringify({
            projectId,
            companyId,
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /customfields params: ${JSON.stringify({
            projectId,
            companyId,
            entity: "TestCase",
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /customfields params: ${JSON.stringify({
            projectId,
            companyId,
            entity: "TestPlan",
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /testplanfolders params: ${JSON.stringify({
            projectId,
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /releases params: ${JSON.stringify({
            projectId,
          })}`
        );
        console.log(
          `${apiLogPrefix} GET /projectusers params: ${JSON.stringify({
            projectId,
          })}`
        );
    
        const [
          suitesList,
          tagsList,
          requirementsList,
          testCaseCustomFieldsList,
          testPlanCustomFieldsList,
          testPlanFoldersList,
          releasesList,
          projectUsersList,
        ] = await Promise.all([
          client.listSuites(projectId).catch((error) => {
            console.warn(
              `${logPrefix} Failed to fetch suites for ${projectId}`,
              error
            );
            return [];
          }),
          client.listTags(projectId).catch((error) => {
            console.warn(
              `${logPrefix} Failed to fetch tags for ${projectId}`,
              error
            );
            return [];
          }),
          client.listRequirements(projectId).catch((error) => {
            console.warn(
              `${logPrefix} Failed to fetch requirements for ${projectId}`,
              error
            );
            return [];
          }),
          companyId
            ? client
                .listProjectCustomFields(projectId, companyId, "TestCase")
                .catch((error) => {
                  console.warn(
                    `${logPrefix} Failed to fetch TestCase custom fields for ${projectId}`,
                    error
                  );
                  return [];
                })
            : Promise.resolve([]),
          companyId
            ? client
                .listProjectCustomFields(projectId, companyId, "TestPlan")
                .catch((error) => {
                  console.warn(
                    `${logPrefix} Failed to fetch TestPlan custom fields for ${projectId}`,
                    error
                  );
                  return [];
                })
            : Promise.resolve([]),
          client.listTestPlanFolders(projectId).catch((error) => {
            console.warn(
              `${logPrefix} Failed to fetch test plan folders for ${projectId}`,
              error
            );
            return [];
          }),
          client.listReleases(projectId).catch((error) => {
            console.warn(`${logPrefix} Failed to fetch releases for ${projectId}`, error);
            return [];
          }),
          client.listProjectUsers(projectId).catch((error) => {
            console.warn(
              `${logPrefix} Failed to fetch project users for ${projectId}`,
              error
            );
            return [];
          }),
        ]);
    
        const suites = buildSuiteTree(Array.isArray(suitesList) ? suitesList : []);
        const tags = mapTags(Array.isArray(tagsList) ? tagsList : []);
        const requirements = mapRequirements(
          Array.isArray(requirementsList) ? requirementsList : []
        );
        const test_case_custom_fields = mapCustomFields(
          Array.isArray(testCaseCustomFieldsList) ? testCaseCustomFieldsList : [],
          "TestCase"
        );
        const test_plan_custom_fields = mapCustomFields(
          Array.isArray(testPlanCustomFieldsList) ? testPlanCustomFieldsList : [],
          "TestPlan"
        );
        // Backward-compatibility alias retained for existing consumers.
        const custom_fields = test_case_custom_fields;
        const test_plan_folders = mapTestPlanFolders(
          Array.isArray(testPlanFoldersList) ? testPlanFoldersList : []
        );
        const releases = mapReleases(Array.isArray(releasesList) ? releasesList : []);
        const users = mapProjectUsers(
          Array.isArray(projectUsersList) ? projectUsersList : []
        );
    
        const payload: ProjectContextPayload = {
          project_id: projectId,
          ...(projectName ? { project_name: projectName } : {}),
          ...(projectDescription ? { project_description: projectDescription } : {}),
          ...(appType ? { app_type: appType } : {}),
          ...(appType === "other" && appTypeOther ? { app_type_other: appTypeOther } : {}),
          suites,
          tags,
          test_case_custom_fields,
          test_plan_custom_fields,
          custom_fields,
          requirements,
          test_plan_folders,
          releases,
          users,
        };
    
        if (cacheKey) {
          contextCache.set(cacheKey, {
            expiresAt: now + cacheTtlMs,
            payload,
          });
        }
    
        const durationMs = Date.now() - startTime;
        console.log(
          `${logPrefix} Project context ready for ${projectId} in ${durationMs}ms (suites: ${suites.length}, tags: ${tags.length}, test_case_custom_fields: ${test_case_custom_fields.length}, test_plan_custom_fields: ${test_plan_custom_fields.length}, requirements: ${requirements.length}, test_plan_folders: ${test_plan_folders.length}, releases: ${releases.length}, users: ${users.length})`
        );
    
        return {
          contents: [
            {
              uri: `testcollab://project/${projectId}/context`,
              mimeType: "application/json",
              text: JSON.stringify(payload, null, 2),
            },
          ],
        };
      } catch (error) {
        console.error(
          `${logPrefix} Failed to build project context for ${projectId}:`,
          error
        );
    
        return {
          contents: [
            {
              uri: `testcollab://project/${projectId}/context`,
              mimeType: "application/json",
              text: JSON.stringify(
                {
                  error: "PROJECT_CONTEXT_FETCH_FAILED",
                  message:
                    error instanceof Error ? error.message : "Unknown error",
                },
                null,
                2
              ),
            },
          ],
        };
      }
    }
  • The registration for the "get_project_context" tool occurs in `src/tools/index.ts`. It invokes `handleProjectContext` defined in `src/resources/project-context.ts`.
      server.tool(
        "get_project_context",
        `Get project context including project name, description, application type, suite tree, tags, test_case_custom_fields, test_plan_custom_fields, requirements, test plan folders, releases, and project users.
    Returns the metadata needed to resolve human-readable names (e.g. suite titles, tag names, folder titles, release titles, user names) to numeric IDs used by other tools.
    Also returns the project description and app_type (web_app, mobile_app, api, desktop_app, other) which should inform the style of test steps you generate.
    
    IMPORTANT: Call this tool at the start of every conversation before using any other TestCollab tool.
    This avoids errors from unresolved suite names, tag names, or custom field references.`,
        {
          project_id: z.number().optional().describe("Project ID (optional — uses default project if omitted)"),
        },
        async (args) => {
          const projectId = resolveProjectId(args.project_id);
          if (!projectId) {
            return {
              content: [
                {
                  type: "text" as const,
                  text: JSON.stringify({ error: "No project_id provided and no default project configured." }),
                },
              ],
            };
          }
    
          const result = await handleProjectContext(projectId);
          const text = result.contents[0]?.text ?? JSON.stringify({ error: "No context returned" });
    
          return {
            content: [
              {
                type: "text" as const,
                text,
              },
            ],
          };
        }
      );

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/TCSoftInc/testcollab-mcp-server'

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