Skip to main content
Glama
tmhr1850

Backlog MCP Server

getUsers

Retrieve user lists from Backlog to manage project teams and assign tasks within the MCP server.

Instructions

ユーザー一覧の取得

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • Generic handler function executed for the 'getUsers' tool. Validates input params using Zod schema, constructs the API endpoint path ('users'), invokes fetchFromBacklog helper, formats response as JSON text, and handles errors.
    const handler = async (params: Record<string, unknown>): Promise<McpResponse> => {
      try {
        // パラメータのバリデーション
        const validatedParams = await endpoint.schema.parseAsync(params);
    
        // URL中のプレースホルダーを置換
        let path = endpoint.path;
        
        // URIスキーム(例:space://info)の場合はBacklog APIのパスに変換
        if (path.includes('://')) {
          // URIスキームを取り除いてAPIパスに変換
          const uriParts = path.split('://');
          const resourceType = uriParts[0];
          const resourcePath = uriParts[1];
          
          // リソースタイプに応じてAPIパスを構築
          switch (resourceType) {
            case 'space':
              path = 'space';
              break;
            case 'projects':
              if (resourcePath === 'list') {
                path = 'projects';
              } else {
                path = `projects/${resourcePath}`;
              }
              break;
            case 'issues':
              if (resourcePath.includes('/list')) {
                const projectPart = resourcePath.split('/')[0];
                path = 'issues';
                validatedParams.projectIdOrKey = projectPart;
              } else if (resourcePath.includes('/details')) {
                const issuePart = resourcePath.split('/')[0];
                path = `issues/${issuePart}`;
              }
              break;
            case 'wikis':
              if (resourcePath.includes('/list')) {
                const projectPart = resourcePath.split('/')[0];
                path = 'wikis';
                validatedParams.projectIdOrKey = projectPart;
              }
              break;
            case 'users':
              path = 'users';
              break;
            default:
              path = resourcePath;
          }
        }
        
        // 通常のパスパラメータの置換処理
        for (const [key, value] of Object.entries(validatedParams)) {
          if (typeof value === "string" || typeof value === "number") {
            const placeholder = `{${key}}`;
            if (path.includes(placeholder)) {
              path = path.replace(placeholder, String(value));
              // プレースホルダーとして使用したパラメータは削除
              delete validatedParams[key];
            }
          }
        }
    
        // メソッドの追加
        if (endpoint.method !== "GET") {
          validatedParams.method = endpoint.method;
        }
    
        // APIリクエストの実行
        const data = await fetchFromBacklog(path, validatedParams);
        return {
          content: [{ 
            type: "text" as const, 
            text: JSON.stringify(data, null, 2) 
          }],
        };
      } catch (error: any) {
        // エラーメッセージの整形
        const errorMessage = error.errors 
          ? `バリデーションエラー: ${JSON.stringify(error.errors)}`
          : `エラー: ${error.message}`;
    
        return {
          content: [{ 
            type: "text" as const, 
            text: errorMessage 
          }],
          isError: true,
        };
      }
    };
  • Input schema and metadata definition for the 'getUsers' tool used in registration and validation. Supports optional keyword search, roleType filter, pagination with offset/count, maps to Backlog /users API.
    {
      name: "getUsers",
      description: "ユーザー一覧の取得",
      path: "users",
      method: "GET",
      schema: z.object({
        keyword: z.string().max(1000, "検索キーワードは1000文字以内である必要があります").optional(),
        roleType: z.enum(["admin", "user", "reporter", "viewer", "guest"], {
          errorMap: () => ({ message: "ロールタイプは 'admin', 'user', 'reporter', 'viewer', 'guest' のいずれかである必要があります" })
        }).optional(),
        offset: offsetSchema,
        count: countSchema,
      }),
      type: "tool",
    },
  • MCP server.tool registration code executed in loop for 'getUsers' endpoint, binding the tool name, description, and shared handler function.
    if (endpoint.type === "tool") {
      // ツールの登録
      server.tool(
        endpoint.name,
        endpoint.description,
        async (args: Record<string, unknown>) => handler(args)
      );
  • Helper function that performs the HTTP request to Backlog API. For 'getUsers', it fetches from /api/v2/users with query params for filtering and pagination, handles auth via apiKey, errors, and returns raw API data.
    export async function fetchFromBacklog(
      endpoint: string,
      params: Record<string, any> = {}
    ): Promise<any> {
      try {
        // 環境変数のバリデーション
        const domain = process.env.BACKLOG_DOMAIN?.trim();
        const apiKey = process.env.BACKLOG_API_KEY?.trim();
        
        if (!domain || !apiKey) {
          throw new Error(
            "環境変数BACKLOG_DOMAINまたはBACKLOG_API_KEYが設定されていません"
          );
        }
    
        // パラメータのサニタイズ
        const sanitizedParams = Object.fromEntries(
          Object.entries(params).map(([key, value]) => [
            key,
            typeof value === "string" ? value.trim() : value
          ])
        );
    
        // エンドポイントのパスパラメータを置換
        let processedEndpoint = endpoint;
        if (endpoint.includes("{")) {
          // {issueId}のようなパスパラメータを実際の値で置換
          const matches = endpoint.match(/\{([^}]+)\}/g) || [];
          for (const match of matches) {
            const paramName = match.slice(1, -1); // {issueId} -> issueId
            if (sanitizedParams[paramName]) {
              processedEndpoint = processedEndpoint.replace(
                match,
                sanitizedParams[paramName]
              );
              delete sanitizedParams[paramName]; // URLに埋め込んだパラメータは削除
            } else {
              throw new Error(`パスパラメータ ${paramName} が指定されていません`);
            }
          }
        }
    
        // URLの構築
        const url = new URL(`https://${domain}/api/v2/${processedEndpoint}`);
        url.searchParams.append("apiKey", apiKey);
    
        // メソッドの判定
        const method = sanitizedParams.method || (endpoint.toLowerCase().includes("post") ? "POST" : "GET");
        delete sanitizedParams.method; // methodパラメータは削除
    
        // リクエストオプションの準備
        const options: RequestInit = {
          method,
          headers: {
            "User-Agent": "Backlog-MCP-Server/1.0.0",
            "Accept": "application/json"
          }
        };
    
        // POSTリクエストの場合はボディにパラメータを設定
        if (method === "POST") {
          // POSTリクエストの場合はFormDataを使用(Backlog APIの仕様に合わせる)
          const formData = new URLSearchParams();
          for (const [key, value] of Object.entries(sanitizedParams)) {
            if (value !== undefined && value !== null) {
              // 配列の場合は複数のパラメータとして追加
              if (Array.isArray(value)) {
                value.forEach(item => {
                  formData.append(`${key}[]`, String(item));
                });
              } else {
                formData.append(key, String(value));
              }
            }
          }
          options.body = formData.toString();
          options.headers = {
            ...options.headers,
            "Content-Type": "application/x-www-form-urlencoded"
          };
        } else {
          // GETリクエストの場合はURLにパラメータを追加
          for (const [key, value] of Object.entries(sanitizedParams)) {
            if (value !== undefined && value !== null) {
              // 配列の場合は複数のパラメータとして追加
              if (Array.isArray(value)) {
                value.forEach(item => {
                  url.searchParams.append(`${key}[]`, String(item));
                });
              } else {
                url.searchParams.append(key, String(value));
              }
            }
          }
        }
    
        // デバッグ情報(開発時のみ使用)
        if (process.env.NODE_ENV === 'development') {
          console.log(`[DEBUG] リクエスト: ${method} ${url.toString()}`);
          if (method === 'POST') {
            console.log(`[DEBUG] リクエストボディ: ${options.body}`);
          }
        }
    
        // リクエストの実行
        const response = await fetch(url.toString(), options);
        const data = await response.json();
    
        // Backlogエラーレスポンスのチェックとハンドリング
        if (isBacklogError(data)) {
          const error = data.errors[0];
          const errorDetails = error.moreInfo ? ` (詳細: ${error.moreInfo})` : '';
          throw new Error(`Backlogエラー [${error.code}]: ${error.message}${errorDetails}`);
        }
    
        if (!response.ok) {
          throw new Error(`HTTPエラー ${response.status}: ${response.statusText}`);
        }
    
        return data;
      } catch (error) {
        if (error instanceof Error) {
          // エラーの種類に応じて詳細なメッセージを提供
          if (error.message.includes('ENOTFOUND')) {
            throw new Error(`Backlog API接続エラー: ドメイン ${process.env.BACKLOG_DOMAIN} に接続できません`);
          } else if (error.message.includes('ETIMEDOUT')) {
            throw new Error(`Backlog API接続タイムアウト: リクエストがタイムアウトしました`);
          } else if (error.message.includes('SyntaxError')) {
            throw new Error(`Backlog APIレスポースエラー: 不正なJSONレスポンスを受信しました`);
          } else {
            throw new Error(`Backlog API呼び出しエラー: ${error.message}`);
          }
        }
        throw error;
      }
    } 
  • server.ts:29-29 (registration)
    Top-level call to registerEndpoints which iterates over endpoints array and registers 'getUsers' tool on the MCP server.
    registerEndpoints(server);
