with one click
add-freee-tool
Automates the standard workflow for adding a new freee MCP tool
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Automates the standard workflow for adding a new freee MCP tool
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
| name | add-freee-tool |
| description | Automates the standard workflow for adding a new freee MCP tool |
| user-invocable | true |
| allowed-tools | ["Bash","Read","Edit","Write","Grep","Glob"] |
| argument-hint | <tool_name> [endpoint_path] |
Automates the 7-step workflow for adding a new MCP tool to the freee-mcp project.
/add-freee-tool freee_get_xxx
/add-freee-tool freee_create_xxx /api/1/xxx
The argument should be the tool name (e.g., freee_get_deals) and optionally the freee API endpoint path.
Check if the endpoint exists in the OpenAPI spec:
SPEC="$(git rev-parse --show-toplevel)/openapi/minimal/accounting.json"
If the file exists, search for the endpoint path:
cat "$SPEC" | jq '.paths["<endpoint_path>"]'
Extract: HTTP method, path parameters, query parameters, response schema.
If the spec file does not exist: warn the user that openapi/minimal/accounting.json is not available, then ask the user to provide the endpoint details (path, method, parameters, response fields) or reference the freee API documentation. Do NOT abort — proceed with user-provided or documentation-based information.
If the endpoint is not found in the spec: warn the user and confirm whether to proceed.
File: src/types/freee.ts
Add a FreeeXxx interface matching the API response structure. Follow snake_case matching the freee API JSON keys.
Pattern reference — read existing interfaces in the file for conventions:
export interface FreeeXxx {
id: number;
company_id: number;
name: string;
// Match freee API response field names (snake_case)
// Use ? for optional fields
created_at?: string;
updated_at?: string;
}
Key conventions:
FreeeXxx (PascalCase with Freee prefix)snake_case matching API response?number, string, boolean, and nested interfaces as appropriateFile: src/api/freeeClient.ts
Add a method to the FreeeClient class. Read the file first to understand existing patterns.
Pattern reference:
async getXxx(companyId: number, params?: { offset?: number; limit?: number }): Promise<{ xxx: FreeeXxx[] }> {
const response = await this.api.get<{ xxx: FreeeXxx[] }>(`/api/1/xxx`, {
params: {
company_id: companyId,
...params,
},
});
return response.data;
}
Key conventions:
getXxx / createXxx / updateXxx / deleteXxxcompanyId: numberthis.api.get/post/put/delete (Axios instance with auth interceptors)response.dataApiCache, CACHE_TTL_*)../types/freee.js if not already importedFile: src/schemas.ts
Add a schema as a plain object (NOT z.object()). Read the file first to see companyIdField, optionalDateField, and other shared helpers.
Pattern reference:
export const GetXxxSchema = {
companyId: companyIdField,
offset: z.number().min(0).optional().describe("Pagination offset"),
limit: z
.number()
.min(1)
.max(100)
.optional()
.describe("Number of results (1-100)"),
};
Key conventions:
GetXxxSchema / CreateXxxSchema (PascalCase)Record<string, z.ZodTypeAny> (plain object, NOT z.object()).describe() with human-readable descriptioncompanyIdField for optional company IDoptionalDateField(description) and dateField(description) for dates.optional() before .describe()File: src/index.ts
Use the registerTool wrapper (NOT server.registerTool()). Read the file first to find the registration section.
Pattern reference:
registerTool(
"freee_get_xxx",
{
description: "Get xxx resources for a company",
inputSchema: schemas.GetXxxSchema,
},
async ({ companyId, offset, limit }) => {
try {
const data = await freeeClient.getXxx(getCompanyId(companyId), {
offset,
limit,
});
return {
content: [
{
type: "text" as const,
text: JSON.stringify(data, null, 2),
},
],
};
} catch (error) {
handleToolError("freee_get_xxx", error);
}
},
);
Key conventions:
freee_ prefix with snake_casetry/catch with handleToolErrorgetCompanyId(companyId) to resolve optional company ID{ content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }] }ResponseFormatter methods if applicableAdd tests in the following locations:
File: src/__tests__/handlers.test.ts
Find the expectedSchemas array in the 'Tool Schema Validation' describe block and add the new schema export name (NOT the tool name):
const expectedSchemas = [
// ... existing schemas
"GetXxxSchema", // Add the schema export name (PascalCase), NOT the tool name
];
Also add the new client method to the mockClient object in beforeEach.
File: src/__tests__/api/freeeClient.test.ts
Add a describe block for the new method:
describe("getXxx", () => {
it("should fetch xxx with correct parameters", async () => {
const mockResponse = { xxx: [{ id: 1, name: "Test" }] };
mockAxiosInstance.get.mockResolvedValue({ data: mockResponse });
const result = await client.getXxx(123, { offset: 0, limit: 10 });
expect(mockAxiosInstance.get).toHaveBeenCalledWith("/api/1/xxx", {
params: { company_id: 123, offset: 0, limit: 10 },
});
expect(result).toEqual(mockResponse);
});
});
File: src/__tests__/schemas.test.ts
Add validation tests using safeParse to verify schema constraints. Read the existing test file first to follow its patterns:
describe("GetXxxSchema validation", () => {
const schema = z.object(schemas.GetXxxSchema);
it("should accept valid input", () => {
const result = schema.safeParse({ companyId: 123 });
expect(result.success).toBe(true);
});
it("should accept optional fields", () => {
const result = schema.safeParse({
companyId: 123,
offset: 0,
limit: 10,
});
expect(result.success).toBe(true);
});
});
Run all checks to ensure nothing is broken:
npm run typecheck && npm run lint && npm test
If any step fails, fix the issues before proceeding. Common issues:
src/api/freeeClient.ts or src/index.ts.describe()| Step | File | Purpose |
|---|---|---|
| 1 | openapi/minimal/accounting.json | API endpoint spec (may not exist) |
| 2 | src/types/freee.ts | Type definitions |
| 3 | src/api/freeeClient.ts | API client methods |
| 4 | src/schemas.ts | Zod input schemas |
| 5 | src/index.ts | Tool registration |
| 6a | src/__tests__/handlers.test.ts | Schema existence tests |
| 6b | src/__tests__/api/freeeClient.test.ts | Client method tests |
| 6c | src/__tests__/schemas.test.ts | Schema validation tests |
| 7 | package.json | Verification commands |
These rules are auto-loaded by path matching but are useful references:
.claude/rules/adding-tools.md — Full registerTool pattern and schema conventions.claude/rules/testing.md — Mock patterns for FreeeClient and TokenManager.claude/rules/mcp-tools.md — Tool registration rules.claude/rules/freee-api.md — API constraints (single-use refresh tokens, rate limits)