create_device_custom_field
Define a new custom field for organization devices by specifying a name, unique code, and field type. Optionally configure dropdown options for dropdown-type fields.
Instructions
Create a new custom field for organization devices. Defines a new field that can be used across all devices in the organization.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| attributeName | Yes | Display label for the custom field (REQUIRED) | |
| attributeCode | Yes | Unique identifier for the custom field. Must contain only lowercase letters, numbers, and underscores (REQUIRED) | |
| kind | Yes | The type of the custom field (REQUIRED) | |
| configuration | No | Dropdown configuration with values. Only required for 'dropdown' kind fields. |
Implementation Reference
- The handler function 'createDeviceCustomField' that executes the tool logic. Constructs a body with attributeName, attributeCode, kind, and optionally configuration, then POSTs it to /fields/custom.
export async function createDeviceCustomField(params: CreateDeviceCustomFieldParams) { const client = getClient(); const body: Record<string, unknown> = { attributeName: params.attributeName, attributeCode: params.attributeCode, kind: params.kind, }; if (params.configuration !== undefined) body.configuration = params.configuration; return client.makePostApiCall("/fields/custom", new URLSearchParams(), body); } - The Zod schema 'CreateDeviceCustomFieldSchema' defining input validation for the tool, including required attributeName, attributeCode, kind (text/number/date/dropdown), and optional dropdown configuration.
export const CreateDeviceCustomFieldSchema = z.object({ attributeName: z.string().describe("Display label for the custom field (REQUIRED)"), attributeCode: z .string() .describe( "Unique identifier for the custom field. Must contain only lowercase letters, numbers, and underscores (REQUIRED)", ), kind: z.enum(["text", "number", "date", "dropdown"]).describe("The type of the custom field (REQUIRED)"), configuration: DropdownConfigurationSchema.optional().describe( "Dropdown configuration with values. Only required for 'dropdown' kind fields.", ), }); - src/index.ts:143-147 (registration)Tool registered as 'create_device_custom_field' in the ListToolsRequestSchema handler, with description and inputSchema.
name: "create_device_custom_field", description: "Create a new custom field for organization devices. Defines a new field that can be used across all devices in the organization.", inputSchema: zodToJsonSchema(CreateDeviceCustomFieldSchema), }, - src/index.ts:304-304 (registration)Tool handler mapping in the toolHandlers record, linking 'create_device_custom_field' to the createDeviceCustomField function with schema parsing.
create_device_custom_field: async (input) => createDeviceCustomField(CreateDeviceCustomFieldSchema.parse(input)), - src/admina-api.ts:21-175 (helper)The AdminaApiClient class (including getClient factory) that provides the makePostApiCall method used by the handler to POST to /fields/custom.
export class AdminaApiClient { private readonly apiKey: string; private readonly organizationId: string; private readonly ADMINA_API_BASE = "https://api.itmc.i.moneyforward.com/api/v1"; constructor(apiKey: string, organizationId: string) { this.apiKey = apiKey; this.organizationId = organizationId; } // Generic method to make GET API calls public async makeApiCall<T>( endpoint: string, queryParams: URLSearchParams, config: AxiosRequestConfig = {}, ): Promise<T> { try { const url = `${this.ADMINA_API_BASE}/organizations/${this.organizationId}${endpoint}?${queryParams.toString()}`; const response = await axios.get(url, { headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", ...MCP_USAGE_TRACKING_HEADERS, }, ...config, }); return response.data as T; } catch (error: unknown) { if (error instanceof AxiosError) { throw createAdminaError(error.status ?? 500, error.response?.data); } throw createAdminaError(500, { errorId: "non_axios_error" }); } } // Generic method to make POST API calls public async makePostApiCall<T>( endpoint: string, queryParams: URLSearchParams, body: Record<string, unknown> = {}, config: AxiosRequestConfig = {}, ): Promise<T> { try { const queryString = queryParams.toString(); const querySuffix = queryString ? `?${queryString}` : ""; const url = `${this.ADMINA_API_BASE}/organizations/${this.organizationId}${endpoint}${querySuffix}`; const response = await axios.post(url, body, { headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", ...MCP_USAGE_TRACKING_HEADERS, }, ...config, }); return response.data as T; } catch (error: unknown) { if (error instanceof AxiosError) { throw createAdminaError(error.status ?? 500, error.response?.data); } throw createAdminaError(500, { errorId: "non_axios_error" }); } } // Generic method to make PATCH API calls public async makePatchApiCall<T>( endpoint: string, body: Record<string, unknown> = {}, config: AxiosRequestConfig = {}, ): Promise<T> { try { const url = `${this.ADMINA_API_BASE}/organizations/${this.organizationId}${endpoint}`; const response = await axios.patch(url, body, { headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", ...MCP_USAGE_TRACKING_HEADERS, }, ...config, }); return response.data as T; } catch (error: unknown) { if (error instanceof AxiosError) { throw createAdminaError(error.status ?? 500, error.response?.data); } throw createAdminaError(500, { errorId: "non_axios_error" }); } } // Generic method to make PUT API calls public async makePutApiCall<T>( endpoint: string, body: Record<string, unknown> = {}, config: AxiosRequestConfig = {}, ): Promise<T> { try { const url = `${this.ADMINA_API_BASE}/organizations/${this.organizationId}${endpoint}`; const response = await axios.put(url, body, { headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", ...MCP_USAGE_TRACKING_HEADERS, }, ...config, }); return response.data as T; } catch (error: unknown) { if (error instanceof AxiosError) { throw createAdminaError(error.status ?? 500, error.response?.data); } throw createAdminaError(500, { errorId: "non_axios_error" }); } } // Generic method to make DELETE API calls public async makeDeleteApiCall<T>(endpoint: string, config: AxiosRequestConfig = {}): Promise<T> { try { const url = `${this.ADMINA_API_BASE}/organizations/${this.organizationId}${endpoint}`; const response = await axios.delete(url, { headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", ...MCP_USAGE_TRACKING_HEADERS, }, ...config, }); return response.data as T; } catch (error: unknown) { if (error instanceof AxiosError) { throw createAdminaError(error.status ?? 500, error.response?.data); } throw createAdminaError(500, { errorId: "non_axios_error" }); } } } let clientInstance: AdminaApiClient | null = null; export function getClient(): AdminaApiClient { if (!clientInstance) { const config = getConfig(); clientInstance = new AdminaApiClient(config.apiKey, config.organizationId); } return clientInstance; }