fellow-mcp 1.0.4 → 1.0.5
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.
- package/dist/index.js +79 -47
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -84,6 +84,40 @@ class FellowClient {
|
|
|
84
84
|
async getRecording(recordingId) {
|
|
85
85
|
return this.request("GET", `/recording/${recordingId}`);
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Fetch a single recording with its transcript.
|
|
89
|
+
* Tries GET /recording/{id} first (which may include transcript).
|
|
90
|
+
* If transcript is missing, falls back to POST /recordings with include.transcript
|
|
91
|
+
* and paginates until the recording is found.
|
|
92
|
+
*/
|
|
93
|
+
async getRecordingWithTranscript(recordingId) {
|
|
94
|
+
// Try direct fetch first - single resource endpoints often return full object
|
|
95
|
+
try {
|
|
96
|
+
const recording = await this.getRecording(recordingId);
|
|
97
|
+
if (recording && recording.transcript) {
|
|
98
|
+
return recording;
|
|
99
|
+
}
|
|
100
|
+
// If we got the recording but no transcript, fall back to list with include
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
// Direct fetch failed, fall back to list endpoint
|
|
104
|
+
}
|
|
105
|
+
// Fall back to list with transcript include, paginating until we find it
|
|
106
|
+
let cursor;
|
|
107
|
+
do {
|
|
108
|
+
const resp = await this.listRecordings({
|
|
109
|
+
include_transcript: true,
|
|
110
|
+
cursor,
|
|
111
|
+
page_size: 50,
|
|
112
|
+
});
|
|
113
|
+
const found = resp.recordings.data.find((r) => r.id === recordingId);
|
|
114
|
+
if (found) {
|
|
115
|
+
return found;
|
|
116
|
+
}
|
|
117
|
+
cursor = resp.recordings.page_info.cursor ?? undefined;
|
|
118
|
+
} while (cursor);
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
87
121
|
async listNotes(options) {
|
|
88
122
|
const body = {};
|
|
89
123
|
// Build filters
|
|
@@ -590,8 +624,8 @@ function formatTranscript(transcript) {
|
|
|
590
624
|
}
|
|
591
625
|
let output = `Language: ${transcript.language_code}\n\n`;
|
|
592
626
|
for (const segment of transcript.speech_segments) {
|
|
593
|
-
const startTime = formatTime(segment.start_time);
|
|
594
|
-
const endTime = formatTime(segment.end_time);
|
|
627
|
+
const startTime = formatTime(segment.start ?? segment.start_time ?? 0);
|
|
628
|
+
const endTime = formatTime(segment.end ?? segment.end_time ?? 0);
|
|
595
629
|
output += `[${startTime} - ${endTime}] ${segment.speaker}: ${segment.text}\n`;
|
|
596
630
|
}
|
|
597
631
|
return output;
|
|
@@ -709,22 +743,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
709
743
|
const { recording_id, meeting_title } = args;
|
|
710
744
|
let recordingWithTranscript = null;
|
|
711
745
|
if (recording_id) {
|
|
712
|
-
//
|
|
713
|
-
|
|
714
|
-
include_transcript: true,
|
|
715
|
-
page_size: 50,
|
|
716
|
-
});
|
|
717
|
-
recordingWithTranscript =
|
|
718
|
-
recordingsResp.recordings.data.find((r) => r.id === recording_id) ?? null;
|
|
719
|
-
if (!recordingWithTranscript) {
|
|
720
|
-
// Try fetching all to find it
|
|
721
|
-
const allRecordingsResp = await client.listRecordings({
|
|
722
|
-
include_transcript: true,
|
|
723
|
-
page_size: 50,
|
|
724
|
-
});
|
|
725
|
-
recordingWithTranscript =
|
|
726
|
-
allRecordingsResp.recordings.data.find((r) => r.id === recording_id) ?? null;
|
|
727
|
-
}
|
|
746
|
+
// Direct fetch by ID with transcript (paginates if needed)
|
|
747
|
+
recordingWithTranscript = await client.getRecordingWithTranscript(recording_id);
|
|
728
748
|
}
|
|
729
749
|
else if (meeting_title) {
|
|
730
750
|
// Search by title and get transcript
|
|
@@ -765,10 +785,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
765
785
|
let targetRecordingId = recording_id;
|
|
766
786
|
// If recording_id provided, get the associated note_id
|
|
767
787
|
if (!noteId && recording_id) {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
788
|
+
try {
|
|
789
|
+
const recording = await client.getRecording(recording_id);
|
|
790
|
+
if (recording) {
|
|
791
|
+
noteId = recording.note_id;
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
catch {
|
|
795
|
+
// Direct fetch failed, try list as fallback
|
|
796
|
+
const recordingsResp = await client.listRecordings({ page_size: 50 });
|
|
797
|
+
const recording = recordingsResp.recordings.data.find((r) => r.id === recording_id);
|
|
798
|
+
if (recording) {
|
|
799
|
+
noteId = recording.note_id;
|
|
800
|
+
}
|
|
772
801
|
}
|
|
773
802
|
}
|
|
774
803
|
// If meeting_title provided, search for the note and recording
|
|
@@ -800,16 +829,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
800
829
|
let eventStart = null;
|
|
801
830
|
let eventGuid = null;
|
|
802
831
|
if (noteId) {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
832
|
+
// Direct fetch by ID - much more reliable than listing and filtering
|
|
833
|
+
try {
|
|
834
|
+
const note = await client.getNote(noteId);
|
|
835
|
+
if (note) {
|
|
836
|
+
noteTitle = note.title;
|
|
837
|
+
eventStart = note.event_start ?? null;
|
|
838
|
+
eventGuid = note.event_guid ?? null;
|
|
839
|
+
noteContent = note.content_markdown ?? null;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
// Note fetch failed, continue without note content
|
|
813
844
|
}
|
|
814
845
|
}
|
|
815
846
|
// Find recording ID if we don't have it
|
|
@@ -823,11 +854,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
823
854
|
// Fetch transcript
|
|
824
855
|
let transcriptText = null;
|
|
825
856
|
if (targetRecordingId) {
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
page_size: 50,
|
|
829
|
-
});
|
|
830
|
-
const recording = recordingsResp.recordings.data.find((r) => r.id === targetRecordingId);
|
|
857
|
+
// Direct fetch by ID with transcript (paginates if needed)
|
|
858
|
+
const recording = await client.getRecordingWithTranscript(targetRecordingId);
|
|
831
859
|
if (recording?.transcript) {
|
|
832
860
|
transcriptText = formatTranscript(recording.transcript);
|
|
833
861
|
if (!noteTitle || noteTitle === "Unknown Meeting") {
|
|
@@ -867,11 +895,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
867
895
|
const { note_id, meeting_title } = args;
|
|
868
896
|
let note = null;
|
|
869
897
|
if (note_id) {
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
}
|
|
874
|
-
|
|
898
|
+
// Direct fetch by ID
|
|
899
|
+
try {
|
|
900
|
+
note = await client.getNote(note_id);
|
|
901
|
+
}
|
|
902
|
+
catch {
|
|
903
|
+
note = null;
|
|
904
|
+
}
|
|
875
905
|
}
|
|
876
906
|
else if (meeting_title) {
|
|
877
907
|
const notesResp = await client.listNotes({
|
|
@@ -919,11 +949,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
919
949
|
const { note_id, meeting_title } = args;
|
|
920
950
|
let note = null;
|
|
921
951
|
if (note_id) {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
|
|
952
|
+
// Direct fetch by ID
|
|
953
|
+
try {
|
|
954
|
+
note = await client.getNote(note_id);
|
|
955
|
+
}
|
|
956
|
+
catch {
|
|
957
|
+
note = null;
|
|
958
|
+
}
|
|
927
959
|
}
|
|
928
960
|
else if (meeting_title) {
|
|
929
961
|
const notesResp = await client.listNotes({
|