@@ -31,11 +31,73 @@ export interface ApiExtractionResult {
3131 releases : ExtractedResource [ ] ;
3232 tagDescriptions : ExtractedResource [ ] ;
3333 wiki : boolean ;
34+ mcpServer : boolean ;
3435 resolvers : ExtractedResource [ ] ;
3536 resolverPolicies : ExtractedResource [ ] ;
3637 policies : string [ ] ;
3738}
3839
40+ function getApiProperties ( apiJson : Record < string , unknown > ) : Record < string , unknown > | undefined {
41+ return apiJson . properties as Record < string , unknown > | undefined ;
42+ }
43+
44+ function hasEmbeddedMcpConfiguration ( apiJson : Record < string , unknown > ) : boolean {
45+ const properties = getApiProperties ( apiJson ) ;
46+ if ( ! properties ) {
47+ return false ;
48+ }
49+
50+ // Check if mcpTools has actual content (non-empty array), excluding null
51+ const mcpTools = properties . mcpTools as unknown [ ] | undefined | null ;
52+ if ( Array . isArray ( mcpTools ) && mcpTools . length > 0 ) {
53+ return true ;
54+ }
55+
56+ // Check if mcpProperties exists and is not null or undefined
57+ if ( properties . mcpProperties != null ) {
58+ return true ;
59+ }
60+
61+ return false ;
62+ }
63+
64+ function buildEmbeddedMcpServerResource ( apiJson : Record < string , unknown > ) : Record < string , unknown > {
65+ const properties = getApiProperties ( apiJson ) ?? { } ;
66+ const resourceProperties : Record < string , unknown > = { } ;
67+
68+ if ( properties . mcpProperties !== undefined ) {
69+ resourceProperties . mcpProperties = properties . mcpProperties ;
70+ }
71+ if ( properties . mcpTools !== undefined ) {
72+ resourceProperties . mcpTools = properties . mcpTools ;
73+ }
74+
75+ return {
76+ name : 'default' ,
77+ properties : resourceProperties ,
78+ } ;
79+
80+ }
81+
82+ function hasMeaningfulMcpContent ( mcpJson : Record < string , unknown > ) : boolean {
83+ const properties = mcpJson . properties as Record < string , unknown > | undefined ;
84+ if ( ! properties ) {
85+ return false ;
86+ }
87+
88+ // Check if mcpTools has actual content (non-empty array), excluding null
89+ const mcpTools = properties . mcpTools as unknown [ ] | undefined | null ;
90+ if ( Array . isArray ( mcpTools ) && mcpTools . length > 0 ) {
91+ return true ;
92+ }
93+
94+ // Check if mcpProperties exists and is not null or undefined
95+ if ( properties . mcpProperties != null ) {
96+ return true ;
97+ }
98+
99+ return false ;
100+ }
39101/**
40102 * Extract all API-specific resources for a single API.
41103 * This includes revisions, specifications, operations, policies, etc.
@@ -63,6 +125,7 @@ export async function extractApiResources(
63125 releases : [ ] ,
64126 tagDescriptions : [ ] ,
65127 wiki : false ,
128+ mcpServer : false ,
66129 resolvers : [ ] ,
67130 resolverPolicies : [ ] ,
68131 policies : [ ] ,
@@ -138,6 +201,11 @@ export async function extractApiResources(
138201 client , store , context , apiDescriptor , outputDir
139202 ) ;
140203
204+ // Extract MCP server configuration (singleton per API; silently skipped when not present)
205+ result . mcpServer = await extractApiMcpServer (
206+ client , store , context , apiDescriptor , apiJson , outputDir
207+ ) ;
208+
141209 // Extract GraphQL resolvers and their policies
142210 const resolverResult = await extractGraphQLResolvers (
143211 client , store , context , apiDescriptor , apiJson , outputDir , filter , workspace
@@ -237,6 +305,11 @@ async function extractApiSpecification(
237305 return false ;
238306 }
239307
308+ if ( apiType ?. toLowerCase ( ) === 'mcp' ) {
309+ logger . debug ( `Skipping spec export for MCP API "${ getNamePart ( apiDescriptor . nameParts , 0 ) } " — MCP APIs use the Model Context Protocol endpoint, not OpenAPI` ) ;
310+ return false ;
311+ }
312+
240313 if ( apiType ?. toLowerCase ( ) === 'graphql' && hasGraphQLSchema ( extractedSchemas ) ) {
241314 logger . debug (
242315 `Skipping spec export for synthetic GraphQL API "${ getNamePart ( apiDescriptor . nameParts , 0 ) } " — schema is captured via ApiSchema`
@@ -449,7 +522,7 @@ async function extractGraphQLResolvers(
449522 const policies : string [ ] = [ ] ;
450523
451524 // Only extract resolvers for GraphQL APIs — use the already-fetched apiJson
452- const properties = apiJson . properties as Record < string , unknown > | undefined ;
525+ const properties = getApiProperties ( apiJson ) ;
453526 const apiType = properties ?. type as string | undefined ;
454527 if ( apiType ?. toLowerCase ( ) !== 'graphql' ) {
455528 return { resolvers, resolverPolicies, policies } ;
@@ -492,3 +565,51 @@ async function extractGraphQLResolvers(
492565
493566 return { resolvers, resolverPolicies, policies } ;
494567}
568+
569+ /**
570+ * Extract MCP (Model Context Protocol) server configuration for an API.
571+ * The MCP server is a singleton resource per API exposed at apis/{id}/mcpServers/default.
572+ * Silently skips if the API does not have MCP enabled or the resource does not exist.
573+ */
574+ async function extractApiMcpServer (
575+ client : IApimClient ,
576+ store : IArtifactStore ,
577+ context : ApimServiceContext ,
578+ apiDescriptor : ResourceDescriptor ,
579+ apiJson : Record < string , unknown > ,
580+ outputDir : string
581+ ) : Promise < boolean > {
582+ const apiType = ( getApiProperties ( apiJson ) ?. type as string | undefined ) ?. toLowerCase ( ) ;
583+
584+ // Avoid creating MCP artifacts for non-MCP APIs unless they carry meaningful MCP metadata.
585+ if ( apiType !== 'mcp' && ! hasEmbeddedMcpConfiguration ( apiJson ) ) {
586+ return false ;
587+ }
588+
589+ const mcpDescriptor : ResourceDescriptor = {
590+ type : ResourceType . McpServer ,
591+ nameParts : [ ...apiDescriptor . nameParts ] ,
592+ workspace : apiDescriptor . workspace ,
593+ } ;
594+
595+ if ( hasEmbeddedMcpConfiguration ( apiJson ) ) {
596+ await store . writeResource ( outputDir , mcpDescriptor , buildEmbeddedMcpServerResource ( apiJson ) ) ;
597+ logger . info ( `Extracted ${ buildResourceLabel ( mcpDescriptor ) } from API metadata` ) ;
598+ return true ;
599+ }
600+
601+ try {
602+ const mcpJson = await client . getResource ( context , mcpDescriptor ) ;
603+ if ( ! mcpJson || ! hasMeaningfulMcpContent ( mcpJson ) ) {
604+ return false ;
605+ }
606+
607+ await store . writeResource ( outputDir , mcpDescriptor , mcpJson ) ;
608+ logger . info ( `Extracted ${ buildResourceLabel ( mcpDescriptor ) } ` ) ;
609+ return true ;
610+ } catch ( error ) {
611+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
612+ logger . debug ( `No MCP server configuration ${ buildResourceLabel ( mcpDescriptor ) } : ${ errorMessage } ` ) ;
613+ return false ;
614+ }
615+ }
0 commit comments