NTPU AI4X MCP Technical Architecture

NTPU AI4X MCP lets AI assistants β€” ChatGPT, Claude, IDE tools, and other MCP clients β€” query NTPU AI4X's public information in a standardized way. It cannot browse the web freely and it cannot modify any data: every answer comes from information that has already been collected, verified, and stored.

This page moves from plain-language concepts to implementation detail. If you're new here, start with System Overview and Request Flow; engineers can continue into Tech Stack, Design Priorities, Security, Engineering Detail, and Deploy & Operate below.

How should I read this page?

System Overview

30-Second Overview

AI4X MCP Main Architecture (MCP Query)

AI Client

ChatGPT / Claude / IDE

Asks a question through an AI tool that supports MCP.

MCP Server

Cloudflare Worker

Receives the MCP request and validates the tool and arguments.

Tool Registry + Zod

Input & Schema Validation

Rejects unregistered tools and invalid arguments at the door.

Domain Service

Business Logic

Decides how to look up the data β€” the only layer allowed to call a repository.

D1

Cloudflare D1

Structured canonical data.

R2

Cloudflare R2

Documents and source snapshots.

Data Sync (Independent Flow)

AI4X Website

Official Source

An allowlisted official data source.

Ingestion Worker

Cloudflare Worker

Triggered on a daily schedule, not by any public route.

Validate / Normalize

Zod + Content-Hash Diff

Converts to the canonical shape and checks whether anything changed.

D1 / R2

Canonical Storage

Only writes a new version if the content hash actually changed.

NTPU AI4X MCP is a public, read-only Model Context Protocol server, built as two independently deployed Cloudflare Workers: a public-facing MCP server (ai4x-mcp-server) that answers every client request, and a separate ingestion worker (ai4x-ingestion-worker) that is not part of the public request path at all.

Every MCP tool call passes through the same pipeline inside the MCP worker: protocol layer, then tool registry and Zod schema validation, then a domain service, then a D1 or R2 repository. There is no route that bypasses tool registration or input validation. Even the one non-MCP HTTP route, Ask AI4X, calls the exact same domain-service functions the MCP tools call, never a second, parallel implementation of the same lookup.

All authoritative records (center info, services, members, projects, documents, events) live in one D1 database per environment; raw source snapshots and document bodies live in a separate R2 bucket per environment. Both are private Worker bindings only; there is no public bucket URL, no generic database tool, and no URL-fetch tool anywhere in the MCP tool surface.

Request Flow

Three independent flows exist in this system, and none of them call each other: an MCP tool call, the optional Ask AI4X assistant, and the scheduled ingestion job that keeps D1 up to date.

For ChatGPT, Claude, IDE tools, and other MCP clients β€” queries the official data.

AI Client

Asks a question

MCP Server

Receives the MCP request

Tool Validation

Tool Registry + Zod

Domain Service

Decides how to get the data

D1 / R2

Reads verified data only

Result + Source

Answer with provenance

Full technical pipeline
AI Client (Claude / ChatGPT / IDE / custom MCP client)
  ↓ MCP over Streamable HTTP (JSON-RPC), POST /mcp
MCP Worker: Protocol Layer
  handles initialize / tools/list / tools/call
  ↓
Tool Registry + Zod Schema Validation
  rejects unknown tools and invalid arguments before any code runs
  ↓
Domain Service (packages/domain)
  the only code allowed to call a repository
  ↓
D1 Repository / R2 Repository (packages/database)
  bound-parameter SQL only, no string-built queries
  ↓
runTool() adapter: authorize() β†’ execute β†’ structured audit event β†’ error mapping
  ↓
Structured result + provenance (source, version, verifiedAt) returned to the client

Tech Stack

Everything below is running in production at https://ai4x.mcp.ntpu.ai, not a proposal under evaluation.

Core Request Path

Every MCP tool call passes through these core components in order.

MCP Server

Cloudflare Workers

Receives and handles every MCP client's query.

Input Validation

