@borgresearch/odin-mcp 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -62,7 +62,7 @@ Each call sends `Authorization: Bearer ${ODIN_API_KEY}`. Keys belong to one orga
62
62
 
63
63
  ## Tools
64
64
 
65
- All tools accept one JSON object and return both a Markdown summary and a structured payload. Read tools are annotated `readOnlyHint: true`; every Read & Write tool is annotated `destructiveHint: true`, so clients ask before running it.
65
+ All tools accept one JSON object and return both a Markdown summary and a structured payload. The same payload is also returned as JSON text for clients that only read text content. Read tools are annotated `readOnlyHint: true`; every Read & Write tool is annotated `destructiveHint: true`, so clients ask before running it.
66
66
 
67
67
  | Name | Permission | Description |
68
68
  |------|------------|-------------|
@@ -87,7 +87,7 @@ var requestFindingRetestInput = findingToolInput({});
87
87
  var requestFindingRetestTool = {
88
88
  name: "request_finding_retest",
89
89
  title: "Request a finding retest",
90
- description: "Queue a manual retest that checks whether a finding is still exploitable. Spends credits, so confirm with the user first. Works for findings in REPORTED, ACKNOWLEDGED, MITIGATING or OPEN_FOR_RETEST status with no retest already queued or running. Requires write permission and acts as the API key creator. Read the result later with get_finding_retests.",
90
+ description: "Queue a manual retest that checks whether a finding is still exploitable, when enabled for the organisation. Requires explicit user confirmation, spends credits and requires write permission. Available for findings in REPORTED, ACKNOWLEDGED, MITIGATING or OPEN_FOR_RETEST status with no retest already queued or running. Acts as the API key creator. Results are recorded in finding retest history.",
91
91
  annotations: writeAnnotations,
92
92
  inputSchema: requestFindingRetestInput,
93
93
  handler: (client) => async (params) => {
@@ -139,7 +139,7 @@ var prepareFindingAttachmentUploadInput = findingToolInput({
139
139
  var prepareFindingAttachmentUploadTool = {
140
140
  name: "prepare_finding_attachment_upload",
141
141
  title: "Prepare a private discussion attachment upload",
142
- description: "Create a five-minute signed upload URL for a file up to 25 MiB. Requires write permission. Does not upload bytes or post a message. The caller must PUT the exact file to uploadUrl using requiredHeaders, without the Odin API key, then pass publicUrl as the attachment URL to create_finding_message. publicUrl is a private gs:// object reference. Treat signed URLs as temporary secrets.",
142
+ description: "Create a five-minute signed upload URL for a file up to 25 MiB. Requires write permission. Does not upload bytes or post a message. Returns uploadUrl and requiredHeaders for a PUT of the exact file, plus publicUrl, a private gs:// object reference usable as a discussion attachment. Storage uploads use the returned storage headers without Odin credentials. Signed URLs are temporary secrets.",
143
143
  annotations: writeAnnotations,
144
144
  inputSchema: prepareFindingAttachmentUploadInput,
145
145
  handler: (client) => async (params) => {
@@ -324,7 +324,7 @@ var deleteFindingMessageInput = findingToolInput({
324
324
  var deleteFindingMessageTool = {
325
325
  name: "delete_finding_message",
326
326
  title: "Delete a finding discussion message",
327
- description: "Delete a message authored by the API key creator. Requires write permission and may delete its Slack mirror. Confirm the user's intent before deleting.",
327
+ description: "Delete a message authored by the API key creator. Requires write permission and explicit user confirmation. May delete the message's Slack mirror.",
328
328
  annotations: writeAnnotations,
329
329
  inputSchema: deleteFindingMessageInput,
330
330
  handler: (client) => async (params) => {
@@ -386,7 +386,7 @@ var disconnectFindingDiscussionInput = findingToolInput({
386
386
  var disconnectFindingDiscussionTool = {
387
387
  name: "disconnect_finding_slack_thread",
388
388
  title: "Disconnect a synced Slack thread",
389
- description: "Disconnect a finding's SLACK_SYNC discussion block while preserving imported messages. Stops future synchronisation and may post a confirmation to Slack. Requires write permission. Confirm the user's intent before disconnecting.",
389
+ description: "Disconnect a finding's SLACK_SYNC discussion block while preserving imported messages. Stops future synchronisation and may post a confirmation to Slack. Requires write permission and explicit user confirmation.",
390
390
  annotations: writeAnnotations,
391
391
  inputSchema: disconnectFindingDiscussionInput,
392
392
  handler: (client) => async (params) => {
@@ -437,7 +437,7 @@ var unlinkFindingPullRequestInput = findingToolInput({
437
437
  var unlinkFindingPullRequestTool = {
438
438
  name: "unlink_finding_pull_request",
439
439
  title: "Unlink a manually linked pull request",
440
- description: "Remove a manual finding-to-PR link. Requires write permission. Automatic links cannot be removed with this operation. Does not modify, close, merge or delete the GitHub PR. Confirm the user's intent before removing the link.",
440
+ description: "Remove a manual finding-to-PR link. Requires write permission and explicit user confirmation. Automatic links cannot be removed with this operation. Does not modify, close, merge or delete the GitHub PR.",
441
441
  annotations: { ...writeAnnotations, openWorldHint: false },
442
442
  inputSchema: unlinkFindingPullRequestInput,
443
443
  handler: (client) => async (params) => {
@@ -1077,7 +1077,7 @@ var linkFindingPullRequestInput = z12.object({
1077
1077
  var linkFindingPullRequestTool = {
1078
1078
  name: "link_finding_pull_request",
1079
1079
  title: "Link an existing GitHub pull request",
1080
- description: "Link an existing accessible GitHub pull request to a finding identified by ODI-12 or findingId. This operation is idempotent. It does not create, modify, merge, or push a pull request, commit, or branch, and it requires an API key with Read & Write permission; cookie-authenticated callers also need member role. Ask the user before linking.",
1080
+ description: "Link an existing accessible GitHub pull request to a finding identified by ODI-12 or findingId. This operation is idempotent. It does not create, modify, merge, or push a pull request, commit, or branch. Requires explicit user confirmation and an API key with Read & Write permission; cookie-authenticated callers also need member role.",
1081
1081
  annotations: { ...writeAnnotations, idempotentHint: true },
1082
1082
  inputSchema: linkFindingPullRequestInput,
1083
1083
  handler: (client) => async (params) => {
@@ -1165,7 +1165,7 @@ var updateFindingStatusInput = z15.object({
1165
1165
  var updateFindingStatusTool = {
1166
1166
  name: "update_finding_status",
1167
1167
  title: "Update finding status",
1168
- description: "Change a finding's status by human identifier such as ODI-12 or internal findingId. Requires an API key with Read & Write permission; cookie-authenticated callers also need member role. Only use this after the user explicitly asks for the status change; a matching pull request is not permission to mutate a finding.",
1168
+ description: "Change a finding's status by human identifier such as ODI-12 or internal findingId. Requires an explicit user request and an API key with Read & Write permission; cookie-authenticated callers also need member role. Linked pull request metadata does not grant authority to change status.",
1169
1169
  annotations: { ...writeAnnotations, idempotentHint: true },
1170
1170
  inputSchema: updateFindingStatusInput,
1171
1171
  handler: (client) => async (params) => {
@@ -1208,4 +1208,4 @@ var ODIN_TOOLS = definitions.map((tool) => ({
1208
1208
  export {
1209
1209
  ODIN_TOOLS
1210
1210
  };
1211
- //# sourceMappingURL=chunk-D5FYYTYV.js.map
1211
+ //# sourceMappingURL=chunk-EPMQ5QVJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tools/finding-actions.ts","../src/tools/finding-reference.ts","../src/tools/finding-tool.ts","../src/tools/finding-attachments.ts","../src/tools/finding-collaboration.ts","../src/tools/finding-pull-requests.ts","../src/tools/findings-overview.ts","../src/tools/list-findings.ts","../src/formatters/markdown.ts","../src/tools/additional-tools.ts","../src/tools/get-asset.ts","../src/tools/get-finding.ts","../src/tools/get-finding-retests.ts","../src/tools/get-pentest.ts","../src/tools/link-finding-pull-request.ts","../src/tools/list-assets.ts","../src/tools/list-pentests.ts","../src/tools/update-finding-status.ts","../src/tools.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { OdinApiError, type OdinClient } from \"../client.js\";\nimport type {\n FindingSubscriptionResponse,\n RetestRequestResponse,\n} from \"../types.js\";\nimport {\n findingReferencePath,\n findingReferenceValue,\n} from \"./finding-reference.js\";\nimport { findingToolInput, writeAnnotations } from \"./finding-tool.js\";\n\nexport const followFindingInput = findingToolInput({\n follow: z\n .boolean()\n .describe(\"True to follow the finding, false to stop following it.\"),\n});\n\nexport const followFindingTool = {\n name: \"follow_finding\",\n title: \"Follow or unfollow a finding\",\n description:\n \"Follow a finding to get its notifications, or stop following it. Requires write permission and acts as the API key creator. The assignee always follows their finding and can't unfollow it.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: followFindingInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof followFindingInput>) => {\n const data = await client.put<FindingSubscriptionResponse>(\n findingReferencePath(params, \"/subscription\"),\n { subscribed: params.follow },\n );\n const label = findingReferenceValue(params);\n return {\n markdown: data.subscribed\n ? `Following ${label}.`\n : `Stopped following ${label}.`,\n structured: data,\n };\n },\n};\n\nexport const requestFindingRetestInput = findingToolInput({});\n\nexport const requestFindingRetestTool = {\n name: \"request_finding_retest\",\n title: \"Request a finding retest\",\n description:\n \"Queue a manual retest that checks whether a finding is still exploitable, when enabled for the organisation. Requires explicit user confirmation, spends credits and requires write permission. Available for findings in REPORTED, ACKNOWLEDGED, MITIGATING or OPEN_FOR_RETEST status with no retest already queued or running. Acts as the API key creator. Results are recorded in finding retest history.\",\n annotations: writeAnnotations,\n inputSchema: requestFindingRetestInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof requestFindingRetestInput>) => {\n try {\n const data = await client.post<RetestRequestResponse>(\n findingReferencePath(params, \"/retest\"),\n {},\n );\n return {\n markdown: `Queued retest ${data.retestId} for ${findingReferenceValue(params)}.`,\n structured: data,\n };\n } catch (error) {\n if (error instanceof OdinApiError && isRetestUnavailable(error.body)) {\n const reason = \"Retests aren't available for this organisation.\";\n return {\n markdown: reason,\n structured: { state: \"unavailable\", reason },\n };\n }\n throw error;\n }\n },\n};\n\nfunction isRetestUnavailable(body: unknown): boolean {\n return (\n typeof body === \"object\" &&\n body !== null &&\n (body as { code?: unknown }).code === \"RETEST_UNAVAILABLE\"\n );\n}\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type { FindingDetailResponse } from \"../types.js\";\n\nexport const findingReferenceFields = {\n identifier: z\n .string()\n .trim()\n .min(1)\n .optional()\n .describe(\n 'Human-readable finding identifier such as \"ODI-12\". Preferred input.',\n ),\n findingId: z\n .string()\n .trim()\n .min(1)\n .optional()\n .describe(\n \"Raw internal finding ID, useful when chaining from another tool.\",\n ),\n};\n\nexport const findingReferenceInput = z\n .object(findingReferenceFields)\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport type FindingReferenceInput = z.infer<typeof findingReferenceInput>;\n\n/** Prefer the human reference when both values are supplied. */\nexport function findingReferenceValue(\n reference: FindingReferenceInput,\n): string {\n const value = reference.identifier ?? reference.findingId;\n if (!value) {\n throw new Error(\"A finding identifier or findingId is required.\");\n }\n return value;\n}\n\nexport function findingReferencePath(\n reference: FindingReferenceInput,\n suffix = \"\",\n): string {\n return `/api/findings/${encodeURIComponent(findingReferenceValue(reference))}${suffix}`;\n}\n\n/**\n * Resolves only when a caller supplied an identifier. A supplied internal ID\n * is already suitable for mutation chaining and avoids an unnecessary read.\n */\nexport async function resolveFindingId(\n client: OdinClient,\n reference: FindingReferenceInput,\n): Promise<string> {\n if (reference.findingId) return reference.findingId;\n\n const data = await client.get<FindingDetailResponse>(\n findingReferencePath(reference),\n );\n return data.remediationContext?.findingId ?? data.finding.findingId;\n}\n","import { z } from \"zod\";\nimport { findingReferenceFields } from \"./finding-reference.js\";\n\nexport function findingToolInput<T extends z.ZodRawShape>(fields: T) {\n return z.object({ ...findingReferenceFields, ...fields }).refine(\n (value) => {\n const reference = value as { identifier?: string; findingId?: string };\n return Boolean(reference.identifier || reference.findingId);\n },\n { message: \"Provide either identifier or findingId.\" },\n );\n}\n\nexport const readAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n};\n\n/**\n * Every tool that changes Odin data is marked destructive, so clients ask\n * before running it. Odin's guidance also tells agents to ask first.\n */\nexport const writeAnnotations = {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: false,\n openWorldHint: true,\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type { AttachmentUploadResponse } from \"../types.js\";\nimport { findingReferencePath } from \"./finding-reference.js\";\nimport {\n findingToolInput,\n readAnnotations,\n writeAnnotations,\n} from \"./finding-tool.js\";\n\nexport const prepareFindingAttachmentUploadInput = findingToolInput({\n filename: z.string().min(1).max(500),\n contentType: z.enum([\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"application/pdf\",\n \"application/zip\",\n \"application/json\",\n \"application/octet-stream\",\n \"text/plain\",\n \"text/markdown\",\n \"text/csv\",\n \"text/log\",\n ]),\n sizeBytes: z\n .number()\n .int()\n .min(1)\n .max(25 * 1024 * 1024),\n});\n\nexport const prepareFindingAttachmentUploadTool = {\n name: \"prepare_finding_attachment_upload\",\n title: \"Prepare a private discussion attachment upload\",\n description:\n \"Create a five-minute signed upload URL for a file up to 25 MiB. Requires write permission. Does not upload bytes or post a message. Returns uploadUrl and requiredHeaders for a PUT of the exact file, plus publicUrl, a private gs:// object reference usable as a discussion attachment. Storage uploads use the returned storage headers without Odin credentials. Signed URLs are temporary secrets.\",\n annotations: writeAnnotations,\n inputSchema: prepareFindingAttachmentUploadInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof prepareFindingAttachmentUploadInput>) => {\n const data = await client.post<AttachmentUploadResponse>(\n findingReferencePath(params, \"/thread/upload-url\"),\n {\n filename: params.filename,\n contentType: params.contentType,\n sizeBytes: params.sizeBytes,\n },\n );\n return {\n markdown:\n \"Upload URL prepared; no bytes have been uploaded. PUT the file to uploadUrl with requiredHeaders, then attach publicUrl in a discussion message. Do not send the Odin API key to the upload URL.\",\n structured: data,\n };\n },\n};\n\nexport const getFindingAttachmentUrlInput = findingToolInput({\n messageId: z.string().min(1),\n url: z\n .string()\n .url()\n .describe(\n \"Exact private attachment URL from this message's attachments array.\",\n ),\n});\n\nexport const getFindingAttachmentUrlTool = {\n name: \"get_finding_attachment_url\",\n title: \"Get a temporary attachment download URL\",\n description:\n \"Resolve a stored discussion attachment to a ten-minute signed download URL. Requires read permission and the attachment must belong to the selected finding and message. Does not download bytes or forward the Odin key to storage. Treat the returned signed URL as a temporary secret.\",\n annotations: { ...readAnnotations, openWorldHint: true },\n inputSchema: getFindingAttachmentUrlInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingAttachmentUrlInput>) => {\n const url = await client.getRedirectUrl(\n findingReferencePath(\n params,\n `/thread/attachments/${encodeURIComponent(params.messageId)}`,\n ),\n { url: params.url },\n );\n return {\n markdown:\n \"Temporary attachment download URL resolved. No bytes were downloaded; the URL is in the structured result.\",\n structured: { url },\n };\n },\n};\n\nexport const getFindingSlackUrlInput = findingToolInput({\n messageId: z.string().min(1).optional(),\n threadId: z.string().min(1).optional(),\n}).refine((value) => Boolean(value.messageId || value.threadId), {\n message: \"Provide a messageId or threadId.\",\n});\n\nexport const getFindingSlackUrlTool = {\n name: \"get_finding_slack_url\",\n title: \"Get a synced discussion's Slack permalink\",\n description:\n \"Resolve a synced message or thread to its Slack permalink. Requires read permission. Does not navigate to Slack or forward the Odin API key.\",\n annotations: { ...readAnnotations, openWorldHint: true },\n inputSchema: getFindingSlackUrlInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingSlackUrlInput>) => {\n const url = await client.getRedirectUrl(\n findingReferencePath(params, \"/thread/slack-link\"),\n { messageId: params.messageId, threadId: params.threadId },\n );\n return { markdown: `Slack permalink: ${url}`, structured: { url } };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type {\n DiscussionMessageResponse,\n FindingDiscussionResponse,\n FindingMentionablesResponse,\n FindingStatusUpdateResponse,\n FindingThreadResponse,\n} from \"../types.js\";\nimport { findingReferencePath } from \"./finding-reference.js\";\nimport {\n findingToolInput,\n readAnnotations,\n writeAnnotations,\n} from \"./finding-tool.js\";\n\nconst attachmentInput = z.object({\n type: z.enum([\"IMAGE\", \"FILE\"]),\n url: z\n .string()\n .url()\n .describe(\n \"Private gs:// URL returned by prepare_finding_attachment_upload after uploading the file.\",\n ),\n name: z.string().min(1).max(500),\n sizeBytes: z.number().int().nonnegative().optional(),\n mimeType: z.string().min(1).max(255).optional(),\n});\n\nexport const assignFindingInput = findingToolInput({\n assigneeUserId: z\n .string()\n .min(1)\n .nullable()\n .describe(\"Organisation member user ID, or null to clear assignment.\"),\n});\n\nexport const assignFindingTool = {\n name: \"assign_finding\",\n title: \"Assign a finding\",\n description:\n \"Assign a finding to an organisation member, or clear its assignment. Requires write permission. Assignment can notify the assignee by email and Slack and synchronise connected issue trackers.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: assignFindingInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof assignFindingInput>) => {\n const data = await client.patch<FindingStatusUpdateResponse>(\n findingReferencePath(params, \"/assignee\"),\n { assigneeUserId: params.assigneeUserId },\n );\n return {\n markdown: `Updated assignment for ${data.finding.identifier ?? data.finding.findingId}: ${params.assigneeUserId ?? \"unassigned\"}.`,\n structured: { finding: data.finding },\n };\n },\n};\n\nexport const getFindingDiscussionInput = findingToolInput({\n limit: z.number().int().min(1).max(100).optional(),\n offset: z.number().int().nonnegative().optional(),\n before: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\n \"Only messages before this timestamp, in the API's stored timestamp units.\",\n ),\n});\n\nexport const getFindingDiscussionTool = {\n name: \"get_finding_discussion\",\n title: \"Read finding discussions\",\n description:\n \"Read native and Slack-synchronised conversation blocks for a finding. Each block paginates independently. API keys have no personal read or unread state.\",\n annotations: readAnnotations,\n inputSchema: getFindingDiscussionInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingDiscussionInput>) => {\n const data = await client.get<FindingDiscussionResponse>(\n findingReferencePath(params, \"/thread\"),\n { limit: params.limit, offset: params.offset, before: params.before },\n );\n const markdown = data.blocks.length\n ? data.blocks\n .map((block) =>\n [\n `## ${block.thread.type} discussion ${block.thread.threadId} (${block.thread.status})`,\n `More messages: ${block.hasMore}.`,\n ...block.messages.map(\n (message) =>\n `### ${message.author.displayName} (${message.messageId})\\n${message.bodyMd}`,\n ),\n ].join(\"\\n\\n\"),\n )\n .join(\"\\n\\n\")\n : \"No discussions exist for this finding.\";\n return { markdown, structured: data };\n },\n};\n\nexport const createFindingMessageInput = findingToolInput({\n bodyMd: z\n .string()\n .min(1)\n .max(50_000)\n .describe(\n \"Markdown body. Mention syntax: @[Label](USER:id), @[Label](OPERATOR:id), @[ODI-12](FINDING:id), or @[Pentest](PENTEST:id); use list_finding_mentionables to discover IDs.\",\n ),\n threadId: z\n .string()\n .min(1)\n .optional()\n .describe(\"Existing block to reply to; omit for the native discussion.\"),\n attachments: z.array(attachmentInput).max(20).optional(),\n});\n\nexport const createFindingMessageTool = {\n name: \"create_finding_message\",\n title: \"Post a finding discussion message\",\n description:\n \"Post a Markdown discussion message. Requires write permission and acts as the API key creator. May send email, mention notifications and Slack messages. Repeating a call creates another message.\",\n annotations: writeAnnotations,\n inputSchema: createFindingMessageInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof createFindingMessageInput>) => {\n const data = await client.post<DiscussionMessageResponse>(\n findingReferencePath(params, \"/thread\"),\n {\n bodyMd: params.bodyMd,\n threadId: params.threadId,\n attachments: params.attachments,\n },\n );\n return {\n markdown: `Posted message ${data.message.messageId}.`,\n structured: data,\n };\n },\n};\n\nexport const editFindingMessageInput = findingToolInput({\n messageId: z.string().min(1),\n bodyMd: z.string().min(1).max(50_000),\n});\n\nexport const editFindingMessageTool = {\n name: \"edit_finding_message\",\n title: \"Edit a finding discussion message\",\n description:\n \"Edit a message authored by the API key creator. Requires write permission. New mentions can send notifications and the edit can synchronise to Slack.\",\n annotations: writeAnnotations,\n inputSchema: editFindingMessageInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof editFindingMessageInput>) => {\n const data = await client.patch<DiscussionMessageResponse>(\n findingReferencePath(\n params,\n `/thread/${encodeURIComponent(params.messageId)}`,\n ),\n { bodyMd: params.bodyMd },\n );\n return {\n markdown: `Edited message ${data.message.messageId}.`,\n structured: data,\n };\n },\n};\n\nexport const deleteFindingMessageInput = findingToolInput({\n messageId: z.string().min(1),\n});\n\nexport const deleteFindingMessageTool = {\n name: \"delete_finding_message\",\n title: \"Delete a finding discussion message\",\n description:\n \"Delete a message authored by the API key creator. Requires write permission and explicit user confirmation. May delete the message's Slack mirror.\",\n annotations: writeAnnotations,\n inputSchema: deleteFindingMessageInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof deleteFindingMessageInput>) => {\n const data = await client.delete<FindingThreadResponse>(\n findingReferencePath(\n params,\n `/thread/${encodeURIComponent(params.messageId)}`,\n ),\n );\n return {\n markdown: `Deleted message ${params.messageId}.`,\n structured: data,\n };\n },\n};\n\nexport const updateFindingDiscussionInput = findingToolInput({\n action: z.enum([\"resolve\", \"reopen\"]),\n});\n\nexport const updateFindingDiscussionTool = {\n name: \"update_finding_discussion\",\n title: \"Resolve or reopen the native discussion\",\n description:\n \"Resolve or reopen a finding's native discussion. Requires write permission and may synchronise the status to Slack. This does not change the finding's security status.\",\n annotations: writeAnnotations,\n inputSchema: updateFindingDiscussionInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof updateFindingDiscussionInput>) => {\n const data = await client.post<FindingThreadResponse>(\n findingReferencePath(params, \"/thread/resolve\"),\n { action: params.action },\n );\n return {\n markdown: `Discussion status: ${data.thread?.status ?? params.action}.`,\n structured: data,\n };\n },\n};\n\nexport const listFindingMentionablesInput = findingToolInput({\n q: z.string().trim().max(100).optional(),\n});\n\nexport const listFindingMentionablesTool = {\n name: \"list_finding_mentionables\",\n title: \"Find people and entities to mention\",\n description:\n \"Find authorised people, findings and pentests that can be mentioned in a finding discussion. Returns up to eight people, eight findings and eight pentests matching q.\",\n annotations: readAnnotations,\n inputSchema: listFindingMentionablesInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof listFindingMentionablesInput>) => {\n const data = await client.get<FindingMentionablesResponse>(\n findingReferencePath(params, \"/thread/mentionables\"),\n { q: params.q },\n );\n return {\n markdown:\n data.mentionables\n .map((item) => `- ${item.label}: ${item.id}`)\n .join(\"\\n\") || \"No matching mentionables.\",\n structured: data,\n };\n },\n};\n\nexport const disconnectFindingDiscussionInput = findingToolInput({\n threadId: z.string().min(1),\n});\n\nexport const disconnectFindingDiscussionTool = {\n name: \"disconnect_finding_slack_thread\",\n title: \"Disconnect a synced Slack thread\",\n description:\n \"Disconnect a finding's SLACK_SYNC discussion block while preserving imported messages. Stops future synchronisation and may post a confirmation to Slack. Requires write permission and explicit user confirmation.\",\n annotations: writeAnnotations,\n inputSchema: disconnectFindingDiscussionInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof disconnectFindingDiscussionInput>) => {\n const data = await client.post<FindingThreadResponse>(\n findingReferencePath(params, \"/thread/disconnect\"),\n { threadId: params.threadId },\n );\n return {\n markdown: `Disconnected Slack discussion ${params.threadId}. Existing messages are preserved.`,\n structured: data,\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type {\n FindingPullRequestsResponse,\n UnlinkFindingPullRequestResponse,\n} from \"../types.js\";\nimport { findingReferencePath } from \"./finding-reference.js\";\nimport {\n findingToolInput,\n readAnnotations,\n writeAnnotations,\n} from \"./finding-tool.js\";\n\nexport const listFindingPullRequestsInput = findingToolInput({\n query: z\n .string()\n .optional()\n .describe(\"PR number, title search, owner/repo#123, or GitHub PR URL.\"),\n page: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),\n});\n\nexport const listFindingPullRequestsTool = {\n name: \"list_finding_pull_requests\",\n title: \"Find accessible pull requests to link\",\n description:\n \"Search accessible GitHub pull requests for this finding. Defaults to the finding's repository, otherwise its connected GitHub organisation. Results are advisory; link_finding_pull_request verifies the chosen PR. Requires read permission and a configured GitHub integration.\",\n annotations: { ...readAnnotations, openWorldHint: true },\n inputSchema: listFindingPullRequestsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof listFindingPullRequestsInput>) => {\n const data = await client.get<FindingPullRequestsResponse>(\n findingReferencePath(params, \"/link-pr\"),\n { query: params.query, page: params.page },\n );\n return {\n markdown: [\n `Accessible pull requests (${data.repository ?? \"connected organisation\"}); more pages: ${data.pagination.hasMore}.`,\n ...data.pullRequests.map(\n (pr) =>\n `- ${pr.repo}#${pr.number}: ${pr.title} (${pr.state}) ${pr.url}`,\n ),\n ].join(\"\\n\"),\n structured: data,\n };\n },\n};\n\nexport const unlinkFindingPullRequestInput = findingToolInput({\n prNodeId: z\n .string()\n .min(1)\n .describe(\n \"Stored prNodeId of a manually linked pull request from get_finding.\",\n ),\n});\n\nexport const unlinkFindingPullRequestTool = {\n name: \"unlink_finding_pull_request\",\n title: \"Unlink a manually linked pull request\",\n description:\n \"Remove a manual finding-to-PR link. Requires write permission and explicit user confirmation. Automatic links cannot be removed with this operation. Does not modify, close, merge or delete the GitHub PR.\",\n annotations: { ...writeAnnotations, openWorldHint: false },\n inputSchema: unlinkFindingPullRequestInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof unlinkFindingPullRequestInput>) => {\n const data = await client.delete<UnlinkFindingPullRequestResponse>(\n findingReferencePath(params, \"/link-pr\"),\n { prNodeId: params.prNodeId },\n );\n return {\n markdown: `Removed manual pull request link ${params.prNodeId} from ${data.finding.identifier ?? data.finding.findingId}.`,\n structured: data,\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type {\n FindingAssetsResponse,\n FindingRepositoriesResponse,\n FindingStatsResponse,\n FindingsExportResponse,\n RetestSummariesResponse,\n ThreadSummariesResponse,\n} from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\nimport { findingFilterFields, serializeFindingQuery } from \"./list-findings.js\";\n\nexport const getFindingStatsInput = z.object({\n pentestId: z.string().optional(),\n});\n\nexport const getFindingStatsTool = {\n name: \"get_finding_stats\",\n title: \"Get finding statistics\",\n description:\n \"Read aggregate finding totals by severity and status, optionally scoped to a pentest. These totals do not apply other finding list filters.\",\n annotations: readAnnotations,\n inputSchema: getFindingStatsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingStatsInput>) => {\n const data = await client.get<FindingStatsResponse>(\n \"/api/findings/stats\",\n params,\n );\n return {\n markdown: `Total findings: ${data.stats.total}.\\n${JSON.stringify(data.stats, null, 2)}`,\n structured: data,\n };\n },\n};\n\nexport const listFindingRepositoriesTool = {\n name: \"list_finding_repositories\",\n title: \"List repositories with findings\",\n description:\n \"List organisation repositories available for the repository filter in list_findings.\",\n annotations: readAnnotations,\n inputSchema: z.object({}),\n handler: (client: OdinClient) => async () => {\n const data = await client.get<FindingRepositoriesResponse>(\n \"/api/findings/repositories\",\n );\n return {\n markdown:\n data.repositories.map((repository) => `- ${repository}`).join(\"\\n\") ||\n \"No repositories with findings.\",\n structured: data,\n };\n },\n};\n\nexport const listFindingAssetsTool = {\n name: \"list_finding_assets\",\n title: \"List assets referenced by findings\",\n description:\n \"List organisation assets referenced by findings for use with the assetId filter. This differs from listing all attack-surface assets.\",\n annotations: readAnnotations,\n inputSchema: z.object({}),\n handler: (client: OdinClient) => async () => {\n const data = await client.get<FindingAssetsResponse>(\n \"/api/findings/assets\",\n );\n return {\n markdown:\n data.assets\n .map((asset) => `- ${asset.value} (${asset.type}, ${asset.assetId})`)\n .join(\"\\n\") || \"No assets referenced by findings.\",\n structured: data,\n };\n },\n};\n\nexport const exportFindingsInput = z.object(findingFilterFields);\n\nexport const exportFindingsTool = {\n name: \"export_findings\",\n title: \"Export findings and revision content\",\n description:\n \"Export findings matching the supplied filters, including revision Markdown and affected assets. The API returns all matching rows without pagination; narrow the filters for large organisations. This reads data and creates no file.\",\n annotations: readAnnotations,\n inputSchema: exportFindingsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof exportFindingsInput>) => {\n const data = await client.get<FindingsExportResponse>(\n \"/api/findings/export\",\n serializeFindingQuery(params),\n );\n return {\n markdown: `Exported ${data.findings.length} matching finding(s). Revision content is in the structured findings result.`,\n structured: data,\n };\n },\n};\n\nexport const findingSummariesInput = z.object({\n findingIds: z\n .array(z.string().trim().min(1))\n .max(200)\n .describe(\n \"Up to 200 internal finding IDs from list_findings. Human identifiers are not accepted by these batch endpoints.\",\n ),\n});\n\nexport const getFindingRetestSummariesTool = {\n name: \"get_finding_retest_summaries\",\n title: \"Get latest retests for several findings\",\n description:\n \"Read the latest retest summary for up to 200 finding IDs. Requires read permission and the organisation's retest capability; never starts a retest or spends credits. Missing entries have no latest retest.\",\n annotations: readAnnotations,\n inputSchema: findingSummariesInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof findingSummariesInput>) => {\n const data = await client.post<RetestSummariesResponse>(\n \"/api/findings/retest-summaries\",\n params,\n );\n return {\n markdown: `Latest retests available for ${Object.keys(data.summaries).length} finding(s).\\n${JSON.stringify(data.summaries, null, 2)}`,\n structured: data,\n };\n },\n};\n\nexport const getFindingDiscussionSummariesTool = {\n name: \"get_finding_discussion_summaries\",\n title: \"Get discussion summaries for several findings\",\n description:\n \"Read message counts and unresolved discussion state for up to 200 finding IDs. API keys have no personal unread state; unreadCount is zero for key-authenticated calls.\",\n annotations: readAnnotations,\n inputSchema: findingSummariesInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof findingSummariesInput>) => {\n const data = await client.post<ThreadSummariesResponse>(\n \"/api/findings/thread-summaries\",\n params,\n );\n return {\n markdown: `Discussion summaries available for ${Object.keys(data.summaries).length} finding(s).\\n${JSON.stringify(data.summaries, null, 2)}`,\n structured: data,\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingsList } from \"../formatters/markdown.js\";\nimport type { FindingsListResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nconst severityInput = z.enum([\n \"CRITICAL\",\n \"HIGH\",\n \"MEDIUM\",\n \"LOW\",\n \"INFORMATIONAL\",\n]);\nconst statusInput = z.enum([\n \"REPORTED\",\n \"ACKNOWLEDGED\",\n \"MITIGATING\",\n \"OPEN_FOR_RETEST\",\n \"NEEDS_REVISION\",\n \"FIXED_AND_RETESTED\",\n]);\nconst sourceInput = z.enum([\n \"MANUAL\",\n \"WATCHDOG\",\n \"IMPORT\",\n \"AGENT\",\n \"THREAT_MODEL\",\n]);\nconst confirmationTypeInput = z.enum([\"static\", \"partial\", \"exploited\"]);\nconst assigneeInput = z.string().trim().min(1).max(500);\n\nexport const findingFilterFields = {\n pentestId: z.string().optional().describe(\"Filter to a single pentest.\"),\n assetId: z\n .string()\n .optional()\n .describe(\n \"Filter to findings whose latest revision references this asset.\",\n ),\n repository: z\n .string()\n .optional()\n .describe(\"Filter to a source repository such as owner/name.\"),\n severity: z.union([severityInput, z.array(severityInput).min(1)]).optional(),\n status: z.union([statusInput, z.array(statusInput).min(1)]).optional(),\n excludeStatuses: z\n .union([statusInput, z.array(statusInput).min(1)])\n .optional()\n .describe(\"Exclude these client-visible statuses.\"),\n source: z.union([sourceInput, z.array(sourceInput).min(1)]).optional(),\n confirmationType: z\n .union([confirmationTypeInput, z.array(confirmationTypeInput).min(1)])\n .optional(),\n assignee: z\n .union([assigneeInput, z.array(assigneeInput).min(1)])\n .refine((value) => String(value).length <= 500, {\n message: \"Combined assignee filter must be at most 500 characters.\",\n })\n .optional()\n .describe(\n \"Client user ID(s), or __unassigned__ to include findings without an assignee.\",\n ),\n cvssMin: z.number().min(0).max(10).optional(),\n cvssMax: z.number().min(0).max(10).optional(),\n hideDormant: z\n .boolean()\n .optional()\n .describe(\"Hide findings on dormant assets.\"),\n search: z\n .string()\n .optional()\n .describe(\"Free-text search on finding titles and human identifiers.\"),\n};\n\nexport const listFindingsInput = z.object({\n ...findingFilterFields,\n lean: z\n .boolean()\n .optional()\n .describe(\n \"Exclude heavy evidence from list results; details stay available.\",\n ),\n stats: z\n .boolean()\n .optional()\n .describe(\"Include aggregate statistics (defaults to true).\"),\n sortBy: z.enum([\"severity\", \"createdAt\"]).optional(),\n sortOrder: z.enum([\"asc\", \"desc\"]).optional(),\n limit: z.number().int().min(1).max(200).optional().default(50),\n offset: z.number().int().min(0).optional().default(0),\n});\n\nexport function serializeFindingQuery(\n params: Record<string, string | number | boolean | string[] | undefined>,\n): Record<string, string | number | undefined> {\n return Object.fromEntries(\n Object.entries(params).map(([key, value]) => [\n key,\n Array.isArray(value)\n ? value.join(\",\")\n : typeof value === \"boolean\"\n ? String(value)\n : value,\n ]),\n );\n}\n\nexport const listFindingsTool = {\n name: \"list_findings\",\n title: \"List findings\",\n description:\n \"List security findings for the organisation. Filter by pentest, asset, repository, severity, client-visible status, source, confirmation type, assignee, CVSS range, or dormant assets. Search titles or human identifiers such as ODI-12.\",\n annotations: readAnnotations,\n inputSchema: listFindingsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof listFindingsInput>) => {\n const data = await client.get<FindingsListResponse>(\n \"/api/findings\",\n serializeFindingQuery(params),\n );\n return {\n markdown: formatFindingsList(data),\n structured: {\n findings: data.findings,\n stats: data.stats,\n pagination: data.pagination,\n },\n };\n },\n};\n","import type {\n Asset,\n AssetDetailResponse,\n AssetsListResponse,\n FindingDetailResponse,\n FindingStatus,\n FindingSummary,\n FindingsListResponse,\n LinkFindingPullRequestResponse,\n Pentest,\n PentestDetailResponse,\n PentestListResponse,\n Retest,\n RetestHistoryResponse,\n} from \"../types.js\";\n\nconst STATUS_LABELS: Record<FindingStatus, string> = {\n REPORTED: \"Reported\",\n ACKNOWLEDGED: \"Risk accepted\",\n MITIGATING: \"Fixing\",\n OPEN_FOR_RETEST: \"Open for Retest\",\n NEEDS_REVISION: \"Needs Revision\",\n FIXED_AND_RETESTED: \"Fixed & Retested\",\n};\n\nconst RETEST_STATUS_LABELS: Record<Retest[\"status\"], string> = {\n QUEUED: \"Queued\",\n RUNNING: \"Running\",\n COMPLETED: \"Completed\",\n FAILED: \"Failed\",\n SKIPPED: \"Skipped\",\n};\n\nexport function formatFindingsList(data: FindingsListResponse): string {\n const { findings, pagination } = data;\n if (findings.length === 0) {\n return \"No findings match the supplied filters.\";\n }\n\n const rows = findings.map((f) => {\n const severity = `${f.severity.label} (CVSS ${f.severity.cvss.toFixed(1)})`;\n return `| ${f.identifier ?? \"—\"} | ${f.findingId} | ${escapePipes(f.title)} | ${severity} | ${formatStatus(f.status)} | ${formatDate(f.createdAt)} |`;\n });\n\n return [\n `Showing ${findings.length} of ${pagination.total} finding(s). Offset ${pagination.offset}, limit ${pagination.limit}. hasMore=${pagination.hasMore}.`,\n \"\",\n \"| Identifier | Internal ID | Title | Severity | Status | Created |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...rows,\n ].join(\"\\n\");\n}\n\nexport function formatFindingDetail(data: FindingDetailResponse): string {\n const { finding, revision } = data;\n const context = data.remediationContext;\n const identifier = context?.identifier ?? finding.identifier ?? null;\n const displayIdentifier = identifier ?? finding.findingId;\n const severity = `${finding.severity.label} (CVSS ${finding.severity.cvss.toFixed(1)})`;\n const header = [\n `# ${escapePipes(displayIdentifier)} — ${escapePipes(finding.title)}`,\n \"\",\n `- **Finding:** ${displayIdentifier}`,\n `- **Internal ID:** ${finding.findingId}`,\n `- **Severity:** ${severity}`,\n `- **Status:** ${formatStatus(finding.status)}`,\n `- **Repository:** ${context?.repository ?? finding.repository ?? \"Not recorded\"}`,\n `- **Pentest:** ${finding.context?.pentestId ?? \"standalone\"}`,\n `- **Created:** ${formatDate(finding.createdAt)}`,\n `- **Updated:** ${formatDate(finding.updatedAt)}`,\n ];\n\n const content = context\n ? {\n revisionNumber: context.revision.revisionNumber,\n summary: context.revision.summary,\n description: context.revision.description,\n impact: context.revision.impact,\n details: context.revision.details,\n remediation: context.revision.remediation,\n affectedAssets: context.revision.affectedAssets,\n codeReference: context.revision.codeReference,\n }\n : revision\n ? {\n revisionNumber: revision.revisionNumber,\n summary: revision.content.summaryMd,\n description: revision.content.descriptionMd,\n impact: revision.content.businessImpactMd,\n details: revision.content.detailsMd,\n remediation: revision.content.remediationMd,\n affectedAssets: revision.content.affectedAssets ?? [],\n codeReference: null,\n }\n : null;\n\n const sections: string[] = [];\n if (!content) {\n sections.push(\"_No revision content available._\");\n } else {\n sections.push(`_Revision ${content.revisionNumber ?? \"unknown\"}_`);\n if (content.summary) sections.push(\"## Summary\", content.summary);\n if (content.description)\n sections.push(\"## Description\", content.description);\n if (content.impact) sections.push(\"## Business Impact\", content.impact);\n if (content.details) sections.push(\"## Details\", content.details);\n if (content.remediation)\n sections.push(\"## Remediation\", content.remediation);\n if (content.codeReference) {\n sections.push(\n \"## Code reference\",\n `- ${content.codeReference.file}:${content.codeReference.line}`,\n );\n if (content.codeReference.snippet) {\n sections.push(\"```\", content.codeReference.snippet, \"```\");\n }\n }\n if (content.affectedAssets?.length) {\n sections.push(\n \"## Affected Assets\",\n content.affectedAssets\n .map((asset) => `- ${asset.display} (${asset.assetId})`)\n .join(\"\\n\"),\n );\n }\n }\n\n if (context) {\n sections.push(\n \"## Remediation workflow\",\n `- **Suggested branch:** ${context.suggestedBranch ?? \"Not available\"}`,\n `- **Ready for a user-created PR:** ${yesNo(context.workflow.readyForUserCreatedPullRequest)}`,\n `- **GitHub connected:** ${yesNo(context.workflow.githubConnected)}`,\n `- **Automatic retests:** ${context.workflow.automaticRetestsReady ? \"Ready after merge\" : \"Not ready\"}`,\n );\n if (context.workflow.blockers.length) {\n sections.push(\n \"### Blockers\",\n context.workflow.blockers.map((blocker) => `- ${blocker}`).join(\"\\n\"),\n );\n }\n if (context.workflow.nextSteps.length) {\n sections.push(\n \"### Next steps\",\n context.workflow.nextSteps.map((step) => `- ${step}`).join(\"\\n\"),\n );\n }\n\n if (context.linkedPullRequests.length) {\n sections.push(\n \"## Linked GitHub pull requests\",\n context.linkedPullRequests\n .map(\n (pullRequest) =>\n `- [#${pullRequest.number}](${pullRequest.url}) ${escapePipes(pullRequest.title)} — ${pullRequest.state}, linked via ${pullRequest.linkedVia}`,\n )\n .join(\"\\n\"),\n );\n } else {\n sections.push(\"## Linked GitHub pull requests\", \"_None linked._\");\n }\n\n sections.push(\"## Latest retest\");\n if (context.latestRetest.state === \"unavailable\") {\n sections.push(`_${context.latestRetest.reason}_`);\n } else if (context.latestRetest.state === \"none\") {\n sections.push(\"_No retest history is available yet._\");\n } else {\n sections.push(formatRetestSummary(context.latestRetest.retest));\n }\n }\n\n return [...header, \"\", ...sections].join(\"\\n\");\n}\n\nexport function formatFindingStatusUpdate(finding: FindingSummary): string {\n const identifier = finding.identifier ?? finding.findingId;\n return [\n `Updated finding **${identifier}** (internal ID ${finding.findingId}) status to **${formatStatus(finding.status)}**.`,\n `Title: ${finding.title}`,\n ].join(\"\\n\");\n}\n\nexport function formatFindingPullRequestLink(\n data: LinkFindingPullRequestResponse,\n): string {\n const identifier = data.finding.identifier ?? data.finding.findingId;\n const pullRequest = data.pullRequest;\n const readiness = data.workflow.automaticRetestsReady\n ? \"Automatic retesting is ready after this PR merges.\"\n : \"Automatic retesting is not currently ready; review the blockers below.\";\n const blockers = data.workflow.blockers.length\n ? `\\n${data.workflow.blockers.map((blocker) => `- ${blocker}`).join(\"\\n\")}`\n : \"\";\n\n return [\n `Linked existing GitHub pull request [#${pullRequest.number}](${pullRequest.url}) to **${identifier}**.`,\n `Repository: ${pullRequest.repo}; state: ${pullRequest.state}; finding status: ${formatStatus(data.finding.status)}.`,\n \"Odin did not create, modify, merge, or push this pull request.\",\n readiness,\n blockers ? `Workflow blockers:${blockers}` : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nexport function formatFindingRetests(data: RetestHistoryResponse): string {\n if (data.state === \"none\" || data.retests.length === 0) {\n return \"No retest history is available for this finding.\";\n }\n\n return [\n `Retest history (${data.retests.length}):`,\n \"\",\n \"| Retest | Status | Trigger | Verdict | Created | Completed |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...data.retests.map(\n (retest) =>\n `| ${retest.retestId} | ${RETEST_STATUS_LABELS[retest.status]} | ${retest.trigger} | ${retest.combinedVerdict ?? \"—\"} | ${formatDate(retest.createdAt)} | ${formatDate(retest.completedAt ?? 0)} |`,\n ),\n \"\",\n \"Queued and running entries are still in progress; completed, skipped, and failed entries are final for that run.\",\n ].join(\"\\n\");\n}\n\nexport function formatPentestsList(data: PentestListResponse): string {\n if (data.pentests.length === 0) return \"No pentests found.\";\n const rows = data.pentests.map(formatPentestRow);\n return [\n \"| Pentest ID | Name | Status | Start | End | Scope |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...rows,\n ].join(\"\\n\");\n}\n\nexport function formatPentestDetail(data: PentestDetailResponse): string {\n const p = data.pentest;\n const end = p.endAt ? formatDate(p.endAt) : \"open\";\n const scope = p.scope.length\n ? p.scope.map((s) => `- ${s}`).join(\"\\n\")\n : \"_No scope defined._\";\n return [\n `# ${p.name}`,\n \"\",\n `- **ID:** ${p.pentestId}`,\n `- **Status:** ${p.status}`,\n `- **Start:** ${formatDate(p.startAt)}`,\n `- **End:** ${end}`,\n `- **Created:** ${formatDate(p.createdAt)}`,\n \"\",\n \"## Scope\",\n \"\",\n scope,\n ].join(\"\\n\");\n}\n\nexport function formatFindingsForAsset(findings: FindingSummary[]): string {\n if (findings.length === 0) return \"_No findings reference this asset._\";\n return [\n `## Findings (${findings.length})`,\n \"\",\n \"| Identifier | Internal ID | Title | Severity | Status | Created |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...findings.map(formatFindingRow),\n ].join(\"\\n\");\n}\n\nexport function formatAssetsList(data: AssetsListResponse): string {\n const { assets, pagination } = data;\n if (assets.length === 0) return \"No assets match the supplied filters.\";\n const rows = assets.map(formatAssetRow);\n return [\n `Showing ${assets.length} of ${pagination.total} asset(s). Offset ${pagination.offset}, limit ${pagination.limit}. hasMore=${pagination.hasMore}.`,\n \"\",\n \"| Asset ID | Type | Value | Source | Status | Last seen |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...rows,\n ].join(\"\\n\");\n}\n\nexport function formatAssetDetail(data: AssetDetailResponse): string {\n const { asset, findings } = data;\n const header = [\n `# ${asset.value}`,\n \"\",\n `- **ID:** ${asset.assetId}`,\n `- **Type:** ${asset.type}`,\n `- **Source:** ${asset.source}`,\n `- **Status:** ${asset.status}`,\n `- **Last seen:** ${formatDate(asset.lastSeenAt)}`,\n `- **Created:** ${formatDate(asset.createdAt)}`,\n ];\n\n return [...header, \"\", formatFindingsForAsset(findings)].join(\"\\n\");\n}\n\nfunction formatFindingRow(finding: FindingSummary): string {\n const severity = `${finding.severity.label} (CVSS ${finding.severity.cvss.toFixed(1)})`;\n return `| ${finding.identifier ?? \"—\"} | ${finding.findingId} | ${escapePipes(finding.title)} | ${severity} | ${formatStatus(finding.status)} | ${formatDate(finding.createdAt)} |`;\n}\n\nfunction formatPentestRow(p: Pentest): string {\n const end = p.endAt ? formatDate(p.endAt) : \"open\";\n const scope =\n p.scope.slice(0, 3).join(\", \") + (p.scope.length > 3 ? \", …\" : \"\");\n return `| ${p.pentestId} | ${escapePipes(p.name)} | ${p.status} | ${formatDate(p.startAt)} | ${end} | ${escapePipes(scope)} |`;\n}\n\nfunction formatAssetRow(a: Asset): string {\n return `| ${a.assetId} | ${a.type} | ${escapePipes(a.value)} | ${a.source} | ${a.status} | ${formatDate(a.lastSeenAt)} |`;\n}\n\nfunction formatRetestSummary(retest: {\n status: Retest[\"status\"];\n combinedVerdict: Retest[\"combinedVerdict\"];\n statusAction: Retest[\"statusAction\"];\n trigger: Retest[\"trigger\"];\n createdAt: number;\n completedAt: number | null;\n}): string {\n return [\n `- **Status:** ${RETEST_STATUS_LABELS[retest.status]}`,\n `- **Trigger:** ${retest.trigger}`,\n `- **Verdict:** ${retest.combinedVerdict ?? \"Not available yet\"}`,\n `- **Finding action:** ${retest.statusAction ?? \"None\"}`,\n `- **Created:** ${formatDate(retest.createdAt)}`,\n `- **Completed:** ${formatDate(retest.completedAt ?? 0)}`,\n ].join(\"\\n\");\n}\n\nfunction formatStatus(status: FindingStatus): string {\n return STATUS_LABELS[status] ?? status;\n}\n\nfunction yesNo(value: boolean): string {\n return value ? \"Yes\" : \"No\";\n}\n\nfunction escapePipes(value: string): string {\n return value.replace(/\\|/g, \"\\\\|\");\n}\n\nfunction formatDate(epoch: number): string {\n if (!epoch) return \"—\";\n // Odin stores timestamps as milliseconds for documents and seconds for some fields\n // (pentest startAt/endAt are seconds). Heuristic: values < 10^12 are seconds.\n const ms = epoch < 1e12 ? epoch * 1000 : epoch;\n return new Date(ms).toISOString().slice(0, 10);\n}\n","import {\n followFindingTool,\n requestFindingRetestTool,\n} from \"./finding-actions.js\";\nimport {\n getFindingAttachmentUrlTool,\n getFindingSlackUrlTool,\n prepareFindingAttachmentUploadTool,\n} from \"./finding-attachments.js\";\nimport {\n assignFindingTool,\n createFindingMessageTool,\n deleteFindingMessageTool,\n disconnectFindingDiscussionTool,\n editFindingMessageTool,\n getFindingDiscussionTool,\n listFindingMentionablesTool,\n updateFindingDiscussionTool,\n} from \"./finding-collaboration.js\";\nimport {\n listFindingPullRequestsTool,\n unlinkFindingPullRequestTool,\n} from \"./finding-pull-requests.js\";\nimport {\n exportFindingsTool,\n getFindingDiscussionSummariesTool,\n getFindingRetestSummariesTool,\n getFindingStatsTool,\n listFindingAssetsTool,\n listFindingRepositoriesTool,\n} from \"./findings-overview.js\";\n\nexport const additionalFindingTools = [\n getFindingStatsTool,\n listFindingRepositoriesTool,\n listFindingAssetsTool,\n exportFindingsTool,\n getFindingRetestSummariesTool,\n getFindingDiscussionSummariesTool,\n assignFindingTool,\n getFindingDiscussionTool,\n createFindingMessageTool,\n editFindingMessageTool,\n deleteFindingMessageTool,\n updateFindingDiscussionTool,\n listFindingMentionablesTool,\n disconnectFindingDiscussionTool,\n prepareFindingAttachmentUploadTool,\n getFindingAttachmentUrlTool,\n getFindingSlackUrlTool,\n listFindingPullRequestsTool,\n unlinkFindingPullRequestTool,\n followFindingTool,\n requestFindingRetestTool,\n];\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatAssetDetail } from \"../formatters/markdown.js\";\nimport type { AssetDetailResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getAssetInput = z.object({\n assetId: z.string().min(1),\n});\n\nexport const getAssetTool = {\n name: \"get_asset\",\n title: \"Get asset\",\n description:\n \"Fetch a single attack-surface asset by ID, including the findings that reference it in their latest revision.\",\n annotations: readAnnotations,\n inputSchema: getAssetInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof getAssetInput>) => {\n const data = await client.get<AssetDetailResponse>(\n `/api/assets/${encodeURIComponent(params.assetId)}`,\n );\n return {\n markdown: formatAssetDetail(data),\n structured: { asset: data.asset, findings: data.findings },\n };\n },\n};\n","import type { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingDetail } from \"../formatters/markdown.js\";\nimport type { FindingDetailResponse } from \"../types.js\";\nimport {\n findingReferenceInput,\n findingReferencePath,\n} from \"./finding-reference.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getFindingInput = findingReferenceInput;\n\nexport const getFindingTool = {\n name: \"get_finding\",\n title: \"Get finding remediation context\",\n description:\n \"Fetch a finding by human identifier such as ODI-12 (preferred) or by internal findingId. Returns bounded remediation context, the suggested branch name, linked GitHub pull requests, workflow readiness, and retest state. Odin only provides context and can link an existing PR; it never creates commits, pushes branches, or creates pull requests.\",\n annotations: readAnnotations,\n inputSchema: getFindingInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof getFindingInput>) => {\n const data = await client.get<FindingDetailResponse>(\n findingReferencePath(params),\n );\n return {\n markdown: formatFindingDetail(data),\n structured: {\n finding: data.remediationContext ?? data.finding,\n revision: data.remediationContext?.revision ?? data.revision,\n remediationContext: data.remediationContext ?? null,\n },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { OdinApiError } from \"../client.js\";\nimport { formatFindingRetests } from \"../formatters/markdown.js\";\nimport type { RetestHistoryResponse } from \"../types.js\";\nimport {\n findingReferenceFields,\n findingReferencePath,\n} from \"./finding-reference.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getFindingRetestsInput = z\n .object(findingReferenceFields)\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport const getFindingRetestsTool = {\n name: \"get_finding_retests\",\n title: \"Get finding retest history\",\n description:\n \"Read the retest history for a finding by human identifier such as ODI-12 or internal findingId. Distinguishes no history, queued or running, completed, skipped, failed, and gated/unavailable results. This tool never triggers a retest or spends credits.\",\n annotations: readAnnotations,\n inputSchema: getFindingRetestsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingRetestsInput>) => {\n try {\n const data = await client.get<RetestHistoryResponse>(\n findingReferencePath(params, \"/retests\"),\n );\n return {\n markdown: formatFindingRetests(data),\n structured: {\n state: data.state,\n retests: data.retests,\n },\n };\n } catch (error) {\n if (\n error instanceof OdinApiError &&\n error.status === 404 &&\n isRetestHistoryUnavailable(error.body)\n ) {\n const reason =\n \"Retest history is gated or unavailable for this organisation.\";\n return {\n markdown: reason,\n structured: { state: \"unavailable\", reason, retests: [] },\n };\n }\n throw error;\n }\n },\n};\n\nfunction isRetestHistoryUnavailable(body: unknown): boolean {\n return (\n typeof body === \"object\" &&\n body !== null &&\n \"code\" in body &&\n (body as { code?: unknown }).code === \"RETEST_HISTORY_UNAVAILABLE\"\n );\n}\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatPentestDetail } from \"../formatters/markdown.js\";\nimport type { PentestDetailResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getPentestInput = z.object({\n pentestId: z.string().min(1),\n});\n\nexport const getPentestTool = {\n name: \"get_pentest\",\n title: \"Get pentest\",\n description:\n \"Fetch a single pentest by ID, including status, scheduled window, and in-scope targets.\",\n annotations: readAnnotations,\n inputSchema: getPentestInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof getPentestInput>) => {\n const data = await client.get<PentestDetailResponse>(\n `/api/pentests/${encodeURIComponent(params.pentestId)}`,\n );\n return {\n markdown: formatPentestDetail(data),\n structured: { pentest: data.pentest },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingPullRequestLink } from \"../formatters/markdown.js\";\nimport type { LinkFindingPullRequestResponse } from \"../types.js\";\nimport {\n findingReferenceFields,\n findingReferencePath,\n} from \"./finding-reference.js\";\nimport { writeAnnotations } from \"./finding-tool.js\";\n\nexport const linkFindingPullRequestInput = z\n .object({\n ...findingReferenceFields,\n url: z\n .string()\n .url()\n .describe(\"Existing GitHub pull request URL to link to the finding.\"),\n })\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport const linkFindingPullRequestTool = {\n name: \"link_finding_pull_request\",\n title: \"Link an existing GitHub pull request\",\n description:\n \"Link an existing accessible GitHub pull request to a finding identified by ODI-12 or findingId. This operation is idempotent. It does not create, modify, merge, or push a pull request, commit, or branch. Requires explicit user confirmation and an API key with Read & Write permission; cookie-authenticated callers also need member role.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: linkFindingPullRequestInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof linkFindingPullRequestInput>) => {\n const data = await client.post<LinkFindingPullRequestResponse>(\n findingReferencePath(params, \"/link-pr\"),\n { url: params.url },\n );\n return {\n markdown: formatFindingPullRequestLink(data),\n structured: {\n finding: data.finding,\n pullRequest: data.pullRequest,\n linkSource: data.linkSource,\n workflow: data.workflow,\n },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatAssetsList } from \"../formatters/markdown.js\";\nimport type { AssetsListResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const listAssetsInput = z.object({\n type: z.enum([\"DOMAIN\", \"SUBDOMAIN\", \"IP\"]).optional(),\n source: z.enum([\"MANUAL\", \"RECON\", \"IMPORT\", \"AGENT\"]).optional(),\n status: z\n .string()\n .optional()\n .describe(\n \"Dynamic asset status set by recon (e.g. ACTIVE, INACTIVE, TLS_ERROR).\",\n ),\n search: z.string().optional(),\n stats: z\n .boolean()\n .optional()\n .describe(\"Include aggregate statistics (defaults to true).\"),\n sortBy: z.enum([\"lastSeenAt\", \"createdAt\", \"value\"]).optional(),\n sortOrder: z.enum([\"asc\", \"desc\"]).optional(),\n limit: z.number().int().min(1).max(100).optional().default(50),\n offset: z.number().int().min(0).optional().default(0),\n});\n\nexport const listAssetsTool = {\n name: \"list_assets\",\n title: \"List assets\",\n description:\n \"List attack-surface assets for the organisation. Supports filtering by type, source, status, and free-text search.\",\n annotations: readAnnotations,\n inputSchema: listAssetsInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof listAssetsInput>) => {\n const data = await client.get<AssetsListResponse>(\"/api/assets\", {\n ...params,\n stats: params.stats === undefined ? undefined : String(params.stats),\n });\n return {\n markdown: formatAssetsList(data),\n structured: {\n assets: data.assets,\n stats: data.stats,\n pagination: data.pagination,\n },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatPentestsList } from \"../formatters/markdown.js\";\nimport type { PentestListResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const listPentestsInput = z.object({});\n\nexport const listPentestsTool = {\n name: \"list_pentests\",\n title: \"List pentests\",\n description:\n \"List all pentests for the organisation, including status, scheduled window, and in-scope targets.\",\n annotations: readAnnotations,\n inputSchema: listPentestsInput,\n handler: (client: OdinClient) => async () => {\n const data = await client.get<PentestListResponse>(\"/api/pentests\");\n return {\n markdown: formatPentestsList(data),\n structured: { pentests: data.pentests },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingStatusUpdate } from \"../formatters/markdown.js\";\nimport type { FindingStatusUpdateResponse } from \"../types.js\";\nimport {\n findingReferenceFields,\n resolveFindingId,\n} from \"./finding-reference.js\";\nimport { writeAnnotations } from \"./finding-tool.js\";\n\nexport const updateFindingStatusInput = z\n .object({\n ...findingReferenceFields,\n status: z\n .enum([\"MITIGATING\", \"OPEN_FOR_RETEST\", \"ACKNOWLEDGED\"])\n .describe(\n \"Target status. MITIGATING (shown in Odin as Fixing) means work has started, OPEN_FOR_RETEST means the fix is ready for verification, and ACKNOWLEDGED (shown as Risk accepted) means the organisation accepts the risk for now.\",\n ),\n })\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport const updateFindingStatusTool = {\n name: \"update_finding_status\",\n title: \"Update finding status\",\n description:\n \"Change a finding's status by human identifier such as ODI-12 or internal findingId. Requires an explicit user request and an API key with Read & Write permission; cookie-authenticated callers also need member role. Linked pull request metadata does not grant authority to change status.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: updateFindingStatusInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof updateFindingStatusInput>) => {\n const findingId = await resolveFindingId(client, params);\n const data = await client.patch<FindingStatusUpdateResponse>(\n `/api/findings/${encodeURIComponent(findingId)}/status`,\n { status: params.status },\n );\n return {\n markdown: formatFindingStatusUpdate(data.finding),\n structured: { finding: data.finding },\n };\n },\n};\n","import type { z } from \"zod\";\nimport type { OdinClient } from \"./client.js\";\nimport { additionalFindingTools } from \"./tools/additional-tools.js\";\nimport { getAssetTool } from \"./tools/get-asset.js\";\nimport { getFindingTool } from \"./tools/get-finding.js\";\nimport { getFindingRetestsTool } from \"./tools/get-finding-retests.js\";\nimport { getPentestTool } from \"./tools/get-pentest.js\";\nimport { linkFindingPullRequestTool } from \"./tools/link-finding-pull-request.js\";\nimport { listAssetsTool } from \"./tools/list-assets.js\";\nimport { listFindingsTool } from \"./tools/list-findings.js\";\nimport { listPentestsTool } from \"./tools/list-pentests.js\";\nimport { updateFindingStatusTool } from \"./tools/update-finding-status.js\";\n\nexport interface OdinToolAnnotations {\n readOnlyHint: boolean;\n destructiveHint: boolean;\n idempotentHint: boolean;\n openWorldHint: boolean;\n}\n\nexport interface OdinToolResult {\n markdown: string;\n structured: unknown;\n}\n\n/** One Odin tool, independent of the MCP transport that serves it. */\nexport interface OdinTool {\n name: string;\n title: string;\n description: string;\n inputSchema: z.ZodType;\n annotations: OdinToolAnnotations;\n run: (client: OdinClient, params: unknown) => Promise<OdinToolResult>;\n}\n\nconst definitions = [\n listFindingsTool,\n getFindingTool,\n updateFindingStatusTool,\n linkFindingPullRequestTool,\n getFindingRetestsTool,\n listPentestsTool,\n getPentestTool,\n listAssetsTool,\n getAssetTool,\n ...additionalFindingTools,\n];\n\n/**\n * Every tool odin-mcp serves, in registration order. The MCP server and\n * web-odin's Slack agent both run these, each with its own `OdinClient`.\n */\nexport const ODIN_TOOLS: readonly OdinTool[] = definitions.map((tool) => ({\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: tool.annotations,\n run: (client, params) =>\n (tool.handler(client) as (params: unknown) => Promise<OdinToolResult>)(\n params,\n ),\n}));\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z"]}
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-4LCKW236.js";
7
7
  import {
8
8
  ODIN_TOOLS
9
- } from "./chunk-D5FYYTYV.js";
9
+ } from "./chunk-EPMQ5QVJ.js";
10
10
  import {
11
11
  OdinApiError
12
12
  } from "./chunk-XHJYVUHG.js";
@@ -16,7 +16,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
16
  import { z } from "zod";
17
17
 
18
18
  // package.json
19
- var version = "0.3.0";
19
+ var version = "0.3.2";
20
20
 
21
21
  // src/server.ts
22
22
  var PACKAGE_VERSION = version;
@@ -74,14 +74,21 @@ function createOdinMcpServer(client, options = {}) {
74
74
  title: tool.title,
75
75
  description: tool.description,
76
76
  inputSchema: tool.inputSchema,
77
- annotations: tool.annotations
77
+ annotations: { ...tool.annotations, title: tool.title }
78
78
  },
79
79
  async (params) => {
80
80
  try {
81
81
  const { markdown, structured } = await tool.run(client, params);
82
+ const structuredContent = { markdown, ...toObject(structured) };
82
83
  return {
83
- content: [{ type: "text", text: markdown }],
84
- structuredContent: { markdown, ...toObject(structured) }
84
+ content: [
85
+ { type: "text", text: markdown },
86
+ {
87
+ type: "text",
88
+ text: JSON.stringify(structuredContent)
89
+ }
90
+ ],
91
+ structuredContent
85
92
  };
86
93
  } catch (error) {
87
94
  return {
@@ -110,4 +117,4 @@ export {
110
117
  PACKAGE_VERSION,
111
118
  createOdinMcpServer
112
119
  };
113
- //# sourceMappingURL=chunk-ODZB3YP2.js.map
120
+ //# sourceMappingURL=chunk-MBBKZPLF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts","../package.json"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { version } from \"../package.json\";\nimport { OdinApiError, type OdinClient } from \"./client.js\";\nimport {\n buildFixFindingPrompt,\n buildServerInstructions,\n buildSetupPrompt,\n} from \"./guidance.js\";\nimport { ODIN_TOOLS } from \"./tools.js\";\n\nexport const PACKAGE_VERSION = version;\n\nexport interface OdinMcpServerOptions {\n /** Public Odin origin for links in guidance. Defaults to production. */\n appUrl?: string;\n}\n\nexport function createOdinMcpServer(\n client: OdinClient,\n options: OdinMcpServerOptions = {},\n) {\n const { appUrl } = options;\n const server = new McpServer(\n { name: \"@borgresearch/odin-mcp\", version: PACKAGE_VERSION },\n { instructions: buildServerInstructions({ appUrl }) },\n );\n\n server.registerPrompt(\n \"setup\",\n {\n title: \"Set up Odin\",\n description:\n \"Learn what Odin does, check what is set up, get recommendations for this repository and reach a first result.\",\n },\n () => ({\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: buildSetupPrompt({ appUrl }),\n },\n },\n ],\n }),\n );\n\n server.registerPrompt(\n \"fix_finding\",\n {\n title: \"Fix an Odin finding\",\n description:\n \"Fix one finding in this repository, open a pull request and link it so Odin can retest the fix.\",\n argsSchema: {\n finding: z\n .string()\n .max(100)\n .optional()\n .describe(\n \"Finding ID such as ODI-12. Leave empty to pick the most severe open finding.\",\n ),\n },\n },\n ({ finding }) => ({\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: buildFixFindingPrompt({ appUrl, finding }),\n },\n },\n ],\n }),\n );\n\n for (const tool of ODIN_TOOLS) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: { ...tool.annotations, title: tool.title },\n },\n async (params: unknown) => {\n try {\n const { markdown, structured } = await tool.run(client, params);\n const structuredContent = { markdown, ...toObject(structured) };\n return {\n content: [\n { type: \"text\" as const, text: markdown },\n {\n type: \"text\" as const,\n text: JSON.stringify(structuredContent),\n },\n ],\n structuredContent,\n };\n } catch (error) {\n return {\n isError: true,\n content: [{ type: \"text\" as const, text: toErrorMessage(error) }],\n };\n }\n },\n );\n }\n\n return server;\n}\n\nfunction toObject(value: unknown): Record<string, unknown> {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return { data: value };\n}\n\nfunction toErrorMessage(error: unknown): string {\n if (error instanceof OdinApiError) return error.message;\n if (error instanceof Error) return `Unexpected error: ${error.message}`;\n return `Unexpected error: ${String(error)}`;\n}\n","{\n \"name\": \"@borgresearch/odin-mcp\",\n \"version\": \"0.3.2\",\n \"mcpName\": \"io.github.BorgDevelopment/odin-mcp\",\n \"description\": \"MCP server for the Odin security API\",\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"odin\",\n \"security\"\n ],\n \"homepage\": \"https://docs.borghq.io/platform/odin-mcp\",\n \"bugs\": {\n \"email\": \"mail@borghq.io\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/BorgDevelopment/borg-platform.git\",\n \"directory\": \"frontend/packages/odin-mcp\"\n },\n \"license\": \"MIT\",\n \"author\": \"Borg Research\",\n \"type\": \"module\",\n \"bin\": {\n \"odin-mcp\": \"./dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./server\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.js\"\n },\n \"./http\": {\n \"types\": \"./dist/http.d.ts\",\n \"import\": \"./dist/http.js\"\n },\n \"./client\": {\n \"types\": \"./dist/client.d.ts\",\n \"import\": \"./dist/client.js\"\n },\n \"./guidance\": {\n \"types\": \"./dist/guidance.d.ts\",\n \"import\": \"./dist/guidance.js\"\n },\n \"./tools\": {\n \"types\": \"./dist/tools.d.ts\",\n \"import\": \"./dist/tools.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:run\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"format\": \"biome format --write .\",\n \"inspect\": \"mcp-inspector node dist/index.js\",\n \"inspect:dev\": \"pnpm build && pnpm inspect\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.31.0\",\n \"zod\": \"^4.3.6\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"2.3.14\",\n \"@modelcontextprotocol/inspector\": \"^0.21.2\",\n \"@types/node\": \"^24.10.1\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.1.11\"\n },\n \"packageManager\": \"pnpm@11.2.2\"\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,SAAS;;;ACChB,cAAW;;;ADSN,IAAM,kBAAkB;AAOxB,SAAS,oBACd,QACA,UAAgC,CAAC,GACjC;AACA,QAAM,EAAE,OAAO,IAAI;AACnB,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,0BAA0B,SAAS,gBAAgB;AAAA,IAC3D,EAAE,cAAc,wBAAwB,EAAE,OAAO,CAAC,EAAE;AAAA,EACtD;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,IACJ;AAAA,IACA,OAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,YACP,MAAM;AAAA,YACN,MAAM,iBAAiB,EAAE,OAAO,CAAC;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,YAAY;AAAA,QACV,SAAS,EACN,OAAO,EACP,IAAI,GAAG,EACP,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,EAAE,QAAQ,OAAO;AAAA,MAChB,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,YACP,MAAM;AAAA,YACN,MAAM,sBAAsB,EAAE,QAAQ,QAAQ,CAAC;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,aAAW,QAAQ,YAAY;AAC7B,WAAO;AAAA,MACL,KAAK;AAAA,MACL;AAAA,QACE,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,QAClB,aAAa,EAAE,GAAG,KAAK,aAAa,OAAO,KAAK,MAAM;AAAA,MACxD;AAAA,MACA,OAAO,WAAoB;AACzB,YAAI;AACF,gBAAM,EAAE,UAAU,WAAW,IAAI,MAAM,KAAK,IAAI,QAAQ,MAAM;AAC9D,gBAAM,oBAAoB,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE;AAC9D,iBAAO;AAAA,YACL,SAAS;AAAA,cACP,EAAE,MAAM,QAAiB,MAAM,SAAS;AAAA,cACxC;AAAA,gBACE,MAAM;AAAA,gBACN,MAAM,KAAK,UAAU,iBAAiB;AAAA,cACxC;AAAA,YACF;AAAA,YACA;AAAA,UACF;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,eAAe,KAAK,EAAE,CAAC;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,SAAS,OAAyC;AACzD,MAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,SAAS,eAAe,OAAwB;AAC9C,MAAI,iBAAiB,aAAc,QAAO,MAAM;AAChD,MAAI,iBAAiB,MAAO,QAAO,qBAAqB,MAAM,OAAO;AACrE,SAAO,qBAAqB,OAAO,KAAK,CAAC;AAC3C;","names":[]}
package/dist/http.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createOdinMcpServer
4
- } from "./chunk-ODZB3YP2.js";
4
+ } from "./chunk-MBBKZPLF.js";
5
5
  import "./chunk-4LCKW236.js";
6
- import "./chunk-D5FYYTYV.js";
6
+ import "./chunk-EPMQ5QVJ.js";
7
7
  import "./chunk-XHJYVUHG.js";
8
8
 
9
9
  // src/http.ts
package/dist/index.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  PACKAGE_VERSION,
4
4
  createOdinMcpServer
5
- } from "./chunk-ODZB3YP2.js";
5
+ } from "./chunk-MBBKZPLF.js";
6
6
  import "./chunk-4LCKW236.js";
7
- import "./chunk-D5FYYTYV.js";
7
+ import "./chunk-EPMQ5QVJ.js";
8
8
  import {
9
9
  DEFAULT_ODIN_BASE_URL,
10
10
  OdinClient
package/dist/server.js CHANGED
@@ -2,9 +2,9 @@
2
2
  import {
3
3
  PACKAGE_VERSION,
4
4
  createOdinMcpServer
5
- } from "./chunk-ODZB3YP2.js";
5
+ } from "./chunk-MBBKZPLF.js";
6
6
  import "./chunk-4LCKW236.js";
7
- import "./chunk-D5FYYTYV.js";
7
+ import "./chunk-EPMQ5QVJ.js";
8
8
  import "./chunk-XHJYVUHG.js";
9
9
  export {
10
10
  PACKAGE_VERSION,
package/dist/tools.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ODIN_TOOLS
4
- } from "./chunk-D5FYYTYV.js";
4
+ } from "./chunk-EPMQ5QVJ.js";
5
5
  import "./chunk-XHJYVUHG.js";
6
6
  export {
7
7
  ODIN_TOOLS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@borgresearch/odin-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "mcpName": "io.github.BorgDevelopment/odin-mcp",
5
5
  "description": "MCP server for the Odin security API",
6
6
  "keywords": [
@@ -9,9 +9,9 @@
9
9
  "odin",
10
10
  "security"
11
11
  ],
12
- "homepage": "https://github.com/BorgDevelopment/borg-platform/tree/develop/frontend/packages/odin-mcp",
12
+ "homepage": "https://docs.borghq.io/platform/odin-mcp",
13
13
  "bugs": {
14
- "url": "https://github.com/BorgDevelopment/borg-platform/issues"
14
+ "email": "mail@borghq.io"
15
15
  },
16
16
  "repository": {
17
17
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/tools/finding-actions.ts","../src/tools/finding-reference.ts","../src/tools/finding-tool.ts","../src/tools/finding-attachments.ts","../src/tools/finding-collaboration.ts","../src/tools/finding-pull-requests.ts","../src/tools/findings-overview.ts","../src/tools/list-findings.ts","../src/formatters/markdown.ts","../src/tools/additional-tools.ts","../src/tools/get-asset.ts","../src/tools/get-finding.ts","../src/tools/get-finding-retests.ts","../src/tools/get-pentest.ts","../src/tools/link-finding-pull-request.ts","../src/tools/list-assets.ts","../src/tools/list-pentests.ts","../src/tools/update-finding-status.ts","../src/tools.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { OdinApiError, type OdinClient } from \"../client.js\";\nimport type {\n FindingSubscriptionResponse,\n RetestRequestResponse,\n} from \"../types.js\";\nimport {\n findingReferencePath,\n findingReferenceValue,\n} from \"./finding-reference.js\";\nimport { findingToolInput, writeAnnotations } from \"./finding-tool.js\";\n\nexport const followFindingInput = findingToolInput({\n follow: z\n .boolean()\n .describe(\"True to follow the finding, false to stop following it.\"),\n});\n\nexport const followFindingTool = {\n name: \"follow_finding\",\n title: \"Follow or unfollow a finding\",\n description:\n \"Follow a finding to get its notifications, or stop following it. Requires write permission and acts as the API key creator. The assignee always follows their finding and can't unfollow it.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: followFindingInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof followFindingInput>) => {\n const data = await client.put<FindingSubscriptionResponse>(\n findingReferencePath(params, \"/subscription\"),\n { subscribed: params.follow },\n );\n const label = findingReferenceValue(params);\n return {\n markdown: data.subscribed\n ? `Following ${label}.`\n : `Stopped following ${label}.`,\n structured: data,\n };\n },\n};\n\nexport const requestFindingRetestInput = findingToolInput({});\n\nexport const requestFindingRetestTool = {\n name: \"request_finding_retest\",\n title: \"Request a finding retest\",\n description:\n \"Queue a manual retest that checks whether a finding is still exploitable. Spends credits, so confirm with the user first. Works for findings in REPORTED, ACKNOWLEDGED, MITIGATING or OPEN_FOR_RETEST status with no retest already queued or running. Requires write permission and acts as the API key creator. Read the result later with get_finding_retests.\",\n annotations: writeAnnotations,\n inputSchema: requestFindingRetestInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof requestFindingRetestInput>) => {\n try {\n const data = await client.post<RetestRequestResponse>(\n findingReferencePath(params, \"/retest\"),\n {},\n );\n return {\n markdown: `Queued retest ${data.retestId} for ${findingReferenceValue(params)}.`,\n structured: data,\n };\n } catch (error) {\n if (error instanceof OdinApiError && isRetestUnavailable(error.body)) {\n const reason = \"Retests aren't available for this organisation.\";\n return {\n markdown: reason,\n structured: { state: \"unavailable\", reason },\n };\n }\n throw error;\n }\n },\n};\n\nfunction isRetestUnavailable(body: unknown): boolean {\n return (\n typeof body === \"object\" &&\n body !== null &&\n (body as { code?: unknown }).code === \"RETEST_UNAVAILABLE\"\n );\n}\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type { FindingDetailResponse } from \"../types.js\";\n\nexport const findingReferenceFields = {\n identifier: z\n .string()\n .trim()\n .min(1)\n .optional()\n .describe(\n 'Human-readable finding identifier such as \"ODI-12\". Preferred input.',\n ),\n findingId: z\n .string()\n .trim()\n .min(1)\n .optional()\n .describe(\n \"Raw internal finding ID, useful when chaining from another tool.\",\n ),\n};\n\nexport const findingReferenceInput = z\n .object(findingReferenceFields)\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport type FindingReferenceInput = z.infer<typeof findingReferenceInput>;\n\n/** Prefer the human reference when both values are supplied. */\nexport function findingReferenceValue(\n reference: FindingReferenceInput,\n): string {\n const value = reference.identifier ?? reference.findingId;\n if (!value) {\n throw new Error(\"A finding identifier or findingId is required.\");\n }\n return value;\n}\n\nexport function findingReferencePath(\n reference: FindingReferenceInput,\n suffix = \"\",\n): string {\n return `/api/findings/${encodeURIComponent(findingReferenceValue(reference))}${suffix}`;\n}\n\n/**\n * Resolves only when a caller supplied an identifier. A supplied internal ID\n * is already suitable for mutation chaining and avoids an unnecessary read.\n */\nexport async function resolveFindingId(\n client: OdinClient,\n reference: FindingReferenceInput,\n): Promise<string> {\n if (reference.findingId) return reference.findingId;\n\n const data = await client.get<FindingDetailResponse>(\n findingReferencePath(reference),\n );\n return data.remediationContext?.findingId ?? data.finding.findingId;\n}\n","import { z } from \"zod\";\nimport { findingReferenceFields } from \"./finding-reference.js\";\n\nexport function findingToolInput<T extends z.ZodRawShape>(fields: T) {\n return z.object({ ...findingReferenceFields, ...fields }).refine(\n (value) => {\n const reference = value as { identifier?: string; findingId?: string };\n return Boolean(reference.identifier || reference.findingId);\n },\n { message: \"Provide either identifier or findingId.\" },\n );\n}\n\nexport const readAnnotations = {\n readOnlyHint: true,\n destructiveHint: false,\n idempotentHint: true,\n openWorldHint: false,\n};\n\n/**\n * Every tool that changes Odin data is marked destructive, so clients ask\n * before running it. Odin's guidance also tells agents to ask first.\n */\nexport const writeAnnotations = {\n readOnlyHint: false,\n destructiveHint: true,\n idempotentHint: false,\n openWorldHint: true,\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type { AttachmentUploadResponse } from \"../types.js\";\nimport { findingReferencePath } from \"./finding-reference.js\";\nimport {\n findingToolInput,\n readAnnotations,\n writeAnnotations,\n} from \"./finding-tool.js\";\n\nexport const prepareFindingAttachmentUploadInput = findingToolInput({\n filename: z.string().min(1).max(500),\n contentType: z.enum([\n \"image/png\",\n \"image/jpeg\",\n \"image/gif\",\n \"image/webp\",\n \"application/pdf\",\n \"application/zip\",\n \"application/json\",\n \"application/octet-stream\",\n \"text/plain\",\n \"text/markdown\",\n \"text/csv\",\n \"text/log\",\n ]),\n sizeBytes: z\n .number()\n .int()\n .min(1)\n .max(25 * 1024 * 1024),\n});\n\nexport const prepareFindingAttachmentUploadTool = {\n name: \"prepare_finding_attachment_upload\",\n title: \"Prepare a private discussion attachment upload\",\n description:\n \"Create a five-minute signed upload URL for a file up to 25 MiB. Requires write permission. Does not upload bytes or post a message. The caller must PUT the exact file to uploadUrl using requiredHeaders, without the Odin API key, then pass publicUrl as the attachment URL to create_finding_message. publicUrl is a private gs:// object reference. Treat signed URLs as temporary secrets.\",\n annotations: writeAnnotations,\n inputSchema: prepareFindingAttachmentUploadInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof prepareFindingAttachmentUploadInput>) => {\n const data = await client.post<AttachmentUploadResponse>(\n findingReferencePath(params, \"/thread/upload-url\"),\n {\n filename: params.filename,\n contentType: params.contentType,\n sizeBytes: params.sizeBytes,\n },\n );\n return {\n markdown:\n \"Upload URL prepared; no bytes have been uploaded. PUT the file to uploadUrl with requiredHeaders, then attach publicUrl in a discussion message. Do not send the Odin API key to the upload URL.\",\n structured: data,\n };\n },\n};\n\nexport const getFindingAttachmentUrlInput = findingToolInput({\n messageId: z.string().min(1),\n url: z\n .string()\n .url()\n .describe(\n \"Exact private attachment URL from this message's attachments array.\",\n ),\n});\n\nexport const getFindingAttachmentUrlTool = {\n name: \"get_finding_attachment_url\",\n title: \"Get a temporary attachment download URL\",\n description:\n \"Resolve a stored discussion attachment to a ten-minute signed download URL. Requires read permission and the attachment must belong to the selected finding and message. Does not download bytes or forward the Odin key to storage. Treat the returned signed URL as a temporary secret.\",\n annotations: { ...readAnnotations, openWorldHint: true },\n inputSchema: getFindingAttachmentUrlInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingAttachmentUrlInput>) => {\n const url = await client.getRedirectUrl(\n findingReferencePath(\n params,\n `/thread/attachments/${encodeURIComponent(params.messageId)}`,\n ),\n { url: params.url },\n );\n return {\n markdown:\n \"Temporary attachment download URL resolved. No bytes were downloaded; the URL is in the structured result.\",\n structured: { url },\n };\n },\n};\n\nexport const getFindingSlackUrlInput = findingToolInput({\n messageId: z.string().min(1).optional(),\n threadId: z.string().min(1).optional(),\n}).refine((value) => Boolean(value.messageId || value.threadId), {\n message: \"Provide a messageId or threadId.\",\n});\n\nexport const getFindingSlackUrlTool = {\n name: \"get_finding_slack_url\",\n title: \"Get a synced discussion's Slack permalink\",\n description:\n \"Resolve a synced message or thread to its Slack permalink. Requires read permission. Does not navigate to Slack or forward the Odin API key.\",\n annotations: { ...readAnnotations, openWorldHint: true },\n inputSchema: getFindingSlackUrlInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingSlackUrlInput>) => {\n const url = await client.getRedirectUrl(\n findingReferencePath(params, \"/thread/slack-link\"),\n { messageId: params.messageId, threadId: params.threadId },\n );\n return { markdown: `Slack permalink: ${url}`, structured: { url } };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type {\n DiscussionMessageResponse,\n FindingDiscussionResponse,\n FindingMentionablesResponse,\n FindingStatusUpdateResponse,\n FindingThreadResponse,\n} from \"../types.js\";\nimport { findingReferencePath } from \"./finding-reference.js\";\nimport {\n findingToolInput,\n readAnnotations,\n writeAnnotations,\n} from \"./finding-tool.js\";\n\nconst attachmentInput = z.object({\n type: z.enum([\"IMAGE\", \"FILE\"]),\n url: z\n .string()\n .url()\n .describe(\n \"Private gs:// URL returned by prepare_finding_attachment_upload after uploading the file.\",\n ),\n name: z.string().min(1).max(500),\n sizeBytes: z.number().int().nonnegative().optional(),\n mimeType: z.string().min(1).max(255).optional(),\n});\n\nexport const assignFindingInput = findingToolInput({\n assigneeUserId: z\n .string()\n .min(1)\n .nullable()\n .describe(\"Organisation member user ID, or null to clear assignment.\"),\n});\n\nexport const assignFindingTool = {\n name: \"assign_finding\",\n title: \"Assign a finding\",\n description:\n \"Assign a finding to an organisation member, or clear its assignment. Requires write permission. Assignment can notify the assignee by email and Slack and synchronise connected issue trackers.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: assignFindingInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof assignFindingInput>) => {\n const data = await client.patch<FindingStatusUpdateResponse>(\n findingReferencePath(params, \"/assignee\"),\n { assigneeUserId: params.assigneeUserId },\n );\n return {\n markdown: `Updated assignment for ${data.finding.identifier ?? data.finding.findingId}: ${params.assigneeUserId ?? \"unassigned\"}.`,\n structured: { finding: data.finding },\n };\n },\n};\n\nexport const getFindingDiscussionInput = findingToolInput({\n limit: z.number().int().min(1).max(100).optional(),\n offset: z.number().int().nonnegative().optional(),\n before: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\n \"Only messages before this timestamp, in the API's stored timestamp units.\",\n ),\n});\n\nexport const getFindingDiscussionTool = {\n name: \"get_finding_discussion\",\n title: \"Read finding discussions\",\n description:\n \"Read native and Slack-synchronised conversation blocks for a finding. Each block paginates independently. API keys have no personal read or unread state.\",\n annotations: readAnnotations,\n inputSchema: getFindingDiscussionInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingDiscussionInput>) => {\n const data = await client.get<FindingDiscussionResponse>(\n findingReferencePath(params, \"/thread\"),\n { limit: params.limit, offset: params.offset, before: params.before },\n );\n const markdown = data.blocks.length\n ? data.blocks\n .map((block) =>\n [\n `## ${block.thread.type} discussion ${block.thread.threadId} (${block.thread.status})`,\n `More messages: ${block.hasMore}.`,\n ...block.messages.map(\n (message) =>\n `### ${message.author.displayName} (${message.messageId})\\n${message.bodyMd}`,\n ),\n ].join(\"\\n\\n\"),\n )\n .join(\"\\n\\n\")\n : \"No discussions exist for this finding.\";\n return { markdown, structured: data };\n },\n};\n\nexport const createFindingMessageInput = findingToolInput({\n bodyMd: z\n .string()\n .min(1)\n .max(50_000)\n .describe(\n \"Markdown body. Mention syntax: @[Label](USER:id), @[Label](OPERATOR:id), @[ODI-12](FINDING:id), or @[Pentest](PENTEST:id); use list_finding_mentionables to discover IDs.\",\n ),\n threadId: z\n .string()\n .min(1)\n .optional()\n .describe(\"Existing block to reply to; omit for the native discussion.\"),\n attachments: z.array(attachmentInput).max(20).optional(),\n});\n\nexport const createFindingMessageTool = {\n name: \"create_finding_message\",\n title: \"Post a finding discussion message\",\n description:\n \"Post a Markdown discussion message. Requires write permission and acts as the API key creator. May send email, mention notifications and Slack messages. Repeating a call creates another message.\",\n annotations: writeAnnotations,\n inputSchema: createFindingMessageInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof createFindingMessageInput>) => {\n const data = await client.post<DiscussionMessageResponse>(\n findingReferencePath(params, \"/thread\"),\n {\n bodyMd: params.bodyMd,\n threadId: params.threadId,\n attachments: params.attachments,\n },\n );\n return {\n markdown: `Posted message ${data.message.messageId}.`,\n structured: data,\n };\n },\n};\n\nexport const editFindingMessageInput = findingToolInput({\n messageId: z.string().min(1),\n bodyMd: z.string().min(1).max(50_000),\n});\n\nexport const editFindingMessageTool = {\n name: \"edit_finding_message\",\n title: \"Edit a finding discussion message\",\n description:\n \"Edit a message authored by the API key creator. Requires write permission. New mentions can send notifications and the edit can synchronise to Slack.\",\n annotations: writeAnnotations,\n inputSchema: editFindingMessageInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof editFindingMessageInput>) => {\n const data = await client.patch<DiscussionMessageResponse>(\n findingReferencePath(\n params,\n `/thread/${encodeURIComponent(params.messageId)}`,\n ),\n { bodyMd: params.bodyMd },\n );\n return {\n markdown: `Edited message ${data.message.messageId}.`,\n structured: data,\n };\n },\n};\n\nexport const deleteFindingMessageInput = findingToolInput({\n messageId: z.string().min(1),\n});\n\nexport const deleteFindingMessageTool = {\n name: \"delete_finding_message\",\n title: \"Delete a finding discussion message\",\n description:\n \"Delete a message authored by the API key creator. Requires write permission and may delete its Slack mirror. Confirm the user's intent before deleting.\",\n annotations: writeAnnotations,\n inputSchema: deleteFindingMessageInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof deleteFindingMessageInput>) => {\n const data = await client.delete<FindingThreadResponse>(\n findingReferencePath(\n params,\n `/thread/${encodeURIComponent(params.messageId)}`,\n ),\n );\n return {\n markdown: `Deleted message ${params.messageId}.`,\n structured: data,\n };\n },\n};\n\nexport const updateFindingDiscussionInput = findingToolInput({\n action: z.enum([\"resolve\", \"reopen\"]),\n});\n\nexport const updateFindingDiscussionTool = {\n name: \"update_finding_discussion\",\n title: \"Resolve or reopen the native discussion\",\n description:\n \"Resolve or reopen a finding's native discussion. Requires write permission and may synchronise the status to Slack. This does not change the finding's security status.\",\n annotations: writeAnnotations,\n inputSchema: updateFindingDiscussionInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof updateFindingDiscussionInput>) => {\n const data = await client.post<FindingThreadResponse>(\n findingReferencePath(params, \"/thread/resolve\"),\n { action: params.action },\n );\n return {\n markdown: `Discussion status: ${data.thread?.status ?? params.action}.`,\n structured: data,\n };\n },\n};\n\nexport const listFindingMentionablesInput = findingToolInput({\n q: z.string().trim().max(100).optional(),\n});\n\nexport const listFindingMentionablesTool = {\n name: \"list_finding_mentionables\",\n title: \"Find people and entities to mention\",\n description:\n \"Find authorised people, findings and pentests that can be mentioned in a finding discussion. Returns up to eight people, eight findings and eight pentests matching q.\",\n annotations: readAnnotations,\n inputSchema: listFindingMentionablesInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof listFindingMentionablesInput>) => {\n const data = await client.get<FindingMentionablesResponse>(\n findingReferencePath(params, \"/thread/mentionables\"),\n { q: params.q },\n );\n return {\n markdown:\n data.mentionables\n .map((item) => `- ${item.label}: ${item.id}`)\n .join(\"\\n\") || \"No matching mentionables.\",\n structured: data,\n };\n },\n};\n\nexport const disconnectFindingDiscussionInput = findingToolInput({\n threadId: z.string().min(1),\n});\n\nexport const disconnectFindingDiscussionTool = {\n name: \"disconnect_finding_slack_thread\",\n title: \"Disconnect a synced Slack thread\",\n description:\n \"Disconnect a finding's SLACK_SYNC discussion block while preserving imported messages. Stops future synchronisation and may post a confirmation to Slack. Requires write permission. Confirm the user's intent before disconnecting.\",\n annotations: writeAnnotations,\n inputSchema: disconnectFindingDiscussionInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof disconnectFindingDiscussionInput>) => {\n const data = await client.post<FindingThreadResponse>(\n findingReferencePath(params, \"/thread/disconnect\"),\n { threadId: params.threadId },\n );\n return {\n markdown: `Disconnected Slack discussion ${params.threadId}. Existing messages are preserved.`,\n structured: data,\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type {\n FindingPullRequestsResponse,\n UnlinkFindingPullRequestResponse,\n} from \"../types.js\";\nimport { findingReferencePath } from \"./finding-reference.js\";\nimport {\n findingToolInput,\n readAnnotations,\n writeAnnotations,\n} from \"./finding-tool.js\";\n\nexport const listFindingPullRequestsInput = findingToolInput({\n query: z\n .string()\n .optional()\n .describe(\"PR number, title search, owner/repo#123, or GitHub PR URL.\"),\n page: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).optional(),\n});\n\nexport const listFindingPullRequestsTool = {\n name: \"list_finding_pull_requests\",\n title: \"Find accessible pull requests to link\",\n description:\n \"Search accessible GitHub pull requests for this finding. Defaults to the finding's repository, otherwise its connected GitHub organisation. Results are advisory; link_finding_pull_request verifies the chosen PR. Requires read permission and a configured GitHub integration.\",\n annotations: { ...readAnnotations, openWorldHint: true },\n inputSchema: listFindingPullRequestsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof listFindingPullRequestsInput>) => {\n const data = await client.get<FindingPullRequestsResponse>(\n findingReferencePath(params, \"/link-pr\"),\n { query: params.query, page: params.page },\n );\n return {\n markdown: [\n `Accessible pull requests (${data.repository ?? \"connected organisation\"}); more pages: ${data.pagination.hasMore}.`,\n ...data.pullRequests.map(\n (pr) =>\n `- ${pr.repo}#${pr.number}: ${pr.title} (${pr.state}) ${pr.url}`,\n ),\n ].join(\"\\n\"),\n structured: data,\n };\n },\n};\n\nexport const unlinkFindingPullRequestInput = findingToolInput({\n prNodeId: z\n .string()\n .min(1)\n .describe(\n \"Stored prNodeId of a manually linked pull request from get_finding.\",\n ),\n});\n\nexport const unlinkFindingPullRequestTool = {\n name: \"unlink_finding_pull_request\",\n title: \"Unlink a manually linked pull request\",\n description:\n \"Remove a manual finding-to-PR link. Requires write permission. Automatic links cannot be removed with this operation. Does not modify, close, merge or delete the GitHub PR. Confirm the user's intent before removing the link.\",\n annotations: { ...writeAnnotations, openWorldHint: false },\n inputSchema: unlinkFindingPullRequestInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof unlinkFindingPullRequestInput>) => {\n const data = await client.delete<UnlinkFindingPullRequestResponse>(\n findingReferencePath(params, \"/link-pr\"),\n { prNodeId: params.prNodeId },\n );\n return {\n markdown: `Removed manual pull request link ${params.prNodeId} from ${data.finding.identifier ?? data.finding.findingId}.`,\n structured: data,\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport type {\n FindingAssetsResponse,\n FindingRepositoriesResponse,\n FindingStatsResponse,\n FindingsExportResponse,\n RetestSummariesResponse,\n ThreadSummariesResponse,\n} from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\nimport { findingFilterFields, serializeFindingQuery } from \"./list-findings.js\";\n\nexport const getFindingStatsInput = z.object({\n pentestId: z.string().optional(),\n});\n\nexport const getFindingStatsTool = {\n name: \"get_finding_stats\",\n title: \"Get finding statistics\",\n description:\n \"Read aggregate finding totals by severity and status, optionally scoped to a pentest. These totals do not apply other finding list filters.\",\n annotations: readAnnotations,\n inputSchema: getFindingStatsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingStatsInput>) => {\n const data = await client.get<FindingStatsResponse>(\n \"/api/findings/stats\",\n params,\n );\n return {\n markdown: `Total findings: ${data.stats.total}.\\n${JSON.stringify(data.stats, null, 2)}`,\n structured: data,\n };\n },\n};\n\nexport const listFindingRepositoriesTool = {\n name: \"list_finding_repositories\",\n title: \"List repositories with findings\",\n description:\n \"List organisation repositories available for the repository filter in list_findings.\",\n annotations: readAnnotations,\n inputSchema: z.object({}),\n handler: (client: OdinClient) => async () => {\n const data = await client.get<FindingRepositoriesResponse>(\n \"/api/findings/repositories\",\n );\n return {\n markdown:\n data.repositories.map((repository) => `- ${repository}`).join(\"\\n\") ||\n \"No repositories with findings.\",\n structured: data,\n };\n },\n};\n\nexport const listFindingAssetsTool = {\n name: \"list_finding_assets\",\n title: \"List assets referenced by findings\",\n description:\n \"List organisation assets referenced by findings for use with the assetId filter. This differs from listing all attack-surface assets.\",\n annotations: readAnnotations,\n inputSchema: z.object({}),\n handler: (client: OdinClient) => async () => {\n const data = await client.get<FindingAssetsResponse>(\n \"/api/findings/assets\",\n );\n return {\n markdown:\n data.assets\n .map((asset) => `- ${asset.value} (${asset.type}, ${asset.assetId})`)\n .join(\"\\n\") || \"No assets referenced by findings.\",\n structured: data,\n };\n },\n};\n\nexport const exportFindingsInput = z.object(findingFilterFields);\n\nexport const exportFindingsTool = {\n name: \"export_findings\",\n title: \"Export findings and revision content\",\n description:\n \"Export findings matching the supplied filters, including revision Markdown and affected assets. The API returns all matching rows without pagination; narrow the filters for large organisations. This reads data and creates no file.\",\n annotations: readAnnotations,\n inputSchema: exportFindingsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof exportFindingsInput>) => {\n const data = await client.get<FindingsExportResponse>(\n \"/api/findings/export\",\n serializeFindingQuery(params),\n );\n return {\n markdown: `Exported ${data.findings.length} matching finding(s). Revision content is in the structured findings result.`,\n structured: data,\n };\n },\n};\n\nexport const findingSummariesInput = z.object({\n findingIds: z\n .array(z.string().trim().min(1))\n .max(200)\n .describe(\n \"Up to 200 internal finding IDs from list_findings. Human identifiers are not accepted by these batch endpoints.\",\n ),\n});\n\nexport const getFindingRetestSummariesTool = {\n name: \"get_finding_retest_summaries\",\n title: \"Get latest retests for several findings\",\n description:\n \"Read the latest retest summary for up to 200 finding IDs. Requires read permission and the organisation's retest capability; never starts a retest or spends credits. Missing entries have no latest retest.\",\n annotations: readAnnotations,\n inputSchema: findingSummariesInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof findingSummariesInput>) => {\n const data = await client.post<RetestSummariesResponse>(\n \"/api/findings/retest-summaries\",\n params,\n );\n return {\n markdown: `Latest retests available for ${Object.keys(data.summaries).length} finding(s).\\n${JSON.stringify(data.summaries, null, 2)}`,\n structured: data,\n };\n },\n};\n\nexport const getFindingDiscussionSummariesTool = {\n name: \"get_finding_discussion_summaries\",\n title: \"Get discussion summaries for several findings\",\n description:\n \"Read message counts and unresolved discussion state for up to 200 finding IDs. API keys have no personal unread state; unreadCount is zero for key-authenticated calls.\",\n annotations: readAnnotations,\n inputSchema: findingSummariesInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof findingSummariesInput>) => {\n const data = await client.post<ThreadSummariesResponse>(\n \"/api/findings/thread-summaries\",\n params,\n );\n return {\n markdown: `Discussion summaries available for ${Object.keys(data.summaries).length} finding(s).\\n${JSON.stringify(data.summaries, null, 2)}`,\n structured: data,\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingsList } from \"../formatters/markdown.js\";\nimport type { FindingsListResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nconst severityInput = z.enum([\n \"CRITICAL\",\n \"HIGH\",\n \"MEDIUM\",\n \"LOW\",\n \"INFORMATIONAL\",\n]);\nconst statusInput = z.enum([\n \"REPORTED\",\n \"ACKNOWLEDGED\",\n \"MITIGATING\",\n \"OPEN_FOR_RETEST\",\n \"NEEDS_REVISION\",\n \"FIXED_AND_RETESTED\",\n]);\nconst sourceInput = z.enum([\n \"MANUAL\",\n \"WATCHDOG\",\n \"IMPORT\",\n \"AGENT\",\n \"THREAT_MODEL\",\n]);\nconst confirmationTypeInput = z.enum([\"static\", \"partial\", \"exploited\"]);\nconst assigneeInput = z.string().trim().min(1).max(500);\n\nexport const findingFilterFields = {\n pentestId: z.string().optional().describe(\"Filter to a single pentest.\"),\n assetId: z\n .string()\n .optional()\n .describe(\n \"Filter to findings whose latest revision references this asset.\",\n ),\n repository: z\n .string()\n .optional()\n .describe(\"Filter to a source repository such as owner/name.\"),\n severity: z.union([severityInput, z.array(severityInput).min(1)]).optional(),\n status: z.union([statusInput, z.array(statusInput).min(1)]).optional(),\n excludeStatuses: z\n .union([statusInput, z.array(statusInput).min(1)])\n .optional()\n .describe(\"Exclude these client-visible statuses.\"),\n source: z.union([sourceInput, z.array(sourceInput).min(1)]).optional(),\n confirmationType: z\n .union([confirmationTypeInput, z.array(confirmationTypeInput).min(1)])\n .optional(),\n assignee: z\n .union([assigneeInput, z.array(assigneeInput).min(1)])\n .refine((value) => String(value).length <= 500, {\n message: \"Combined assignee filter must be at most 500 characters.\",\n })\n .optional()\n .describe(\n \"Client user ID(s), or __unassigned__ to include findings without an assignee.\",\n ),\n cvssMin: z.number().min(0).max(10).optional(),\n cvssMax: z.number().min(0).max(10).optional(),\n hideDormant: z\n .boolean()\n .optional()\n .describe(\"Hide findings on dormant assets.\"),\n search: z\n .string()\n .optional()\n .describe(\"Free-text search on finding titles and human identifiers.\"),\n};\n\nexport const listFindingsInput = z.object({\n ...findingFilterFields,\n lean: z\n .boolean()\n .optional()\n .describe(\n \"Exclude heavy evidence from list results; details stay available.\",\n ),\n stats: z\n .boolean()\n .optional()\n .describe(\"Include aggregate statistics (defaults to true).\"),\n sortBy: z.enum([\"severity\", \"createdAt\"]).optional(),\n sortOrder: z.enum([\"asc\", \"desc\"]).optional(),\n limit: z.number().int().min(1).max(200).optional().default(50),\n offset: z.number().int().min(0).optional().default(0),\n});\n\nexport function serializeFindingQuery(\n params: Record<string, string | number | boolean | string[] | undefined>,\n): Record<string, string | number | undefined> {\n return Object.fromEntries(\n Object.entries(params).map(([key, value]) => [\n key,\n Array.isArray(value)\n ? value.join(\",\")\n : typeof value === \"boolean\"\n ? String(value)\n : value,\n ]),\n );\n}\n\nexport const listFindingsTool = {\n name: \"list_findings\",\n title: \"List findings\",\n description:\n \"List security findings for the organisation. Filter by pentest, asset, repository, severity, client-visible status, source, confirmation type, assignee, CVSS range, or dormant assets. Search titles or human identifiers such as ODI-12.\",\n annotations: readAnnotations,\n inputSchema: listFindingsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof listFindingsInput>) => {\n const data = await client.get<FindingsListResponse>(\n \"/api/findings\",\n serializeFindingQuery(params),\n );\n return {\n markdown: formatFindingsList(data),\n structured: {\n findings: data.findings,\n stats: data.stats,\n pagination: data.pagination,\n },\n };\n },\n};\n","import type {\n Asset,\n AssetDetailResponse,\n AssetsListResponse,\n FindingDetailResponse,\n FindingStatus,\n FindingSummary,\n FindingsListResponse,\n LinkFindingPullRequestResponse,\n Pentest,\n PentestDetailResponse,\n PentestListResponse,\n Retest,\n RetestHistoryResponse,\n} from \"../types.js\";\n\nconst STATUS_LABELS: Record<FindingStatus, string> = {\n REPORTED: \"Reported\",\n ACKNOWLEDGED: \"Risk accepted\",\n MITIGATING: \"Fixing\",\n OPEN_FOR_RETEST: \"Open for Retest\",\n NEEDS_REVISION: \"Needs Revision\",\n FIXED_AND_RETESTED: \"Fixed & Retested\",\n};\n\nconst RETEST_STATUS_LABELS: Record<Retest[\"status\"], string> = {\n QUEUED: \"Queued\",\n RUNNING: \"Running\",\n COMPLETED: \"Completed\",\n FAILED: \"Failed\",\n SKIPPED: \"Skipped\",\n};\n\nexport function formatFindingsList(data: FindingsListResponse): string {\n const { findings, pagination } = data;\n if (findings.length === 0) {\n return \"No findings match the supplied filters.\";\n }\n\n const rows = findings.map((f) => {\n const severity = `${f.severity.label} (CVSS ${f.severity.cvss.toFixed(1)})`;\n return `| ${f.identifier ?? \"—\"} | ${f.findingId} | ${escapePipes(f.title)} | ${severity} | ${formatStatus(f.status)} | ${formatDate(f.createdAt)} |`;\n });\n\n return [\n `Showing ${findings.length} of ${pagination.total} finding(s). Offset ${pagination.offset}, limit ${pagination.limit}. hasMore=${pagination.hasMore}.`,\n \"\",\n \"| Identifier | Internal ID | Title | Severity | Status | Created |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...rows,\n ].join(\"\\n\");\n}\n\nexport function formatFindingDetail(data: FindingDetailResponse): string {\n const { finding, revision } = data;\n const context = data.remediationContext;\n const identifier = context?.identifier ?? finding.identifier ?? null;\n const displayIdentifier = identifier ?? finding.findingId;\n const severity = `${finding.severity.label} (CVSS ${finding.severity.cvss.toFixed(1)})`;\n const header = [\n `# ${escapePipes(displayIdentifier)} — ${escapePipes(finding.title)}`,\n \"\",\n `- **Finding:** ${displayIdentifier}`,\n `- **Internal ID:** ${finding.findingId}`,\n `- **Severity:** ${severity}`,\n `- **Status:** ${formatStatus(finding.status)}`,\n `- **Repository:** ${context?.repository ?? finding.repository ?? \"Not recorded\"}`,\n `- **Pentest:** ${finding.context?.pentestId ?? \"standalone\"}`,\n `- **Created:** ${formatDate(finding.createdAt)}`,\n `- **Updated:** ${formatDate(finding.updatedAt)}`,\n ];\n\n const content = context\n ? {\n revisionNumber: context.revision.revisionNumber,\n summary: context.revision.summary,\n description: context.revision.description,\n impact: context.revision.impact,\n details: context.revision.details,\n remediation: context.revision.remediation,\n affectedAssets: context.revision.affectedAssets,\n codeReference: context.revision.codeReference,\n }\n : revision\n ? {\n revisionNumber: revision.revisionNumber,\n summary: revision.content.summaryMd,\n description: revision.content.descriptionMd,\n impact: revision.content.businessImpactMd,\n details: revision.content.detailsMd,\n remediation: revision.content.remediationMd,\n affectedAssets: revision.content.affectedAssets ?? [],\n codeReference: null,\n }\n : null;\n\n const sections: string[] = [];\n if (!content) {\n sections.push(\"_No revision content available._\");\n } else {\n sections.push(`_Revision ${content.revisionNumber ?? \"unknown\"}_`);\n if (content.summary) sections.push(\"## Summary\", content.summary);\n if (content.description)\n sections.push(\"## Description\", content.description);\n if (content.impact) sections.push(\"## Business Impact\", content.impact);\n if (content.details) sections.push(\"## Details\", content.details);\n if (content.remediation)\n sections.push(\"## Remediation\", content.remediation);\n if (content.codeReference) {\n sections.push(\n \"## Code reference\",\n `- ${content.codeReference.file}:${content.codeReference.line}`,\n );\n if (content.codeReference.snippet) {\n sections.push(\"```\", content.codeReference.snippet, \"```\");\n }\n }\n if (content.affectedAssets?.length) {\n sections.push(\n \"## Affected Assets\",\n content.affectedAssets\n .map((asset) => `- ${asset.display} (${asset.assetId})`)\n .join(\"\\n\"),\n );\n }\n }\n\n if (context) {\n sections.push(\n \"## Remediation workflow\",\n `- **Suggested branch:** ${context.suggestedBranch ?? \"Not available\"}`,\n `- **Ready for a user-created PR:** ${yesNo(context.workflow.readyForUserCreatedPullRequest)}`,\n `- **GitHub connected:** ${yesNo(context.workflow.githubConnected)}`,\n `- **Automatic retests:** ${context.workflow.automaticRetestsReady ? \"Ready after merge\" : \"Not ready\"}`,\n );\n if (context.workflow.blockers.length) {\n sections.push(\n \"### Blockers\",\n context.workflow.blockers.map((blocker) => `- ${blocker}`).join(\"\\n\"),\n );\n }\n if (context.workflow.nextSteps.length) {\n sections.push(\n \"### Next steps\",\n context.workflow.nextSteps.map((step) => `- ${step}`).join(\"\\n\"),\n );\n }\n\n if (context.linkedPullRequests.length) {\n sections.push(\n \"## Linked GitHub pull requests\",\n context.linkedPullRequests\n .map(\n (pullRequest) =>\n `- [#${pullRequest.number}](${pullRequest.url}) ${escapePipes(pullRequest.title)} — ${pullRequest.state}, linked via ${pullRequest.linkedVia}`,\n )\n .join(\"\\n\"),\n );\n } else {\n sections.push(\"## Linked GitHub pull requests\", \"_None linked._\");\n }\n\n sections.push(\"## Latest retest\");\n if (context.latestRetest.state === \"unavailable\") {\n sections.push(`_${context.latestRetest.reason}_`);\n } else if (context.latestRetest.state === \"none\") {\n sections.push(\"_No retest history is available yet._\");\n } else {\n sections.push(formatRetestSummary(context.latestRetest.retest));\n }\n }\n\n return [...header, \"\", ...sections].join(\"\\n\");\n}\n\nexport function formatFindingStatusUpdate(finding: FindingSummary): string {\n const identifier = finding.identifier ?? finding.findingId;\n return [\n `Updated finding **${identifier}** (internal ID ${finding.findingId}) status to **${formatStatus(finding.status)}**.`,\n `Title: ${finding.title}`,\n ].join(\"\\n\");\n}\n\nexport function formatFindingPullRequestLink(\n data: LinkFindingPullRequestResponse,\n): string {\n const identifier = data.finding.identifier ?? data.finding.findingId;\n const pullRequest = data.pullRequest;\n const readiness = data.workflow.automaticRetestsReady\n ? \"Automatic retesting is ready after this PR merges.\"\n : \"Automatic retesting is not currently ready; review the blockers below.\";\n const blockers = data.workflow.blockers.length\n ? `\\n${data.workflow.blockers.map((blocker) => `- ${blocker}`).join(\"\\n\")}`\n : \"\";\n\n return [\n `Linked existing GitHub pull request [#${pullRequest.number}](${pullRequest.url}) to **${identifier}**.`,\n `Repository: ${pullRequest.repo}; state: ${pullRequest.state}; finding status: ${formatStatus(data.finding.status)}.`,\n \"Odin did not create, modify, merge, or push this pull request.\",\n readiness,\n blockers ? `Workflow blockers:${blockers}` : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\");\n}\n\nexport function formatFindingRetests(data: RetestHistoryResponse): string {\n if (data.state === \"none\" || data.retests.length === 0) {\n return \"No retest history is available for this finding.\";\n }\n\n return [\n `Retest history (${data.retests.length}):`,\n \"\",\n \"| Retest | Status | Trigger | Verdict | Created | Completed |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...data.retests.map(\n (retest) =>\n `| ${retest.retestId} | ${RETEST_STATUS_LABELS[retest.status]} | ${retest.trigger} | ${retest.combinedVerdict ?? \"—\"} | ${formatDate(retest.createdAt)} | ${formatDate(retest.completedAt ?? 0)} |`,\n ),\n \"\",\n \"Queued and running entries are still in progress; completed, skipped, and failed entries are final for that run.\",\n ].join(\"\\n\");\n}\n\nexport function formatPentestsList(data: PentestListResponse): string {\n if (data.pentests.length === 0) return \"No pentests found.\";\n const rows = data.pentests.map(formatPentestRow);\n return [\n \"| Pentest ID | Name | Status | Start | End | Scope |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...rows,\n ].join(\"\\n\");\n}\n\nexport function formatPentestDetail(data: PentestDetailResponse): string {\n const p = data.pentest;\n const end = p.endAt ? formatDate(p.endAt) : \"open\";\n const scope = p.scope.length\n ? p.scope.map((s) => `- ${s}`).join(\"\\n\")\n : \"_No scope defined._\";\n return [\n `# ${p.name}`,\n \"\",\n `- **ID:** ${p.pentestId}`,\n `- **Status:** ${p.status}`,\n `- **Start:** ${formatDate(p.startAt)}`,\n `- **End:** ${end}`,\n `- **Created:** ${formatDate(p.createdAt)}`,\n \"\",\n \"## Scope\",\n \"\",\n scope,\n ].join(\"\\n\");\n}\n\nexport function formatFindingsForAsset(findings: FindingSummary[]): string {\n if (findings.length === 0) return \"_No findings reference this asset._\";\n return [\n `## Findings (${findings.length})`,\n \"\",\n \"| Identifier | Internal ID | Title | Severity | Status | Created |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...findings.map(formatFindingRow),\n ].join(\"\\n\");\n}\n\nexport function formatAssetsList(data: AssetsListResponse): string {\n const { assets, pagination } = data;\n if (assets.length === 0) return \"No assets match the supplied filters.\";\n const rows = assets.map(formatAssetRow);\n return [\n `Showing ${assets.length} of ${pagination.total} asset(s). Offset ${pagination.offset}, limit ${pagination.limit}. hasMore=${pagination.hasMore}.`,\n \"\",\n \"| Asset ID | Type | Value | Source | Status | Last seen |\",\n \"| --- | --- | --- | --- | --- | --- |\",\n ...rows,\n ].join(\"\\n\");\n}\n\nexport function formatAssetDetail(data: AssetDetailResponse): string {\n const { asset, findings } = data;\n const header = [\n `# ${asset.value}`,\n \"\",\n `- **ID:** ${asset.assetId}`,\n `- **Type:** ${asset.type}`,\n `- **Source:** ${asset.source}`,\n `- **Status:** ${asset.status}`,\n `- **Last seen:** ${formatDate(asset.lastSeenAt)}`,\n `- **Created:** ${formatDate(asset.createdAt)}`,\n ];\n\n return [...header, \"\", formatFindingsForAsset(findings)].join(\"\\n\");\n}\n\nfunction formatFindingRow(finding: FindingSummary): string {\n const severity = `${finding.severity.label} (CVSS ${finding.severity.cvss.toFixed(1)})`;\n return `| ${finding.identifier ?? \"—\"} | ${finding.findingId} | ${escapePipes(finding.title)} | ${severity} | ${formatStatus(finding.status)} | ${formatDate(finding.createdAt)} |`;\n}\n\nfunction formatPentestRow(p: Pentest): string {\n const end = p.endAt ? formatDate(p.endAt) : \"open\";\n const scope =\n p.scope.slice(0, 3).join(\", \") + (p.scope.length > 3 ? \", …\" : \"\");\n return `| ${p.pentestId} | ${escapePipes(p.name)} | ${p.status} | ${formatDate(p.startAt)} | ${end} | ${escapePipes(scope)} |`;\n}\n\nfunction formatAssetRow(a: Asset): string {\n return `| ${a.assetId} | ${a.type} | ${escapePipes(a.value)} | ${a.source} | ${a.status} | ${formatDate(a.lastSeenAt)} |`;\n}\n\nfunction formatRetestSummary(retest: {\n status: Retest[\"status\"];\n combinedVerdict: Retest[\"combinedVerdict\"];\n statusAction: Retest[\"statusAction\"];\n trigger: Retest[\"trigger\"];\n createdAt: number;\n completedAt: number | null;\n}): string {\n return [\n `- **Status:** ${RETEST_STATUS_LABELS[retest.status]}`,\n `- **Trigger:** ${retest.trigger}`,\n `- **Verdict:** ${retest.combinedVerdict ?? \"Not available yet\"}`,\n `- **Finding action:** ${retest.statusAction ?? \"None\"}`,\n `- **Created:** ${formatDate(retest.createdAt)}`,\n `- **Completed:** ${formatDate(retest.completedAt ?? 0)}`,\n ].join(\"\\n\");\n}\n\nfunction formatStatus(status: FindingStatus): string {\n return STATUS_LABELS[status] ?? status;\n}\n\nfunction yesNo(value: boolean): string {\n return value ? \"Yes\" : \"No\";\n}\n\nfunction escapePipes(value: string): string {\n return value.replace(/\\|/g, \"\\\\|\");\n}\n\nfunction formatDate(epoch: number): string {\n if (!epoch) return \"—\";\n // Odin stores timestamps as milliseconds for documents and seconds for some fields\n // (pentest startAt/endAt are seconds). Heuristic: values < 10^12 are seconds.\n const ms = epoch < 1e12 ? epoch * 1000 : epoch;\n return new Date(ms).toISOString().slice(0, 10);\n}\n","import {\n followFindingTool,\n requestFindingRetestTool,\n} from \"./finding-actions.js\";\nimport {\n getFindingAttachmentUrlTool,\n getFindingSlackUrlTool,\n prepareFindingAttachmentUploadTool,\n} from \"./finding-attachments.js\";\nimport {\n assignFindingTool,\n createFindingMessageTool,\n deleteFindingMessageTool,\n disconnectFindingDiscussionTool,\n editFindingMessageTool,\n getFindingDiscussionTool,\n listFindingMentionablesTool,\n updateFindingDiscussionTool,\n} from \"./finding-collaboration.js\";\nimport {\n listFindingPullRequestsTool,\n unlinkFindingPullRequestTool,\n} from \"./finding-pull-requests.js\";\nimport {\n exportFindingsTool,\n getFindingDiscussionSummariesTool,\n getFindingRetestSummariesTool,\n getFindingStatsTool,\n listFindingAssetsTool,\n listFindingRepositoriesTool,\n} from \"./findings-overview.js\";\n\nexport const additionalFindingTools = [\n getFindingStatsTool,\n listFindingRepositoriesTool,\n listFindingAssetsTool,\n exportFindingsTool,\n getFindingRetestSummariesTool,\n getFindingDiscussionSummariesTool,\n assignFindingTool,\n getFindingDiscussionTool,\n createFindingMessageTool,\n editFindingMessageTool,\n deleteFindingMessageTool,\n updateFindingDiscussionTool,\n listFindingMentionablesTool,\n disconnectFindingDiscussionTool,\n prepareFindingAttachmentUploadTool,\n getFindingAttachmentUrlTool,\n getFindingSlackUrlTool,\n listFindingPullRequestsTool,\n unlinkFindingPullRequestTool,\n followFindingTool,\n requestFindingRetestTool,\n];\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatAssetDetail } from \"../formatters/markdown.js\";\nimport type { AssetDetailResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getAssetInput = z.object({\n assetId: z.string().min(1),\n});\n\nexport const getAssetTool = {\n name: \"get_asset\",\n title: \"Get asset\",\n description:\n \"Fetch a single attack-surface asset by ID, including the findings that reference it in their latest revision.\",\n annotations: readAnnotations,\n inputSchema: getAssetInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof getAssetInput>) => {\n const data = await client.get<AssetDetailResponse>(\n `/api/assets/${encodeURIComponent(params.assetId)}`,\n );\n return {\n markdown: formatAssetDetail(data),\n structured: { asset: data.asset, findings: data.findings },\n };\n },\n};\n","import type { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingDetail } from \"../formatters/markdown.js\";\nimport type { FindingDetailResponse } from \"../types.js\";\nimport {\n findingReferenceInput,\n findingReferencePath,\n} from \"./finding-reference.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getFindingInput = findingReferenceInput;\n\nexport const getFindingTool = {\n name: \"get_finding\",\n title: \"Get finding remediation context\",\n description:\n \"Fetch a finding by human identifier such as ODI-12 (preferred) or by internal findingId. Returns bounded remediation context, the suggested branch name, linked GitHub pull requests, workflow readiness, and retest state. Odin only provides context and can link an existing PR; it never creates commits, pushes branches, or creates pull requests.\",\n annotations: readAnnotations,\n inputSchema: getFindingInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof getFindingInput>) => {\n const data = await client.get<FindingDetailResponse>(\n findingReferencePath(params),\n );\n return {\n markdown: formatFindingDetail(data),\n structured: {\n finding: data.remediationContext ?? data.finding,\n revision: data.remediationContext?.revision ?? data.revision,\n remediationContext: data.remediationContext ?? null,\n },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { OdinApiError } from \"../client.js\";\nimport { formatFindingRetests } from \"../formatters/markdown.js\";\nimport type { RetestHistoryResponse } from \"../types.js\";\nimport {\n findingReferenceFields,\n findingReferencePath,\n} from \"./finding-reference.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getFindingRetestsInput = z\n .object(findingReferenceFields)\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport const getFindingRetestsTool = {\n name: \"get_finding_retests\",\n title: \"Get finding retest history\",\n description:\n \"Read the retest history for a finding by human identifier such as ODI-12 or internal findingId. Distinguishes no history, queued or running, completed, skipped, failed, and gated/unavailable results. This tool never triggers a retest or spends credits.\",\n annotations: readAnnotations,\n inputSchema: getFindingRetestsInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof getFindingRetestsInput>) => {\n try {\n const data = await client.get<RetestHistoryResponse>(\n findingReferencePath(params, \"/retests\"),\n );\n return {\n markdown: formatFindingRetests(data),\n structured: {\n state: data.state,\n retests: data.retests,\n },\n };\n } catch (error) {\n if (\n error instanceof OdinApiError &&\n error.status === 404 &&\n isRetestHistoryUnavailable(error.body)\n ) {\n const reason =\n \"Retest history is gated or unavailable for this organisation.\";\n return {\n markdown: reason,\n structured: { state: \"unavailable\", reason, retests: [] },\n };\n }\n throw error;\n }\n },\n};\n\nfunction isRetestHistoryUnavailable(body: unknown): boolean {\n return (\n typeof body === \"object\" &&\n body !== null &&\n \"code\" in body &&\n (body as { code?: unknown }).code === \"RETEST_HISTORY_UNAVAILABLE\"\n );\n}\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatPentestDetail } from \"../formatters/markdown.js\";\nimport type { PentestDetailResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const getPentestInput = z.object({\n pentestId: z.string().min(1),\n});\n\nexport const getPentestTool = {\n name: \"get_pentest\",\n title: \"Get pentest\",\n description:\n \"Fetch a single pentest by ID, including status, scheduled window, and in-scope targets.\",\n annotations: readAnnotations,\n inputSchema: getPentestInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof getPentestInput>) => {\n const data = await client.get<PentestDetailResponse>(\n `/api/pentests/${encodeURIComponent(params.pentestId)}`,\n );\n return {\n markdown: formatPentestDetail(data),\n structured: { pentest: data.pentest },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingPullRequestLink } from \"../formatters/markdown.js\";\nimport type { LinkFindingPullRequestResponse } from \"../types.js\";\nimport {\n findingReferenceFields,\n findingReferencePath,\n} from \"./finding-reference.js\";\nimport { writeAnnotations } from \"./finding-tool.js\";\n\nexport const linkFindingPullRequestInput = z\n .object({\n ...findingReferenceFields,\n url: z\n .string()\n .url()\n .describe(\"Existing GitHub pull request URL to link to the finding.\"),\n })\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport const linkFindingPullRequestTool = {\n name: \"link_finding_pull_request\",\n title: \"Link an existing GitHub pull request\",\n description:\n \"Link an existing accessible GitHub pull request to a finding identified by ODI-12 or findingId. This operation is idempotent. It does not create, modify, merge, or push a pull request, commit, or branch, and it requires an API key with Read & Write permission; cookie-authenticated callers also need member role. Ask the user before linking.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: linkFindingPullRequestInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof linkFindingPullRequestInput>) => {\n const data = await client.post<LinkFindingPullRequestResponse>(\n findingReferencePath(params, \"/link-pr\"),\n { url: params.url },\n );\n return {\n markdown: formatFindingPullRequestLink(data),\n structured: {\n finding: data.finding,\n pullRequest: data.pullRequest,\n linkSource: data.linkSource,\n workflow: data.workflow,\n },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatAssetsList } from \"../formatters/markdown.js\";\nimport type { AssetsListResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const listAssetsInput = z.object({\n type: z.enum([\"DOMAIN\", \"SUBDOMAIN\", \"IP\"]).optional(),\n source: z.enum([\"MANUAL\", \"RECON\", \"IMPORT\", \"AGENT\"]).optional(),\n status: z\n .string()\n .optional()\n .describe(\n \"Dynamic asset status set by recon (e.g. ACTIVE, INACTIVE, TLS_ERROR).\",\n ),\n search: z.string().optional(),\n stats: z\n .boolean()\n .optional()\n .describe(\"Include aggregate statistics (defaults to true).\"),\n sortBy: z.enum([\"lastSeenAt\", \"createdAt\", \"value\"]).optional(),\n sortOrder: z.enum([\"asc\", \"desc\"]).optional(),\n limit: z.number().int().min(1).max(100).optional().default(50),\n offset: z.number().int().min(0).optional().default(0),\n});\n\nexport const listAssetsTool = {\n name: \"list_assets\",\n title: \"List assets\",\n description:\n \"List attack-surface assets for the organisation. Supports filtering by type, source, status, and free-text search.\",\n annotations: readAnnotations,\n inputSchema: listAssetsInput,\n handler:\n (client: OdinClient) => async (params: z.infer<typeof listAssetsInput>) => {\n const data = await client.get<AssetsListResponse>(\"/api/assets\", {\n ...params,\n stats: params.stats === undefined ? undefined : String(params.stats),\n });\n return {\n markdown: formatAssetsList(data),\n structured: {\n assets: data.assets,\n stats: data.stats,\n pagination: data.pagination,\n },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatPentestsList } from \"../formatters/markdown.js\";\nimport type { PentestListResponse } from \"../types.js\";\nimport { readAnnotations } from \"./finding-tool.js\";\n\nexport const listPentestsInput = z.object({});\n\nexport const listPentestsTool = {\n name: \"list_pentests\",\n title: \"List pentests\",\n description:\n \"List all pentests for the organisation, including status, scheduled window, and in-scope targets.\",\n annotations: readAnnotations,\n inputSchema: listPentestsInput,\n handler: (client: OdinClient) => async () => {\n const data = await client.get<PentestListResponse>(\"/api/pentests\");\n return {\n markdown: formatPentestsList(data),\n structured: { pentests: data.pentests },\n };\n },\n};\n","import { z } from \"zod\";\nimport type { OdinClient } from \"../client.js\";\nimport { formatFindingStatusUpdate } from \"../formatters/markdown.js\";\nimport type { FindingStatusUpdateResponse } from \"../types.js\";\nimport {\n findingReferenceFields,\n resolveFindingId,\n} from \"./finding-reference.js\";\nimport { writeAnnotations } from \"./finding-tool.js\";\n\nexport const updateFindingStatusInput = z\n .object({\n ...findingReferenceFields,\n status: z\n .enum([\"MITIGATING\", \"OPEN_FOR_RETEST\", \"ACKNOWLEDGED\"])\n .describe(\n \"Target status. MITIGATING (shown in Odin as Fixing) means work has started, OPEN_FOR_RETEST means the fix is ready for verification, and ACKNOWLEDGED (shown as Risk accepted) means the organisation accepts the risk for now.\",\n ),\n })\n .refine((value) => Boolean(value.identifier || value.findingId), {\n message: \"Provide either identifier or findingId.\",\n });\n\nexport const updateFindingStatusTool = {\n name: \"update_finding_status\",\n title: \"Update finding status\",\n description:\n \"Change a finding's status by human identifier such as ODI-12 or internal findingId. Requires an API key with Read & Write permission; cookie-authenticated callers also need member role. Only use this after the user explicitly asks for the status change; a matching pull request is not permission to mutate a finding.\",\n annotations: { ...writeAnnotations, idempotentHint: true },\n inputSchema: updateFindingStatusInput,\n handler:\n (client: OdinClient) =>\n async (params: z.infer<typeof updateFindingStatusInput>) => {\n const findingId = await resolveFindingId(client, params);\n const data = await client.patch<FindingStatusUpdateResponse>(\n `/api/findings/${encodeURIComponent(findingId)}/status`,\n { status: params.status },\n );\n return {\n markdown: formatFindingStatusUpdate(data.finding),\n structured: { finding: data.finding },\n };\n },\n};\n","import type { z } from \"zod\";\nimport type { OdinClient } from \"./client.js\";\nimport { additionalFindingTools } from \"./tools/additional-tools.js\";\nimport { getAssetTool } from \"./tools/get-asset.js\";\nimport { getFindingTool } from \"./tools/get-finding.js\";\nimport { getFindingRetestsTool } from \"./tools/get-finding-retests.js\";\nimport { getPentestTool } from \"./tools/get-pentest.js\";\nimport { linkFindingPullRequestTool } from \"./tools/link-finding-pull-request.js\";\nimport { listAssetsTool } from \"./tools/list-assets.js\";\nimport { listFindingsTool } from \"./tools/list-findings.js\";\nimport { listPentestsTool } from \"./tools/list-pentests.js\";\nimport { updateFindingStatusTool } from \"./tools/update-finding-status.js\";\n\nexport interface OdinToolAnnotations {\n readOnlyHint: boolean;\n destructiveHint: boolean;\n idempotentHint: boolean;\n openWorldHint: boolean;\n}\n\nexport interface OdinToolResult {\n markdown: string;\n structured: unknown;\n}\n\n/** One Odin tool, independent of the MCP transport that serves it. */\nexport interface OdinTool {\n name: string;\n title: string;\n description: string;\n inputSchema: z.ZodType;\n annotations: OdinToolAnnotations;\n run: (client: OdinClient, params: unknown) => Promise<OdinToolResult>;\n}\n\nconst definitions = [\n listFindingsTool,\n getFindingTool,\n updateFindingStatusTool,\n linkFindingPullRequestTool,\n getFindingRetestsTool,\n listPentestsTool,\n getPentestTool,\n listAssetsTool,\n getAssetTool,\n ...additionalFindingTools,\n];\n\n/**\n * Every tool odin-mcp serves, in registration order. The MCP server and\n * web-odin's Slack agent both run these, each with its own `OdinClient`.\n */\nexport const ODIN_TOOLS: readonly OdinTool[] = definitions.map((tool) => ({\n name: tool.name,\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: tool.annotations,\n run: (client, params) =>\n (tool.handler(client) as (params: unknown) => Promise<OdinToolResult>)(\n params,\n ),\n}));\n"],"mappings":"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","names":["z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z","z"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server.ts","../package.json"],"sourcesContent":["import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { version } from \"../package.json\";\nimport { OdinApiError, type OdinClient } from \"./client.js\";\nimport {\n buildFixFindingPrompt,\n buildServerInstructions,\n buildSetupPrompt,\n} from \"./guidance.js\";\nimport { ODIN_TOOLS } from \"./tools.js\";\n\nexport const PACKAGE_VERSION = version;\n\nexport interface OdinMcpServerOptions {\n /** Public Odin origin for links in guidance. Defaults to production. */\n appUrl?: string;\n}\n\nexport function createOdinMcpServer(\n client: OdinClient,\n options: OdinMcpServerOptions = {},\n) {\n const { appUrl } = options;\n const server = new McpServer(\n { name: \"@borgresearch/odin-mcp\", version: PACKAGE_VERSION },\n { instructions: buildServerInstructions({ appUrl }) },\n );\n\n server.registerPrompt(\n \"setup\",\n {\n title: \"Set up Odin\",\n description:\n \"Learn what Odin does, check what is set up, get recommendations for this repository and reach a first result.\",\n },\n () => ({\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: buildSetupPrompt({ appUrl }),\n },\n },\n ],\n }),\n );\n\n server.registerPrompt(\n \"fix_finding\",\n {\n title: \"Fix an Odin finding\",\n description:\n \"Fix one finding in this repository, open a pull request and link it so Odin can retest the fix.\",\n argsSchema: {\n finding: z\n .string()\n .max(100)\n .optional()\n .describe(\n \"Finding ID such as ODI-12. Leave empty to pick the most severe open finding.\",\n ),\n },\n },\n ({ finding }) => ({\n messages: [\n {\n role: \"user\" as const,\n content: {\n type: \"text\" as const,\n text: buildFixFindingPrompt({ appUrl, finding }),\n },\n },\n ],\n }),\n );\n\n for (const tool of ODIN_TOOLS) {\n server.registerTool(\n tool.name,\n {\n title: tool.title,\n description: tool.description,\n inputSchema: tool.inputSchema,\n annotations: tool.annotations,\n },\n async (params: unknown) => {\n try {\n const { markdown, structured } = await tool.run(client, params);\n return {\n content: [{ type: \"text\" as const, text: markdown }],\n structuredContent: { markdown, ...toObject(structured) },\n };\n } catch (error) {\n return {\n isError: true,\n content: [{ type: \"text\" as const, text: toErrorMessage(error) }],\n };\n }\n },\n );\n }\n\n return server;\n}\n\nfunction toObject(value: unknown): Record<string, unknown> {\n if (value && typeof value === \"object\" && !Array.isArray(value)) {\n return value as Record<string, unknown>;\n }\n return { data: value };\n}\n\nfunction toErrorMessage(error: unknown): string {\n if (error instanceof OdinApiError) return error.message;\n if (error instanceof Error) return `Unexpected error: ${error.message}`;\n return `Unexpected error: ${String(error)}`;\n}\n","{\n \"name\": \"@borgresearch/odin-mcp\",\n \"version\": \"0.3.0\",\n \"mcpName\": \"io.github.BorgDevelopment/odin-mcp\",\n \"description\": \"MCP server for the Odin security API\",\n \"keywords\": [\n \"mcp\",\n \"model-context-protocol\",\n \"odin\",\n \"security\"\n ],\n \"homepage\": \"https://github.com/BorgDevelopment/borg-platform/tree/develop/frontend/packages/odin-mcp\",\n \"bugs\": {\n \"url\": \"https://github.com/BorgDevelopment/borg-platform/issues\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/BorgDevelopment/borg-platform.git\",\n \"directory\": \"frontend/packages/odin-mcp\"\n },\n \"license\": \"MIT\",\n \"author\": \"Borg Research\",\n \"type\": \"module\",\n \"bin\": {\n \"odin-mcp\": \"./dist/index.js\"\n },\n \"main\": \"./dist/index.js\",\n \"module\": \"./dist/index.js\",\n \"types\": \"./dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./dist/index.d.ts\",\n \"import\": \"./dist/index.js\"\n },\n \"./server\": {\n \"types\": \"./dist/server.d.ts\",\n \"import\": \"./dist/server.js\"\n },\n \"./http\": {\n \"types\": \"./dist/http.d.ts\",\n \"import\": \"./dist/http.js\"\n },\n \"./client\": {\n \"types\": \"./dist/client.d.ts\",\n \"import\": \"./dist/client.js\"\n },\n \"./guidance\": {\n \"types\": \"./dist/guidance.d.ts\",\n \"import\": \"./dist/guidance.js\"\n },\n \"./tools\": {\n \"types\": \"./dist/tools.d.ts\",\n \"import\": \"./dist/tools.js\"\n }\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"publishConfig\": {\n \"access\": \"public\",\n \"registry\": \"https://registry.npmjs.org/\"\n },\n \"engines\": {\n \"node\": \">=24.0.0\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"typecheck\": \"tsc --noEmit\",\n \"test\": \"vitest run\",\n \"test:run\": \"vitest run\",\n \"lint\": \"biome check .\",\n \"format\": \"biome format --write .\",\n \"inspect\": \"mcp-inspector node dist/index.js\",\n \"inspect:dev\": \"pnpm build && pnpm inspect\"\n },\n \"dependencies\": {\n \"@modelcontextprotocol/sdk\": \"^1.31.0\",\n \"zod\": \"^4.3.6\"\n },\n \"devDependencies\": {\n \"@biomejs/biome\": \"2.3.14\",\n \"@modelcontextprotocol/inspector\": \"^0.21.2\",\n \"@types/node\": \"^24.10.1\",\n \"tsup\": \"^8.3.5\",\n \"typescript\": \"^5.9.3\",\n \"vitest\": \"^4.1.11\"\n },\n \"packageManager\": \"pnpm@11.2.2\"\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,SAAS;;;ACChB,cAAW;;;ADSN,IAAM,kBAAkB;AAOxB,SAAS,oBACd,QACA,UAAgC,CAAC,GACjC;AACA,QAAM,EAAE,OAAO,IAAI;AACnB,QAAM,SAAS,IAAI;AAAA,IACjB,EAAE,MAAM,0BAA0B,SAAS,gBAAgB;AAAA,IAC3D,EAAE,cAAc,wBAAwB,EAAE,OAAO,CAAC,EAAE;AAAA,EACtD;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,IACJ;AAAA,IACA,OAAO;AAAA,MACL,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,YACP,MAAM;AAAA,YACN,MAAM,iBAAiB,EAAE,OAAO,CAAC;AAAA,UACnC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,MACE,OAAO;AAAA,MACP,aACE;AAAA,MACF,YAAY;AAAA,QACV,SAAS,EACN,OAAO,EACP,IAAI,GAAG,EACP,SAAS,EACT;AAAA,UACC;AAAA,QACF;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,EAAE,QAAQ,OAAO;AAAA,MAChB,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS;AAAA,YACP,MAAM;AAAA,YACN,MAAM,sBAAsB,EAAE,QAAQ,QAAQ,CAAC;AAAA,UACjD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,aAAW,QAAQ,YAAY;AAC7B,WAAO;AAAA,MACL,KAAK;AAAA,MACL;AAAA,QACE,OAAO,KAAK;AAAA,QACZ,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,QAClB,aAAa,KAAK;AAAA,MACpB;AAAA,MACA,OAAO,WAAoB;AACzB,YAAI;AACF,gBAAM,EAAE,UAAU,WAAW,IAAI,MAAM,KAAK,IAAI,QAAQ,MAAM;AAC9D,iBAAO;AAAA,YACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,SAAS,CAAC;AAAA,YACnD,mBAAmB,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE;AAAA,UACzD;AAAA,QACF,SAAS,OAAO;AACd,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,eAAe,KAAK,EAAE,CAAC;AAAA,UAClE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,SAAS,OAAyC;AACzD,MAAI,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,KAAK,GAAG;AAC/D,WAAO;AAAA,EACT;AACA,SAAO,EAAE,MAAM,MAAM;AACvB;AAEA,SAAS,eAAe,OAAwB;AAC9C,MAAI,iBAAiB,aAAc,QAAO,MAAM;AAChD,MAAI,iBAAiB,MAAO,QAAO,qBAAqB,MAAM,OAAO;AACrE,SAAO,qBAAqB,OAAO,KAAK,CAAC;AAC3C;","names":[]}