@esaio/esa-mcp-server 0.6.1 → 0.7.0

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/bin/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{l as e}from"./mcp-response-Cn2WFDAj.js";import{n as t,t as n}from"./prompts-OTelQWjQ.js";import"./teams-B9y1URzV.js";import{t as r}from"./resources-BOHSQfAG.js";import{t as i}from"./tools-6rPxMwBY.js";import{McpServer as a}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as o}from"@modelcontextprotocol/sdk/server/stdio.js";const s={esa:{apiAccessToken:process.env.ESA_ACCESS_TOKEN||``,apiBaseUrl:process.env.ESA_API_BASE_URL||`https://api.esa.io`},server:{name:`esa-mcp-server`,version:e,description:`Official MCP server for esa.io`}};function c(){if(!s.esa.apiAccessToken)throw Error(`ESA_ACCESS_TOKEN environment variable is required`)}try{c()}catch(e){console.error(`Configuration error:`,e),process.exit(1)}async function l(){await t();let e=new a({name:s.server.name,version:s.server.version});i(e,s.esa),r(e,s.esa),n(e,s.esa);let c=new o;c.onclose=()=>{console.error(`Transport closed`)},c.onerror=e=>{console.error(`Transport error:`,e)},await e.connect(c),console.error(`${s.server.name} v${s.server.version} started`)}await l().catch(e=>{console.error(`Server startup error:`,e),process.exit(1)});export{};
2
+ import{l as e}from"./mcp-response-XQamL4IZ.js";import{n as t,t as n}from"./prompts-3uyOmWBt.js";import{t as r}from"./resources-B_W925Tu.js";import{t as i}from"./tools-B1-4McPh.js";import{McpServer as a}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as o}from"@modelcontextprotocol/sdk/server/stdio.js";const s={esa:{apiAccessToken:process.env.ESA_ACCESS_TOKEN||``,apiBaseUrl:process.env.ESA_API_BASE_URL||`https://api.esa.io`},server:{name:`esa-mcp-server`,version:e,description:`Official MCP server for esa.io`}};function c(){if(!s.esa.apiAccessToken)throw Error(`ESA_ACCESS_TOKEN environment variable is required`)}try{c()}catch(e){console.error(`Configuration error:`,e),process.exit(1)}async function l(){await t();let e=new a({name:s.server.name,version:s.server.version});i(e,s.esa),r(e,s.esa),n(e,s.esa);let c=new o;c.onclose=()=>{console.error(`Transport closed`)},c.onerror=e=>{console.error(`Transport error:`,e)},await e.connect(c),console.error(`${s.server.name} v${s.server.version} started`)}await l().catch(e=>{console.error(`Server startup error:`,e),process.exit(1)});export{};
3
3
  //# sourceMappingURL=index.js.map
package/bin/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["packageJson.version"],"sources":["../src/config/index.ts","../src/index.ts"],"sourcesContent":["import packageJson from \"../../package.json\" with { type: \"json\" };\nimport type { StdioContext } from \"../context/stdio-context.js\";\n\nexport const config = {\n esa: {\n apiAccessToken: process.env.ESA_ACCESS_TOKEN || \"\",\n apiBaseUrl: process.env.ESA_API_BASE_URL || \"https://api.esa.io\",\n } satisfies StdioContext,\n server: {\n name: \"esa-mcp-server\",\n version: packageJson.version,\n description: \"Official MCP server for esa.io\",\n },\n} as const;\n\nexport function validateConfig(): void {\n if (!config.esa.apiAccessToken) {\n throw new Error(\"ESA_ACCESS_TOKEN environment variable is required\");\n }\n}\n","#!/usr/bin/env node\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { config, validateConfig } from \"./config/index.js\";\nimport { initI18n } from \"./i18n/index.js\";\nimport { setupPrompts } from \"./prompts/index.js\";\nimport { setupResources } from \"./resources/index.js\";\nimport { setupTools } from \"./tools/index.js\";\n\ntry {\n validateConfig();\n} catch (error) {\n console.error(\"Configuration error:\", error);\n process.exit(1);\n}\n\nasync function main() {\n await initI18n();\n\n const server = new McpServer({\n name: config.server.name,\n version: config.server.version,\n });\n\n setupTools(server, config.esa);\n setupResources(server, config.esa);\n setupPrompts(server, config.esa);\n\n const transport = new StdioServerTransport();\n\n // Handle transport errors gracefully\n transport.onclose = () => {\n console.error(\"Transport closed\");\n };\n\n transport.onerror = (error) => {\n console.error(\"Transport error:\", error);\n };\n\n await server.connect(transport);\n console.error(`${config.server.name} v${config.server.version} started`);\n}\n\nawait main().catch((error) => {\n console.error(\"Server startup error:\", error);\n process.exit(1);\n});\n"],"mappings":";qWAGA,MAAa,EAAS,CACpB,IAAK,CACH,eAAgB,QAAQ,IAAI,kBAAoB,GAChD,WAAY,QAAQ,IAAI,kBAAoB,qBAC7C,CACD,OAAQ,CACN,KAAM,iBACGA,UACT,YAAa,iCACd,CACF,CAED,SAAgB,GAAuB,CACrC,GAAI,CAAC,EAAO,IAAI,eACd,MAAU,MAAM,oDAAoD,CCRxE,GAAI,CACF,GAAgB,OACT,EAAO,CACd,QAAQ,MAAM,uBAAwB,EAAM,CAC5C,QAAQ,KAAK,EAAE,CAGjB,eAAe,GAAO,CACpB,MAAM,GAAU,CAEhB,IAAM,EAAS,IAAI,EAAU,CAC3B,KAAM,EAAO,OAAO,KACpB,QAAS,EAAO,OAAO,QACxB,CAAC,CAEF,EAAW,EAAQ,EAAO,IAAI,CAC9B,EAAe,EAAQ,EAAO,IAAI,CAClC,EAAa,EAAQ,EAAO,IAAI,CAEhC,IAAM,EAAY,IAAI,EAGtB,EAAU,YAAgB,CACxB,QAAQ,MAAM,mBAAmB,EAGnC,EAAU,QAAW,GAAU,CAC7B,QAAQ,MAAM,mBAAoB,EAAM,EAG1C,MAAM,EAAO,QAAQ,EAAU,CAC/B,QAAQ,MAAM,GAAG,EAAO,OAAO,KAAK,IAAI,EAAO,OAAO,QAAQ,UAAU,CAG1E,MAAM,GAAM,CAAC,MAAO,GAAU,CAC5B,QAAQ,MAAM,wBAAyB,EAAM,CAC7C,QAAQ,KAAK,EAAE,EACf"}
1
+ {"version":3,"file":"index.js","names":["packageJson.version"],"sources":["../src/config/index.ts","../src/index.ts"],"sourcesContent":["import packageJson from \"../../package.json\" with { type: \"json\" };\nimport type { StdioContext } from \"../context/stdio-context.js\";\n\nexport const config = {\n esa: {\n apiAccessToken: process.env.ESA_ACCESS_TOKEN || \"\",\n apiBaseUrl: process.env.ESA_API_BASE_URL || \"https://api.esa.io\",\n } satisfies StdioContext,\n server: {\n name: \"esa-mcp-server\",\n version: packageJson.version,\n description: \"Official MCP server for esa.io\",\n },\n} as const;\n\nexport function validateConfig(): void {\n if (!config.esa.apiAccessToken) {\n throw new Error(\"ESA_ACCESS_TOKEN environment variable is required\");\n }\n}\n","#!/usr/bin/env node\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { config, validateConfig } from \"./config/index.js\";\nimport { initI18n } from \"./i18n/index.js\";\nimport { setupPrompts } from \"./prompts/index.js\";\nimport { setupResources } from \"./resources/index.js\";\nimport { setupTools } from \"./tools/index.js\";\n\ntry {\n validateConfig();\n} catch (error) {\n console.error(\"Configuration error:\", error);\n process.exit(1);\n}\n\nasync function main() {\n await initI18n();\n\n const server = new McpServer({\n name: config.server.name,\n version: config.server.version,\n });\n\n setupTools(server, config.esa);\n setupResources(server, config.esa);\n setupPrompts(server, config.esa);\n\n const transport = new StdioServerTransport();\n\n // Handle transport errors gracefully\n transport.onclose = () => {\n console.error(\"Transport closed\");\n };\n\n transport.onerror = (error) => {\n console.error(\"Transport error:\", error);\n };\n\n await server.connect(transport);\n console.error(`${config.server.name} v${config.server.version} started`);\n}\n\nawait main().catch((error) => {\n console.error(\"Server startup error:\", error);\n process.exit(1);\n});\n"],"mappings":";yUAGA,MAAa,EAAS,CACpB,IAAK,CACH,eAAgB,QAAQ,IAAI,kBAAoB,GAChD,WAAY,QAAQ,IAAI,kBAAoB,qBAC7C,CACD,OAAQ,CACN,KAAM,iBACGA,UACT,YAAa,iCACd,CACF,CAED,SAAgB,GAAuB,CACrC,GAAI,CAAC,EAAO,IAAI,eACd,MAAU,MAAM,oDAAoD,CCRxE,GAAI,CACF,GAAgB,OACT,EAAO,CACd,QAAQ,MAAM,uBAAwB,EAAM,CAC5C,QAAQ,KAAK,EAAE,CAGjB,eAAe,GAAO,CACpB,MAAM,GAAU,CAEhB,IAAM,EAAS,IAAI,EAAU,CAC3B,KAAM,EAAO,OAAO,KACpB,QAAS,EAAO,OAAO,QACxB,CAAC,CAEF,EAAW,EAAQ,EAAO,IAAI,CAC9B,EAAe,EAAQ,EAAO,IAAI,CAClC,EAAa,EAAQ,EAAO,IAAI,CAEhC,IAAM,EAAY,IAAI,EAGtB,EAAU,YAAgB,CACxB,QAAQ,MAAM,mBAAmB,EAGnC,EAAU,QAAW,GAAU,CAC7B,QAAQ,MAAM,mBAAoB,EAAM,EAG1C,MAAM,EAAO,QAAQ,EAAU,CAC/B,QAAQ,MAAM,GAAG,EAAO,OAAO,KAAK,IAAI,EAAO,OAAO,QAAQ,UAAU,CAG1E,MAAM,GAAM,CAAC,MAAO,GAAU,CAC5B,QAAQ,MAAM,wBAAyB,EAAM,CAC7C,QAAQ,KAAK,EAAE,EACf"}
@@ -1,2 +1,2 @@
1
- import e from"openapi-fetch";var t=`0.6.1`;function n(e){return{async onRequest({request:t}){return t.headers.set(`Authorization`,`Bearer ${e}`),t},async onResponse({response:e}){let t=e.headers.get(`x-ratelimit-limit`),n=e.headers.get(`x-ratelimit-remaining`);t&&n&&console.error(`Rate limit: ${n}/${t}`)},async onError({error:e}){console.error(`Network Error:`,e)}}}const r=t;function i(e){return{async onRequest({request:t}){return t.headers.set(`User-Agent`,`esa-mcp-server/${e} (official)`),t}}}function a(t,a=`https://api.esa.io`){let o=e({baseUrl:a});return o.use(i(r)),o.use(n(t)),o}async function o(e,t,...n){let r;if(`apiAccessToken`in e&&`apiBaseUrl`in e)r=a(e.apiAccessToken,e.apiBaseUrl);else throw Error(`Unsupported context type. Only StdioContext is currently supported.`);return t(r,...n)}var s=class extends Error{constructor(){super(`Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.`),this.name=`MissingTeamNameError`}};function c(e){return e instanceof Error?`Error: ${e.message}`:typeof e==`number`&&e!==null?`Error: API Response(status: ${e})`:typeof e==`object`&&e?`Error: ${JSON.stringify(e,null,2)}`:`Error: ${String(e)}`}function l(e){return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}function u(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:JSON.stringify(e,null,2)}]}}function d(e){return{messages:[{role:`user`,content:{type:`text`,text:e}}]}}function f(e){return{content:[{type:`text`,text:c(e)}]}}function p(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:c(e)}]}}function m(e){return{messages:[{role:`user`,content:{type:`text`,text:c(e)}}]}}export{f as a,o as c,u as i,t as l,d as n,l as o,p as r,s,m as t};
2
- //# sourceMappingURL=mcp-response-Cn2WFDAj.js.map
1
+ import e from"openapi-fetch";var t=`0.7.0`;function n(e){return{async onRequest({request:t}){return t.headers.set(`Authorization`,`Bearer ${e}`),t},async onResponse({response:e}){let t=e.headers.get(`x-ratelimit-limit`),n=e.headers.get(`x-ratelimit-remaining`);t&&n&&console.error(`Rate limit: ${n}/${t}`)},async onError({error:e}){console.error(`Network Error:`,e)}}}const r=t;function i(e){return{async onRequest({request:t}){return t.headers.set(`User-Agent`,`esa-mcp-server/${e} (official)`),t}}}function a(t,a=`https://api.esa.io`){let o=e({baseUrl:a});return o.use(i(r)),o.use(n(t)),o}async function o(e,t,...n){let r;if(`apiAccessToken`in e&&`apiBaseUrl`in e)r=a(e.apiAccessToken,e.apiBaseUrl);else throw Error(`Unsupported context type. Only StdioContext is currently supported.`);return t(r,...n)}var s=class extends Error{constructor(){super(`Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.`),this.name=`MissingTeamNameError`}};function c(e){return e instanceof Error?`Error: ${e.message}`:typeof e==`number`&&e!==null?`Error: API Response(status: ${e})`:typeof e==`object`&&e?`Error: ${JSON.stringify(e,null,2)}`:`Error: ${String(e)}`}function l(e){return{content:[{type:`text`,text:JSON.stringify(e,null,2)}]}}function u(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:JSON.stringify(e,null,2)}]}}function d(e){return{messages:[{role:`user`,content:{type:`text`,text:e}}]}}function f(e){return{content:[{type:`text`,text:c(e)}]}}function p(e,t){return{contents:[{uri:t,mimeType:`application/json`,text:c(e)}]}}function m(e){return{messages:[{role:`user`,content:{type:`text`,text:c(e)}}]}}export{f as a,o as c,u as i,t as l,d as n,l as o,p as r,s,m as t};
2
+ //# sourceMappingURL=mcp-response-XQamL4IZ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-response-Cn2WFDAj.js","names":["packageJson.version"],"sources":["../package.json","../src/api_client/middleware.ts","../src/api_client/index.ts","../src/api_client/with-context.ts","../src/errors/missing-team-name-error.ts","../src/formatters/mcp-response.ts"],"sourcesContent":["","import type { Middleware } from \"openapi-fetch\";\n\nexport function createAuthMiddleware(apiAccessToken: string): Middleware {\n return {\n async onRequest({ request }) {\n request.headers.set(\"Authorization\", `Bearer ${apiAccessToken}`);\n return request;\n },\n async onResponse({ response }) {\n const rateLimit = response.headers.get(\"x-ratelimit-limit\");\n const remaining = response.headers.get(\"x-ratelimit-remaining\");\n if (rateLimit && remaining) {\n console.error(`Rate limit: ${remaining}/${rateLimit}`);\n }\n // return undefined to avoid openapi-fetch instanceof Response check issue\n // https://github.com/openapi-ts/openapi-typescript/issues/1847\n },\n async onError({ error }) {\n console.error(\"Network Error:\", error);\n },\n };\n}\n","import createClient from \"openapi-fetch\";\nimport packageJson from \"../../package.json\" with { type: \"json\" };\nimport type { paths } from \"../generated/api-types.js\";\nimport { createAuthMiddleware } from \"./middleware.js\";\n\nconst packageVersion = packageJson.version;\n\nfunction createUserAgentMiddleware(version: string) {\n return {\n async onRequest({ request }: { request: Request }) {\n request.headers.set(\"User-Agent\", `esa-mcp-server/${version} (official)`);\n return request;\n },\n };\n}\n\nexport function createEsaClient(\n apiAccessToken: string,\n apiBaseUrl: string = \"https://api.esa.io\",\n) {\n const client = createClient<paths>({\n baseUrl: apiBaseUrl,\n });\n\n client.use(createUserAgentMiddleware(packageVersion));\n client.use(createAuthMiddleware(apiAccessToken));\n\n return client;\n}\n","import { createEsaClient } from \"../api_client/index.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\n\nexport async function withContext<T extends unknown[], R>(\n context: MCPContext,\n handler: (\n client: ReturnType<typeof createEsaClient>,\n ...args: T\n ) => Promise<R>,\n ...args: T\n): Promise<R> {\n let client: ReturnType<typeof createEsaClient>;\n\n if (\"apiAccessToken\" in context && \"apiBaseUrl\" in context) {\n client = createEsaClient(\n context.apiAccessToken as string,\n context.apiBaseUrl as string,\n );\n } else {\n throw new Error(\n \"Unsupported context type. Only StdioContext is currently supported.\",\n );\n }\n\n return handler(client, ...args);\n}\n","export class MissingTeamNameError extends Error {\n constructor() {\n super(\n \"Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.\",\n );\n this.name = \"MissingTeamNameError\";\n }\n}\n","import type {\n CallToolResult,\n GetPromptResult,\n ReadResourceResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nfunction formatErrorText(error: unknown): string {\n return error instanceof Error\n ? `Error: ${error.message}`\n : typeof error === \"number\" && error !== null\n ? `Error: API Response(status: ${error})`\n : typeof error === \"object\" && error !== null\n ? `Error: ${JSON.stringify(error, null, 2)}`\n : `Error: ${String(error)}`;\n}\n\nexport function formatToolResponse(data: unknown): CallToolResult {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n\nexport function formatResourceResponse(\n data: unknown,\n uri: string,\n): ReadResourceResult {\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n\nexport function formatPromptResponse(message: string): GetPromptResult {\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: message,\n },\n },\n ],\n };\n}\n\nexport function formatToolError(error: unknown): CallToolResult {\n return {\n content: [\n {\n type: \"text\",\n text: formatErrorText(error),\n },\n ],\n };\n}\n\nexport function formatResourceError(\n error: unknown,\n uri: string,\n): ReadResourceResult {\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: formatErrorText(error),\n },\n ],\n };\n}\n\nexport function formatPromptError(error: unknown): GetPromptResult {\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: formatErrorText(error),\n },\n },\n ],\n };\n}\n"],"mappings":"2CCEA,SAAgB,EAAqB,EAAoC,CACvE,MAAO,CACL,MAAM,UAAU,CAAE,WAAW,CAE3B,OADA,EAAQ,QAAQ,IAAI,gBAAiB,UAAU,IAAiB,CACzD,GAET,MAAM,WAAW,CAAE,YAAY,CAC7B,IAAM,EAAY,EAAS,QAAQ,IAAI,oBAAoB,CACrD,EAAY,EAAS,QAAQ,IAAI,wBAAwB,CAC3D,GAAa,GACf,QAAQ,MAAM,eAAe,EAAU,GAAG,IAAY,EAK1D,MAAM,QAAQ,CAAE,SAAS,CACvB,QAAQ,MAAM,iBAAkB,EAAM,EAEzC,CCfH,MAAM,EAAiBA,EAEvB,SAAS,EAA0B,EAAiB,CAClD,MAAO,CACL,MAAM,UAAU,CAAE,WAAiC,CAEjD,OADA,EAAQ,QAAQ,IAAI,aAAc,kBAAkB,EAAQ,aAAa,CAClE,GAEV,CAGH,SAAgB,EACd,EACA,EAAqB,qBACrB,CACA,IAAM,EAAS,EAAoB,CACjC,QAAS,EACV,CAAC,CAKF,OAHA,EAAO,IAAI,EAA0B,EAAe,CAAC,CACrD,EAAO,IAAI,EAAqB,EAAe,CAAC,CAEzC,ECxBT,eAAsB,EACpB,EACA,EAIA,GAAG,EACS,CACZ,IAAI,EAEJ,GAAI,mBAAoB,GAAW,eAAgB,EACjD,EAAS,EACP,EAAQ,eACR,EAAQ,WACT,MAED,MAAU,MACR,sEACD,CAGH,OAAO,EAAQ,EAAQ,GAAG,EAAK,CCxBjC,IAAa,EAAb,cAA0C,KAAM,CAC9C,aAAc,CACZ,MACE,wHACD,CACD,KAAK,KAAO,yBCChB,SAAS,EAAgB,EAAwB,CAC/C,OAAO,aAAiB,MACpB,UAAU,EAAM,UAChB,OAAO,GAAU,UAAY,IAAU,KACrC,+BAA+B,EAAM,GACrC,OAAO,GAAU,UAAY,EAC3B,UAAU,KAAK,UAAU,EAAO,KAAM,EAAE,GACxC,UAAU,OAAO,EAAM,GAGjC,SAAgB,EAAmB,EAA+B,CAChE,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,KAAK,UAAU,EAAM,KAAM,EAAE,CACpC,CACF,CACF,CAGH,SAAgB,EACd,EACA,EACoB,CACpB,MAAO,CACL,SAAU,CACR,CACE,MACA,SAAU,mBACV,KAAM,KAAK,UAAU,EAAM,KAAM,EAAE,CACpC,CACF,CACF,CAGH,SAAgB,EAAqB,EAAkC,CACrE,MAAO,CACL,SAAU,CACR,CACE,KAAM,OACN,QAAS,CACP,KAAM,OACN,KAAM,EACP,CACF,CACF,CACF,CAGH,SAAgB,EAAgB,EAAgC,CAC9D,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,EAAgB,EAAM,CAC7B,CACF,CACF,CAGH,SAAgB,EACd,EACA,EACoB,CACpB,MAAO,CACL,SAAU,CACR,CACE,MACA,SAAU,mBACV,KAAM,EAAgB,EAAM,CAC7B,CACF,CACF,CAGH,SAAgB,EAAkB,EAAiC,CACjE,MAAO,CACL,SAAU,CACR,CACE,KAAM,OACN,QAAS,CACP,KAAM,OACN,KAAM,EAAgB,EAAM,CAC7B,CACF,CACF,CACF"}
1
+ {"version":3,"file":"mcp-response-XQamL4IZ.js","names":["packageJson.version"],"sources":["../package.json","../src/api_client/middleware.ts","../src/api_client/index.ts","../src/api_client/with-context.ts","../src/errors/missing-team-name-error.ts","../src/formatters/mcp-response.ts"],"sourcesContent":["","import type { Middleware } from \"openapi-fetch\";\n\nexport function createAuthMiddleware(apiAccessToken: string): Middleware {\n return {\n async onRequest({ request }) {\n request.headers.set(\"Authorization\", `Bearer ${apiAccessToken}`);\n return request;\n },\n async onResponse({ response }) {\n const rateLimit = response.headers.get(\"x-ratelimit-limit\");\n const remaining = response.headers.get(\"x-ratelimit-remaining\");\n if (rateLimit && remaining) {\n console.error(`Rate limit: ${remaining}/${rateLimit}`);\n }\n // return undefined to avoid openapi-fetch instanceof Response check issue\n // https://github.com/openapi-ts/openapi-typescript/issues/1847\n },\n async onError({ error }) {\n console.error(\"Network Error:\", error);\n },\n };\n}\n","import createClient from \"openapi-fetch\";\nimport packageJson from \"../../package.json\" with { type: \"json\" };\nimport type { paths } from \"../generated/api-types.js\";\nimport { createAuthMiddleware } from \"./middleware.js\";\n\nconst packageVersion = packageJson.version;\n\nfunction createUserAgentMiddleware(version: string) {\n return {\n async onRequest({ request }: { request: Request }) {\n request.headers.set(\"User-Agent\", `esa-mcp-server/${version} (official)`);\n return request;\n },\n };\n}\n\nexport function createEsaClient(\n apiAccessToken: string,\n apiBaseUrl: string = \"https://api.esa.io\",\n) {\n const client = createClient<paths>({\n baseUrl: apiBaseUrl,\n });\n\n client.use(createUserAgentMiddleware(packageVersion));\n client.use(createAuthMiddleware(apiAccessToken));\n\n return client;\n}\n","import { createEsaClient } from \"../api_client/index.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\n\nexport async function withContext<T extends unknown[], R>(\n context: MCPContext,\n handler: (\n client: ReturnType<typeof createEsaClient>,\n ...args: T\n ) => Promise<R>,\n ...args: T\n): Promise<R> {\n let client: ReturnType<typeof createEsaClient>;\n\n if (\"apiAccessToken\" in context && \"apiBaseUrl\" in context) {\n client = createEsaClient(\n context.apiAccessToken as string,\n context.apiBaseUrl as string,\n );\n } else {\n throw new Error(\n \"Unsupported context type. Only StdioContext is currently supported.\",\n );\n }\n\n return handler(client, ...args);\n}\n","export class MissingTeamNameError extends Error {\n constructor() {\n super(\n \"Missing required parameter 'teamName'. Use esa_get_teams to list available teams, then retry with teamName specified.\",\n );\n this.name = \"MissingTeamNameError\";\n }\n}\n","import type {\n CallToolResult,\n GetPromptResult,\n ReadResourceResult,\n} from \"@modelcontextprotocol/sdk/types.js\";\n\nfunction formatErrorText(error: unknown): string {\n return error instanceof Error\n ? `Error: ${error.message}`\n : typeof error === \"number\" && error !== null\n ? `Error: API Response(status: ${error})`\n : typeof error === \"object\" && error !== null\n ? `Error: ${JSON.stringify(error, null, 2)}`\n : `Error: ${String(error)}`;\n}\n\nexport function formatToolResponse(data: unknown): CallToolResult {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n\nexport function formatResourceResponse(\n data: unknown,\n uri: string,\n): ReadResourceResult {\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n };\n}\n\nexport function formatPromptResponse(message: string): GetPromptResult {\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: message,\n },\n },\n ],\n };\n}\n\nexport function formatToolError(error: unknown): CallToolResult {\n return {\n content: [\n {\n type: \"text\",\n text: formatErrorText(error),\n },\n ],\n };\n}\n\nexport function formatResourceError(\n error: unknown,\n uri: string,\n): ReadResourceResult {\n return {\n contents: [\n {\n uri,\n mimeType: \"application/json\",\n text: formatErrorText(error),\n },\n ],\n };\n}\n\nexport function formatPromptError(error: unknown): GetPromptResult {\n return {\n messages: [\n {\n role: \"user\",\n content: {\n type: \"text\",\n text: formatErrorText(error),\n },\n },\n ],\n };\n}\n"],"mappings":"2CCEA,SAAgB,EAAqB,EAAoC,CACvE,MAAO,CACL,MAAM,UAAU,CAAE,WAAW,CAE3B,OADA,EAAQ,QAAQ,IAAI,gBAAiB,UAAU,IAAiB,CACzD,GAET,MAAM,WAAW,CAAE,YAAY,CAC7B,IAAM,EAAY,EAAS,QAAQ,IAAI,oBAAoB,CACrD,EAAY,EAAS,QAAQ,IAAI,wBAAwB,CAC3D,GAAa,GACf,QAAQ,MAAM,eAAe,EAAU,GAAG,IAAY,EAK1D,MAAM,QAAQ,CAAE,SAAS,CACvB,QAAQ,MAAM,iBAAkB,EAAM,EAEzC,CCfH,MAAM,EAAiBA,EAEvB,SAAS,EAA0B,EAAiB,CAClD,MAAO,CACL,MAAM,UAAU,CAAE,WAAiC,CAEjD,OADA,EAAQ,QAAQ,IAAI,aAAc,kBAAkB,EAAQ,aAAa,CAClE,GAEV,CAGH,SAAgB,EACd,EACA,EAAqB,qBACrB,CACA,IAAM,EAAS,EAAoB,CACjC,QAAS,EACV,CAAC,CAKF,OAHA,EAAO,IAAI,EAA0B,EAAe,CAAC,CACrD,EAAO,IAAI,EAAqB,EAAe,CAAC,CAEzC,ECxBT,eAAsB,EACpB,EACA,EAIA,GAAG,EACS,CACZ,IAAI,EAEJ,GAAI,mBAAoB,GAAW,eAAgB,EACjD,EAAS,EACP,EAAQ,eACR,EAAQ,WACT,MAED,MAAU,MACR,sEACD,CAGH,OAAO,EAAQ,EAAQ,GAAG,EAAK,CCxBjC,IAAa,EAAb,cAA0C,KAAM,CAC9C,aAAc,CACZ,MACE,wHACD,CACD,KAAK,KAAO,yBCChB,SAAS,EAAgB,EAAwB,CAC/C,OAAO,aAAiB,MACpB,UAAU,EAAM,UAChB,OAAO,GAAU,UAAY,IAAU,KACrC,+BAA+B,EAAM,GACrC,OAAO,GAAU,UAAY,EAC3B,UAAU,KAAK,UAAU,EAAO,KAAM,EAAE,GACxC,UAAU,OAAO,EAAM,GAGjC,SAAgB,EAAmB,EAA+B,CAChE,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,KAAK,UAAU,EAAM,KAAM,EAAE,CACpC,CACF,CACF,CAGH,SAAgB,EACd,EACA,EACoB,CACpB,MAAO,CACL,SAAU,CACR,CACE,MACA,SAAU,mBACV,KAAM,KAAK,UAAU,EAAM,KAAM,EAAE,CACpC,CACF,CACF,CAGH,SAAgB,EAAqB,EAAkC,CACrE,MAAO,CACL,SAAU,CACR,CACE,KAAM,OACN,QAAS,CACP,KAAM,OACN,KAAM,EACP,CACF,CACF,CACF,CAGH,SAAgB,EAAgB,EAAgC,CAC9D,MAAO,CACL,QAAS,CACP,CACE,KAAM,OACN,KAAM,EAAgB,EAAM,CAC7B,CACF,CACF,CAGH,SAAgB,EACd,EACA,EACoB,CACpB,MAAO,CACL,SAAU,CACR,CACE,MACA,SAAU,mBACV,KAAM,EAAgB,EAAM,CAC7B,CACF,CACF,CAGH,SAAgB,EAAkB,EAAiC,CACjE,MAAO,CACL,SAAU,CACR,CACE,KAAM,OACN,QAAS,CACP,KAAM,OACN,KAAM,EAAgB,EAAM,CAC7B,CACF,CACF,CACF"}
@@ -1 +1 @@
1
- import"../mcp-response-Cn2WFDAj.js";import{t as e}from"../prompts-OTelQWjQ.js";export{e as setupPrompts};
1
+ import{t as e}from"../prompts-3uyOmWBt.js";export{e as setupPrompts};
@@ -1,7 +1,7 @@
1
- import{c as e,n as t,s as n,t as r}from"./mcp-response-Cn2WFDAj.js";import i from"i18next";import{z as a}from"zod";var o={prompts:{summarize_post:{title:`Summarize esa post`,description:`Summarize an esa post in various formats (bullet points, paragraph, or keywords)`,args:{team_name:`The name of the esa team`,post_number:`The post number to summarize`,format:`Summary format (bullet/paragraph/keywords)`}}}},s={prompts:{summarize_post:{title:`esaの記事の要約`,description:`esa記事を指定した形式で要約します(bullet: 箇条書き, paragraph: 文章, keywords: キーワード)`,args:{team_name:`esaチーム名`,post_number:`要約する記事番号`,format:`要約形式 (bullet/paragraph/keywords)`}}}};async function c(){let e=process.env.LC_ALL?.split(/[-_.]/)[0]||process.env.LC_MESSAGES?.split(/[-_.]/)[0]||process.env.LANG?.split(/[-_.]/)[0]||process.env.LANGUAGE?.split(/[-_.]/)[0]||`en`;return await i.init({lng:e,fallbackLng:`en`,showSupportNotice:!1,resources:{ja:{translation:s},en:{translation:o}},interpolation:{escapeValue:!1}}),i}function l(e,t){return i.t(e,t)}const u=()=>a.object({teamName:a.string().describe(l(`prompts.summarize_post.args.team_name`)),postNumber:a.string().describe(l(`prompts.summarize_post.args.post_number`)),format:a.enum([`bullet`,`paragraph`,`keywords`]).optional().describe(l(`prompts.summarize_post.args.format`))});async function d(e,i){let{teamName:a,postNumber:o,format:s=`bullet`}=i;if(!a)throw new n;let c=Number.parseInt(o,10);if(Number.isNaN(c)||c<=0)return r(`Post number must be a positive integer`);try{let{data:n,error:i,response:o}=await e.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:a,post_number:c}}});if(i||!o.ok)return r(i||o.status);let l=n,u=`Please summarize the following post:
1
+ import{c as e,n as t,s as n,t as r}from"./mcp-response-XQamL4IZ.js";import i from"i18next";import{z as a}from"zod";var o={prompts:{summarize_post:{title:`Summarize esa post`,description:`Summarize an esa post in various formats (bullet points, paragraph, or keywords)`,args:{team_name:`The name of the esa team`,post_number:`The post number to summarize`,format:`Summary format (bullet/paragraph/keywords)`}}}},s={prompts:{summarize_post:{title:`esaの記事の要約`,description:`esa記事を指定した形式で要約します(bullet: 箇条書き, paragraph: 文章, keywords: キーワード)`,args:{team_name:`esaチーム名`,post_number:`要約する記事番号`,format:`要約形式 (bullet/paragraph/keywords)`}}}};async function c(){let e=process.env.LC_ALL?.split(/[-_.]/)[0]||process.env.LC_MESSAGES?.split(/[-_.]/)[0]||process.env.LANG?.split(/[-_.]/)[0]||process.env.LANGUAGE?.split(/[-_.]/)[0]||`en`;return await i.init({lng:e,fallbackLng:`en`,showSupportNotice:!1,resources:{ja:{translation:s},en:{translation:o}},interpolation:{escapeValue:!1}}),i}function l(e,t){return i.t(e,t)}const u=()=>a.object({teamName:a.string().describe(l(`prompts.summarize_post.args.team_name`)),postNumber:a.string().describe(l(`prompts.summarize_post.args.post_number`)),format:a.enum([`bullet`,`paragraph`,`keywords`]).optional().describe(l(`prompts.summarize_post.args.format`))});async function d(e,i){let{teamName:a,postNumber:o,format:s=`bullet`}=i;if(!a)throw new n;let c=Number.parseInt(o,10);if(Number.isNaN(c)||c<=0)return r(`Post number must be a positive integer`);try{let{data:n,error:i,response:o}=await e.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:a,post_number:c}}});if(i||!o.ok)return r(i||o.status);let l=n,u=`Please summarize the following post:
2
2
 
