Examples Stock Analyzer — zero custom ops
Library only

Stock Analyzer

A live-data workflow built without a single custom operator. Two parallel HTTPGetOp calls hit Yahoo Finance for a quote and a news headline. JSONExtractOp pulls fields out of the responses. AIParseNumberOp coerces stringly-typed numerics to float64. SubFloatOp computes the price change. AIScoreOp turns the headline into a sentiment score. A chain of StringConcatOp assembles the final analysis prompt, and AIComputeStringToStringOp emits the Buy/Hold/Sell recommendation.

What this example teaches

  • Zero custom operators — the whole workflow is library ops only
  • Dynamic URL building via RegisterConst + StringConcatOp chains
  • Parallel HTTPGetOp + JSONExtractOp for two independent fetches
  • AIParseNumberOp as a fallback when the source returns stringly-typed numerics
  • Mixing deterministic math (SubFloatOp) with AI scoring (AIScoreOp) in one graph
  • Per-vertex provider selection — every AI op pins provider: "gemini"
View source on GitHub
The workflow

Two parallel branches, one synthesis

det op

Deterministic steps

RegisterConst (×11)

URL prefixes/suffixes, JSON paths, and prompt fragments registered as constant ops.

StringConcatOp chains

Assemble the quote URL, news URL, and the final analysis prompt by chaining concats.

HTTPGetOp (×2 parallel)

Independent fetches for quote and news. Scheduled concurrently by the engine.

JSONExtractOp (×3)

Dot-path extraction of regularMarketPrice, chartPreviousClose, and the latest news title.

SubFloatOp + Float64ToStringOp

Compute and stringify the price change since previous close — pure arithmetic, zero tokens.

AI op

AI steps

AIParseNumberOp (×2)

Coerce the price strings to float64 — fallback for cases where the JSON value isn't a clean number.

AIScoreOp

Score the headline 0–1 against the criterion "indicates a positive/bullish outlook".

AIComputeStringToStringOp

Synthesises the Buy/Hold/Sell recommendation from the assembled prompt with all numeric inputs filled in.

Implementation

Walkthrough

1. Building URLs with RegisterConst + StringConcatOp

RegisterConst turns a fixed Go value into a registered op. Combine with the ticker context-val and two StringConcatOp vertices to build a per-run URL without any custom code.

main.go — init() + buildGraph()
library.RegisterConst("quote_prefix", "https://query2.finance.yahoo.com/v8/finance/chart/")
library.RegisterConst("quote_suffix", "?interval=1d&range=1d")

b.Vertex("q_join_1").Op("StringConcatOp").
    Input("A", "q_pre").Input("B", "ticker").Output("Result", "q_mid")
b.Vertex("quote_url").Op("StringConcatOp").
    Input("A", "q_mid").Input("B", "q_suf").Output("Result", "quote_url")

2. Parallel fetch + JSON extraction

Two independent HTTPGetOp vertices. The engine sees they share no upstream dependency and runs them concurrently. Three JSONExtractOp vertices then pull the three numeric/string fields the downstream stages need.

main.go — buildGraph()
b.Vertex("fetch_quote").Op("HTTPGetOp").Input("URL", "quote_url").Output("Body", "quote_json")
b.Vertex("fetch_news").Op("HTTPGetOp").Input("URL", "news_url").Output("Body", "news_json")

b.Vertex("extract_price").Op("JSONExtractOp").
    Input("JSON", "quote_json").Input("Path", "p_path").
    Output("Value", "price_raw")

3. Mixing deterministic math and AI scoring

Once the price floats are parsed, the change is one SubFloatOp. The headline's sentiment is one AIScoreOp. Two ops, one a pure function and one a typed AI call, both produce float64 wires the downstream prompt-assembly stage stringifies and concatenates.

main.go — buildGraph()
b.Vertex("calc_change").Op("SubFloatOp").
    Input("A", "price").Input("B", "prev_close").Output("Result", "change")

b.Vertex("sentiment").Op("AIScoreOp").
    Params(map[string]string{
        "provider":  "gemini",
        "model":     "gemini-3-flash-preview",
        "criterion": "The headline indicates a positive/bullish outlook",
    }).
    Input("Input", "headline").Output("Result", "sentiment_score")

Run it

shell
export GEMINI_API_KEY=<your key>

# default ticker is AAPL
go run ./examples/stock-analyzer

# or specify your own
go run ./examples/stock-analyzer --ticker MSFT

Run it as an MCP server

The same binary is dual-mode. Pass --mcp and instead of running once and exiting it speaks the Model Context Protocol over stdin/stdout, exposing the entire workflow as a single MCP tool, analyze_stock. The Go SDK derives the tool's input schema from the workflow's UserInput struct and validates every tools/call request against it, so an agent runs the whole quote + news → sentiment → recommendation pipeline with one tool call.

shell
# Speak MCP over stdin/stdout instead of running once
go run ./examples/stock-analyzer --mcp

Register it with any MCP client by pointing the client at that command:

.mcp.json
{
  "mcpServers": {
    "stock-analyzer": {
      "command": "go",
      "args": ["run", "./examples/stock-analyzer", "--mcp"]
    }
  }
}
← Remote MCP Next: RAG (BM25) →