Skip to main content
Glama
pypi-ahmad

Local Intelligence Server

by pypi-ahmad

Local Intelligence MCP Server

Offline server-side LLM analysis over the Model Context Protocol

A production-shaped educational project that exposes one MCP tool — analyze_data — which takes arbitrary text plus a natural-language instruction, runs analysis on a local Ollama model (qwen3.5:4b), and returns the result to the MCP client. No cloud LLM calls. No embeddings. No vector database. Inference stays on 127.0.0.1:11434.

Field

Value

Protocol

MCP via official Python SDK (mcp[cli])

Server name

Local Intelligence Server

Tool

analyze_data(raw_text: str, instruction: str) -> str

Model

qwen3.5:4b (Ollama)

Sampling

temperature=0, think=False

Runtime

Python 3.13.13 · uv only

Last verified real run

2026-07-11 08:57:55 UTC · quality gate passed


Who this README is for

Audience

What you get

Portfolio / technical reviewers

Architecture decisions with tradeoffs, reproducibility path, real captured outputs, honest limitations

Hands-on operators

Install, run, demo CLI, Inspector, troubleshooting, how to extend tools

Tutorial learners

MCP concepts, request lifecycle, why this is not RAG, line-by-line implementation flow


Related MCP server: MCP LLM Integration Server

Table of contents

  1. Problem statement

  2. What this project does (and does not do)

  3. Architecture

  4. Architecture decisions (for reviewers)

  5. Conceptual walkthrough (for learners)

  6. Implementation flow

  7. Project layout

  8. Requirements

  9. Installation

  10. Operation

  11. Sample data

  12. Real run — evidence

  13. Troubleshooting

  14. Extending the server

  15. Limitations and known issues

  16. Dependencies

  17. License


1. Problem statement

MCP clients (Claude Desktop, Cursor, custom agents, the MCP Inspector) are excellent at orchestration, but they are not always the right place to hold large raw payloads or to spend tokens re-summarizing noisy logs. Two common patterns emerge:

  1. Client-side analysis — the host model receives the full log dump in its context window, burns tokens, and may leak sensitive lines into a cloud provider.

  2. Cloud tool backends — the MCP server forwards text to a remote API; convenient, but data leaves the machine and costs scale with volume.

This project implements a third pattern:

Server-side local intelligence: the MCP server receives raw_text + instruction, calls a local LLM, and returns a compact analysis string. The client works with the result, not necessarily the full dump.

That pattern is useful for log triage, entity extraction, reformatting messy text, and any workflow where “preprocess offline on the machine that holds the data” matters more than multi-document retrieval.


2. What this project does (and does not do)

Does

  • Run an MCP server named Local Intelligence Server

  • Register exactly one tool: analyze_data

  • Build a fixed chat prompt (system + user) and call Ollama locally

  • Return analysis as a string; convert exceptions into error strings so the client still gets a tool result

  • Provide a reproducible demo (run_demo.py) against real public Loghub samples

  • Document verbatim model outputs from a timed local run

Does not

  • Call OpenAI / Anthropic / any remote chat API

  • Use embeddings (nomic-embed-text, qwen3-embedding, etc.)

  • Implement RAG (chunking, vector index, retriever, reranker)

  • Fine-tune models or use Unsloth / LoRA

  • Expose multiple tools, resources, or prompt templates (Phase 1 scope)

If you need multi-document grounding over a private corpus, that is a different architecture (see §5.4 Why this is not RAG). This repo deliberately stays single-pass.


3. Architecture

┌──────────────────────────────────────────────────────────────────┐
│  MCP Client                                                       │
│  (Inspector UI · Claude Desktop · custom ClientSession · agent)   │
└───────────────────────────────┬──────────────────────────────────┘
                                │  MCP over stdio
                                │  tools/list · tools/call
                                ▼
