Skip to content
Open
155 changes: 155 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,161 @@ Worker modules for the [III engine](https://github.com/iii-hq/iii).

## Modules

### mcp

MCP protocol worker — exposes iii-engine functions as MCP tools via stdio and Streamable HTTP.

**Protocol version:** `2025-11-25`

**Features:**
- Dual transport: stdio (Claude Desktop, Cursor) + HTTP (`POST /mcp`)
- 6 built-in tools: worker register/stop, trigger register/unregister/void/enqueue
- 4 MCP resources: `iii://functions`, `iii://workers`, `iii://triggers`, `iii://context`
- 4 MCP prompts: register-function, build-api, setup-cron, event-pipeline
- Metadata filtering: only functions with `mcp.expose: true` are exposed (unless `--expose-all`)
- Spawn Node.js/Python workers on the fly via `iii_worker_register` tool

#### Build

```bash
cd mcp
cargo build --release
```

#### Usage

```bash
iii-mcp # MCP stdio (Claude Desktop, Cursor)
iii-mcp --no-stdio # MCP HTTP only (POST /mcp)
iii-mcp --expose-all # show all functions, ignore metadata filter
```

Tag functions for MCP exposure:
```js
iii.registerFunction({
id: 'orders::process',
metadata: { "mcp.expose": true }
}, handler)
```

#### Testing with MCP Inspector

Use [MCP Inspector](https://github.com/modelcontextprotocol/inspector) to debug and validate the MCP worker interactively.

**Setup:**

Create `mcp-inspector-config.json`:
```json
{
"mcpServers": {
"iii-mcp": {
"command": "./mcp/target/release/iii-mcp",
"args": []
}
}
}
```

**Web UI (interactive):**
```bash
npx @modelcontextprotocol/inspector \
--config mcp-inspector-config.json \
--server iii-mcp
```
Opens a browser at `http://localhost:6274` where you can list tools, call functions, read resources, and test prompts.

**CLI (scriptable):**
```bash
# List tools
npx @modelcontextprotocol/inspector --cli \
--config mcp-inspector-config.json \
--server iii-mcp \
--method tools/list

# Call a tool
npx @modelcontextprotocol/inspector --cli \
--config mcp-inspector-config.json \
--server iii-mcp \
--method tools/call \
--tool-name demo__echo \
--tool-arg 'message=hello'

# List resources
npx @modelcontextprotocol/inspector --cli \
--config mcp-inspector-config.json \
--server iii-mcp \
--method resources/list
```

**End-to-end test:**

```bash
# Terminal 1: start engine
iii --use-default-config

# Terminal 2: start MCP worker
iii-mcp --debug

# Terminal 3: start a test worker with metadata
node -e "
import { registerWorker } from 'iii-sdk'
const iii = registerWorker('ws://localhost:49134')
iii.registerFunction({
id: 'demo::echo',
description: 'Echo input',
metadata: { \"mcp.expose\": true },
request_format: { type: 'object', properties: { message: { type: 'string' } }, required: ['message'] }
}, async (input) => ({ echoed: input.message }))
setInterval(() => {}, 10000)
" --input-type=module

# Terminal 4: validate (wait ~6s for function discovery)
npx @modelcontextprotocol/inspector --cli \
--config mcp-inspector-config.json \
--server iii-mcp \
--method tools/list
# Should show demo__echo alongside the 6 built-in tools
```

> Functions registered without `mcp.expose: true` metadata will not appear in `tools/list` unless `--expose-all` is set. The engine polls for new functions every 5 seconds, so allow a brief delay after registration.

---

### a2a

A2A protocol worker — exposes iii-engine functions as A2A agent skills via HTTP.

**Features:**
- Full A2A type system: AgentCard, Task (8 states), Message, Part, Artifact
- Methods: `message/send`, `tasks/get`, `tasks/cancel`, `tasks/list`
- Agent card at `GET /.well-known/agent-card.json`
- Task state stored via engine KV (`a2a:tasks` scope)
- Metadata filtering: only functions with `a2a.expose: true` are exposed (unless `--expose-all`)

#### Build

```bash
cd a2a
cargo build --release
```

#### Usage

```bash
iii-a2a # A2A HTTP (POST /a2a + GET /.well-known/agent-card.json)
iii-a2a --expose-all # show all functions as skills
```
Comment on lines +145 to +150
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Document the public URL flag in the A2A usage section.

The PR adds a configurable agent-card URL, but this block only shows local/default invocations. Please document --base-url here and clarify whether it expects the public origin or the full /a2a endpoint; otherwise non-local deployments have no guidance for advertising a reachable interface.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 64 - 69, Update the Usage section to document the new
--base-url flag: explain that the flag configures the public origin used to
build the agent-card URL (not the full /a2a path), give a concrete example
invocation like "iii-a2a --base-url https://example.com" and note that the
agent-card will be served at <base-url>/a2a and the /.well-known/agent-card.json
will reference that origin so non-local deployments can advertise a reachable
interface; reference the CLI invocation shown (iii-a2a) and the --base-url flag
so readers can find the relevant behavior.


Tag functions for A2A exposure:
```js
iii.registerFunction({
id: 'orders::process',
metadata: { "a2a.expose": true }
}, handler)
```

---

### image-resize

A Rust-based image resize worker that connects to the III engine via WebSocket and processes images through stream channels.
Expand Down
27 changes: 27 additions & 0 deletions a2a/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[package]
name = "iii-a2a"
version = "0.3.0"
edition = "2024"
description = "A2A protocol worker for iii-engine"
license = "Apache-2.0"
authors = ["Rohit Ghumare <ghumare64@gmail.com>"]
repository = "https://github.com/iii-hq/workers"
homepage = "https://github.com/iii-hq/workers"
rust-version = "1.85"
keywords = ["a2a", "agent-to-agent", "ai", "iii-engine"]
categories = ["command-line-utilities"]

[[bin]]
name = "iii-a2a"
path = "src/main.rs"

[dependencies]
iii-sdk = "0.10.0"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "sync", "time", "signal"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
clap = { version = "4", features = ["derive"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
anyhow = "1"
uuid = { version = "1", features = ["v4"] }
Loading
Loading