Skip to content

Commit 9389479

Browse files
authored
Merge pull request #122 from OpenAF/codex/create-mcp-server-for-ollama-api
Codex-generated pull request
2 parents 357458c + 674a71b commit 9389479

8 files changed

Lines changed: 566 additions & 70 deletions

File tree

CHEATSHEET.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ mini-a mode=chatbot goal="help me plan a trip"
546546
# Use web mode
547547
./mini-a-web.sh mode=web onport=8888
548548

549-
# Custom preset (in ~/.openaf-mini-a_modes.yaml)
549+
# Custom preset (in ~/.openaf-mini-a/modes.yaml)
550550
modes:
551551
mypreset:
552552
useshell: true

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ The tester includes automatic cleanup with shutdown handlers to properly close M
344344
- **Autonomous Delegation** - LLM decides when to delegate via `delegate-subtask` tool
345345
- **Manual Delegation** - Console commands for interactive control (`/delegate`, `/subtasks`, `/subtask`)
346346
- **Depth Tracking** - Configurable nesting limits with automatic retry and deadline enforcement
347-
- **Conversation Persistence** - Save and resume conversations across sessions (`conversation=...`; in `mini-a-con`, use `resume=true` to continue the latest thread)
347+
- **Conversation Persistence** - Save and resume conversations across sessions (`conversation=...`; in `mini-a-con`, combine `usehistory=true`, `historykeep=true`, and `resume=true` to pick and continue prior console threads stored under `~/.openaf-mini-a/history/`; use `historykeepperiod=` and/or `historykeepcount=` for retention)
348348
- **Rate Limiting** - Built-in rate limiting for API usage control
349349
- **Metrics & Observability** - Comprehensive runtime metrics for monitoring and cost tracking
350350
- **ASCII Sketch Guidance** - Encourage text-based sketch outputs in responses (`useascii=true`)
@@ -386,7 +386,7 @@ Mini-A ships with complementary components:
386386
- **`mini-a-subtask.js`** - SubtaskManager for local child-agent delegation and remote worker delegation
387387
- **`mini-a-web.sh` / `mini-a-web.yaml`** - Lightweight HTTP server for browser UI
388388
- **`mini-a-worker.yaml`** - Headless HTTP API server for programmatic agent delegation (launch with `mini-a workermode=true`)
389-
- **`mini-a-modes.yaml`** - Built-in configuration presets for common use cases (can be extended with `~/.openaf-mini-a_modes.yaml`)
389+
- **`mini-a-modes.yaml`** - Built-in configuration presets for common use cases (can be extended with `~/.openaf-mini-a_modes.yaml` or `~/.openaf-mini-a/modes.yaml`)
390390
- **`public/`** - Browser interface assets
391391

392392
## Common Configuration Options
@@ -422,7 +422,7 @@ Mini-A ships with complementary components:
422422
| `usemaps` | Encourage Leaflet-based interactive map outputs for geographic data | `false` |
423423
| `usemath` | Encourage LaTeX-style math formulas (`$...$`, `$$...$$`) for KaTeX rendering in the web UI | `false` |
424424
| `usestream` | Enable real-time token streaming as LLM generates responses | `false` |
425-
| `mode` | Apply preset from `mini-a-modes.yaml` or `~/.openaf-mini-a_modes.yaml` | - |
425+
| `mode` | Apply preset from `mini-a-modes.yaml`, `~/.openaf-mini-a_modes.yaml`, or `~/.openaf-mini-a/modes.yaml` | - |
426426
| `modelman` | Launch the interactive model definitions manager | `false` |
427427
| `workermode` | Launch the Worker API server (`mini-a-worker.yaml`) from the console entrypoint | `false` |
428428
| `workers` | Comma-separated list of worker URLs for remote delegation (`workers=http://host1:8080,http://host2:8080`) | - |

USAGE.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ oJob wrappers (for example `ojob mini-a.yaml modelman=true`).
324324

325325
## Mode Presets
326326

