mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-07 08:02:40 +00:00
feat(mcp): add documentation endpoint (#7331)
* simplify inkeep endpoint + add to openapi * adapt frontend * add to mcp endpoints * nit * udpate ref
This commit is contained in:
@@ -1 +1 @@
|
||||
8f6e0bd9f273ac9da5f4fb3a44979f17b8e1b9b5
|
||||
ffc3e065de448f94d2cd6b39d4aac917e1a77307
|
||||
@@ -69,6 +69,41 @@ paths:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/inkeep:
|
||||
post:
|
||||
summary: query Windmill AI documentation assistant (EE only)
|
||||
operationId: queryDocumentation
|
||||
x-mcp-tool: true
|
||||
tags:
|
||||
- documentation
|
||||
requestBody:
|
||||
description: query to send to the AI documentation assistant
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
query:
|
||||
type: string
|
||||
description: The documentation query to send to the AI assistant
|
||||
required:
|
||||
- query
|
||||
responses:
|
||||
"200":
|
||||
description: AI documentation assistant response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
description: Response from Inkeep service
|
||||
"403":
|
||||
description: Enterprise Edition required
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/openapi.yaml:
|
||||
get:
|
||||
summary: get openapi yaml spec
|
||||
@@ -8930,9 +8965,9 @@ paths:
|
||||
"200":
|
||||
description: confirmation message
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
|
||||
/w/{workspace}/trigger/{trigger_kind}/cancel_suspended_trigger_jobs/{trigger_path}:
|
||||
post:
|
||||
@@ -16643,7 +16678,7 @@ components:
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
"unassigned_singlestepflow",
|
||||
]
|
||||
schedule_path:
|
||||
type: string
|
||||
@@ -16759,7 +16794,7 @@ components:
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
"unassigned_singlestepflow",
|
||||
]
|
||||
schedule_path:
|
||||
type: string
|
||||
@@ -16883,7 +16918,7 @@ components:
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
"unassigned_singlestepflow",
|
||||
]
|
||||
trigger:
|
||||
type: string
|
||||
@@ -17031,7 +17066,7 @@ components:
|
||||
"aiagent",
|
||||
"unassigned_script",
|
||||
"unassigned_flow",
|
||||
"unassigned_singlestepflow"
|
||||
"unassigned_singlestepflow",
|
||||
]
|
||||
trigger:
|
||||
type: string
|
||||
|
||||
@@ -3,9 +3,21 @@
|
||||
pub use crate::inkeep_ee::*;
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
use axum::Router;
|
||||
use axum::{routing::post, Router};
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
use windmill_common::error::Error;
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
pub fn global_service() -> Router {
|
||||
Router::new()
|
||||
Router::new().route("/", post(inkeep_not_available))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "private"))]
|
||||
async fn inkeep_not_available() -> windmill_common::error::Result<()> {
|
||||
Err(Error::Generic(
|
||||
http::StatusCode::FORBIDDEN,
|
||||
"Inkeep AI documentation assistant is only available in Windmill Enterprise Edition"
|
||||
.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,27 @@ pub struct EndpointTool {
|
||||
|
||||
pub fn all_tools() -> Vec<EndpointTool> {
|
||||
vec![
|
||||
EndpointTool {
|
||||
name: Cow::Borrowed("queryDocumentation"),
|
||||
description: Cow::Borrowed("query Windmill AI documentation assistant (EE only)"),
|
||||
instructions: Cow::Borrowed(""),
|
||||
path: Cow::Borrowed("/inkeep"),
|
||||
method: Cow::Borrowed("POST"),
|
||||
path_params_schema: None,
|
||||
query_params_schema: None,
|
||||
body_schema: Some(serde_json::json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "The documentation query to send to the AI assistant"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"query"
|
||||
]
|
||||
})),
|
||||
},
|
||||
EndpointTool {
|
||||
name: Cow::Borrowed("createVariable"),
|
||||
description: Cow::Borrowed("create variable"),
|
||||
@@ -447,6 +468,10 @@ pub fn all_tools() -> Vec<EndpointTool> {
|
||||
"languages": {
|
||||
"type": "string",
|
||||
"description": "Filter to only include scripts written in the given languages.\nAccepts multiple values as a comma-separated list.\n"
|
||||
},
|
||||
"without_description": {
|
||||
"type": "boolean",
|
||||
"description": "(default false)\nIf true, the description field will be omitted from the response.\n"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@@ -530,6 +555,10 @@ pub fn all_tools() -> Vec<EndpointTool> {
|
||||
"with_deployment_msg": {
|
||||
"type": "boolean",
|
||||
"description": "(default false)\ninclude deployment message\n"
|
||||
},
|
||||
"without_description": {
|
||||
"type": "boolean",
|
||||
"description": "(default false)\nIf true, the description field will be omitted from the response.\n"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@@ -683,6 +712,28 @@ pub fn all_tools() -> Vec<EndpointTool> {
|
||||
"type": "string",
|
||||
"description": "mask to filter by schedule path"
|
||||
},
|
||||
"trigger_path": {
|
||||
"type": "string",
|
||||
"description": "mask to filter by trigger path"
|
||||
},
|
||||
"trigger_kind": {
|
||||
"description": "trigger kind (schedule, http, websocket...)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"webhook",
|
||||
"default_email",
|
||||
"email",
|
||||
"schedule",
|
||||
"http",
|
||||
"websocket",
|
||||
"postgres",
|
||||
"kafka",
|
||||
"nats",
|
||||
"mqtt",
|
||||
"sqs",
|
||||
"gcp"
|
||||
]
|
||||
},
|
||||
"script_hash": {
|
||||
"type": "string",
|
||||
"description": "mask to filter exact matching path"
|
||||
@@ -817,10 +868,25 @@ pub fn all_tools() -> Vec<EndpointTool> {
|
||||
"format": "date-time",
|
||||
"description": "filter on created after (exclusive) timestamp"
|
||||
},
|
||||
"created_or_started_before": {
|
||||
"completed_before": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp"
|
||||
"description": "filter on started before (inclusive) timestamp"
|
||||
},
|
||||
"completed_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on started after (exclusive) timestamp"
|
||||
},
|
||||
"created_before_queue": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on jobs created before X for jobs in the queue only"
|
||||
},
|
||||
"created_after_queue": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on jobs created after X for jobs in the queue only"
|
||||
},
|
||||
"running": {
|
||||
"type": "boolean",
|
||||
@@ -830,16 +896,6 @@ pub fn all_tools() -> Vec<EndpointTool> {
|
||||
"type": "boolean",
|
||||
"description": "filter on jobs scheduled_for before now (hence waitinf for a worker)"
|
||||
},
|
||||
"created_or_started_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp"
|
||||
},
|
||||
"created_or_started_after_completed_jobs": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs"
|
||||
},
|
||||
"job_kinds": {
|
||||
"type": "string",
|
||||
"description": "filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by,"
|
||||
@@ -864,14 +920,28 @@ pub fn all_tools() -> Vec<EndpointTool> {
|
||||
"type": "boolean",
|
||||
"description": "allow wildcards (*) in the filter of label, tag, worker"
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"description": "which page to return (start at 1, default 1)"
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"description": "number of items to return for a given page (default 30, max 100)"
|
||||
},
|
||||
"trigger_kind": {
|
||||
"description": "trigger kind (schedule, http, websocket...)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"webhook",
|
||||
"default_email",
|
||||
"email",
|
||||
"schedule",
|
||||
"http",
|
||||
"websocket",
|
||||
"postgres",
|
||||
"kafka",
|
||||
"nats",
|
||||
"mqtt",
|
||||
"sqs",
|
||||
"gcp"
|
||||
]
|
||||
},
|
||||
"is_skipped": {
|
||||
"type": "boolean",
|
||||
"description": "is the job skipped"
|
||||
|
||||
@@ -235,13 +235,15 @@ async function getDocumentation(args: { request: string }): Promise<string> {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'inkeep-rag',
|
||||
messages: [{ role: 'user', content: args.request }],
|
||||
response_format: {
|
||||
type: 'json_object'
|
||||
}
|
||||
query: args.request
|
||||
})
|
||||
})
|
||||
|
||||
if (!retrieval.ok) {
|
||||
const errorText = await retrieval.text()
|
||||
throw new Error(errorText)
|
||||
}
|
||||
|
||||
const data = await retrieval.json()
|
||||
if (!data.choices?.[0]?.message?.content) {
|
||||
return 'No documentation found for this request'
|
||||
@@ -291,11 +293,11 @@ const getTriggerableComponentsTool: Tool<{}> = {
|
||||
def: GET_TRIGGERABLE_COMPONENTS_TOOL,
|
||||
fn: async ({ toolId, toolCallbacks }) => {
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'Scanning the page...',
|
||||
content: 'Scanning the page...'
|
||||
})
|
||||
const components = getTriggerableComponents()
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'Scanned the page',
|
||||
content: 'Scanned the page'
|
||||
})
|
||||
return components
|
||||
}
|
||||
@@ -319,9 +321,14 @@ export const getDocumentationTool: Tool<{}> = {
|
||||
toolCallbacks.setToolStatus(toolId, { content: 'Retrieved documentation' })
|
||||
return docResult
|
||||
} catch (error) {
|
||||
toolCallbacks.setToolStatus(toolId, { content: 'Error getting documentation', error: 'Error getting documentation' })
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'Error getting documentation',
|
||||
error: 'Error getting documentation'
|
||||
})
|
||||
console.error('Error getting documentation:', error)
|
||||
return 'Failed to get documentation, pursuing with the user request...'
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : 'An error occurred while getting documentation'
|
||||
return `Failed to get documentation: ${errorMessage}, pursuing with the user request...`
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,7 +342,10 @@ const getAvailableResourcesTool: Tool<{}> = {
|
||||
toolCallbacks.setToolStatus(toolId, { content: 'Retrieved available resources' })
|
||||
return resources
|
||||
} catch (error) {
|
||||
toolCallbacks.setToolStatus(toolId, { content: 'Error getting available resources', error: 'Error getting available resources' })
|
||||
toolCallbacks.setToolStatus(toolId, {
|
||||
content: 'Error getting available resources',
|
||||
error: 'Error getting available resources'
|
||||
})
|
||||
console.error('Error getting available resources:', error)
|
||||
return 'Failed to get available resources, pursuing with the user request...'
|
||||
}
|
||||
@@ -350,7 +360,9 @@ export const navigatorTools: Tool<{}>[] = [
|
||||
getAvailableResourcesTool
|
||||
]
|
||||
|
||||
export function prepareNavigatorSystemMessage(customPrompt?: string): ChatCompletionSystemMessageParam {
|
||||
export function prepareNavigatorSystemMessage(
|
||||
customPrompt?: string
|
||||
): ChatCompletionSystemMessageParam {
|
||||
let content = CHAT_SYSTEM_PROMPT
|
||||
|
||||
// If there's a custom prompt, append it to the system prompt
|
||||
|
||||
@@ -13,6 +13,27 @@ export interface EndpointTool {
|
||||
}
|
||||
|
||||
export const mcpEndpointTools: EndpointTool[] = [
|
||||
{
|
||||
name: "queryDocumentation",
|
||||
description: "query Windmill AI documentation assistant (EE only)",
|
||||
instructions: "",
|
||||
path: "/inkeep",
|
||||
method: "POST",
|
||||
pathParamsSchema: undefined,
|
||||
queryParamsSchema: undefined,
|
||||
bodySchema: {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "The documentation query to send to the AI assistant"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"query"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "createVariable",
|
||||
description: "create variable",
|
||||
@@ -440,6 +461,10 @@ export const mcpEndpointTools: EndpointTool[] = [
|
||||
"languages": {
|
||||
"type": "string",
|
||||
"description": "Filter to only include scripts written in the given languages.\nAccepts multiple values as a comma-separated list.\n"
|
||||
},
|
||||
"without_description": {
|
||||
"type": "boolean",
|
||||
"description": "(default false)\nIf true, the description field will be omitted from the response.\n"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@@ -523,6 +548,10 @@ export const mcpEndpointTools: EndpointTool[] = [
|
||||
"with_deployment_msg": {
|
||||
"type": "boolean",
|
||||
"description": "(default false)\ninclude deployment message\n"
|
||||
},
|
||||
"without_description": {
|
||||
"type": "boolean",
|
||||
"description": "(default false)\nIf true, the description field will be omitted from the response.\n"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
@@ -676,6 +705,28 @@ export const mcpEndpointTools: EndpointTool[] = [
|
||||
"type": "string",
|
||||
"description": "mask to filter by schedule path"
|
||||
},
|
||||
"trigger_path": {
|
||||
"type": "string",
|
||||
"description": "mask to filter by trigger path"
|
||||
},
|
||||
"trigger_kind": {
|
||||
"description": "trigger kind (schedule, http, websocket...)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"webhook",
|
||||
"default_email",
|
||||
"email",
|
||||
"schedule",
|
||||
"http",
|
||||
"websocket",
|
||||
"postgres",
|
||||
"kafka",
|
||||
"nats",
|
||||
"mqtt",
|
||||
"sqs",
|
||||
"gcp"
|
||||
]
|
||||
},
|
||||
"script_hash": {
|
||||
"type": "string",
|
||||
"description": "mask to filter exact matching path"
|
||||
@@ -810,10 +861,25 @@ export const mcpEndpointTools: EndpointTool[] = [
|
||||
"format": "date-time",
|
||||
"description": "filter on created after (exclusive) timestamp"
|
||||
},
|
||||
"created_or_started_before": {
|
||||
"completed_before": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on created_at for non non started job and started_at otherwise before (inclusive) timestamp"
|
||||
"description": "filter on started before (inclusive) timestamp"
|
||||
},
|
||||
"completed_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on started after (exclusive) timestamp"
|
||||
},
|
||||
"created_before_queue": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on jobs created before X for jobs in the queue only"
|
||||
},
|
||||
"created_after_queue": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on jobs created after X for jobs in the queue only"
|
||||
},
|
||||
"running": {
|
||||
"type": "boolean",
|
||||
@@ -823,16 +889,6 @@ export const mcpEndpointTools: EndpointTool[] = [
|
||||
"type": "boolean",
|
||||
"description": "filter on jobs scheduled_for before now (hence waitinf for a worker)"
|
||||
},
|
||||
"created_or_started_after": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp"
|
||||
},
|
||||
"created_or_started_after_completed_jobs": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "filter on created_at for non non started job and started_at otherwise after (exclusive) timestamp but only for the completed jobs"
|
||||
},
|
||||
"job_kinds": {
|
||||
"type": "string",
|
||||
"description": "filter on job kind (values 'preview', 'script', 'dependencies', 'flow') separated by,"
|
||||
@@ -857,14 +913,28 @@ export const mcpEndpointTools: EndpointTool[] = [
|
||||
"type": "boolean",
|
||||
"description": "allow wildcards (*) in the filter of label, tag, worker"
|
||||
},
|
||||
"page": {
|
||||
"type": "integer",
|
||||
"description": "which page to return (start at 1, default 1)"
|
||||
},
|
||||
"per_page": {
|
||||
"type": "integer",
|
||||
"description": "number of items to return for a given page (default 30, max 100)"
|
||||
},
|
||||
"trigger_kind": {
|
||||
"description": "trigger kind (schedule, http, websocket...)",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"webhook",
|
||||
"default_email",
|
||||
"email",
|
||||
"schedule",
|
||||
"http",
|
||||
"websocket",
|
||||
"postgres",
|
||||
"kafka",
|
||||
"nats",
|
||||
"mqtt",
|
||||
"sqs",
|
||||
"gcp"
|
||||
]
|
||||
},
|
||||
"is_skipped": {
|
||||
"type": "boolean",
|
||||
"description": "is the job skipped"
|
||||
|
||||
Reference in New Issue
Block a user