Zod

Every tool name and input argument must pass schema validation first.

Business Logic

Domain Layer

Centralizes query rules β€” the only service layer allowed to call the data storage layer.

Database

Cloudflare D1

Stores structured canonical data for center info, services, members, projects, and events.

File Storage

Cloudflare R2

Keeps source snapshots, document content, and other unstructured data.

Data & Operations

These components handle data updates, AI Q&A, and system auditing.

Data Sync

Ingestion Worker

Fetches from allowlisted official sources and updates canonical data daily.

AI Q&A

Cloudflare Workers AI

Provides the Playground's optional Q&A feature, sharing the same data and domain logic as MCP.

Audit Trail

Cloudflare Workers Logs

Logs structured events for tool calls, trace ids, latency, results, and status.

Design Priorities

This service answers questions about a real institution. When design decisions conflict, they are resolved in this order:

  1. 1

    Verifiable sources

    An answer without a verifiable source is never presented as fact: the system returns an explicit NO_AUTHORITATIVE_SOURCE error instead of guessing.

  2. 2

    Read-only safety

    v1 exposes no write, execution, or generic-access tool; every registered tool is R0 (low risk) and L0 (public data).

  3. 3

    Data integrity

    Canonical records change only through the versioned ingestion pipeline's content-hash diff and record-version history, never by ad hoc edit.

  4. 4

    Auditability

    Every tool call produces a structured audit event with a trace id, and every canonical record change is versioned.

  5. 5

    Correctness

    Tool selection and arguments are enforced by Zod schemas and checked against a golden evaluation set.

  6. 6

    Availability

    The stateless design allows ordinary horizontal scaling; the service targets 99.9% monthly availability.

  7. 7

    Performance

    The target is under 1,000 ms p95 at the Worker boundary for a simple D1-backed tool.

  8. 8

    Visual presentation

    The web surface (Home, Docs, Playground, Learn) is a later addition on top of the protocol service, not the core design constraint.

The system would rather say it found no reliable source than guess at an answer.

Technical behavior: returns NO_AUTHORITATIVE_SOURCE.

Security & Data Protection

Can AI modify data?

No. Every MCP tool is read-only.

Every registered tool must declare readOnly: true and riskLevel: 'R0' before it can be called. Forbidden generic tools (execute_sql, run_shell, fetch_url, read_file, write_file) simply do not exist.

Can AI ask the system to run SQL or a shell command?

No. Those tools do not exist here.

Every tool argument is checked by a strict Zod schema with explicit length and range bounds before it reaches a repository, and every SQL statement uses bound parameters, never a string-built query.

What if a document contains a malicious instruction?

It is never executed as a command.

There is no LLM anywhere in the MCP request path, so text inside a document body is returned to the client verbatim, never executed as an instruction.

Can the database be reached directly from the internet?

No. Only through the Worker's own binding.

The R2 bucket has no public bucket domain or r2.dev access configured in any environment; every read goes through the Worker's own binding.

Could a test environment accidentally touch production data?

No. Each environment is fully separate.

Dev, staging, and production each have their own D1 database and R2 bucket, so a mistake in one environment cannot touch another environment's data.

Is every query tracked?

Yes, with a structured audit trail.

Every tool call emits a structured audit event (trace id, tool name and version, an argument hash rather than raw arguments, result count, latency, status), captured by Cloudflare Workers Logs.

See engineering implementation detail β€Ί

Engineering Implementation Detail

For engineers who want the implementation details behind the tech-stack cards above.

Expand technical detail

Tool Registry & Policy

packages/policy's tool registry and authorize() check: every tool must be registered with a name, version, risk level, read-only flag, and maximum data class before it can run. All 11 v1 tools are registered R0 (public read-only) and L0 (public data).

Zod Validation

Zod schemas (packages/schemas) validate every tool's input and output, and every row read back from D1 before it is trusted as domain data.

Domain Service