327-
Mini-A ships with reusable argument bundles so you can switch behaviors without remembering every flag. Pass `mode=<name>` with `opack exec mini-a`, `mini-a`, `mini-a.sh`, `mini-a.yaml`, or `mini-a-main.yaml` and the runtime will merge the corresponding preset from [`mini-a-modes.yaml`](mini-a-modes.yaml) and optionally from `~/.openaf-mini-a_modes.yaml` (custom modes override built-in ones) before applying any explicit flags you provide on the command line.
327+
Mini-A ships with reusable argument bundles so you can switch behaviors without remembering every flag. Pass `mode=<name>` with `opack exec mini-a`, `mini-a`, `mini-a.sh`, `mini-a.yaml`, or `mini-a-main.yaml` and the runtime will merge the corresponding preset from [`mini-a-modes.yaml`](mini-a-modes.yaml) and optionally from `~/.openaf-mini-a_modes.yaml` and `~/.openaf-mini-a/modes.yaml` (custom modes override built-in ones, and `~/.openaf-mini-a/modes.yaml` overrides the legacy file when both exist) before applying any explicit flags you provide on the command line.
328328

329329
Set `OAF_MINI_A_MODE=<name>` to pick a default preset when you do not supply `mode=` on the command line (helpful when using the `mini-a` alias). Explicit `mode=` arguments continue to take precedence over the environment variable.
330330

@@ -340,12 +340,12 @@ Set `OAF_MINI_A_MODE=<name>` to pick a default preset when you do not supply `mo
340340

341341
### Creating Custom Presets
342342

343-
Create your own presets by creating a `~/.openaf-mini-a_modes.yaml` file in your home directory. Custom modes are automatically merged with the built-in presets from `mini-a-modes.yaml`, with custom definitions taking precedence. The agent loads both YAML files on each run, so custom additions and overrides are immediately available.
343+
Create your own presets by creating either a `~/.openaf-mini-a_modes.yaml` file or a `~/.openaf-mini-a/modes.yaml` file in your home directory. Custom modes are automatically merged with the built-in presets from `mini-a-modes.yaml`, with custom definitions taking precedence. If both custom files exist, `~/.openaf-mini-a/modes.yaml` overrides the legacy file. The agent loads these YAML files on each run, so custom additions and overrides are immediately available.
344344

345345
**Example custom preset:**
346346

347347
```yaml
348-
# In ~/.openaf-mini-a_modes.yaml
348+
# In ~/.openaf-mini-a/modes.yaml
349349
modes:
350350
mypreset:
351351
useshell: true
@@ -766,7 +766,7 @@ The `start()` method accepts various configuration options:
766766
- **`showthinking`** (boolean, default: false): Use raw prompt calls to surface XML-tagged thinking blocks (for example `<thinking>...</thinking>`) as thought logs
767767
- **`chatbotmode`** (boolean, default: false): Replace the agent workflow with a lightweight conversational assistant prompt
768768
- **`useplanning`** (boolean, default: false): Load (or maintain) a persistent task plan in agent mode. When no pre-generated plan is available Mini-A falls back to the adaptive in-session planner and disables the feature for trivial goals.
769-
- **`mode`** (string): Apply a preset from [`mini-a-modes.yaml`](mini-a-modes.yaml) or `~/.openaf-mini-a_modes.yaml` to prefill a bundle of related flags
769+
- **`mode`** (string): Apply a preset from [`mini-a-modes.yaml`](mini-a-modes.yaml), `~/.openaf-mini-a_modes.yaml`, or `~/.openaf-mini-a/modes.yaml` to prefill a bundle of related flags
770770

771771
#### Dual-Model Controls
772772
- **`modellc`** (string): Override the low-cost model configuration at runtime (same format as `OAF_LC_MODEL`). Useful for quick per-run model selection without changing environment variables.
@@ -925,11 +925,16 @@ Only when every stage returns an empty list (or errors) does Mini-A log the issu
925925