┌──────────────────────────────────────────────────────────────────┐
│  server.py                                                        │
│  FastMCP("Local Intelligence Server")                             │
│                                                                   │
│  @mcp.tool()                                                      │
│  def analyze_data(raw_text, instruction) -> str                   │
│        │                                                          │
│        ├─ system: "You are a precise data analysis assistant."    │
│        ├─ user:   f"{instruction}\n\n{raw_text}"                  │
│        │                                                          │
│        └─ ollama.chat(                                            │
│             model="qwen3.5:4b",                                   │
│             options={"temperature": 0},                           │
│             think=False,                                          │
│           )                                                       │
│        → response["message"]["content"]  (or error string)        │
└───────────────────────────────┬──────────────────────────────────┘
                                │  HTTP POST /api/chat
                                │  127.0.0.1:11434 only
                                ▼
                     ┌─────────────────────┐
                     │  Ollama daemon       │
                     │  qwen3.5:4b weights  │
                     └─────────────────────┘

Component responsibilities

Layer

Component

Responsibility

Client

Inspector / agent

Discover tools, send arguments, display result

Transport

MCP stdio

JSON-RPC messages between client and server process

Server

FastMCP + analyze_data

Validate surface, build prompt, call Ollama, shape errors

Runtime

Ollama

Load model, generate tokens, never leave localhost

Demo harness

run_demo.py

Feed Loghub slices, call tool + MCP client, write artifacts/

Request lifecycle (one tool call)

  1. Client sends tools/call with name=analyze_data and {raw_text, instruction}.

  2. FastMCP deserializes arguments into the Python function.

  3. The function builds a two-message chat (system + user).

  4. ollama.chat posts to the local daemon; generation runs entirely on-device.

  5. Server returns the assistant content string as the tool result text.

  6. Client renders that text; raw logs need not remain in the client’s long-term context.


4. Architecture decisions (for reviewers)

Each decision below is intentional. Alternatives that were rejected are listed so the design can be evaluated, not just described.

D1 — Official mcp[cli] FastMCP, not the standalone fastmcp package

Chosen

Rejected

from mcp.server.fastmcp import FastMCP after uv add "mcp[cli]"

Third-party PyPI package named fastmcp (different maintainer lineage)

Why: Tutorial and SDK docs refer to FastMCP bundled inside the official MCP Python SDK. Installing both packages confuses imports and versions. Verified import path against installed mcp 1.28.1.

D2 — Single tool, single pass

Chosen

Rejected

One tool analyze_data

Tool sprawl (separate summarize / extract / format tools) or multi-agent graphs

Why: Phase 1 teaches “LLM inside an MCP tool.” One well-described tool with free-form instruction covers summarize, extract, format, and pattern-finding without combinatorial API surface. Extra tools can be added later (see §14).

D3 — Local Ollama + fixed model qwen3.5:4b

Chosen

Rejected

qwen3.5:4b only

Cloud chat APIs; silent fallbacks to other tags; embedding models

Why: Portfolio consistency with other local projects; 4B fits common laptop GPUs/CPU budgets; no API keys in the critical path. The model name is pinned in code so demos do not silently drift.

D4 — temperature=0 and think=False

Setting

Rationale

Evidence

temperature=0

Log triage is a factual task; lower sampling variance

Stable structure across direct vs MCP re-run on same Apache input

think=False

Default Qwen3.5 CoT can exhaust the generation budget and return empty message.content while still HTTP 200

A/B on Apache case: default think → 0 content chars / ~39 s; think=False1790 chars / ~7 s (2026-07-11)

Empty content is treated as an error string so clients never receive a silent blank “success.”

D5 — Errors as return values, not raised exceptions

except Exception as e:
    return f"Error calling local LLM: {e}"

Why: MCP tool failures that raise often surface as opaque transport errors. Returning a string keeps the tool contract (-> str) and gives the client something to show the user (e.g. “connection refused to Ollama”).

D6 — uv-only environment, Python 3.13.13

Chosen

Rejected

uv venv, uv add, uv run, pin 3.13.13

Raw pip + python -m venv as the primary path

