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