926926
#### Conversation Management
927927
- **`conversation`** (string): Path to file for loading/saving conversation history
928-
- **`resume`** (boolean, default: false, `mini-a-con`): Reuse the last conversation and restore the most recent goal/result context when running from the interactive console (`mini-a` / `opack exec mini-a`)
928+
- **`resume`** (boolean, default: false, `mini-a-con`): Resume a previous console conversation. When `conversation` is omitted and `usehistory=true`, the console lists `~/.openaf-mini-a/history/*.json` and lets you choose which thread to continue.
929+
- **`usehistory`** (boolean, default: false, `mini-a-con`): Enable console history discovery from `~/.openaf-mini-a/history/`.
930+
- **`historykeep`** (boolean, default: false, `mini-a-con`): Store console conversation files in `~/.openaf-mini-a/history/` using `conversation-yyyyMMdd-HHmmss.json` style names.
931+
- **`historykeepperiod`** (number, `mini-a-con`): Automatically delete kept conversation files older than the provided number of minutes.
932+
- **`historykeepcount`** (number, `mini-a-con`): Automatically delete kept conversation files beyond the newest N entries.
933+
- When both `historykeepperiod` and `historykeepcount` are set, either rule can prune a saved conversation file.
929934
- **`state`** (object|string): Initial structured state (JSON/SLON) injected before the first step and persisted across turns
930935

931936
#### Mode Presets
932-
- **`mode`** (string): Shortcut for loading a preset argument bundle from [`mini-a-modes.yaml`](mini-a-modes.yaml) or `~/.openaf-mini-a_modes.yaml` (custom modes override built-in ones). Presets are merged before explicit flags, so command-line overrides always win. Bundled configurations include:
937+
- **`mode`** (string): Shortcut for loading a preset argument bundle from [`mini-a-modes.yaml`](mini-a-modes.yaml), `~/.openaf-mini-a_modes.yaml`, or `~/.openaf-mini-a/modes.yaml` (custom modes override built-in ones, and the new path overrides the legacy one when both exist). Presets are merged before explicit flags, so command-line overrides always win. Bundled configurations include:
933938
- `shell` – Enables read-only shell access.
934939
- `shellrw` – Enables shell access with write permissions (`readwrite=true`).
935940
- `shellutils` – Adds the Mini File Tool helpers as an MCP (`useutils=true mini-a-docs=true usetools=true`) exposing `init`, `filesystemQuery`, `filesystemModify`, and `markdownFiles` actions.
@@ -2259,6 +2264,14 @@ Resume the last conversation directly from the interactive console:
22592264
mini-a conversation=chat-history.json resume=true
22602265
```
22612266

2267+
Or keep console sessions in the default history folder and choose one interactively when resuming:
2268+
2269+
```bash
2270+
mini-a usehistory=true historykeep=true resume=true
2271+
```
2272+
2273+
Console history payloads now keep both `created_at` and `updated_at` timestamps in addition to the conversation entries, which makes retention and manual inspection easier.
2274+
22622275
### Context Management
22632276

22642277
```javascript
@@ -2427,6 +2440,7 @@ This will show:
24272440
Mini-A records extensive counters that help track behaviour and costs:
24282441

24292442
- Call `agent.getMetrics()` to obtain a snapshot grouped by LLM usage, outcomes, shell approvals/denials, context management, and summarization activity.
2443+
- Console history flows also update `history` metrics so you can track started/resumed sessions plus kept/deleted conversation files when using `mini-a-con`, including separate counters for age-based and count-based pruning.
24302444
- OpenAF automatically registers these counters under the `mini-a` namespace via `ow.metrics.add('mini-a', ...)`, so collectors that understand OpenAF metrics can scrape them.
24312445
- Metrics are updated live as the agent progresses, making them ideal for dashboards or alerting when an agent gets stuck.
24322446

