• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

thoughtspot / mcp-server / 16607548209

29 Jul 2025 09:11PM UTC coverage: 90.536% (-1.2%) from 91.706%
16607548209

Pull #54

github

web-flow
Merge 188b5de85 into 586b0d878
Pull Request #54: Png image

190 of 220 branches covered (86.36%)

Branch coverage included in aggregate %.

56 of 70 new or added lines in 7 files covered. (80.0%)

1 existing line in 1 file now uncovered.

671 of 731 relevant lines covered (91.79%)

137.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.38
/src/servers/api-server.ts
1
import { Hono } from 'hono'
2
import type { Props } from '../utils';
3
import { getFromKV, McpServerError } from '../utils';
4
import { getDataSources, ThoughtSpotService } from '../thoughtspot/thoughtspot-service';
5
import { getThoughtSpotClient } from '../thoughtspot/thoughtspot-client';
6
import { getActiveSpan, WithSpan } from '../metrics/tracing/tracing-utils';
7
import { context, type Span, SpanStatusCode, trace } from "@opentelemetry/api";
8

9
const apiServer = new Hono<{ Bindings: Env & { props: Props } }>()
37✔
10

11
class ApiHandler {
12

13
    private initSpan(props: Props) {
14
        const span = getActiveSpan();
72✔
15
        span?.setAttributes({
72✔
16
            instance_url: props.instanceUrl,
17
        });
18
    }
19

20
    private getThoughtSpotService(props: Props): ThoughtSpotService {
21
        this.initSpan(props);
72✔
22
        return new ThoughtSpotService(getThoughtSpotClient(props.instanceUrl, props.accessToken));
72✔
23
    }
24

25
    @WithSpan('api-relevant-questions')
26
    async getRelevantQuestions(props: Props, query: string, datasourceIds: string[], additionalContext?: string) {
37✔
27
        const service = this.getThoughtSpotService(props);
27✔
28
        return await service.getRelevantQuestions(query, datasourceIds, additionalContext || '');
27✔
29
    }
30

31
    @WithSpan('api-get-answer')
32
    async getAnswer(props: Props, question: string, datasourceId: string) {
37✔
33
        const service = this.getThoughtSpotService(props);
9✔
34
        return await service.getAnswerForQuestion(question, datasourceId, false);
9✔
35
    }
36

37
    @WithSpan('api-create-liveboard')
38
    async createLiveboard(props: Props, name: string, answers: any[]) {
37✔
39
        const service = this.getThoughtSpotService(props);
18✔
40
        const result = await service.fetchTMLAndCreateLiveboard(name, answers);
18✔
41
        return result.url || '';
9!
42
    }
43

44
    @WithSpan('api-get-datasources')
45
    async getDataSources(props: Props) {
37✔
46
        const service = this.getThoughtSpotService(props);
18✔
47
        return await service.getDataSources();
18✔
48
    }
49

50
    @WithSpan('api-get-answer-image')
51
    async getAnswerImage(props: Props, sessionId: string, generationNo: number) {
37✔
NEW
52
        const service = this.getThoughtSpotService(props);
×
NEW
53
        return await service.getAnswerImage(sessionId, generationNo);
×
54
    }
55

56
    @WithSpan('api-proxy-post')
57
    async proxyPost(props: Props, path: string, body: any) {
37✔
58
        const span = getActiveSpan();
18✔
59
        span?.setAttributes({
18✔
60
            instance_url: props.instanceUrl,
61
            path: path,
62
        });
63
        span?.addEvent("proxy-post");
18✔
64
        return fetch(props.instanceUrl + path, {
18✔
65
            method: 'POST',
66
            headers: {
67
                "Authorization": `Bearer ${props.accessToken}`,
68
                "Accept": "application/json",
69
                "Content-Type": "application/json",
70
                "User-Agent": "ThoughtSpot-ts-client",
71
            },
72
            body: JSON.stringify(body),
73
        });
74
    }
75

76
    @WithSpan('api-proxy-get')
77
    async proxyGet(props: Props, path: string) {
37✔
78
        const span = getActiveSpan();
18✔
79
        span?.setAttributes({
18✔
80
            instance_url: props.instanceUrl,
81
            path: path,
82
        });
83
        span?.addEvent("proxy-get");
18✔
84
        return fetch(props.instanceUrl + path, {
18✔
85
            method: 'GET',
86
            headers: {
87
                "Authorization": `Bearer ${props.accessToken}`,
88
                "Accept": "application/json",
89
                "User-Agent": "ThoughtSpot-ts-client",
90
            }
91
        });
92
    }
93
}
94

95
const handler = new ApiHandler();
37✔
96

97
apiServer.post("/api/tools/relevant-questions", async (c) => {
37✔
98
    const { props } = c.executionCtx;
36✔
99
    const { query, datasourceIds, additionalContext } = await c.req.json();
36✔
100
    const questions = await handler.getRelevantQuestions(props, query, datasourceIds, additionalContext);
27✔
101
    return c.json(questions);
27✔
102
});
103

104
apiServer.post("/api/tools/get-answer", async (c) => {
37✔
105
    const { props } = c.executionCtx;
9✔
106
    const { question, datasourceId } = await c.req.json();
9✔
107
    const answer = await handler.getAnswer(props, question, datasourceId);
9✔
108
    return c.json(answer);
9✔
109
});
110

111
apiServer.post("/api/tools/create-liveboard", async (c) => {
37✔
112
    const { props } = c.executionCtx;
18✔
113
    const { name, answers } = await c.req.json();
18✔
114
    const liveboardUrl = await handler.createLiveboard(props, name, answers);
18✔
115
    return c.text(liveboardUrl);
9✔
116
});
117

118
apiServer.get("/api/resources/datasources", async (c) => {
37✔
119
    const { props } = c.executionCtx;
18✔
120
    const datasources = await handler.getDataSources(props);
18✔
121
    return c.json(datasources);
9✔
122
});
123

124
apiServer.post("/api/rest/2.0/*", async (c) => {
37✔
125
    const { props } = c.executionCtx;
18✔
126
    const path = c.req.path;
18✔
127
    const method = c.req.method;
18✔
128
    const body = await c.req.json();
18✔
129
    return handler.proxyPost(props, path, body);
18✔
130
});
131

132
apiServer.get("/api/rest/2.0/*", async (c) => {
37✔
133
    const { props } = c.executionCtx;
18✔
134
    const path = c.req.path;
18✔
135
    return handler.proxyGet(props, path);
18✔
136
});
137

138
export {
139
    apiServer,
140
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc