Design
1. System Interactions
Section titled “1. System Interactions”The MCP server sits between AI agent clients and GitHub, serving two categories of data through fundamentally different paths.
Runtime Architecture
Section titled “Runtime Architecture”Authentication Flow
Section titled “Authentication Flow”The token undergoes a two-stage transformation:
This obfuscation hides the fact that the server relies on GitHub from callers. The token format is opaque to clients — they only know it as “the MCP server’s auth token.”
Data Flow Summary
Section titled “Data Flow Summary”| Tool | Data Source | Cache | GitHub Calls |
|---|---|---|---|
list_skills | Bundled JSON | N/A | 0 |
list_agents | Bundled JSON | N/A | 0 |
list_docs | Bundled JSON | N/A | 0 |
get_skill | GitHub API | 10 min TTL | 1 |
get_skill_file | GitHub API | 10 min TTL | 1 |
get_agent | GitHub API | 10 min TTL | 1 |
get_doc | GitHub API | 10 min TTL | 1-2 (fallback) |
2. Deployment Architecture
Section titled “2. Deployment Architecture”Cloudflare Resources
Section titled “Cloudflare Resources”Resource Usage
Section titled “Resource Usage”| Cloudflare Resource | Usage | Tier |
|---|---|---|
| Worker | Request routing, static index serving | Free (100K req/day) |
| Durable Object | MCP session state, SSE connection management | Free tier includes DO |
| Cache API | Per-colo caching of get_* responses (10 min TTL) | Free (no storage limits) |
| Worker bundle | ~50-100 KB (source + bundled JSON indexes) | Free (max 10 MB) |
| CPU time | JSON parse/serialize, Base64 decode — well under 10ms/invocation | Free tier limit: 10ms |
No KV, R2, D1, or Queues are used. The entire server runs within the free tier.
What Runs Where
Section titled “What Runs Where”- Build time (GitHub Actions): Clone source repos, generate index JSON files, bundle into Worker, deploy via
wrangler deploy. - Runtime (Cloudflare edge): Worker receives HTTP requests, Durable Object manages MCP sessions.
list_*tools return bundled data.get_*tools fetch from GitHub (with caching). - No origin server: The Worker is the entire backend. There is no origin, database, or long-running process.
3. Testing Strategy
Section titled “3. Testing Strategy”Test Architecture
Section titled “Test Architecture”Testing Approach
Section titled “Testing Approach”The testing strategy separates pure logic (testable in isolation) from platform integration (verified manually post-deploy).
What Is Tested in CI
Section titled “What Is Tested in CI”| Layer | Test File | Mock Strategy | Coverage |
|---|---|---|---|
| Auth decoding | auth.test.ts | None (pure function) | URL-safe Base64 decode, missing header, invalid Base64, non-Bearer prefix |
| Path validation | validation.test.ts | None (pure function) | .. traversal, absolute paths, backslashes, valid paths pass |
| GitHub client | github.test.ts | Global fetch mock returning canned GitHub API responses | URL construction, content decoding, error mapping (401/403/404/5xx), index.md fallback logic |
| Cache wrapper | cache.test.ts | In-memory Map replacing Cache API | Cache hit returns stored value, miss triggers fetch, skipCache bypasses and refreshes, key construction |
| Tool handlers | tools.test.ts | fetch mock + cache mock + fixture indexes | list_* returns bundled data with zero fetch calls, get_* integrates auth + validation + github + cache |
What Is NOT Tested in CI
Section titled “What Is NOT Tested in CI”| Concern | Why Not | How Verified |
|---|---|---|
McpAgent Durable Object lifecycle | Requires Cloudflare runtime — cannot be fully simulated in Vitest | Manual smoke test: connect MCP client to deployed Worker |
| SSE transport | Cloudflare-specific HTTP streaming | Manual: verify SSE stream opens and tool calls succeed |
wrangler deploy | Infrastructure concern | CI job success + manual verification |
| Index generation scripts | Run as Node.js scripts against cloned repos | Tested implicitly by CI pipeline (generate → build → deploy); can also run locally |
Mock Design
Section titled “Mock Design”fetch mock (GitHub API):
// Vitest global mock returning typed responsesvi.stubGlobal('fetch', vi.fn())
function mockGitHubSuccess(content: string, sha = 'abc123') { return new Response(JSON.stringify({ content: btoa(content), sha, size: content.length, encoding: 'base64', type: 'file', name: 'SKILL.md', path: 'instructions/claude/skills/test/SKILL.md', }))}
function mockGitHubError(status: number) { return new Response('', { status })}Cache API mock:
// In-memory cache for testingconst cacheStore = new Map<string, { response: Response; expiry: number }>()
const mockCache = { match: async (key: string) => { const entry = cacheStore.get(key) if (!entry || Date.now() > entry.expiry) return undefined return entry.response.clone() }, put: async (key: string, response: Response) => { cacheStore.set(key, { response: response.clone(), expiry: Date.now() + 600_000 }) },}Bundled index fixtures:
// Test fixture matching the generated index shapeconst testSkillsIndex = [ { name: 'test-skill', description: 'A test skill', argumentHint: null, userInvocable: false, files: [] }, { name: 'with-files', description: 'Skill with extras', argumentHint: '[arg]', userInvocable: true, files: ['examples/demo.ts'] },]4. Deployment Pipeline
Section titled “4. Deployment Pipeline”Pipeline Architecture
Section titled “Pipeline Architecture”Pipeline Triggers
Section titled “Pipeline Triggers”| Trigger | When | Purpose |
|---|---|---|
push to main | PR merge | Deploy changes to the MCP server code itself |
schedule (cron) | Saturday 00:00 Pacific (07:00 UTC) | Pick up changes in source repos (skills, agents, docs) |
workflow_dispatch | Manual | On-demand rebuild for urgent updates |
repository_dispatch | Future | Direct trigger from source repo pipelines on merge |
Secrets and Variables
Section titled “Secrets and Variables”| Name | Type | Where Used | Purpose |
|---|---|---|---|
REPO_ACCESS_TOKEN | Secret | Index generation step | GitHub PAT to clone source repos (never in Worker bundle) |
CLOUDFLARE_API_TOKEN | Secret | Deploy step | Authenticate wrangler deploy to Cloudflare |
ARDA_GH_ACTION_PROJECT_WRITER | Secret | PR upkeep workflow | Add PRs to GitHub Projects board |
REPO | Variable | Index generation step | Arda-cards/agentic-workspace |
DOCS_REPO | Variable | Index generation step | Arda-cards/documentation |
SKILLS_PATH | Variable | Index generation step | instructions/claude/skills |
AGENTS_PATH | Variable | Index generation step | instructions/claude/agents |
DOCS_CONTENT_PATH | Variable | Index generation step | src/content/docs |
Pipeline Isolation
Section titled “Pipeline Isolation”The pipeline has a strict separation between build-time and runtime secrets:
- Build-time:
REPO_ACCESS_TOKENclones source repos. This token is used only in the GitHub Actions runner and is never embedded in the Worker bundle. - Runtime: The Worker has no secrets. The caller’s PAT arrives in the HTTP request header, is decoded, used for the GitHub API call, and discarded. It is never stored in Durable Object state, KV, or logs.
Copyright: © Arda Systems 2025-2026, All rights reserved