@@ -2447,6 +2461,7 @@ Mini-A records extensive counters that help track behaviour and costs:
24472461
| `tool_cache` | `hits`, `misses`, `total_requests`, `hit_rate` | Tool result caching metrics for deterministic and read-only MCP tools. Tracks cache effectiveness and provides hit rate percentage. |
24482462
| `mcp_resilience` | `circuit_breaker_trips`, `circuit_breaker_resets`, `lazy_init_success`, `lazy_init_failed` | MCP resilience and optimization metrics. Circuit breaker trips/resets track connection health management. Lazy initialization metrics show deferred MCP connection establishment when `mcplazy=true`. |
24492463
| `delegation` | `total`, `running`, `completed`, `failed`, `cancelled`, `timedout`, `retried`, `workers_total`, `workers_static`, `workers_dynamic`, `workers_healthy`, `avgDurationMs`, `maxDepthUsed` | Subtask delegation metrics (when `usedelegation=true`). Tracks child agent lifecycle, retries, worker pool composition/health, average duration, and deepest nesting level reached. |
2464+
| `history` | `sessions_started`, `sessions_resumed`, `files_kept`, `files_deleted`, `files_deleted_by_period`, `files_deleted_by_count` | Console conversation history metrics. Tracks new vs resumed console sessions, how many history files were kept, and how many were pruned overall, by age rule, or by count rule. |
24502465

24512466
These counters mirror what is exported via `ow.metrics.add('mini-a', ...)`, so the same structure appears in Prometheus/Grafana when scraped through OpenAF.
24522467

mcps/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
| mcp-s3 | S3 object storage MCP | STDIO/HTTP | (included) | [mcp-s3.yaml](mcp-s3.yaml) |
2626
| mcp-telco | Telecommunications utilities MCP (country codes, phone number info) | STDIO/HTTP | (included) | [mcp-telco.yaml](mcp-telco.yaml) |
2727
| mcp-weather | Weather information MCP (wttr.in) | STDIO/HTTP | (included) | [mcp-weather.yaml](mcp-weather.yaml) |
28+
| mcp-ollama-web-search | Ollama web search API MCP | STDIO/HTTP | (included) | [mcp-ollama-web-search.yaml](mcp-ollama-web-search.yaml) |
2829
| mcp-web | Web search and URL fetching MCP | STDIO/HTTP | (included) | [mcp-web.yaml](mcp-web.yaml) |
2930
| mcp-math | Mathematical operations and unit conversions | STDIO/HTTP | (included) | [mcp-math.yaml](mcp-math.yaml) |
3031