Why: Reproducible lockfile (uv.lock), consistent with the rest of the local portfolio toolchain.

D7 — No RAG in this repo

Retrieval adds index management, chunk quality, embedding cost, and a second failure mode (retrieve-wrong → generate-confidently-wrong). For single blob + instruction analysis, stuffing the blob into the user message is the simplest correct design. Reviewers should judge this as scoped correctness, not missing features.

Decision summary matrix

Concern

Decision

Primary tradeoff

SDK

Official mcp[cli]

Slightly heavier install than a micro-server

Model

Local 4B Qwen

Weaker than frontier cloud models; fully private

Decoding

temp 0, no think

Less creative; more reliable content field

Scope

One tool

Less API surface; less specialization

Data plane

Logs in tool args

Fine for KB-scale slices; not for multi-GB dumps


5. Conceptual walkthrough (for learners)

5.1 What is MCP?

The Model Context Protocol is a standard way for AI applications (hosts/clients) to talk to capability servers. A server can expose:

Primitive

Role

Used here?

Tools

Callable functions with schemas (side effects or computation)

Yesanalyze_data

Resources

Readable data URIs (files, records)

No

Prompts

Reusable prompt templates

No

Think of MCP like a USB standard for AI tools: the client does not hard-code every vendor API; it lists tools, reads JSON schemas, and calls them.

5.2 Client vs server vs model

Piece

Runs where

Knows about

MCP client

Host app

User intent, tool selection, conversation UI

MCP server (server.py)

Separate process (stdio)

How to call Ollama, how to shape the tool

LLM

Ollama process

Tokens in → tokens out; no MCP awareness

The model never “speaks MCP.” The server translates MCP tool calls into ollama.chat and back.

5.3 What “server-side analysis” means

Without this server, a typical flow is:

User → Host LLM → (sees full logs in prompt) → answer

With this server:

User → Host LLM → tools/call analyze_data(raw_text, instruction)
                      → Local LLM analyzes
                      → compact string returns
                 → Host LLM uses summary for the user

Benefits:

  • Privacy: raw lines stay on the machine running Ollama.

  • Token budget: host context holds analysis, not megabytes of logs (when the host chooses not to re-paste them).

  • Specialization: the tool’s system prompt is fixed for “precise data analysis.”

5.4 Why this is not RAG (and when you would add it)

RAG (Retrieval-Augmented Generation) is a pipeline roughly:

Query → embed query → vector search over chunks → (optional rerank)
      → stuff top-k chunks into prompt → generate answer

RAG component

Purpose

In this project?

Document loader

Ingest corpus

No (client passes text)

Chunker

Split long docs

No (40-line slices for demos)

Embedding model

Map text → vectors

No

Vector store

Index / search

No

Retriever / reranker

Select context

No

Generator LLM

Answer from context

Yes — but context is the whole raw_text argument

This project is tool-augmented generation with a local LLM, not RAG. The “context” is supplied explicitly as raw_text. That is the right pattern when:

  • the user (or agent) already has the exact text to analyze, or

  • the corpus is small enough to fit in one call.

You would add RAG when:

  • the knowledge base is large and unknown to the caller,

  • you need citations from many documents,

  • or repeated queries should not re-send the full corpus each time.

A future extension could still live behind MCP (e.g. search_logs tool + analyze_data), but that is out of Phase 1 scope.

5.5 System vs user message design

Role

Content

Why

system

Fixed persona: precise data analysis assistant

Stabilizes style and task framing

user

{instruction} then blank line then {raw_text}

Puts the operator’s goal first; data second

Putting the instruction first helps the model treat the log as evidence for a stated task rather than free-form “continue the log.”

5.6 Temperature and thinking (decoded)

  • Temperature controls randomness. 0 means greedy / near-deterministic decoding — preferred for ops-style extraction.

  • Thinking / CoT (Qwen3.5) can produce a long private reasoning trace. If the runtime budgets tokens primarily for that trace, the visible answer field can be empty. Disabling think keeps tokens for content.