packages/domain: one service class per data family (for example ServiceCatalogService). This is the only layer allowed to call a repository directly: MCP tool handlers never touch D1 or R2 themselves.

Repository

packages/database: Cloudflare D1 (SQLite)-backed repository implementations, one repository interface per entity, all bound-parameter SQL. Cloudflare R2 holds immutable raw source snapshots, archived before any parsing happens and deduplicated by content hash so an unchanged source is never re-archived.

Provenance

A shared content-hash helper (sha256HexOfJson) is used by ingestion's change-detection and is reflected in every record's provenance, so every result carries its source, version, and verification time.

Audit

packages/audit's structured JSON audit events (one per tool call: trace id, tool name and version, scope decision, an argument hash rather than raw arguments, result count, latency, status), captured automatically by Cloudflare Workers Logs; no separate logging service.

Runtime Environment & Service Boundaries

AI4X MCP is made of several deployment units kept strictly separate: the public MCP query path, the data-sync job, Ask AI4X, and the static website each have a clear boundary and never bypass one another's intended path.

MCP Server

Runtime
agents/mcp/server's createMcpHandler(), wrapping @modelcontextprotocol/server v2's McpServer.
Behavior
Every MCP request is handled statelessly β€” it never depends on memory or a session from a previous request.
Public entry
/mcp
Purpose
Handles the MCP protocol, tool registration, argument validation, and tool calls.

Ingestion Worker

Deployment unit
ai4x-ingestion-worker
Trigger
Only a daily Cloudflare Cron Trigger β€” nothing else runs it.
Public access
Not publicly callable. Its HTTP fetch handler always returns 404.
Purpose
Fetches, validates, normalizes, and updates D1/R2 from allowlisted official sources.

Ask AI4X

Public entry
POST /api/ask-ai4x
Origin restriction
Callable only from the same-origin web UI.
Model
Cloudflare Workers AI
@cf/google/gemma-4-26b-a4b-it
Tool-selection limit
At most three tool-selection rounds per question.
Relationship to MCP
Never goes through the MCP tool adapter and never makes a loopback HTTP call to /mcp; it directly reuses the same domain service as the matching MCP tool.

Static Web

Technology
HTML + CSS + Vanilla JavaScript
Deployment
Cloudflare Workers Static Assets
Domain
Shared with MCP: https://ai4x.mcp.ntpu.ai
Purpose
Serves the homepage, docs, Playground, and Learn content.

Worker Route Priority

Specific paths must be handled by Worker code first, never served as a static file.

Currently includes:

/mcp
/health
/about
/api/ask-ai4x

The run_worker_first rule guarantees these paths are always routed to Worker code first.

Request
   |
   v
run_worker_first
   |
   +--> /mcp ------------> MCP handler
   |
   +--> /health ---------> Worker handler
   |
   +--> /about ----------> Worker handler
   |
   +--> /api/ask-ai4x ---> Ask AI4X handler
   |
   `--> everything else -> Static Assets

Deploy & Operate

For system maintainers and engineers.

Expand technical detail

Three environments (dev, staging, production), each deployed independently with wrangler deploy --env <name> and each bound to its own D1 database and R2 bucket.

Production is reached at the custom domain https://ai4x.mcp.ntpu.ai (a Cloudflare Custom Domain on the account-owned ntpu.ai zone), plus its *.workers.dev fallback hostname. An ai4x.ntpu.edu.tw subdomain was considered and not used for this service, since that zone belongs to the university's own site and is not under this project's Cloudflare account.

Release order: lint, typecheck, unit tests, contract tests, integration tests, security tests, evaluation smoke tests, staging deploy, staging smoke probes, manual production approval, production deploy, post-deploy smoke tests.

Rollback: wrangler rollback <version-id> --env <env> reactivates a previously uploaded Worker Version in seconds. This does not roll back D1 or R2 state; a data-level mistake is fixed with a forward migration instead.

Every D1 schema change is a versioned migration file, applied with wrangler d1 migrations apply. The schema is never mutated at Worker startup.

Further Reading