mcps/mcp-mini-a.yaml

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -243,25 +243,29 @@ jobs:
243243
244244
// Load custom modes from user's home directory
245245
var modesHome = isDef(__gHDir) ? __gHDir() : java.lang.System.getProperty("user.home")
246-
var customModesPath = resolveCanonicalPath(modesHome, ".openaf-mini-a_modes.yaml")
247-
if (io.fileExists(customModesPath)) {
248-
try {
249-
var customLoaded = io.readFileYAML(customModesPath)
250-
var customPresets = {}
251-
if (isMap(customLoaded) && isMap(customLoaded.modes)) {
252-
customPresets = customLoaded.modes
253-
} else if (isMap(customLoaded)) {
254-
customPresets = customLoaded
246+
;[
247+
resolveCanonicalPath(modesHome, ".openaf-mini-a_modes.yaml"),
248+
resolveCanonicalPath(modesHome, ".openaf-mini-a/modes.yaml")
249+
].forEach(function(customModesPath) {
250+
if (io.fileExists(customModesPath)) {
251+
try {
252+
var customLoaded = io.readFileYAML(customModesPath)
253+
var customPresets = {}
254+
if (isMap(customLoaded) && isMap(customLoaded.modes)) {
255+
customPresets = customLoaded.modes
256+
} else if (isMap(customLoaded)) {
257+
customPresets = customLoaded
258+
}
259+
// Merge custom modes with default modes (later custom files override earlier ones)
260+
if (isMap(customPresets) && Object.keys(customPresets).length > 0) {
261+
presets = merge(presets, customPresets)
262+
}
263+
} catch(e) {
264+
var errMsg = (isDef(e) && isString(e.message)) ? e.message : e
265+
logWarn("Failed to load custom mode presets from '" + customModesPath + "': " + errMsg)
255266
}
256-
// Merge custom modes with default modes (custom overrides defaults)
257-
if (isMap(customPresets) && Object.keys(customPresets).length > 0) {
258-
presets = merge(presets, customPresets)
259-
}
260-
} catch(e) {
261-
var errMsg = (isDef(e) && isString(e.message)) ? e.message : e
262-
logWarn("Failed to load custom mode presets from '" + customModesPath + "': " + errMsg)
263267
}
264-
}
268+
})
265269
266270
if (!isMap(presets) || isUnDef(presets[modeName])) {
267271
logWarn("Mode '" + modeName + "' requested but not found in presets.")

mcps/mcp-ollama-web-search.yaml

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Author: OpenAI Assistant
2+
help:
3+
text : A STDIO/HTTP MCP server for the Ollama web search API
4+
expects:
5+
- name : apiKey
6+
desc : Ollama API key (can also be provided via OLLAMA_API_KEY env var)
7+
mandatory: true
8+
- name : baseUrl
9+
desc : Ollama API base URL
10+
example : "https://ollama.com"
11+
mandatory: false
12+
- name : onport
13+
desc : If defined starts an HTTP MCP server on this port; otherwise uses STDIO
14+
example : "8888"
15+
mandatory: false
16+
17+
18+
todo:
19+
- Init
20+
- (if ): "isDef(args.onport)"
21+
((then)):
22+
- (httpdStart ): "${onport:-8080}"
23+
- (httpdHealthz ): "${onport:-8080}"
24+
- (httpdMetrics ): "${onport:-8080}"
25+
- (httpdMCP ): "${onport:-8080}"
26+
((description)): &MCPSERVER
27+
serverInfo:
28+
name : mini-a-ollama-web-search
29+
title : OpenAF mini-a MCP Ollama web search server
30+
version: 1.0.0
31+
32+
((fnsMeta)): &MCPFNSMETA
33+
web-search:
34+
name : web-search
35+
description: Search the web through Ollama's web_search API endpoint.
36+
inputSchema:
37+
type : object
38+
properties:
39+
query:
40+
type : string
41+
description: Search query to send to Ollama.
42+
options:
43+
type : object
44+
description: Optional extra API payload fields forwarded as-is.
45+
required: [ query ]
46+
annotations:
47+
title : web-search
48+
readOnlyHint : true
49+
idempotentHint: true
50+
51+
((fns )): &MCPFNS
52+
web-search: Ollama web search
53+
54+
((else)):
55+
- (stdioMCP ): *MCPSERVER
56+
((fnsMeta)): *MCPFNSMETA
57+
((fns )): *MCPFNS
58+
59+
ojob:
60+
opacks :
61+
- openaf : 20250915
62+
- oJob-common: 20250914
63+
catch : printErrnl("[" + job.name + "] "); $err(exception, __, __, job.exec)
64+
logToConsole: false
65+
argsFromEnvs: true
66+
daemon : true
67+
unique :
68+
pidFile : .mcp-ollama-web-search.pid
69+
killPrevious: true
70+
71+
include:
72+
- oJobMCP.yaml
73+
74+
jobs:
75+
# -----------
76+
- name : Init
77+
check:
78+
in:
79+
apiKey : isString.default(__)
80+
baseUrl: isString.default("https://ollama.com")
81+
exec : | #js
82+
if (isUnDef(global.__ollamaWebSearchConfig)) {
83+
if (isUnDef(args.apiKey) && isDef(__envs.OLLAMA_API_KEY)) args.apiKey = __envs.OLLAMA_API_KEY
84+
_$(args.apiKey, "apiKey").isString().$_()
85+
86+
global.__ollamaWebSearchConfig = {
87+
baseUrl: String(args.baseUrl).replace(/\/$/, ""),
88+
apiKey : args.apiKey
89+
}
90+
}
91+
92+
# ------------------------
93+
- name : Ollama web search
94+
from : Init
95+
check:
96+
in:
97+
query : isString
98+
options: isMap.default({})
99+
exec : | #js
100+
var payload = merge({ query: args.query }, args.options)
101+
var cfg = global.__ollamaWebSearchConfig
102+
103+
var rest = $rest({
104+
requestHeaders: {
105+
"Authorization": "Bearer " + cfg.apiKey,
106+
"Content-Type" : "application/json"
107+
}
108+
})
109+
110+
return rest.post(cfg.baseUrl + "/api/web_search", payload)

0 commit comments

Comments
 (0)