@cyanheads/stackexchange-mcp-server 0.1.1

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.
Files changed (53) hide show
  1. package/AGENTS.md +360 -0
  2. package/CLAUDE.md +360 -0
  3. package/Dockerfile +99 -0
  4. package/LICENSE +201 -0
  5. package/README.md +307 -0
  6. package/changelog/0.1.x/0.1.1.md +27 -0
  7. package/changelog/template.md +127 -0
  8. package/dist/config/server-config.d.ts +11 -0
  9. package/dist/config/server-config.d.ts.map +1 -0
  10. package/dist/config/server-config.js +21 -0
  11. package/dist/config/server-config.js.map +1 -0
  12. package/dist/index.d.ts +7 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +24 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/mcp-server/tools/definitions/index.d.ts +176 -0
  17. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
  18. package/dist/mcp-server/tools/definitions/index.js +22 -0
  19. package/dist/mcp-server/tools/definitions/index.js.map +1 -0
  20. package/dist/mcp-server/tools/definitions/stackexchange-get-tag-faq.tool.d.ts +37 -0
  21. package/dist/mcp-server/tools/definitions/stackexchange-get-tag-faq.tool.d.ts.map +1 -0
  22. package/dist/mcp-server/tools/definitions/stackexchange-get-tag-faq.tool.js +118 -0
  23. package/dist/mcp-server/tools/definitions/stackexchange-get-tag-faq.tool.js.map +1 -0
  24. package/dist/mcp-server/tools/definitions/stackexchange-get-thread.tool.d.ts +54 -0
  25. package/dist/mcp-server/tools/definitions/stackexchange-get-thread.tool.d.ts.map +1 -0
  26. package/dist/mcp-server/tools/definitions/stackexchange-get-thread.tool.js +205 -0
  27. package/dist/mcp-server/tools/definitions/stackexchange-get-thread.tool.js.map +1 -0
  28. package/dist/mcp-server/tools/definitions/stackexchange-get-user.tool.d.ts +48 -0
  29. package/dist/mcp-server/tools/definitions/stackexchange-get-user.tool.d.ts.map +1 -0
  30. package/dist/mcp-server/tools/definitions/stackexchange-get-user.tool.js +151 -0
  31. package/dist/mcp-server/tools/definitions/stackexchange-get-user.tool.js.map +1 -0
  32. package/dist/mcp-server/tools/definitions/stackexchange-list-sites.tool.d.ts +20 -0
  33. package/dist/mcp-server/tools/definitions/stackexchange-list-sites.tool.d.ts.map +1 -0
  34. package/dist/mcp-server/tools/definitions/stackexchange-list-sites.tool.js +94 -0
  35. package/dist/mcp-server/tools/definitions/stackexchange-list-sites.tool.js.map +1 -0
  36. package/dist/mcp-server/tools/definitions/stackexchange-search-questions.tool.d.ts +45 -0
  37. package/dist/mcp-server/tools/definitions/stackexchange-search-questions.tool.d.ts.map +1 -0
  38. package/dist/mcp-server/tools/definitions/stackexchange-search-questions.tool.js +145 -0
  39. package/dist/mcp-server/tools/definitions/stackexchange-search-questions.tool.js.map +1 -0
  40. package/dist/services/stackexchange/html-normalizer.d.ts +18 -0
  41. package/dist/services/stackexchange/html-normalizer.d.ts.map +1 -0
  42. package/dist/services/stackexchange/html-normalizer.js +143 -0
  43. package/dist/services/stackexchange/html-normalizer.js.map +1 -0
  44. package/dist/services/stackexchange/stackexchange-service.d.ts +143 -0
  45. package/dist/services/stackexchange/stackexchange-service.d.ts.map +1 -0
  46. package/dist/services/stackexchange/stackexchange-service.js +336 -0
  47. package/dist/services/stackexchange/stackexchange-service.js.map +1 -0
  48. package/dist/services/stackexchange/types.d.ts +104 -0
  49. package/dist/services/stackexchange/types.d.ts.map +1 -0
  50. package/dist/services/stackexchange/types.js +7 -0
  51. package/dist/services/stackexchange/types.js.map +1 -0
  52. package/package.json +101 -0
  53. package/server.json +111 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-get-thread.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-get-thread.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @fileoverview Tool to fetch a Stack Exchange user profile by ID.