3
3
  `;switch(u+=`Title: ${l.name}\n`,u+=`URL: ${l.url}\n`,u+=`Author: ${l.created_by.name}\n`,u+=`Created: ${l.created_at}\n`,u+=`Updated: ${l.updated_at}\n`,l.category&&(u+=`Category: ${l.category}\n`),l.tags&&l.tags.length>0&&(u+=`Tags: ${l.tags.join(`, `)}\n`),u+=`
4
4
  ---
5
5
 
6
6
  `,l.body_md&&(u+=`Content:\n${l.body_md}\n\n---\n\n`),s){case`bullet`:u+=`Please provide a summary in bullet points (3-5 main points).`;break;case`paragraph`:u+=`Please provide a summary in 2-3 paragraphs.`;break;case`keywords`:u+=`Please extract and list 10-15 important keywords from this post.`;break}return t(u)}catch(e){return r(e)}}function f(t,n){console.error(`Setting up MCP prompts...`),t.registerPrompt(`esa_summarize_post`,{title:l(`prompts.summarize_post.title`),description:l(`prompts.summarize_post.description`),argsSchema:u().shape},async t=>e(n,d,t))}export{c as n,f as t};
7
- //# sourceMappingURL=prompts-OTelQWjQ.js.map
7
+ //# sourceMappingURL=prompts-3uyOmWBt.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prompts-OTelQWjQ.js","names":["jaTranslations","enTranslations"],"sources":["../src/locales/en.json","../src/locales/ja.json","../src/i18n/index.ts","../src/prompts/summarize-post.ts","../src/prompts/index.ts"],"sourcesContent":["","","import i18next from \"i18next\";\nimport enTranslations from \"../locales/en.json\" with { type: \"json\" };\nimport jaTranslations from \"../locales/ja.json\" with { type: \"json\" };\n\nexport async function initI18n() {\n // https://github.com/neet/i18next-cli-language-detector/blob/main/src/i18next-cli-language-detector.ts\n // を参考に簡易版の環境変数からの自動検出\n const lng =\n process.env.LC_ALL?.split(/[-_.]/)[0] ||\n process.env.LC_MESSAGES?.split(/[-_.]/)[0] ||\n process.env.LANG?.split(/[-_.]/)[0] ||\n process.env.LANGUAGE?.split(/[-_.]/)[0] ||\n \"en\";\n await i18next.init({\n lng,\n fallbackLng: \"en\",\n showSupportNotice: false,\n resources: {\n ja: {\n translation: jaTranslations,\n },\n en: {\n translation: enTranslations,\n },\n },\n interpolation: {\n escapeValue: false,\n },\n });\n\n return i18next;\n}\n\nexport function setLanguage(lng: string) {\n return i18next.changeLanguage(lng);\n}\n\nexport function t(key: string, options?: Record<string, unknown>) {\n return i18next.t(key, options);\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatPromptError,\n formatPromptResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { t } from \"../i18n/index.js\";\n\n// 多言語化対応のために 関数化してスキーマを遅延定義している\nexport const createSummarizePostSchema = () =>\n z.object({\n teamName: z.string().describe(t(\"prompts.summarize_post.args.team_name\")),\n postNumber: z\n .string()\n .describe(t(\"prompts.summarize_post.args.post_number\")),\n format: z\n .enum([\"bullet\", \"paragraph\", \"keywords\"])\n .optional()\n .describe(t(\"prompts.summarize_post.args.format\")),\n });\n\nexport async function summarizePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<ReturnType<typeof createSummarizePostSchema>>,\n) {\n const { teamName, postNumber: postNumberStr, format = \"bullet\" } = args;\n\n if (!teamName) {\n throw new MissingTeamNameError();\n }\n const postNumber = Number.parseInt(postNumberStr, 10);\n\n if (Number.isNaN(postNumber) || postNumber <= 0) {\n return formatPromptError(\"Post number must be a positive integer\");\n }\n\n try {\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: teamName, post_number: postNumber },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatPromptError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n\n let prompt = `Please summarize the following post:\\n\\n`;\n prompt += `Title: ${post.name}\\n`;\n prompt += `URL: ${post.url}\\n`;\n prompt += `Author: ${post.created_by.name}\\n`;\n prompt += `Created: ${post.created_at}\\n`;\n prompt += `Updated: ${post.updated_at}\\n`;\n\n if (post.category) {\n prompt += `Category: ${post.category}\\n`;\n }\n\n if (post.tags && post.tags.length > 0) {\n prompt += `Tags: ${post.tags.join(\", \")}\\n`;\n }\n\n prompt += \"\\n---\\n\\n\";\n\n if (post.body_md) {\n prompt += `Content:\\n${post.body_md}\\n\\n---\\n\\n`;\n }\n\n switch (format) {\n case \"bullet\":\n prompt +=\n \"Please provide a summary in bullet points (3-5 main points).\";\n break;\n case \"paragraph\":\n prompt += \"Please provide a summary in 2-3 paragraphs.\";\n break;\n case \"keywords\":\n prompt +=\n \"Please extract and list 10-15 important keywords from this post.\";\n break;\n }\n\n return formatPromptResponse(prompt);\n } catch (error) {\n return formatPromptError(error);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { z } from \"zod\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\nimport { t } from \"../i18n/index.js\";\nimport { createSummarizePostSchema, summarizePost } from \"./summarize-post.js\";\n\nexport function setupPrompts(server: McpServer, context: MCPContext): void {\n console.error(\"Setting up MCP prompts...\");\n // NOTE: Streamable HTTP transport では ユーザーの LANG 設定に応じて i18next の lang 設定を変える\n server.registerPrompt(\n \"esa_summarize_post\",\n {\n title: t(\"prompts.summarize_post.title\"),\n description: t(\"prompts.summarize_post.description\"),\n argsSchema: createSummarizePostSchema().shape,\n },\n async (params: z.infer<ReturnType<typeof createSummarizePostSchema>>) =>\n withContext(context, summarizePost, params),\n );\n}\n"],"mappings":"unBEIA,eAAsB,GAAW,CAG/B,IAAM,EACJ,QAAQ,IAAI,QAAQ,MAAM,QAAQ,CAAC,IACnC,QAAQ,IAAI,aAAa,MAAM,QAAQ,CAAC,IACxC,QAAQ,IAAI,MAAM,MAAM,QAAQ,CAAC,IACjC,QAAQ,IAAI,UAAU,MAAM,QAAQ,CAAC,IACrC,KAkBF,OAjBA,MAAM,EAAQ,KAAK,CACjB,MACA,YAAa,KACb,kBAAmB,GACnB,UAAW,CACT,GAAI,CACF,YAAaA,EACd,CACD,GAAI,CACF,YAAaC,EACd,CACF,CACD,cAAe,CACb,YAAa,GACd,CACF,CAAC,CAEK,EAOT,SAAgB,EAAE,EAAa,EAAmC,CAChE,OAAO,EAAQ,EAAE,EAAK,EAAQ,CC3BhC,MAAa,MACX,EAAE,OAAO,CACP,SAAU,EAAE,QAAQ,CAAC,SAAS,EAAE,wCAAwC,CAAC,CACzE,WAAY,EACT,QAAQ,CACR,SAAS,EAAE,0CAA0C,CAAC,CACzD,OAAQ,EACL,KAAK,CAAC,SAAU,YAAa,WAAW,CAAC,CACzC,UAAU,CACV,SAAS,EAAE,qCAAqC,CAAC,CACrD,CAAC,CAEJ,eAAsB,EACpB,EACA,EACA,CACA,GAAM,CAAE,WAAU,WAAY,EAAe,SAAS,UAAa,EAEnE,GAAI,CAAC,EACH,MAAM,IAAI,EAEZ,IAAM,EAAa,OAAO,SAAS,EAAe,GAAG,CAErD,GAAI,OAAO,MAAM,EAAW,EAAI,GAAc,EAC5C,OAAO,EAAkB,yCAAyC,CAGpE,GAAI,CACF,GAAM,CAAE,OAAM,QAAO,YAAa,MAAM,EAAO,IAC7C,4CACA,CACE,OAAQ,CACN,KAAM,CAAE,UAAW,EAAU,YAAa,EAAY,CACvD,CACF,CACF,CAED,GAAI,GAAS,CAAC,EAAS,GACrB,OAAO,EAAkB,GAAS,EAAS,OAAO,CAGpD,IAAM,EAAsC,EAExC,EAAS;;EAqBb,OApBA,GAAU,UAAU,EAAK,KAAK,IAC9B,GAAU,QAAQ,EAAK,IAAI,IAC3B,GAAU,WAAW,EAAK,WAAW,KAAK,IAC1C,GAAU,YAAY,EAAK,WAAW,IACtC,GAAU,YAAY,EAAK,WAAW,IAElC,EAAK,WACP,GAAU,aAAa,EAAK,SAAS,KAGnC,EAAK,MAAQ,EAAK,KAAK,OAAS,IAClC,GAAU,SAAS,EAAK,KAAK,KAAK,KAAK,CAAC,KAG1C,GAAU;;;EAEN,EAAK,UACP,GAAU,aAAa,EAAK,QAAQ,cAG9B,EAAR,CACE,IAAK,SACH,GACE,+DACF,MACF,IAAK,YACH,GAAU,8CACV,MACF,IAAK,WACH,GACE,mEACF,MAGJ,OAAO,EAAqB,EAAO,OAC5B,EAAO,CACd,OAAO,EAAkB,EAAM,ECpFnC,SAAgB,EAAa,EAAmB,EAA2B,CACzE,QAAQ,MAAM,4BAA4B,CAE1C,EAAO,eACL,qBACA,CACE,MAAO,EAAE,+BAA+B,CACxC,YAAa,EAAE,qCAAqC,CACpD,WAAY,GAA2B,CAAC,MACzC,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C"}
1
+ {"version":3,"file":"prompts-3uyOmWBt.js","names":["jaTranslations","enTranslations"],"sources":["../src/locales/en.json","../src/locales/ja.json","../src/i18n/index.ts","../src/prompts/summarize-post.ts","../src/prompts/index.ts"],"sourcesContent":["","","import i18next from \"i18next\";\nimport enTranslations from \"../locales/en.json\" with { type: \"json\" };\nimport jaTranslations from \"../locales/ja.json\" with { type: \"json\" };\n\nexport async function initI18n() {\n // https://github.com/neet/i18next-cli-language-detector/blob/main/src/i18next-cli-language-detector.ts\n // を参考に簡易版の環境変数からの自動検出\n const lng =\n process.env.LC_ALL?.split(/[-_.]/)[0] ||\n process.env.LC_MESSAGES?.split(/[-_.]/)[0] ||\n process.env.LANG?.split(/[-_.]/)[0] ||\n process.env.LANGUAGE?.split(/[-_.]/)[0] ||\n \"en\";\n await i18next.init({\n lng,\n fallbackLng: \"en\",\n showSupportNotice: false,\n resources: {\n ja: {\n translation: jaTranslations,\n },\n en: {\n translation: enTranslations,\n },\n },\n interpolation: {\n escapeValue: false,\n },\n });\n\n return i18next;\n}\n\nexport function setLanguage(lng: string) {\n return i18next.changeLanguage(lng);\n}\n\nexport function t(key: string, options?: Record<string, unknown>) {\n return i18next.t(key, options);\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatPromptError,\n formatPromptResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { t } from \"../i18n/index.js\";\n\n// 多言語化対応のために 関数化してスキーマを遅延定義している\nexport const createSummarizePostSchema = () =>\n z.object({\n teamName: z.string().describe(t(\"prompts.summarize_post.args.team_name\")),\n postNumber: z\n .string()\n .describe(t(\"prompts.summarize_post.args.post_number\")),\n format: z\n .enum([\"bullet\", \"paragraph\", \"keywords\"])\n .optional()\n .describe(t(\"prompts.summarize_post.args.format\")),\n });\n\nexport async function summarizePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<ReturnType<typeof createSummarizePostSchema>>,\n) {\n const { teamName, postNumber: postNumberStr, format = \"bullet\" } = args;\n\n if (!teamName) {\n throw new MissingTeamNameError();\n }\n const postNumber = Number.parseInt(postNumberStr, 10);\n\n if (Number.isNaN(postNumber) || postNumber <= 0) {\n return formatPromptError(\"Post number must be a positive integer\");\n }\n\n try {\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: teamName, post_number: postNumber },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatPromptError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n\n let prompt = `Please summarize the following post:\\n\\n`;\n prompt += `Title: ${post.name}\\n`;\n prompt += `URL: ${post.url}\\n`;\n prompt += `Author: ${post.created_by.name}\\n`;\n prompt += `Created: ${post.created_at}\\n`;\n prompt += `Updated: ${post.updated_at}\\n`;\n\n if (post.category) {\n prompt += `Category: ${post.category}\\n`;\n }\n\n if (post.tags && post.tags.length > 0) {\n prompt += `Tags: ${post.tags.join(\", \")}\\n`;\n }\n\n prompt += \"\\n---\\n\\n\";\n\n if (post.body_md) {\n prompt += `Content:\\n${post.body_md}\\n\\n---\\n\\n`;\n }\n\n switch (format) {\n case \"bullet\":\n prompt +=\n \"Please provide a summary in bullet points (3-5 main points).\";\n break;\n case \"paragraph\":\n prompt += \"Please provide a summary in 2-3 paragraphs.\";\n break;\n case \"keywords\":\n prompt +=\n \"Please extract and list 10-15 important keywords from this post.\";\n break;\n }\n\n return formatPromptResponse(prompt);\n } catch (error) {\n return formatPromptError(error);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { z } from \"zod\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\nimport { t } from \"../i18n/index.js\";\nimport { createSummarizePostSchema, summarizePost } from \"./summarize-post.js\";\n\nexport function setupPrompts(server: McpServer, context: MCPContext): void {\n console.error(\"Setting up MCP prompts...\");\n // NOTE: Streamable HTTP transport では ユーザーの LANG 設定に応じて i18next の lang 設定を変える\n server.registerPrompt(\n \"esa_summarize_post\",\n {\n title: t(\"prompts.summarize_post.title\"),\n description: t(\"prompts.summarize_post.description\"),\n argsSchema: createSummarizePostSchema().shape,\n },\n async (params: z.infer<ReturnType<typeof createSummarizePostSchema>>) =>\n withContext(context, summarizePost, params),\n );\n}\n"],"mappings":"unBEIA,eAAsB,GAAW,CAG/B,IAAM,EACJ,QAAQ,IAAI,QAAQ,MAAM,QAAQ,CAAC,IACnC,QAAQ,IAAI,aAAa,MAAM,QAAQ,CAAC,IACxC,QAAQ,IAAI,MAAM,MAAM,QAAQ,CAAC,IACjC,QAAQ,IAAI,UAAU,MAAM,QAAQ,CAAC,IACrC,KAkBF,OAjBA,MAAM,EAAQ,KAAK,CACjB,MACA,YAAa,KACb,kBAAmB,GACnB,UAAW,CACT,GAAI,CACF,YAAaA,EACd,CACD,GAAI,CACF,YAAaC,EACd,CACF,CACD,cAAe,CACb,YAAa,GACd,CACF,CAAC,CAEK,EAOT,SAAgB,EAAE,EAAa,EAAmC,CAChE,OAAO,EAAQ,EAAE,EAAK,EAAQ,CC3BhC,MAAa,MACX,EAAE,OAAO,CACP,SAAU,EAAE,QAAQ,CAAC,SAAS,EAAE,wCAAwC,CAAC,CACzE,WAAY,EACT,QAAQ,CACR,SAAS,EAAE,0CAA0C,CAAC,CACzD,OAAQ,EACL,KAAK,CAAC,SAAU,YAAa,WAAW,CAAC,CACzC,UAAU,CACV,SAAS,EAAE,qCAAqC,CAAC,CACrD,CAAC,CAEJ,eAAsB,EACpB,EACA,EACA,CACA,GAAM,CAAE,WAAU,WAAY,EAAe,SAAS,UAAa,EAEnE,GAAI,CAAC,EACH,MAAM,IAAI,EAEZ,IAAM,EAAa,OAAO,SAAS,EAAe,GAAG,CAErD,GAAI,OAAO,MAAM,EAAW,EAAI,GAAc,EAC5C,OAAO,EAAkB,yCAAyC,CAGpE,GAAI,CACF,GAAM,CAAE,OAAM,QAAO,YAAa,MAAM,EAAO,IAC7C,4CACA,CACE,OAAQ,CACN,KAAM,CAAE,UAAW,EAAU,YAAa,EAAY,CACvD,CACF,CACF,CAED,GAAI,GAAS,CAAC,EAAS,GACrB,OAAO,EAAkB,GAAS,EAAS,OAAO,CAGpD,IAAM,EAAsC,EAExC,EAAS;;EAqBb,OApBA,GAAU,UAAU,EAAK,KAAK,IAC9B,GAAU,QAAQ,EAAK,IAAI,IAC3B,GAAU,WAAW,EAAK,WAAW,KAAK,IAC1C,GAAU,YAAY,EAAK,WAAW,IACtC,GAAU,YAAY,EAAK,WAAW,IAElC,EAAK,WACP,GAAU,aAAa,EAAK,SAAS,KAGnC,EAAK,MAAQ,EAAK,KAAK,OAAS,IAClC,GAAU,SAAS,EAAK,KAAK,KAAK,KAAK,CAAC,KAG1C,GAAU;;;EAEN,EAAK,UACP,GAAU,aAAa,EAAK,QAAQ,cAG9B,EAAR,CACE,IAAK,SACH,GACE,+DACF,MACF,IAAK,YACH,GAAU,8CACV,MACF,IAAK,WACH,GACE,mEACF,MAGJ,OAAO,EAAqB,EAAO,OAC5B,EAAO,CACd,OAAO,EAAkB,EAAM,ECpFnC,SAAgB,EAAa,EAAmB,EAA2B,CACzE,QAAQ,MAAM,4BAA4B,CAE1C,EAAO,eACL,qBACA,CACE,MAAO,EAAE,+BAA+B,CACxC,YAAa,EAAE,qCAAqC,CACpD,WAAY,GAA2B,CAAC,MACzC,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C"}
@@ -1 +1 @@
1
- import"../mcp-response-Cn2WFDAj.js";import"../teams-B9y1URzV.js";import{t as e}from"../resources-BOHSQfAG.js";export{e as setupResources};
1
+ import{t as e}from"../resources-B_W925Tu.js";export{e as setupResources};
@@ -1,2 +1,2 @@
1
- import{c as e,i as t,r as n}from"./mcp-response-Cn2WFDAj.js";import{d as r,s as i}from"./teams-B9y1URzV.js";import{ResourceTemplate as a}from"@modelcontextprotocol/sdk/server/mcp.js";const o={sort:`updated`,order:`desc`};async function s(e,i){let{teamName:a,uri:s}=i;try{let{data:i,error:c,response:l}=await e.GET(`/v1/teams/{team_name}/posts`,{params:{path:{team_name:a},query:o}});return c||!l.ok?n(c||l.status,s):t({...i,posts:i.posts?.map(e=>r(e,{truncateBody:500}))},s)}catch(e){return n(e,s)}}async function c(t){try{let n=await e(t,i,{});return JSON.parse(n.content[0].text).teams?.map(e=>({uri:`esa://teams/${e.name}/posts/recent`,name:`Recent posts from ${e.name}`,description:`Recent posts from ${e.name}${e.description?` (${e.description})`:``}`,mimeType:`application/json`}))||[]}catch(e){return console.error(`Failed to list teams:`,e),[]}}function l(t,n){console.error(`Setting up MCP resources...`),t.registerResource(`esa_recent_posts`,new a(`esa://teams/{teamName}/posts/recent`,{list:async()=>({resources:await c(n)})}),{title:`Recent Posts`,description:`Fetch recent updated posts from esa team`,mimeType:`application/json`},async(t,r)=>e(n,s,{...r,uri:t.href}))}export{l as t};
2
- //# sourceMappingURL=resources-BOHSQfAG.js.map
1
+ import{c as e,i as t,r as n}from"./mcp-response-XQamL4IZ.js";import{d as r,s as i}from"./teams-C1ndCpwK.js";import{ResourceTemplate as a}from"@modelcontextprotocol/sdk/server/mcp.js";const o={sort:`updated`,order:`desc`};async function s(e,i){let{teamName:a,uri:s}=i;try{let{data:i,error:c,response:l}=await e.GET(`/v1/teams/{team_name}/posts`,{params:{path:{team_name:a},query:o}});return c||!l.ok?n(c||l.status,s):t({...i,posts:i.posts?.map(e=>r(e,{truncateBody:500}))},s)}catch(e){return n(e,s)}}async function c(t){try{let n=await e(t,i,{});return JSON.parse(n.content[0].text).teams?.map(e=>({uri:`esa://teams/${e.name}/posts/recent`,name:`Recent posts from ${e.name}`,description:`Recent posts from ${e.name}${e.description?` (${e.description})`:``}`,mimeType:`application/json`}))||[]}catch(e){return console.error(`Failed to list teams:`,e),[]}}function l(t,n){console.error(`Setting up MCP resources...`),t.registerResource(`esa_recent_posts`,new a(`esa://teams/{teamName}/posts/recent`,{list:async()=>({resources:await c(n)})}),{title:`Recent Posts`,description:`Fetch recent updated posts from esa team`,mimeType:`application/json`},async(t,r)=>e(n,s,{...r,uri:t.href}))}export{l as t};
2
+ //# sourceMappingURL=resources-B_W925Tu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"resources-BOHSQfAG.js","names":[],"sources":["../src/resources/recent-posts.ts","../src/resources/recent-posts-list.ts","../src/resources/index.ts"],"sourcesContent":["import type { createEsaClient } from \"../api_client/index.js\";\nimport {\n formatResourceError,\n formatResourceResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { transformPost } from \"../transformers/post-transformer.js\";\n\nconst RECENT_POSTS_QUERY = {\n sort: \"updated\",\n order: \"desc\",\n} as const;\n\nexport async function getRecentPosts(\n client: ReturnType<typeof createEsaClient>,\n args: { teamName: string; uri: string },\n) {\n const { teamName, uri } = args;\n try {\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts\",\n {\n params: {\n path: { team_name: teamName },\n query: RECENT_POSTS_QUERY,\n },\n },\n );\n\n if (error || !response.ok) {\n return formatResourceError(error || response.status, uri);\n }\n\n const transformed = {\n ...data,\n posts: data.posts?.map((post: components[\"schemas\"][\"Post\"]) =>\n transformPost(post, { truncateBody: 500 }),\n ),\n };\n\n return formatResourceResponse(transformed, uri);\n } catch (error) {\n return formatResourceError(error, uri);\n }\n}\n","import type { TextContent } from \"@modelcontextprotocol/sdk/types.js\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\nimport { getTeams } from \"../tools/teams.js\";\n\nexport async function createRecentPostsResourceList(context: MCPContext) {\n try {\n const result = await withContext(context, getTeams, {});\n const data = JSON.parse((result.content[0] as TextContent).text);\n\n return (\n data.teams?.map((team: { name: string; description: string }) => ({\n uri: `esa://teams/${team.name}/posts/recent`,\n name: `Recent posts from ${team.name}`,\n description: `Recent posts from ${team.name}${team.description ? ` (${team.description})` : \"\"}`,\n mimeType: \"application/json\",\n })) || []\n );\n } catch (error) {\n console.error(\"Failed to list teams:\", error);\n return [];\n }\n}\n","import {\n type McpServer,\n ResourceTemplate,\n} from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\n\nimport { getRecentPosts } from \"./recent-posts.js\";\nimport { createRecentPostsResourceList } from \"./recent-posts-list.js\";\n\nexport function setupResources(server: McpServer, context: MCPContext): void {\n console.error(\"Setting up MCP resources...\");\n\n server.registerResource(\n \"esa_recent_posts\",\n new ResourceTemplate(\"esa://teams/{teamName}/posts/recent\", {\n list: async () => {\n const resources = await createRecentPostsResourceList(context);\n return { resources };\n },\n }),\n {\n title: \"Recent Posts\",\n description: \"Fetch recent updated posts from esa team\",\n mimeType: \"application/json\",\n },\n async (uri, params) =>\n withContext(context, getRecentPosts, { ...params, uri: uri.href } as {\n teamName: string;\n uri: string;\n }),\n );\n}\n"],"mappings":"uLAQA,MAAM,EAAqB,CACzB,KAAM,UACN,MAAO,OACR,CAED,eAAsB,EACpB,EACA,EACA,CACA,GAAM,CAAE,WAAU,OAAQ,EAC1B,GAAI,CACF,GAAM,CAAE,OAAM,QAAO,YAAa,MAAM,EAAO,IAC7C,8BACA,CACE,OAAQ,CACN,KAAM,CAAE,UAAW,EAAU,CAC7B,MAAO,EACR,CACF,CACF,CAaD,OAXI,GAAS,CAAC,EAAS,GACd,EAAoB,GAAS,EAAS,OAAQ,EAAI,CAUpD,EAPa,CAClB,GAAG,EACH,MAAO,EAAK,OAAO,IAAK,GACtB,EAAc,EAAM,CAAE,aAAc,IAAK,CAAC,CAC3C,CACF,CAE0C,EAAI,OACxC,EAAO,CACd,OAAO,EAAoB,EAAO,EAAI,ECrC1C,eAAsB,EAA8B,EAAqB,CACvE,GAAI,CACF,IAAM,EAAS,MAAM,EAAY,EAAS,EAAU,EAAE,CAAC,CAGvD,OAFa,KAAK,MAAO,EAAO,QAAQ,GAAmB,KAAK,CAGzD,OAAO,IAAK,IAAiD,CAChE,IAAK,eAAe,EAAK,KAAK,eAC9B,KAAM,qBAAqB,EAAK,OAChC,YAAa,qBAAqB,EAAK,OAAO,EAAK,YAAc,KAAK,EAAK,YAAY,GAAK,KAC5F,SAAU,mBACX,EAAE,EAAI,EAAE,OAEJ,EAAO,CAEd,OADA,QAAQ,MAAM,wBAAyB,EAAM,CACtC,EAAE,ECVb,SAAgB,EAAe,EAAmB,EAA2B,CAC3E,QAAQ,MAAM,8BAA8B,CAE5C,EAAO,iBACL,mBACA,IAAI,EAAiB,sCAAuC,CAC1D,KAAM,UAEG,CAAE,UADS,MAAM,EAA8B,EAAQ,CAC1C,EAEvB,CAAC,CACF,CACE,MAAO,eACP,YAAa,2CACb,SAAU,mBACX,CACD,MAAO,EAAK,IACV,EAAY,EAAS,EAAgB,CAAE,GAAG,EAAQ,IAAK,EAAI,KAAM,CAG/D,CACL"}
1
+ {"version":3,"file":"resources-B_W925Tu.js","names":[],"sources":["../src/resources/recent-posts.ts","../src/resources/recent-posts-list.ts","../src/resources/index.ts"],"sourcesContent":["import type { createEsaClient } from \"../api_client/index.js\";\nimport {\n formatResourceError,\n formatResourceResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { transformPost } from \"../transformers/post-transformer.js\";\n\nconst RECENT_POSTS_QUERY = {\n sort: \"updated\",\n order: \"desc\",\n} as const;\n\nexport async function getRecentPosts(\n client: ReturnType<typeof createEsaClient>,\n args: { teamName: string; uri: string },\n) {\n const { teamName, uri } = args;\n try {\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts\",\n {\n params: {\n path: { team_name: teamName },\n query: RECENT_POSTS_QUERY,\n },\n },\n );\n\n if (error || !response.ok) {\n return formatResourceError(error || response.status, uri);\n }\n\n const transformed = {\n ...data,\n posts: data.posts?.map((post: components[\"schemas\"][\"Post\"]) =>\n transformPost(post, { truncateBody: 500 }),\n ),\n };\n\n return formatResourceResponse(transformed, uri);\n } catch (error) {\n return formatResourceError(error, uri);\n }\n}\n","import type { TextContent } from \"@modelcontextprotocol/sdk/types.js\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\nimport { getTeams } from \"../tools/teams.js\";\n\nexport async function createRecentPostsResourceList(context: MCPContext) {\n try {\n const result = await withContext(context, getTeams, {});\n const data = JSON.parse((result.content[0] as TextContent).text);\n\n return (\n data.teams?.map((team: { name: string; description: string }) => ({\n uri: `esa://teams/${team.name}/posts/recent`,\n name: `Recent posts from ${team.name}`,\n description: `Recent posts from ${team.name}${team.description ? ` (${team.description})` : \"\"}`,\n mimeType: \"application/json\",\n })) || []\n );\n } catch (error) {\n console.error(\"Failed to list teams:\", error);\n return [];\n }\n}\n","import {\n type McpServer,\n ResourceTemplate,\n} from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\n\nimport { getRecentPosts } from \"./recent-posts.js\";\nimport { createRecentPostsResourceList } from \"./recent-posts-list.js\";\n\nexport function setupResources(server: McpServer, context: MCPContext): void {\n console.error(\"Setting up MCP resources...\");\n\n server.registerResource(\n \"esa_recent_posts\",\n new ResourceTemplate(\"esa://teams/{teamName}/posts/recent\", {\n list: async () => {\n const resources = await createRecentPostsResourceList(context);\n return { resources };\n },\n }),\n {\n title: \"Recent Posts\",\n description: \"Fetch recent updated posts from esa team\",\n mimeType: \"application/json\",\n },\n async (uri, params) =>\n withContext(context, getRecentPosts, { ...params, uri: uri.href } as {\n teamName: string;\n uri: string;\n }),\n );\n}\n"],"mappings":"uLAQA,MAAM,EAAqB,CACzB,KAAM,UACN,MAAO,OACR,CAED,eAAsB,EACpB,EACA,EACA,CACA,GAAM,CAAE,WAAU,OAAQ,EAC1B,GAAI,CACF,GAAM,CAAE,OAAM,QAAO,YAAa,MAAM,EAAO,IAC7C,8BACA,CACE,OAAQ,CACN,KAAM,CAAE,UAAW,EAAU,CAC7B,MAAO,EACR,CACF,CACF,CAaD,OAXI,GAAS,CAAC,EAAS,GACd,EAAoB,GAAS,EAAS,OAAQ,EAAI,CAUpD,EAPa,CAClB,GAAG,EACH,MAAO,EAAK,OAAO,IAAK,GACtB,EAAc,EAAM,CAAE,aAAc,IAAK,CAAC,CAC3C,CACF,CAE0C,EAAI,OACxC,EAAO,CACd,OAAO,EAAoB,EAAO,EAAI,ECrC1C,eAAsB,EAA8B,EAAqB,CACvE,GAAI,CACF,IAAM,EAAS,MAAM,EAAY,EAAS,EAAU,EAAE,CAAC,CAGvD,OAFa,KAAK,MAAO,EAAO,QAAQ,GAAmB,KAAK,CAGzD,OAAO,IAAK,IAAiD,CAChE,IAAK,eAAe,EAAK,KAAK,eAC9B,KAAM,qBAAqB,EAAK,OAChC,YAAa,qBAAqB,EAAK,OAAO,EAAK,YAAc,KAAK,EAAK,YAAY,GAAK,KAC5F,SAAU,mBACX,EAAE,EAAI,EAAE,OAEJ,EAAO,CAEd,OADA,QAAQ,MAAM,wBAAyB,EAAM,CACtC,EAAE,ECVb,SAAgB,EAAe,EAAmB,EAA2B,CAC3E,QAAQ,MAAM,8BAA8B,CAE5C,EAAO,iBACL,mBACA,IAAI,EAAiB,sCAAuC,CAC1D,KAAM,UAEG,CAAE,UADS,MAAM,EAA8B,EAAQ,CAC1C,EAEvB,CAAC,CACF,CACE,MAAO,eACP,YAAa,2CACb,SAAU,mBACX,CACD,MAAO,EAAK,IACV,EAAY,EAAS,EAAgB,CAAE,GAAG,EAAQ,IAAK,EAAI,KAAM,CAG/D,CACL"}
@@ -0,0 +1,2 @@
1
+ import{a as e,o as t,s as n}from"./mcp-response-XQamL4IZ.js";import{z as r}from"zod";function i(e,t){if(!t.omitBody)return t.truncateBody&&e&&e.length>t.truncateBody?`${e.slice(0,t.truncateBody)}\n\n... (truncated)`:e}function a(e,t={}){let n=i(e.body_md,t);return{url:e.url,wip:e.wip?`WIP`:`Shipped`,kind:e.kind,category_and_title_and_tags:e.full_name,...n!==void 0&&{body_md:n},created_at:e.created_at,updated_at:e.updated_at,created_by:e.created_by,updated_by:e.updated_by,stats:{tasks_count:e.tasks_count,done_tasks_count:e.done_tasks_count,comments_count:e.comments_count,stargazers_count:e.stargazers_count,watchers_count:e.watchers_count}}}function o(e){let t=e.indexOf(`.`);return t>=0?e.substring(0,t):e}const s=r.string().default(``).describe(`Team name (required). Use esa_get_teams first to see available teams.`).transform(o);function c(e){return r.object({teamName:s,...e})}const l=r.object({page:r.number().optional().describe(`Page number (starts from 1)`),perPage:r.number().optional().describe(`Number of items per page`),role:r.enum([`member`,`owner`]).optional().describe(`Filter by role`)});async function u(n,r={}){try{let{data:i,error:a,response:o}=await n.GET(`/v1/teams`,{params:{query:{page:r.page,per_page:r.perPage,role:r.role}}});return a||!o.ok?e(a||o.status):t({...i,teams:i.teams?.map(e=>({url:e.url,name:e.name,description:e.description}))})}catch(t){return e(t)}}const d=c({});async function f(r,i){try{if(!i.teamName)throw new n;let{data:a,error:o,response:s}=await r.GET(`/v1/teams/{team_name}/stats`,{params:{path:{team_name:i.teamName}}});return o||!s.ok?e(o||s.status):t(a)}catch(t){return e(t)}}const p=c({page:r.number().optional().describe(`Page number (starts from 1)`),perPage:r.number().optional().describe(`Number of items per page`)});async function m(r,i){try{if(!i.teamName)throw new n;let{data:a,error:o,response:s}=await r.GET(`/v1/teams/{team_name}/tags`,{params:{path:{team_name:i.teamName},query:{page:i.page,per_page:i.perPage}}});return o||!s.ok?e(o||s.status):t(a)}catch(t){return e(t)}}const h=c({page:r.number().optional().describe(`Page number (starts from 1)`),perPage:r.number().optional().describe(`Number of items per page`),sort:r.enum([`posts_count`,`joined`,`last_accessed`]).optional().describe(`Sort criteria`),order:r.enum([`desc`,`asc`]).optional().describe(`Sort order`)});async function g(r,i){try{if(!i.teamName)throw new n;let{data:a,error:o,response:s}=await r.GET(`/v1/teams/{team_name}/members`,{params:{path:{team_name:i.teamName},query:{page:i.page,per_page:i.perPage,sort:i.sort,order:i.order}}});return o||!s.ok?e(o||s.status):t(a)}catch(t){return e(t)}}export{m as a,l as c,a as d,i as f,d as i,c as l,h as n,p as o,f as r,u as s,g as t,o as u};
2
+ //# sourceMappingURL=teams-C1ndCpwK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams-C1ndCpwK.js","names":[],"sources":["../src/transformers/body-transformer.ts","../src/transformers/post-transformer.ts","../src/transformers/team-name-normalizer.ts","../src/schemas/team-name-schema.ts","../src/tools/teams.ts"],"sourcesContent":["export interface BodyTransformOptions {\n truncateBody?: number;\n omitBody?: boolean;\n}\n\nexport function processBodyMd(\n bodyMd: string | undefined,\n options: BodyTransformOptions,\n): string | undefined {\n if (options.omitBody) {\n return undefined;\n }\n if (options.truncateBody && bodyMd && bodyMd.length > options.truncateBody) {\n return `${bodyMd.slice(0, options.truncateBody)}\\n\\n... (truncated)`;\n }\n return bodyMd;\n}\n","import type { components } from \"../generated/api-types.js\";\nimport {\n type BodyTransformOptions,\n processBodyMd,\n} from \"./body-transformer.js\";\n\nexport type PostTransformOptions = BodyTransformOptions;\n\nexport function transformPost(\n post: components[\"schemas\"][\"Post\"],\n options: PostTransformOptions = {},\n) {\n const bodyMd = processBodyMd(post.body_md, options);\n\n return {\n url: post.url,\n wip: post.wip ? \"WIP\" : (\"Shipped\" as const),\n kind: post.kind,\n category_and_title_and_tags: post.full_name,\n ...(bodyMd !== undefined && { body_md: bodyMd }),\n created_at: post.created_at,\n updated_at: post.updated_at,\n created_by: post.created_by,\n updated_by: post.updated_by,\n stats: {\n tasks_count: post.tasks_count,\n done_tasks_count: post.done_tasks_count,\n comments_count: post.comments_count,\n stargazers_count: post.stargazers_count,\n watchers_count: post.watchers_count,\n },\n };\n}\n\nexport type TransformedPost = ReturnType<typeof transformPost>;\n","export function normalizeTeamName(teamName: string): string {\n const dotIndex = teamName.indexOf(\".\");\n if (dotIndex >= 0) {\n return teamName.substring(0, dotIndex);\n }\n return teamName;\n}\n","import { z } from \"zod\";\nimport { normalizeTeamName } from \"../transformers/team-name-normalizer.js\";\n\nexport const teamNameSchema = z\n .string()\n .default(\"\")\n .describe(\n \"Team name (required). Use esa_get_teams first to see available teams.\",\n )\n .transform(normalizeTeamName);\n\nexport function createSchemaWithTeamName<T extends z.ZodRawShape>(\n schema: T,\n): z.ZodObject<T & { teamName: typeof teamNameSchema }> {\n return z.object({\n teamName: teamNameSchema,\n ...schema,\n });\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\n\nexport const getTeamsSchema = z.object({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n role: z.enum([\"member\", \"owner\"]).optional().describe(\"Filter by role\"),\n});\n\nexport async function getTeams(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamsSchema> = {},\n) {\n try {\n const { data, error, response } = await client.GET(\"/v1/teams\", {\n params: {\n query: {\n page: args.page,\n per_page: args.perPage,\n role: args.role,\n },\n },\n });\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const transformed = {\n ...data,\n teams: data.teams?.map((team: components[\"schemas\"][\"Team\"]) => ({\n url: team.url,\n name: team.name,\n description: team.description,\n })),\n };\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamStatsSchema = createSchemaWithTeamName({});\n\nexport async function getTeamStats(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamStatsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n `/v1/teams/{team_name}/stats`,\n {\n params: {\n path: { team_name: args.teamName },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamTagsSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getTeamTags(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamTagsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n `/v1/teams/{team_name}/tags`,\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamMembersSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n sort: z\n .enum([\"posts_count\", \"joined\", \"last_accessed\"])\n .optional()\n .describe(\"Sort criteria\"),\n order: z.enum([\"desc\", \"asc\"]).optional().describe(\"Sort order\"),\n});\n\nexport async function getTeamMembers(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamMembersSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n `/v1/teams/{team_name}/members`,\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n page: args.page,\n per_page: args.perPage,\n sort: args.sort,\n order: args.order,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- import"../mcp-response-Cn2WFDAj.js";import"../teams-B9y1URzV.js";import{t as e}from"../tools-6rPxMwBY.js";export{e as setupTools};
1
+ import{t as e}from"../tools-B1-4McPh.js";export{e as setupTools};
@@ -0,0 +1,29 @@
1
+ import{a as e,c as t,o as n,s as r}from"./mcp-response-XQamL4IZ.js";import{a as i,c as a,d as o,f as s,i as c,l,n as u,o as d,r as ee,s as f,t as p,u as m}from"./teams-C1ndCpwK.js";import{z as h}from"zod";const g=[`image/jpeg`,`image/png`,`image/gif`,`image/webp`],_=l({url:h.string().describe(`Attachment URL. Can be a full URL (https://files.esa.io/..., https://dl.esa.io/...) or a path (/uploads/...)`),forceSignedUrl:h.boolean().optional().describe(`If true, always return signed URLs instead of base64-encoded images. Default is false.`)}),v=[`files.esa.io`,`dl.esa.io`];function y(e){if(e.startsWith(`/`))return!0;try{let t=new URL(e);return v.includes(t.hostname)}catch{return!0}}function te(e){if(e.startsWith(`/`))return e;try{return new URL(e).pathname}catch{return e}}function ne(e){return g.includes(e)}async function b(e,t){if(t)return{type:`text`,text:e};let n=await fetch(e);if(!n.ok)throw Error(`Failed to fetch attachment: ${n.status} ${n.statusText}`);let r=n.headers.get(`content-type`)||``,i=n.headers.get(`content-length`),a=i?Number.parseInt(i,10):0;if(ne(r)&&a>0&&a<=31457280){let e=await n.arrayBuffer();return{type:`image`,data:Buffer.from(e).toString(`base64`),mimeType:r}}return{type:`text`,text:e}}async function x(t,n){try{if(!n.teamName)throw new r;let i=n.forceSignedUrl??!1;if(y(n.url)){let r=te(n.url),{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/signed_urls`,{params:{path:{team_name:n.teamName},query:{urls:r,v:2,expires_in:300}}});if(o||!s.ok)return e(o||s.status);if(!a.signed_urls||a.signed_urls.length===0)throw Error(`No signed URLs returned from API`);let[c,l]=a.signed_urls[0];if(l===null)throw Error(`File not found: ${c}`);try{return{content:[await b(l,i)]}}catch(e){throw Error(`Failed to fetch attachment for ${c}: ${e instanceof Error?e.message:String(e)}`)}}try{return{content:[await b(n.url,i)]}}catch(e){throw Error(`Failed to fetch attachment for ${n.url}: ${e instanceof Error?e.message:String(e)}`)}}catch(t){return e(t)}}function S(e){return{full_name:e.full_name,count:e.count,has_child:e.has_child||!1}}function C(e){return{current_category:e.current_category,categories:e.categories?.map(S)||[],parent_categories:e.parent_categories?.map(e=>({current_category:e.current_category,categories:e.categories?.map(S)||[]})),readme:e.readme?o(e.readme,{truncateBody:500}):void 0,no_category:e.no_category?S(e.no_category):void 0,descendant_posts:e.descendant_posts,posts:e.posts?.map(e=>o(e,{truncateBody:500})),total_count:e.total_count,per_page:e.per_page,page:e.page,prev_page:e.prev_page,next_page:e.next_page,max_per_page:e.max_per_page}}const w=l({select:h.string().describe(`Category path to retrieve`),include:h.enum([`posts`,`parent_categories`]).optional().describe(`Additional information to include`),descendantPosts:h.boolean().optional().describe(`Include descendant posts (only effective with include=posts)`),page:h.number().optional().describe(`Page number (starts from 1)`),perPage:h.number().optional().describe(`Number of items per page`)});async function T(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/categories`,{params:{path:{team_name:i.teamName},query:{select:i.select,include:i.include,descendant_posts:i.descendantPosts,page:i.page,per_page:i.perPage}}});return o||!s.ok?e(o||s.status):n(C(a))}catch(t){return e(t)}}const E=l({});async function D(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/categories/top`,{params:{path:{team_name:i.teamName}}});return o||!s.ok?e(o||s.status):n(C(a))}catch(t){return e(t)}}const O=l({page:h.number().optional().describe(`Page number (starts from 1)`),perPage:h.number().optional().describe(`Number of items per page`),prefix:h.string().optional().describe(`Filter paths starting with specified string (e.g., 'dev' finds 'dev', 'dev/api', 'dev/docs')`),suffix:h.string().optional().describe(`Filter paths ending with specified string (e.g., 'api' finds 'dev/api', 'backend/api')`),match:h.string().optional().describe(`Filter paths containing specified substring anywhere (e.g., 'doc' finds 'docs', 'dev/docs', 'documentation')`),exactMatch:h.string().optional().describe(`Filter paths matching exactly (e.g., 'dev/api' matches only 'dev/api', ignores leading/trailing slashes)`)});async function k(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/categories/paths`,{params:{path:{team_name:i.teamName},query:{v:2,page:i.page,per_page:i.perPage,prefix:i.prefix,suffix:i.suffix,match:i.match,exact_match:i.exactMatch}}});return o||!s.ok?e(o||s.status):n(a)}catch(t){return e(t)}}function A(e,t={}){let n=s(e.body_md,t);return{id:e.id,post_number:e.post_number,url:e.url,...n!==void 0&&{body_md:n},created_at:e.created_at,updated_at:e.updated_at,created_by:e.created_by,stats:{stargazers_count:e.stargazers_count,star:e.star},stargazers:e.stargazers}}const j=l({commentId:h.number().describe(`The comment ID to retrieve`),include:h.enum([`stargazers`]).optional().describe(`Specify 'stargazers' to include stargazers in the response`)});async function M(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/comments/{comment_id}`,{params:{path:{team_name:i.teamName,comment_id:i.commentId},query:{include:i.include}}});return o||!s.ok?e(o||s.status):n(A(a,{truncateBody:1e4}))}catch(t){return e(t)}}const N=l({postNumber:h.number().describe(`The post number to comment on`),bodyMd:h.string().describe(`The comment content in Markdown format`),user:h.string().optional().describe(`Comment author's screen_name (owner permission required)`)});async function P(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.POST(`/v1/teams/{team_name}/posts/{post_number}/comments`,{params:{path:{team_name:i.teamName,post_number:i.postNumber}},body:{comment:{body_md:i.bodyMd,user:i.user}}});return o||!s.ok?e(o||s.status):n(A(a,{omitBody:!0}))}catch(t){return e(t)}}const F=l({commentId:h.number().describe(`The comment ID to update`),bodyMd:h.string().describe(`The updated comment content in Markdown format`),user:h.string().optional().describe(`Comment author's screen_name (owner permission required)`)});async function I(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.PATCH(`/v1/teams/{team_name}/comments/{comment_id}`,{params:{path:{team_name:i.teamName,comment_id:i.commentId}},body:{comment:{body_md:i.bodyMd,user:i.user}}});return o||!s.ok?e(o||s.status):n(A(a,{omitBody:!0}))}catch(t){return e(t)}}const L=l({commentId:h.number().describe(`The comment ID to delete`)});async function R(t,i){try{if(!i.teamName)throw new r;let{error:a,response:o}=await t.DELETE(`/v1/teams/{team_name}/comments/{comment_id}`,{params:{path:{team_name:i.teamName,comment_id:i.commentId}}});return a||!o.ok?e(a||o.status):n({success:!0,message:`Comment deleted successfully`})}catch(t){return e(t)}}const z=l({postNumber:h.number().describe(`The post number to get comments for`),page:h.number().optional().describe(`Page number (starts from 1)`),perPage:h.number().optional().describe(`Number of items per page`)});async function B(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/posts/{post_number}/comments`,{params:{path:{team_name:i.teamName,post_number:i.postNumber},query:{page:i.page,per_page:i.perPage}}});if(o||!s.ok)return e(o||s.status);let c=a.comments.map(e=>A(e,{truncateBody:300}));return n({...a,comments:c})}catch(t){return e(t)}}const re=l({page:h.number().optional().describe(`Page number (starts from 1)`),perPage:h.number().optional().describe(`Number of items per page`)});async function V(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/comments`,{params:{path:{team_name:i.teamName},query:{page:i.page,per_page:i.perPage}}});if(o||!s.ok)return e(o||s.status);let c=a.comments.map(e=>A(e,{truncateBody:300}));return n({...a,comments:c})}catch(t){return e(t)}}function H(e,t){if(!e||t!==void 0)return{name:e,category:t};if(e.includes(`/`)){let t=e.split(`/`),n=t.pop(),r=t.join(`/`);return{name:n||void 0,category:r}}return{name:e,category:t}}const U=l({postNumber:h.number().describe(`The post number to retrieve`),include:h.enum([`comments`]).optional().describe(`Specify 'comments' to include comments in the response`)});async function W(t,i){try{if(!i.teamName)throw new r;let{data:a,error:s,response:c}=await t.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:i.teamName,post_number:i.postNumber},query:{include:i.include}}});return s||!c.ok?e(s||c.status):n(o(a,{truncateBody:1e4}))}catch(t){return e(t)}}const G=l({name:h.string().describe(`The post name (title)`),bodyMd:h.string().optional().describe(`The post content in Markdown format`),tags:h.array(h.string()).optional().describe(`Tags for the post`),category:h.string().optional().describe(`Category path (e.g., 'dev/docs')`),wip:h.boolean().default(!0).describe(`Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)`),message:h.string().optional().describe(`Update message for the post`)});async function K(t,i){try{if(!i.teamName)throw new r;let{name:a,category:s}=H(i.name,i.category),{data:c,error:l,response:u}=await t.POST(`/v1/teams/{team_name}/posts`,{params:{path:{team_name:i.teamName}},body:{post:{name:a,body_md:i.bodyMd,tags:i.tags,category:s,wip:i.wip,message:i.message}}});return l||!u.ok?e(l||u.status):n(o(c,{omitBody:!0}))}catch(t){return e(t)}}const q=l({postNumber:h.number().describe(`The post number to update`),name:h.string().optional().describe(`The post name (title)`),bodyMd:h.string().optional().describe(`The post content in Markdown format`),tags:h.array(h.string()).optional().describe(`Tags for the post`),category:h.string().optional().describe(`Category path (e.g., 'dev/docs')`),wip:h.boolean().optional().describe(`Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)`),message:h.string().optional().describe(`Update message for the post`),originalRevision:h.object({bodyMd:h.string(),number:h.number(),user:h.string()}).optional().describe(`Original revision to check for conflicts`)});async function J(t,i){try{if(!i.teamName)throw new r;let{name:a,category:s}=H(i.name,i.category),{data:c,error:l,response:u}=await t.PATCH(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:i.teamName,post_number:i.postNumber}},body:{post:{name:a,body_md:i.bodyMd,tags:i.tags,category:s,wip:i.wip,message:i.message,original_revision:i.originalRevision?{body_md:i.originalRevision.bodyMd,number:i.originalRevision.number,user:i.originalRevision.user}:void 0}}});return l||!u.ok?e(l||u.status):n(o(c,{omitBody:!0}))}catch(t){return e(t)}}function Y(e){let t=e;return t===`*`&&(t=``),t=t.replace(/\bafter:(\d{4}-\d{2}-\d{2})\b/gi,`created:>$1`),t=t.replace(/\bbefore:(\d{4}-\d{2}-\d{2})\b/gi,`created:<$1`),t=t.replace(/\bsince:(\d{4}-\d{2}-\d{2})\b/gi,`created:>$1`),t=t.replace(/\buntil:(\d{4}-\d{2}-\d{2})\b/gi,`created:<$1`),t}const X=l({query:h.string().describe(`Search query string. Use specific terms, not wildcards like "*". Empty string returns all posts.
2
+ ## Important Note for Date Queries:
3
+ **WARNING: Do NOT use 'after:', 'before:', 'since:', or 'until:' syntax (these are from GitHub/Gmail/pplog).
4
+ Use esa-specific date syntax: created:>YYYY-MM-DD, created:<YYYY-MM-DD, updated:>YYYY-MM-DD, updated:<YYYY-MM-DD
5
+
6
+ ## Important Note for Relative Date Queries:
7
+ **CRITICAL: Always get today's actual date from the system before processing
8
+ relative date queries (e.g., "today", "yesterday", "last week", "recent").
9
+ When searching, apply these strategies:
10
+ 1. Convert concepts to technical terms (e.g., general descriptions → specific property names, method names, or technical keywords)
11
+ 2. Translate between Japanese and English technical terms (e.g., Japanese concepts → English API/property names)
12
+ 3. Expand to related technical elements (e.g., one concept → multiple implementation approaches, related technologies, or alternative solutions)
13
+ IMPORTANT: Space-separated terms are treated as AND conditions. Use "OR" operator for alternative terms: "word-break OR word-wrap OR overflow-wrap".
14
+ Advanced search: "tag:release", "category:dev", "wip:false", "keyword:API", "title:設計書".
15
+ Category search: "on:category" (posts directly in category), "in:category" (posts in category and subcategories), "on:/" (uncategorized posts).
16
+ For broader results, use OR between related terms rather than listing them with spaces.`).transform(Y),sort:h.enum([`updated`,`created`,`number`,`stars`,`watches`,`comments`,`best_match`]).optional().describe(`Sort key`),order:h.enum([`desc`,`asc`]).optional().describe(`Sort direction`),page:h.number().int().positive().optional().describe(`Page number`),perPage:h.number().int().min(1).max(100).optional().describe(`Items per page`),include:h.enum([`comments`]).optional().describe(`Specify 'comments' to include comments in the response`)});async function Z(t,i){try{if(!i.teamName)throw new r;let{data:a,error:s,response:c}=await t.GET(`/v1/teams/{team_name}/posts`,{params:{path:{team_name:i.teamName},query:{q:i.query,sort:i.sort,order:i.order,page:i.page,per_page:i.perPage,include:i.include}}});if(s||!c.ok)return e(s||c.status);let l=a.posts,u=l.map(e=>o(e,{truncateBody:500}));return l.length===0&&ie(i.query)?{content:[{type:`text`,text:ae(i.query)}]}:n(u)}catch(t){return e(t)}}function ie(e){return!e||e.trim()===``||/\bOR\b/.test(e)||e.includes(`|`)?!1:e.split(/\s+/).filter(Boolean).length>=2}function ae(e){return`---
17
+ No results found. Your query uses AND conditions (space-separated terms).
18
+ Suggestions:
19
+ - Try OR search: "${e.split(/\s+/).filter(Boolean).join(` OR `)}"
20
+ - Omit some keywords from your query`}const Q={TEAM:`docs`,SEARCH_OPTIONS_POST_ID:104,MARKDOWN_SYNTAX_POST_ID:49},oe=X.omit({teamName:!0,order:!0,include:!0,sort:!0});async function $(e,t){return W(e,{teamName:Q.TEAM,postNumber:Q.SEARCH_OPTIONS_POST_ID})}async function se(e,t){return W(e,{teamName:Q.TEAM,postNumber:Q.MARKDOWN_SYNTAX_POST_ID})}async function ce(e,t){return Z(e,{teamName:Q.TEAM,sort:`best_match`,...t})}const le=l({postNumber:h.number().describe(`The post number to archive`),message:h.string().optional().describe(`Archive message for the post`)});async function ue(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:i.teamName,post_number:i.postNumber}}});if(o||!s.ok)return e(o||s.status);let c=a.category||``;if(c.startsWith(`Archived/`))return n({message:`Post is already archived`,category:c});let l=c===``?`Archived`:`Archived/${c}`;return await J(t,{teamName:i.teamName,postNumber:i.postNumber,category:l,message:i.message||`Archive post`})}catch(t){return e(t)}}const de=l({postNumber:h.number().describe(`The post number to ship`)});async function fe(t,n){try{if(!n.teamName)throw new r;return await J(t,{teamName:n.teamName,postNumber:n.postNumber,wip:!1,message:`Ship It!`})}catch(t){return e(t)}}const pe=l({postNumber:h.number().describe(`The source post number to prepare for duplication`),targetTeamName:h.string().optional().describe(`The name of the esa team`).transform(e=>e?m(e):void 0)});async function me(t,n){try{if(!n.teamName)throw new r;let{data:i,error:a,response:o}=await t.GET(`/v1/teams/{team_name}/posts/new`,{params:{path:{team_name:n.teamName},query:{parent_post_id:n.postNumber}}});if(a||!o.ok)return e(a||o.status);let s=i.post;return K(t,{teamName:n.targetTeamName||n.teamName,name:s.name,bodyMd:s.body_md,wip:!0})}catch(t){return e(t)}}function he(e,n){console.error(`Setting up MCP tools...`),e.registerTool(`esa_get_teams`,{title:`Get user's accessible esa teams`,description:`Retrieves a list of esa teams that the user has access to.`,inputSchema:a.shape,annotations:{readOnlyHint:!0}},async e=>t(n,f,e)),e.registerTool(`esa_get_team_stats`,{title:`Get team statistics`,description:`Retrieves team statistics including member count, posts count (total/WIP/shipped), comments, stars, watches, and daily/weekly/monthly active users`,inputSchema:c.shape,annotations:{readOnlyHint:!0}},async e=>t(n,ee,e)),e.registerTool(`esa_get_team_tags`,{title:`Get team tags`,description:`Retrieves all tags used in posts within a team, along with the count of posts for each tag`,inputSchema:d.shape,annotations:{readOnlyHint:!0}},async e=>t(n,i,e)),e.registerTool(`esa_get_team_members`,{title:`Get team members`,description:`Retrieves all members of a team with their roles and profile information`,inputSchema:u.shape,annotations:{readOnlyHint:!0}},async e=>t(n,p,e)),e.registerTool(`esa_get_post`,{title:`Get a specific esa post`,description:`Retrieves a specific post from an esa team by post number, with optional comments included.`,inputSchema:U.shape,annotations:{readOnlyHint:!0}},async e=>t(n,W,e)),e.registerTool(`esa_search_posts`,{title:`Search Posts`,description:`Search for posts in esa.io`,inputSchema:X.shape,annotations:{readOnlyHint:!0}},async e=>t(n,Z,e)),e.registerTool(`esa_create_post`,{title:`Create a new esa post`,description:`Creates a new post in an esa team with optional tags, category, and WIP status.`,inputSchema:G.shape,annotations:{destructiveHint:!0}},async e=>t(n,K,e)),e.registerTool(`esa_update_post`,{title:`Update an existing esa post`,description:`Updates an existing post in an esa team by post number. You can update the title, content, tags, category, and WIP status. To ship a post (mark as complete), set wip to false - this is preferred over using esa_ship_post when updating other fields simultaneously.`,inputSchema:q.shape,annotations:{destructiveHint:!0}},async e=>t(n,J,e)),e.registerTool(`esa_get_comment`,{title:`Get a specific comment`,description:`Retrieves a specific comment by comment ID, with optional stargazers included.`,inputSchema:j.shape,annotations:{readOnlyHint:!0}},async e=>t(n,M,e)),e.registerTool(`esa_create_comment`,{title:`Create a new comment on a post`,description:`Creates a new comment on an existing post in an esa team.`,inputSchema:N.shape,annotations:{destructiveHint:!0}},async e=>t(n,P,e)),e.registerTool(`esa_update_comment`,{title:`Update an existing comment`,description:`Updates an existing comment in an esa team by comment ID.`,inputSchema:F.shape,annotations:{destructiveHint:!0}},async e=>t(n,I,e)),e.registerTool(`esa_delete_comment`,{title:`Delete a comment`,description:`Deletes a comment from an esa team by comment ID.`,inputSchema:L.shape,annotations:{destructiveHint:!0}},async e=>t(n,R,e)),e.registerTool(`esa_get_post_comments`,{title:`Get comments for a specific post`,description:`Retrieves a list of comments for a specific post with pagination support.`,inputSchema:z.shape,annotations:{readOnlyHint:!0}},async e=>t(n,B,e)),e.registerTool(`esa_get_team_comments`,{title:`Get team comments`,description:`Retrieves a list of comments in a team with pagination support.`,inputSchema:re.shape,annotations:{readOnlyHint:!0}},async e=>t(n,V,e)),e.registerTool(`esa_get_categories`,{title:`Get categories for a specific path`,description:`Retrieves category information and subcategories for a specific category path, with optional posts and parent categories included`,inputSchema:w.shape,annotations:{readOnlyHint:!0}},async e=>t(n,T,e)),e.registerTool(`esa_get_top_categories`,{title:`Get top-level categories`,description:`Retrieves all top-level categories for a team`,inputSchema:E.shape,annotations:{readOnlyHint:!0}},async e=>t(n,D,e)),e.registerTool(`esa_get_all_category_paths`,{title:`Get category paths with pagination`,description:`Retrieves category paths in a team to understand the overall category structure. Perfect for category organization, cleanup, migration planning, or finding similar categories. Returns a paginated list of paths with post counts, sorted in lexicographic order. Supports filtering (prefix/suffix/match/exact_match) to find categories by pattern.`,inputSchema:O.shape,annotations:{readOnlyHint:!0}},async e=>t(n,k,e)),e.registerTool(`esa_archive_post`,{title:`Archive a post`,description:`Archives a post by moving it to the Archived/ category. If the post is in 'dev/docs', it becomes 'Archived/dev/docs'. Posts without category go to 'Archived'.`,inputSchema:le.shape,annotations:{destructiveHint:!0}},async e=>t(n,ue,e)),e.registerTool(`esa_ship_post`,{title:`Ship a post`,description:`Ships a post by setting wip to false. This marks the post as complete and ready to be published. Use this only when you need to ship without making other changes - if you're also updating title, content, or other fields, use esa_update_post with wip: false instead.`,inputSchema:de.shape,annotations:{destructiveHint:!0}},async e=>t(n,fe,e)),e.registerTool(`esa_duplicate_post`,{title:`Prepare a post for duplication`,description:`Prepares a post for duplication by retrieving its name and body_md content. Returns the name and body_md that can be used with esa_create_post to create a duplicate of the original post.`,inputSchema:pe.shape,annotations:{destructiveHint:!0}},async e=>t(n,me,e)),e.registerTool(`esa_get_search_options_help`,{title:`Get esa search options documentation`,description:`Get esa search syntax documentation when you need to construct complex
21
+ search queries. Use this BEFORE esa_search_posts if you're unsure how to
22
+ translate user's search requirements into proper esa query syntax (e.g., date
23
+ ranges, tag filters, category searches, advanced operators).`,inputSchema:{},annotations:{readOnlyHint:!0}},async e=>t(n,$,e)),e.registerTool(`esa_get_markdown_syntax_help`,{title:`Get esa Markdown syntax documentation`,description:`Get esa Markdown and formatting documentation when unsure about syntax.
24
+ Use this BEFORE using any tools with *_md parameters (like esa_create_post,
25
+ esa_update_post, esa_create_comment, esa_update_comment) if you need
26
+ clarification on Markdown syntax, esa-specific extensions, or formatting options.`,inputSchema:{},annotations:{readOnlyHint:!0}},async e=>t(n,se,e)),e.registerTool(`esa_search_help`,{title:`Search esa documentation and help`,description:`Search esa documentation for features, terminology, and specifications.
27
+ Use this when users mention esa-specific terms, ask about esa functionality,
28
+ or request help with esa workflows that you're not familiar with.`,inputSchema:oe.shape,annotations:{readOnlyHint:!0}},async e=>t(n,ce,e)),e.registerTool(`esa_get_attachment`,{title:`Get attachment file from esa`,description:`Retrieves an attachment file from esa with signed URLs. For supported images (JPEG, PNG, GIF, WebP) under 30MB, returns base64-encoded data. For other file types, larger images, or when forceSignedUrl is true, returns signed URLs.`,inputSchema:_.shape,annotations:{readOnlyHint:!0}},async e=>t(n,x,e))}export{he as t};
29
+ //# sourceMappingURL=tools-B1-4McPh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-B1-4McPh.js","names":[],"sources":["../src/tools/attachments.ts","../src/transformers/category-transformer.ts","../src/tools/categories.ts","../src/transformers/comment-transformer.ts","../src/tools/comments.ts","../src/transformers/post-name-normalizer.ts","../src/tools/posts.ts","../src/transformers/query-normalizer.ts","../src/tools/search.ts","../src/tools/helps.ts","../src/tools/post-actions.ts","../src/tools/index.ts"],"sourcesContent":["import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport { formatToolError } from \"../formatters/mcp-response.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\n\n// Maximum file size for base64 encoding (30MB)\nconst MAX_IMAGE_SIZE = 30 * 1024 * 1024;\n\n// Supported image MIME types for base64 encoding\nconst SUPPORTED_IMAGE_TYPES = [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n] as const;\n\nexport const getAttachmentSchema = createSchemaWithTeamName({\n url: z\n .string()\n .describe(\n \"Attachment URL. Can be a full URL (https://files.esa.io/..., https://dl.esa.io/...) or a path (/uploads/...)\",\n ),\n forceSignedUrl: z\n .boolean()\n .optional()\n .describe(\n \"If true, always return signed URLs instead of base64-encoded images. Default is false.\",\n ),\n});\n\n// Hosts that require signed URLs via the esa API\nconst SIGNED_URL_HOSTS = [\"files.esa.io\", \"dl.esa.io\"];\n\n/**\n * Checks if the URL needs a signed URL or can be fetched directly.\n * URLs from img.esa.io are publicly accessible and don't need signing.\n * Paths (e.g. /uploads/...) and URLs from files.esa.io/dl.esa.io need signing.\n */\nfunction needsSignedUrl(url: string): boolean {\n // Paths always need signed URLs\n if (url.startsWith(\"/\")) {\n return true;\n }\n\n try {\n const urlObj = new URL(url);\n return SIGNED_URL_HOSTS.includes(urlObj.hostname);\n } catch {\n // If URL parsing fails, assume it needs signing\n return true;\n }\n}\n\n/**\n * Extracts the path from a full URL or returns the input if it's already a path\n */\nfunction normalizeUrl(url: string): string {\n // If it's already a path (starts with /), return as-is\n if (url.startsWith(\"/\")) {\n return url;\n }\n\n try {\n // Try to parse as URL and extract pathname\n const urlObj = new URL(url);\n return urlObj.pathname;\n } catch {\n // If URL parsing fails, assume it's already a path\n return url;\n }\n}\n\n/**\n * Checks if the MIME type is a supported image format\n */\nfunction isSupportedImage(mimeType: string): boolean {\n return SUPPORTED_IMAGE_TYPES.includes(\n mimeType as (typeof SUPPORTED_IMAGE_TYPES)[number],\n );\n}\n\n/**\n * Fetches content and returns base64-encoded data if it's a supported image under size limit\n */\nasync function fetchAttachment(\n signedUrl: string,\n forceSignedUrl: boolean,\n): Promise<\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"text\"; text: string }\n> {\n // If forceSignedUrl is true, always return signed URL\n if (forceSignedUrl) {\n return {\n type: \"text\" as const,\n text: signedUrl,\n };\n }\n\n const response = await fetch(signedUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch attachment: ${response.status} ${response.statusText}`,\n );\n }\n\n const contentType = response.headers.get(\"content-type\") || \"\";\n const contentLength = response.headers.get(\"content-length\");\n const size = contentLength ? Number.parseInt(contentLength, 10) : 0;\n\n // Check if it's a supported image and within size limit\n if (isSupportedImage(contentType) && size > 0 && size <= MAX_IMAGE_SIZE) {\n const arrayBuffer = await response.arrayBuffer();\n const buffer = Buffer.from(arrayBuffer);\n const base64 = buffer.toString(\"base64\");\n\n return {\n type: \"image\" as const,\n data: base64,\n mimeType: contentType,\n };\n }\n\n // For non-images or oversized images, return the signed URL\n return {\n type: \"text\" as const,\n text: signedUrl,\n };\n}\n\nexport async function getAttachment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getAttachmentSchema>,\n): Promise<CallToolResult> {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n\n const forceSignedUrl = args.forceSignedUrl ?? false;\n\n if (needsSignedUrl(args.url)) {\n // Normalize URL to path for signed URL API\n const normalizedUrl = normalizeUrl(args.url);\n\n // Get signed URL from esa API\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/signed_urls\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n urls: normalizedUrl,\n v: 2,\n expires_in: 300, // 5 minutes\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n if (!data.signed_urls || data.signed_urls.length === 0) {\n throw new Error(\"No signed URLs returned from API\");\n }\n\n const [originalUrl, signedUrl] = data.signed_urls[0];\n\n if (signedUrl === null) {\n throw new Error(`File not found: ${originalUrl}`);\n }\n\n try {\n const result = await fetchAttachment(signedUrl, forceSignedUrl);\n return { content: [result] };\n } catch (err) {\n throw new Error(\n `Failed to fetch attachment for ${originalUrl}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n // URL doesn't need signing (e.g. img.esa.io) - fetch directly\n try {\n const result = await fetchAttachment(args.url, forceSignedUrl);\n return { content: [result] };\n } catch (err) {\n throw new Error(\n `Failed to fetch attachment for ${args.url}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n } catch (err) {\n return formatToolError(err);\n }\n}\n","import type { components } from \"../generated/api-types.js\";\nimport { transformPost } from \"./post-transformer.js\";\n\nexport function transformCategory(category: components[\"schemas\"][\"Category\"]) {\n return {\n full_name: category.full_name,\n count: category.count,\n has_child: category.has_child || false,\n };\n}\n\nexport function transformCategoryList(\n categoryList: components[\"schemas\"][\"CategoryList\"],\n) {\n return {\n current_category: categoryList.current_category,\n categories: categoryList.categories?.map(transformCategory) || [],\n parent_categories: categoryList.parent_categories?.map(\n (parentCategory) => ({\n current_category: parentCategory.current_category,\n categories: parentCategory.categories?.map(transformCategory) || [],\n }),\n ),\n readme: categoryList.readme\n ? transformPost(categoryList.readme, { truncateBody: 500 })\n : undefined,\n no_category: categoryList.no_category\n ? transformCategory(categoryList.no_category)\n : undefined,\n descendant_posts: categoryList.descendant_posts,\n posts: categoryList.posts?.map((post) =>\n transformPost(post, { truncateBody: 500 }),\n ),\n total_count: categoryList.total_count,\n per_page: categoryList.per_page,\n page: categoryList.page,\n prev_page: categoryList.prev_page,\n next_page: categoryList.next_page,\n max_per_page: categoryList.max_per_page,\n };\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { transformCategoryList } from \"../transformers/category-transformer.js\";\n\nexport const getCategoriesSchema = createSchemaWithTeamName({\n select: z.string().describe(\"Category path to retrieve\"),\n include: z\n .enum([\"posts\", \"parent_categories\"])\n .optional()\n .describe(\"Additional information to include\"),\n descendantPosts: z\n .boolean()\n .optional()\n .describe(\"Include descendant posts (only effective with include=posts)\"),\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getCategories(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getCategoriesSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/categories\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n select: args.select,\n include: args.include,\n descendant_posts: args.descendantPosts,\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const categoryList: components[\"schemas\"][\"CategoryList\"] = data;\n const transformed = transformCategoryList(categoryList);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTopCategoriesSchema = createSchemaWithTeamName({});\n\nexport async function getTopCategories(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTopCategoriesSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/categories/top\",\n {\n params: {\n path: { team_name: args.teamName },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const categoryList: components[\"schemas\"][\"CategoryList\"] = data;\n const transformed = transformCategoryList(categoryList);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getAllCategoryPathsSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n prefix: z\n .string()\n .optional()\n .describe(\n \"Filter paths starting with specified string (e.g., 'dev' finds 'dev', 'dev/api', 'dev/docs')\",\n ),\n suffix: z\n .string()\n .optional()\n .describe(\n \"Filter paths ending with specified string (e.g., 'api' finds 'dev/api', 'backend/api')\",\n ),\n match: z\n .string()\n .optional()\n .describe(\n \"Filter paths containing specified substring anywhere (e.g., 'doc' finds 'docs', 'dev/docs', 'documentation')\",\n ),\n exactMatch: z\n .string()\n .optional()\n .describe(\n \"Filter paths matching exactly (e.g., 'dev/api' matches only 'dev/api', ignores leading/trailing slashes)\",\n ),\n});\n\nexport async function getAllCategoryPaths(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getAllCategoryPathsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/categories/paths\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n v: 2,\n page: args.page,\n per_page: args.perPage,\n prefix: args.prefix,\n suffix: args.suffix,\n match: args.match,\n exact_match: args.exactMatch,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n","import type { components } from \"../generated/api-types.js\";\nimport {\n type BodyTransformOptions,\n processBodyMd,\n} from \"./body-transformer.js\";\n\nexport type CommentTransformOptions = BodyTransformOptions;\n\nexport function transformComment(\n comment: components[\"schemas\"][\"Comment\"],\n options: CommentTransformOptions = {},\n) {\n const bodyMd = processBodyMd(comment.body_md, options);\n\n return {\n id: comment.id,\n post_number: comment.post_number,\n url: comment.url,\n ...(bodyMd !== undefined && { body_md: bodyMd }),\n created_at: comment.created_at,\n updated_at: comment.updated_at,\n created_by: comment.created_by,\n stats: {\n stargazers_count: comment.stargazers_count,\n star: comment.star,\n },\n stargazers: comment.stargazers,\n };\n}\n\nexport type TransformedComment = ReturnType<typeof transformComment>;\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { transformComment } from \"../transformers/comment-transformer.js\";\n\nexport const getCommentSchema = createSchemaWithTeamName({\n commentId: z.number().describe(\"The comment ID to retrieve\"),\n include: z\n .enum([\"stargazers\"])\n .optional()\n .describe(\"Specify 'stargazers' to include stargazers in the response\"),\n});\n\nexport async function getComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/comments/{comment_id}\",\n {\n params: {\n path: { team_name: args.teamName, comment_id: args.commentId },\n query: {\n include: args.include,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comment: components[\"schemas\"][\"Comment\"] = data;\n const transformed = transformComment(comment, { truncateBody: 10000 });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const createCommentSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to comment on\"),\n bodyMd: z.string().describe(\"The comment content in Markdown format\"),\n user: z\n .string()\n .optional()\n .describe(\"Comment author's screen_name (owner permission required)\"),\n});\n\nexport async function createComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof createCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.POST(\n \"/v1/teams/{team_name}/posts/{post_number}/comments\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n },\n body: {\n comment: {\n body_md: args.bodyMd,\n user: args.user,\n } as components[\"schemas\"][\"CommentInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comment: components[\"schemas\"][\"Comment\"] = data;\n const transformed = transformComment(comment, { omitBody: true });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const updateCommentSchema = createSchemaWithTeamName({\n commentId: z.number().describe(\"The comment ID to update\"),\n bodyMd: z.string().describe(\"The updated comment content in Markdown format\"),\n user: z\n .string()\n .optional()\n .describe(\"Comment author's screen_name (owner permission required)\"),\n});\n\nexport async function updateComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof updateCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.PATCH(\n \"/v1/teams/{team_name}/comments/{comment_id}\",\n {\n params: {\n path: { team_name: args.teamName, comment_id: args.commentId },\n },\n body: {\n comment: {\n body_md: args.bodyMd,\n user: args.user,\n } as components[\"schemas\"][\"CommentInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comment: components[\"schemas\"][\"Comment\"] = data;\n const transformed = transformComment(comment, { omitBody: true });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const deleteCommentSchema = createSchemaWithTeamName({\n commentId: z.number().describe(\"The comment ID to delete\"),\n});\n\nexport async function deleteComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof deleteCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { error, response } = await client.DELETE(\n \"/v1/teams/{team_name}/comments/{comment_id}\",\n {\n params: {\n path: { team_name: args.teamName, comment_id: args.commentId },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse({\n success: true,\n message: \"Comment deleted successfully\",\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getPostCommentsSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to get comments for\"),\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getPostComments(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getPostCommentsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}/comments\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n query: {\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comments: components[\"schemas\"][\"Comment\"][] = data.comments;\n const transformed = comments.map((comment) =>\n transformComment(comment, { truncateBody: 300 }),\n );\n\n return formatToolResponse({ ...data, comments: transformed });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamCommentsSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getTeamComments(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamCommentsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/comments\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comments: components[\"schemas\"][\"Comment\"][] = data.comments;\n const transformed = comments.map((comment) =>\n transformComment(comment, { truncateBody: 300 }),\n );\n\n return formatToolResponse({ ...data, comments: transformed });\n } catch (error) {\n return formatToolError(error);\n }\n}\n","export interface PostNameParts {\n name?: string;\n category?: string;\n}\n\nexport function normalizePostName(\n name?: string,\n category?: string,\n): PostNameParts {\n if (!name) {\n return { name, category };\n }\n\n if (category !== undefined) {\n return { name, category };\n }\n\n if (name.includes(\"/\")) {\n const parts = name.split(\"/\");\n const extractedName = parts.pop();\n const extractedCategory = parts.join(\"/\");\n\n return {\n name: extractedName || undefined,\n category: extractedCategory,\n };\n }\n\n return { name, category };\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { normalizePostName } from \"../transformers/post-name-normalizer.js\";\nimport { transformPost } from \"../transformers/post-transformer.js\";\n\nexport const getPostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to retrieve\"),\n include: z\n .enum([\"comments\"])\n .optional()\n .describe(\"Specify 'comments' to include comments in the response\"),\n});\n\nexport async function getPost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getPostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n query: {\n include: args.include,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n const post: components[\"schemas\"][\"Post\"] = data;\n const transformed = transformPost(post, { truncateBody: 10000 });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const createPostSchema = createSchemaWithTeamName({\n name: z.string().describe(\"The post name (title)\"),\n bodyMd: z.string().optional().describe(\"The post content in Markdown format\"),\n tags: z.array(z.string()).optional().describe(\"Tags for the post\"),\n category: z.string().optional().describe(\"Category path (e.g., 'dev/docs')\"),\n wip: z\n .boolean()\n .default(true)\n .describe(\n \"Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)\",\n ),\n message: z.string().optional().describe(\"Update message for the post\"),\n});\n\nexport async function createPost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof createPostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { name, category } = normalizePostName(args.name, args.category);\n\n const { data, error, response } = await client.POST(\n \"/v1/teams/{team_name}/posts\",\n {\n params: {\n path: { team_name: args.teamName },\n },\n body: {\n post: {\n name: name,\n body_md: args.bodyMd,\n tags: args.tags,\n category: category,\n wip: args.wip,\n message: args.message,\n } as components[\"schemas\"][\"PostCreateInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n const transformed = transformPost(post, { omitBody: true });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const updatePostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to update\"),\n name: z.string().optional().describe(\"The post name (title)\"),\n bodyMd: z.string().optional().describe(\"The post content in Markdown format\"),\n tags: z.array(z.string()).optional().describe(\"Tags for the post\"),\n category: z.string().optional().describe(\"Category path (e.g., 'dev/docs')\"),\n wip: z\n .boolean()\n .optional()\n .describe(\n \"Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)\",\n ),\n message: z.string().optional().describe(\"Update message for the post\"),\n originalRevision: z\n .object({\n bodyMd: z.string(),\n number: z.number(),\n user: z.string(),\n })\n .optional()\n .describe(\"Original revision to check for conflicts\"),\n});\n\nexport async function updatePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof updatePostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { name, category } = normalizePostName(args.name, args.category);\n\n const { data, error, response } = await client.PATCH(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n },\n body: {\n post: {\n name: name,\n body_md: args.bodyMd,\n tags: args.tags,\n category: category,\n wip: args.wip,\n message: args.message,\n original_revision: args.originalRevision\n ? {\n body_md: args.originalRevision.bodyMd,\n number: args.originalRevision.number,\n user: args.originalRevision.user,\n }\n : undefined,\n } as components[\"schemas\"][\"PostUpdateInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n const transformed = transformPost(post, { omitBody: true });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n","export function normalizeSearchQuery(query: string): string {\n let normalized = query;\n\n // Convert wildcard \"*\" to empty string (for \"all posts\" queries)\n if (normalized === \"*\") {\n normalized = \"\";\n }\n\n // Convert common date syntax patterns from other services to esa format\n // GitHub/Gmail style: after:YYYY-MM-DD -> created:>YYYY-MM-DD\n normalized = normalized.replace(\n /\\bafter:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:>$1\",\n );\n\n // GitHub/Gmail style: before:YYYY-MM-DD -> created:<YYYY-MM-DD\n normalized = normalized.replace(\n /\\bbefore:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:<$1\",\n );\n\n // Alternative patterns that might be used\n // since:YYYY-MM-DD -> created:>YYYY-MM-DD\n normalized = normalized.replace(\n /\\bsince:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:>$1\",\n );\n\n // until:YYYY-MM-DD -> created:<YYYY-MM-DD\n normalized = normalized.replace(\n /\\buntil:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:<$1\",\n );\n\n return normalized;\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { transformPost } from \"../transformers/post-transformer.js\";\nimport { normalizeSearchQuery } from \"../transformers/query-normalizer.js\";\n\nexport const searchPostsSchema = createSchemaWithTeamName({\n query: z\n .string()\n .describe(`Search query string. Use specific terms, not wildcards like \"*\". Empty string returns all posts.\n## Important Note for Date Queries:\n**WARNING: Do NOT use 'after:', 'before:', 'since:', or 'until:' syntax (these are from GitHub/Gmail/pplog).\nUse esa-specific date syntax: created:>YYYY-MM-DD, created:<YYYY-MM-DD, updated:>YYYY-MM-DD, updated:<YYYY-MM-DD\n\n## Important Note for Relative Date Queries:\n**CRITICAL: Always get today's actual date from the system before processing\nrelative date queries (e.g., \"today\", \"yesterday\", \"last week\", \"recent\").\nWhen searching, apply these strategies:\n1. Convert concepts to technical terms (e.g., general descriptions → specific property names, method names, or technical keywords)\n2. Translate between Japanese and English technical terms (e.g., Japanese concepts → English API/property names)\n3. Expand to related technical elements (e.g., one concept → multiple implementation approaches, related technologies, or alternative solutions)\nIMPORTANT: Space-separated terms are treated as AND conditions. Use \"OR\" operator for alternative terms: \"word-break OR word-wrap OR overflow-wrap\".\nAdvanced search: \"tag:release\", \"category:dev\", \"wip:false\", \"keyword:API\", \"title:設計書\".\nCategory search: \"on:category\" (posts directly in category), \"in:category\" (posts in category and subcategories), \"on:/\" (uncategorized posts).\nFor broader results, use OR between related terms rather than listing them with spaces.`)\n .transform(normalizeSearchQuery),\n sort: z\n .enum([\n \"updated\",\n \"created\",\n \"number\",\n \"stars\",\n \"watches\",\n \"comments\",\n \"best_match\",\n ])\n .optional()\n .describe(\"Sort key\"),\n order: z.enum([\"desc\", \"asc\"]).optional().describe(\"Sort direction\"),\n page: z.number().int().positive().optional().describe(\"Page number\"),\n perPage: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\"Items per page\"),\n include: z\n .enum([\"comments\"])\n .optional()\n .describe(\"Specify 'comments' to include comments in the response\"),\n});\n\nexport async function searchPosts(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof searchPostsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n q: args.query,\n sort: args.sort,\n order: args.order,\n page: args.page,\n per_page: args.perPage,\n include: args.include,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n const posts: components[\"schemas\"][\"Post\"][] = data.posts;\n const transformed = posts.map((post) =>\n transformPost(post, { truncateBody: 500 }),\n );\n\n if (posts.length === 0 && isAndQuery(args.query)) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: generateOrSearchSuggestion(args.query),\n },\n ],\n };\n }\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nfunction isAndQuery(query: string): boolean {\n if (!query || query.trim() === \"\") {\n return false;\n }\n\n if (/\\bOR\\b/.test(query) || query.includes(\"|\")) {\n return false;\n }\n\n const tokens = query.split(/\\s+/).filter(Boolean);\n return tokens.length >= 2;\n}\n\nfunction generateOrSearchSuggestion(query: string): string {\n const tokens = query.split(/\\s+/).filter(Boolean);\n const orQuery = tokens.join(\" OR \");\n return `---\nNo results found. Your query uses AND conditions (space-separated terms).\nSuggestions:\n- Try OR search: \"${orQuery}\"\n- Omit some keywords from your query`;\n}\n","import type { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { getPost } from \"./posts.js\";\nimport { searchPosts, searchPostsSchema } from \"./search.js\";\n\n// Documentation team and post constants\nexport const HELP_DOCS = {\n TEAM: \"docs\",\n SEARCH_OPTIONS_POST_ID: 104,\n MARKDOWN_SYNTAX_POST_ID: 49,\n} as const;\n\n// Schema for searchHelp - omit teamName, order, include, and sort from searchPostsSchema\nexport const searchHelpSchema = searchPostsSchema.omit({\n teamName: true,\n order: true,\n include: true,\n sort: true,\n});\n\nexport async function getSearchOptionsHelp(\n client: ReturnType<typeof createEsaClient>,\n _args: Record<string, never>,\n) {\n return getPost(client, {\n teamName: HELP_DOCS.TEAM,\n postNumber: HELP_DOCS.SEARCH_OPTIONS_POST_ID,\n });\n}\n\nexport async function getMarkdownSyntaxHelp(\n client: ReturnType<typeof createEsaClient>,\n _args: Record<string, never>,\n) {\n return getPost(client, {\n teamName: HELP_DOCS.TEAM,\n postNumber: HELP_DOCS.MARKDOWN_SYNTAX_POST_ID,\n });\n}\n\nexport async function searchHelp(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof searchHelpSchema>,\n) {\n return searchPosts(client, {\n teamName: HELP_DOCS.TEAM,\n sort: \"best_match\",\n ...args,\n });\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { normalizeTeamName } from \"../transformers/team-name-normalizer.js\";\nimport { createPost, updatePost } from \"./posts.js\";\n\nexport const archivePostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to archive\"),\n message: z.string().optional().describe(\"Archive message for the post\"),\n});\n\nexport async function archivePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof archivePostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n const currentCategory = post.category || \"\";\n\n if (currentCategory.startsWith(\"Archived/\")) {\n return formatToolResponse({\n message: \"Post is already archived\",\n category: currentCategory,\n });\n }\n\n const archivedCategory =\n currentCategory === \"\" ? \"Archived\" : `Archived/${currentCategory}`;\n\n return await updatePost(client, {\n teamName: args.teamName,\n postNumber: args.postNumber,\n category: archivedCategory,\n message: args.message || \"Archive post\",\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const shipPostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to ship\"),\n});\n\nexport async function shipPost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof shipPostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n return await updatePost(client, {\n teamName: args.teamName,\n postNumber: args.postNumber,\n wip: false,\n message: \"Ship It!\",\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const duplicatePostSchema = createSchemaWithTeamName({\n postNumber: z\n .number()\n .describe(\"The source post number to prepare for duplication\"),\n targetTeamName: z\n .string()\n .optional()\n .describe(\"The name of the esa team\")\n .transform((val) => (val ? normalizeTeamName(val) : undefined)),\n});\n\nexport async function duplicatePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof duplicatePostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/new\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n parent_post_id: args.postNumber,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const postNew = data.post as components[\"schemas\"][\"PostNew\"];\n\n return createPost(client, {\n teamName: args.targetTeamName || args.teamName,\n name: postNew.name,\n bodyMd: postNew.body_md,\n wip: true,\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { z } from \"zod\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\nimport { getAttachment, getAttachmentSchema } from \"./attachments.js\";\nimport {\n getAllCategoryPaths,\n getAllCategoryPathsSchema,\n getCategories,\n getCategoriesSchema,\n getTopCategories,\n getTopCategoriesSchema,\n} from \"./categories.js\";\nimport {\n createComment,\n createCommentSchema,\n deleteComment,\n deleteCommentSchema,\n getComment,\n getCommentSchema,\n getPostComments,\n getPostCommentsSchema,\n getTeamComments,\n getTeamCommentsSchema,\n updateComment,\n updateCommentSchema,\n} from \"./comments.js\";\nimport {\n getMarkdownSyntaxHelp,\n getSearchOptionsHelp,\n searchHelp,\n searchHelpSchema,\n} from \"./helps.js\";\nimport {\n archivePost,\n archivePostSchema,\n duplicatePost,\n duplicatePostSchema,\n shipPost,\n shipPostSchema,\n} from \"./post-actions.js\";\nimport {\n createPost,\n createPostSchema,\n getPost,\n getPostSchema,\n updatePost,\n updatePostSchema,\n} from \"./posts.js\";\nimport { searchPosts, searchPostsSchema } from \"./search.js\";\nimport {\n getTeamMembers,\n getTeamMembersSchema,\n getTeamStats,\n getTeamStatsSchema,\n getTeams,\n getTeamsSchema,\n getTeamTags,\n getTeamTagsSchema,\n} from \"./teams.js\";\n\nexport function setupTools(server: McpServer, context: MCPContext): void {\n console.error(\"Setting up MCP tools...\");\n\n server.registerTool(\n \"esa_get_teams\",\n {\n title: \"Get user's accessible esa teams\",\n description: \"Retrieves a list of esa teams that the user has access to.\",\n inputSchema: getTeamsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamsSchema>) =>\n withContext(context, getTeams, params),\n );\n\n server.registerTool(\n \"esa_get_team_stats\",\n {\n title: \"Get team statistics\",\n description:\n \"Retrieves team statistics including member count, posts count (total/WIP/shipped), comments, stars, watches, and daily/weekly/monthly active users\",\n inputSchema: getTeamStatsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamStatsSchema>) =>\n withContext(context, getTeamStats, params),\n );\n\n server.registerTool(\n \"esa_get_team_tags\",\n {\n title: \"Get team tags\",\n description:\n \"Retrieves all tags used in posts within a team, along with the count of posts for each tag\",\n inputSchema: getTeamTagsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamTagsSchema>) =>\n withContext(context, getTeamTags, params),\n );\n\n server.registerTool(\n \"esa_get_team_members\",\n {\n title: \"Get team members\",\n description:\n \"Retrieves all members of a team with their roles and profile information\",\n inputSchema: getTeamMembersSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamMembersSchema>) =>\n withContext(context, getTeamMembers, params),\n );\n\n server.registerTool(\n \"esa_get_post\",\n {\n title: \"Get a specific esa post\",\n description:\n \"Retrieves a specific post from an esa team by post number, with optional comments included.\",\n inputSchema: getPostSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getPostSchema>) =>\n withContext(context, getPost, params),\n );\n\n server.registerTool(\n \"esa_search_posts\",\n {\n title: \"Search Posts\",\n description: \"Search for posts in esa.io\",\n inputSchema: searchPostsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof searchPostsSchema>) =>\n withContext(context, searchPosts, params),\n );\n\n server.registerTool(\n \"esa_create_post\",\n {\n title: \"Create a new esa post\",\n description:\n \"Creates a new post in an esa team with optional tags, category, and WIP status.\",\n inputSchema: createPostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof createPostSchema>) =>\n withContext(context, createPost, params),\n );\n\n server.registerTool(\n \"esa_update_post\",\n {\n title: \"Update an existing esa post\",\n description:\n \"Updates an existing post in an esa team by post number. You can update the title, content, tags, category, and WIP status. To ship a post (mark as complete), set wip to false - this is preferred over using esa_ship_post when updating other fields simultaneously.\",\n inputSchema: updatePostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof updatePostSchema>) =>\n withContext(context, updatePost, params),\n );\n\n server.registerTool(\n \"esa_get_comment\",\n {\n title: \"Get a specific comment\",\n description:\n \"Retrieves a specific comment by comment ID, with optional stargazers included.\",\n inputSchema: getCommentSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getCommentSchema>) =>\n withContext(context, getComment, params),\n );\n\n server.registerTool(\n \"esa_create_comment\",\n {\n title: \"Create a new comment on a post\",\n description: \"Creates a new comment on an existing post in an esa team.\",\n inputSchema: createCommentSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof createCommentSchema>) =>\n withContext(context, createComment, params),\n );\n\n server.registerTool(\n \"esa_update_comment\",\n {\n title: \"Update an existing comment\",\n description: \"Updates an existing comment in an esa team by comment ID.\",\n inputSchema: updateCommentSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof updateCommentSchema>) =>\n withContext(context, updateComment, params),\n );\n\n server.registerTool(\n \"esa_delete_comment\",\n {\n title: \"Delete a comment\",\n description: \"Deletes a comment from an esa team by comment ID.\",\n inputSchema: deleteCommentSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof deleteCommentSchema>) =>\n withContext(context, deleteComment, params),\n );\n\n server.registerTool(\n \"esa_get_post_comments\",\n {\n title: \"Get comments for a specific post\",\n description:\n \"Retrieves a list of comments for a specific post with pagination support.\",\n inputSchema: getPostCommentsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getPostCommentsSchema>) =>\n withContext(context, getPostComments, params),\n );\n\n server.registerTool(\n \"esa_get_team_comments\",\n {\n title: \"Get team comments\",\n description:\n \"Retrieves a list of comments in a team with pagination support.\",\n inputSchema: getTeamCommentsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamCommentsSchema>) =>\n withContext(context, getTeamComments, params),\n );\n\n server.registerTool(\n \"esa_get_categories\",\n {\n title: \"Get categories for a specific path\",\n description:\n \"Retrieves category information and subcategories for a specific category path, with optional posts and parent categories included\",\n inputSchema: getCategoriesSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getCategoriesSchema>) =>\n withContext(context, getCategories, params),\n );\n\n server.registerTool(\n \"esa_get_top_categories\",\n {\n title: \"Get top-level categories\",\n description: \"Retrieves all top-level categories for a team\",\n inputSchema: getTopCategoriesSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTopCategoriesSchema>) =>\n withContext(context, getTopCategories, params),\n );\n\n server.registerTool(\n \"esa_get_all_category_paths\",\n {\n title: \"Get category paths with pagination\",\n description:\n \"Retrieves category paths in a team to understand the overall category structure. Perfect for category organization, cleanup, migration planning, or finding similar categories. Returns a paginated list of paths with post counts, sorted in lexicographic order. Supports filtering (prefix/suffix/match/exact_match) to find categories by pattern.\",\n inputSchema: getAllCategoryPathsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getAllCategoryPathsSchema>) =>\n withContext(context, getAllCategoryPaths, params),\n );\n\n server.registerTool(\n \"esa_archive_post\",\n {\n title: \"Archive a post\",\n description:\n \"Archives a post by moving it to the Archived/ category. If the post is in 'dev/docs', it becomes 'Archived/dev/docs'. Posts without category go to 'Archived'.\",\n inputSchema: archivePostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof archivePostSchema>) =>\n withContext(context, archivePost, params),\n );\n\n server.registerTool(\n \"esa_ship_post\",\n {\n title: \"Ship a post\",\n description:\n \"Ships a post by setting wip to false. This marks the post as complete and ready to be published. Use this only when you need to ship without making other changes - if you're also updating title, content, or other fields, use esa_update_post with wip: false instead.\",\n inputSchema: shipPostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof shipPostSchema>) =>\n withContext(context, shipPost, params),\n );\n\n server.registerTool(\n \"esa_duplicate_post\",\n {\n title: \"Prepare a post for duplication\",\n description:\n \"Prepares a post for duplication by retrieving its name and body_md content. Returns the name and body_md that can be used with esa_create_post to create a duplicate of the original post.\",\n inputSchema: duplicatePostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof duplicatePostSchema>) =>\n withContext(context, duplicatePost, params),\n );\n\n server.registerTool(\n \"esa_get_search_options_help\",\n {\n title: \"Get esa search options documentation\",\n description: `Get esa search syntax documentation when you need to construct complex\nsearch queries. Use this BEFORE esa_search_posts if you're unsure how to\ntranslate user's search requirements into proper esa query syntax (e.g., date\nranges, tag filters, category searches, advanced operators).`,\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: Record<string, never>) =>\n withContext(context, getSearchOptionsHelp, params),\n );\n\n server.registerTool(\n \"esa_get_markdown_syntax_help\",\n {\n title: \"Get esa Markdown syntax documentation\",\n description: `Get esa Markdown and formatting documentation when unsure about syntax.\nUse this BEFORE using any tools with *_md parameters (like esa_create_post,\nesa_update_post, esa_create_comment, esa_update_comment) if you need\nclarification on Markdown syntax, esa-specific extensions, or formatting options.`,\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: Record<string, never>) =>\n withContext(context, getMarkdownSyntaxHelp, params),\n );\n\n server.registerTool(\n \"esa_search_help\",\n {\n title: \"Search esa documentation and help\",\n description: `Search esa documentation for features, terminology, and specifications.\nUse this when users mention esa-specific terms, ask about esa functionality,\nor request help with esa workflows that you're not familiar with.`,\n inputSchema: searchHelpSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof searchHelpSchema>) =>\n withContext(context, searchHelp, params),\n );\n\n server.registerTool(\n \"esa_get_attachment\",\n {\n title: \"Get attachment file from esa\",\n description:\n \"Retrieves an attachment file from esa with signed URLs. For supported images (JPEG, PNG, GIF, WebP) under 30MB, returns base64-encoded data. For other file types, larger images, or when forceSignedUrl is true, returns signed URLs.\",\n inputSchema: getAttachmentSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getAttachmentSchema>) =>\n withContext(context, getAttachment, params),\n );\n}\n"],"mappings":"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ACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAa,EAAO,CAC5C,CAED,EAAO,aACL,kBACA,CACE,MAAO,wBACP,YACE,kFACF,YAAa,EAAiB,MAC9B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAY,EAAO,CAC3C,CAED,EAAO,aACL,kBACA,CACE,MAAO,8BACP,YACE,yQACF,YAAa,EAAiB,MAC9B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAY,EAAO,CAC3C,CAED,EAAO,aACL,kBACA,CACE,MAAO,yBACP,YACE,iFACF,YAAa,EAAiB,MAC9B,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAY,EAAO,CAC3C,CAED,EAAO,aACL,qBACA,CACE,MAAO,iCACP,YAAa,4DACb,YAAa,EAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,qBACA,CACE,MAAO,6BACP,YAAa,4DACb,YAAa,EAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,qBACA,CACE,MAAO,mBACP,YAAa,oDACb,YAAa,EAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,wBACA,CACE,MAAO,mCACP,YACE,4EACF,YAAa,EAAsB,MACnC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAiB,EAAO,CAChD,CAED,EAAO,aACL,wBACA,CACE,MAAO,oBACP,YACE,kEACF,YAAa,GAAsB,MACnC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAiB,EAAO,CAChD,CAED,EAAO,aACL,qBACA,CACE,MAAO,qCACP,YACE,oIACF,YAAa,EAAoB,MACjC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,yBACA,CACE,MAAO,2BACP,YAAa,gDACb,YAAa,EAAuB,MACpC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAkB,EAAO,CACjD,CAED,EAAO,aACL,6BACA,CACE,MAAO,qCACP,YACE,yVACF,YAAa,EAA0B,MACvC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAqB,EAAO,CACpD,CAED,EAAO,aACL,mBACA,CACE,MAAO,iBACP,YACE,iKACF,YAAa,GAAkB,MAC/B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAa,EAAO,CAC5C,CAED,EAAO,aACL,gBACA,CACE,MAAO,cACP,YACE,4QACF,YAAa,GAAe,MAC5B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAU,EAAO,CACzC,CAED,EAAO,aACL,qBACA,CACE,MAAO,iCACP,YACE,6LACF,YAAa,GAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAe,EAAO,CAC9C,CAED,EAAO,aACL,8BACA,CACE,MAAO,uCACP,YAAa;;;8DAIb,YAAa,EAAE,CACf,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAsB,EAAO,CACrD,CAED,EAAO,aACL,+BACA,CACE,MAAO,wCACP,YAAa;;;mFAIb,YAAa,EAAE,CACf,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAuB,EAAO,CACtD,CAED,EAAO,aACL,kBACA,CACE,MAAO,oCACP,YAAa;;mEAGb,YAAa,GAAiB,MAC9B,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAY,EAAO,CAC3C,CAED,EAAO,aACL,qBACA,CACE,MAAO,+BACP,YACE,yOACF,YAAa,EAAoB,MACjC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@esaio/esa-mcp-server",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "mcpName": "io.github.esaio/esa",
5
5
  "description": "Official MCP server for esa.io - STDIO transport version",
