依据WCAG 2.2设计和检查Web、iOS及Android界面,核查读屏标签、键盘操作、配色对比度和操作目标尺寸,使界面支持辅助技术。
- 为纯图标按钮添加说明
- 检查键盘操作与模态框焦点
- 检查对比度与操作目标尺寸
使用 Java 的 tinystruct 实现可从终端和网页调用的功能,按开发流程生成启动脚本、构建 JSON API、保存数据并测试,也提供文件上传、服务器通知和 MCP 工具示例。
Expert guidance for developing with the tinystruct Java framework. Use when working on the tinystruct codebase or any project built on tinystruct — including generating the bin/dispatcher and bin/dispatcher.cmd launcher scripts when a project lacks them, creating Application classes, @Action-mapped routes, unit tests, ActionRegistry, HTTP/CLI dual-mode handling, the built-in HTTP server, the event system, JSON with Builder/Builders, database persistence with AbstractData, POJO generation, Server-Sent Events (SSE), file uploads, and outbound HTTP networking.
tinystruct-patterns 指导使用 Java 的 tinystruct 实现可从终端和网页调用的功能。内容包括创建 Application、通过 @Action 定义调用入口、生成 JSON、将数据保存到数据库以及测试。除了生成启动脚本,还提供文件上传、通过服务器发送事件(SSE)推送通知,以及供 AI 代理调用的 MCP 工具的实现示例。
适合为现有 tinystruct 项目添加功能,或让同一项功能同时用于终端和网页。如果遇到找不到操作、参数未传入等问题,也可以沿着使用 bin/dispatcher 验证的流程排查。
bin/dispatcher。”@Action 创建返回 JSON 的 API,并从终端验证运行情况。”需要 Java 和 tinystruct 的 jar 文件。Windows 下使用 bin/dispatcher.cmd,并设置 JAVA_HOME。额外的库放在 lib/ 中。启动脚本只会为运行生成操作的当前操作系统生成,首次运行时还可能解压 Maven Wrapper。
本介绍由 AI(OpenAI Codex)根据公开的 SKILL.md 撰写。准确的规格请查看下方原文。
AI 仅根据 SKILL.md 的内容,从 4 个方面进行了评估。星标数和热度不计入评估。
总分按每项 A=25 分、B=15 分、C=5 分相加。 由 AI(OpenAI Codex)于 2026年10月19日 评估。仅供参考,安装前请同时查看原文。
安装前可以查看交给 AI 代理的指令内容。
Architecture and implementation patterns for building modules with the tinystruct Java framework – a lightweight, high-performance framework that treats CLI and HTTP as equal citizens, requiring no main() method and minimal configuration.
CLI and HTTP are equal citizens. Every method annotated with @Action should ideally be runnable from both a terminal and a web browser without modification. This "dual-mode" capability is the core design philosophy of tinystruct.
bin/dispatcherbin/dispatcher is the default, highest-priority tool for developing, running, testing, and debugging a tinystruct application — reach for it before an IDE run configuration, a hand-written main(), curl, or a browser. Because every @Action is dual-mode by design, bin/dispatcher lets you exercise routing, argument binding, and business logic directly from the terminal, with the fastest possible feedback loop and no server/browser required.
# Run any @Action directly - the fastest way to verify a new action works
# (the class must be imported; see --import or default.import.applications in application.properties)
bin/dispatcher greet/James --import com.example.MyService
bin/dispatcher echo --words "Praise the Lord"
# Start the HTTP server when you need the web-facing counterpart
bin/dispatcher start --import org.tinystruct.system.HttpServer
# Import additional Application/MCP classes for the current run
# (one --import per class; a comma-separated list fails with ClassNotFoundException)
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyService
# Discover what's available
bin/dispatcher --help
bin/dispatcher --version
Default to this workflow: implement the @Action, run it immediately via bin/dispatcher <action> to confirm it behaves correctly in CLI mode, and only start the HTTP server (also via bin/dispatcher start ...) once you need to verify the web-facing path (e.g. mode = Mode.HTTP_POST, sessions, file uploads). Never hardcode a main(String[] args) as an app's entry point — bin/dispatcher (or bin/dispatcher.cmd on Windows) is the one entry point for every module.
bin/dispatcher and bin/dispatcher.cmdEvery tinystruct project needs its launcher in bin/. The framework generates it — never write it by hand, and never copy it from another project or substitute a main(). ApplicationManager.init() writes the launcher for the current operating system into bin/ under the working directory whenever it is missing, from templates inside the tinystruct jar, with that jar's version filled in. Line endings and the executable bit are already right.
1. Find the tinystruct version the project builds against: the <tinystruct.version> property or the org.tinystruct:tinystruct dependency in pom.xml. The jar must be reachable, in ~/.m2/repository/org/tinystruct/tinystruct/<version>/ (run mvn dependency:resolve if it is not) or in the project's lib/.
2. From the project root, run the framework once:
java -cp ~/.m2/repository/org/tinystruct/tinystruct/<version>/tinystruct-<version>.jar \
org.tinystruct.system.Dispatcher --version
Replace <version> with the version found in step 1. On Windows use %USERPROFILE%\.m2\repository\org\tinystruct\tinystruct\<version>\tinystruct-<version>.jar and the same class. This creates only the script for the OS you ran it on, and only if it is not already there:
| Run on | Creates |
|---|---|
| Linux, macOS | bin/dispatcher (executable, LF) |
| Windows | bin\dispatcher.cmd (CRLF) |
3. Verify. From the project root run bin/dispatcher --version (Windows: bin\dispatcher.cmd --version). It prints Dispatcher (cli) (built on tinystruct-<version>), and the VERSION inside the script equals the jar's version. Then bin/dispatcher --help lists the commands and every imported action. Show the user the file you created.
4. Get both scripts by running step 2 on each OS, or let a teammate or CI on the other OS run it and commit the result. Commit both, so a checkout on either OS works. Do not try to fake the other OS with -Dos.name=…: on Windows it fails (UnsupportedOperationException from setPosixFilePermissions) and leaves an empty bin/dispatcher. Recommended .gitattributes, so a checkout does not corrupt the line endings:
bin/dispatcher text eol=lf
bin/dispatcher.cmd text eol=crlf
Upgrading tinystruct: change the version in pom.xml, delete the old script, and repeat step 2 with the new jar. Alternatively bin/dispatcher update checks Maven Central for the latest release, upgrades the project's dependency and regenerates the script with force; it needs the network, edits the pom.xml, and goes to the latest version, not a chosen one.
It also happens implicitly. Any code that reaches ApplicationManager.init() (the dispatcher itself, or ApplicationManager.install(app, config) in a unit test) creates the script in the current directory if it is missing. If unit tests scatter bin/dispatcher.cmd into module directories, run them from the build directory: in the surefire configuration set <workingDirectory>${project.build.directory}</workingDirectory>.
What the scripts do, which explains most problems:
.cmd takes the parent of bin/.target/classes, lib/*.jar, WEB-INF/lib/*, WEB-INF/classes and the tinystruct jar (from lib/, else ~/.m2), then they run org.tinystruct.system.Dispatcher with your arguments.mvnw / mvnw.cmd is missing, they extract the Maven Wrapper into the project root (unzip on Unix, PowerShell on Windows). Tell the user this happens; the files can be committed or ignored.-D… and -X… arguments as JVM options.Gotchas:
bin\dispatcher.cmd. The shell script joins the classpath with :, so under Git Bash/MSYS it fails with ClassNotFoundException: org.tinystruct.system.Dispatcher. Use WSL, or the .cmd.lib/: mvn dependency:copy-dependencies -DoutputDirectory=lib (and git-ignore lib/). In a multi-module build, keep the launcher in the directory that holds target/classes and lib/, and copy the sibling modules' jars into that lib/; running it from a module whose siblings are not on its classpath fails with NoClassDefFoundError.--import or configuration. One --import per class (--import a.A,b.B fails; write --import a.A --import b.B), or list them once in application.properties: default.import.applications=a.A;b.B (;-separated).JAVA_HOME must be set for the .cmd; it stops with an error otherwise.bin/ sits directly under the directory that holds target/classes and lib/. The .cmd treats the parent of bin/ as the root, so a bin/ nested in the wrong directory puts the wrong classpath in front of Dispatcher.@Action via bin/dispatcher — this is the default way to work with a tinystruct app, before reaching for HTTP or an IDE run configuration.bin/dispatcher / bin/dispatcher.cmd — have the framework generate it (see "Generating bin/dispatcher and bin/dispatcher.cmd").Application modules by extending AbstractApplication.@Action.Context.Builder and Builders components.AbstractData POJOs.generate command, with an XML mapping file or annotations (--mapping annotation).URLRequest and HTTPHandler.application.properties.The tinystruct framework treats any method annotated with @Action as a routable endpoint for both terminal and web environments. Applications are created by extending AbstractApplication, which provides core lifecycle hooks like init() and access to the request Context.
Routing is handled by the ActionRegistry, which automatically maps path segments to method arguments and injects dependencies. For data-only services, the native Builder and Builders components should be used for JSON serialization to maintain a zero-dependency footprint. The database layer uses AbstractData POJOs mapped to tables with @Table/@Column annotations or XML mapping files for CRUD operations without external ORM libraries. Missing tables can be created automatically with database.autocreate=true.
public class MyService extends AbstractApplication {
@Override
public void init() {
this.setTemplateRequired(false); // Disable .view lookup for data/API apps
}
@Override public String version() { return "1.0.0"; }
@Action("greet")
public String greet() {
return "Hello from tinystruct!";
}
// Path parameter: GET /?q=greet/James OR bin/dispatcher greet/James
@Action("greet")
public String greet(String name) {
return "Hello, " + name + "!";
}
}
@Action(value = "login", mode = Mode.HTTP_POST)
public String doLogin(Request<?, ?> request) throws ApplicationException {
request.getSession().setAttribute("userId", "42");
return "Logged in";
}
import org.tinystruct.data.component.Builder;
import org.tinystruct.data.component.Builders;
@Action("api/data")
public String getData() throws ApplicationException {
Builders dataList = new Builders();
Builder item = new Builder();
item.put("id", 1);
item.put("name", "James");
dataList.add(item);
Builder response = new Builder();
response.put("status", "success");
response.put("data", dataList);
return response.toString(); // {"status":"success","data":[{"id":1,"name":"James"}]}
}
import org.tinystruct.http.SSEPushManager;
@Action("sse/connect")
public String connect() {
return "{\"type\":\"connect\",\"message\":\"Connected to SSE\"}";
}
// Push to a specific client
String sessionId = getContext().getId();
Builder msg = new Builder();
msg.put("text", "Hello, user!");
SSEPushManager.getInstance().push(sessionId, msg);
// Broadcast to all
// Broadcast to all
SSEPushManager.getInstance().broadcast(msg);
import org.tinystruct.data.FileEntity;
@Action(value = "upload", mode = Mode.HTTP_POST)
public String upload(Request<?, ?> request) throws ApplicationException {
List<FileEntity> files = request.getAttachments();
if (files != null) {
for (FileEntity file : files) {
System.out.println("Uploaded: " + file.getFilename());
}
}
return "Upload OK";
}
DatabaseOperator provides direct access to connection-level catalog, schema, and metadata:
import org.tinystruct.data.DatabaseOperator;
DatabaseOperator operator = new DatabaseOperator();
try {
String catalog = operator.getCatalog();
String schema = operator.getSchema();
java.sql.DatabaseMetaData metaData = operator.getMetaData();
System.out.println("Using catalog: " + catalog + ", schema: " + schema);
} finally {
operator.close();
}
tinystruct provides native support for the Model Context Protocol (MCP) starting with SDK version 1.7.0.
The MCP APIs (e.g., org.tinystruct.mcp.MCPTool, org.tinystruct.mcp.MCPServer, org.tinystruct.mcp.MCPException) are included directly in the core dependency:
<dependency>
<groupId>org.tinystruct</groupId>
<artifactId>tinystruct</artifactId>
<version>1.7.34</version>
</dependency>
SECURITY WARNING (Prompt Injection): Tool return values are fed directly back into the AI model's context window. You MUST validate and sanitize all caller-supplied arguments before including them in the tool's return string. Failure to sanitize inputs can allow an attacker to inject adversarial instructions (Prompt Injection) that override the model's behavior. Always validate length, character sets, and nullity.
To create an MCP Tool:
org.tinystruct.mcp.MCPTool.@Action and declare parameters using @Argument within the arguments array.@Argument. (Do not use getContext().getAttribute(...) for tool arguments).import org.tinystruct.mcp.MCPTool;
import org.tinystruct.mcp.MCPException;
import org.tinystruct.system.annotation.Action;
import org.tinystruct.system.annotation.Argument;
public class MyCustomTool extends MCPTool {
public MyCustomTool() {
super("custom", "A custom tool for demonstrating MCP");
}
@Action(
value = "custom/hello",
description = "Say hello to someone",
arguments = {
@Argument(key = "name", description = "The name to greet", type = "string", optional = false)
}
)
public String hello(String name) throws MCPException {
// SECURITY: Validate/sanitize tool inputs before returning to the model
// to prevent prompt injection vulnerabilities.
if (name == null || name.length() > 50 || !name.matches("^[a-zA-Z0-9 ]+$")) {
throw new MCPException("Invalid name provided");
}
return "Hello, " + name + "!";
}
}
To deploy an MCP Server:
org.tinystruct.mcp.MCPServer.init() and register your tools using this.registerTool(). The framework automatically scans and maps the @Action methods.import org.tinystruct.mcp.MCPServer;
public class MyMCPServer extends MCPServer {
@Override
public void init() {
super.init();
this.registerTool(new MyCustomTool());
}
@Override
public String version() {
return "1.0.0";
}
}
Run the server via the dispatcher:
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyMCPServer
tinystruct supports overloaded methods for the same tool name in MCPTool (sharing the same tool name but accepting different parameter signatures).
Settings are managed in src/main/resources/application.properties.
# Database
driver=org.h2.Driver
database.url=jdbc:h2:~/mydb
database.user=sa
database.password=
# Optional: create missing tables from the class mapping on first use (off by default)
# database.autocreate=true
# Server
default.home.page=hello
server.port=8080
default.server.open_browser=true
# Locale
default.language=en_US
# Session (Redis for clustered environments)
# default.session.repository=org.tinystruct.http.RedisSessionRepository
# redis.host=127.0.0.1
# redis.port=6379
# Programmatic Logging Configuration
logging.enabled=true
logging.level=INFO
org.tinystruct.level=FINE
Access config values in your application:
String port = this.getConfiguration("server.port");
The framework includes a programmatic wrapper around java.util.logging (JUL) that provides beautiful console output and advanced caller tracing out of the box.
StackWalker API to trace the call stack at runtime. It identifies and logs the exact class, method, filename, and line number of the caller that initiated the log (bypassing internal utility and logging layers).application.properties (e.g., org.tinystruct.level=FINE).| Symptom | Correct Pattern |
|---|---|
Importing com.google.gson or com.fasterxml.jackson | Use org.tinystruct.data.component.Builder / Builders. |
Using List<Builder> for JSON arrays | Use Builders to avoid generic type erasure issues. |
ApplicationRuntimeException: template not found | Call setTemplateRequired(false) in init() for API-only apps. |
Annotating private methods with @Action | Actions must be public to be registered by the framework. |
Hardcoding main(String[] args) in apps, or testing only via curl/browser/IDE run configs | Use bin/dispatcher as the entry point and default dev/test tool for all modules. |
bin/dispatcher is missing, or a hand-written or copied launcher script | Let the framework generate it: run org.tinystruct.system.Dispatcher --version once from the project root (creates the current OS's script). |
bin/dispatcher fails with ClassNotFoundException: org.tinystruct.system.Dispatcher on Windows | The shell script cannot run under Git Bash/MSYS; use bin\dispatcher.cmd or WSL. |
ClassNotFoundException for --import a.A,b.B | One --import per class: --import a.A --import b.B. |
A shell-script launcher that fails with \r: command not found | The file has CRLF endings; convert bin/dispatcher to LF. |
Manual ActionRegistry registration | Prefer the @Action annotation for automatic discovery. |
| Action not found at runtime | Ensure class is imported via --import or listed in application.properties. |
| CLI arg not visible | Pass with --key value; access via getContext().getAttribute("--key"). |
| Two methods same path, wrong one fires | Set explicit mode (e.g., HTTP_GET vs HTTP_POST) to disambiguate. |
bin/dispatcher first: Develop and verify against bin/dispatcher before anything else — run the action from the CLI, confirm behavior, then layer on HTTP/mode concerns. It's the fastest inner-loop and the one tool guaranteed to match how the framework actually routes and binds arguments.init(): Leverage init() for setup (config, DB) rather than the constructor. Do NOT call setAction() — use @Action annotation.Mode parameter in @Action to restrict sensitive operations to CLI only or specific HTTP methods.getContext().getAttribute("--flag") rather than adding parameters to the method signature.CompletableFuture.runAsync() inside the event handler.Detailed guides are available in the references/ directory:
src/main/java/org/tinystruct/AbstractApplication.java — Core base class with lifecycle hookssrc/main/java/org/tinystruct/system/annotation/Action.java — Annotation & Modessrc/main/java/org/tinystruct/application/ActionRegistry.java — Routing Enginesrc/main/java/org/tinystruct/data/component/Builder.java — JSON object serializersrc/main/java/org/tinystruct/data/component/Builders.java — JSON array serializersrc/main/java/org/tinystruct/data/component/AbstractData.java — Base POJO class with CRUDsrc/main/java/org/tinystruct/data/Mapping.java — Mapping metadata (annotations or XML), cached per classsrc/main/java/org/tinystruct/data/annotation/Table.java — @Table, with @Id and @Column alongside itsrc/main/java/org/tinystruct/data/tools/TableCreator.java — Creates missing tables (database.autocreate)src/main/java/org/tinystruct/data/tools/MySQLGenerator.java — POJO generator reference (MappingMode selects XML or annotations)src/main/java/org/tinystruct/data/component/FieldType.java — SQL-to-Java type mappingssrc/main/java/org/tinystruct/data/component/Condition.java — Fluent SQL query buildersrc/main/java/org/tinystruct/http/SSEPushManager.java — SSE connection managementsrc/main/java/org/tinystruct/system/logging/LogFormatter.java — Custom log formatter with ANSI console colors and StackWalker-based caller tracingsrc/main/java/org/tinystruct/system/logging/LoggerConfigurer.java — Programmatic logging configurator from application propertiessrc/test/java/org/tinystruct/application/ActionRegistryTest.java — Registry test examplessrc/test/java/org/tinystruct/system/HttpServerHttpModeTest.java — HTTP integration test patterns还没有评价。欢迎分享你的使用感受。
无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。
还没有评论。来写第一条吧。
依据WCAG 2.2设计和检查Web、iOS及Android界面,核查读屏标签、键盘操作、配色对比度和操作目标尺寸,使界面支持辅助技术。
从指令、记忆、工具执行和界面显示等十二个层面排查人工智能代理故障,依据代码和日志梳理原因,按严重程度列出问题并提出修复方案。
用实际开发任务比较多个编程智能体,记录成功率、可获取的API费用、耗时和重复运行的稳定性,为工具选型及更新后的性能评估提供数据。
设计和调整AI智能体使用的工具、输入与返回格式,明确出错后的恢复、重试和停止条件,管理上下文信息量,并以完成率、重试次数和成功任务成本评估改进。
根据错误信息和执行环境,分析AI代理反复失败或重复操作的原因。通过小范围、可撤销的恢复操作检验假设,并将结果与依据整理成可供交接的报告。
在AI代理反复失败或重复操作时,记录错误与环境前提、排查原因,采取影响有限的恢复操作,并汇总结果和证据,为有依据的重试与交接提供支持。