5.7 Stdio transport in one sentence

The MCP host starts uv run mcp run server.py as a child process and speaks JSON-RPC on stdin/stdout. No open network port is required for the default local setup (Inspector adds a local proxy for the browser UI).


6. Implementation flow

Map the code path in server.py to the lifecycle above.

1. Import FastMCP from official SDK
2. Instantiate mcp = FastMCP("Local Intelligence Server")
3. Decorate analyze_data with @mcp.tool()  → schema + description auto-derived
4. On call:
     a. try:
     b.   ollama.chat(model, messages, options, think=False)
     c.   read message.content
     d.   if empty → error string
     e.   else return content
     f. except → error string
5. mcp CLI loads module, finds global `mcp`, runs server.run()

Line-level map (server.py)

Lines

Code intent

1–2

Imports: FastMCP + Ollama client

4

Server identity string shown to clients

7–12

Tool registration + docstring (appears in tools/list)

14–15

Comment documenting the empty-content failure mode

16–24

Local chat completion with pinned model and decoding flags

25–28

Content extraction and empty-content guard

29–30

Exception → client-visible error string

There is no if __name__ == "__main__" block. uv run mcp run server.py imports the module, locates the mcp object, and calls run() for you.

Demo harness flow (run_demo.py)

for each Loghub 40-line slice:
    analyze_data(raw, instruction)     # direct Python call
    write artifacts/<id>_direct.txt

MCP ClientSession over stdio:
    list_tools → expect ["analyze_data"]
    call_tool(apache case)
    write artifacts/mcp_stdio_apache.txt

write artifacts/run_summary.json + full_outputs.json
assert quality gate (non-empty, no errors)

7. Project layout

.
├── server.py                 # MCP server + analyze_data
├── run_demo.py               # Reproducible real-run harness
├── main.py                   # uv init stub (unused by MCP path)
├── pyproject.toml            # project metadata + deps
├── uv.lock                   # locked graph
├── .python-version           # 3.13.13
├── LICENSE                   # MIT (code)
├── README.md                 # this file
├── data/
│   ├── SOURCE.md             # Loghub provenance
│   ├── apache_access_sample.log
│   ├── linux_syslog_sample.log
│   ├── hdfs_sample.log
│   ├── apache_40.log         # analysis slice
│   ├── linux_40.log
│   └── hdfs_40.log
└── artifacts/                # outputs from real runs
    ├── run_summary.json
    ├── full_outputs.json
    ├── apache_mod_jk_errors_direct.txt
    ├── linux_ssh_auth_failures_direct.txt
    ├── hdfs_block_activity_direct.txt
    └── mcp_stdio_apache.txt

8. Requirements

Dependency

Notes

Linux environment

Verified on Ubuntu

uv

Package manager and runner

Python 3.13.13

Pinned in .python-version

Ollama

Daemon listening locally

Model qwen3.5:4b

Must appear in ollama list

Node.js / npx

Only for mcp dev (Inspector UI)

ollama list | grep 'qwen3.5:4b'
# expect a line with qwen3.5:4b

9. Installation

# From the project root
uv python install 3.13.13    # if needed; fallback policy is 3.12.10 only if 3.13.13 is unresolvable
uv venv --python 3.13.13
uv python pin 3.13.13
uv sync                      # restores mcp[cli] + ollama from uv.lock

Confirm packages:

uv run python -c "
import importlib.metadata as m
from mcp.server.fastmcp import FastMCP
print('mcp', m.version('mcp'))
print('ollama', m.version('ollama'))
print('FastMCP OK', FastMCP)
"

Expected (as of the documented run): mcp 1.28.1, ollama 0.6.2.


10. Operation

With Ollama up and qwen3.5:4b available:

uv run python run_demo.py

This rewrites artifacts/, prints full model outputs, and exits non-zero if the quality gate fails.

10.2 MCP Inspector (browser)

uv run mcp dev server.py

Typical console lines:

Proxy server listening on localhost:6277
MCP Inspector is up and running at:
  http://localhost:6274/?MCP_PROXY_AUTH_TOKEN=...

In the UI:

  1. Confirm transport stdio and command defaults (uv + run --with mcp mcp run server.py).

  2. Connect.

  3. Open Toolsanalyze_data.

  4. Paste log text into raw_text, set instruction, Run Tool.

10.3 Server only (stdio)

uv run mcp run server.py

Useful when a host app launches the server as a subprocess.

10.4 Ad-hoc Python call (no MCP)

uv run python -c "
from pathlib import Path
from server import analyze_data
raw = Path('data/apache_40.log').read_text()
print(analyze_data(raw, 'List unique error messages and approximate counts.'))
"

10.5 Example tool arguments

Argument

Example

raw_text

Contents of data/linux_40.log

instruction

Extract attacking IPs and recommend firewall actions in bullets.


11. Sample data

Real public logs from Loghub (LogPAI), downloaded 2026-07-11. See data/SOURCE.md.

Local full file

Upstream

Lines

Demo slice

data/apache_access_sample.log

Apache/Apache_2k.log

1999

data/apache_40.log

data/linux_syslog_sample.log

Linux/Linux_2k.log

1999

data/linux_40.log

data/hdfs_sample.log

HDFS/HDFS_2k.log

2000

data/hdfs_40.log

Upstream URLs:

40-line slices keep prompts inside a practical context budget for a 4B local model while remaining realistic (error storms, SSH noise, healthy HDFS INFO).

License note: Loghub data is third-party. This repo’s MIT license covers project code, not the log corpus.


12. Real run — evidence

12.1 Run metadata

Field

Value

Timestamp (UTC)

2026-07-11 08:57:55

Model

qwen3.5:4b

Decoding

temperature=0, think=False

Packages

mcp 1.28.1 · ollama 0.6.2

Network

POST http://127.0.0.1:11434/api/chatHTTP 200 on all calls

Quality gate

passed (artifacts/run_summary.json"all_ok": true)

12.2 Timing table

Case

Invocation path

Elapsed

Output size

Artifact

Apache mod_jk errors

direct analyze_data

6.53 s

1790 chars

artifacts/apache_mod_jk_errors_direct.txt

Linux SSH auth failures

direct analyze_data

10.49 s

2166 chars

artifacts/linux_ssh_auth_failures_direct.txt

HDFS block activity

direct analyze_data

9.08 s

2499 chars

artifacts/hdfs_block_activity_direct.txt

Apache (same case)

MCP stdio tools/call

8.52 s

1790 chars

artifacts/mcp_stdio_apache.txt

MCP path also listed tools as ['analyze_data'] with isError: false. Direct and MCP Apache outputs matched (same structure and length under think=False + temperature=0).


12.3 Case 1 — Apache httpd / mod_jk (failure storm)

Input sample (data/apache_40.log):

[Sun Dec 04 04:47:44 2005] [notice] workerEnv.init() ok /etc/httpd/conf/workers2.properties
[Sun Dec 04 04:47:44 2005] [error] mod_jk child workerEnv in error state 6
[Sun Dec 04 04:51:08 2005] [notice] jk2_init() Found child 6725 in scoreboard slot 10
...
[Sun Dec 04 04:51:18 2005] [error] mod_jk child workerEnv in error state 6

Instruction: summarize dominant failure pattern; list unique errors + counts; suggest root cause from evidence only.

Model output (verbatim, 6.53 s):

### 1. Dominant Failure Pattern
The dominant failure pattern is the continuous cycling of Apache JK (mod_jk) backend workers into an **unrecoverable "error" state** despite repeated initialization attempts by `workerEnv.init()`. While new child processes are successfully spawned and registered in the scoreboard, they consistently fail to transition from a healthy or transient error state 6 directly to a stable operational state.

### 2. Unique Error Messages and Approximate Counts
Based on the provided log snippet:

*   **`[error] mod_jk child workerEnv in error state 6`** (Approximately 10 occurrences)
    *   This is the most frequent specific error, appearing repeatedly across different timestamps (e.g., at 04:51:18 and 04:52:15). It indicates a persistent failure condition where the worker cannot recover.
*   **`[error] mod_jk child workerEnv in error state 7`** (Approximately 3 occurrences)
    *   This appears as an escalation or alternative manifestation of the same underlying issue, seen specifically at timestamp `04:52:15`.

*(Note: The logs also contain numerous `[notice] jk2_init() Found child...` and `[notice] workerEnv.init() ok ...` messages indicating successful spawning and initial configuration checks.)*

### 3. Most Likely Root Cause
The root cause is likely a **configuration mismatch or resource exhaustion** preventing the JK workers from completing their initialization sequence, as evidenced by `workerEnv.init()` returning "ok" while the subsequent state check immediately reports an error in states 6 and 7. This suggests that although the Apache process starts successfully, the backend application (Java/Tomcat) running within these children fails to bind or start due to missing ports, insufficient memory, or a deadlock during startup logic.

Reviewer note: counts are approximate model estimates, not deterministic regex tallies. Good for triage; not a substitute for grep -c.

MCP re-run: same case via ClientSession produced the same analysis body (artifacts/mcp_stdio_apache.txt, 8.52 s, isError=false).


12.4 Case 2 — Linux syslog SSH noise

Input sample (data/linux_40.log):

Jun 14 15:16:01 combo sshd(pam_unix)[19939]: authentication failure; ... rhost=218.188.2.4
Jun 15 02:04:59 combo sshd(pam_unix)[20882]: authentication failure; ... rhost=220-135-151-1.hinet-ip.hinet.net  user=root

Model output (verbatim, 10.49 s):

### 1) Security-Relevant Events Identified
*   **SSH Authentication Failures:** Multiple repeated failures detected via `sshd(pam_unix)` indicating brute-force or credential stuffing attempts against the root user (`user=root`).
    *   Source: Jun 15 02:04:59 (IP: 220-135-151-1.hinet-ip.hinet.net) – **7 instances**.
    *   Source: Jun 15 12:12:34 & 12:13:19 (IP: 218.188.2.4) – **Multiple instances** (~16 total).
*   **Successful Privilege Escalation:** Two `su` sessions opened by root (`uid=0`) for non-root users, indicating successful compromise or authorized admin activity.
    *   User: `cyrus` (Jun 15 04:06:18–20).
    *   User: `news` (Jun 15 04:12:42–43).

### 2) Distinct Remote Hosts/IPs Involved in Auth Failures
*   **220-135-151-1.hinet-ip.hinet.net** (IPv6 mapped notation for `220.135.151.1`) – Associated with high-volume root login attempts at 02:04 UTC on Jun 15.
*   **218.188.2.4** (IPv4) – Associated with repeated authentication failures starting Jun 14 and continuing through the morning of Jun 15.

### 3) Non-Auth System Events
*   **Service Activity:** Successful `su` sessions for users `cyrus` and `news`.
*   **Log Rotation Alert:** `logrotate: ALERT exited abnormally with [1]` at Jun 15 04:06:20. This indicates a configuration or disk space issue preventing log rotation, which may be causing syslog buffer issues given the high volume of auth logs generated during the attack window.

### 4) Ops Triage Recommendation
*   **Immediate Action:** Block both remote IPs (`218.188.2.4` and `220-135-151-1.hinet-ip.hinet.net`) at the firewall level immediately to stop active credential stuffing attacks on root accounts.
*   **Investigation Priority 1 (Compromise):** Verify if users `cyrus` or `news` were authorized by an admin who used their credentials for `su`. If unauthorized, assume a successful breach and initiate incident response procedures (reset passwords, audit user activity).
*   **Log Management:** Investigate the `logrotate` failure. With such high log volume from SSH attacks, disk space exhaustion is likely; ensure logs are being archived or rotated to prevent system instability during future attack waves.