Behavior2/5

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

No annotations are provided, so the description carries full burden. It only states the action ('取得' - get) without disclosing behavioral traits like pagination, sorting, filtering capabilities, authentication requirements, rate limits, or what data is returned. This leaves significant gaps for a tool that likely returns a list of users.

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

Conciseness5/5

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

The description is a single, efficient phrase ('ユーザー一覧の取得') that directly states the purpose without any wasted words. It's appropriately sized for a simple tool with no parameters.

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

Completeness2/5

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

Given the tool's simplicity (0 parameters, no output schema), the description is minimal but incomplete. Without annotations or output schema, it doesn't explain return values, error conditions, or usage context. For a tool that likely returns user data, more detail on behavior would be helpful.

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

Parameters4/5

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

The input schema has 0 parameters with 100% coverage, so no parameter documentation is needed. The description appropriately doesn't mention parameters, aligning with the schema. Baseline is 4 for zero parameters, as it avoids unnecessary information.

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

Purpose3/5

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

The description 'ユーザー一覧の取得' (Get user list) clearly states the verb ('取得' - get) and resource ('ユーザー一覧' - user list), providing basic purpose. However, it doesn't differentiate from potential sibling tools like 'getIssues' or 'getProjects' beyond the resource type, making it somewhat vague in a multi-tool context.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives. With sibling tools like 'getIssues' and 'getProjects' available, there's no indication of context, prerequisites, or exclusions for selecting this user-focused tool over others.

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/tmhr1850/backlog-mcp-server'

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