3
+ * @module mcp-server/tools/definitions/stackexchange-get-user
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ export declare const stackexchangeGetUser: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
8
+ userId: z.ZodNumber;
9
+ site: z.ZodDefault<z.ZodString>;
10
+ }, z.core.$strip>, z.ZodObject<{
11
+ userId: z.ZodNumber;
12
+ displayName: z.ZodString;
13
+ link: z.ZodString;
14
+ reputation: z.ZodNumber;
15
+ badgeCounts: z.ZodOptional<z.ZodObject<{
16
+ gold: z.ZodOptional<z.ZodNumber>;
17
+ silver: z.ZodOptional<z.ZodNumber>;
18
+ bronze: z.ZodOptional<z.ZodNumber>;
19
+ }, z.core.$strip>>;
20
+ location: z.ZodOptional<z.ZodString>;
21
+ websiteUrl: z.ZodOptional<z.ZodString>;
22
+ answerCount: z.ZodOptional<z.ZodNumber>;
23
+ questionCount: z.ZodOptional<z.ZodNumber>;
24
+ topTags: z.ZodArray<z.ZodObject<{
25
+ tagName: z.ZodString;
26
+ answerCount: z.ZodOptional<z.ZodNumber>;
27
+ answerScore: z.ZodOptional<z.ZodNumber>;
28
+ }, z.core.$strip>>;
29
+ }, z.core.$strip>, readonly [{
30
+ readonly reason: "user_not_found";
31
+ readonly code: JsonRpcErrorCode.NotFound;
32
+ readonly when: "The user lookup returns an empty result set — SE returns HTTP 200 with no items for unknown user IDs rather than 404.";
33
+ readonly recovery: "Verify the user ID or look up a valid ID from an answer via stackexchange_get_thread.";
34
+ }, {
35
+ readonly reason: "invalid_site";
36
+ readonly code: JsonRpcErrorCode.InvalidParams;
37
+ readonly when: "The provided site value is not a valid Stack Exchange network site identifier.";
38
+ readonly recovery: "Call stackexchange_list_sites to discover valid site api_site_parameter values and retry.";
39
+ }, {
40
+ readonly reason: "quota_exceeded";
41
+ readonly code: JsonRpcErrorCode.RateLimited;
42
+ readonly when: "The Stack Exchange API quota_remaining has reached 0.";
43
+ readonly recovery: "Quota resets at midnight UTC; set STACKEXCHANGE_API_KEY to lift the limit to 10,000 per day.";
44
+ }], {
45
+ readonly quotaRemaining: z.ZodNumber;
46
+ readonly quotaMax: z.ZodNumber;
47
+ }>;
48
+ //# sourceMappingURL=stackexchange-get-user.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-get-user.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-get-user.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyJ/B,CAAC"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * @fileoverview Tool to fetch a Stack Exchange user profile by ID.
3
+ * @module mcp-server/tools/definitions/stackexchange-get-user
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ import { getStackExchangeService } from '../../../services/stackexchange/stackexchange-service.js';
8
+ export const stackexchangeGetUser = tool('stackexchange_get_user', {
9
+ title: 'Get Stack Exchange User Profile',
10
+ description: 'Fetch a Stack Exchange user profile by numeric user ID: reputation, badge counts, top tags by answer score, ' +
11
+ 'and account metadata. Useful for credibility context on an answer author — the owner.user_id from ' +
12
+ 'stackexchange_get_thread output can be passed directly. Returns profile fields plus up to 10 top tags by answer score.',
13
+ annotations: {
14
+ readOnlyHint: true,
15
+ idempotentHint: true,
16
+ openWorldHint: true,
17
+ },
18
+ input: z.object({
19
+ userId: z
20
+ .number()
21
+ .int()
22
+ .describe('Numeric user ID (e.g. from owner.user_id in stackexchange_get_thread output).'),
23
+ site: z
24
+ .string()
25
+ .default('stackoverflow')
26
+ .describe('Stack Exchange site — use the api_site_parameter value (e.g. "stackoverflow", "superuser"). ' +
27
+ 'Defaults to "stackoverflow". Call stackexchange_list_sites to discover valid values.'),
28
+ }),
29
+ output: z.object({
30
+ userId: z.number().int().describe('Numeric user ID on this Stack Exchange site.'),
31
+ displayName: z.string().describe('Display name shown on the site.'),
32
+ link: z.string().describe('Direct URL to the user profile.'),
33
+ reputation: z.number().int().describe('User reputation score.'),
34
+ badgeCounts: z
35
+ .object({
36
+ gold: z.number().int().optional().describe('Number of gold badges.'),
37
+ silver: z.number().int().optional().describe('Number of silver badges.'),
38
+ bronze: z.number().int().optional().describe('Number of bronze badges.'),
39
+ })
40
+ .optional()
41
+ .describe('Badge counts when provided by the API.'),
42
+ location: z.string().optional().describe('User-provided location string when available.'),
43
+ websiteUrl: z.string().optional().describe('User-provided website URL when available.'),
44
+ answerCount: z
45
+ .number()
46
+ .int()
47
+ .optional()
48
+ .describe('Total number of answers posted when provided by the API.'),
49
+ questionCount: z
50
+ .number()
51
+ .int()
52
+ .optional()
53
+ .describe('Total number of questions posted when provided by the API.'),
54
+ topTags: z
55
+ .array(z
56
+ .object({
57
+ tagName: z.string().describe('Tag name.'),
58
+ answerCount: z.number().int().optional().describe('Number of answers in this tag.'),
59
+ answerScore: z.number().int().optional().describe('Total answer score in this tag.'),
60
+ })
61
+ .describe('A tag the user has answered in, with answer count and score.'))
62
+ .describe('Top tags by answer score (up to 10). Empty array for new users with no answers.'),
63
+ }),
64
+ enrichment: {
65
+ quotaRemaining: z.number().describe('Remaining API quota calls for the current day.'),
66
+ quotaMax: z
67
+ .number()
68
+ .describe('Maximum API quota calls per day (300 keyless, ~10,000 with API key).'),
69
+ },
70
+ enrichmentTrailer: {
71
+ quotaRemaining: { label: 'Quota Remaining' },
72
+ quotaMax: { label: 'Quota Max' },
73
+ },
74
+ errors: [
75
+ {
76
+ reason: 'user_not_found',
77
+ code: JsonRpcErrorCode.NotFound,
78
+ when: 'The user lookup returns an empty result set — SE returns HTTP 200 with no items for unknown user IDs rather than 404.',
79
+ recovery: 'Verify the user ID or look up a valid ID from an answer via stackexchange_get_thread.',
80
+ },
81
+ {
82
+ reason: 'invalid_site',
83
+ code: JsonRpcErrorCode.InvalidParams,
84
+ when: 'The provided site value is not a valid Stack Exchange network site identifier.',
85
+ recovery: 'Call stackexchange_list_sites to discover valid site api_site_parameter values and retry.',
86
+ },
87
+ {
88
+ reason: 'quota_exceeded',
89
+ code: JsonRpcErrorCode.RateLimited,
90
+ when: 'The Stack Exchange API quota_remaining has reached 0.',
91
+ recovery: 'Quota resets at midnight UTC; set STACKEXCHANGE_API_KEY to lift the limit to 10,000 per day.',
92
+ },
93
+ ],
94
+ async handler(input, ctx) {
95
+ const svc = getStackExchangeService();
96
+ const { user, quotaRemaining, quotaMax } = await svc.getUser({ userId: input.userId, site: input.site }, ctx);
97
+ ctx.enrich({ quotaRemaining, quotaMax });
98
+ ctx.log.info('Fetched SE user', {
99
+ userId: user.userId,
100
+ displayName: user.displayName,
101
+ site: input.site,
102
+ });
103
+ return user;
104
+ },
105
+ format: (result) => {
106
+ const lines = [];
107
+ lines.push(`## ${result.displayName}`);
108
+ lines.push(`**User ID:** ${result.userId}`);
109
+ lines.push(`**Reputation:** ${result.reputation.toLocaleString()}`);
110
+ lines.push(`**Profile:** ${result.link}`);
111
+ if (result.badgeCounts) {
112
+ const badges = [];
113
+ if (result.badgeCounts.gold != null)
114
+ badges.push(`🥇 ${result.badgeCounts.gold}`);
115
+ if (result.badgeCounts.silver != null)
116
+ badges.push(`🥈 ${result.badgeCounts.silver}`);
117
+ if (result.badgeCounts.bronze != null)
118
+ badges.push(`🥉 ${result.badgeCounts.bronze}`);
119
+ if (badges.length > 0)
120
+ lines.push(`**Badges:** ${badges.join(' ')}`);
121
+ }
122
+ if (result.location)
123
+ lines.push(`**Location:** ${result.location}`);
124
+ if (result.websiteUrl)
125
+ lines.push(`**Website:** ${result.websiteUrl}`);
126
+ if (result.answerCount != null || result.questionCount != null) {
127
+ const parts = [];
128
+ if (result.answerCount != null)
129
+ parts.push(`${result.answerCount} answers`);
130
+ if (result.questionCount != null)
131
+ parts.push(`${result.questionCount} questions`);
132
+ lines.push(`**Posts:** ${parts.join(' · ')}`);
133
+ }
134
+ if (result.topTags.length > 0) {
135
+ lines.push('\n### Top Tags');
136
+ for (const t of result.topTags) {
137
+ const parts = [`\`${t.tagName}\``];
138
+ if (t.answerScore != null)
139
+ parts.push(`score: ${t.answerScore}`);
140
+ if (t.answerCount != null)
141
+ parts.push(`${t.answerCount} answers`);
142
+ lines.push(`- ${parts.join(' · ')}`);
143
+ }
144
+ }
145
+ else {
146
+ lines.push('\n*No top tags — user has no answers yet.*');
147
+ }
148
+ return [{ type: 'text', text: lines.join('\n') }];
149
+ },
150
+ });
151
+ //# sourceMappingURL=stackexchange-get-user.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-get-user.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-get-user.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileoverview Tool to enumerate Stack Exchange network sites.
3
+ * @module mcp-server/tools/definitions/stackexchange-list-sites
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const stackexchangeListSites: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
7
+ filter: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strip>, z.ZodObject<{
9
+ sites: z.ZodArray<z.ZodObject<{
10
+ name: z.ZodString;
11
+ apiSiteParameter: z.ZodString;
12
+ siteUrl: z.ZodString;
13
+ audience: z.ZodOptional<z.ZodString>;
14
+ }, z.core.$strip>>;
15
+ totalCount: z.ZodNumber;
16
+ }, z.core.$strip>, undefined, {
17
+ readonly quotaRemaining: z.ZodNumber;
18
+ readonly quotaMax: z.ZodNumber;
19
+ }>;
20
+ //# sourceMappingURL=stackexchange-list-sites.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-list-sites.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-list-sites.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGjD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;EAqGjC,CAAC"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview Tool to enumerate Stack Exchange network sites.
3
+ * @module mcp-server/tools/definitions/stackexchange-list-sites
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { getStackExchangeService } from '../../../services/stackexchange/stackexchange-service.js';
7
+ export const stackexchangeListSites = tool('stackexchange_list_sites', {
8
+ title: 'List Stack Exchange Sites',
9
+ description: 'Enumerate all sites in the Stack Exchange network — name, api_site_parameter, audience, and URL. ' +
10
+ 'The api_site_parameter value is what other tools accept as the `site` input (e.g. "stackoverflow", "superuser", "serverfault"). ' +
11
+ 'Results are fetched live and optionally filtered by name. ' +
12
+ 'Use this tool to discover valid site parameters before calling other stackexchange_* tools.',
13
+ annotations: {
14
+ readOnlyHint: true,
15
+ idempotentHint: true,
16
+ openWorldHint: false,
17
+ },
18
+ input: z.object({
19
+ filter: z
20
+ .string()
21
+ .optional()
22
+ .describe('Optional case-insensitive name filter — returns only sites whose name contains all provided tokens. ' +
23
+ 'Omit to return all sites.'),
24
+ }),
25
+ output: z.object({
26
+ sites: z
27
+ .array(z
28
+ .object({
29
+ name: z.string().describe('Human-readable site name (e.g. "Stack Overflow").'),
30
+ apiSiteParameter: z
31
+ .string()
32
+ .describe('Value to pass as the `site` parameter on all other stackexchange_* tools (e.g. "stackoverflow").'),
33
+ siteUrl: z.string().describe('Public URL of the site.'),
34
+ audience: z
35
+ .string()
36
+ .optional()
37
+ .describe('Intended audience description when provided by the API.'),
38
+ })
39
+ .describe('A Stack Exchange network site entry.'))
40
+ .describe('Stack Exchange network sites matching the optional name filter.'),
41
+ totalCount: z.number().describe('Total number of sites returned after filtering.'),
42
+ }),
43
+ enrichment: {
44
+ quotaRemaining: z.number().describe('Remaining API quota calls for the current day.'),
45
+ quotaMax: z
46
+ .number()
47
+ .describe('Maximum API quota calls per day (300 keyless, ~10,000 with API key).'),
48
+ },
49
+ enrichmentTrailer: {
50
+ quotaRemaining: { label: 'Quota Remaining' },
51
+ quotaMax: { label: 'Quota Max' },
52
+ },
53
+ async handler(input, ctx) {
54
+ const svc = getStackExchangeService();
55
+ const { sites, quotaRemaining, quotaMax } = await svc.getSites(ctx);
56
+ ctx.enrich({ quotaRemaining, quotaMax });
57
+ let filtered = sites;
58
+ if (input.filter?.trim()) {
59
+ const normalize = (s) => s
60
+ .toLowerCase()
61
+ .normalize('NFKD')
62
+ .replace(/[̀-ͯ]/g, '')
63
+ .replace(/[^a-z0-9\s]/g, ' ');
64
+ const tokens = normalize(input.filter).split(/\s+/).filter(Boolean);
65
+ filtered = sites.filter((s) => {
66
+ const hay = `${normalize(s.name)} ${normalize(s.apiSiteParameter)}`;
67
+ return tokens.every((t) => hay.includes(t));
68
+ });
69
+ if (filtered.length === 0) {
70
+ ctx.enrich.notice(`No site matched "${input.filter}". Call stackexchange_list_sites without a filter to browse all sites.`);
71
+ }
72
+ }
73
+ ctx.log.info('Listed SE sites', { total: filtered.length, filtered: !!input.filter });
74
+ return { sites: filtered, totalCount: filtered.length };
75
+ },
76
+ format: (result) => {
77
+ if (result.sites.length === 0) {
78
+ return [{ type: 'text', text: 'No sites matched the filter.' }];
79
+ }
80
+ const lines = [
81
+ `**${result.totalCount} site${result.totalCount === 1 ? '' : 's'}**\n`,
82
+ ];
83
+ for (const s of result.sites) {
84
+ lines.push(`## ${s.name}`);
85
+ lines.push(`**api_site_parameter:** \`${s.apiSiteParameter}\``);
86
+ lines.push(`**URL:** ${s.siteUrl}`);
87
+ if (s.audience)
88
+ lines.push(`**Audience:** ${s.audience}`);
89
+ lines.push('');
90
+ }
91
+ return [{ type: 'text', text: lines.join('\n') }];
92
+ },
93
+ });
94
+ //# sourceMappingURL=stackexchange-list-sites.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-list-sites.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-list-sites.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,mDAAmD,CAAC;AAE5F,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,CAAC,0BAA0B,EAAE;IACrE,KAAK,EAAE,2BAA2B;IAClC,WAAW,EACT,mGAAmG;QACnG,kIAAkI;QAClI,4DAA4D;QAC5D,6FAA6F;IAC/F,WAAW,EAAE;QACX,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,IAAI;QACpB,aAAa,EAAE,KAAK;KACrB;IACD,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;QACd,MAAM,EAAE,CAAC;aACN,MAAM,EAAE;aACR,QAAQ,EAAE;aACV,QAAQ,CACP,sGAAsG;YACpG,2BAA2B,CAC9B;KACJ,CAAC;IACF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,KAAK,EAAE,CAAC;aACL,KAAK,CACJ,CAAC;aACE,MAAM,CAAC;YACN,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mDAAmD,CAAC;YAC9E,gBAAgB,EAAE,CAAC;iBAChB,MAAM,EAAE;iBACR,QAAQ,CACP,kGAAkG,CACnG;YACH,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;YACvD,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,yDAAyD,CAAC;SACvE,CAAC;aACD,QAAQ,CAAC,sCAAsC,CAAC,CACpD;aACA,QAAQ,CAAC,iEAAiE,CAAC;QAC9E,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;KACnF,CAAC;IACF,UAAU,EAAE;QACV,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,gDAAgD,CAAC;QACrF,QAAQ,EAAE,CAAC;aACR,MAAM,EAAE;aACR,QAAQ,CAAC,sEAAsE,CAAC;KACpF;IACD,iBAAiB,EAAE;QACjB,cAAc,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;QAC5C,QAAQ,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE;KACjC;IAED,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,MAAM,GAAG,GAAG,uBAAuB,EAAE,CAAC;QACtC,MAAM,EAAE,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAEpE,GAAG,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEzC,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC;YACzB,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAC9B,CAAC;iBACE,WAAW,EAAE;iBACb,SAAS,CAAC,MAAM,CAAC;iBACjB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;iBACrB,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;YAClC,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpE,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC5B,MAAM,GAAG,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBACpE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC1B,GAAG,CAAC,MAAM,CAAC,MAAM,CACf,oBAAoB,KAAK,CAAC,MAAM,wEAAwE,CACzG,CAAC;YACJ,CAAC;QACH,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;QACtF,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;QACjB,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,8BAA8B,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,KAAK,GAAa;YACtB,KAAK,MAAM,CAAC,UAAU,QAAQ,MAAM,CAAC,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM;SACvE,CAAC;QACF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,gBAAgB,IAAI,CAAC,CAAC;YAChE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YACpC,IAAI,CAAC,CAAC,QAAQ;gBAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC1D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjB,CAAC;QACD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @fileoverview Tool to search questions across a Stack Exchange site.
3
+ * @module mcp-server/tools/definitions/stackexchange-search-questions
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ export declare const stackexchangeSearchQuestions: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
8
+ query: z.ZodString;
9
+ site: z.ZodDefault<z.ZodString>;
10
+ tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
11
+ acceptedOnly: z.ZodOptional<z.ZodBoolean>;
12
+ minScore: z.ZodOptional<z.ZodNumber>;
13
+ sort: z.ZodDefault<z.ZodEnum<{
14
+ relevance: "relevance";
15
+ votes: "votes";
16
+ activity: "activity";
17
+ newest: "newest";
18
+ }>>;
19
+ pageSize: z.ZodDefault<z.ZodNumber>;
20
+ }, z.core.$strip>, z.ZodObject<{
21
+ questions: z.ZodArray<z.ZodObject<{
22
+ questionId: z.ZodNumber;
23
+ title: z.ZodString;
24
+ link: z.ZodString;
25
+ score: z.ZodNumber;
26
+ answerCount: z.ZodNumber;
27
+ isAnswered: z.ZodBoolean;
28
+ tags: z.ZodArray<z.ZodString>;
29
+ excerpt: z.ZodOptional<z.ZodString>;
30
+ }, z.core.$strip>>;
31
+ }, z.core.$strip>, readonly [{
32
+ readonly reason: "invalid_site";
33
+ readonly code: JsonRpcErrorCode.InvalidParams;
34
+ readonly when: "The provided site value is not a valid Stack Exchange network site identifier.";
35
+ readonly recovery: "Call stackexchange_list_sites to discover valid site api_site_parameter values and retry.";
36
+ }, {
37
+ readonly reason: "quota_exceeded";
38
+ readonly code: JsonRpcErrorCode.RateLimited;
39
+ readonly when: "The Stack Exchange API quota_remaining has reached 0.";
40
+ readonly recovery: "Quota resets at midnight UTC; set STACKEXCHANGE_API_KEY to lift the limit to 10,000 per day.";
41
+ }], {
42
+ readonly quotaRemaining: z.ZodNumber;
43
+ readonly quotaMax: z.ZodNumber;
44
+ }>;
45
+ //# sourceMappingURL=stackexchange-search-questions.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-search-questions.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-search-questions.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAGjE,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkKvC,CAAC"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * @fileoverview Tool to search questions across a Stack Exchange site.
3
+ * @module mcp-server/tools/definitions/stackexchange-search-questions
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
7
+ import { getStackExchangeService } from '../../../services/stackexchange/stackexchange-service.js';
8
+ export const stackexchangeSearchQuestions = tool('stackexchange_search_questions', {
9
+ title: 'Search Stack Exchange Questions',
10
+ description: 'Search questions across a Stack Exchange site. Returns ranked questions with title, score, answer count, ' +
11
+ 'accepted status, tags, and excerpt — no bodies at this stage. Results supply question_id values for ' +
12
+ 'stackexchange_get_thread, which fetches the full question body and all answers. ' +
13
+ 'Use the `site` parameter to target a specific community (e.g. "stackoverflow", "superuser", "unix"); ' +
14
+ 'call stackexchange_list_sites to discover valid site values.',
15
+ annotations: {
16
+ readOnlyHint: true,
17
+ idempotentHint: true,
18
+ openWorldHint: true,
19
+ },
20
+ input: z.object({
21
+ query: z
22
+ .string()
23
+ .describe('Full-text search query (e.g. "python async generator send value").'),
24
+ site: z
25
+ .string()
26
+ .default('stackoverflow')
27
+ .describe('Stack Exchange site to search — use the api_site_parameter value (e.g. "stackoverflow", "superuser", "serverfault"). ' +
28
+ 'Defaults to "stackoverflow". Call stackexchange_list_sites to discover valid values.'),
29
+ tags: z
30
+ .array(z.string().describe('A single tag to filter by (e.g. "python", "async").'))
31
+ .optional()
32
+ .describe('Filter results to questions with all specified tags.'),
33
+ acceptedOnly: z
34
+ .boolean()
35
+ .optional()
36
+ .describe('When true, return only questions that have an accepted answer.'),
37
+ minScore: z
38
+ .number()
39
+ .int()
40
+ .optional()
41
+ .describe('Minimum question score — excludes questions with lower scores.'),
42
+ sort: z
43
+ .enum(['relevance', 'votes', 'activity', 'newest'])
44
+ .default('relevance')
45
+ .describe('Result ordering: "relevance" (default, best match), "votes" (highest score first), ' +
46
+ '"activity" (most recently active), "newest" (most recently created).'),
47
+ pageSize: z
48
+ .number()
49
+ .int()
50
+ .min(1)
51
+ .max(30)
52
+ .default(10)
53
+ .describe('Number of results to return (1–30, default 10).'),
54
+ }),
55
+ output: z.object({
56
+ questions: z
57
+ .array(z
58
+ .object({
59
+ questionId: z
60
+ .number()
61
+ .int()
62
+ .describe('Question ID — pass to stackexchange_get_thread to fetch the full thread.'),
63
+ title: z.string().describe('Question title.'),
64
+ link: z.string().describe('Direct URL to the question.'),
65
+ score: z.number().int().describe('Question score (upvotes minus downvotes).'),
66
+ answerCount: z.number().int().describe('Total number of answers.'),
67
+ isAnswered: z
68
+ .boolean()
69
+ .describe('True when the question has an accepted answer or at least one positively-scored answer.'),
70
+ tags: z
71
+ .array(z.string().describe('A tag applied to this question.'))
72
+ .describe('Tags applied to this question.'),
73
+ excerpt: z
74
+ .string()
75
+ .optional()
76
+ .describe('Short text excerpt from the question when available.'),
77
+ })
78
+ .describe('A Stack Exchange question with score, answer count, tags, and optional excerpt.'))
79
+ .describe('Questions matching the search query, ordered by the specified sort.'),
80
+ }),
81
+ enrichment: {
82
+ quotaRemaining: z.number().describe('Remaining API quota calls for the current day.'),
83
+ quotaMax: z
84
+ .number()
85
+ .describe('Maximum API quota calls per day (300 keyless, ~10,000 with API key).'),
86
+ },
87
+ enrichmentTrailer: {
88
+ quotaRemaining: { label: 'Quota Remaining' },
89
+ quotaMax: { label: 'Quota Max' },
90
+ },
91
+ errors: [
92
+ {
93
+ reason: 'invalid_site',
94
+ code: JsonRpcErrorCode.InvalidParams,
95
+ when: 'The provided site value is not a valid Stack Exchange network site identifier.',
96
+ recovery: 'Call stackexchange_list_sites to discover valid site api_site_parameter values and retry.',
97
+ },
98
+ {
99
+ reason: 'quota_exceeded',
100
+ code: JsonRpcErrorCode.RateLimited,
101
+ when: 'The Stack Exchange API quota_remaining has reached 0.',
102
+ recovery: 'Quota resets at midnight UTC; set STACKEXCHANGE_API_KEY to lift the limit to 10,000 per day.',
103
+ },
104
+ ],
105
+ async handler(input, ctx) {
106
+ const svc = getStackExchangeService();
107
+ const filteredTags = input.tags?.filter(Boolean);
108
+ const { questions, quotaRemaining, quotaMax } = await svc.searchQuestions({
109
+ query: input.query,
110
+ site: input.site,
111
+ ...(filteredTags && filteredTags.length > 0 ? { tags: filteredTags } : {}),
112
+ ...(input.acceptedOnly !== undefined ? { acceptedOnly: input.acceptedOnly } : {}),
113
+ ...(input.minScore !== undefined ? { minScore: input.minScore } : {}),
114
+ sort: input.sort,
115
+ pageSize: input.pageSize,
116
+ }, ctx);
117
+ ctx.enrich({ quotaRemaining, quotaMax });
118
+ if (questions.length === 0) {
119
+ ctx.enrich.notice(`No questions matched "${input.query}" on ${input.site}. Try broader terms, different tags, or a different site.`);
120
+ }
121
+ ctx.log.info('Searched SE questions', {
122
+ query: input.query,
123
+ site: input.site,
124
+ count: questions.length,
125
+ });
126
+ return { questions };
127
+ },
128
+ format: (result) => {
129
+ if (result.questions.length === 0) {
130
+ return [{ type: 'text', text: 'No questions found for the given query.' }];
131
+ }
132
+ const lines = [];
133
+ for (const q of result.questions) {
134
+ lines.push(`## ${q.title}`);
135
+ lines.push(`**ID:** ${q.questionId} | **Score:** ${q.score} | **Answers:** ${q.answerCount} | **Answered:** ${q.isAnswered ? 'Yes' : 'No'}`);
136
+ lines.push(`**Tags:** ${q.tags.join(', ')}`);
137
+ lines.push(`**Link:** ${q.link}`);
138
+ if (q.excerpt)
139
+ lines.push(`\n${q.excerpt}`);
140
+ lines.push('');
141
+ }
142
+ return [{ type: 'text', text: lines.join('\n') }];
143
+ },
144
+ });
145
+ //# sourceMappingURL=stackexchange-search-questions.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stackexchange-search-questions.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/stackexchange-search-questions.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @fileoverview Lightweight HTML→markdown normalizer for Stack Exchange post bodies.
3
+ * Handles SE's known tag set: p, pre/code, strong, em, a, ul, ol, li, h1-h6,
4
+ * blockquote, inline code. No external dependency required.
5
+ * @module services/stackexchange/html-normalizer
6
+ */
7
+ /**
8
+ * Convert a Stack Exchange HTML post body to clean markdown.
9
+ * Operates on SE's predictable, limited HTML tag set.
10
+ */
11
+ export declare function normalizeHtml(html: string): string;
12
+ /**
13
+ * Decode basic HTML entities in a plain-text string (not HTML).
14
+ * Use this for SE API fields like site names and audiences that arrive
15
+ * HTML-encoded but contain no markup.
16
+ */
17
+ export declare function decodeHtmlEntities(text: string): string;
18
+ //# sourceMappingURL=html-normalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html-normalizer.d.ts","sourceRoot":"","sources":["../../../src/services/stackexchange/html-normalizer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwJlD;AAOD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD"}