.claude/skills/api-development/SKILL.md
REST API development guide for the HMIS project. Use when creating or extending any REST API endpoint — covers file structure, auth pattern, response format, ApplicationConfig registration, CapabilityStatementResource update, AnthropicApiService integration (system prompt module + tool handler), and the full post-implementation checklist.
npx skillsauth add hmislk/hmis api-developmentInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Any time a new @Path class is created under com.divudi.ws.*, or an existing API gets new endpoints.
src/main/java/com/divudi/ws/<module>/<Name>Api.java@Path("<name>"), @RequestScoped@Stateless on the REST class itselfFinance header (unless module uses a different scheme)String key = requestContext.getHeader("Finance");
if (validateApiKey(key) == null) return errorResponse("Not a valid key", 401);
Copy validateApiKey, errorResponse, successResponse verbatim from DepartmentApi.java (lines 437–488). Do not reinvent.
{"status":"success","code":200,"data":{...}}
{"status":"error","code":400,"message":"..."}
For POST duplicate-detection: {"status":"already_exists","id":...,"name":...} (no wrapping envelope).
private static final Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
src/main/java/com/divudi/ws/common/ApplicationConfig.java — add one line to addRestResourceClasses():
resources.add(com.divudi.ws.<module>.<Name>Api.class);
src/main/java/com/divudi/ws/common/CapabilityStatementResource.java — add a resource(...) entry to buildResources().
Both are required every time:
a) buildSystemPrompt — add an appendModule(...) block so the system prompt tells Claude what the endpoint does:
appendModule(sb, "My Module", "/my_module",
"Description of what this module manages.",
null, // or githubUrl(branch, "developer_docs/API_MY_MODULE.md")
new String[][]{
{"GET", "/my_module", "List entries. Supports query, page, size"},
{"POST", "/my_module", "Create a new entry. Body: {name, code}"},
{"PUT", "/my_module/{id}", "Update an entry by ID"},
{"DELETE", "/my_module/{id}", "Soft-delete an entry by ID"}
});
b) buildToolsArray + executeToolCall — add a tool so Claude can actually call the API:
JsonObject myModuleTool in buildToolsArray() and include it in the returned array.case "my_tool_name": in executeToolCall(...) that calls a private callMyModuleApi(...) method.callMyModuleApi method uses hmisBaseUrl + hmisApiKey (both available as params to executeToolCall).AiChatController.sendMessage(...) already passes hmisApiBaseUrl and userHmisApiKey through to executeToolCall — use them.See ClinicalMetadataApi + manage_clinical_metadata tool in AnthropicApiService as a reference implementation.
developer_docs/API_<MODULE>.md — list all endpoints, params, example request/response.
Reference it in the appendModule(...) call via githubUrl(branch, "developer_docs/API_<MODULE>.md").
Map<String, Object> params = new HashMap<>();
params.put("t", symanticType); // if filtering by type
String jpql;
if (query != null && !query.isEmpty()) {
jpql = "select c from MyEntity c where c.retired=false and c.symanticType=:t"
+ " and upper(c.name) like :n order by c.name";
params.put("n", "%" + query.trim().toUpperCase() + "%");
} else {
jpql = "select c from MyEntity c where c.retired=false and c.symanticType=:t order by c.name";
}
List<MyEntity> items = facade.findByJpql(jpql, params, size); // 3-arg: (jpql, map, maxRecords)
MyEntity existing = facade.findFirstByJpql(
"select c from MyEntity c where c.retired=false and upper(c.name)=:n",
Map.of("n", name.toUpperCase()));
if (existing != null) {
// return already_exists response
}
entity.setRetired(true);
entity.setRetiredAt(new Date());
facade.edit(entity);
For thin CRUD over a single entity (like ClinicalMetadataApi), no @Stateless service EJB is needed — inject facades directly into the REST class. Only add a service layer when there is real business logic, multiple entity coordination, or the method is reused elsewhere.
# List
curl -s -H "Finance: <key>" "http://localhost:9090/rh/api/<path>?type=X"
# Create
curl -s -H "Finance: <key>" -H "Content-Type: application/json" \
-X POST "http://localhost:9090/rh/api/<path>?type=X" \
-d '{"name":"Test"}' | python -m json.tool
# Duplicate check (POST same name again — must return already_exists)
# same command as above
# Update
curl -s -H "Finance: <key>" -H "Content-Type: application/json" \
-X PUT "http://localhost:9090/rh/api/<path>/<id>" \
-d '{"name":"Updated"}' | python -m json.tool
# Delete
curl -s -H "Finance: <key>" -X DELETE "http://localhost:9090/rh/api/<path>/<id>" | python -m json.tool
# Capabilities (confirm new entry appears)
curl -s http://localhost:9090/rh/api/capabilities | python -m json.tool
For complete reference: developer_docs/api/rest-api-development-guide.md
tools
Use when work should span one or more detached tasks but still behave like one job with a single owner context. TaskFlow is the durable flow substrate under authoring layers like Lobster, ACPX, plugins, or plain code. Keep conditional logic in the caller; use TaskFlow for flow identity, child-task linkage, waiting state, revision-checked mutations, and user-facing emergence.
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
# Lobster Lobster executes multi-step workflows with approval checkpoints. Use it when: - User wants a repeatable automation (triage, monitor, sync) - Actions need human approval before executing (send, post, delete) - Multiple tool calls should run as one deterministic operation ## When to use Lobster | User intent | Use Lobster? | | ------------------------------------------------------ | --------------------------
tools
A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.