@iflow-mcp/kdcokenny-lsbible 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2707 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { z } from 'zod';
5
+ import { parseHTML } from 'linkedom';
6
+
7
+ function registerPrompts(server) {
8
+ server.registerPrompt(
9
+ "bible_study",
10
+ {
11
+ title: "Bible Study Guide",
12
+ description: "Generate a comprehensive Bible study guide for a passage",
13
+ argsSchema: {
14
+ passage: z.string().describe('Bible passage reference (e.g., "John 3:16-21")'),
15
+ focusAreas: z.string().optional().describe(
16
+ 'Optional comma-separated areas to focus on (e.g., "context, application, cross-references")'
17
+ )
18
+ }
19
+ },
20
+ (args) => {
21
+ const { passage, focusAreas } = args;
22
+ const focusText = focusAreas ? `
23
+
24
+ Focus areas: ${focusAreas}` : "";
25
+ return {
26
+ messages: [
27
+ {
28
+ role: "user",
29
+ content: {
30
+ type: "text",
31
+ text: `Create a comprehensive Bible study guide for ${passage}.
32
+
33
+ Include:
34
+ 1. Historical and cultural context
35
+ 2. Key themes and theological insights
36
+ 3. Practical applications for modern readers
37
+ 4. Discussion questions
38
+ 5. Cross-references to related passages${focusText}`
39
+ }
40
+ }
41
+ ]
42
+ };
43
+ }
44
+ );
45
+ server.registerPrompt(
46
+ "cross_reference",
47
+ {
48
+ title: "Find Cross References",
49
+ description: "Find verses related to a specific verse",
50
+ argsSchema: {
51
+ verse: z.string().describe('Bible verse reference (e.g., "Romans 8:28")'),
52
+ maxResults: z.string().optional().describe("Maximum number of results (default: 10)")
53
+ }
54
+ },
55
+ (args) => {
56
+ const { verse, maxResults: maxResultsStr } = args;
57
+ const maxResults = maxResultsStr ? Number.parseInt(maxResultsStr, 10) : 10;
58
+ return {
59
+ messages: [
60
+ {
61
+ role: "user",
62
+ content: {
63
+ type: "text",
64
+ text: `Find up to ${maxResults} Bible verses that are thematically related to ${verse}.
65
+
66
+ For each related verse:
67
+ 1. Explain the thematic connection
68
+ 2. Note any parallel language or concepts
69
+ 3. Highlight complementary or contrasting perspectives`
70
+ }
71
+ }
72
+ ]
73
+ };
74
+ }
75
+ );
76
+ }
77
+
78
+ // src/books.ts
79
+ var BIBLE_STRUCTURE = {
80
+ 1: {
81
+ number: 1,
82
+ name: "Genesis",
83
+ testament: "OT" /* OLD_TESTAMENT */,
84
+ chapters: 50,
85
+ verses: [
86
+ 31,
87
+ 25,
88
+ 24,
89
+ 26,
90
+ 32,
91
+ 22,
92
+ 24,
93
+ 22,
94
+ 29,
95
+ 32,
96
+ 32,
97
+ 20,
98
+ 18,
99
+ 24,
100
+ 21,
101
+ 16,
102
+ 27,
103
+ 33,
104
+ 38,
105
+ 18,
106
+ 34,
107
+ 24,
108
+ 20,
109
+ 67,
110
+ 34,
111
+ 35,
112
+ 46,
113
+ 22,
114
+ 35,
115
+ 43,
116
+ 55,
117
+ 32,
118
+ 20,
119
+ 31,
120
+ 29,
121
+ 43,
122
+ 36,
123
+ 30,
124
+ 23,
125
+ 23,
126
+ 57,
127
+ 38,
128
+ 34,
129
+ 34,
130
+ 28,
131
+ 34,
132
+ 31,
133
+ 22,
134
+ 33,
135
+ 26
136
+ ]
137
+ },
138
+ 2: {
139
+ number: 2,
140
+ name: "Exodus",
141
+ testament: "OT" /* OLD_TESTAMENT */,
142
+ chapters: 40,
143
+ verses: [
144
+ 22,
145
+ 25,
146
+ 22,
147
+ 31,
148
+ 23,
149
+ 30,
150
+ 25,
151
+ 32,
152
+ 35,
153
+ 29,
154
+ 10,
155
+ 51,
156
+ 22,
157
+ 31,
158
+ 27,
159
+ 36,
160
+ 16,
161
+ 27,
162
+ 25,
163
+ 26,
164
+ 36,
165
+ 31,
166
+ 33,
167
+ 18,
168
+ 40,
169
+ 37,
170
+ 21,
171
+ 43,
172
+ 46,
173
+ 38,
174
+ 18,
175
+ 35,
176
+ 23,
177
+ 35,
178
+ 35,
179
+ 38,
180
+ 29,
181
+ 31,
182
+ 43,
183
+ 38
184
+ ]
185
+ },
186
+ 3: {
187
+ number: 3,
188
+ name: "Leviticus",
189
+ testament: "OT" /* OLD_TESTAMENT */,
190
+ chapters: 27,
191
+ verses: [
192
+ 17,
193
+ 16,
194
+ 17,
195
+ 35,
196
+ 19,
197
+ 30,
198
+ 38,
199
+ 36,
200
+ 24,
201
+ 20,
202
+ 47,
203
+ 8,
204
+ 59,
205
+ 57,
206
+ 33,
207
+ 34,
208
+ 16,
209
+ 30,
210
+ 37,
211
+ 27,
212
+ 24,
213
+ 33,
214
+ 44,
215
+ 23,
216
+ 55,
217
+ 46,
218
+ 34
219
+ ]
220
+ },
221
+ 4: {
222
+ number: 4,
223
+ name: "Numbers",
224
+ testament: "OT" /* OLD_TESTAMENT */,
225
+ chapters: 36,
226
+ verses: [
227
+ 54,
228
+ 34,
229
+ 51,
230
+ 49,
231
+ 31,
232
+ 27,
233
+ 89,
234
+ 26,
235
+ 23,
236
+ 36,
237
+ 35,
238
+ 16,
239
+ 33,
240
+ 45,
241
+ 41,
242
+ 50,
243
+ 13,
244
+ 32,
245
+ 22,
246
+ 29,
247
+ 35,
248
+ 41,
249
+ 30,
250
+ 25,
251
+ 18,
252
+ 65,
253
+ 23,
254
+ 31,
255
+ 40,
256
+ 16,
257
+ 54,
258
+ 42,
259
+ 56,
260
+ 29,
261
+ 34,
262
+ 13
263
+ ]
264
+ },
265
+ 5: {
266
+ number: 5,
267
+ name: "Deuteronomy",
268
+ testament: "OT" /* OLD_TESTAMENT */,
269
+ chapters: 34,
270
+ verses: [
271
+ 46,
272
+ 37,
273
+ 29,
274
+ 49,
275
+ 33,
276
+ 25,
277
+ 26,
278
+ 20,
279
+ 29,
280
+ 22,
281
+ 32,
282
+ 32,
283
+ 18,
284
+ 29,
285
+ 23,
286
+ 22,
287
+ 20,
288
+ 22,
289
+ 21,
290
+ 20,
291
+ 23,
292
+ 30,
293
+ 25,
294
+ 22,
295
+ 19,
296
+ 19,
297
+ 26,
298
+ 68,
299
+ 29,
300
+ 20,
301
+ 30,
302
+ 52,
303
+ 29,
304
+ 12
305
+ ]
306
+ },
307
+ 6: {
308
+ number: 6,
309
+ name: "Joshua",
310
+ testament: "OT" /* OLD_TESTAMENT */,
311
+ chapters: 24,
312
+ verses: [
313
+ 18,
314
+ 24,
315
+ 17,
316
+ 24,
317
+ 15,
318
+ 27,
319
+ 26,
320
+ 35,
321
+ 27,
322
+ 43,
323
+ 23,
324
+ 24,
325
+ 33,
326
+ 15,
327
+ 63,
328
+ 10,
329
+ 18,
330
+ 28,
331
+ 51,
332
+ 9,
333
+ 45,
334
+ 34,
335
+ 16,
336
+ 33
337
+ ]
338
+ },
339
+ 7: {
340
+ number: 7,
341
+ name: "Judges",
342
+ testament: "OT" /* OLD_TESTAMENT */,
343
+ chapters: 21,
344
+ verses: [36, 23, 31, 24, 31, 40, 25, 35, 57, 18, 40, 15, 25, 20, 20, 31, 13, 31, 30, 48, 25]
345
+ },
346
+ 8: {
347
+ number: 8,
348
+ name: "Ruth",
349
+ testament: "OT" /* OLD_TESTAMENT */,
350
+ chapters: 4,
351
+ verses: [22, 23, 18, 22]
352
+ },
353
+ 9: {
354
+ number: 9,
355
+ name: "1 Samuel",
356
+ testament: "OT" /* OLD_TESTAMENT */,
357
+ chapters: 31,
358
+ verses: [
359
+ 28,
360
+ 36,
361
+ 21,
362
+ 22,
363
+ 12,
364
+ 21,
365
+ 17,
366
+ 22,
367
+ 27,
368
+ 27,
369
+ 15,
370
+ 25,
371
+ 23,
372
+ 52,
373
+ 35,
374
+ 23,
375
+ 58,
376
+ 30,
377
+ 24,
378
+ 42,
379
+ 15,
380
+ 23,
381
+ 29,
382
+ 22,
383
+ 44,
384
+ 25,
385
+ 12,
386
+ 25,
387
+ 11,
388
+ 31,
389
+ 13
390
+ ]
391
+ },
392
+ 10: {
393
+ number: 10,
394
+ name: "2 Samuel",
395
+ testament: "OT" /* OLD_TESTAMENT */,
396
+ chapters: 24,
397
+ verses: [
398
+ 27,
399
+ 32,
400
+ 39,
401
+ 12,
402
+ 25,
403
+ 23,
404
+ 29,
405
+ 18,
406
+ 13,
407
+ 19,
408
+ 27,
409
+ 31,
410
+ 39,
411
+ 33,
412
+ 37,
413
+ 23,
414
+ 29,
415
+ 33,
416
+ 43,
417
+ 26,
418
+ 22,
419
+ 51,
420
+ 39,
421
+ 25
422
+ ]
423
+ },
424
+ 11: {
425
+ number: 11,
426
+ name: "1 Kings",
427
+ testament: "OT" /* OLD_TESTAMENT */,
428
+ chapters: 22,
429
+ verses: [
430
+ 53,
431
+ 46,
432
+ 28,
433
+ 34,
434
+ 18,
435
+ 38,
436
+ 51,
437
+ 66,
438
+ 28,
439
+ 29,
440
+ 43,
441
+ 33,
442
+ 34,
443
+ 31,
444
+ 34,
445
+ 34,
446
+ 24,
447
+ 46,
448
+ 21,
449
+ 43,
450
+ 29,
451
+ 53
452
+ ]
453
+ },
454
+ 12: {
455
+ number: 12,
456
+ name: "2 Kings",
457
+ testament: "OT" /* OLD_TESTAMENT */,
458
+ chapters: 25,
459
+ verses: [
460
+ 18,
461
+ 25,
462
+ 27,
463
+ 44,
464
+ 27,
465
+ 33,
466
+ 20,
467
+ 29,
468
+ 37,
469
+ 36,
470
+ 21,
471
+ 21,
472
+ 25,
473
+ 29,
474
+ 38,
475
+ 20,
476
+ 41,
477
+ 37,
478
+ 37,
479
+ 21,
480
+ 26,
481
+ 20,
482
+ 37,
483
+ 20,
484
+ 30
485
+ ]
486
+ },
487
+ 13: {
488
+ number: 13,
489
+ name: "1 Chronicles",
490
+ testament: "OT" /* OLD_TESTAMENT */,
491
+ chapters: 29,
492
+ verses: [
493
+ 54,
494
+ 55,
495
+ 24,
496
+ 43,
497
+ 26,
498
+ 81,
499
+ 40,
500
+ 40,
501
+ 44,
502
+ 14,
503
+ 47,
504
+ 40,
505
+ 14,
506
+ 17,
507
+ 29,
508
+ 43,
509
+ 27,
510
+ 17,
511
+ 19,
512
+ 8,
513
+ 30,
514
+ 19,
515
+ 32,
516
+ 31,
517
+ 31,
518
+ 32,
519
+ 34,
520
+ 21,
521
+ 30
522
+ ]
523
+ },
524
+ 14: {
525
+ number: 14,
526
+ name: "2 Chronicles",
527
+ testament: "OT" /* OLD_TESTAMENT */,
528
+ chapters: 36,
529
+ verses: [
530
+ 17,
531
+ 18,
532
+ 17,
533
+ 22,
534
+ 14,
535
+ 42,
536
+ 22,
537
+ 18,
538
+ 31,
539
+ 19,
540
+ 23,
541
+ 16,
542
+ 22,
543
+ 15,
544
+ 19,
545
+ 14,
546
+ 19,
547
+ 34,
548
+ 11,
549
+ 37,
550
+ 20,
551
+ 12,
552
+ 21,
553
+ 27,
554
+ 28,
555
+ 23,
556
+ 9,
557
+ 27,
558
+ 36,
559
+ 27,
560
+ 21,
561
+ 33,
562
+ 25,
563
+ 33,
564
+ 27,
565
+ 23
566
+ ]
567
+ },
568
+ 15: {
569
+ number: 15,
570
+ name: "Ezra",
571
+ testament: "OT" /* OLD_TESTAMENT */,
572
+ chapters: 10,
573
+ verses: [11, 70, 13, 24, 17, 22, 28, 36, 15, 44]
574
+ },
575
+ 16: {
576
+ number: 16,
577
+ name: "Nehemiah",
578
+ testament: "OT" /* OLD_TESTAMENT */,
579
+ chapters: 13,
580
+ verses: [11, 20, 32, 23, 19, 19, 73, 18, 38, 39, 36, 47, 31]
581
+ },
582
+ 17: {
583
+ number: 17,
584
+ name: "Esther",
585
+ testament: "OT" /* OLD_TESTAMENT */,
586
+ chapters: 10,
587
+ verses: [22, 23, 15, 17, 14, 14, 10, 17, 32, 3]
588
+ },
589
+ 18: {
590
+ number: 18,
591
+ name: "Job",
592
+ testament: "OT" /* OLD_TESTAMENT */,
593
+ chapters: 42,
594
+ verses: [
595
+ 22,
596
+ 13,
597
+ 26,
598
+ 21,
599
+ 27,
600
+ 30,
601
+ 21,
602
+ 22,
603
+ 35,
604
+ 22,
605
+ 20,
606
+ 25,
607
+ 28,
608
+ 22,
609
+ 35,
610
+ 22,
611
+ 16,
612
+ 21,
613
+ 29,
614
+ 29,
615
+ 34,
616
+ 30,
617
+ 17,
618
+ 25,
619
+ 6,
620
+ 14,
621
+ 23,
622
+ 28,
623
+ 25,
624
+ 31,
625
+ 40,
626
+ 22,
627
+ 33,
628
+ 37,
629
+ 16,
630
+ 33,
631
+ 24,
632
+ 41,
633
+ 30,
634
+ 24,
635
+ 34,
636
+ 17
637
+ ]
638
+ },
639
+ 19: {
640
+ number: 19,
641
+ name: "Psalms",
642
+ testament: "OT" /* OLD_TESTAMENT */,
643
+ chapters: 150,
644
+ verses: [
645
+ 6,
646
+ 12,
647
+ 8,
648
+ 8,
649
+ 12,
650
+ 10,
651
+ 17,
652
+ 9,
653
+ 20,
654
+ 18,
655
+ 7,
656
+ 8,
657
+ 6,
658
+ 7,
659
+ 5,
660
+ 11,
661
+ 15,
662
+ 50,
663
+ 14,
664
+ 9,
665
+ 13,
666
+ 31,
667
+ 6,
668
+ 10,
669
+ 22,
670
+ 12,
671
+ 14,
672
+ 9,
673
+ 11,
674
+ 12,
675
+ 24,
676
+ 11,
677
+ 22,
678
+ 22,
679
+ 28,
680
+ 12,
681
+ 40,
682
+ 22,
683
+ 13,
684
+ 17,
685
+ 13,
686
+ 11,
687
+ 5,
688
+ 26,
689
+ 17,
690
+ 11,
691
+ 9,
692
+ 14,
693
+ 20,
694
+ 23,
695
+ 19,
696
+ 9,
697
+ 6,
698
+ 7,
699
+ 23,
700
+ 13,
701
+ 11,
702
+ 11,
703
+ 17,
704
+ 12,
705
+ 8,
706
+ 12,
707
+ 11,
708
+ 10,
709
+ 13,
710
+ 20,
711
+ 7,
712
+ 35,
713
+ 36,
714
+ 5,
715
+ 24,
716
+ 20,
717
+ 28,
718
+ 23,
719
+ 10,
720
+ 12,
721
+ 20,
722
+ 72,
723
+ 13,
724
+ 19,
725
+ 16,
726
+ 8,
727
+ 18,
728
+ 12,
729
+ 13,
730
+ 17,
731
+ 7,
732
+ 18,
733
+ 52,
734
+ 17,
735
+ 16,
736
+ 15,
737
+ 5,
738
+ 23,
739
+ 11,
740
+ 13,
741
+ 12,
742
+ 9,
743
+ 9,
744
+ 5,
745
+ 8,
746
+ 28,
747
+ 22,
748
+ 35,
749
+ 45,
750
+ 48,
751
+ 43,
752
+ 13,
753
+ 31,
754
+ 7,
755
+ 10,
756
+ 10,
757
+ 9,
758
+ 8,
759
+ 18,
760
+ 19,
761
+ 2,
762
+ 29,
763
+ 176,
764
+ 7,
765
+ 8,
766
+ 9,
767
+ 4,
768
+ 8,
769
+ 5,
770
+ 6,
771
+ 5,
772
+ 6,
773
+ 8,
774
+ 8,
775
+ 3,
776
+ 18,
777
+ 3,
778
+ 3,
779
+ 21,
780
+ 26,
781
+ 9,
782
+ 8,
783
+ 24,
784
+ 13,
785
+ 10,
786
+ 7,
787
+ 12,
788
+ 15,
789
+ 21,
790
+ 10,
791
+ 20,
792
+ 14,
793
+ 9,
794
+ 6
795
+ ]
796
+ },
797
+ 20: {
798
+ number: 20,
799
+ name: "Proverbs",
800
+ testament: "OT" /* OLD_TESTAMENT */,
801
+ chapters: 31,
802
+ verses: [
803
+ 33,
804
+ 22,
805
+ 35,
806
+ 27,
807
+ 23,
808
+ 35,
809
+ 27,
810
+ 36,
811
+ 18,
812
+ 32,
813
+ 31,
814
+ 28,
815
+ 25,
816
+ 35,
817
+ 33,
818
+ 33,
819
+ 28,
820
+ 24,
821
+ 29,
822
+ 30,
823
+ 31,
824
+ 29,
825
+ 35,
826
+ 34,
827
+ 28,
828
+ 28,
829
+ 27,
830
+ 28,
831
+ 27,
832
+ 33,
833
+ 31
834
+ ]
835
+ },
836
+ 21: {
837
+ number: 21,
838
+ name: "Ecclesiastes",
839
+ testament: "OT" /* OLD_TESTAMENT */,
840
+ chapters: 12,
841
+ verses: [18, 26, 22, 16, 20, 12, 29, 17, 18, 20, 10, 14]
842
+ },
843
+ 22: {
844
+ number: 22,
845
+ name: "Song of Songs",
846
+ testament: "OT" /* OLD_TESTAMENT */,
847
+ chapters: 8,
848
+ verses: [17, 17, 11, 16, 16, 13, 13, 14]
849
+ },
850
+ 23: {
851
+ number: 23,
852
+ name: "Isaiah",
853
+ testament: "OT" /* OLD_TESTAMENT */,
854
+ chapters: 66,
855
+ verses: [
856
+ 31,
857
+ 22,
858
+ 26,
859
+ 6,
860
+ 30,
861
+ 13,
862
+ 25,
863
+ 22,
864
+ 21,
865
+ 34,
866
+ 16,
867
+ 6,
868
+ 22,
869
+ 32,
870
+ 9,
871
+ 14,
872
+ 14,
873
+ 7,
874
+ 25,
875
+ 6,
876
+ 17,
877
+ 25,
878
+ 18,
879
+ 23,
880
+ 12,
881
+ 21,
882
+ 13,
883
+ 29,
884
+ 24,
885
+ 33,
886
+ 9,
887
+ 20,
888
+ 24,
889
+ 17,
890
+ 10,
891
+ 22,
892
+ 38,
893
+ 22,
894
+ 8,
895
+ 31,
896
+ 29,
897
+ 25,
898
+ 28,
899
+ 28,
900
+ 25,
901
+ 13,
902
+ 15,
903
+ 22,
904
+ 26,
905
+ 11,
906
+ 23,
907
+ 15,
908
+ 12,
909
+ 17,
910
+ 13,
911
+ 12,
912
+ 21,
913
+ 14,
914
+ 21,
915
+ 22,
916
+ 11,
917
+ 12,
918
+ 19,
919
+ 12,
920
+ 25,
921
+ 24
922
+ ]
923
+ },
924
+ 24: {
925
+ number: 24,
926
+ name: "Jeremiah",
927
+ testament: "OT" /* OLD_TESTAMENT */,
928
+ chapters: 52,
929
+ verses: [
930
+ 19,
931
+ 37,
932
+ 25,
933
+ 31,
934
+ 31,
935
+ 30,
936
+ 34,
937
+ 22,
938
+ 26,
939
+ 25,
940
+ 23,
941
+ 17,
942
+ 27,
943
+ 22,
944
+ 21,
945
+ 21,
946
+ 27,
947
+ 23,
948
+ 15,
949
+ 18,
950
+ 14,
951
+ 30,
952
+ 40,
953
+ 10,
954
+ 38,
955
+ 24,
956
+ 22,
957
+ 17,
958
+ 32,
959
+ 24,
960
+ 40,
961
+ 44,
962
+ 26,
963
+ 22,
964
+ 19,
965
+ 32,
966
+ 21,
967
+ 28,
968
+ 18,
969
+ 16,
970
+ 18,
971
+ 22,
972
+ 13,
973
+ 30,
974
+ 5,
975
+ 28,
976
+ 7,
977
+ 47,
978
+ 39,
979
+ 46,
980
+ 64,
981
+ 34
982
+ ]
983
+ },
984
+ 25: {
985
+ number: 25,
986
+ name: "Lamentations",
987
+ testament: "OT" /* OLD_TESTAMENT */,
988
+ chapters: 5,
989
+ verses: [22, 22, 66, 22, 22]
990
+ },
991
+ 26: {
992
+ number: 26,
993
+ name: "Ezekiel",
994
+ testament: "OT" /* OLD_TESTAMENT */,
995
+ chapters: 48,
996
+ verses: [
997
+ 28,
998
+ 10,
999
+ 27,
1000
+ 17,
1001
+ 17,
1002
+ 14,
1003
+ 27,
1004
+ 18,
1005
+ 11,
1006
+ 22,
1007
+ 25,
1008
+ 28,
1009
+ 23,
1010
+ 23,
1011
+ 8,
1012
+ 63,
1013
+ 24,
1014
+ 32,
1015
+ 14,
1016
+ 49,
1017
+ 32,
1018
+ 31,
1019
+ 49,
1020
+ 27,
1021
+ 17,
1022
+ 21,
1023
+ 36,
1024
+ 26,
1025
+ 21,
1026
+ 26,
1027
+ 18,
1028
+ 32,
1029
+ 33,
1030
+ 31,
1031
+ 15,
1032
+ 38,
1033
+ 28,
1034
+ 23,
1035
+ 29,
1036
+ 49,
1037
+ 26,
1038
+ 20,
1039
+ 27,
1040
+ 31,
1041
+ 25,
1042
+ 24,
1043
+ 23,
1044
+ 35
1045
+ ]
1046
+ },
1047
+ 27: {
1048
+ number: 27,
1049
+ name: "Daniel",
1050
+ testament: "OT" /* OLD_TESTAMENT */,
1051
+ chapters: 12,
1052
+ verses: [21, 49, 30, 37, 31, 28, 28, 27, 27, 21, 45, 13]
1053
+ },
1054
+ 28: {
1055
+ number: 28,
1056
+ name: "Hosea",
1057
+ testament: "OT" /* OLD_TESTAMENT */,
1058
+ chapters: 14,
1059
+ verses: [11, 23, 5, 19, 15, 11, 16, 14, 17, 15, 12, 14, 16, 9]
1060
+ },
1061
+ 29: {
1062
+ number: 29,
1063
+ name: "Joel",
1064
+ testament: "OT" /* OLD_TESTAMENT */,
1065
+ chapters: 3,
1066
+ verses: [20, 32, 21]
1067
+ },
1068
+ 30: {
1069
+ number: 30,
1070
+ name: "Amos",
1071
+ testament: "OT" /* OLD_TESTAMENT */,
1072
+ chapters: 9,
1073
+ verses: [15, 16, 15, 13, 27, 14, 17, 14, 15]
1074
+ },
1075
+ 31: {
1076
+ number: 31,
1077
+ name: "Obadiah",
1078
+ testament: "OT" /* OLD_TESTAMENT */,
1079
+ chapters: 1,
1080
+ verses: [21]
1081
+ },
1082
+ 32: {
1083
+ number: 32,
1084
+ name: "Jonah",
1085
+ testament: "OT" /* OLD_TESTAMENT */,
1086
+ chapters: 4,
1087
+ verses: [17, 10, 10, 11]
1088
+ },
1089
+ 33: {
1090
+ number: 33,
1091
+ name: "Micah",
1092
+ testament: "OT" /* OLD_TESTAMENT */,
1093
+ chapters: 7,
1094
+ verses: [16, 13, 12, 13, 15, 16, 20]
1095
+ },
1096
+ 34: {
1097
+ number: 34,
1098
+ name: "Nahum",
1099
+ testament: "OT" /* OLD_TESTAMENT */,
1100
+ chapters: 3,
1101
+ verses: [15, 13, 19]
1102
+ },
1103
+ 35: {
1104
+ number: 35,
1105
+ name: "Habakkuk",
1106
+ testament: "OT" /* OLD_TESTAMENT */,
1107
+ chapters: 3,
1108
+ verses: [17, 20, 19]
1109
+ },
1110
+ 36: {
1111
+ number: 36,
1112
+ name: "Zephaniah",
1113
+ testament: "OT" /* OLD_TESTAMENT */,
1114
+ chapters: 3,
1115
+ verses: [18, 15, 20]
1116
+ },
1117
+ 37: {
1118
+ number: 37,
1119
+ name: "Haggai",
1120
+ testament: "OT" /* OLD_TESTAMENT */,
1121
+ chapters: 2,
1122
+ verses: [15, 23]
1123
+ },
1124
+ 38: {
1125
+ number: 38,
1126
+ name: "Zechariah",
1127
+ testament: "OT" /* OLD_TESTAMENT */,
1128
+ chapters: 14,
1129
+ verses: [21, 13, 10, 14, 11, 15, 14, 23, 17, 12, 17, 14, 9, 21]
1130
+ },
1131
+ 39: {
1132
+ number: 39,
1133
+ name: "Malachi",
1134
+ testament: "OT" /* OLD_TESTAMENT */,
1135
+ chapters: 4,
1136
+ verses: [14, 17, 18, 6]
1137
+ },
1138
+ 40: {
1139
+ number: 40,
1140
+ name: "Matthew",
1141
+ testament: "NT" /* NEW_TESTAMENT */,
1142
+ chapters: 28,
1143
+ verses: [
1144
+ 25,
1145
+ 23,
1146
+ 17,
1147
+ 25,
1148
+ 48,
1149
+ 34,
1150
+ 29,
1151
+ 34,
1152
+ 38,
1153
+ 42,
1154
+ 30,
1155
+ 50,
1156
+ 58,
1157
+ 36,
1158
+ 39,
1159
+ 28,
1160
+ 27,
1161
+ 35,
1162
+ 30,
1163
+ 34,
1164
+ 46,
1165
+ 46,
1166
+ 39,
1167
+ 51,
1168
+ 46,
1169
+ 75,
1170
+ 66,
1171
+ 20
1172
+ ]
1173
+ },
1174
+ 41: {
1175
+ number: 41,
1176
+ name: "Mark",
1177
+ testament: "NT" /* NEW_TESTAMENT */,
1178
+ chapters: 16,
1179
+ verses: [45, 28, 35, 41, 43, 56, 37, 38, 50, 52, 33, 44, 37, 72, 47, 20]
1180
+ },
1181
+ 42: {
1182
+ number: 42,
1183
+ name: "Luke",
1184
+ testament: "NT" /* NEW_TESTAMENT */,
1185
+ chapters: 24,
1186
+ verses: [
1187
+ 80,
1188
+ 52,
1189
+ 38,
1190
+ 44,
1191
+ 39,
1192
+ 49,
1193
+ 50,
1194
+ 56,
1195
+ 62,
1196
+ 42,
1197
+ 54,
1198
+ 59,
1199
+ 35,
1200
+ 35,
1201
+ 32,
1202
+ 31,
1203
+ 37,
1204
+ 43,
1205
+ 48,
1206
+ 47,
1207
+ 38,
1208
+ 71,
1209
+ 56,
1210
+ 53
1211
+ ]
1212
+ },
1213
+ 43: {
1214
+ number: 43,
1215
+ name: "John",
1216
+ testament: "NT" /* NEW_TESTAMENT */,
1217
+ chapters: 21,
1218
+ verses: [51, 25, 36, 54, 47, 71, 53, 59, 41, 42, 57, 50, 38, 31, 27, 33, 26, 40, 42, 31, 25]
1219
+ },
1220
+ 44: {
1221
+ number: 44,
1222
+ name: "Acts",
1223
+ testament: "NT" /* NEW_TESTAMENT */,
1224
+ chapters: 28,
1225
+ verses: [
1226
+ 26,
1227
+ 47,
1228
+ 26,
1229
+ 37,
1230
+ 42,
1231
+ 15,
1232
+ 60,
1233
+ 40,
1234
+ 43,
1235
+ 48,
1236
+ 30,
1237
+ 25,
1238
+ 52,
1239
+ 28,
1240
+ 41,
1241
+ 40,
1242
+ 34,
1243
+ 28,
1244
+ 41,
1245
+ 38,
1246
+ 40,
1247
+ 30,
1248
+ 35,
1249
+ 27,
1250
+ 27,
1251
+ 32,
1252
+ 44,
1253
+ 31
1254
+ ]
1255
+ },
1256
+ 45: {
1257
+ number: 45,
1258
+ name: "Romans",
1259
+ testament: "NT" /* NEW_TESTAMENT */,
1260
+ chapters: 16,
1261
+ verses: [32, 29, 31, 25, 21, 23, 25, 39, 33, 21, 36, 21, 14, 23, 33, 27]
1262
+ },
1263
+ 46: {
1264
+ number: 46,
1265
+ name: "1 Corinthians",
1266
+ testament: "NT" /* NEW_TESTAMENT */,
1267
+ chapters: 16,
1268
+ verses: [31, 16, 23, 21, 13, 20, 40, 13, 27, 33, 34, 31, 13, 40, 58, 24]
1269
+ },
1270
+ 47: {
1271
+ number: 47,
1272
+ name: "2 Corinthians",
1273
+ testament: "NT" /* NEW_TESTAMENT */,
1274
+ chapters: 13,
1275
+ verses: [24, 17, 18, 18, 21, 18, 16, 24, 15, 18, 33, 21, 14]
1276
+ },
1277
+ 48: {
1278
+ number: 48,
1279
+ name: "Galatians",
1280
+ testament: "NT" /* NEW_TESTAMENT */,
1281
+ chapters: 6,
1282
+ verses: [24, 21, 29, 31, 26, 18]
1283
+ },
1284
+ 49: {
1285
+ number: 49,
1286
+ name: "Ephesians",
1287
+ testament: "NT" /* NEW_TESTAMENT */,
1288
+ chapters: 6,
1289
+ verses: [23, 22, 21, 32, 33, 24]
1290
+ },
1291
+ 50: {
1292
+ number: 50,
1293
+ name: "Philippians",
1294
+ testament: "NT" /* NEW_TESTAMENT */,
1295
+ chapters: 4,
1296
+ verses: [30, 30, 21, 23]
1297
+ },
1298
+ 51: {
1299
+ number: 51,
1300
+ name: "Colossians",
1301
+ testament: "NT" /* NEW_TESTAMENT */,
1302
+ chapters: 4,
1303
+ verses: [29, 23, 25, 18]
1304
+ },
1305
+ 52: {
1306
+ number: 52,
1307
+ name: "1 Thessalonians",
1308
+ testament: "NT" /* NEW_TESTAMENT */,
1309
+ chapters: 5,
1310
+ verses: [10, 20, 13, 18, 28]
1311
+ },
1312
+ 53: {
1313
+ number: 53,
1314
+ name: "2 Thessalonians",
1315
+ testament: "NT" /* NEW_TESTAMENT */,
1316
+ chapters: 3,
1317
+ verses: [12, 17, 18]
1318
+ },
1319
+ 54: {
1320
+ number: 54,
1321
+ name: "1 Timothy",
1322
+ testament: "NT" /* NEW_TESTAMENT */,
1323
+ chapters: 6,
1324
+ verses: [20, 15, 16, 16, 25, 21]
1325
+ },
1326
+ 55: {
1327
+ number: 55,
1328
+ name: "2 Timothy",
1329
+ testament: "NT" /* NEW_TESTAMENT */,
1330
+ chapters: 4,
1331
+ verses: [18, 26, 17, 22]
1332
+ },
1333
+ 56: {
1334
+ number: 56,
1335
+ name: "Titus",
1336
+ testament: "NT" /* NEW_TESTAMENT */,
1337
+ chapters: 3,
1338
+ verses: [16, 15, 15]
1339
+ },
1340
+ 57: {
1341
+ number: 57,
1342
+ name: "Philemon",
1343
+ testament: "NT" /* NEW_TESTAMENT */,
1344
+ chapters: 1,
1345
+ verses: [25]
1346
+ },
1347
+ 58: {
1348
+ number: 58,
1349
+ name: "Hebrews",
1350
+ testament: "NT" /* NEW_TESTAMENT */,
1351
+ chapters: 13,
1352
+ verses: [14, 18, 19, 16, 14, 20, 28, 13, 28, 39, 40, 29, 25]
1353
+ },
1354
+ 59: {
1355
+ number: 59,
1356
+ name: "James",
1357
+ testament: "NT" /* NEW_TESTAMENT */,
1358
+ chapters: 5,
1359
+ verses: [27, 26, 18, 17, 20]
1360
+ },
1361
+ 60: {
1362
+ number: 60,
1363
+ name: "1 Peter",
1364
+ testament: "NT" /* NEW_TESTAMENT */,
1365
+ chapters: 5,
1366
+ verses: [25, 25, 22, 19, 14]
1367
+ },
1368
+ 61: {
1369
+ number: 61,
1370
+ name: "2 Peter",
1371
+ testament: "NT" /* NEW_TESTAMENT */,
1372
+ chapters: 3,
1373
+ verses: [21, 22, 18]
1374
+ },
1375
+ 62: {
1376
+ number: 62,
1377
+ name: "1 John",
1378
+ testament: "NT" /* NEW_TESTAMENT */,
1379
+ chapters: 5,
1380
+ verses: [10, 29, 24, 21, 21]
1381
+ },
1382
+ 63: {
1383
+ number: 63,
1384
+ name: "2 John",
1385
+ testament: "NT" /* NEW_TESTAMENT */,
1386
+ chapters: 1,
1387
+ verses: [13]
1388
+ },
1389
+ 64: {
1390
+ number: 64,
1391
+ name: "3 John",
1392
+ testament: "NT" /* NEW_TESTAMENT */,
1393
+ chapters: 1,
1394
+ verses: [14]
1395
+ },
1396
+ 65: {
1397
+ number: 65,
1398
+ name: "Jude",
1399
+ testament: "NT" /* NEW_TESTAMENT */,
1400
+ chapters: 1,
1401
+ verses: [25]
1402
+ },
1403
+ 66: {
1404
+ number: 66,
1405
+ name: "Revelation",
1406
+ testament: "NT" /* NEW_TESTAMENT */,
1407
+ chapters: 22,
1408
+ verses: [20, 29, 22, 11, 14, 17, 17, 13, 21, 11, 19, 17, 18, 20, 8, 21, 18, 24, 21, 15, 27, 21]
1409
+ }
1410
+ };
1411
+ var BOOK_NAMES = Object.fromEntries(
1412
+ Object.values(BIBLE_STRUCTURE).map((book) => [book.number, book.name])
1413
+ );
1414
+ var BOOK_NUMBERS = Object.fromEntries(
1415
+ Object.values(BIBLE_STRUCTURE).map((book) => [book.name.toLowerCase(), book.number])
1416
+ );
1417
+ var BookName = /* @__PURE__ */ ((BookName2) => {
1418
+ BookName2["GENESIS"] = "Genesis";
1419
+ BookName2["EXODUS"] = "Exodus";
1420
+ BookName2["LEVITICUS"] = "Leviticus";
1421
+ BookName2["NUMBERS"] = "Numbers";
1422
+ BookName2["DEUTERONOMY"] = "Deuteronomy";
1423
+ BookName2["JOSHUA"] = "Joshua";
1424
+ BookName2["JUDGES"] = "Judges";
1425
+ BookName2["RUTH"] = "Ruth";
1426
+ BookName2["SAMUEL_1"] = "1 Samuel";
1427
+ BookName2["SAMUEL_2"] = "2 Samuel";
1428
+ BookName2["KINGS_1"] = "1 Kings";
1429
+ BookName2["KINGS_2"] = "2 Kings";
1430
+ BookName2["CHRONICLES_1"] = "1 Chronicles";
1431
+ BookName2["CHRONICLES_2"] = "2 Chronicles";
1432
+ BookName2["EZRA"] = "Ezra";
1433
+ BookName2["NEHEMIAH"] = "Nehemiah";
1434
+ BookName2["ESTHER"] = "Esther";
1435
+ BookName2["JOB"] = "Job";
1436
+ BookName2["PSALMS"] = "Psalms";
1437
+ BookName2["PROVERBS"] = "Proverbs";
1438
+ BookName2["ECCLESIASTES"] = "Ecclesiastes";
1439
+ BookName2["SONG_OF_SONGS"] = "Song of Songs";
1440
+ BookName2["ISAIAH"] = "Isaiah";
1441
+ BookName2["JEREMIAH"] = "Jeremiah";
1442
+ BookName2["LAMENTATIONS"] = "Lamentations";
1443
+ BookName2["EZEKIEL"] = "Ezekiel";
1444
+ BookName2["DANIEL"] = "Daniel";
1445
+ BookName2["HOSEA"] = "Hosea";
1446
+ BookName2["JOEL"] = "Joel";
1447
+ BookName2["AMOS"] = "Amos";
1448
+ BookName2["OBADIAH"] = "Obadiah";
1449
+ BookName2["JONAH"] = "Jonah";
1450
+ BookName2["MICAH"] = "Micah";
1451
+ BookName2["NAHUM"] = "Nahum";
1452
+ BookName2["HABAKKUK"] = "Habakkuk";
1453
+ BookName2["ZEPHANIAH"] = "Zephaniah";
1454
+ BookName2["HAGGAI"] = "Haggai";
1455
+ BookName2["ZECHARIAH"] = "Zechariah";
1456
+ BookName2["MALACHI"] = "Malachi";
1457
+ BookName2["MATTHEW"] = "Matthew";
1458
+ BookName2["MARK"] = "Mark";
1459
+ BookName2["LUKE"] = "Luke";
1460
+ BookName2["JOHN"] = "John";
1461
+ BookName2["ACTS"] = "Acts";
1462
+ BookName2["ROMANS"] = "Romans";
1463
+ BookName2["CORINTHIANS_1"] = "1 Corinthians";
1464
+ BookName2["CORINTHIANS_2"] = "2 Corinthians";
1465
+ BookName2["GALATIANS"] = "Galatians";
1466
+ BookName2["EPHESIANS"] = "Ephesians";
1467
+ BookName2["PHILIPPIANS"] = "Philippians";
1468
+ BookName2["COLOSSIANS"] = "Colossians";
1469
+ BookName2["THESSALONIANS_1"] = "1 Thessalonians";
1470
+ BookName2["THESSALONIANS_2"] = "2 Thessalonians";
1471
+ BookName2["TIMOTHY_1"] = "1 Timothy";
1472
+ BookName2["TIMOTHY_2"] = "2 Timothy";
1473
+ BookName2["TITUS"] = "Titus";
1474
+ BookName2["PHILEMON"] = "Philemon";
1475
+ BookName2["HEBREWS"] = "Hebrews";
1476
+ BookName2["JAMES"] = "James";
1477
+ BookName2["PETER_1"] = "1 Peter";
1478
+ BookName2["PETER_2"] = "2 Peter";
1479
+ BookName2["JOHN_1"] = "1 John";
1480
+ BookName2["JOHN_2"] = "2 John";
1481
+ BookName2["JOHN_3"] = "3 John";
1482
+ BookName2["JUDE"] = "Jude";
1483
+ BookName2["REVELATION"] = "Revelation";
1484
+ return BookName2;
1485
+ })(BookName || {});
1486
+ var VerseReferenceSchema = z.object({
1487
+ bookNumber: z.number().int().min(1).max(66),
1488
+ chapter: z.number().int().min(1),
1489
+ verse: z.number().int().min(1)
1490
+ }).refine(
1491
+ (data) => {
1492
+ const bookInfo = BIBLE_STRUCTURE[data.bookNumber];
1493
+ if (!bookInfo) return false;
1494
+ return data.chapter <= bookInfo.chapters;
1495
+ },
1496
+ (data) => {
1497
+ const bookInfo = BIBLE_STRUCTURE[data.bookNumber];
1498
+ const bookName = BOOK_NAMES[data.bookNumber];
1499
+ return {
1500
+ message: `${bookName} only has ${bookInfo?.chapters} chapters, but chapter ${data.chapter} was requested`,
1501
+ path: ["chapter"]
1502
+ };
1503
+ }
1504
+ ).refine(
1505
+ (data) => {
1506
+ const bookInfo = BIBLE_STRUCTURE[data.bookNumber];
1507
+ if (!bookInfo) return false;
1508
+ const maxVerse = bookInfo.verses[data.chapter - 1];
1509
+ if (!maxVerse) return false;
1510
+ return data.verse <= maxVerse;
1511
+ },
1512
+ (data) => {
1513
+ const bookInfo = BIBLE_STRUCTURE[data.bookNumber];
1514
+ const bookName = BOOK_NAMES[data.bookNumber];
1515
+ const maxVerse = bookInfo?.verses[data.chapter - 1];
1516
+ return {
1517
+ message: `${bookName} ${data.chapter} only has ${maxVerse} verses, but verse ${data.verse} was requested`,
1518
+ path: ["verse"]
1519
+ };
1520
+ }
1521
+ );
1522
+ function createVerseReference(data) {
1523
+ const validated = VerseReferenceSchema.parse(data);
1524
+ return {
1525
+ ...validated,
1526
+ get bookName() {
1527
+ return BookName[BOOK_NAMES[this.bookNumber]] || BOOK_NAMES[this.bookNumber];
1528
+ },
1529
+ toString() {
1530
+ return `${BOOK_NAMES[this.bookNumber]} ${this.chapter}:${this.verse}`;
1531
+ }
1532
+ };
1533
+ }
1534
+ var TextSegmentSchema = z.object({
1535
+ text: z.string(),
1536
+ isRedLetter: z.boolean().default(false),
1537
+ // Words of Jesus in red
1538
+ isItalic: z.boolean().default(false),
1539
+ // Italicized text (clarifications)
1540
+ isBold: z.boolean().default(false),
1541
+ // Bold text
1542
+ isSmallCaps: z.boolean().default(false)
1543
+ // LORD (Yahweh) in small caps
1544
+ });
1545
+ var VerseContentSchema = z.object({
1546
+ reference: VerseReferenceSchema,
1547
+ verseNumber: z.number().int().min(1),
1548
+ segments: z.array(TextSegmentSchema),
1549
+ hasSubheading: z.boolean().default(false),
1550
+ subheadingText: z.string().nullable().default(null),
1551
+ isPoetry: z.boolean().default(false),
1552
+ isProse: z.boolean().default(false),
1553
+ chapterStart: z.boolean().default(false)
1554
+ // First verse of chapter
1555
+ });
1556
+ function createVerseContent(data) {
1557
+ const validated = VerseContentSchema.parse(data);
1558
+ const reference = createVerseReference(validated.reference);
1559
+ return {
1560
+ ...validated,
1561
+ reference,
1562
+ get plainText() {
1563
+ return this.segments.map((seg) => seg.text).join(" ");
1564
+ },
1565
+ get formattedText() {
1566
+ const parts = this.segments.map((seg) => {
1567
+ let text = seg.text;
1568
+ if (seg.isItalic) text = `[${text}]`;
1569
+ if (seg.isRedLetter) text = `"${text}"`;
1570
+ return text;
1571
+ });
1572
+ return parts.join(" ");
1573
+ }
1574
+ };
1575
+ }
1576
+ var PassageSchema = z.object({
1577
+ fromRef: VerseReferenceSchema,
1578
+ toRef: VerseReferenceSchema,
1579
+ title: z.string(),
1580
+ verses: z.array(VerseContentSchema)
1581
+ });
1582
+ function createPassage(data) {
1583
+ const validated = PassageSchema.parse(data);
1584
+ const fromRef = createVerseReference(validated.fromRef);
1585
+ const toRef = createVerseReference(validated.toRef);
1586
+ const verses = validated.verses.map((v) => createVerseContent(v));
1587
+ return {
1588
+ ...validated,
1589
+ fromRef,
1590
+ toRef,
1591
+ verses,
1592
+ get isSingleVerse() {
1593
+ return this.fromRef.bookNumber === this.toRef.bookNumber && this.fromRef.chapter === this.toRef.chapter && this.fromRef.verse === this.toRef.verse;
1594
+ },
1595
+ get verseCount() {
1596
+ return this.verses.length;
1597
+ }
1598
+ };
1599
+ }
1600
+ var SearchResponseSchema = z.object({
1601
+ query: z.string(),
1602
+ matchCount: z.number().int().min(0),
1603
+ passages: z.array(PassageSchema),
1604
+ durationMs: z.number().int().min(0),
1605
+ timestamp: z.number().int(),
1606
+ // Optional metadata for text searches
1607
+ totalCount: z.number().int().min(0).optional(),
1608
+ filteredCount: z.number().int().min(0).optional(),
1609
+ countsByBook: z.record(z.string(), z.number()).optional(),
1610
+ countsBySection: z.record(z.string(), z.number()).optional()
1611
+ });
1612
+ function createSearchResponse(data) {
1613
+ const validated = SearchResponseSchema.parse(data);
1614
+ const passages = validated.passages.map((p) => createPassage(p));
1615
+ return {
1616
+ ...validated,
1617
+ passages,
1618
+ get passageCount() {
1619
+ return this.passages.length;
1620
+ },
1621
+ get totalVerses() {
1622
+ return this.passages.reduce((sum, p) => sum + p.verseCount, 0);
1623
+ },
1624
+ get hasSearchMetadata() {
1625
+ return this.totalCount !== void 0;
1626
+ }
1627
+ };
1628
+ }
1629
+
1630
+ // src/mcp/resources.ts
1631
+ function registerResources(server) {
1632
+ server.registerResource(
1633
+ "books",
1634
+ "bible://books",
1635
+ {
1636
+ title: "Bible Books",
1637
+ description: "List of all 66 books in the Bible",
1638
+ mimeType: "application/json"
1639
+ },
1640
+ async (uri) => {
1641
+ const books = Object.values(BIBLE_STRUCTURE).map(
1642
+ (bookInfo) => ({
1643
+ name: bookInfo.name,
1644
+ testament: bookInfo.testament,
1645
+ chapters: bookInfo.chapters,
1646
+ verses: bookInfo.verses.reduce((sum, count) => sum + count, 0)
1647
+ })
1648
+ );
1649
+ return {
1650
+ contents: [
1651
+ {
1652
+ uri: uri.href,
1653
+ mimeType: "application/json",
1654
+ text: JSON.stringify(books, null, 2)
1655
+ }
1656
+ ]
1657
+ };
1658
+ }
1659
+ );
1660
+ server.registerResource(
1661
+ "structure",
1662
+ new ResourceTemplate("bible://structure/{book}", {
1663
+ list: void 0,
1664
+ complete: {
1665
+ book: (value) => {
1666
+ return Object.values(BookName).filter((bookName) => bookName.toLowerCase().startsWith(value.toLowerCase())).slice(0, 10);
1667
+ }
1668
+ }
1669
+ }),
1670
+ {
1671
+ title: "Bible Book Structure",
1672
+ description: "Chapter and verse counts for a specific book",
1673
+ mimeType: "application/json"
1674
+ },
1675
+ async (uri, { book }) => {
1676
+ const bookName = book;
1677
+ const bookEntry = Object.values(BIBLE_STRUCTURE).find(
1678
+ (b) => b.name.toLowerCase() === bookName.toLowerCase()
1679
+ );
1680
+ if (!bookEntry) {
1681
+ throw new Error(`Unknown book: ${bookName}`);
1682
+ }
1683
+ const data = {
1684
+ uri: uri.href,
1685
+ book: bookEntry.name,
1686
+ testament: bookEntry.testament,
1687
+ totalChapters: bookEntry.chapters,
1688
+ totalVerses: bookEntry.verses.reduce((sum, verses) => sum + verses, 0),
1689
+ chapters: bookEntry.verses.map((verses, index) => ({
1690
+ chapter: index + 1,
1691
+ verses
1692
+ }))
1693
+ };
1694
+ return {
1695
+ contents: [
1696
+ {
1697
+ uri: uri.href,
1698
+ mimeType: "application/json",
1699
+ text: JSON.stringify(data, null, 2)
1700
+ }
1701
+ ]
1702
+ };
1703
+ }
1704
+ );
1705
+ }
1706
+
1707
+ // src/cache/memory.ts
1708
+ var MemoryCacheProvider = class {
1709
+ cache = /* @__PURE__ */ new Map();
1710
+ async get(key) {
1711
+ const entry = this.cache.get(key);
1712
+ if (!entry) {
1713
+ return void 0;
1714
+ }
1715
+ if (Date.now() > entry.expires) {
1716
+ this.cache.delete(key);
1717
+ return void 0;
1718
+ }
1719
+ return entry.data;
1720
+ }
1721
+ async set(key, value, ttl) {
1722
+ const expires = Date.now() + ttl * 1e3;
1723
+ this.cache.set(key, { data: value, expires });
1724
+ }
1725
+ /**
1726
+ * Clear all cache entries.
1727
+ *
1728
+ * Useful for testing or manual cache invalidation.
1729
+ */
1730
+ clear() {
1731
+ this.cache.clear();
1732
+ }
1733
+ /**
1734
+ * Get the number of entries in the cache.
1735
+ *
1736
+ * Includes expired entries that haven't been accessed yet.
1737
+ */
1738
+ size() {
1739
+ return this.cache.size;
1740
+ }
1741
+ };
1742
+
1743
+ // src/cache/interface.ts
1744
+ var CacheTTL = {
1745
+ /** 1 month - for immutable Bible content (verses, passages, chapters) */
1746
+ BIBLE_CONTENT: 2592e3,
1747
+ /** 1 week - for search results that may change with API updates */
1748
+ SEARCH_RESULTS: 604800};
1749
+
1750
+ // src/exceptions.ts
1751
+ var LSBibleError = class extends Error {
1752
+ constructor(message) {
1753
+ super(message);
1754
+ this.name = "LSBibleError";
1755
+ if (Error.captureStackTrace) {
1756
+ Error.captureStackTrace(this, this.constructor);
1757
+ }
1758
+ }
1759
+ };
1760
+ var InvalidReferenceError = class extends LSBibleError {
1761
+ constructor(message) {
1762
+ super(message);
1763
+ this.name = "InvalidReferenceError";
1764
+ }
1765
+ };
1766
+ var APIError = class extends LSBibleError {
1767
+ constructor(message) {
1768
+ super(message);
1769
+ this.name = "APIError";
1770
+ }
1771
+ };
1772
+ var BuildIDError = class extends LSBibleError {
1773
+ constructor(message) {
1774
+ super(message);
1775
+ this.name = "BuildIDError";
1776
+ }
1777
+ };
1778
+ function parseSearchResultHtml(html) {
1779
+ const { document } = parseHTML(`<!DOCTYPE html><html><body>${html}</body></html>`);
1780
+ return document.body.textContent?.trim() ?? "";
1781
+ }
1782
+ function parsePassageHtml(html) {
1783
+ const { document } = parseHTML(`<!DOCTYPE html><html><body>${html}</body></html>`);
1784
+ const verses = [];
1785
+ const verseSpans = document.querySelectorAll("span.verse");
1786
+ for (const verseSpan of verseSpans) {
1787
+ const verse = parseVerse(verseSpan);
1788
+ verses.push(verse);
1789
+ }
1790
+ return verses;
1791
+ }
1792
+ function parseVerse(verseSpan, _document) {
1793
+ const dataKey = verseSpan.getAttribute("data-key");
1794
+ if (!dataKey) {
1795
+ throw new Error("Verse span missing data-key attribute");
1796
+ }
1797
+ const reference = parseVerseReference(dataKey);
1798
+ const verseNumElem = verseSpan.querySelector("small[data-verse]");
1799
+ let verseNumber;
1800
+ if (verseNumElem) {
1801
+ verseNumber = Number.parseInt(verseNumElem.getAttribute("data-verse") ?? "0", 10);
1802
+ } else {
1803
+ verseNumber = reference.verse;
1804
+ }
1805
+ let hasSubheading = false;
1806
+ let subheadingText = null;
1807
+ const prevSibling = verseSpan.previousElementSibling;
1808
+ if (prevSibling?.classList.contains("subhead")) {
1809
+ hasSubheading = true;
1810
+ subheadingText = prevSibling.textContent?.trim() ?? null;
1811
+ }
1812
+ let currentNode = verseSpan;
1813
+ let isPoetry = false;
1814
+ while (currentNode) {
1815
+ if (currentNode.classList?.contains("poetry")) {
1816
+ isPoetry = true;
1817
+ break;
1818
+ }
1819
+ currentNode = currentNode.parentElement;
1820
+ }
1821
+ const isProse = verseSpan.querySelector(".prose") !== null;
1822
+ const chapterStart = verseSpan.querySelector(".chapter-number") !== null;
1823
+ const segments = extractSegments(verseSpan);
1824
+ return createVerseContent({
1825
+ reference,
1826
+ verseNumber,
1827
+ segments,
1828
+ hasSubheading,
1829
+ subheadingText,
1830
+ isPoetry,
1831
+ isProse,
1832
+ chapterStart
1833
+ });
1834
+ }
1835
+ function parseVerseReference(dataKey) {
1836
+ const parts = dataKey.split("-");
1837
+ if (parts.length !== 3) {
1838
+ throw new Error(`Invalid data-key format: ${dataKey}`);
1839
+ }
1840
+ const bookNumber = Number.parseInt(parts[0] ?? "0", 10);
1841
+ const chapter = Number.parseInt(parts[1] ?? "0", 10);
1842
+ const verse = Number.parseInt(parts[2] ?? "0", 10);
1843
+ return createVerseReference({ bookNumber, chapter, verse });
1844
+ }
1845
+ function extractSegments(element) {
1846
+ const segments = [];
1847
+ for (const smallElem of element.querySelectorAll("small[data-verse]")) {
1848
+ smallElem.remove();
1849
+ }
1850
+ for (const chapterElem of element.querySelectorAll(".chapter-number")) {
1851
+ chapterElem.remove();
1852
+ }
1853
+ for (const subheadElem of element.querySelectorAll(".subhead")) {
1854
+ subheadElem.remove();
1855
+ }
1856
+ extractTextNodes(element, segments);
1857
+ return segments;
1858
+ }
1859
+ function extractTextNodes(node, segments) {
1860
+ for (const child of node.childNodes) {
1861
+ if (child.nodeType === 3) {
1862
+ const text = child.textContent?.trim();
1863
+ if (!text) continue;
1864
+ let isRedLetter = false;
1865
+ let isItalic = false;
1866
+ let isBold = false;
1867
+ let isSmallCaps = false;
1868
+ let parent = child.parentElement;
1869
+ while (parent) {
1870
+ if (parent.classList.contains("red-letter")) {
1871
+ isRedLetter = true;
1872
+ }
1873
+ if (parent.tagName === "I" || parent.classList.contains("italic")) {
1874
+ isItalic = true;
1875
+ }
1876
+ if (parent.tagName === "B" || parent.classList.contains("bold")) {
1877
+ isBold = true;
1878
+ }
1879
+ if (parent.classList.contains("small-caps")) {
1880
+ isSmallCaps = true;
1881
+ }
1882
+ parent = parent.parentElement;
1883
+ }
1884
+ segments.push({
1885
+ text,
1886
+ isRedLetter,
1887
+ isItalic,
1888
+ isBold,
1889
+ isSmallCaps
1890
+ });
1891
+ } else if (child.nodeType === 1) {
1892
+ extractTextNodes(child, segments);
1893
+ }
1894
+ }
1895
+ }
1896
+
1897
+ // src/validators.ts
1898
+ function normalizeBookName(book) {
1899
+ if (typeof book === "string" && Object.values(BookName).includes(book)) {
1900
+ return book;
1901
+ }
1902
+ if (typeof book !== "string") {
1903
+ throw new InvalidReferenceError(`Invalid book type: ${typeof book}`);
1904
+ }
1905
+ const bookStr = book.trim();
1906
+ const bookLower = bookStr.toLowerCase();
1907
+ if (bookLower in BOOK_NUMBERS) {
1908
+ const bookNum = BOOK_NUMBERS[bookLower];
1909
+ if (bookNum) {
1910
+ return BIBLE_STRUCTURE[bookNum]?.name ?? book;
1911
+ }
1912
+ }
1913
+ const normalized = bookLower.split(/\s+/).join(" ");
1914
+ if (normalized in BOOK_NUMBERS) {
1915
+ const bookNum = BOOK_NUMBERS[normalized];
1916
+ if (bookNum) {
1917
+ return BIBLE_STRUCTURE[bookNum]?.name ?? book;
1918
+ }
1919
+ }
1920
+ if (/^\d/.test(bookLower)) {
1921
+ const spaced = bookLower.replace(/^(\d+)([a-z])/, "$1 $2");
1922
+ if (spaced in BOOK_NUMBERS) {
1923
+ const bookNum = BOOK_NUMBERS[spaced];
1924
+ if (bookNum) {
1925
+ return BIBLE_STRUCTURE[bookNum]?.name ?? book;
1926
+ }
1927
+ }
1928
+ }
1929
+ throw new InvalidReferenceError(`Unknown book: ${book}`);
1930
+ }
1931
+ function getBookNumber(book) {
1932
+ const normalized = normalizeBookName(book);
1933
+ const bookNum = BOOK_NUMBERS[normalized.toLowerCase()];
1934
+ if (!bookNum) {
1935
+ throw new InvalidReferenceError(`Unknown book: ${book}`);
1936
+ }
1937
+ return bookNum;
1938
+ }
1939
+ function validateReference(book, chapter, verse) {
1940
+ try {
1941
+ const bookNumber = getBookNumber(book);
1942
+ return createVerseReference({
1943
+ bookNumber,
1944
+ chapter,
1945
+ verse
1946
+ });
1947
+ } catch (error) {
1948
+ if (error instanceof Error) {
1949
+ throw new InvalidReferenceError(error.message);
1950
+ }
1951
+ throw error;
1952
+ }
1953
+ }
1954
+
1955
+ // src/client.ts
1956
+ function getSdkVersion() {
1957
+ return "0.1.0";
1958
+ }
1959
+ function getUserAgent() {
1960
+ const version = getSdkVersion();
1961
+ const runtime = "Bun" in globalThis ? "Bun" : "Deno" in globalThis ? "Deno" : "Node.js";
1962
+ return `lsbible-typescript/${version} (${runtime}; +https://github.com/kdcokenny/lsbible)`;
1963
+ }
1964
+ var LSBibleClient = class _LSBibleClient {
1965
+ static BASE_URL = "https://read.lsbible.org";
1966
+ cacheProvider;
1967
+ cacheTtls;
1968
+ timeout;
1969
+ buildId;
1970
+ buildIdFetched;
1971
+ headers;
1972
+ /**
1973
+ * Create a new LSBible client.
1974
+ *
1975
+ * @param options - Client configuration options
1976
+ */
1977
+ constructor(options = {}) {
1978
+ this.cacheProvider = options.cache?.provider ?? void 0;
1979
+ this.cacheTtls = {
1980
+ verse: options.cache?.ttl?.verse ?? CacheTTL.BIBLE_CONTENT,
1981
+ passage: options.cache?.ttl?.passage ?? CacheTTL.BIBLE_CONTENT,
1982
+ chapter: options.cache?.ttl?.chapter ?? CacheTTL.BIBLE_CONTENT,
1983
+ search: options.cache?.ttl?.search ?? CacheTTL.SEARCH_RESULTS
1984
+ };
1985
+ this.timeout = (options.timeout ?? 30) * 1e3;
1986
+ this.buildId = options.buildId;
1987
+ this.buildIdFetched = options.buildId !== void 0;
1988
+ this.headers = {
1989
+ "User-Agent": getUserAgent(),
1990
+ Accept: "*/*",
1991
+ ...options.headers
1992
+ };
1993
+ }
1994
+ /**
1995
+ * Get the Next.js build ID.
1996
+ *
1997
+ * Strategies:
1998
+ * 1. Use cached/provided build ID
1999
+ * 2. Extract from homepage HTML (__NEXT_DATA__ script)
2000
+ * 3. Try to find it in script tags
2001
+ *
2002
+ * @throws BuildIDError if build ID cannot be determined
2003
+ */
2004
+ async getBuildId() {
2005
+ if (this.buildId && this.buildIdFetched) {
2006
+ return this.buildId;
2007
+ }
2008
+ try {
2009
+ const controller = new AbortController();
2010
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2011
+ const response = await fetch(_LSBibleClient.BASE_URL, {
2012
+ signal: controller.signal,
2013
+ headers: this.headers
2014
+ });
2015
+ clearTimeout(timeoutId);
2016
+ if (!response.ok) {
2017
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
2018
+ }
2019
+ const html = await response.text();
2020
+ const buildIdMatch = html.match(/"buildId":"([^"]+)"/);
2021
+ if (buildIdMatch?.[1]) {
2022
+ this.buildId = buildIdMatch[1];
2023
+ this.buildIdFetched = true;
2024
+ return this.buildId;
2025
+ }
2026
+ const scriptMatch = html.match(/\/_next\/data\/([^/]+)\//);
2027
+ if (scriptMatch?.[1]) {
2028
+ this.buildId = scriptMatch[1];
2029
+ this.buildIdFetched = true;
2030
+ return this.buildId;
2031
+ }
2032
+ } catch (error) {
2033
+ throw new BuildIDError(
2034
+ `Failed to fetch build ID: ${error instanceof Error ? error.message : String(error)}`
2035
+ );
2036
+ }
2037
+ throw new BuildIDError("Could not determine build ID from homepage");
2038
+ }
2039
+ /**
2040
+ * Cache wrapper for async operations.
2041
+ *
2042
+ * @param key - Cache key
2043
+ * @param ttl - Time to live in seconds
2044
+ * @param fetcher - Function to fetch fresh data
2045
+ * @returns Cached or fresh data
2046
+ */
2047
+ async withCache(key, ttl, fetcher) {
2048
+ if (!this.cacheProvider) {
2049
+ return fetcher();
2050
+ }
2051
+ const cached = await this.cacheProvider.get(key);
2052
+ if (cached !== void 0) {
2053
+ return cached;
2054
+ }
2055
+ const data = await fetcher();
2056
+ await this.cacheProvider.set(key, data, ttl);
2057
+ return data;
2058
+ }
2059
+ /**
2060
+ * Make a request to the LSBible API.
2061
+ *
2062
+ * @param query - Search query or verse reference
2063
+ * @returns API response JSON
2064
+ * @throws APIError if request fails
2065
+ */
2066
+ async makeRequest(query) {
2067
+ const buildId = await this.getBuildId();
2068
+ const url = new URL(`${_LSBibleClient.BASE_URL}/_next/data/${buildId}/index.json`);
2069
+ url.searchParams.set("q", query);
2070
+ try {
2071
+ const controller = new AbortController();
2072
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
2073
+ const response = await fetch(url.toString(), {
2074
+ signal: controller.signal,
2075
+ headers: {
2076
+ ...this.headers,
2077
+ referer: `${_LSBibleClient.BASE_URL}/`,
2078
+ "x-nextjs-data": "1"
2079
+ }
2080
+ });
2081
+ clearTimeout(timeoutId);
2082
+ if (!response.ok) {
2083
+ throw new APIError(
2084
+ `API request failed with status ${response.status}: ${response.statusText}`
2085
+ );
2086
+ }
2087
+ return await response.json();
2088
+ } catch (error) {
2089
+ if (error instanceof APIError) {
2090
+ throw error;
2091
+ }
2092
+ throw new APIError(
2093
+ `API request failed: ${error instanceof Error ? error.message : String(error)}`
2094
+ );
2095
+ }
2096
+ }
2097
+ /**
2098
+ * Detect if response is from text search or Bible reference lookup.
2099
+ *
2100
+ * Text searches have initialItems array with metadata.
2101
+ * Bible reference lookups have passages array with full HTML.
2102
+ */
2103
+ isTextSearch(pageProps) {
2104
+ return "initialItems" in pageProps && "totalCount" in pageProps;
2105
+ }
2106
+ /**
2107
+ * Parse text search results from initialItems format.
2108
+ */
2109
+ parseSearchResults(pageProps, query) {
2110
+ const passages = [];
2111
+ const initialItems = pageProps.initialItems ?? [];
2112
+ for (const item of initialItems) {
2113
+ const key = item.key;
2114
+ const parts = key.split("-");
2115
+ if (parts.length !== 3) continue;
2116
+ const bookNumber = Number.parseInt(parts[0] ?? "0", 10);
2117
+ const chapter = Number.parseInt(parts[1] ?? "0", 10);
2118
+ const verse = Number.parseInt(parts[2] ?? "0", 10);
2119
+ const verseRef = createVerseReference({ bookNumber, chapter, verse });
2120
+ const htmlSnippet = item.html ?? "";
2121
+ const plainText = parseSearchResultHtml(htmlSnippet);
2122
+ const segment = {
2123
+ text: plainText,
2124
+ isRedLetter: false,
2125
+ isItalic: false,
2126
+ isBold: false,
2127
+ isSmallCaps: false
2128
+ };
2129
+ const verseContent = createVerseContent({
2130
+ reference: verseRef,
2131
+ verseNumber: verse,
2132
+ segments: [segment],
2133
+ hasSubheading: false,
2134
+ subheadingText: null,
2135
+ isPoetry: false,
2136
+ isProse: false,
2137
+ chapterStart: false
2138
+ });
2139
+ passages.push(
2140
+ createPassage({
2141
+ fromRef: verseRef,
2142
+ toRef: verseRef,
2143
+ title: verseRef.toString(),
2144
+ verses: [verseContent]
2145
+ })
2146
+ );
2147
+ }
2148
+ const totalCount = pageProps.totalCount ?? 0;
2149
+ const filteredCount = pageProps.filteredCount ?? 0;
2150
+ const countsByBook = pageProps.countsByBook;
2151
+ const countsBySection = pageProps.countsBySection;
2152
+ return createSearchResponse({
2153
+ query,
2154
+ matchCount: totalCount,
2155
+ passages,
2156
+ durationMs: pageProps.duration ?? 0,
2157
+ timestamp: pageProps.start ?? 0,
2158
+ totalCount,
2159
+ filteredCount,
2160
+ countsByBook,
2161
+ countsBySection
2162
+ });
2163
+ }
2164
+ /**
2165
+ * Parse Bible reference lookup results from passages format.
2166
+ */
2167
+ parseReferenceResults(pageProps, query) {
2168
+ const passages = [];
2169
+ const passagesData = pageProps.passages ?? [];
2170
+ for (const passageData of passagesData) {
2171
+ const fromData = passageData.from;
2172
+ const toData = passageData.to;
2173
+ const fromRef = createVerseReference({
2174
+ bookNumber: fromData.bn ?? 0,
2175
+ chapter: fromData.cn ?? 0,
2176
+ verse: fromData.vn ?? 0
2177
+ });
2178
+ const toRef = createVerseReference({
2179
+ bookNumber: toData.bn ?? 0,
2180
+ chapter: toData.cn ?? 0,
2181
+ verse: toData.vn ?? 0
2182
+ });
2183
+ const passageHtml = passageData.passageHtml ?? "";
2184
+ const verses = parsePassageHtml(passageHtml);
2185
+ passages.push(
2186
+ createPassage({
2187
+ fromRef,
2188
+ toRef,
2189
+ title: passageData.title ?? "",
2190
+ verses
2191
+ })
2192
+ );
2193
+ }
2194
+ return createSearchResponse({
2195
+ query,
2196
+ matchCount: pageProps.searchMatchCount ?? 0,
2197
+ passages,
2198
+ durationMs: pageProps.duration ?? 0,
2199
+ timestamp: pageProps.start ?? 0
2200
+ });
2201
+ }
2202
+ /**
2203
+ * Parse API response into SearchResponse.
2204
+ */
2205
+ parseResponse(data, query) {
2206
+ const responseData = data;
2207
+ const pageProps = responseData.pageProps ?? {};
2208
+ if (this.isTextSearch(pageProps)) {
2209
+ return this.parseSearchResults(pageProps, query);
2210
+ }
2211
+ return this.parseReferenceResults(pageProps, query);
2212
+ }
2213
+ /**
2214
+ * Search for passages containing text.
2215
+ *
2216
+ * @param query - Search text (e.g., "love", "faith")
2217
+ * @returns SearchResponse with structured passage data
2218
+ * @throws APIError if API request fails
2219
+ */
2220
+ async search(query) {
2221
+ return this.withCache(`search:${query}`, this.cacheTtls.search, async () => {
2222
+ const data = await this.makeRequest(query);
2223
+ return this.parseResponse(data, query);
2224
+ });
2225
+ }
2226
+ /**
2227
+ * Get a specific verse with validated parameters.
2228
+ *
2229
+ * @param book - Book name as enum or string
2230
+ * @param chapter - Chapter number (validated against book)
2231
+ * @param verse - Verse number (validated against chapter)
2232
+ * @returns Single Passage containing the verse
2233
+ * @throws InvalidReferenceError if book/chapter/verse combination is invalid
2234
+ * @throws APIError if API request fails
2235
+ *
2236
+ * @example
2237
+ * ```ts
2238
+ * // Using enum (recommended - type-safe with autocomplete)
2239
+ * const passage = await client.getVerse(BookName.JOHN, 3, 16);
2240
+ *
2241
+ * // Using string (validated at runtime)
2242
+ * const passage2 = await client.getVerse("John", 3, 16);
2243
+ * ```
2244
+ */
2245
+ async getVerse(book, chapter, verse) {
2246
+ validateReference(book, chapter, verse);
2247
+ const bookName = normalizeBookName(book);
2248
+ const query = `${bookName} ${chapter}:${verse}`;
2249
+ return this.withCache(`verse:${query}`, this.cacheTtls.verse, async () => {
2250
+ const response = await this.search(query);
2251
+ if (response.passages.length === 0) {
2252
+ throw new APIError(`No passage found for ${query}`);
2253
+ }
2254
+ const passage = response.passages[0];
2255
+ if (!passage) {
2256
+ throw new APIError(`No passage found for ${query}`);
2257
+ }
2258
+ return passage;
2259
+ });
2260
+ }
2261
+ /**
2262
+ * Get a passage spanning multiple verses.
2263
+ *
2264
+ * @param fromBook - Starting book
2265
+ * @param fromChapter - Starting chapter
2266
+ * @param fromVerse - Starting verse
2267
+ * @param toBook - Ending book
2268
+ * @param toChapter - Ending chapter
2269
+ * @param toVerse - Ending verse
2270
+ * @returns Passage containing all verses in range
2271
+ * @throws InvalidReferenceError if any reference is invalid
2272
+ * @throws APIError if API request fails
2273
+ *
2274
+ * @example
2275
+ * ```ts
2276
+ * // Get John 3:16-18
2277
+ * const passage = await client.getPassage(
2278
+ * BookName.JOHN, 3, 16,
2279
+ * BookName.JOHN, 3, 18
2280
+ * );
2281
+ * ```
2282
+ */
2283
+ async getPassage(fromBook, fromChapter, fromVerse, toBook, toChapter, toVerse) {
2284
+ const fromRef = validateReference(fromBook, fromChapter, fromVerse);
2285
+ const toRef = validateReference(toBook, toChapter, toVerse);
2286
+ const fromBookName = normalizeBookName(fromBook);
2287
+ const toBookName = normalizeBookName(toBook);
2288
+ let query;
2289
+ if (fromRef.bookNumber === toRef.bookNumber) {
2290
+ if (fromChapter === toChapter) {
2291
+ query = `${fromBookName} ${fromChapter}:${fromVerse}-${toVerse}`;
2292
+ } else {
2293
+ query = `${fromBookName} ${fromChapter}:${fromVerse}-${toChapter}:${toVerse}`;
2294
+ }
2295
+ } else {
2296
+ query = `${fromBookName} ${fromChapter}:${fromVerse}-${toBookName} ${toChapter}:${toVerse}`;
2297
+ }
2298
+ return this.withCache(`passage:${query}`, this.cacheTtls.passage, async () => {
2299
+ const response = await this.search(query);
2300
+ if (response.passages.length === 0) {
2301
+ throw new APIError(`No passage found for ${query}`);
2302
+ }
2303
+ const passage = response.passages[0];
2304
+ if (!passage) {
2305
+ throw new APIError(`No passage found for ${query}`);
2306
+ }
2307
+ return passage;
2308
+ });
2309
+ }
2310
+ /**
2311
+ * Get an entire chapter.
2312
+ *
2313
+ * @param book - Book name
2314
+ * @param chapter - Chapter number
2315
+ * @returns Passage containing all verses in the chapter
2316
+ * @throws InvalidReferenceError if book/chapter is invalid
2317
+ * @throws APIError if API request fails
2318
+ *
2319
+ * @example
2320
+ * ```ts
2321
+ * // Get all of John chapter 3
2322
+ * const passage = await client.getChapter(BookName.JOHN, 3);
2323
+ * ```
2324
+ */
2325
+ async getChapter(book, chapter) {
2326
+ const bookNumber = getBookNumber(book);
2327
+ const bookInfo = BIBLE_STRUCTURE[bookNumber];
2328
+ if (!bookInfo) {
2329
+ throw new APIError(`Invalid book number: ${bookNumber}`);
2330
+ }
2331
+ const maxChapter = bookInfo.chapters;
2332
+ if (chapter < 1 || chapter > maxChapter) {
2333
+ const bookName2 = normalizeBookName(book);
2334
+ throw new APIError(
2335
+ `${bookName2} only has ${maxChapter} chapters, but chapter ${chapter} was requested`
2336
+ );
2337
+ }
2338
+ const bookName = normalizeBookName(book);
2339
+ const query = `${bookName} ${chapter}`;
2340
+ return this.withCache(`chapter:${query}`, this.cacheTtls.chapter, async () => {
2341
+ const response = await this.search(query);
2342
+ if (response.passages.length === 0) {
2343
+ throw new APIError(`No passage found for ${query}`);
2344
+ }
2345
+ const passage = response.passages[0];
2346
+ if (!passage) {
2347
+ throw new APIError(`No passage found for ${query}`);
2348
+ }
2349
+ return passage;
2350
+ });
2351
+ }
2352
+ /**
2353
+ * Clear all cached data.
2354
+ *
2355
+ * This method calls the cache provider's clear method if one is configured.
2356
+ * If the cache provider doesn't implement a clear method, this is a no-op.
2357
+ *
2358
+ * @example
2359
+ * ```ts
2360
+ * const client = new LSBibleClient({
2361
+ * cache: { provider: new MemoryCacheProvider() }
2362
+ * });
2363
+ *
2364
+ * // Clear all cached data
2365
+ * client.clearCache();
2366
+ * ```
2367
+ */
2368
+ clearCache() {
2369
+ if (this.cacheProvider && "clear" in this.cacheProvider) {
2370
+ this.cacheProvider.clear();
2371
+ }
2372
+ }
2373
+ };
2374
+
2375
+ // src/mcp/tools.ts
2376
+ function registerTools(server, client) {
2377
+ const apiClient = new LSBibleClient({
2378
+ cache: {
2379
+ provider: new MemoryCacheProvider()
2380
+ }
2381
+ });
2382
+ server.registerTool(
2383
+ "get_verse",
2384
+ {
2385
+ title: "Get Bible Verse",
2386
+ description: "Fetch a single Bible verse by book, chapter, and verse",
2387
+ inputSchema: {
2388
+ book: z.nativeEnum(BookName).describe("Book name (e.g., John, Genesis)"),
2389
+ chapter: z.number().int().positive().describe("Chapter number"),
2390
+ verse: z.number().int().positive().describe("Verse number")
2391
+ },
2392
+ outputSchema: {
2393
+ reference: z.object({
2394
+ book: z.string(),
2395
+ chapter: z.number(),
2396
+ verse: z.number()
2397
+ }),
2398
+ text: z.string(),
2399
+ segments: z.array(
2400
+ z.object({
2401
+ text: z.string(),
2402
+ isRedLetter: z.boolean().optional(),
2403
+ isItalic: z.boolean().optional(),
2404
+ isSmallCaps: z.boolean().optional(),
2405
+ isBold: z.boolean().optional()
2406
+ })
2407
+ )
2408
+ }
2409
+ },
2410
+ async ({ book, chapter, verse }) => {
2411
+ try {
2412
+ const passage = await apiClient.getVerse(book, chapter, verse);
2413
+ const verseData = passage.verses[0];
2414
+ if (!verseData) {
2415
+ const output2 = {
2416
+ error: `No verse found for ${book} ${chapter}:${verse}`
2417
+ };
2418
+ return {
2419
+ content: [{ type: "text", text: JSON.stringify(output2) }],
2420
+ isError: true
2421
+ };
2422
+ }
2423
+ const output = {
2424
+ reference: {
2425
+ book: verseData.reference.bookName,
2426
+ chapter: verseData.reference.chapter,
2427
+ verse: verseData.reference.verse
2428
+ },
2429
+ text: verseData.plainText,
2430
+ segments: verseData.segments
2431
+ };
2432
+ return {
2433
+ content: [{ type: "text", text: JSON.stringify(output) }],
2434
+ structuredContent: output
2435
+ };
2436
+ } catch (error) {
2437
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2438
+ return {
2439
+ content: [{ type: "text", text: `Error: ${errorMessage}` }],
2440
+ isError: true
2441
+ };
2442
+ }
2443
+ }
2444
+ );
2445
+ server.registerTool(
2446
+ "get_passage",
2447
+ {
2448
+ title: "Get Bible Passage",
2449
+ description: "Fetch a range of verses (single or multi-chapter)",
2450
+ inputSchema: {
2451
+ fromBook: z.nativeEnum(BookName).describe("Starting book"),
2452
+ fromChapter: z.number().int().positive().describe("Starting chapter"),
2453
+ fromVerse: z.number().int().positive().describe("Starting verse"),
2454
+ toBook: z.nativeEnum(BookName).describe("Ending book"),
2455
+ toChapter: z.number().int().positive().describe("Ending chapter"),
2456
+ toVerse: z.number().int().positive().describe("Ending verse")
2457
+ },
2458
+ outputSchema: {
2459
+ reference: z.object({
2460
+ from: z.object({
2461
+ book: z.string(),
2462
+ chapter: z.number(),
2463
+ verse: z.number()
2464
+ }),
2465
+ to: z.object({
2466
+ book: z.string(),
2467
+ chapter: z.number(),
2468
+ verse: z.number()
2469
+ })
2470
+ }),
2471
+ verses: z.array(
2472
+ z.object({
2473
+ reference: z.object({
2474
+ book: z.string(),
2475
+ chapter: z.number(),
2476
+ verse: z.number()
2477
+ }),
2478
+ text: z.string(),
2479
+ segments: z.array(
2480
+ z.object({
2481
+ text: z.string(),
2482
+ isRedLetter: z.boolean().optional(),
2483
+ isItalic: z.boolean().optional(),
2484
+ isSmallCaps: z.boolean().optional(),
2485
+ isBold: z.boolean().optional()
2486
+ })
2487
+ )
2488
+ })
2489
+ )
2490
+ }
2491
+ },
2492
+ async ({ fromBook, fromChapter, fromVerse, toBook, toChapter, toVerse }) => {
2493
+ try {
2494
+ const passage = await apiClient.getPassage(
2495
+ fromBook,
2496
+ fromChapter,
2497
+ fromVerse,
2498
+ toBook,
2499
+ toChapter,
2500
+ toVerse
2501
+ );
2502
+ const output = {
2503
+ reference: {
2504
+ from: {
2505
+ book: passage.fromRef.bookName,
2506
+ chapter: passage.fromRef.chapter,
2507
+ verse: passage.fromRef.verse
2508
+ },
2509
+ to: {
2510
+ book: passage.toRef.bookName,
2511
+ chapter: passage.toRef.chapter,
2512
+ verse: passage.toRef.verse
2513
+ }
2514
+ },
2515
+ verses: passage.verses.map((v) => ({
2516
+ reference: {
2517
+ book: v.reference.bookName,
2518
+ chapter: v.reference.chapter,
2519
+ verse: v.reference.verse
2520
+ },
2521
+ text: v.plainText,
2522
+ segments: v.segments
2523
+ }))
2524
+ };
2525
+ return {
2526
+ content: [{ type: "text", text: JSON.stringify(output) }],
2527
+ structuredContent: output
2528
+ };
2529
+ } catch (error) {
2530
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2531
+ return {
2532
+ content: [{ type: "text", text: `Error: ${errorMessage}` }],
2533
+ isError: true
2534
+ };
2535
+ }
2536
+ }
2537
+ );
2538
+ server.registerTool(
2539
+ "get_chapter",
2540
+ {
2541
+ title: "Get Bible Chapter",
2542
+ description: "Fetch an entire chapter",
2543
+ inputSchema: {
2544
+ book: z.nativeEnum(BookName).describe("Book name"),
2545
+ chapter: z.number().int().positive().describe("Chapter number")
2546
+ },
2547
+ outputSchema: {
2548
+ reference: z.object({
2549
+ book: z.string(),
2550
+ chapter: z.number(),
2551
+ verseCount: z.number()
2552
+ }),
2553
+ verses: z.array(
2554
+ z.object({
2555
+ reference: z.object({
2556
+ book: z.string(),
2557
+ chapter: z.number(),
2558
+ verse: z.number()
2559
+ }),
2560
+ text: z.string(),
2561
+ segments: z.array(
2562
+ z.object({
2563
+ text: z.string(),
2564
+ isRedLetter: z.boolean().optional(),
2565
+ isItalic: z.boolean().optional(),
2566
+ isSmallCaps: z.boolean().optional(),
2567
+ isBold: z.boolean().optional()
2568
+ })
2569
+ )
2570
+ })
2571
+ )
2572
+ }
2573
+ },
2574
+ async ({ book, chapter }) => {
2575
+ try {
2576
+ const passage = await apiClient.getChapter(book, chapter);
2577
+ const output = {
2578
+ reference: {
2579
+ book: passage.verses[0]?.reference.bookName ?? book,
2580
+ chapter,
2581
+ verseCount: passage.verses.length
2582
+ },
2583
+ verses: passage.verses.map((v) => ({
2584
+ reference: {
2585
+ book: v.reference.bookName,
2586
+ chapter: v.reference.chapter,
2587
+ verse: v.reference.verse
2588
+ },
2589
+ text: v.plainText,
2590
+ segments: v.segments
2591
+ }))
2592
+ };
2593
+ return {
2594
+ content: [{ type: "text", text: JSON.stringify(output) }],
2595
+ structuredContent: output
2596
+ };
2597
+ } catch (error) {
2598
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2599
+ return {
2600
+ content: [{ type: "text", text: `Error: ${errorMessage}` }],
2601
+ isError: true
2602
+ };
2603
+ }
2604
+ }
2605
+ );
2606
+ server.registerTool(
2607
+ "search_bible",
2608
+ {
2609
+ title: "Search Bible",
2610
+ description: "Full-text search across the entire Bible",
2611
+ inputSchema: {
2612
+ query: z.string().describe("Search query text"),
2613
+ includeDistribution: z.boolean().optional().default(false).describe("Include search result distribution by section and book")
2614
+ },
2615
+ outputSchema: {
2616
+ query: z.string(),
2617
+ resultCount: z.number(),
2618
+ results: z.array(
2619
+ z.object({
2620
+ reference: z.object({
2621
+ book: z.string(),
2622
+ chapter: z.number(),
2623
+ verse: z.number()
2624
+ }),
2625
+ text: z.string(),
2626
+ segments: z.array(
2627
+ z.object({
2628
+ text: z.string(),
2629
+ isRedLetter: z.boolean().optional(),
2630
+ isItalic: z.boolean().optional(),
2631
+ isSmallCaps: z.boolean().optional(),
2632
+ isBold: z.boolean().optional()
2633
+ })
2634
+ )
2635
+ })
2636
+ ),
2637
+ distribution: z.object({
2638
+ bySection: z.record(z.string(), z.number()),
2639
+ byBook: z.record(z.string(), z.number())
2640
+ }).optional()
2641
+ }
2642
+ },
2643
+ async ({ query, includeDistribution = false }) => {
2644
+ try {
2645
+ const searchResponse = await apiClient.search(query);
2646
+ const distribution = includeDistribution ? {
2647
+ bySection: searchResponse.countsBySection ?? {},
2648
+ byBook: searchResponse.countsByBook ?? {}
2649
+ } : void 0;
2650
+ const results = searchResponse.passages.flatMap(
2651
+ (passage) => passage.verses.map((v) => ({
2652
+ reference: {
2653
+ book: v.reference.bookName,
2654
+ chapter: v.reference.chapter,
2655
+ verse: v.reference.verse
2656
+ },
2657
+ text: v.plainText,
2658
+ segments: v.segments
2659
+ }))
2660
+ );
2661
+ const output = {
2662
+ query,
2663
+ resultCount: searchResponse.matchCount,
2664
+ results,
2665
+ distribution
2666
+ };
2667
+ return {
2668
+ content: [{ type: "text", text: JSON.stringify(output) }],
2669
+ structuredContent: output
2670
+ };
2671
+ } catch (error) {
2672
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2673
+ return {
2674
+ content: [{ type: "text", text: `Error: ${errorMessage}` }],
2675
+ isError: true
2676
+ };
2677
+ }
2678
+ }
2679
+ );
2680
+ }
2681
+
2682
+ // src/mcp/server.ts
2683
+ function createLSBibleMCPServer() {
2684
+ const server = new McpServer({
2685
+ name: "lsbible",
2686
+ version: "0.1.0"
2687
+ });
2688
+ registerTools(server);
2689
+ registerResources(server);
2690
+ registerPrompts(server);
2691
+ return server;
2692
+ }
2693
+
2694
+ // src/mcp/stdio.ts
2695
+ async function main() {
2696
+ const server = createLSBibleMCPServer();
2697
+ const transport = new StdioServerTransport();
2698
+ await server.connect(transport);
2699
+ console.error("LSBible MCP Server running on stdio");
2700
+ console.error("Ready to accept connections from MCP clients");
2701
+ }
2702
+ main().catch((error) => {
2703
+ console.error("Fatal error:", error);
2704
+ process.exit(1);
2705
+ });
2706
+ //# sourceMappingURL=stdio.js.map
2707
+ //# sourceMappingURL=stdio.js.map