@@ -16,64 +16,81 @@ import (
1616)
1717
1818var (
19- //go:embed resources/info.md
20- infoMD string
19+ //go:embed resources/concepts-guide.md
20+ conceptsMD string
21+
22+ //go:embed resources/file-types-guide.md
23+ fileTypesMD string
2124
2225 //go:embed resources/flowfile_schema.json
2326 flowFileSchema string
2427
28+ //go:embed resources/template_schema.json
29+ templateSchema string
30+
2531 //go:embed resources/workspace_schema.json
2632 workspaceSchema string
27-
28- flowFileSchemaURI = "flow://schema/flowfile"
29- workspaceConfigSchemaURI = "flow://schema/workspace"
3033)
3134
3235func addServerResources (srv * server.MCPServer ) {
3336 getCtx := mcp .NewResource (
3437 "flow://context/current" ,
3538 "context" ,
3639 mcp .WithResourceDescription ("Current flow execution context (workspace, namespace, vault)" ),
37- mcp .WithMIMEType ("application/json" ),
38- )
40+ mcp .WithMIMEType ("application/json" ))
3941 srv .AddResource (getCtx , getContextResourceHandler )
4042
4143 getWorkspace := mcp .NewResourceTemplate (
4244 "flow://workspace/{name}" ,
4345 "workspace" ,
44- mcp .WithTemplateDescription ("Flow workspace configuration and details" ),
45- )
46+ mcp .WithTemplateDescription ("Flow workspace configuration and details" ))
4647 srv .AddResourceTemplate (getWorkspace , getWorkspaceResourceHandler )
4748
4849 getWorkspaceExecutables := mcp .NewResourceTemplate (
4950 "flow://workspace/{name}/executables" ,
5051 "workspace_executables" ,
51- mcp .WithTemplateDescription ("Flow executables for a given workspace" ),
52- )
52+ mcp .WithTemplateDescription ("Flow executables for a given workspace" ))
5353 srv .AddResourceTemplate (getWorkspaceExecutables , getWorkspaceExecutablesHandler )
5454
55- getFlowInfo := mcp .NewResource (
56- "flow://info" ,
57- "flow_info" ,
58- mcp .WithResourceDescription ("Information about Flow and it's usage" ),
59- mcp .WithMIMEType ("text/markdown" ),
60- )
61- srv .AddResource (getFlowInfo , getFlowInfoResourceHandler )
55+ getExecutable := mcp .NewResourceTemplate (
56+ "flow://executable/{ref}" ,
57+ "executable" ,
58+ mcp .WithTemplateDescription ("Flow executable details by reference" ))
59+ srv .AddResourceTemplate (getExecutable , getExecutableResourceHandler )
60+
61+ getFlowConcepts := mcp .NewResource (
62+ "flow://guide/concepts" ,
63+ "flow_concepts" ,
64+ mcp .WithResourceDescription ("Information about flow and it's usage" ),
65+ mcp .WithMIMEType ("text/markdown" ))
66+ srv .AddResource (getFlowConcepts , getFlowConceptsResourceHandler )
67+
68+ getFlowFileTypes := mcp .NewResource (
69+ "flow://guide/file-types" ,
70+ "flow_file_types" ,
71+ mcp .WithResourceDescription ("Information about flow file types and their usage" ),
72+ mcp .WithMIMEType ("text/markdown" ))
73+ srv .AddResource (getFlowFileTypes , getFlowFileTypesResourceHandler )
6274
6375 getFlowFileSchema := mcp .NewResource (
64- flowFileSchemaURI ,
76+ "flow://schema/flowfile" ,
6577 "flowfile_schema" ,
6678 mcp .WithResourceDescription ("Flow file (*.flow, *.flow.yaml, *.flow.yml) schema" ),
67- mcp .WithMIMEType ("application/json" ),
68- )
79+ mcp .WithMIMEType ("application/json" ))
6980 srv .AddResource (getFlowFileSchema , getFlowFileSchemaResourceHandler )
7081
82+ getTemplateSchema := mcp .NewResource (
83+ "flow://schema/template" ,
84+ "template_schema" ,
85+ mcp .WithResourceDescription ("Flow template (*.flow.tmpl, *.flow.yaml.tmpl, *.flow.yml.tmpl) schema" ),
86+ mcp .WithMIMEType ("application/json" ))
87+ srv .AddResource (getTemplateSchema , getTemplateSchemaResourceHandler )
88+
7189 getWorkspaceSchema := mcp .NewResource (
72- workspaceConfigSchemaURI ,
90+ "flow://schema/workspace" ,
7391 "workspace_schema" ,
7492 mcp .WithResourceDescription ("Flow workspace configuration (flow.yaml) schema" ),
75- mcp .WithMIMEType ("application/json" ),
76- )
93+ mcp .WithMIMEType ("application/json" ))
7794 srv .AddResource (getWorkspaceSchema , getWorkspaceSchemaResourceHandler )
7895}
7996
@@ -152,26 +169,49 @@ func getWorkspaceExecutablesHandler(_ context.Context, request mcp.ReadResourceR
152169 }, nil
153170}
154171
155- func extractWsName ( uri string ) (string , error ) {
156- // Assuming the URI is in the format "flow://workspace/{name}" or "flow://workspace/{name}/executables"
157- if len ( uri ) < len ( "flow://workspace/" ) {
158- return "" , fmt . Errorf ( "invalid workspace URI: %s" , uri )
172+ func getExecutableResourceHandler ( _ context. Context , request mcp. ReadResourceRequest ) ([]mcp. ResourceContents , error ) {
173+ executableVerb , executableID , err := extractExecutableRef ( request . Params . URI )
174+ if err != nil {
175+ return nil , err
159176 }
160177
161- wsName := uri [len ("flow://workspace/" ):]
162- strings .TrimSuffix (wsName , "/executables" )
163- if wsName == "" {
164- return "" , fmt .Errorf ("workspace name cannot be empty in URI: %s" , uri )
178+ cmdArgs := []string {"browse" , "--output" , "json" , executableVerb }
179+ if executableID != "" {
180+ cmdArgs = append (cmdArgs , executableID )
165181 }
166- return wsName , nil
182+ cmd := exec .Command ("flow" , cmdArgs ... )
183+
184+ output , err := cmd .CombinedOutput ()
185+ if err != nil {
186+ ref := strings .Join ([]string {executableVerb , executableID }, " " )
187+ return nil , fmt .Errorf ("%s executable details retrieval failed: %s" , ref , output )
188+ }
189+
190+ return []mcp.ResourceContents {
191+ mcp.TextResourceContents {
192+ URI : request .Params .URI ,
193+ MIMEType : "application/json" ,
194+ Text : string (output ),
195+ },
196+ }, nil
197+ }
198+
199+ func getFlowConceptsResourceHandler (_ context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
200+ return []mcp.ResourceContents {
201+ mcp.TextResourceContents {
202+ URI : request .Params .URI ,
203+ MIMEType : "text/markdown" ,
204+ Text : conceptsMD ,
205+ },
206+ }, nil
167207}
168208
169- func getFlowInfoResourceHandler (_ context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
209+ func getFlowFileTypesResourceHandler (_ context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
170210 return []mcp.ResourceContents {
171211 mcp.TextResourceContents {
172212 URI : request .Params .URI ,
173213 MIMEType : "text/markdown" ,
174- Text : infoMD ,
214+ Text : fileTypesMD ,
175215 },
176216 }, nil
177217}
@@ -186,6 +226,16 @@ func getFlowFileSchemaResourceHandler(_ context.Context, request mcp.ReadResourc
186226 }, nil
187227}
188228
229+ func getTemplateSchemaResourceHandler (_ context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
230+ return []mcp.ResourceContents {
231+ mcp.TextResourceContents {
232+ URI : request .Params .URI ,
233+ MIMEType : "application/json" ,
234+ Text : templateSchema ,
235+ },
236+ }, nil
237+ }
238+
189239func getWorkspaceSchemaResourceHandler (_ context.Context , request mcp.ReadResourceRequest ) ([]mcp.ResourceContents , error ) {
190240 return []mcp.ResourceContents {
191241 mcp.TextResourceContents {
@@ -195,3 +245,38 @@ func getWorkspaceSchemaResourceHandler(_ context.Context, request mcp.ReadResour
195245 },
196246 }, nil
197247}
248+
249+ func extractWsName (uri string ) (string , error ) {
250+ // Assuming the URI is in the format "flow://workspace/{name}" or "flow://workspace/{name}/executables"
251+ if len (uri ) < len ("flow://workspace/" ) {
252+ return "" , fmt .Errorf ("invalid workspace URI: %s" , uri )
253+ }
254+
255+ wsName := uri [len ("flow://workspace/" ):]
256+ strings .TrimSuffix (wsName , "/executables" )
257+ if wsName == "" {
258+ return "" , fmt .Errorf ("workspace name cannot be empty in URI: %s" , uri )
259+ }
260+ return wsName , nil
261+ }
262+
263+ func extractExecutableRef (uri string ) (string , string , error ) {
264+ if len (uri ) < len ("flow://executable/" ) {
265+ return "" , "" , fmt .Errorf ("invalid executable URI: %s" , uri )
266+ }
267+
268+ ref := uri [len ("flow://executable/" ):]
269+ if ref == "" {
270+ return "" , "" , fmt .Errorf ("executable reference cannot be empty in URI: %s" , uri )
271+ }
272+
273+ parts := strings .SplitN (ref , " " , 2 )
274+ switch len (parts ) {
275+ case 1 :
276+ return parts [0 ], "" , nil
277+ case 2 :
278+ return parts [0 ], parts [1 ], nil
279+ default :
280+ return "" , "" , fmt .Errorf ("invalid executable reference format in URI: %s" , uri )
281+ }
282+ }
0 commit comments