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

thoughtspot / mcp-server / 16209157863

11 Jul 2025 12:46AM UTC coverage: 89.668% (+1.2%) from 88.438%
16209157863

push

github

web-flow
Honeycomb traces (#34)

* Integrate with honeycomb otel library to get traces from MCP server in honeycomb

* use decorator patter using annotations to add tracing

* update error message

146 of 171 branches covered (85.38%)

Branch coverage included in aggregate %.

346 of 378 new or added lines in 8 files covered. (91.53%)

1 existing line in 1 file now uncovered.

557 of 613 relevant lines covered (90.86%)

102.38 hits per line

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

98.39
/src/servers/api-server.ts
1
import { Hono } from 'hono'
2
import type { Props } from '../utils';
3
import { 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 } }>()
36✔
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) {
36✔
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) {
36✔
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[]) {
36✔
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) {
36✔
46
        const service = this.getThoughtSpotService(props);
18✔
47
        return await service.getDataSources();
18✔
48
    }
49

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

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

89
const handler = new ApiHandler();
36✔
90

91
apiServer.post("/api/tools/relevant-questions", async (c) => {
36✔
92
    const { props } = c.executionCtx;
36✔
93
    const { query, datasourceIds, additionalContext } = await c.req.json();
36✔
94
    const questions = await handler.getRelevantQuestions(props, query, datasourceIds, additionalContext);
27✔
95
    return c.json(questions);
27✔
96
});
97

98
apiServer.post("/api/tools/get-answer", async (c) => {
36✔
99
    const { props } = c.executionCtx;
9✔
100
    const { question, datasourceId } = await c.req.json();
9✔
101
    const answer = await handler.getAnswer(props, question, datasourceId);
9✔
102
    return c.json(answer);
9✔
103
});
104

105
apiServer.post("/api/tools/create-liveboard", async (c) => {
36✔
106
    const { props } = c.executionCtx;
18✔
107
    const { name, answers } = await c.req.json();
18✔
108
    const liveboardUrl = await handler.createLiveboard(props, name, answers);
18✔
109
    return c.text(liveboardUrl);
9✔
110
});
111

112
apiServer.get("/api/resources/datasources", async (c) => {
36✔
113
    const { props } = c.executionCtx;
18✔
114
    const datasources = await handler.getDataSources(props);
18✔
115
    return c.json(datasources);
9✔
116
});
117

118
apiServer.post("/api/rest/2.0/*", async (c) => {
36✔
119
    const { props } = c.executionCtx;
18✔
120
    const path = c.req.path;
18✔
121
    const method = c.req.method;
18✔
122
    const body = await c.req.json();
18✔
123
    return handler.proxyPost(props, path, body);
18✔
124
});
125

126
apiServer.get("/api/rest/2.0/*", async (c) => {
36✔
127
    const { props } = c.executionCtx;
18✔
128
    const path = c.req.path;
18✔
129
    return handler.proxyGet(props, path);
18✔
130
});
131

132
export {
133
    apiServer,
134
}
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

© 2025 Coveralls, Inc