DEV Community

Riley Zhang
Riley Zhang

Posted on

Weekend Build Log: A $0 Issue Summarizer on a Free Server (Scope Cut Included)

Weekend Build Log: A $0 Issue Summarizer on a Free Server (Scope Cut Included)

Last weekend, I faced a problem. My GitHub issues were piling up. Reading each one took minutes. I wanted a bot to summarize them. But my budget was exactly zero.

I found MonkeyCode's free model access. It also includes a free server option. That combination sounded like a risky bet. I took it anyway.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode is an open-source toolkit for AI-powered workflows. The free tier gives you a model endpoint. The free server hosts small services. Both are enough for a weekend demo. I verified their current limits in the docs. No SLA. No guaranteed uptime. Acceptable for a build log.

Step 1: Cut the Scope

I made a wishlist first. Summarize issues. Add labels. Suggest assignees. Extract keywords. Translate to Spanish. That list was dangerous.

I trimmed it down to one feature: summarize a single issue in under 100 words. Nothing else.

Planned Feature Kept? Why
Single issue summary Core value
Auto labels More model calls
Keyword extraction More parsing logic
Translation More prompts

Cutting scope is the real skill. It reduces cost and time.

Step 2: Write a Thin Model Client

MonkeyCode exposes an OpenAI-compatible API. I wrote a small Python client. The code below is illustrative, not a copy of exact docs.

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.getenv("MONKEY_API_KEY"),
    base_url=os.getenv("MONKEY_BASE_URL")
)

def summarize(title: str, body: str) -> str:
    prompt = f"Summarize this issue in under 100 words.\nTitle: {title}\nBody: {body[:2000]}"
    resp = client.chat.completions.create(
        model="free-model",  # set via env instead
        messages=[{"role": "user", "content": prompt}]
    )
    return resp.choices[0].message.content
Enter fullscreen mode Exit fullscreen mode

I used body[:2000]. This truncation prevents token bloat. Long issues would otherwise flood the model.

Token Budget: Why Truncation Matters

The free tier is not unlimited. Every token costs part of your allowance. A 10,000-word issue could eat thousands of tokens.

I wrote a quick test. Here's the calculation:

  • 1 token ≈ 4 characters in English.
  • 2,000 characters ≈ 500 tokens.
  • 100-word summary ≈ 150 tokens.
  • Total per issue ≈ 650 tokens.

That fits a modest free quota. Without truncation, one issue could consume a day's budget.

But I don't know the exact quota today. So I check the dashboard before every session. You should too.

Step 3: Iterate on the Prompt

The first prompt gave vague summaries. I tried two rewrites.

First version: Summarize this issue. Result: everything sounded urgent.

Second version: Summarize this issue. Use three bullet points. Result: better structure, but too long.

Final version: Summarize this issue in under 100 words. Focus on the problem, not the suggested fix. That worked best.

Prompt Result
"Summarize this issue." Vague, verbose
"Use three bullet points." Too long, structured
"Focus on the problem." Concise, useful

I kept the last one.

Step 4: Build the Webhook Receiver

Next, I needed a listener. I used Flask. It receives GitHub's issues webhook.

from flask import Flask, request
from my_summarizer import summarize

app = Flask(__name__)

@app.route("/webhook", methods=["POST"])
def webhook():
    data = request.json
    if data.get("action") != "opened":
        return "ok", 200

    issue = data["issue"]
    summary = summarize(issue["title"], issue.get("body") or "")
    print("SUMMARY:", summary)
    return ("ok", 200)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)
Enter fullscreen mode Exit fullscreen mode

I skipped authentication. That kept the demo simple. For production, add a shared secret.

Test it in a terminal:

curl -X POST http://localhost:5000/webhook \
  -H "Content-Type: application/json" \
  -d '{"action":"opened","issue":{"title":"Bug","body":"Crash on login"}}'
Enter fullscreen mode Exit fullscreen mode

You should see a summary in the logs.

Step 5: Deploy to the Free Server

MonkeyCode's free server runs Docker containers. I wrote a minimal Dockerfile.

FROM python:3.12-slim
COPY . /app
WORKDIR /app
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

I set environment variables for credentials. Then I started the container. The first request was slow. Later requests were faster. That matches a cold start.

I pointed my repo's webhook at the server. Then I opened a test issue. The bot summarized it in about four seconds. Good enough.

What I Skipped (and Why)

  • Retry logic — I planned exponential backoff. I cut it. The demo survived.
  • Persistent storage — Summaries go to logs. No database.
  • Frontend — No dashboard. Logs are the UI.
  • Status page — I didn't need one.

Those cuts kept the project under one day.

Limitations and Who Should Not Use This

The free tier has real limitations. Here's what you accept:

  • No uptime guarantee. The server may sleep or recycle.
  • Rate limits. Too many requests return 429.
  • Data privacy. Issue text goes to a third-party model.
  • Hallucination risk. I saw one fabricated bug mention.

Avoid this approach if:

  • You handle sensitive data.
  • You need a strict SLA.
  • You process dozens of issues per hour.

For a one-day prototype? It works.

Final Take

The scope cut was the win. I shipped a working bot in one day. The cost was zero.

MonkeyCode's free model access and free server made this possible. The tool doesn't hide its limits. That's refreshing.

If you want to try this pattern, start tiny. One feature. One prompt. One endpoint. Cut ruthlessly.

That's the real skill.

Top comments (0)