Honest limitation: labeling su as possible “compromise” is over-inference — the log alone does not prove unauthorized access. A careful operator treats that as a hypothesis, not a finding of fact. The IP extraction and auth-failure theme are well grounded.


12.5 Case 3 — HDFS healthy INFO traffic

Model output (verbatim, 9.08 s):

Based on the provided HDFS NameNode/DataNode style logs, here is the analysis:

### 1. Summary of Block/Packet Operations
The log snippet depicts a standard **block replication and storage lifecycle** within an HDFS cluster. The operations observed include:
*   **Block Allocation:** New blocks are being allocated for tasks (specifically `/user/root/rand/_temporary/...`), indicated by `NameSystem.allocateBlock`. This suggests active data processing or job execution where new files need to be stored immediately upon creation.
*   **Data Reception & Replication:** Multiple DataNodes (`dfs.DataNode$PacketResponder`) are receiving blocks from various remote peers (e.g., `/10.251.x.x`). The consistent block size of `67108864` bytes indicates standard 64MB replication chunks.
*   **Storage Registration:** As data is received, the NameNode (`dfs.FSNamesystem`) updates its in-memory map to track these blocks as "stored," linking specific DataNodes (e.g., `10.251.73.220:50010`) to their respective block IDs via `NameSystem.addStoredBlock`.
*   **Verification:** A single instance of successful verification (`dfs.DataBlockScanner`), confirming data integrity for a specific block ID before it was likely committed or moved in the broader context (though this log only shows success).

### 2. Unique Components and Thread Names
The logs contain explicit thread/component identifiers following the standard Hadoop logging format `Component$Subcomponent`. The unique components identified are:
*   **dfs.DataNode$PacketResponder**: Handles incoming block data packets for specific blocks or general termination of responder threads.
*   **dfs.FSNamesystem**: Manages high-level filesystem operations, including adding stored blocks to the map and allocating new blocks.
*   **dfs.DataNode$DataXceiver**: Specifically handles the network reception stream (`Receiving block ... src: ... dest: ...`).
*   **dfs.DataBlockScanner**: Responsible for verifying data integrity (checksums) of specific blocks.

*(Note: While timestamps like `081109` and thread IDs in packet responders exist, they are not unique component names).*

### 3. Health Status Assessment
**The snippet looks healthy.**

*   **Evidence:** All log entries are prefixed with the level `INFO`. There are no lines containing `WARN`, `ERROR`, or any other severity indicators indicating a failure or warning condition. The operations (allocation, reception, verification) completed successfully without interruption in this timeframe.

Why this case matters: a useful analyzer must also say “healthy” when appropriate, not invent incidents. The model correctly treated an INFO-only window as non-alarming.


12.6 Reproducibility checklist

Step

Command / artifact

Environment

uv sync + Python 3.13.13

Model present

ollama list includes qwen3.5:4b

Data present

