MCP server wrapping the FIA Project GraphQL API — exposing Bible translation internalization data as MCP tools. Deployed as a Cloudflare Worker.
bt-servant-worker → FIA MCP Server → FIA GraphQL API
(consumer) (this project) (api.fiaproject.org)
The bt-servant-worker calls this MCP server using JSON-RPC 2.0 over HTTP. This server translates those calls into GraphQL queries against the FIA API, handling authentication transparently.
The Faith Internalization Audio project (launched 2022) delivers a 6-step audio/multimedia process for every pericope (~2,893) in the Bible. 1.2M+ audio/video files across 15+ languages including ASL.
- Listen — Hear the passage
- Learn — Setting the stage (background, context)
- Linger — Dwell on the passage
- Live — Apply to daily life
- Love — Share with others
- Lead — Teach and disciple
List all available languages. Use this first to discover languageId values.
- Input: none
- Output: Markdown table with ID, English name, local name, text direction, ISO code
List Bible books available in a language.
- Input:
languageId(required),testament(optional:ot/oldornt/new) - Output: Markdown table with book ID, identifier, translated title, lineup order
List pericopes (Scripture passage units) for a book.
- Input:
bookId(required) - Output: Markdown table with pericope ID, pId, sequence, verse ranges
Get all 6 internalization steps for a pericope in a specific language.
- Input:
pericopeId(required),languageId(required) - Output: Markdown with each step's title, word count, instructional text, and audio URL
Get maps, images, and videos for a pericope.
- Input:
pericopeId(required),languageId(optional) - Output: Markdown with maps (image + PDF URLs) and media items (photos, videos, illustrations)
Get glossary/dictionary terms for a pericope.
- Input:
pericopeId(required),languageId(required) - Output: Markdown with terms, alternates, descriptions, definitions, and audio URLs
Every tool response includes _meta.downstream_api_calls with the actual count of FIA API calls made (always 1 per tool). This tells bt-servant-worker's budget tracker the real cost instead of the default 12-call estimate.
A full pericope walkthrough uses ~6 downstream API calls (one per tool).
Callers must send a shared secret as a Bearer token:
Authorization: Bearer <MCP_SHARED_SECRET>
Set MCP_SHARED_SECRET as a Cloudflare secret per environment. If not set, the server runs open (useful for local dev).
All FIA authentication is handled internally:
- Access key (
FIA_ACCESS_KEY) stored as a Cloudflare secret - Server exchanges it for a short-lived bearer token (15-min TTL, cached for 14 min)
- Token automatically refreshes on expiry or 401
npm install --legacy-peer-deps
npm run devCreate .dev.vars with your FIA access key:
FIA_ACCESS_KEY=your_key_here
# Test tools/list (no auth needed in local dev if MCP_SHARED_SECRET is unset)
curl -X POST http://localhost:8787/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
# Test a tool call
curl -X POST http://localhost:8787/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"fia_list_languages","arguments":{}}}'
# With auth (required in deployed environments)
curl -X POST https://fia.mcp.servant.bible/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer YOUR_MCP_SHARED_SECRET" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'| Environment | Worker Name | Deployment |
|---|---|---|
| Production | fia-mcp |
npm run deploy |
| Staging | fia-mcp-staging |
npm run deploy:staging |
| Development | fia-mcp-dev |
npm run deploy:dev |
All environments hit the same FIA API. Secrets are set per environment:
wrangler secret put FIA_ACCESS_KEY # prod
wrangler secret put FIA_ACCESS_KEY --env staging # staging
wrangler secret put FIA_ACCESS_KEY --env dev # dev
wrangler secret put MCP_SHARED_SECRET # prod
wrangler secret put MCP_SHARED_SECRET --env staging # staging
wrangler secret put MCP_SHARED_SECRET --env dev # dev- Max 50 lines per function
- Max 25 statements per function
- Cyclomatic complexity ≤ 10
- Max nesting depth 4
- Max 5 parameters
Runs on every commit:
- Dependency audit (production)
- lint-staged (ESLint + Prettier)
- Type checking (
tsc --noEmit) - Architecture check (no circular deps, no cross-tool imports)
- Tests
- Build
- Security audit → lint, typecheck, architecture, test, build (parallel)
- Staging auto-deploys on PR
- Production deploys via manual workflow dispatch
Add to MCP_SERVERS KV (key: unfoldingWord):
{
"id": "fia",
"name": "FIA Internalization",
"url": "https://fia.mcp.servant.bible/",
"authToken": "<MCP_SHARED_SECRET>",
"enabled": true,
"priority": 2
}bt-servant-worker's buildHeaders() automatically sends Authorization: Bearer {authToken} when authToken is set. No worker code changes needed.
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }Returns array of 6 tool definitions with names, descriptions, and input schemas.
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "fia_get_step_renderings",
"arguments": {
"pericopeId": "mrk-p17",
"languageId": "eng"
}
}
}Returns:
{
"result": {
"content": [{ "type": "text", "text": "## mark — 4:35-4:41\n..." }],
"_meta": { "downstream_api_calls": 1 }
},
"jsonrpc": "2.0",
"id": 2
}See docs/graphql-schema.md for the full FIA API schema.