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