data/*_40.log from Loghub slices

Run

uv run python run_demo.py

Expect

all_ok: true in artifacts/run_summary.json

Compare

Outputs may vary slightly across Ollama versions; structure and themes should match


13. Troubleshooting

Symptom

Likely cause

Fix

Error calling local LLM: ... Connection refused

Ollama not running

ollama serve or start the desktop service; check port 11434

Empty or near-empty answers (older code without think=False)

CoT consumed generation budget

Ensure server.py passes think=False (current code does)

model 'qwen3.5:4b' not found

Model not pulled

ollama pull qwen3.5:4b — do not silently swap tags if following this repo’s model policy

mcp: command not found outside uv

CLI not on bare PATH

Always use uv run mcp ...

Inspector fails with npx not found

Node missing

Install Node.js LTS; only required for mcp dev

Inspector auth 401 on proxy

Missing session token

Use the URL with MCP_PROXY_AUTH_TOKEN printed at startup; header is x-mcp-proxy-auth: Bearer <token>

Extremely slow first call

Model cold load

First ollama.chat after boot loads weights; subsequent calls are faster

Quality gate fails in run_demo.py

Empty content / Ollama error

Read the printed error string; verify model health with a tiny ollama run qwen3.5:4b "ping"

Import error mcp.server.fastmcp

Wrong package

Uninstall standalone fastmcp if present; reinstall mcp[cli] via uv

Health probes

# Ollama up?
curl -s http://127.0.0.1:11434/api/tags | head

# Tool path only
uv run python -c "from server import analyze_data; print(analyze_data('a=1\nb=2','Sum the numbers'))"

# MCP path only
uv run python run_demo.py

14. Extending the server

Phase 1 is intentionally minimal. Practical extensions:

14.1 Add another tool

@mcp.tool()
def count_error_lines(raw_text: str) -> str:
    """Deterministic count of lines containing ERROR/Error/error."""
    n = sum(1 for line in raw_text.splitlines() if "error" in line.lower())
    return f"error_line_count={n}"

Keep deterministic helpers separate from LLM tools so reviewers can tell which path is probabilistic.

14.2 Optional file-path tool

Accept a path under a sandboxed directory, read with size limits, then call the same chat helper. Always enforce:

  • path confinement (no ../../etc/passwd)

  • max bytes

  • allowlist of extensions

14.3 Toward RAG (only if requirements demand it)

A second-phase design might look like:

index_logs(directory) → chunks + embeddings + store
search_logs(query)    → top-k passages
analyze_data(passages + instruction) → still local generation

That adds embedding model choice, index freshness, and evaluation of retrieval quality separately from generation. Do not fold that complexity into analyze_data without measuring need.

14.4 Host configuration sketch

Example Claude Desktop-style config shape (paths will differ):

{
  "mcpServers": {
    "local-intelligence": {
      "command": "uv",
      "args": ["run", "--directory", "/absolute/path/to/project", "mcp", "run", "server.py"]
    }
  }
}

15. Limitations and known issues

Be explicit with stakeholders:

  1. Small model qualityqwen3.5:4b can mis-count events, invent root causes, or overstate severity (see SSH su example). Use for triage drafts; verify with grep/metrics for decisions.

  2. No retrieval — the model only sees the text you pass. It cannot “know” logs you did not include.

  3. Context limits — full 2k-line Loghub files are stored for provenance; demos use 40-line slices. Multi-MB dumps need chunking or map-reduce style tooling (not implemented).

  4. Approximate structure, not schema-validated output — return type is free-form str, not JSON with Pydantic validation.

  5. Latency — several seconds per call on local hardware (see timing table); not a sub-100ms API.

  6. Single-process stdio — classic MCP local pattern; not a multi-tenant HTTP service with authn/z.

  7. Data license ≠ code license — Loghub samples remain under upstream terms.

  8. Nondeterminism residual — even at temperature 0, backend/version changes can alter wording; do not treat outputs as cryptographic.


16. Dependencies

Managed exclusively with uv (pyproject.toml / uv.lock):

Package

Version (locked run)

Role

mcp[cli]

1.28.1

Official MCP Python SDK, FastMCP, mcp CLI

ollama

0.6.2

Python client for local Ollama HTTP API

Transitive highlights: pydantic, httpx, anyio, Starlette/SSE stack for SDK features.

Not used: standalone fastmcp, Unsloth, embedding libraries, cloud SDKs.


17. License

Project source code and documentation are released under the MIT License.

Sample logs under data/ are from Loghub and are not re-licensed by this repository. See data/SOURCE.md.


Quick reference

# install
uv sync

# prove the stack
uv run python run_demo.py

# interactive MCP
uv run mcp dev server.py

# headless MCP server
uv run mcp run server.py

Bottom line for reviewers: this is a small, fully offline MCP server that correctly places a local LLM behind a single well-scoped tool, ships real Loghub-based evidence, and documents the failure mode (think vs content) that would otherwise look like a silent bug. Scope is intentional; RAG is deliberately out of band until retrieval is a real requirement.

Install Server
A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pypi-ahmad/add-an-llm-to-your-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server