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

thoughtspot / mcp-server / 16633091239

30 Jul 2025 08:30PM UTC coverage: 92.389% (-0.09%) from 92.477%
16633091239

Pull #54

github

web-flow
Merge 25cca711a into a8f556ab2
Pull Request #54: Png image

194 of 220 branches covered (88.18%)

Branch coverage included in aggregate %.

50 of 58 new or added lines in 7 files covered. (86.21%)

4 existing lines in 2 files now uncovered.

680 of 726 relevant lines covered (93.66%)

140.7 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 } }>()
35✔
10

11
class ApiHandler {
12

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

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

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

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

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

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

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

56
    @WithSpan('api-proxy-post')
57
    async proxyPost(props: Props, path: string, body: any) {
35✔
58
        const span = getActiveSpan();
16✔
59
        span?.setAttributes({
16✔
60
            instance_url: props.instanceUrl,
61
            path: path,
62
        });
63
        span?.addEvent("proxy-post");
16✔
64
        return fetch(props.instanceUrl + path, {
16✔
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) {
35✔
78
        const span = getActiveSpan();
16✔
79
        span?.setAttributes({
16✔
80
            instance_url: props.instanceUrl,
81
            path: path,
82
        });
83
        span?.addEvent("proxy-get");
16✔
84
        return fetch(props.instanceUrl + path, {
16✔
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();
35✔
96

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

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

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

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

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

132
apiServer.get("/api/rest/2.0/*", async (c) => {
35✔
133
    const { props } = c.executionCtx;
16✔
134
    const path = c.req.path;
16✔
135
    return handler.proxyGet(props, path);
16✔
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