@frontmcp/skills 1.0.3 → 1.0.4
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/catalog/frontmcp-development/SKILL.md +4 -3
- package/catalog/frontmcp-development/examples/{official-adapters → openapi-adapter}/authenticated-adapter-with-polling.md +2 -2
- package/catalog/frontmcp-development/examples/{official-adapters → openapi-adapter}/basic-openapi-adapter.md +2 -2
- package/catalog/frontmcp-development/examples/openapi-adapter/format-resolution-and-custom-resolvers.md +108 -0
- package/catalog/frontmcp-development/examples/{official-adapters → openapi-adapter}/multi-api-hub-with-inline-spec.md +2 -2
- package/catalog/frontmcp-development/examples/openapi-adapter/ref-security-and-filtering.md +111 -0
- package/catalog/frontmcp-development/references/official-adapters.md +24 -153
- package/catalog/frontmcp-development/references/openapi-adapter.md +431 -0
- package/catalog/skills-manifest.json +39 -9
- package/package.json +1 -1
|
@@ -52,7 +52,8 @@ Entry point for building MCP server components. This skill helps you find the ri
|
|
|
52
52
|
| Write instruction-only AI guidance (no code execution) | `create-skill` | Skill entries with markdown instructions from files, strings, or URLs |
|
|
53
53
|
| Write AI guidance that also orchestrates tools | `create-skill-with-tools` | Skill entries that combine instructions with registered tools |
|
|
54
54
|
| Look up any decorator signature or option | `decorators-guide` | Complete reference for @Tool, @Resource, @Prompt, @Agent, @App, @FrontMcp, and more |
|
|
55
|
-
|
|
|
55
|
+
| Overview of all official adapters | `official-adapters` | Router to all adapter types; adapter vs plugin comparison |
|
|
56
|
+
| Integrate an external API via OpenAPI spec | `openapi-adapter` | OpenapiAdapter with auth, polling, filtering, transforms, format resolution, $ref security |
|
|
56
57
|
| Use official plugins (caching, remember, feature flags) | `official-plugins` | Built-in plugins for caching, session memory, approval, and feature flags (dashboard is beta) |
|
|
57
58
|
| Connect to an external data source via a custom adapter | `create-adapter` | Create custom adapters for external data sources |
|
|
58
59
|
| Configure LLM settings for an agent component | `create-agent-llm-config` | Configure LLM settings for agent components |
|
|
@@ -70,7 +71,7 @@ Entry point for building MCP server components. This skill helps you find the ri
|
|
|
70
71
|
6. **`create-agent`** — Build autonomous AI loops (advanced)
|
|
71
72
|
7. **`create-job`** / **`create-workflow`** — Background processing (advanced)
|
|
72
73
|
8. **`create-skill`** / **`create-skill-with-tools`** — Author your own skills (meta)
|
|
73
|
-
9. **`official-adapters`** — Integrate external APIs via OpenAPI specs
|
|
74
|
+
9. **`official-adapters`** / **`openapi-adapter`** — Integrate external APIs via OpenAPI specs
|
|
74
75
|
10. **`official-plugins`** — Add caching, session memory, feature flags, and more
|
|
75
76
|
|
|
76
77
|
## Cross-Cutting Patterns
|
|
@@ -123,4 +124,4 @@ Entry point for building MCP server components. This skill helps you find the ri
|
|
|
123
124
|
## Reference
|
|
124
125
|
|
|
125
126
|
- [FrontMCP Overview](https://docs.agentfront.dev/frontmcp/fundamentals/overview)
|
|
126
|
-
- Related skills: `create-tool`, `create-resource`, `create-prompt`, `create-agent`, `create-provider`, `create-job`, `create-workflow`, `create-skill`, `create-skill-with-tools`, `decorators-guide`, `official-adapters`, `official-plugins`
|
|
127
|
+
- Related skills: `create-tool`, `create-resource`, `create-prompt`, `create-agent`, `create-provider`, `create-job`, `create-workflow`, `create-skill`, `create-skill-with-tools`, `decorators-guide`, `official-adapters`, `openapi-adapter`, `official-plugins`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: authenticated-adapter-with-polling
|
|
3
|
-
reference:
|
|
3
|
+
reference: openapi-adapter
|
|
4
4
|
level: intermediate
|
|
5
5
|
description: 'Demonstrates configuring authentication (API key and bearer token) and automatic spec polling for OpenAPI adapters.'
|
|
6
6
|
tags: [development, auth, openapi, security, adapters, authenticated]
|
|
@@ -80,5 +80,5 @@ class MyServer {}
|
|
|
80
80
|
|
|
81
81
|
## Related
|
|
82
82
|
|
|
83
|
-
- See `
|
|
83
|
+
- See `openapi-adapter` for all five auth strategies, format resolution, and $ref security
|
|
84
84
|
- See `decorators-guide` for the full `@App` and `@FrontMcp` field reference
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: basic-openapi-adapter
|
|
3
|
-
reference:
|
|
3
|
+
reference: openapi-adapter
|
|
4
4
|
level: basic
|
|
5
5
|
description: 'Demonstrates converting an OpenAPI specification into MCP tools automatically using `OpenapiAdapter` with minimal configuration.'
|
|
6
6
|
tags: [development, openapi, adapters, adapter]
|
|
@@ -51,4 +51,4 @@ class MyServer {}
|
|
|
51
51
|
|
|
52
52
|
## Related
|
|
53
53
|
|
|
54
|
-
- See `
|
|
54
|
+
- See `openapi-adapter` for authentication, filtering, transforms, and security options
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: format-resolution-and-custom-resolvers
|
|
3
|
+
reference: openapi-adapter
|
|
4
|
+
level: intermediate
|
|
5
|
+
description: 'Demonstrates using built-in and custom format resolvers to enrich tool input schemas with concrete constraints from OpenAPI format values.'
|
|
6
|
+
tags: [development, openapi, adapters, format, schema, validation]
|
|
7
|
+
features:
|
|
8
|
+
- 'Enabling built-in format resolvers with `resolveFormats: true` for uuid, date-time, email, int32, etc.'
|
|
9
|
+
- 'Adding custom format resolvers for domain-specific formats (phone, currency)'
|
|
10
|
+
- 'Merging custom resolvers with built-ins where custom takes precedence'
|
|
11
|
+
- 'Using custom-only resolvers without built-ins for full control'
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Format Resolution and Custom Resolvers
|
|
15
|
+
|
|
16
|
+
Demonstrates using built-in and custom format resolvers to enrich tool input schemas with concrete constraints from OpenAPI format values.
|
|
17
|
+
|
|
18
|
+
## Code
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
// src/server.ts
|
|
22
|
+
import { FrontMcp, App } from '@frontmcp/sdk';
|
|
23
|
+
import { OpenapiAdapter } from '@frontmcp/adapters';
|
|
24
|
+
|
|
25
|
+
@App({
|
|
26
|
+
name: 'format-demo',
|
|
27
|
+
adapters: [
|
|
28
|
+
// Built-in format resolvers only
|
|
29
|
+
// uuid fields get regex patterns, date-time gets ISO 8601 format,
|
|
30
|
+
// int32 gets min/max constraints, email gets validation pattern
|
|
31
|
+
OpenapiAdapter.init({
|
|
32
|
+
name: 'users-api',
|
|
33
|
+
url: 'https://api.example.com/openapi.json',
|
|
34
|
+
baseUrl: 'https://api.example.com',
|
|
35
|
+
generateOptions: {
|
|
36
|
+
resolveFormats: true,
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
|
|
40
|
+
// Built-in + custom format resolvers
|
|
41
|
+
// Custom resolvers are merged with built-ins; custom takes precedence
|
|
42
|
+
OpenapiAdapter.init({
|
|
43
|
+
name: 'payments-api',
|
|
44
|
+
url: 'https://payments.example.com/openapi.json',
|
|
45
|
+
baseUrl: 'https://payments.example.com',
|
|
46
|
+
generateOptions: {
|
|
47
|
+
resolveFormats: true,
|
|
48
|
+
formatResolvers: {
|
|
49
|
+
// Domain-specific formats
|
|
50
|
+
phone: (schema) => ({
|
|
51
|
+
...schema,
|
|
52
|
+
pattern: '^\\+[1-9]\\d{1,14}$',
|
|
53
|
+
description: 'E.164 international phone number (e.g., +14155552671)',
|
|
54
|
+
}),
|
|
55
|
+
currency: (schema) => ({
|
|
56
|
+
...schema,
|
|
57
|
+
pattern: '^[A-Z]{3}$',
|
|
58
|
+
description: 'ISO 4217 currency code (e.g., USD, EUR, GBP)',
|
|
59
|
+
}),
|
|
60
|
+
'country-code': (schema) => ({
|
|
61
|
+
...schema,
|
|
62
|
+
pattern: '^[A-Z]{2}$',
|
|
63
|
+
description: 'ISO 3166-1 alpha-2 country code (e.g., US, GB, DE)',
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
}),
|
|
68
|
+
|
|
69
|
+
// Custom resolvers only (no built-ins)
|
|
70
|
+
// Only the formats you explicitly define are resolved
|
|
71
|
+
OpenapiAdapter.init({
|
|
72
|
+
name: 'legacy-api',
|
|
73
|
+
url: 'https://legacy.example.com/openapi.json',
|
|
74
|
+
baseUrl: 'https://legacy.example.com',
|
|
75
|
+
generateOptions: {
|
|
76
|
+
// Without resolveFormats: true, only custom resolvers apply
|
|
77
|
+
formatResolvers: {
|
|
78
|
+
'social-security': (schema) => ({
|
|
79
|
+
...schema,
|
|
80
|
+
pattern: '^\\d{3}-\\d{2}-\\d{4}$',
|
|
81
|
+
description: 'US Social Security Number (XXX-XX-XXXX)',
|
|
82
|
+
}),
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
],
|
|
87
|
+
})
|
|
88
|
+
class FormatDemoApp {}
|
|
89
|
+
|
|
90
|
+
@FrontMcp({
|
|
91
|
+
info: { name: 'format-demo-server', version: '1.0.0' },
|
|
92
|
+
apps: [FormatDemoApp],
|
|
93
|
+
http: { port: 3000 },
|
|
94
|
+
})
|
|
95
|
+
class MyServer {}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## What This Demonstrates
|
|
99
|
+
|
|
100
|
+
- Enabling built-in format resolvers with `resolveFormats: true` for uuid, date-time, email, int32, etc.
|
|
101
|
+
- Adding custom format resolvers for domain-specific formats (phone, currency)
|
|
102
|
+
- Merging custom resolvers with built-ins where custom takes precedence
|
|
103
|
+
- Using custom-only resolvers without built-ins for full control
|
|
104
|
+
|
|
105
|
+
## Related
|
|
106
|
+
|
|
107
|
+
- See `openapi-adapter` for all generate options, authentication, and $ref security
|
|
108
|
+
- See `create-tool` for building tools with manual schema definitions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: multi-api-hub-with-inline-spec
|
|
3
|
-
reference:
|
|
3
|
+
reference: openapi-adapter
|
|
4
4
|
level: advanced
|
|
5
5
|
description: 'Demonstrates registering multiple OpenAPI adapters from different APIs in a single app, including one with an inline spec definition instead of a remote URL.'
|
|
6
6
|
tags: [development, openapi, remote, adapters, multi, api]
|
|
@@ -126,5 +126,5 @@ class MyServer {}
|
|
|
126
126
|
|
|
127
127
|
## Related
|
|
128
128
|
|
|
129
|
-
- See `
|
|
129
|
+
- See `openapi-adapter` for spec polling, `securityResolver`, and filtering operations
|
|
130
130
|
- See `decorators-guide` for the `@Adapter` decorator and how adapters fit in the hierarchy
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ref-security-and-filtering
|
|
3
|
+
reference: openapi-adapter
|
|
4
|
+
level: intermediate
|
|
5
|
+
description: 'Demonstrates configuring $ref resolution security to prevent SSRF attacks and filtering which API operations become MCP tools.'
|
|
6
|
+
tags: [development, openapi, adapters, security, ssrf, filtering]
|
|
7
|
+
features:
|
|
8
|
+
- 'Configuring `refResolution` to restrict which hosts and protocols are allowed for external `$ref` pointers'
|
|
9
|
+
- 'Using `allowedHosts` to restrict $refs to trusted schema servers'
|
|
10
|
+
- 'Using `allowedProtocols` to enable or disable file://, http://, https://, and other protocols'
|
|
11
|
+
- 'Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`'
|
|
12
|
+
- 'Combining security hardening with operation filtering for a production-ready setup'
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# $ref Security and Operation Filtering
|
|
16
|
+
|
|
17
|
+
Demonstrates configuring $ref resolution security to prevent SSRF attacks and filtering which API operations become MCP tools.
|
|
18
|
+
|
|
19
|
+
## Code
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// src/server.ts
|
|
23
|
+
import { FrontMcp, App } from '@frontmcp/sdk';
|
|
24
|
+
import { OpenapiAdapter } from '@frontmcp/adapters';
|
|
25
|
+
|
|
26
|
+
@App({
|
|
27
|
+
name: 'secure-app',
|
|
28
|
+
adapters: [
|
|
29
|
+
// Production-hardened: restrict $refs to trusted hosts + filter operations
|
|
30
|
+
OpenapiAdapter.init({
|
|
31
|
+
name: 'partner-api',
|
|
32
|
+
url: 'https://api.partner.com/openapi.json',
|
|
33
|
+
baseUrl: 'https://api.partner.com',
|
|
34
|
+
loadOptions: {
|
|
35
|
+
refResolution: {
|
|
36
|
+
// Only allow $refs pointing to the partner's own schema server
|
|
37
|
+
allowedHosts: ['schemas.partner.com', 'api.partner.com'],
|
|
38
|
+
// Block additional hosts known to be problematic
|
|
39
|
+
blockedHosts: ['internal.partner.com'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
generateOptions: {
|
|
43
|
+
// Only expose read operations to MCP clients
|
|
44
|
+
filterFn: (op) => op.method === 'get',
|
|
45
|
+
// Skip deprecated endpoints
|
|
46
|
+
includeDeprecated: false,
|
|
47
|
+
},
|
|
48
|
+
staticAuth: {
|
|
49
|
+
apiKey: process.env.PARTNER_API_KEY!,
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
// Internal API: allow internal IPs but lock down to specific operations
|
|
54
|
+
OpenapiAdapter.init({
|
|
55
|
+
name: 'internal-api',
|
|
56
|
+
url: 'http://10.0.0.5:8080/openapi.json',
|
|
57
|
+
baseUrl: 'http://10.0.0.5:8080',
|
|
58
|
+
loadOptions: {
|
|
59
|
+
refResolution: {
|
|
60
|
+
// Allow internal IPs since this is a trusted internal service
|
|
61
|
+
allowInternalIPs: true,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
generateOptions: {
|
|
65
|
+
// Only include specific safe operations
|
|
66
|
+
includeOperations: ['getStatus', 'listMetrics', 'getConfig'],
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
|
|
70
|
+
// Maximum lockdown: no external $refs at all
|
|
71
|
+
OpenapiAdapter.init({
|
|
72
|
+
name: 'sandbox-api',
|
|
73
|
+
url: 'https://sandbox.example.com/openapi.json',
|
|
74
|
+
baseUrl: 'https://sandbox.example.com',
|
|
75
|
+
loadOptions: {
|
|
76
|
+
refResolution: {
|
|
77
|
+
// Block ALL external $ref resolution — only local #/ pointers work
|
|
78
|
+
allowedProtocols: [],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
generateOptions: {
|
|
82
|
+
// Exclude admin and dangerous operations
|
|
83
|
+
excludeOperations: ['deleteAll', 'resetDatabase', 'adminPanel'],
|
|
84
|
+
// Only include billing-related paths
|
|
85
|
+
filterFn: (op) => op.path.startsWith('/billing') || op.path.startsWith('/invoices'),
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
})
|
|
90
|
+
class SecureApp {}
|
|
91
|
+
|
|
92
|
+
@FrontMcp({
|
|
93
|
+
info: { name: 'secure-server', version: '1.0.0' },
|
|
94
|
+
apps: [SecureApp],
|
|
95
|
+
http: { port: 3000 },
|
|
96
|
+
})
|
|
97
|
+
class MyServer {}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## What This Demonstrates
|
|
101
|
+
|
|
102
|
+
- Configuring `refResolution` to restrict which hosts and protocols are allowed for external `$ref` pointers
|
|
103
|
+
- Using `allowedHosts` to restrict $refs to trusted schema servers
|
|
104
|
+
- Using `allowedProtocols` to enable or disable file://, http://, https://, and other protocols
|
|
105
|
+
- Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`
|
|
106
|
+
- Combining security hardening with operation filtering for a production-ready setup
|
|
107
|
+
|
|
108
|
+
## Related
|
|
109
|
+
|
|
110
|
+
- See `openapi-adapter` for all `refResolution` options and the full blocked IP list
|
|
111
|
+
- See `official-adapters` for the adapter vs plugin comparison
|
|
@@ -1,39 +1,41 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: official-adapters
|
|
3
|
-
description:
|
|
3
|
+
description: Overview of all official FrontMCP adapters that convert external definitions into MCP primitives
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Official Adapters
|
|
7
7
|
|
|
8
|
-
Adapters convert external definitions (OpenAPI specs, Lambda functions, etc.) into MCP tools, resources, and prompts automatically.
|
|
8
|
+
Adapters convert external definitions (OpenAPI specs, Lambda functions, etc.) into MCP tools, resources, and prompts automatically. They are registered in the `adapters` array of `@App`.
|
|
9
9
|
|
|
10
10
|
## When to Use This Skill
|
|
11
11
|
|
|
12
12
|
### Must Use
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
- Setting up authentication (API key, bearer token, OAuth) for an adapter-generated API integration
|
|
14
|
+
- Integrating an external API or service that has a machine-readable specification
|
|
15
|
+
- Auto-generating MCP tools from an API definition instead of writing them manually
|
|
17
16
|
|
|
18
17
|
### Recommended
|
|
19
18
|
|
|
20
19
|
- Registering multiple external APIs as namespaced tool sets in a single server
|
|
21
|
-
-
|
|
22
|
-
- Providing an inline OpenAPI spec for APIs without a hosted spec URL
|
|
20
|
+
- Setting up authentication, polling, or transforms for adapter-generated tools
|
|
23
21
|
|
|
24
22
|
### Skip When
|
|
25
23
|
|
|
26
|
-
- The external API has no
|
|
24
|
+
- The external API has no machine-readable spec and uses a custom protocol (see `create-adapter`)
|
|
27
25
|
- You need cross-cutting behavior like caching or logging (see `create-plugin` or `official-plugins`)
|
|
28
26
|
- You are building tools manually without an external spec (see `create-tool`)
|
|
29
27
|
|
|
30
|
-
> **Decision:** Use this skill when you have an
|
|
28
|
+
> **Decision:** Use this skill when you have an external API definition and want to automatically generate MCP primitives from it.
|
|
31
29
|
|
|
32
|
-
##
|
|
30
|
+
## Available Adapters
|
|
33
31
|
|
|
34
|
-
|
|
32
|
+
| Adapter | Package | Description | Dedicated Skill |
|
|
33
|
+
| ------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
34
|
+
| **OpenAPI Adapter** | `@frontmcp/adapters` | Converts OpenAPI 3.x specifications into MCP tools — one tool per operation. Supports authentication, spec polling, filtering, transforms, format resolution, and $ref security. | [`openapi-adapter`](openapi-adapter.md) |
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
> More adapters will be added in future releases. To build a custom adapter for a non-OpenAPI source, see `create-adapter`.
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
37
39
|
|
|
38
40
|
```typescript
|
|
39
41
|
import { OpenapiAdapter } from '@frontmcp/adapters';
|
|
@@ -48,99 +50,10 @@ import { OpenapiAdapter } from '@frontmcp/adapters';
|
|
|
48
50
|
],
|
|
49
51
|
})
|
|
50
52
|
class MyApp {}
|
|
53
|
+
// Generated tools: petstore:addPet, petstore:getPetById, petstore:deletePet, etc.
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
### With Authentication
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
// API Key via static auth
|
|
59
|
-
OpenapiAdapter.init({
|
|
60
|
-
name: 'my-api',
|
|
61
|
-
url: 'https://api.example.com/openapi.json',
|
|
62
|
-
baseUrl: 'https://api.example.com',
|
|
63
|
-
staticAuth: {
|
|
64
|
-
apiKey: process.env.API_KEY!,
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// API Key via additional headers
|
|
69
|
-
OpenapiAdapter.init({
|
|
70
|
-
name: 'my-api',
|
|
71
|
-
url: 'https://api.example.com/openapi.json',
|
|
72
|
-
baseUrl: 'https://api.example.com',
|
|
73
|
-
additionalHeaders: {
|
|
74
|
-
'X-API-Key': process.env.API_KEY!,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Bearer token via static auth
|
|
79
|
-
OpenapiAdapter.init({
|
|
80
|
-
name: 'my-api',
|
|
81
|
-
url: 'https://api.example.com/openapi.json',
|
|
82
|
-
baseUrl: 'https://api.example.com',
|
|
83
|
-
staticAuth: {
|
|
84
|
-
jwt: process.env.API_TOKEN!,
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Dynamic auth per tool using securityResolver
|
|
89
|
-
OpenapiAdapter.init({
|
|
90
|
-
name: 'my-api',
|
|
91
|
-
url: 'https://api.example.com/openapi.json',
|
|
92
|
-
baseUrl: 'https://api.example.com',
|
|
93
|
-
securityResolver: (tool, ctx) => {
|
|
94
|
-
return { jwt: ctx.authInfo?.token };
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Spec Polling
|
|
100
|
-
|
|
101
|
-
Automatically refresh the OpenAPI spec at intervals:
|
|
102
|
-
|
|
103
|
-
```typescript
|
|
104
|
-
OpenapiAdapter.init({
|
|
105
|
-
name: 'evolving-api',
|
|
106
|
-
url: 'https://api.example.com/openapi.json',
|
|
107
|
-
polling: {
|
|
108
|
-
intervalMs: 300000, // Re-fetch every 5 minutes
|
|
109
|
-
},
|
|
110
|
-
});
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
### Inline Spec
|
|
114
|
-
|
|
115
|
-
Provide the OpenAPI spec directly instead of fetching from URL:
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
OpenapiAdapter.init({
|
|
119
|
-
name: 'my-api',
|
|
120
|
-
spec: {
|
|
121
|
-
openapi: '3.0.0',
|
|
122
|
-
info: { title: 'My API', version: '1.0.0' },
|
|
123
|
-
paths: { ... },
|
|
124
|
-
},
|
|
125
|
-
})
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### Multiple Adapters
|
|
129
|
-
|
|
130
|
-
Register adapters from different APIs in the same app:
|
|
131
|
-
|
|
132
|
-
```typescript
|
|
133
|
-
@App({
|
|
134
|
-
name: 'IntegrationHub',
|
|
135
|
-
adapters: [
|
|
136
|
-
OpenapiAdapter.init({ name: 'github', url: 'https://api.github.com/openapi.json' }),
|
|
137
|
-
OpenapiAdapter.init({ name: 'jira', url: 'https://jira.example.com/openapi.json' }),
|
|
138
|
-
OpenapiAdapter.init({ name: 'slack', url: 'https://slack.com/openapi.json' }),
|
|
139
|
-
],
|
|
140
|
-
})
|
|
141
|
-
class IntegrationHub {}
|
|
142
|
-
// Tools: github:createIssue, jira:createTicket, slack:postMessage, etc.
|
|
143
|
-
```
|
|
56
|
+
For full configuration, authentication, security, and advanced features, see the [`openapi-adapter`](openapi-adapter.md) reference.
|
|
144
57
|
|
|
145
58
|
## Adapter vs Plugin
|
|
146
59
|
|
|
@@ -153,57 +66,15 @@ class IntegrationHub {}
|
|
|
153
66
|
|
|
154
67
|
## Common Patterns
|
|
155
68
|
|
|
156
|
-
| Pattern | Correct | Incorrect
|
|
157
|
-
| -------------------- | ---------------------------------------------------------------------------- |
|
|
158
|
-
| Adapter registration | `OpenapiAdapter.init({
|
|
159
|
-
| Tool naming | Tools auto-named as
|
|
160
|
-
|
|
|
161
|
-
| Spec source | Use `url` for hosted specs or `spec` for inline definitions | Using both `url` and `spec` simultaneously | Only one source should be provided; `spec` takes precedence and `url` is ignored |
|
|
162
|
-
| Multiple APIs | Register separate `OpenapiAdapter.init()` calls with unique `name` values | Using the same `name` for different adapters | Duplicate names cause tool naming collisions |
|
|
163
|
-
|
|
164
|
-
## Verification Checklist
|
|
165
|
-
|
|
166
|
-
### Configuration
|
|
167
|
-
|
|
168
|
-
- [ ] `@frontmcp/adapters` package is installed
|
|
169
|
-
- [ ] `OpenapiAdapter.init()` is in the `adapters` array of `@App`
|
|
170
|
-
- [ ] Adapter has a unique `name` for tool namespacing
|
|
171
|
-
- [ ] `url` points to a valid, reachable OpenAPI JSON/YAML endpoint (or `spec` is inline)
|
|
172
|
-
|
|
173
|
-
### Runtime
|
|
174
|
-
|
|
175
|
-
- [ ] Generated tools appear in `tools/list` with `<name>:<operationId>` naming
|
|
176
|
-
- [ ] Auth headers are sent correctly on API calls
|
|
177
|
-
- [ ] Spec polling refreshes tool definitions at the configured interval
|
|
178
|
-
- [ ] Invalid spec URL produces a clear startup error
|
|
179
|
-
|
|
180
|
-
### Production
|
|
181
|
-
|
|
182
|
-
- [ ] API tokens and secrets are loaded from environment variables
|
|
183
|
-
- [ ] Polling interval is appropriate for the API's update frequency
|
|
184
|
-
- [ ] Multiple adapter registrations use distinct names
|
|
185
|
-
|
|
186
|
-
## Troubleshooting
|
|
187
|
-
|
|
188
|
-
| Problem | Cause | Solution |
|
|
189
|
-
| ---------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
190
|
-
| No tools generated from spec | Spec URL returns non-OpenAPI content or is unreachable | Verify URL returns valid OpenAPI 3.x JSON; check network access |
|
|
191
|
-
| Authentication errors on API calls | Wrong auth config or missing credentials | Configure `staticAuth` for fixed credentials, `securityResolver`/`authProviderMapper` for dynamic auth, or `additionalHeaders` for header-based tokens; verify env vars are set |
|
|
192
|
-
| Duplicate tool name error | Two adapters registered with the same `name` | Give each adapter a unique `name` (e.g., `'github'`, `'jira'`) |
|
|
193
|
-
| Stale tools after API update | Spec polling not configured | Add `polling: { intervalMs: 300000 }` to refresh every 5 minutes |
|
|
194
|
-
| TypeScript error importing adapter | Wrong import path | Import from `@frontmcp/adapters`: `import { OpenapiAdapter } from '@frontmcp/adapters'` |
|
|
195
|
-
|
|
196
|
-
## Examples
|
|
197
|
-
|
|
198
|
-
| Example | Level | Description |
|
|
199
|
-
| ----------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
200
|
-
| [`authenticated-adapter-with-polling`](../examples/official-adapters/authenticated-adapter-with-polling.md) | Intermediate | Demonstrates configuring authentication (API key and bearer token) and automatic spec polling for OpenAPI adapters. |
|
|
201
|
-
| [`basic-openapi-adapter`](../examples/official-adapters/basic-openapi-adapter.md) | Basic | Demonstrates converting an OpenAPI specification into MCP tools automatically using `OpenapiAdapter` with minimal configuration. |
|
|
202
|
-
| [`multi-api-hub-with-inline-spec`](../examples/official-adapters/multi-api-hub-with-inline-spec.md) | Advanced | Demonstrates registering multiple OpenAPI adapters from different APIs in a single app, including one with an inline spec definition instead of a remote URL. |
|
|
203
|
-
|
|
204
|
-
> See all examples in [`examples/official-adapters/`](../examples/official-adapters/)
|
|
69
|
+
| Pattern | Correct | Incorrect | Why |
|
|
70
|
+
| -------------------- | ---------------------------------------------------------------------------- | -------------------------------------------- | ------------------------------------------------------------------------ |
|
|
71
|
+
| Adapter registration | `OpenapiAdapter.init({ ... })` in `adapters` array | Placing adapter in `plugins` array | Adapters go in `adapters`, not `plugins`; they serve different purposes |
|
|
72
|
+
| Tool naming | Tools auto-named as `<name>:<operationId>` using adapter `name` as namespace | Expecting flat names like `listPets` | Adapter name is prepended to prevent collisions across multiple adapters |
|
|
73
|
+
| Multiple APIs | Register separate `OpenapiAdapter.init()` calls with unique `name` values | Using the same `name` for different adapters | Duplicate names cause tool naming collisions |
|
|
205
74
|
|
|
206
75
|
## Reference
|
|
207
76
|
|
|
77
|
+
- [`openapi-adapter`](openapi-adapter.md) — Full OpenAPI adapter reference
|
|
78
|
+
- `create-adapter` — Build a custom adapter for non-OpenAPI sources
|
|
79
|
+
- `create-plugin` — Build plugins for cross-cutting concerns
|
|
208
80
|
- [Adapter Overview Documentation](https://docs.agentfront.dev/frontmcp/adapters/overview)
|
|
209
|
-
- Related skills: `create-adapter`, `create-plugin`, `create-tool`
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: openapi-adapter
|
|
3
|
+
description: Convert OpenAPI 3.x specifications into MCP tools with authentication, polling, transforms, format resolution, and $ref security
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# OpenAPI Adapter
|
|
7
|
+
|
|
8
|
+
The OpenAPI adapter converts OpenAPI 3.x specifications into MCP tools — one tool per operation. It supports authentication, spec polling, operation filtering, input/output/tool transforms, format resolution, and built-in SSRF protection.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
### Must Use
|
|
13
|
+
|
|
14
|
+
- Converting an OpenAPI/Swagger 3.x specification into MCP tools automatically
|
|
15
|
+
- Integrating a REST API that provides a public OpenAPI spec (Petstore, GitHub, Jira, Slack)
|
|
16
|
+
- Setting up authentication (API key, bearer token, OAuth) for adapter-generated tools
|
|
17
|
+
|
|
18
|
+
### Recommended
|
|
19
|
+
|
|
20
|
+
- Enriching tool schemas with format resolution (uuid, date-time, email, etc.)
|
|
21
|
+
- Filtering which API operations become tools
|
|
22
|
+
- Hiding sensitive inputs and injecting server-side values via input transforms
|
|
23
|
+
- Enabling spec polling to auto-refresh tools when the upstream API changes
|
|
24
|
+
|
|
25
|
+
### Skip When
|
|
26
|
+
|
|
27
|
+
- The external API has no OpenAPI spec (see `create-adapter` for custom adapters)
|
|
28
|
+
- You need to build tools manually with custom logic (see `create-tool`)
|
|
29
|
+
- You only need adapters overview and comparison (see `official-adapters`)
|
|
30
|
+
|
|
31
|
+
> **Decision:** Use this skill when you have an OpenAPI 3.x spec and want comprehensive guidance on `OpenapiAdapter` configuration.
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { FrontMcp, App } from '@frontmcp/sdk';
|
|
37
|
+
import { OpenapiAdapter } from '@frontmcp/adapters';
|
|
38
|
+
|
|
39
|
+
@App({
|
|
40
|
+
name: 'MyApp',
|
|
41
|
+
adapters: [
|
|
42
|
+
OpenapiAdapter.init({
|
|
43
|
+
name: 'petstore',
|
|
44
|
+
url: 'https://petstore3.swagger.io/api/v3/openapi.json',
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
})
|
|
48
|
+
class MyApp {}
|
|
49
|
+
|
|
50
|
+
@FrontMcp({
|
|
51
|
+
info: { name: 'my-server', version: '1.0.0' },
|
|
52
|
+
apps: [MyApp],
|
|
53
|
+
http: { port: 3000 },
|
|
54
|
+
})
|
|
55
|
+
class MyServer {}
|
|
56
|
+
// Generated tools: petstore:addPet, petstore:getPetById, petstore:deletePet, etc.
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Each OpenAPI operation becomes a tool named `<adapter-name>:<operationId>`.
|
|
60
|
+
|
|
61
|
+
## Authentication
|
|
62
|
+
|
|
63
|
+
Five strategies with different security risk levels:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// 1. Static Headers (Medium Risk) — server-to-server APIs
|
|
67
|
+
OpenapiAdapter.init({
|
|
68
|
+
name: 'my-api',
|
|
69
|
+
url: 'https://api.example.com/openapi.json',
|
|
70
|
+
baseUrl: 'https://api.example.com',
|
|
71
|
+
additionalHeaders: {
|
|
72
|
+
'x-api-key': process.env.API_KEY!,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// 2. Auth Provider Mapper (Low Risk) ⭐ Recommended — multi-provider
|
|
77
|
+
OpenapiAdapter.init({
|
|
78
|
+
name: 'multi-auth-api',
|
|
79
|
+
url: 'https://api.example.com/openapi.json',
|
|
80
|
+
baseUrl: 'https://api.example.com',
|
|
81
|
+
authProviderMapper: {
|
|
82
|
+
GitHubAuth: (ctx) => ctx.authInfo.user?.githubToken,
|
|
83
|
+
SlackAuth: (ctx) => ctx.authInfo.user?.slackToken,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 3. Custom Security Resolver (Low Risk) — full control
|
|
88
|
+
OpenapiAdapter.init({
|
|
89
|
+
name: 'my-api',
|
|
90
|
+
url: 'https://api.example.com/openapi.json',
|
|
91
|
+
baseUrl: 'https://api.example.com',
|
|
92
|
+
securityResolver: (tool, ctx) => {
|
|
93
|
+
return { jwt: ctx.authInfo?.token };
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 4. Static Auth (Medium Risk) — fixed credentials
|
|
98
|
+
OpenapiAdapter.init({
|
|
99
|
+
name: 'my-api',
|
|
100
|
+
url: 'https://api.example.com/openapi.json',
|
|
101
|
+
baseUrl: 'https://api.example.com',
|
|
102
|
+
staticAuth: {
|
|
103
|
+
jwt: process.env.API_TOKEN!,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// 5. Dynamic Headers & Body Mapping (Low Risk) — context injection
|
|
108
|
+
OpenapiAdapter.init({
|
|
109
|
+
name: 'my-api',
|
|
110
|
+
url: 'https://api.example.com/openapi.json',
|
|
111
|
+
baseUrl: 'https://api.example.com',
|
|
112
|
+
headersMapper: (ctx, headers) => {
|
|
113
|
+
headers.set('Authorization', `Bearer ${ctx.authInfo?.token}`);
|
|
114
|
+
return headers;
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Risk Level | Strategy | Description |
|
|
120
|
+
| ---------- | ------------------------------------------ | ---------------------------------------------------- |
|
|
121
|
+
| LOW | `authProviderMapper` or `securityResolver` | Auth from user context, not exposed to clients |
|
|
122
|
+
| MEDIUM | `staticAuth`, `additionalHeaders` | Static credentials |
|
|
123
|
+
| HIGH | `includeSecurityInInput: true` | Auth fields exposed to MCP clients (not recommended) |
|
|
124
|
+
|
|
125
|
+
## Spec Polling
|
|
126
|
+
|
|
127
|
+
Auto-refresh tool definitions when the upstream API changes:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
OpenapiAdapter.init({
|
|
131
|
+
name: 'evolving-api',
|
|
132
|
+
url: 'https://api.example.com/openapi.json',
|
|
133
|
+
polling: {
|
|
134
|
+
enabled: true,
|
|
135
|
+
intervalMs: 300000, // Re-fetch every 5 minutes
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Inline Spec
|
|
141
|
+
|
|
142
|
+
Provide the OpenAPI spec directly instead of fetching from URL:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
OpenapiAdapter.init({
|
|
146
|
+
name: 'my-api',
|
|
147
|
+
spec: {
|
|
148
|
+
openapi: '3.0.0',
|
|
149
|
+
info: { title: 'My API', version: '1.0.0' },
|
|
150
|
+
paths: {
|
|
151
|
+
'/users': {
|
|
152
|
+
get: {
|
|
153
|
+
operationId: 'listUsers',
|
|
154
|
+
summary: 'List all users',
|
|
155
|
+
responses: { '200': { description: 'OK' } },
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Multiple Adapters
|
|
164
|
+
|
|
165
|
+
Register adapters from different APIs in the same app:
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
@App({
|
|
169
|
+
name: 'IntegrationHub',
|
|
170
|
+
adapters: [
|
|
171
|
+
OpenapiAdapter.init({ name: 'github', url: 'https://api.github.com/openapi.json' }),
|
|
172
|
+
OpenapiAdapter.init({ name: 'jira', url: 'https://jira.example.com/openapi.json' }),
|
|
173
|
+
OpenapiAdapter.init({ name: 'slack', url: 'https://slack.com/openapi.json' }),
|
|
174
|
+
],
|
|
175
|
+
})
|
|
176
|
+
class IntegrationHub {}
|
|
177
|
+
// Tools: github:createIssue, jira:createTicket, slack:postMessage, etc.
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Filtering Operations
|
|
181
|
+
|
|
182
|
+
Control which API operations become MCP tools:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Filter by path prefix
|
|
186
|
+
OpenapiAdapter.init({
|
|
187
|
+
name: 'billing-api',
|
|
188
|
+
url: 'https://api.example.com/openapi.json',
|
|
189
|
+
generateOptions: {
|
|
190
|
+
filterFn: (op) => op.path.startsWith('/invoices') || op.path.startsWith('/customers'),
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Include only specific operations
|
|
195
|
+
OpenapiAdapter.init({
|
|
196
|
+
name: 'my-api',
|
|
197
|
+
url: 'https://api.example.com/openapi.json',
|
|
198
|
+
generateOptions: {
|
|
199
|
+
includeOperations: ['getUser', 'createUser', 'updateUser'],
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Exclude specific operations
|
|
204
|
+
OpenapiAdapter.init({
|
|
205
|
+
name: 'my-api',
|
|
206
|
+
url: 'https://api.example.com/openapi.json',
|
|
207
|
+
generateOptions: {
|
|
208
|
+
excludeOperations: ['deprecatedEndpoint', 'internalOnly'],
|
|
209
|
+
},
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Input Transforms
|
|
214
|
+
|
|
215
|
+
Hide inputs from AI/users and inject values server-side:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
OpenapiAdapter.init({
|
|
219
|
+
name: 'tenant-api',
|
|
220
|
+
url: 'https://api.example.com/openapi.json',
|
|
221
|
+
baseUrl: 'https://api.example.com',
|
|
222
|
+
inputTransforms: {
|
|
223
|
+
global: [
|
|
224
|
+
// Hide tenant header from AI, inject from user context
|
|
225
|
+
{ inputKey: 'X-Tenant-Id', inject: (ctx) => ctx.authInfo.user?.tenantId },
|
|
226
|
+
// Add correlation ID to all requests
|
|
227
|
+
{ inputKey: 'X-Correlation-Id', inject: () => crypto.randomUUID() },
|
|
228
|
+
],
|
|
229
|
+
perTool: {
|
|
230
|
+
createAuditLog: [{ inputKey: 'userId', inject: (ctx) => ctx.authInfo.user?.id }],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Format Resolution
|
|
237
|
+
|
|
238
|
+
Enrich generated tool schemas with concrete constraints from OpenAPI `format` values (uuid, date-time, email, int32, etc.):
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
// Enable built-in format resolvers
|
|
242
|
+
OpenapiAdapter.init({
|
|
243
|
+
name: 'my-api',
|
|
244
|
+
url: 'https://api.example.com/openapi.json',
|
|
245
|
+
generateOptions: {
|
|
246
|
+
resolveFormats: true,
|
|
247
|
+
},
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// Add custom format resolvers (merged with built-ins when resolveFormats: true)
|
|
251
|
+
OpenapiAdapter.init({
|
|
252
|
+
name: 'my-api',
|
|
253
|
+
url: 'https://api.example.com/openapi.json',
|
|
254
|
+
generateOptions: {
|
|
255
|
+
resolveFormats: true,
|
|
256
|
+
formatResolvers: {
|
|
257
|
+
phone: (schema) => ({
|
|
258
|
+
...schema,
|
|
259
|
+
pattern: '^\\+[1-9]\\d{1,14}$',
|
|
260
|
+
description: 'E.164 phone number',
|
|
261
|
+
}),
|
|
262
|
+
currency: (schema) => ({
|
|
263
|
+
...schema,
|
|
264
|
+
pattern: '^[A-Z]{3}$',
|
|
265
|
+
description: 'ISO 4217 currency code',
|
|
266
|
+
}),
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Custom resolvers only (no built-ins)
|
|
272
|
+
OpenapiAdapter.init({
|
|
273
|
+
name: 'my-api',
|
|
274
|
+
url: 'https://api.example.com/openapi.json',
|
|
275
|
+
generateOptions: {
|
|
276
|
+
formatResolvers: {
|
|
277
|
+
phone: (schema) => ({ ...schema, pattern: '^\\+[1-9]\\d{1,14}$' }),
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
| Option | Type | Default | Description |
|
|
284
|
+
| ----------------- | -------------------------------- | ----------- | -------------------------------------------------------------------------------------------- |
|
|
285
|
+
| `resolveFormats` | `boolean` | `false` | Enable built-in format resolvers (uuid, date-time, email, int32, etc.) |
|
|
286
|
+
| `formatResolvers` | `Record<string, FormatResolver>` | `undefined` | Custom resolvers; merged with built-ins when `resolveFormats: true`, custom takes precedence |
|
|
287
|
+
|
|
288
|
+
## $ref Resolution Security
|
|
289
|
+
|
|
290
|
+
By default, the adapter blocks dangerous `$ref` resolution patterns to prevent SSRF attacks:
|
|
291
|
+
|
|
292
|
+
- `file://` protocol is blocked (prevents local file reads)
|
|
293
|
+
- Internal/private IPs are blocked (prevents cloud metadata theft, internal network probing)
|
|
294
|
+
- `http://` and `https://` to public hosts are allowed
|
|
295
|
+
|
|
296
|
+
Configure via `loadOptions.refResolution`:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
// Restrict $refs to specific hosts only
|
|
300
|
+
OpenapiAdapter.init({
|
|
301
|
+
name: 'my-api',
|
|
302
|
+
url: 'https://api.example.com/openapi.json',
|
|
303
|
+
loadOptions: {
|
|
304
|
+
refResolution: {
|
|
305
|
+
allowedHosts: ['schemas.example.com'],
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Allow file:// protocol (for specs referencing local schema files)
|
|
311
|
+
OpenapiAdapter.init({
|
|
312
|
+
name: 'my-api',
|
|
313
|
+
url: 'https://api.example.com/openapi.json',
|
|
314
|
+
loadOptions: {
|
|
315
|
+
refResolution: {
|
|
316
|
+
allowedProtocols: ['http', 'https', 'file'],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Allow internal IPs (only in trusted environments)
|
|
322
|
+
OpenapiAdapter.init({
|
|
323
|
+
name: 'internal-api',
|
|
324
|
+
url: 'http://10.0.0.5:8080/openapi.json',
|
|
325
|
+
loadOptions: {
|
|
326
|
+
refResolution: {
|
|
327
|
+
allowInternalIPs: true,
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
// Block ALL external refs (only resolve local #/ pointers)
|
|
333
|
+
OpenapiAdapter.init({
|
|
334
|
+
name: 'my-api',
|
|
335
|
+
url: 'https://api.example.com/openapi.json',
|
|
336
|
+
loadOptions: {
|
|
337
|
+
refResolution: {
|
|
338
|
+
allowedProtocols: [],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
| Option | Type | Default | Description |
|
|
345
|
+
| ------------------ | ---------- | ------------------- | ----------------------------------------------------------------------------------------- |
|
|
346
|
+
| `allowedProtocols` | `string[]` | `['http', 'https']` | Protocols allowed for external `$ref` resolution (http, https, ftp, ws, etc.) |
|
|
347
|
+
| `allowedHosts` | `string[]` | `undefined` | When set, only refs to these hostnames are resolved |
|
|
348
|
+
| `blockedHosts` | `string[]` | `undefined` | Additional hostnames/IPs to block beyond the built-in list |
|
|
349
|
+
| `allowInternalIPs` | `boolean` | `false` | Disable the built-in internal IP block list (127.x, 10.x, 172.16.x, 169.254.x, localhost) |
|
|
350
|
+
|
|
351
|
+
## Load Options
|
|
352
|
+
|
|
353
|
+
Configure how the OpenAPI spec is loaded:
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
OpenapiAdapter.init({
|
|
357
|
+
name: 'my-api',
|
|
358
|
+
url: 'https://api.example.com/openapi.json',
|
|
359
|
+
baseUrl: 'https://api.example.com',
|
|
360
|
+
loadOptions: {
|
|
361
|
+
headers: { authorization: `Bearer ${process.env.SPEC_ACCESS_TOKEN}` },
|
|
362
|
+
timeout: 10000,
|
|
363
|
+
validate: true,
|
|
364
|
+
dereference: true,
|
|
365
|
+
},
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Common Patterns
|
|
370
|
+
|
|
371
|
+
| Pattern | Correct | Incorrect | Why |
|
|
372
|
+
| -------------------- | ---------------------------------------------------------- | --------------------------------------------------- | ----------------------------------------------- |
|
|
373
|
+
| Adapter registration | `OpenapiAdapter.init({ ... })` in `adapters` array | Placing adapter in `plugins` array | Adapters go in `adapters`, not `plugins` |
|
|
374
|
+
| Tool naming | Tools auto-named as `<name>:<operationId>` | Expecting flat names like `listPets` | Adapter name prevents collisions |
|
|
375
|
+
| Auth configuration | `staticAuth: { jwt: process.env.API_TOKEN! }` | Hardcoding secrets: `staticAuth: { jwt: 'sk-xxx' }` | Always use environment variables |
|
|
376
|
+
| Spec source | Use `url` for hosted specs or `spec` for inline | Using both `url` and `spec` simultaneously | Only one source; `spec` takes precedence |
|
|
377
|
+
| Multiple APIs | Separate `OpenapiAdapter.init()` with unique `name` values | Same `name` for different adapters | Duplicate names cause tool collisions |
|
|
378
|
+
| $ref security | Use default `refResolution` (blocks file://, internal IPs) | Setting `allowInternalIPs: true` in production | Default protects against SSRF |
|
|
379
|
+
| Format resolution | `generateOptions: { resolveFormats: true }` | Writing manual patterns for standard formats | Built-in resolvers handle uuid, date-time, etc. |
|
|
380
|
+
|
|
381
|
+
## Verification Checklist
|
|
382
|
+
|
|
383
|
+
### Configuration
|
|
384
|
+
|
|
385
|
+
- [ ] `@frontmcp/adapters` package is installed
|
|
386
|
+
- [ ] `OpenapiAdapter.init()` is in the `adapters` array of `@App`
|
|
387
|
+
- [ ] Adapter has a unique `name` for tool namespacing
|
|
388
|
+
- [ ] `url` points to a valid, reachable OpenAPI JSON/YAML endpoint (or `spec` is inline)
|
|
389
|
+
|
|
390
|
+
### Runtime
|
|
391
|
+
|
|
392
|
+
- [ ] Generated tools appear in `tools/list` with `<name>:<operationId>` naming
|
|
393
|
+
- [ ] Auth headers are sent correctly on API calls
|
|
394
|
+
- [ ] Spec polling refreshes tool definitions at the configured interval
|
|
395
|
+
- [ ] Invalid spec URL produces a clear startup error
|
|
396
|
+
|
|
397
|
+
### Production
|
|
398
|
+
|
|
399
|
+
- [ ] API tokens and secrets are loaded from environment variables
|
|
400
|
+
- [ ] Polling interval is appropriate for the API's update frequency
|
|
401
|
+
- [ ] Multiple adapter registrations use distinct names
|
|
402
|
+
- [ ] $ref resolution defaults are appropriate (or explicitly configured)
|
|
403
|
+
|
|
404
|
+
## Troubleshooting
|
|
405
|
+
|
|
406
|
+
| Problem | Cause | Solution |
|
|
407
|
+
| ---------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
|
|
408
|
+
| No tools generated from spec | Spec URL returns non-OpenAPI content or is unreachable | Verify URL returns valid OpenAPI 3.x JSON; check network access |
|
|
409
|
+
| Authentication errors on API calls | Wrong auth config or missing credentials | Configure `staticAuth`, `securityResolver`, `authProviderMapper`, or `additionalHeaders`; verify env vars |
|
|
410
|
+
| Duplicate tool name error | Two adapters with the same `name` | Give each adapter a unique `name` |
|
|
411
|
+
| Stale tools after API update | Spec polling not configured | Add `polling: { intervalMs: 300000 }` |
|
|
412
|
+
| External $refs not resolving | Default SSRF protection blocks external refs | Add `loadOptions.refResolution.allowedHosts` or `allowedProtocols` |
|
|
413
|
+
| SSRF warning / $ref to internal IP | Spec contains $refs to internal services | Blocked by default; use `refResolution.allowInternalIPs: true` only in trusted environments |
|
|
414
|
+
| TypeScript error importing adapter | Wrong import path | Import from `@frontmcp/adapters` |
|
|
415
|
+
|
|
416
|
+
## Examples
|
|
417
|
+
|
|
418
|
+
| Example | Level | Description |
|
|
419
|
+
| ----------------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
420
|
+
| [`basic-openapi-adapter`](../examples/openapi-adapter/basic-openapi-adapter.md) | Basic | Demonstrates converting an OpenAPI specification into MCP tools automatically using `OpenapiAdapter` with minimal configuration. |
|
|
421
|
+
| [`authenticated-adapter-with-polling`](../examples/openapi-adapter/authenticated-adapter-with-polling.md) | Intermediate | Demonstrates configuring authentication (API key and bearer token) and automatic spec polling for OpenAPI adapters. |
|
|
422
|
+
| [`format-resolution-and-custom-resolvers`](../examples/openapi-adapter/format-resolution-and-custom-resolvers.md) | Intermediate | Demonstrates using built-in and custom format resolvers to enrich tool input schemas with concrete constraints from OpenAPI format values. |
|
|
423
|
+
| [`ref-security-and-filtering`](../examples/openapi-adapter/ref-security-and-filtering.md) | Intermediate | Demonstrates configuring $ref resolution security to prevent SSRF attacks and filtering which API operations become MCP tools. |
|
|
424
|
+
| [`multi-api-hub-with-inline-spec`](../examples/openapi-adapter/multi-api-hub-with-inline-spec.md) | Advanced | Demonstrates registering multiple OpenAPI adapters from different APIs in a single app, including one with an inline spec definition instead of a remote URL. |
|
|
425
|
+
|
|
426
|
+
> See all examples in [`examples/openapi-adapter/`](../examples/openapi-adapter/)
|
|
427
|
+
|
|
428
|
+
## Reference
|
|
429
|
+
|
|
430
|
+
- [OpenAPI Adapter Documentation](https://docs.agentfront.dev/frontmcp/adapters/openapi-adapter)
|
|
431
|
+
- Related skills: `official-adapters`, `create-adapter`, `create-tool`
|
|
@@ -1348,8 +1348,25 @@
|
|
|
1348
1348
|
},
|
|
1349
1349
|
{
|
|
1350
1350
|
"name": "official-adapters",
|
|
1351
|
-
"description": "
|
|
1351
|
+
"description": "Overview of all official FrontMCP adapters that convert external definitions into MCP primitives",
|
|
1352
|
+
"examples": []
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
"name": "openapi-adapter",
|
|
1356
|
+
"description": "Convert OpenAPI 3.x specifications into MCP tools with authentication, polling, transforms, format resolution, and $ref security",
|
|
1352
1357
|
"examples": [
|
|
1358
|
+
{
|
|
1359
|
+
"name": "basic-openapi-adapter",
|
|
1360
|
+
"description": "Demonstrates converting an OpenAPI specification into MCP tools automatically using `OpenapiAdapter` with minimal configuration.",
|
|
1361
|
+
"level": "basic",
|
|
1362
|
+
"tags": ["development", "openapi", "adapters", "adapter"],
|
|
1363
|
+
"features": [
|
|
1364
|
+
"Using `OpenapiAdapter.init()` with just `name` and `url` to auto-generate MCP tools",
|
|
1365
|
+
"Each OpenAPI operation becomes a tool named `<adapter-name>:<operationId>`",
|
|
1366
|
+
"The adapter is registered in the `adapters` array of `@App`, not in `plugins`",
|
|
1367
|
+
"The `name` field serves as the namespace prefix to prevent tool name collisions"
|
|
1368
|
+
]
|
|
1369
|
+
},
|
|
1353
1370
|
{
|
|
1354
1371
|
"name": "authenticated-adapter-with-polling",
|
|
1355
1372
|
"description": "Demonstrates configuring authentication (API key and bearer token) and automatic spec polling for OpenAPI adapters.",
|
|
@@ -1364,15 +1381,28 @@
|
|
|
1364
1381
|
]
|
|
1365
1382
|
},
|
|
1366
1383
|
{
|
|
1367
|
-
"name": "
|
|
1368
|
-
"description": "Demonstrates
|
|
1369
|
-
"level": "
|
|
1370
|
-
"tags": ["development", "openapi", "adapters", "
|
|
1384
|
+
"name": "format-resolution-and-custom-resolvers",
|
|
1385
|
+
"description": "Demonstrates using built-in and custom format resolvers to enrich tool input schemas with concrete constraints from OpenAPI format values.",
|
|
1386
|
+
"level": "intermediate",
|
|
1387
|
+
"tags": ["development", "openapi", "adapters", "format", "schema", "validation"],
|
|
1371
1388
|
"features": [
|
|
1372
|
-
"
|
|
1373
|
-
"
|
|
1374
|
-
"
|
|
1375
|
-
"
|
|
1389
|
+
"Enabling built-in format resolvers with `resolveFormats: true` for uuid, date-time, email, int32, etc.",
|
|
1390
|
+
"Adding custom format resolvers for domain-specific formats (phone, currency)",
|
|
1391
|
+
"Merging custom resolvers with built-ins where custom takes precedence",
|
|
1392
|
+
"Using custom-only resolvers without built-ins for full control"
|
|
1393
|
+
]
|
|
1394
|
+
},
|
|
1395
|
+
{
|
|
1396
|
+
"name": "ref-security-and-filtering",
|
|
1397
|
+
"description": "Demonstrates configuring $ref resolution security to prevent SSRF attacks and filtering which API operations become MCP tools.",
|
|
1398
|
+
"level": "intermediate",
|
|
1399
|
+
"tags": ["development", "openapi", "adapters", "security", "ssrf", "filtering"],
|
|
1400
|
+
"features": [
|
|
1401
|
+
"Configuring `refResolution` to restrict which hosts and protocols are allowed for external `$ref` pointers",
|
|
1402
|
+
"Using `allowedHosts` to restrict $refs to trusted schema servers",
|
|
1403
|
+
"Using `allowedProtocols` to enable or disable file://, http://, https://, and other protocols",
|
|
1404
|
+
"Filtering operations with `includeOperations`, `excludeOperations`, and `filterFn`",
|
|
1405
|
+
"Combining security hardening with operation filtering for a production-ready setup"
|
|
1376
1406
|
]
|
|
1377
1407
|
},
|
|
1378
1408
|
{
|