6
6
  "main": "bin/index.js",
@@ -1,2 +0,0 @@
1
- import{a as e,o as t,s as n}from"./mcp-response-Cn2WFDAj.js";import{z as r}from"zod";function i(e,t={}){let{truncateBody:n}=t,r=e.body_md;return n&&r&&r.length>n&&(r=`${r.slice(0,n)}...`),{url:e.url,wip:e.wip?`WIP`:`Shipped`,kind:e.kind,category_and_title_and_tags:e.full_name,body_md:r,created_at:e.created_at,updated_at:e.updated_at,created_by:e.created_by,updated_by:e.updated_by,stats:{tasks_count:e.tasks_count,done_tasks_count:e.done_tasks_count,comments_count:e.comments_count,stargazers_count:e.stargazers_count,watchers_count:e.watchers_count}}}function a(e){let t=e.indexOf(`.`);return t>=0?e.substring(0,t):e}const o=r.string().default(``).describe(`Team name (required). Use esa_get_teams first to see available teams.`).transform(a);function s(e){return r.object({teamName:o,...e})}const c=r.object({page:r.number().optional().describe(`Page number (starts from 1)`),perPage:r.number().optional().describe(`Number of items per page`),role:r.enum([`member`,`owner`]).optional().describe(`Filter by role`)});async function l(n,r={}){try{let{data:i,error:a,response:o}=await n.GET(`/v1/teams`,{params:{query:{page:r.page,per_page:r.perPage,role:r.role}}});return a||!o.ok?e(a||o.status):t({...i,teams:i.teams?.map(e=>({url:e.url,name:e.name,description:e.description}))})}catch(t){return e(t)}}const u=s({});async function d(r,i){try{if(!i.teamName)throw new n;let{data:a,error:o,response:s}=await r.GET(`/v1/teams/{team_name}/stats`,{params:{path:{team_name:i.teamName}}});return o||!s.ok?e(o||s.status):t(a)}catch(t){return e(t)}}const f=s({page:r.number().optional().describe(`Page number (starts from 1)`),perPage:r.number().optional().describe(`Number of items per page`)});async function p(r,i){try{if(!i.teamName)throw new n;let{data:a,error:o,response:s}=await r.GET(`/v1/teams/{team_name}/tags`,{params:{path:{team_name:i.teamName},query:{page:i.page,per_page:i.perPage}}});return o||!s.ok?e(o||s.status):t(a)}catch(t){return e(t)}}const m=s({page:r.number().optional().describe(`Page number (starts from 1)`),perPage:r.number().optional().describe(`Number of items per page`),sort:r.enum([`posts_count`,`joined`,`last_accessed`]).optional().describe(`Sort criteria`),order:r.enum([`desc`,`asc`]).optional().describe(`Sort order`)});async function h(r,i){try{if(!i.teamName)throw new n;let{data:a,error:o,response:s}=await r.GET(`/v1/teams/{team_name}/members`,{params:{path:{team_name:i.teamName},query:{page:i.page,per_page:i.perPage,sort:i.sort,order:i.order}}});return o||!s.ok?e(o||s.status):t(a)}catch(t){return e(t)}}export{p as a,c,i as d,u as i,s as l,m as n,f as o,d as r,l as s,h as t,a as u};
2
- //# sourceMappingURL=teams-B9y1URzV.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"teams-B9y1URzV.js","names":[],"sources":["../src/transformers/post-transformer.ts","../src/transformers/team-name-normalizer.ts","../src/schemas/team-name-schema.ts","../src/tools/teams.ts"],"sourcesContent":["import type { components } from \"../generated/api-types.js\";\n\nexport interface PostTransformOptions {\n truncateBody?: number;\n}\n\nexport function transformPost(\n post: components[\"schemas\"][\"Post\"],\n options: PostTransformOptions = {},\n) {\n const { truncateBody } = options;\n\n let bodyMd = post.body_md;\n if (truncateBody && bodyMd && bodyMd.length > truncateBody) {\n bodyMd = `${bodyMd.slice(0, truncateBody)}...`;\n }\n\n return {\n url: post.url,\n wip: post.wip ? \"WIP\" : (\"Shipped\" as const),\n kind: post.kind,\n category_and_title_and_tags: post.full_name,\n body_md: bodyMd,\n created_at: post.created_at,\n updated_at: post.updated_at,\n created_by: post.created_by,\n updated_by: post.updated_by,\n stats: {\n tasks_count: post.tasks_count,\n done_tasks_count: post.done_tasks_count,\n comments_count: post.comments_count,\n stargazers_count: post.stargazers_count,\n watchers_count: post.watchers_count,\n },\n };\n}\n\nexport type TransformedPost = ReturnType<typeof transformPost>;\n","export function normalizeTeamName(teamName: string): string {\n const dotIndex = teamName.indexOf(\".\");\n if (dotIndex >= 0) {\n return teamName.substring(0, dotIndex);\n }\n return teamName;\n}\n","import { z } from \"zod\";\nimport { normalizeTeamName } from \"../transformers/team-name-normalizer.js\";\n\nexport const teamNameSchema = z\n .string()\n .default(\"\")\n .describe(\n \"Team name (required). Use esa_get_teams first to see available teams.\",\n )\n .transform(normalizeTeamName);\n\nexport function createSchemaWithTeamName<T extends z.ZodRawShape>(\n schema: T,\n): z.ZodObject<T & { teamName: typeof teamNameSchema }> {\n return z.object({\n teamName: teamNameSchema,\n ...schema,\n });\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\n\nexport const getTeamsSchema = z.object({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n role: z.enum([\"member\", \"owner\"]).optional().describe(\"Filter by role\"),\n});\n\nexport async function getTeams(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamsSchema> = {},\n) {\n try {\n const { data, error, response } = await client.GET(\"/v1/teams\", {\n params: {\n query: {\n page: args.page,\n per_page: args.perPage,\n role: args.role,\n },\n },\n });\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const transformed = {\n ...data,\n teams: data.teams?.map((team: components[\"schemas\"][\"Team\"]) => ({\n url: team.url,\n name: team.name,\n description: team.description,\n })),\n };\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamStatsSchema = createSchemaWithTeamName({});\n\nexport async function getTeamStats(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamStatsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n `/v1/teams/{team_name}/stats`,\n {\n params: {\n path: { team_name: args.teamName },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamTagsSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getTeamTags(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamTagsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n `/v1/teams/{team_name}/tags`,\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamMembersSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n sort: z\n .enum([\"posts_count\", \"joined\", \"last_accessed\"])\n .optional()\n .describe(\"Sort criteria\"),\n order: z.enum([\"desc\", \"asc\"]).optional().describe(\"Sort order\"),\n});\n\nexport async function getTeamMembers(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamMembersSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n `/v1/teams/{team_name}/members`,\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n page: args.page,\n per_page: args.perPage,\n sort: args.sort,\n order: args.order,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n"],"mappings":"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"}
@@ -1,29 +0,0 @@
1
- import{a as e,c as t,o as n,s as r}from"./mcp-response-Cn2WFDAj.js";import{a as i,c as a,d as o,i as s,l as c,n as l,o as u,r as d,s as ee,t as f,u as p}from"./teams-B9y1URzV.js";import{z as m}from"zod";const h=[`image/jpeg`,`image/png`,`image/gif`,`image/webp`],g=c({url:m.string().describe(`Attachment URL. Can be a full URL (https://files.esa.io/..., https://dl.esa.io/...) or a path (/uploads/...)`),forceSignedUrl:m.boolean().optional().describe(`If true, always return signed URLs instead of base64-encoded images. Default is false.`)}),_=[`files.esa.io`,`dl.esa.io`];function v(e){if(e.startsWith(`/`))return!0;try{let t=new URL(e);return _.includes(t.hostname)}catch{return!0}}function y(e){if(e.startsWith(`/`))return e;try{return new URL(e).pathname}catch{return e}}function b(e){return h.includes(e)}async function x(e,t){if(t)return{type:`text`,text:e};let n=await fetch(e);if(!n.ok)throw Error(`Failed to fetch attachment: ${n.status} ${n.statusText}`);let r=n.headers.get(`content-type`)||``,i=n.headers.get(`content-length`),a=i?Number.parseInt(i,10):0;if(b(r)&&a>0&&a<=31457280){let e=await n.arrayBuffer();return{type:`image`,data:Buffer.from(e).toString(`base64`),mimeType:r}}return{type:`text`,text:e}}async function te(t,n){try{if(!n.teamName)throw new r;let i=n.forceSignedUrl??!1;if(v(n.url)){let r=y(n.url),{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/signed_urls`,{params:{path:{team_name:n.teamName},query:{urls:r,v:2,expires_in:300}}});if(o||!s.ok)return e(o||s.status);if(!a.signed_urls||a.signed_urls.length===0)throw Error(`No signed URLs returned from API`);let[c,l]=a.signed_urls[0];if(l===null)throw Error(`File not found: ${c}`);try{return{content:[await x(l,i)]}}catch(e){throw Error(`Failed to fetch attachment for ${c}: ${e instanceof Error?e.message:String(e)}`)}}try{return{content:[await x(n.url,i)]}}catch(e){throw Error(`Failed to fetch attachment for ${n.url}: ${e instanceof Error?e.message:String(e)}`)}}catch(t){return e(t)}}function S(e){return{full_name:e.full_name,count:e.count,has_child:e.has_child||!1}}function C(e){return{current_category:e.current_category,categories:e.categories?.map(S)||[],parent_categories:e.parent_categories?.map(e=>({current_category:e.current_category,categories:e.categories?.map(S)||[]})),readme:e.readme,no_category:e.no_category?S(e.no_category):void 0,descendant_posts:e.descendant_posts,posts:e.posts,total_count:e.total_count,per_page:e.per_page,page:e.page,prev_page:e.prev_page,next_page:e.next_page,max_per_page:e.max_per_page}}const w=c({select:m.string().describe(`Category path to retrieve`),include:m.enum([`posts`,`parent_categories`]).optional().describe(`Additional information to include`),descendantPosts:m.boolean().optional().describe(`Include descendant posts (only effective with include=posts)`),page:m.number().optional().describe(`Page number (starts from 1)`),perPage:m.number().optional().describe(`Number of items per page`)});async function T(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/categories`,{params:{path:{team_name:i.teamName},query:{select:i.select,include:i.include,descendant_posts:i.descendantPosts,page:i.page,per_page:i.perPage}}});return o||!s.ok?e(o||s.status):n(C(a))}catch(t){return e(t)}}const E=c({});async function D(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/categories/top`,{params:{path:{team_name:i.teamName}}});return o||!s.ok?e(o||s.status):n(C(a))}catch(t){return e(t)}}const O=c({page:m.number().optional().describe(`Page number (starts from 1)`),perPage:m.number().optional().describe(`Number of items per page`),prefix:m.string().optional().describe(`Filter paths starting with specified string (e.g., 'dev' finds 'dev', 'dev/api', 'dev/docs')`),suffix:m.string().optional().describe(`Filter paths ending with specified string (e.g., 'api' finds 'dev/api', 'backend/api')`),match:m.string().optional().describe(`Filter paths containing specified substring anywhere (e.g., 'doc' finds 'docs', 'dev/docs', 'documentation')`),exactMatch:m.string().optional().describe(`Filter paths matching exactly (e.g., 'dev/api' matches only 'dev/api', ignores leading/trailing slashes)`)});async function k(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/categories/paths`,{params:{path:{team_name:i.teamName},query:{v:2,page:i.page,per_page:i.perPage,prefix:i.prefix,suffix:i.suffix,match:i.match,exact_match:i.exactMatch}}});return o||!s.ok?e(o||s.status):n(a)}catch(t){return e(t)}}function A(e,t={}){let{truncateBody:n}=t,r=e.body_md;return n&&r&&r.length>n&&(r=`${r.slice(0,n)}...`),{id:e.id,post_number:e.post_number,url:e.url,body_md:r,created_at:e.created_at,updated_at:e.updated_at,created_by:e.created_by,stats:{stargazers_count:e.stargazers_count,star:e.star},stargazers:e.stargazers}}const j=c({commentId:m.number().describe(`The comment ID to retrieve`),include:m.enum([`stargazers`]).optional().describe(`Specify 'stargazers' to include stargazers in the response`)});async function M(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/comments/{comment_id}`,{params:{path:{team_name:i.teamName,comment_id:i.commentId},query:{include:i.include}}});return o||!s.ok?e(o||s.status):n(A(a))}catch(t){return e(t)}}const N=c({postNumber:m.number().describe(`The post number to comment on`),bodyMd:m.string().describe(`The comment content in Markdown format`),user:m.string().optional().describe(`Comment author's screen_name (owner permission required)`)});async function P(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.POST(`/v1/teams/{team_name}/posts/{post_number}/comments`,{params:{path:{team_name:i.teamName,post_number:i.postNumber}},body:{comment:{body_md:i.bodyMd,user:i.user}}});return o||!s.ok?e(o||s.status):n(A(a,{truncateBody:300}))}catch(t){return e(t)}}const F=c({commentId:m.number().describe(`The comment ID to update`),bodyMd:m.string().describe(`The updated comment content in Markdown format`),user:m.string().optional().describe(`Comment author's screen_name (owner permission required)`)});async function I(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.PATCH(`/v1/teams/{team_name}/comments/{comment_id}`,{params:{path:{team_name:i.teamName,comment_id:i.commentId}},body:{comment:{body_md:i.bodyMd,user:i.user}}});return o||!s.ok?e(o||s.status):n(A(a))}catch(t){return e(t)}}const L=c({commentId:m.number().describe(`The comment ID to delete`)});async function R(t,i){try{if(!i.teamName)throw new r;let{error:a,response:o}=await t.DELETE(`/v1/teams/{team_name}/comments/{comment_id}`,{params:{path:{team_name:i.teamName,comment_id:i.commentId}}});return a||!o.ok?e(a||o.status):n({success:!0,message:`Comment deleted successfully`})}catch(t){return e(t)}}const z=c({postNumber:m.number().describe(`The post number to get comments for`),page:m.number().optional().describe(`Page number (starts from 1)`),perPage:m.number().optional().describe(`Number of items per page`)});async function B(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/posts/{post_number}/comments`,{params:{path:{team_name:i.teamName,post_number:i.postNumber},query:{page:i.page,per_page:i.perPage}}});if(o||!s.ok)return e(o||s.status);let c=a.comments.map(e=>A(e,{truncateBody:300}));return n({...a,comments:c})}catch(t){return e(t)}}const V=c({page:m.number().optional().describe(`Page number (starts from 1)`),perPage:m.number().optional().describe(`Number of items per page`)});async function ne(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/comments`,{params:{path:{team_name:i.teamName},query:{page:i.page,per_page:i.perPage}}});if(o||!s.ok)return e(o||s.status);let c=a.comments.map(e=>A(e,{truncateBody:300}));return n({...a,comments:c})}catch(t){return e(t)}}function H(e,t){if(!e||t!==void 0)return{name:e,category:t};if(e.includes(`/`)){let t=e.split(`/`),n=t.pop(),r=t.join(`/`);return{name:n||void 0,category:r}}return{name:e,category:t}}const U=c({postNumber:m.number().describe(`The post number to retrieve`),include:m.enum([`comments`]).optional().describe(`Specify 'comments' to include comments in the response`)});async function W(t,i){try{if(!i.teamName)throw new r;let{data:a,error:s,response:c}=await t.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:i.teamName,post_number:i.postNumber},query:{include:i.include}}});return s||!c.ok?e(s||c.status):n(o(a))}catch(t){return e(t)}}const G=c({name:m.string().describe(`The post name (title)`),bodyMd:m.string().optional().describe(`The post content in Markdown format`),tags:m.array(m.string()).optional().describe(`Tags for the post`),category:m.string().optional().describe(`Category path (e.g., 'dev/docs')`),wip:m.boolean().default(!0).describe(`Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)`),message:m.string().optional().describe(`Update message for the post`)});async function K(t,i){try{if(!i.teamName)throw new r;let{name:a,category:s}=H(i.name,i.category),{data:c,error:l,response:u}=await t.POST(`/v1/teams/{team_name}/posts`,{params:{path:{team_name:i.teamName}},body:{post:{name:a,body_md:i.bodyMd,tags:i.tags,category:s,wip:i.wip,message:i.message}}});return l||!u.ok?e(l||u.status):n(o(c))}catch(t){return e(t)}}const q=c({postNumber:m.number().describe(`The post number to update`),name:m.string().optional().describe(`The post name (title)`),bodyMd:m.string().optional().describe(`The post content in Markdown format`),tags:m.array(m.string()).optional().describe(`Tags for the post`),category:m.string().optional().describe(`Category path (e.g., 'dev/docs')`),wip:m.boolean().optional().describe(`Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)`),message:m.string().optional().describe(`Update message for the post`),originalRevision:m.object({bodyMd:m.string(),number:m.number(),user:m.string()}).optional().describe(`Original revision to check for conflicts`)});async function J(t,i){try{if(!i.teamName)throw new r;let{name:a,category:s}=H(i.name,i.category),{data:c,error:l,response:u}=await t.PATCH(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:i.teamName,post_number:i.postNumber}},body:{post:{name:a,body_md:i.bodyMd,tags:i.tags,category:s,wip:i.wip,message:i.message,original_revision:i.originalRevision?{body_md:i.originalRevision.bodyMd,number:i.originalRevision.number,user:i.originalRevision.user}:void 0}}});return l||!u.ok?e(l||u.status):n(o(c))}catch(t){return e(t)}}function Y(e){let t=e;return t===`*`&&(t=``),t=t.replace(/\bafter:(\d{4}-\d{2}-\d{2})\b/gi,`created:>$1`),t=t.replace(/\bbefore:(\d{4}-\d{2}-\d{2})\b/gi,`created:<$1`),t=t.replace(/\bsince:(\d{4}-\d{2}-\d{2})\b/gi,`created:>$1`),t=t.replace(/\buntil:(\d{4}-\d{2}-\d{2})\b/gi,`created:<$1`),t}const X=c({query:m.string().describe(`Search query string. Use specific terms, not wildcards like "*". Empty string returns all posts.
2
- ## Important Note for Date Queries:
3
- **WARNING: Do NOT use 'after:', 'before:', 'since:', or 'until:' syntax (these are from GitHub/Gmail/pplog).
4
- Use esa-specific date syntax: created:>YYYY-MM-DD, created:<YYYY-MM-DD, updated:>YYYY-MM-DD, updated:<YYYY-MM-DD
5
-
6
- ## Important Note for Relative Date Queries:
7
- **CRITICAL: Always get today's actual date from the system before processing
8
- relative date queries (e.g., "today", "yesterday", "last week", "recent").
9
- When searching, apply these strategies:
10
- 1. Convert concepts to technical terms (e.g., general descriptions → specific property names, method names, or technical keywords)
11
- 2. Translate between Japanese and English technical terms (e.g., Japanese concepts → English API/property names)
12
- 3. Expand to related technical elements (e.g., one concept → multiple implementation approaches, related technologies, or alternative solutions)
13
- IMPORTANT: Space-separated terms are treated as AND conditions. Use "OR" operator for alternative terms: "word-break OR word-wrap OR overflow-wrap".
14
- Advanced search: "tag:release", "category:dev", "wip:false", "keyword:API", "title:設計書".
15
- Category search: "on:category" (posts directly in category), "in:category" (posts in category and subcategories), "on:/" (uncategorized posts).
16
- For broader results, use OR between related terms rather than listing them with spaces.`).transform(Y),sort:m.enum([`updated`,`created`,`number`,`stars`,`watches`,`comments`,`best_match`]).optional().describe(`Sort key`),order:m.enum([`desc`,`asc`]).optional().describe(`Sort direction`),page:m.number().int().positive().optional().describe(`Page number`),perPage:m.number().int().min(1).max(100).optional().describe(`Items per page`),include:m.enum([`comments`]).optional().describe(`Specify 'comments' to include comments in the response`)});async function Z(t,i){try{if(!i.teamName)throw new r;let{data:a,error:s,response:c}=await t.GET(`/v1/teams/{team_name}/posts`,{params:{path:{team_name:i.teamName},query:{q:i.query,sort:i.sort,order:i.order,page:i.page,per_page:i.perPage,include:i.include}}});if(s||!c.ok)return e(s||c.status);let l=a.posts,u=l.map(e=>o(e,{truncateBody:500}));return l.length===0&&re(i.query)?{content:[{type:`text`,text:ie(i.query)}]}:n(u)}catch(t){return e(t)}}function re(e){return!e||e.trim()===``||/\bOR\b/.test(e)||e.includes(`|`)?!1:e.split(/\s+/).filter(Boolean).length>=2}function ie(e){return`---
17
- No results found. Your query uses AND conditions (space-separated terms).
18
- Suggestions:
19
- - Try OR search: "${e.split(/\s+/).filter(Boolean).join(` OR `)}"
20
- - Omit some keywords from your query`}const Q={TEAM:`docs`,SEARCH_OPTIONS_POST_ID:104,MARKDOWN_SYNTAX_POST_ID:49},ae=X.omit({teamName:!0,order:!0,include:!0,sort:!0});async function oe(e,t){return W(e,{teamName:Q.TEAM,postNumber:Q.SEARCH_OPTIONS_POST_ID})}async function $(e,t){return W(e,{teamName:Q.TEAM,postNumber:Q.MARKDOWN_SYNTAX_POST_ID})}async function se(e,t){return Z(e,{teamName:Q.TEAM,sort:`best_match`,...t})}const ce=c({postNumber:m.number().describe(`The post number to archive`),message:m.string().optional().describe(`Archive message for the post`)});async function le(t,i){try{if(!i.teamName)throw new r;let{data:a,error:o,response:s}=await t.GET(`/v1/teams/{team_name}/posts/{post_number}`,{params:{path:{team_name:i.teamName,post_number:i.postNumber}}});if(o||!s.ok)return e(o||s.status);let c=a.category||``;if(c.startsWith(`Archived/`))return n({message:`Post is already archived`,category:c});let l=c===``?`Archived`:`Archived/${c}`;return await J(t,{teamName:i.teamName,postNumber:i.postNumber,category:l,message:i.message||`Archive post`})}catch(t){return e(t)}}const ue=c({postNumber:m.number().describe(`The post number to ship`)});async function de(t,n){try{if(!n.teamName)throw new r;return await J(t,{teamName:n.teamName,postNumber:n.postNumber,wip:!1,message:`Ship It!`})}catch(t){return e(t)}}const fe=c({postNumber:m.number().describe(`The source post number to prepare for duplication`),targetTeamName:m.string().optional().describe(`The name of the esa team`).transform(e=>e?p(e):void 0)});async function pe(t,n){try{if(!n.teamName)throw new r;let{data:i,error:a,response:o}=await t.GET(`/v1/teams/{team_name}/posts/new`,{params:{path:{team_name:n.teamName},query:{parent_post_id:n.postNumber}}});if(a||!o.ok)return e(a||o.status);let s=i.post;return K(t,{teamName:n.targetTeamName||n.teamName,name:s.name,bodyMd:s.body_md,wip:!0})}catch(t){return e(t)}}function me(e,n){console.error(`Setting up MCP tools...`),e.registerTool(`esa_get_teams`,{title:`Get user's accessible esa teams`,description:`Retrieves a list of esa teams that the user has access to.`,inputSchema:a.shape,annotations:{readOnlyHint:!0}},async e=>t(n,ee,e)),e.registerTool(`esa_get_team_stats`,{title:`Get team statistics`,description:`Retrieves team statistics including member count, posts count (total/WIP/shipped), comments, stars, watches, and daily/weekly/monthly active users`,inputSchema:s.shape,annotations:{readOnlyHint:!0}},async e=>t(n,d,e)),e.registerTool(`esa_get_team_tags`,{title:`Get team tags`,description:`Retrieves all tags used in posts within a team, along with the count of posts for each tag`,inputSchema:u.shape,annotations:{readOnlyHint:!0}},async e=>t(n,i,e)),e.registerTool(`esa_get_team_members`,{title:`Get team members`,description:`Retrieves all members of a team with their roles and profile information`,inputSchema:l.shape,annotations:{readOnlyHint:!0}},async e=>t(n,f,e)),e.registerTool(`esa_get_post`,{title:`Get a specific esa post`,description:`Retrieves a specific post from an esa team by post number, with optional comments included.`,inputSchema:U.shape,annotations:{readOnlyHint:!0}},async e=>t(n,W,e)),e.registerTool(`esa_search_posts`,{title:`Search Posts`,description:`Search for posts in esa.io`,inputSchema:X.shape,annotations:{readOnlyHint:!0}},async e=>t(n,Z,e)),e.registerTool(`esa_create_post`,{title:`Create a new esa post`,description:`Creates a new post in an esa team with optional tags, category, and WIP status.`,inputSchema:G.shape,annotations:{destructiveHint:!0}},async e=>t(n,K,e)),e.registerTool(`esa_update_post`,{title:`Update an existing esa post`,description:`Updates an existing post in an esa team by post number. You can update the title, content, tags, category, and WIP status. To ship a post (mark as complete), set wip to false - this is preferred over using esa_ship_post when updating other fields simultaneously.`,inputSchema:q.shape,annotations:{destructiveHint:!0}},async e=>t(n,J,e)),e.registerTool(`esa_get_comment`,{title:`Get a specific comment`,description:`Retrieves a specific comment by comment ID, with optional stargazers included.`,inputSchema:j.shape,annotations:{readOnlyHint:!0}},async e=>t(n,M,e)),e.registerTool(`esa_create_comment`,{title:`Create a new comment on a post`,description:`Creates a new comment on an existing post in an esa team.`,inputSchema:N.shape,annotations:{destructiveHint:!0}},async e=>t(n,P,e)),e.registerTool(`esa_update_comment`,{title:`Update an existing comment`,description:`Updates an existing comment in an esa team by comment ID.`,inputSchema:F.shape,annotations:{destructiveHint:!0}},async e=>t(n,I,e)),e.registerTool(`esa_delete_comment`,{title:`Delete a comment`,description:`Deletes a comment from an esa team by comment ID.`,inputSchema:L.shape,annotations:{destructiveHint:!0}},async e=>t(n,R,e)),e.registerTool(`esa_get_post_comments`,{title:`Get comments for a specific post`,description:`Retrieves a list of comments for a specific post with pagination support.`,inputSchema:z.shape,annotations:{readOnlyHint:!0}},async e=>t(n,B,e)),e.registerTool(`esa_get_team_comments`,{title:`Get team comments`,description:`Retrieves a list of comments in a team with pagination support.`,inputSchema:V.shape,annotations:{readOnlyHint:!0}},async e=>t(n,ne,e)),e.registerTool(`esa_get_categories`,{title:`Get categories for a specific path`,description:`Retrieves category information and subcategories for a specific category path, with optional posts and parent categories included`,inputSchema:w.shape,annotations:{readOnlyHint:!0}},async e=>t(n,T,e)),e.registerTool(`esa_get_top_categories`,{title:`Get top-level categories`,description:`Retrieves all top-level categories for a team`,inputSchema:E.shape,annotations:{readOnlyHint:!0}},async e=>t(n,D,e)),e.registerTool(`esa_get_all_category_paths`,{title:`Get category paths with pagination`,description:`Retrieves category paths in a team to understand the overall category structure. Perfect for category organization, cleanup, migration planning, or finding similar categories. Returns a paginated list of paths with post counts, sorted in lexicographic order. Supports filtering (prefix/suffix/match/exact_match) to find categories by pattern.`,inputSchema:O.shape,annotations:{readOnlyHint:!0}},async e=>t(n,k,e)),e.registerTool(`esa_archive_post`,{title:`Archive a post`,description:`Archives a post by moving it to the Archived/ category. If the post is in 'dev/docs', it becomes 'Archived/dev/docs'. Posts without category go to 'Archived'.`,inputSchema:ce.shape,annotations:{destructiveHint:!0}},async e=>t(n,le,e)),e.registerTool(`esa_ship_post`,{title:`Ship a post`,description:`Ships a post by setting wip to false. This marks the post as complete and ready to be published. Use this only when you need to ship without making other changes - if you're also updating title, content, or other fields, use esa_update_post with wip: false instead.`,inputSchema:ue.shape,annotations:{destructiveHint:!0}},async e=>t(n,de,e)),e.registerTool(`esa_duplicate_post`,{title:`Prepare a post for duplication`,description:`Prepares a post for duplication by retrieving its name and body_md content. Returns the name and body_md that can be used with esa_create_post to create a duplicate of the original post.`,inputSchema:fe.shape,annotations:{destructiveHint:!0}},async e=>t(n,pe,e)),e.registerTool(`esa_get_search_options_help`,{title:`Get esa search options documentation`,description:`Get esa search syntax documentation when you need to construct complex
21
- search queries. Use this BEFORE esa_search_posts if you're unsure how to
22
- translate user's search requirements into proper esa query syntax (e.g., date
23
- ranges, tag filters, category searches, advanced operators).`,inputSchema:{},annotations:{readOnlyHint:!0}},async e=>t(n,oe,e)),e.registerTool(`esa_get_markdown_syntax_help`,{title:`Get esa Markdown syntax documentation`,description:`Get esa Markdown and formatting documentation when unsure about syntax.
24
- Use this BEFORE using any tools with *_md parameters (like esa_create_post,
25
- esa_update_post, esa_create_comment, esa_update_comment) if you need
26
- clarification on Markdown syntax, esa-specific extensions, or formatting options.`,inputSchema:{},annotations:{readOnlyHint:!0}},async e=>t(n,$,e)),e.registerTool(`esa_search_help`,{title:`Search esa documentation and help`,description:`Search esa documentation for features, terminology, and specifications.
27
- Use this when users mention esa-specific terms, ask about esa functionality,
28
- or request help with esa workflows that you're not familiar with.`,inputSchema:ae.shape,annotations:{readOnlyHint:!0}},async e=>t(n,se,e)),e.registerTool(`esa_get_attachment`,{title:`Get attachment file from esa`,description:`Retrieves an attachment file from esa with signed URLs. For supported images (JPEG, PNG, GIF, WebP) under 30MB, returns base64-encoded data. For other file types, larger images, or when forceSignedUrl is true, returns signed URLs.`,inputSchema:g.shape,annotations:{readOnlyHint:!0}},async e=>t(n,te,e))}export{me as t};
29
- //# sourceMappingURL=tools-6rPxMwBY.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tools-6rPxMwBY.js","names":[],"sources":["../src/tools/attachments.ts","../src/transformers/category-transformer.ts","../src/tools/categories.ts","../src/transformers/comment-transformer.ts","../src/tools/comments.ts","../src/transformers/post-name-normalizer.ts","../src/tools/posts.ts","../src/transformers/query-normalizer.ts","../src/tools/search.ts","../src/tools/helps.ts","../src/tools/post-actions.ts","../src/tools/index.ts"],"sourcesContent":["import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport { formatToolError } from \"../formatters/mcp-response.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\n\n// Maximum file size for base64 encoding (30MB)\nconst MAX_IMAGE_SIZE = 30 * 1024 * 1024;\n\n// Supported image MIME types for base64 encoding\nconst SUPPORTED_IMAGE_TYPES = [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n] as const;\n\nexport const getAttachmentSchema = createSchemaWithTeamName({\n url: z\n .string()\n .describe(\n \"Attachment URL. Can be a full URL (https://files.esa.io/..., https://dl.esa.io/...) or a path (/uploads/...)\",\n ),\n forceSignedUrl: z\n .boolean()\n .optional()\n .describe(\n \"If true, always return signed URLs instead of base64-encoded images. Default is false.\",\n ),\n});\n\n// Hosts that require signed URLs via the esa API\nconst SIGNED_URL_HOSTS = [\"files.esa.io\", \"dl.esa.io\"];\n\n/**\n * Checks if the URL needs a signed URL or can be fetched directly.\n * URLs from img.esa.io are publicly accessible and don't need signing.\n * Paths (e.g. /uploads/...) and URLs from files.esa.io/dl.esa.io need signing.\n */\nfunction needsSignedUrl(url: string): boolean {\n // Paths always need signed URLs\n if (url.startsWith(\"/\")) {\n return true;\n }\n\n try {\n const urlObj = new URL(url);\n return SIGNED_URL_HOSTS.includes(urlObj.hostname);\n } catch {\n // If URL parsing fails, assume it needs signing\n return true;\n }\n}\n\n/**\n * Extracts the path from a full URL or returns the input if it's already a path\n */\nfunction normalizeUrl(url: string): string {\n // If it's already a path (starts with /), return as-is\n if (url.startsWith(\"/\")) {\n return url;\n }\n\n try {\n // Try to parse as URL and extract pathname\n const urlObj = new URL(url);\n return urlObj.pathname;\n } catch {\n // If URL parsing fails, assume it's already a path\n return url;\n }\n}\n\n/**\n * Checks if the MIME type is a supported image format\n */\nfunction isSupportedImage(mimeType: string): boolean {\n return SUPPORTED_IMAGE_TYPES.includes(\n mimeType as (typeof SUPPORTED_IMAGE_TYPES)[number],\n );\n}\n\n/**\n * Fetches content and returns base64-encoded data if it's a supported image under size limit\n */\nasync function fetchAttachment(\n signedUrl: string,\n forceSignedUrl: boolean,\n): Promise<\n | { type: \"image\"; data: string; mimeType: string }\n | { type: \"text\"; text: string }\n> {\n // If forceSignedUrl is true, always return signed URL\n if (forceSignedUrl) {\n return {\n type: \"text\" as const,\n text: signedUrl,\n };\n }\n\n const response = await fetch(signedUrl);\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch attachment: ${response.status} ${response.statusText}`,\n );\n }\n\n const contentType = response.headers.get(\"content-type\") || \"\";\n const contentLength = response.headers.get(\"content-length\");\n const size = contentLength ? Number.parseInt(contentLength, 10) : 0;\n\n // Check if it's a supported image and within size limit\n if (isSupportedImage(contentType) && size > 0 && size <= MAX_IMAGE_SIZE) {\n const arrayBuffer = await response.arrayBuffer();\n const buffer = Buffer.from(arrayBuffer);\n const base64 = buffer.toString(\"base64\");\n\n return {\n type: \"image\" as const,\n data: base64,\n mimeType: contentType,\n };\n }\n\n // For non-images or oversized images, return the signed URL\n return {\n type: \"text\" as const,\n text: signedUrl,\n };\n}\n\nexport async function getAttachment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getAttachmentSchema>,\n): Promise<CallToolResult> {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n\n const forceSignedUrl = args.forceSignedUrl ?? false;\n\n if (needsSignedUrl(args.url)) {\n // Normalize URL to path for signed URL API\n const normalizedUrl = normalizeUrl(args.url);\n\n // Get signed URL from esa API\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/signed_urls\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n urls: normalizedUrl,\n v: 2,\n expires_in: 300, // 5 minutes\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n if (!data.signed_urls || data.signed_urls.length === 0) {\n throw new Error(\"No signed URLs returned from API\");\n }\n\n const [originalUrl, signedUrl] = data.signed_urls[0];\n\n if (signedUrl === null) {\n throw new Error(`File not found: ${originalUrl}`);\n }\n\n try {\n const result = await fetchAttachment(signedUrl, forceSignedUrl);\n return { content: [result] };\n } catch (err) {\n throw new Error(\n `Failed to fetch attachment for ${originalUrl}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n // URL doesn't need signing (e.g. img.esa.io) - fetch directly\n try {\n const result = await fetchAttachment(args.url, forceSignedUrl);\n return { content: [result] };\n } catch (err) {\n throw new Error(\n `Failed to fetch attachment for ${args.url}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n } catch (err) {\n return formatToolError(err);\n }\n}\n","import type { components } from \"../generated/api-types.js\";\n\nexport function transformCategory(category: components[\"schemas\"][\"Category\"]) {\n return {\n full_name: category.full_name,\n count: category.count,\n has_child: category.has_child || false,\n };\n}\n\nexport function transformCategoryList(\n categoryList: components[\"schemas\"][\"CategoryList\"],\n) {\n return {\n current_category: categoryList.current_category,\n categories: categoryList.categories?.map(transformCategory) || [],\n parent_categories: categoryList.parent_categories?.map(\n (parentCategory) => ({\n current_category: parentCategory.current_category,\n categories: parentCategory.categories?.map(transformCategory) || [],\n }),\n ),\n readme: categoryList.readme,\n no_category: categoryList.no_category\n ? transformCategory(categoryList.no_category)\n : undefined,\n descendant_posts: categoryList.descendant_posts,\n posts: categoryList.posts,\n total_count: categoryList.total_count,\n per_page: categoryList.per_page,\n page: categoryList.page,\n prev_page: categoryList.prev_page,\n next_page: categoryList.next_page,\n max_per_page: categoryList.max_per_page,\n };\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { transformCategoryList } from \"../transformers/category-transformer.js\";\n\nexport const getCategoriesSchema = createSchemaWithTeamName({\n select: z.string().describe(\"Category path to retrieve\"),\n include: z\n .enum([\"posts\", \"parent_categories\"])\n .optional()\n .describe(\"Additional information to include\"),\n descendantPosts: z\n .boolean()\n .optional()\n .describe(\"Include descendant posts (only effective with include=posts)\"),\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getCategories(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getCategoriesSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/categories\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n select: args.select,\n include: args.include,\n descendant_posts: args.descendantPosts,\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const categoryList: components[\"schemas\"][\"CategoryList\"] = data;\n const transformed = transformCategoryList(categoryList);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTopCategoriesSchema = createSchemaWithTeamName({});\n\nexport async function getTopCategories(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTopCategoriesSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/categories/top\",\n {\n params: {\n path: { team_name: args.teamName },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const categoryList: components[\"schemas\"][\"CategoryList\"] = data;\n const transformed = transformCategoryList(categoryList);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getAllCategoryPathsSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n prefix: z\n .string()\n .optional()\n .describe(\n \"Filter paths starting with specified string (e.g., 'dev' finds 'dev', 'dev/api', 'dev/docs')\",\n ),\n suffix: z\n .string()\n .optional()\n .describe(\n \"Filter paths ending with specified string (e.g., 'api' finds 'dev/api', 'backend/api')\",\n ),\n match: z\n .string()\n .optional()\n .describe(\n \"Filter paths containing specified substring anywhere (e.g., 'doc' finds 'docs', 'dev/docs', 'documentation')\",\n ),\n exactMatch: z\n .string()\n .optional()\n .describe(\n \"Filter paths matching exactly (e.g., 'dev/api' matches only 'dev/api', ignores leading/trailing slashes)\",\n ),\n});\n\nexport async function getAllCategoryPaths(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getAllCategoryPathsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/categories/paths\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n v: 2,\n page: args.page,\n per_page: args.perPage,\n prefix: args.prefix,\n suffix: args.suffix,\n match: args.match,\n exact_match: args.exactMatch,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse(data);\n } catch (error) {\n return formatToolError(error);\n }\n}\n","import type { components } from \"../generated/api-types.js\";\n\nexport interface CommentTransformOptions {\n truncateBody?: number;\n}\n\nexport function transformComment(\n comment: components[\"schemas\"][\"Comment\"],\n options: CommentTransformOptions = {},\n) {\n const { truncateBody } = options;\n\n let bodyMd = comment.body_md;\n if (truncateBody && bodyMd && bodyMd.length > truncateBody) {\n bodyMd = `${bodyMd.slice(0, truncateBody)}...`;\n }\n\n return {\n id: comment.id,\n post_number: comment.post_number,\n url: comment.url,\n body_md: bodyMd,\n created_at: comment.created_at,\n updated_at: comment.updated_at,\n created_by: comment.created_by,\n stats: {\n stargazers_count: comment.stargazers_count,\n star: comment.star,\n },\n stargazers: comment.stargazers,\n };\n}\n\nexport type TransformedComment = ReturnType<typeof transformComment>;\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { transformComment } from \"../transformers/comment-transformer.js\";\n\nexport const getCommentSchema = createSchemaWithTeamName({\n commentId: z.number().describe(\"The comment ID to retrieve\"),\n include: z\n .enum([\"stargazers\"])\n .optional()\n .describe(\"Specify 'stargazers' to include stargazers in the response\"),\n});\n\nexport async function getComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/comments/{comment_id}\",\n {\n params: {\n path: { team_name: args.teamName, comment_id: args.commentId },\n query: {\n include: args.include,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comment: components[\"schemas\"][\"Comment\"] = data;\n const transformed = transformComment(comment);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const createCommentSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to comment on\"),\n bodyMd: z.string().describe(\"The comment content in Markdown format\"),\n user: z\n .string()\n .optional()\n .describe(\"Comment author's screen_name (owner permission required)\"),\n});\n\nexport async function createComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof createCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.POST(\n \"/v1/teams/{team_name}/posts/{post_number}/comments\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n },\n body: {\n comment: {\n body_md: args.bodyMd,\n user: args.user,\n } as components[\"schemas\"][\"CommentInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comment: components[\"schemas\"][\"Comment\"] = data;\n const transformed = transformComment(comment, { truncateBody: 300 });\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const updateCommentSchema = createSchemaWithTeamName({\n commentId: z.number().describe(\"The comment ID to update\"),\n bodyMd: z.string().describe(\"The updated comment content in Markdown format\"),\n user: z\n .string()\n .optional()\n .describe(\"Comment author's screen_name (owner permission required)\"),\n});\n\nexport async function updateComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof updateCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.PATCH(\n \"/v1/teams/{team_name}/comments/{comment_id}\",\n {\n params: {\n path: { team_name: args.teamName, comment_id: args.commentId },\n },\n body: {\n comment: {\n body_md: args.bodyMd,\n user: args.user,\n } as components[\"schemas\"][\"CommentInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comment: components[\"schemas\"][\"Comment\"] = data;\n const transformed = transformComment(comment);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const deleteCommentSchema = createSchemaWithTeamName({\n commentId: z.number().describe(\"The comment ID to delete\"),\n});\n\nexport async function deleteComment(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof deleteCommentSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { error, response } = await client.DELETE(\n \"/v1/teams/{team_name}/comments/{comment_id}\",\n {\n params: {\n path: { team_name: args.teamName, comment_id: args.commentId },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n return formatToolResponse({\n success: true,\n message: \"Comment deleted successfully\",\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getPostCommentsSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to get comments for\"),\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getPostComments(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getPostCommentsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}/comments\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n query: {\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comments: components[\"schemas\"][\"Comment\"][] = data.comments;\n const transformed = comments.map((comment) =>\n transformComment(comment, { truncateBody: 300 }),\n );\n\n return formatToolResponse({ ...data, comments: transformed });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const getTeamCommentsSchema = createSchemaWithTeamName({\n page: z.number().optional().describe(\"Page number (starts from 1)\"),\n perPage: z.number().optional().describe(\"Number of items per page\"),\n});\n\nexport async function getTeamComments(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getTeamCommentsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/comments\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n page: args.page,\n per_page: args.perPage,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const comments: components[\"schemas\"][\"Comment\"][] = data.comments;\n const transformed = comments.map((comment) =>\n transformComment(comment, { truncateBody: 300 }),\n );\n\n return formatToolResponse({ ...data, comments: transformed });\n } catch (error) {\n return formatToolError(error);\n }\n}\n","export interface PostNameParts {\n name?: string;\n category?: string;\n}\n\nexport function normalizePostName(\n name?: string,\n category?: string,\n): PostNameParts {\n if (!name) {\n return { name, category };\n }\n\n if (category !== undefined) {\n return { name, category };\n }\n\n if (name.includes(\"/\")) {\n const parts = name.split(\"/\");\n const extractedName = parts.pop();\n const extractedCategory = parts.join(\"/\");\n\n return {\n name: extractedName || undefined,\n category: extractedCategory,\n };\n }\n\n return { name, category };\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { normalizePostName } from \"../transformers/post-name-normalizer.js\";\nimport { transformPost } from \"../transformers/post-transformer.js\";\n\nexport const getPostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to retrieve\"),\n include: z\n .enum([\"comments\"])\n .optional()\n .describe(\"Specify 'comments' to include comments in the response\"),\n});\n\nexport async function getPost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof getPostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n query: {\n include: args.include,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n const post: components[\"schemas\"][\"Post\"] = data;\n const transformed = transformPost(post);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const createPostSchema = createSchemaWithTeamName({\n name: z.string().describe(\"The post name (title)\"),\n bodyMd: z.string().optional().describe(\"The post content in Markdown format\"),\n tags: z.array(z.string()).optional().describe(\"Tags for the post\"),\n category: z.string().optional().describe(\"Category path (e.g., 'dev/docs')\"),\n wip: z\n .boolean()\n .default(true)\n .describe(\n \"Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)\",\n ),\n message: z.string().optional().describe(\"Update message for the post\"),\n});\n\nexport async function createPost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof createPostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { name, category } = normalizePostName(args.name, args.category);\n\n const { data, error, response } = await client.POST(\n \"/v1/teams/{team_name}/posts\",\n {\n params: {\n path: { team_name: args.teamName },\n },\n body: {\n post: {\n name: name,\n body_md: args.bodyMd,\n tags: args.tags,\n category: category,\n wip: args.wip,\n message: args.message,\n } as components[\"schemas\"][\"PostCreateInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n const transformed = transformPost(post);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const updatePostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to update\"),\n name: z.string().optional().describe(\"The post name (title)\"),\n bodyMd: z.string().optional().describe(\"The post content in Markdown format\"),\n tags: z.array(z.string()).optional().describe(\"Tags for the post\"),\n category: z.string().optional().describe(\"Category path (e.g., 'dev/docs')\"),\n wip: z\n .boolean()\n .optional()\n .describe(\n \"Whether the post is Work In Progress. Set to false to ship it (mark as complete and ready to be published)\",\n ),\n message: z.string().optional().describe(\"Update message for the post\"),\n originalRevision: z\n .object({\n bodyMd: z.string(),\n number: z.number(),\n user: z.string(),\n })\n .optional()\n .describe(\"Original revision to check for conflicts\"),\n});\n\nexport async function updatePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof updatePostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { name, category } = normalizePostName(args.name, args.category);\n\n const { data, error, response } = await client.PATCH(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n },\n body: {\n post: {\n name: name,\n body_md: args.bodyMd,\n tags: args.tags,\n category: category,\n wip: args.wip,\n message: args.message,\n original_revision: args.originalRevision\n ? {\n body_md: args.originalRevision.bodyMd,\n number: args.originalRevision.number,\n user: args.originalRevision.user,\n }\n : undefined,\n } as components[\"schemas\"][\"PostUpdateInput\"],\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n const transformed = transformPost(post);\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n","export function normalizeSearchQuery(query: string): string {\n let normalized = query;\n\n // Convert wildcard \"*\" to empty string (for \"all posts\" queries)\n if (normalized === \"*\") {\n normalized = \"\";\n }\n\n // Convert common date syntax patterns from other services to esa format\n // GitHub/Gmail style: after:YYYY-MM-DD -> created:>YYYY-MM-DD\n normalized = normalized.replace(\n /\\bafter:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:>$1\",\n );\n\n // GitHub/Gmail style: before:YYYY-MM-DD -> created:<YYYY-MM-DD\n normalized = normalized.replace(\n /\\bbefore:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:<$1\",\n );\n\n // Alternative patterns that might be used\n // since:YYYY-MM-DD -> created:>YYYY-MM-DD\n normalized = normalized.replace(\n /\\bsince:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:>$1\",\n );\n\n // until:YYYY-MM-DD -> created:<YYYY-MM-DD\n normalized = normalized.replace(\n /\\buntil:(\\d{4}-\\d{2}-\\d{2})\\b/gi,\n \"created:<$1\",\n );\n\n return normalized;\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { transformPost } from \"../transformers/post-transformer.js\";\nimport { normalizeSearchQuery } from \"../transformers/query-normalizer.js\";\n\nexport const searchPostsSchema = createSchemaWithTeamName({\n query: z\n .string()\n .describe(`Search query string. Use specific terms, not wildcards like \"*\". Empty string returns all posts.\n## Important Note for Date Queries:\n**WARNING: Do NOT use 'after:', 'before:', 'since:', or 'until:' syntax (these are from GitHub/Gmail/pplog).\nUse esa-specific date syntax: created:>YYYY-MM-DD, created:<YYYY-MM-DD, updated:>YYYY-MM-DD, updated:<YYYY-MM-DD\n\n## Important Note for Relative Date Queries:\n**CRITICAL: Always get today's actual date from the system before processing\nrelative date queries (e.g., \"today\", \"yesterday\", \"last week\", \"recent\").\nWhen searching, apply these strategies:\n1. Convert concepts to technical terms (e.g., general descriptions → specific property names, method names, or technical keywords)\n2. Translate between Japanese and English technical terms (e.g., Japanese concepts → English API/property names)\n3. Expand to related technical elements (e.g., one concept → multiple implementation approaches, related technologies, or alternative solutions)\nIMPORTANT: Space-separated terms are treated as AND conditions. Use \"OR\" operator for alternative terms: \"word-break OR word-wrap OR overflow-wrap\".\nAdvanced search: \"tag:release\", \"category:dev\", \"wip:false\", \"keyword:API\", \"title:設計書\".\nCategory search: \"on:category\" (posts directly in category), \"in:category\" (posts in category and subcategories), \"on:/\" (uncategorized posts).\nFor broader results, use OR between related terms rather than listing them with spaces.`)\n .transform(normalizeSearchQuery),\n sort: z\n .enum([\n \"updated\",\n \"created\",\n \"number\",\n \"stars\",\n \"watches\",\n \"comments\",\n \"best_match\",\n ])\n .optional()\n .describe(\"Sort key\"),\n order: z.enum([\"desc\", \"asc\"]).optional().describe(\"Sort direction\"),\n page: z.number().int().positive().optional().describe(\"Page number\"),\n perPage: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe(\"Items per page\"),\n include: z\n .enum([\"comments\"])\n .optional()\n .describe(\"Specify 'comments' to include comments in the response\"),\n});\n\nexport async function searchPosts(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof searchPostsSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n q: args.query,\n sort: args.sort,\n order: args.order,\n page: args.page,\n per_page: args.perPage,\n include: args.include,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n const posts: components[\"schemas\"][\"Post\"][] = data.posts;\n const transformed = posts.map((post) =>\n transformPost(post, { truncateBody: 500 }),\n );\n\n if (posts.length === 0 && isAndQuery(args.query)) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: generateOrSearchSuggestion(args.query),\n },\n ],\n };\n }\n\n return formatToolResponse(transformed);\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nfunction isAndQuery(query: string): boolean {\n if (!query || query.trim() === \"\") {\n return false;\n }\n\n if (/\\bOR\\b/.test(query) || query.includes(\"|\")) {\n return false;\n }\n\n const tokens = query.split(/\\s+/).filter(Boolean);\n return tokens.length >= 2;\n}\n\nfunction generateOrSearchSuggestion(query: string): string {\n const tokens = query.split(/\\s+/).filter(Boolean);\n const orQuery = tokens.join(\" OR \");\n return `---\nNo results found. Your query uses AND conditions (space-separated terms).\nSuggestions:\n- Try OR search: \"${orQuery}\"\n- Omit some keywords from your query`;\n}\n","import type { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { getPost } from \"./posts.js\";\nimport { searchPosts, searchPostsSchema } from \"./search.js\";\n\n// Documentation team and post constants\nexport const HELP_DOCS = {\n TEAM: \"docs\",\n SEARCH_OPTIONS_POST_ID: 104,\n MARKDOWN_SYNTAX_POST_ID: 49,\n} as const;\n\n// Schema for searchHelp - omit teamName, order, include, and sort from searchPostsSchema\nexport const searchHelpSchema = searchPostsSchema.omit({\n teamName: true,\n order: true,\n include: true,\n sort: true,\n});\n\nexport async function getSearchOptionsHelp(\n client: ReturnType<typeof createEsaClient>,\n _args: Record<string, never>,\n) {\n return getPost(client, {\n teamName: HELP_DOCS.TEAM,\n postNumber: HELP_DOCS.SEARCH_OPTIONS_POST_ID,\n });\n}\n\nexport async function getMarkdownSyntaxHelp(\n client: ReturnType<typeof createEsaClient>,\n _args: Record<string, never>,\n) {\n return getPost(client, {\n teamName: HELP_DOCS.TEAM,\n postNumber: HELP_DOCS.MARKDOWN_SYNTAX_POST_ID,\n });\n}\n\nexport async function searchHelp(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof searchHelpSchema>,\n) {\n return searchPosts(client, {\n teamName: HELP_DOCS.TEAM,\n sort: \"best_match\",\n ...args,\n });\n}\n","import { z } from \"zod\";\nimport type { createEsaClient } from \"../api_client/index.js\";\nimport { MissingTeamNameError } from \"../errors/missing-team-name-error.js\";\nimport {\n formatToolError,\n formatToolResponse,\n} from \"../formatters/mcp-response.js\";\nimport type { components } from \"../generated/api-types.js\";\nimport { createSchemaWithTeamName } from \"../schemas/team-name-schema.js\";\nimport { normalizeTeamName } from \"../transformers/team-name-normalizer.js\";\nimport { createPost, updatePost } from \"./posts.js\";\n\nexport const archivePostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to archive\"),\n message: z.string().optional().describe(\"Archive message for the post\"),\n});\n\nexport async function archivePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof archivePostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/{post_number}\",\n {\n params: {\n path: { team_name: args.teamName, post_number: args.postNumber },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const post: components[\"schemas\"][\"Post\"] = data;\n const currentCategory = post.category || \"\";\n\n if (currentCategory.startsWith(\"Archived/\")) {\n return formatToolResponse({\n message: \"Post is already archived\",\n category: currentCategory,\n });\n }\n\n const archivedCategory =\n currentCategory === \"\" ? \"Archived\" : `Archived/${currentCategory}`;\n\n return await updatePost(client, {\n teamName: args.teamName,\n postNumber: args.postNumber,\n category: archivedCategory,\n message: args.message || \"Archive post\",\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const shipPostSchema = createSchemaWithTeamName({\n postNumber: z.number().describe(\"The post number to ship\"),\n});\n\nexport async function shipPost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof shipPostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n return await updatePost(client, {\n teamName: args.teamName,\n postNumber: args.postNumber,\n wip: false,\n message: \"Ship It!\",\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n\nexport const duplicatePostSchema = createSchemaWithTeamName({\n postNumber: z\n .number()\n .describe(\"The source post number to prepare for duplication\"),\n targetTeamName: z\n .string()\n .optional()\n .describe(\"The name of the esa team\")\n .transform((val) => (val ? normalizeTeamName(val) : undefined)),\n});\n\nexport async function duplicatePost(\n client: ReturnType<typeof createEsaClient>,\n args: z.infer<typeof duplicatePostSchema>,\n) {\n try {\n if (!args.teamName) {\n throw new MissingTeamNameError();\n }\n const { data, error, response } = await client.GET(\n \"/v1/teams/{team_name}/posts/new\",\n {\n params: {\n path: { team_name: args.teamName },\n query: {\n parent_post_id: args.postNumber,\n },\n },\n },\n );\n\n if (error || !response.ok) {\n return formatToolError(error || response.status);\n }\n\n const postNew = data.post as components[\"schemas\"][\"PostNew\"];\n\n return createPost(client, {\n teamName: args.targetTeamName || args.teamName,\n name: postNew.name,\n bodyMd: postNew.body_md,\n wip: true,\n });\n } catch (error) {\n return formatToolError(error);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { z } from \"zod\";\nimport { withContext } from \"../api_client/with-context.js\";\nimport type { MCPContext } from \"../context/mcp-context.js\";\nimport { getAttachment, getAttachmentSchema } from \"./attachments.js\";\nimport {\n getAllCategoryPaths,\n getAllCategoryPathsSchema,\n getCategories,\n getCategoriesSchema,\n getTopCategories,\n getTopCategoriesSchema,\n} from \"./categories.js\";\nimport {\n createComment,\n createCommentSchema,\n deleteComment,\n deleteCommentSchema,\n getComment,\n getCommentSchema,\n getPostComments,\n getPostCommentsSchema,\n getTeamComments,\n getTeamCommentsSchema,\n updateComment,\n updateCommentSchema,\n} from \"./comments.js\";\nimport {\n getMarkdownSyntaxHelp,\n getSearchOptionsHelp,\n searchHelp,\n searchHelpSchema,\n} from \"./helps.js\";\nimport {\n archivePost,\n archivePostSchema,\n duplicatePost,\n duplicatePostSchema,\n shipPost,\n shipPostSchema,\n} from \"./post-actions.js\";\nimport {\n createPost,\n createPostSchema,\n getPost,\n getPostSchema,\n updatePost,\n updatePostSchema,\n} from \"./posts.js\";\nimport { searchPosts, searchPostsSchema } from \"./search.js\";\nimport {\n getTeamMembers,\n getTeamMembersSchema,\n getTeamStats,\n getTeamStatsSchema,\n getTeams,\n getTeamsSchema,\n getTeamTags,\n getTeamTagsSchema,\n} from \"./teams.js\";\n\nexport function setupTools(server: McpServer, context: MCPContext): void {\n console.error(\"Setting up MCP tools...\");\n\n server.registerTool(\n \"esa_get_teams\",\n {\n title: \"Get user's accessible esa teams\",\n description: \"Retrieves a list of esa teams that the user has access to.\",\n inputSchema: getTeamsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamsSchema>) =>\n withContext(context, getTeams, params),\n );\n\n server.registerTool(\n \"esa_get_team_stats\",\n {\n title: \"Get team statistics\",\n description:\n \"Retrieves team statistics including member count, posts count (total/WIP/shipped), comments, stars, watches, and daily/weekly/monthly active users\",\n inputSchema: getTeamStatsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamStatsSchema>) =>\n withContext(context, getTeamStats, params),\n );\n\n server.registerTool(\n \"esa_get_team_tags\",\n {\n title: \"Get team tags\",\n description:\n \"Retrieves all tags used in posts within a team, along with the count of posts for each tag\",\n inputSchema: getTeamTagsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamTagsSchema>) =>\n withContext(context, getTeamTags, params),\n );\n\n server.registerTool(\n \"esa_get_team_members\",\n {\n title: \"Get team members\",\n description:\n \"Retrieves all members of a team with their roles and profile information\",\n inputSchema: getTeamMembersSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamMembersSchema>) =>\n withContext(context, getTeamMembers, params),\n );\n\n server.registerTool(\n \"esa_get_post\",\n {\n title: \"Get a specific esa post\",\n description:\n \"Retrieves a specific post from an esa team by post number, with optional comments included.\",\n inputSchema: getPostSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getPostSchema>) =>\n withContext(context, getPost, params),\n );\n\n server.registerTool(\n \"esa_search_posts\",\n {\n title: \"Search Posts\",\n description: \"Search for posts in esa.io\",\n inputSchema: searchPostsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof searchPostsSchema>) =>\n withContext(context, searchPosts, params),\n );\n\n server.registerTool(\n \"esa_create_post\",\n {\n title: \"Create a new esa post\",\n description:\n \"Creates a new post in an esa team with optional tags, category, and WIP status.\",\n inputSchema: createPostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof createPostSchema>) =>\n withContext(context, createPost, params),\n );\n\n server.registerTool(\n \"esa_update_post\",\n {\n title: \"Update an existing esa post\",\n description:\n \"Updates an existing post in an esa team by post number. You can update the title, content, tags, category, and WIP status. To ship a post (mark as complete), set wip to false - this is preferred over using esa_ship_post when updating other fields simultaneously.\",\n inputSchema: updatePostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof updatePostSchema>) =>\n withContext(context, updatePost, params),\n );\n\n server.registerTool(\n \"esa_get_comment\",\n {\n title: \"Get a specific comment\",\n description:\n \"Retrieves a specific comment by comment ID, with optional stargazers included.\",\n inputSchema: getCommentSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getCommentSchema>) =>\n withContext(context, getComment, params),\n );\n\n server.registerTool(\n \"esa_create_comment\",\n {\n title: \"Create a new comment on a post\",\n description: \"Creates a new comment on an existing post in an esa team.\",\n inputSchema: createCommentSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof createCommentSchema>) =>\n withContext(context, createComment, params),\n );\n\n server.registerTool(\n \"esa_update_comment\",\n {\n title: \"Update an existing comment\",\n description: \"Updates an existing comment in an esa team by comment ID.\",\n inputSchema: updateCommentSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof updateCommentSchema>) =>\n withContext(context, updateComment, params),\n );\n\n server.registerTool(\n \"esa_delete_comment\",\n {\n title: \"Delete a comment\",\n description: \"Deletes a comment from an esa team by comment ID.\",\n inputSchema: deleteCommentSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof deleteCommentSchema>) =>\n withContext(context, deleteComment, params),\n );\n\n server.registerTool(\n \"esa_get_post_comments\",\n {\n title: \"Get comments for a specific post\",\n description:\n \"Retrieves a list of comments for a specific post with pagination support.\",\n inputSchema: getPostCommentsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getPostCommentsSchema>) =>\n withContext(context, getPostComments, params),\n );\n\n server.registerTool(\n \"esa_get_team_comments\",\n {\n title: \"Get team comments\",\n description:\n \"Retrieves a list of comments in a team with pagination support.\",\n inputSchema: getTeamCommentsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTeamCommentsSchema>) =>\n withContext(context, getTeamComments, params),\n );\n\n server.registerTool(\n \"esa_get_categories\",\n {\n title: \"Get categories for a specific path\",\n description:\n \"Retrieves category information and subcategories for a specific category path, with optional posts and parent categories included\",\n inputSchema: getCategoriesSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getCategoriesSchema>) =>\n withContext(context, getCategories, params),\n );\n\n server.registerTool(\n \"esa_get_top_categories\",\n {\n title: \"Get top-level categories\",\n description: \"Retrieves all top-level categories for a team\",\n inputSchema: getTopCategoriesSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getTopCategoriesSchema>) =>\n withContext(context, getTopCategories, params),\n );\n\n server.registerTool(\n \"esa_get_all_category_paths\",\n {\n title: \"Get category paths with pagination\",\n description:\n \"Retrieves category paths in a team to understand the overall category structure. Perfect for category organization, cleanup, migration planning, or finding similar categories. Returns a paginated list of paths with post counts, sorted in lexicographic order. Supports filtering (prefix/suffix/match/exact_match) to find categories by pattern.\",\n inputSchema: getAllCategoryPathsSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getAllCategoryPathsSchema>) =>\n withContext(context, getAllCategoryPaths, params),\n );\n\n server.registerTool(\n \"esa_archive_post\",\n {\n title: \"Archive a post\",\n description:\n \"Archives a post by moving it to the Archived/ category. If the post is in 'dev/docs', it becomes 'Archived/dev/docs'. Posts without category go to 'Archived'.\",\n inputSchema: archivePostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof archivePostSchema>) =>\n withContext(context, archivePost, params),\n );\n\n server.registerTool(\n \"esa_ship_post\",\n {\n title: \"Ship a post\",\n description:\n \"Ships a post by setting wip to false. This marks the post as complete and ready to be published. Use this only when you need to ship without making other changes - if you're also updating title, content, or other fields, use esa_update_post with wip: false instead.\",\n inputSchema: shipPostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof shipPostSchema>) =>\n withContext(context, shipPost, params),\n );\n\n server.registerTool(\n \"esa_duplicate_post\",\n {\n title: \"Prepare a post for duplication\",\n description:\n \"Prepares a post for duplication by retrieving its name and body_md content. Returns the name and body_md that can be used with esa_create_post to create a duplicate of the original post.\",\n inputSchema: duplicatePostSchema.shape,\n annotations: {\n destructiveHint: true,\n },\n },\n async (params: z.infer<typeof duplicatePostSchema>) =>\n withContext(context, duplicatePost, params),\n );\n\n server.registerTool(\n \"esa_get_search_options_help\",\n {\n title: \"Get esa search options documentation\",\n description: `Get esa search syntax documentation when you need to construct complex\nsearch queries. Use this BEFORE esa_search_posts if you're unsure how to\ntranslate user's search requirements into proper esa query syntax (e.g., date\nranges, tag filters, category searches, advanced operators).`,\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: Record<string, never>) =>\n withContext(context, getSearchOptionsHelp, params),\n );\n\n server.registerTool(\n \"esa_get_markdown_syntax_help\",\n {\n title: \"Get esa Markdown syntax documentation\",\n description: `Get esa Markdown and formatting documentation when unsure about syntax.\nUse this BEFORE using any tools with *_md parameters (like esa_create_post,\nesa_update_post, esa_create_comment, esa_update_comment) if you need\nclarification on Markdown syntax, esa-specific extensions, or formatting options.`,\n inputSchema: {},\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: Record<string, never>) =>\n withContext(context, getMarkdownSyntaxHelp, params),\n );\n\n server.registerTool(\n \"esa_search_help\",\n {\n title: \"Search esa documentation and help\",\n description: `Search esa documentation for features, terminology, and specifications.\nUse this when users mention esa-specific terms, ask about esa functionality,\nor request help with esa workflows that you're not familiar with.`,\n inputSchema: searchHelpSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof searchHelpSchema>) =>\n withContext(context, searchHelp, params),\n );\n\n server.registerTool(\n \"esa_get_attachment\",\n {\n title: \"Get attachment file from esa\",\n description:\n \"Retrieves an attachment file from esa with signed URLs. For supported images (JPEG, PNG, GIF, WebP) under 30MB, returns base64-encoded data. For other file types, larger images, or when forceSignedUrl is true, returns signed URLs.\",\n inputSchema: getAttachmentSchema.shape,\n annotations: {\n readOnlyHint: true,\n },\n },\n async (params: z.infer<typeof getAttachmentSchema>) =>\n withContext(context, getAttachment, params),\n );\n}\n"],"mappings":"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,EAAY,EAAO,CAC3C,CAED,EAAO,aACL,kBACA,CACE,MAAO,8BACP,YACE,yQACF,YAAa,EAAiB,MAC9B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAY,EAAO,CAC3C,CAED,EAAO,aACL,kBACA,CACE,MAAO,yBACP,YACE,iFACF,YAAa,EAAiB,MAC9B,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAY,EAAO,CAC3C,CAED,EAAO,aACL,qBACA,CACE,MAAO,iCACP,YAAa,4DACb,YAAa,EAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,qBACA,CACE,MAAO,6BACP,YAAa,4DACb,YAAa,EAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,qBACA,CACE,MAAO,mBACP,YAAa,oDACb,YAAa,EAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,wBACA,CACE,MAAO,mCACP,YACE,4EACF,YAAa,EAAsB,MACnC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAiB,EAAO,CAChD,CAED,EAAO,aACL,wBACA,CACE,MAAO,oBACP,YACE,kEACF,YAAa,EAAsB,MACnC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAiB,EAAO,CAChD,CAED,EAAO,aACL,qBACA,CACE,MAAO,qCACP,YACE,oIACF,YAAa,EAAoB,MACjC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAe,EAAO,CAC9C,CAED,EAAO,aACL,yBACA,CACE,MAAO,2BACP,YAAa,gDACb,YAAa,EAAuB,MACpC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAkB,EAAO,CACjD,CAED,EAAO,aACL,6BACA,CACE,MAAO,qCACP,YACE,yVACF,YAAa,EAA0B,MACvC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAqB,EAAO,CACpD,CAED,EAAO,aACL,mBACA,CACE,MAAO,iBACP,YACE,iKACF,YAAa,GAAkB,MAC/B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAa,EAAO,CAC5C,CAED,EAAO,aACL,gBACA,CACE,MAAO,cACP,YACE,4QACF,YAAa,GAAe,MAC5B,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAU,EAAO,CACzC,CAED,EAAO,aACL,qBACA,CACE,MAAO,iCACP,YACE,6LACF,YAAa,GAAoB,MACjC,YAAa,CACX,gBAAiB,GAClB,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAe,EAAO,CAC9C,CAED,EAAO,aACL,8BACA,CACE,MAAO,uCACP,YAAa;;;8DAIb,YAAa,EAAE,CACf,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAsB,EAAO,CACrD,CAED,EAAO,aACL,+BACA,CACE,MAAO,wCACP,YAAa;;;mFAIb,YAAa,EAAE,CACf,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,EAAuB,EAAO,CACtD,CAED,EAAO,aACL,kBACA,CACE,MAAO,oCACP,YAAa;;mEAGb,YAAa,GAAiB,MAC9B,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAY,EAAO,CAC3C,CAED,EAAO,aACL,qBACA,CACE,MAAO,+BACP,YACE,yOACF,YAAa,EAAoB,MACjC,YAAa,CACX,aAAc,GACf,CACF,CACD,KAAO,IACL,EAAY,EAAS,GAAe,EAAO,CAC9C"}