Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ target/
.claude/
.idea/
.DS_Store
docs
docsmcp/Cargo.lock
157 changes: 156 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,164 @@
# Motia Workers
# III Workers

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