@directive-run/knowledge 1.14.0 → 1.16.0

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.
Files changed (43) hide show
  1. package/README.md +50 -15
  2. package/ai/ai-adapters.md +7 -0
  3. package/ai/ai-agents-streaming.md +187 -149
  4. package/ai/ai-budget-resilience.md +305 -132
  5. package/ai/ai-communication.md +220 -197
  6. package/ai/ai-debug-observability.md +259 -173
  7. package/ai/ai-guardrails-memory.md +191 -153
  8. package/ai/ai-mcp-rag.md +204 -199
  9. package/ai/ai-multi-agent.md +254 -153
  10. package/ai/ai-orchestrator.md +353 -114
  11. package/ai/ai-security.md +287 -180
  12. package/ai/ai-tasks.md +8 -1
  13. package/ai/ai-testing-evals.md +363 -256
  14. package/core/anti-patterns.md +18 -2
  15. package/core/constraints.md +13 -2
  16. package/core/core-patterns.md +12 -0
  17. package/core/error-boundaries.md +8 -0
  18. package/core/history.md +7 -0
  19. package/core/multi-module.md +15 -3
  20. package/core/naming.md +128 -90
  21. package/core/plugins.md +7 -0
  22. package/core/react-adapter.md +256 -174
  23. package/core/resolvers.md +10 -0
  24. package/core/schema-types.md +8 -0
  25. package/core/system-api.md +10 -0
  26. package/core/testing.md +257 -143
  27. package/examples/checkers.ts +15 -16
  28. package/examples/contact-form.ts +2 -2
  29. package/examples/counter-react.ts +1 -1
  30. package/examples/counter-svelte.ts +1 -1
  31. package/examples/counter-vue.ts +1 -1
  32. package/examples/counter.ts +1 -1
  33. package/examples/data-triggers.ts +4 -4
  34. package/examples/feature-flags.ts +2 -2
  35. package/examples/form-wizard.ts +2 -2
  36. package/examples/newsletter.ts +2 -2
  37. package/examples/server.ts +2 -2
  38. package/examples/shopping-cart.ts +1 -1
  39. package/examples/topic-guard.ts +1 -1
  40. package/package.json +3 -3
  41. package/sitemap.md +17 -11
  42. package/examples/debounce-constraints.ts +0 -95
  43. package/examples/multi-module.ts +0 -58
package/ai/ai-mcp-rag.md CHANGED
@@ -1,288 +1,293 @@
1
- # AI MCP and RAG
1
+ # AI MCP + RAG
2
2
 
3
- MCP (Model Context Protocol) server integration and RAG (Retrieval-Augmented Generation) enrichment for Directive AI agents.
3
+ > Covers `@directive-run/ai/mcp` and `@directive-run/ai` MCP adapter (`createMCPAdapter`), RAG enricher, embedder utilities.
4
4
 
5
- ## Decision Tree: "How do I connect external tools or knowledge?"
5
+ Model Context Protocol (MCP) server integration and Retrieval-Augmented Generation (RAG) enrichment for Directive AI agents. Import from `@directive-run/ai/mcp` for the MCP adapter (the main barrel re-exports it with `@deprecated` notices for v2 removal), and from `@directive-run/ai` for the RAG enricher + embedder utilities.
6
+
7
+ ## Decision tree
6
8
 
7
9
  ```
8
10
  What do you need?
9
- ├── External tool servers (MCP) → createMCPAdapter({ servers: [...] })
10
- │ ├── stdio transport → command-based MCP servers
11
- │ └── SSE transport → HTTP-based MCP servers
12
-
13
- ├── Knowledge retrieval (RAG) → createRAGEnricher({ embedder, storage })
14
- │ ├── Need embeddings → createOpenAIEmbedder() or createAnthropicEmbedder()
15
- │ ├── Need vector storage → createJSONFileStore() or custom VectorStore
16
- │ └── Need chunk ingestion → enricher.ingest(documents)
11
+ ├── External tool servers (MCP) → createMCPAdapter({ servers, ... })
12
+ │ ├── stdio transport → command-based MCP servers
13
+ │ └── SSE transport → HTTP-based MCP servers
17
14
 
18
- Where do I import from?
19
- ├── MCP adapter import { createMCPAdapter } from '@directive-run/ai'
20
- ├── RAG enricherimport { createRAGEnricher } from '@directive-run/ai'
21
- └── Embeddersimport from '@directive-run/ai/openai' (subpath)
15
+ ├── Knowledge retrieval (RAG) → createRAGEnricher({ embedder, storage })
16
+ ├── User-supplied embeddings EmbedderFn = (text) => Promise<number[]>
17
+ ├── Batched embedding calls createBatchedEmbedder({ embed, batchSize, ... })
18
+ │ ├── Vector storage createJSONFileStore(opts) or your own RAGStorage
19
+ │ └── Ingest documents → enricher.ingest(documents)
22
20
  ```
23
21
 
24
- ## MCP Server Integration
22
+ ## MCP server integration
25
23
 
26
- Connect to MCP servers to give agents access to external tools:
24
+ The MCP adapter manages connections to one or more MCP servers, exposes their tools to your agents, and provides constraint-driven approval + risk-scoring for tool calls.
27
25
 
28
26
  ```typescript
29
- import { createMCPAdapter } from "@directive-run/ai";
27
+ import { createMCPAdapter, type MCPTool } from "@directive-run/ai/mcp";
30
28
 
31
29
  const mcp = createMCPAdapter({
32
30
  servers: [
33
- // stdio transport runs a local process
34
- {
35
- name: "tools",
36
- transport: "stdio",
37
- command: "npx mcp-server-tools",
38
- },
39
- // SSE transport – connects to an HTTP server
40
- {
41
- name: "data",
42
- transport: "sse",
43
- url: "http://localhost:3001/sse",
44
- },
31
+ { name: "tools", transport: "stdio", command: "npx", args: ["mcp-server-tools"] },
32
+ { name: "data", transport: "sse", url: "http://localhost:3001/sse" },
45
33
  ],
46
-
47
- // Per-tool constraints
48
34
  toolConstraints: {
49
- "tools/dangerous-tool": {
50
- requireApproval: true,
51
- maxAttempts: 3,
52
- },
53
- "tools/read-only": {
54
- requireApproval: false,
55
- },
35
+ "tools/dangerous-tool": { requireApproval: true, maxAttempts: 3 },
36
+ "tools/read-only": { requireApproval: false },
37
+ },
38
+ autoConnect: false, // default false — opt-in to connecting on creation
39
+ autoReconnect: true, // default false — opt-in to reconnect on disconnect
40
+ approvalTimeoutMs: 5 * 60 * 1000, // default 300_000
41
+ allowDirectCalls: false, // default false — must be true to use callToolDirect()
42
+ clientFactory: (cfg) => new Client(cfg), // optional — supply a real MCP SDK client
43
+ events: {
44
+ onConnect: ({ server }) => console.log(`mcp:${server} up`),
45
+ onDisconnect: ({ server, reason }) => console.warn(`mcp:${server} down — ${reason}`),
46
+ onToolCall: ({ server, tool, args }) => log("mcp:tool", { server, tool }),
47
+ onApproval: (request) => showApprovalDialog(request),
56
48
  },
57
-
58
- // Connection options
59
- connectionTimeout: 10000,
60
- reconnect: true,
61
49
  });
62
50
 
63
- // Connect to all servers
64
51
  await mcp.connect();
65
52
 
66
- // Get available tools (normalized for Directive agents)
67
- const tools = mcp.getTools();
53
+ // Get available tools returns Map<serverName, MCPTool[]>, NOT a flat array
54
+ const toolsMap: Map<string, MCPTool[]> = mcp.getTools();
55
+ const flatTools = Array.from(toolsMap.values()).flat();
68
56
 
69
57
  // Use tools with an agent
70
58
  const agent = {
71
59
  name: "researcher",
72
60
  instructions: "Use available tools to research topics.",
73
61
  model: "claude-sonnet-4-5",
74
- tools: tools,
62
+ tools: flatTools,
75
63
  };
76
64
  ```
77
65
 
78
- ### Anti-Pattern #36: Importing MCP from subpath
66
+ ## MCP server lifecycle
67
+
68
+ The lifecycle verbs are `connect` / `connectServer(name)` for opening connections, and `disconnect()` / `disconnectServer(name)` for closing them. There is no `mcp.disconnect("name")` mixed form, no `mcp.disconnectAll()`, and no `mcp.getStatus()`.
79
69
 
80
70
  ```typescript
81
- // WRONG – there is no /mcp subpath export
82
- import { createMCPToolProvider } from "@directive-run/ai/mcp";
71
+ // Connect everything
72
+ await mcp.connect();
83
73
 
84
- // CORRECT MCP adapter is exported from the main package
85
- import { createMCPAdapter } from "@directive-run/ai";
74
+ // Or one server at a time
75
+ await mcp.connectServer("tools");
76
+
77
+ // Status — typed per-server
78
+ const single: MCPServerState | undefined = mcp.getServerStatus("tools");
79
+ const all: Map<string, MCPServerState> = mcp.getAllServerStatuses();
80
+
81
+ // Disconnect one server
82
+ await mcp.disconnectServer("tools");
83
+
84
+ // Disconnect all
85
+ await mcp.disconnect();
86
86
  ```
87
87
 
88
- ## MCP Server Lifecycle
88
+ ## Calling MCP tools with constraints
89
+
90
+ `callTool(server, tool, args, facts)` applies per-tool constraints (rate limits, approvals, argument-size caps). Use this in resolvers. `callToolDirect(server, tool, args)` bypasses the constraint pipeline — only available when `allowDirectCalls: true`.
89
91
 
90
92
  ```typescript
91
- // Connect to all configured servers
92
- await mcp.connect();
93
+ const result = await mcp.callTool(
94
+ "tools",
95
+ "search-docs",
96
+ { query: "directive constraints" },
97
+ context.facts, // for constraint evaluation
98
+ );
99
+
100
+ // For trusted internal calls only:
101
+ const direct = await mcp.callToolDirect("tools", "internal-ping", {});
102
+ ```
93
103
 
94
- // Check server status
95
- const status = mcp.getStatus();
96
- // { tools: "connected", data: "connected" }
104
+ ## Approval workflow for sensitive tools
97
105
 
98
- // Disconnect a specific server
99
- await mcp.disconnect("tools");
106
+ When `toolConstraints[…].requireApproval: true`, the adapter queues an `MCPApprovalRequest` instead of executing immediately. Surface it via `events.onApproval` and resolve via the instance methods.
100
107
 
101
- // Disconnect all servers
102
- await mcp.disconnectAll();
108
+ ```typescript
109
+ const pending = mcp.getPendingApprovals();
110
+ mcp.approve(request.id);
111
+ mcp.reject(request.id, "violates data policy");
103
112
 
104
- // Reconnect after disconnect
105
- await mcp.connect();
113
+ // Read the rejection reason for a previously-resolved request
114
+ const reason = mcp.getRejectionReason(request.id);
106
115
  ```
107
116
 
108
- ## MCP with Orchestrator
117
+ ## Resource sync
118
+
119
+ MCP also exposes resources (read-only content the agent can pull). `syncResources` materializes them into Directive facts so constraints can react to them.
109
120
 
110
121
  ```typescript
111
- import { createAgentOrchestrator } from "@directive-run/ai";
122
+ const resourcesMap = mcp.getResources(); // Map<serverName, MCPResource[]>
123
+ const oneResource = await mcp.readResource("data", "file://config.yaml");
124
+ await mcp.syncResources(system.facts);
125
+ ```
112
126
 
113
- const mcp = createMCPAdapter({
114
- servers: [
115
- { name: "tools", transport: "stdio", command: "npx mcp-server-tools" },
116
- ],
127
+ ## RAG enrichment
128
+
129
+ `createRAGEnricher` wires an embedder + a vector store into an agent's input pipeline, retrieving relevant context chunks before the agent runs.
130
+
131
+ ```typescript
132
+ import {
133
+ createRAGEnricher,
134
+ createJSONFileStore,
135
+ createBatchedEmbedder,
136
+ createTestEmbedder,
137
+ type EmbedderFn,
138
+ type RAGEnricher,
139
+ } from "@directive-run/ai";
140
+
141
+ // 1. Supply an embedder. EmbedderFn = (text: string) => Promise<number[]>
142
+ // There is no createOpenAIEmbedder / createAnthropicEmbedder factory —
143
+ // you bring your own and pass it through createBatchedEmbedder for production.
144
+ const rawEmbed: EmbedderFn = async (text) => {
145
+ const res = await myEmbedAPI.embed(text);
146
+ return res.embedding;
147
+ };
148
+
149
+ const { embed } = createBatchedEmbedder({
150
+ embed: rawEmbed,
151
+ batchSize: 16,
152
+ flushIntervalMs: 50,
117
153
  });
118
154
 
119
- await mcp.connect();
155
+ // 2. Storage — a JSON file store for prototyping, or your own RAGStorage adapter
156
+ const storage = createJSONFileStore({ path: "./rag-store.json" });
120
157
 
121
- const orchestrator = createAgentOrchestrator({
122
- runner,
123
- hooks: {
124
- onStart: async () => {
125
- await mcp.connect();
126
- },
127
- },
158
+ const enricher: RAGEnricher = createRAGEnricher({
159
+ embedder: embed,
160
+ storage,
161
+ topK: 5,
162
+ minSimilarity: 0.7,
128
163
  });
129
164
 
130
- const agent = {
131
- name: "worker",
132
- instructions: "Complete tasks using available tools.",
133
- model: "claude-sonnet-4-5",
134
- tools: mcp.getTools(),
165
+ // 3. Ingest documents
166
+ await enricher.ingest([
167
+ { id: "doc-1", text: "Directive is a constraint-driven runtime…", metadata: { source: "README.md" } },
168
+ { id: "doc-2", text: "Auto-tracked derivations recompute on read…", metadata: { source: "concepts" } },
169
+ ]);
170
+
171
+ // 4. Use the enricher in your runner pipeline
172
+ const enrichedRunner: AgentRunner = async (agent, input, opts) => {
173
+ const enriched = await enricher.enrich(input);
174
+ return baseRunner(agent, enriched, opts);
135
175
  };
136
176
 
137
- const result = await orchestrator.run(agent, "Search for recent AI papers");
177
+ const orchestrator = createAgentOrchestrator({ runner: enrichedRunner });
138
178
  ```
139
179
 
140
- ---
180
+ For testing without API calls, use `createTestEmbedder(dimensions?)` — deterministic, no network.
141
181
 
142
- ## RAG Enrichment
182
+ ## Anti-patterns
143
183
 
144
- Augment agent prompts with relevant context from a knowledge base:
184
+ ### `mcp.disconnect(name)` / `mcp.disconnectAll()`
145
185
 
146
186
  ```typescript
147
- import { createRAGEnricher } from "@directive-run/ai";
148
- import { createOpenAIEmbedder } from "@directive-run/ai/openai";
187
+ // WRONG neither shape exists
188
+ await mcp.disconnect("tools");
189
+ await mcp.disconnectAll();
149
190
 
150
- const enricher = createRAGEnricher({
151
- // Embedder for similarity search
152
- embedder: createOpenAIEmbedder({
153
- apiKey: process.env.OPENAI_API_KEY,
154
- }),
191
+ // CORRECT
192
+ await mcp.disconnectServer("tools");
193
+ await mcp.disconnect();
194
+ ```
155
195
 
156
- // Vector storage backend
157
- storage: createJSONFileStore({ filePath: "./chunks.json" }),
196
+ ### `mcp.getStatus()`
158
197
 
159
- // Retrieval settings
160
- topK: 5, // Max chunks to retrieve
161
- minSimilarity: 0.3, // Minimum cosine similarity threshold
198
+ ```typescript
199
+ // WRONG no flat-object status method
200
+ const status = mcp.getStatus(); // ?? { tools: "connected", data: "connected" }
162
201
 
163
- // Format each retrieved chunk
164
- formatChunk: (chunk, similarity) => {
165
- return `[${similarity.toFixed(2)}] ${chunk.content}`;
166
- },
167
- });
202
+ // CORRECT typed per-server
203
+ const all = mcp.getAllServerStatuses(); // Map<name, MCPServerState>
204
+ const one = mcp.getServerStatus("tools"); // MCPServerState | undefined
168
205
  ```
169
206
 
170
- ## Ingesting Documents
207
+ ### Treating `mcp.getTools()` as a flat array
171
208
 
172
209
  ```typescript
173
- // Ingest raw text with metadata
174
- await enricher.ingest([
175
- {
176
- content: "Directive uses proxy-based facts for auto-tracking.",
177
- metadata: { source: "docs", topic: "facts" },
178
- },
179
- {
180
- content: "Derivations are auto-tracked computed values.",
181
- metadata: { source: "docs", topic: "derivations" },
182
- },
183
- ]);
210
+ // WRONG getTools() returns Map<serverName, MCPTool[]>
211
+ const flat: MCPTool[] = mcp.getTools();
212
+ agent.tools = flat;
184
213
 
185
- // Ingest from files (chunks automatically)
186
- await enricher.ingestFile("./docs/architecture.md", {
187
- chunkSize: 500,
188
- chunkOverlap: 50,
189
- metadata: { source: "architecture" },
190
- });
214
+ // CORRECT flatten if your agent expects a flat list
215
+ const flat = Array.from(mcp.getTools().values()).flat();
216
+ agent.tools = flat;
191
217
  ```
192
218
 
193
- ## Enriching Prompts
219
+ ### Importing from `@directive-run/ai` only
194
220
 
195
221
  ```typescript
196
- // Basic enrichment prepends relevant context
197
- const enrichedInput = await enricher.enrich("How do facts work?", {
198
- prefix: "Use this context to answer:\n",
199
- });
200
- // Result: "Use this context to answer:\n[0.92] Directive uses proxy-based..."
222
+ // WORKS, but fires v2-deprecation notices
223
+ import { createMCPAdapter } from "@directive-run/ai";
201
224
 
202
- // With conversation history for better retrieval
203
- const enrichedInput = await enricher.enrich("Tell me more about that", {
204
- prefix: "Use this context:\n",
205
- history: messages,
206
- });
225
+ // PREFERRED the subpath barrel is the v2-stable import
226
+ import { createMCPAdapter } from "@directive-run/ai/mcp";
207
227
  ```
208
228
 
209
- ## RAG with Orchestrator
229
+ ### `MCPAdapterConfig` option names from a different library
210
230
 
211
231
  ```typescript
212
- import { createAgentOrchestrator } from "@directive-run/ai";
213
-
214
- const orchestrator = createAgentOrchestrator({
215
- runner,
216
- hooks: {
217
- onBeforeRun: async (agent, prompt) => {
218
- // Enrich every prompt with relevant context
219
- const enriched = await enricher.enrich(prompt, {
220
- prefix: "Relevant context:\n",
221
- });
222
-
223
- return { approved: true, modifiedPrompt: enriched };
224
- },
225
- },
226
- });
232
+ // WRONG these options don't exist
233
+ createMCPAdapter({
234
+ servers,
235
+ connectionTimeout: 10_000,
236
+ reconnect: true,
237
+ })
238
+
239
+ // CORRECT the real names
240
+ createMCPAdapter({
241
+ servers,
242
+ autoConnect: true,
243
+ autoReconnect: true,
244
+ approvalTimeoutMs: 300_000,
245
+ allowDirectCalls: false,
246
+ clientFactory: (cfg) => new Client(cfg),
247
+ })
227
248
  ```
228
249
 
229
- ## Custom Embedders
230
-
231
- Implement the `Embedder` interface for any provider:
250
+ ### `createOpenAIEmbedder` / `createAnthropicEmbedder`
232
251
 
233
252
  ```typescript
234
- import type { Embedder } from "@directive-run/ai";
235
-
236
- const customEmbedder: Embedder = {
237
- embed: async (texts: string[]) => {
238
- // Return float arrays, one per input text
239
- const embeddings = await myEmbeddingAPI.embed(texts);
240
-
241
- return embeddings.map((e) => e.vector);
242
- },
243
-
244
- dimensions: 1536, // Vector dimensions
245
- };
253
+ // WRONG no provider-specific embedder factories ship from @directive-run/ai
254
+ import { createOpenAIEmbedder } from "@directive-run/ai/openai";
255
+ const embedder = createOpenAIEmbedder({ apiKey });
246
256
 
247
- const enricher = createRAGEnricher({
248
- embedder: customEmbedder,
249
- storage: createJSONFileStore({ filePath: "./chunks.json" }),
250
- topK: 5,
251
- minSimilarity: 0.3,
252
- });
257
+ // CORRECT supply your own EmbedderFn, optionally batched
258
+ const rawEmbed: EmbedderFn = async (text) => (await openai.embeddings.create({ input: text, model: "text-embedding-3-small" })).data[0].embedding;
259
+ const { embed } = createBatchedEmbedder({ embed: rawEmbed, batchSize: 16 });
253
260
  ```
254
261
 
255
- ## Custom Vector Storage
256
-
257
- Implement the `VectorStore` interface for any backend:
262
+ ### Treating `Embedder` as an object with `embed(texts[]): number[][]`
258
263
 
259
264
  ```typescript
260
- import type { VectorStore } from "@directive-run/ai";
261
-
262
- const pgStore: VectorStore = {
263
- add: async (chunks) => {
264
- await db.query("INSERT INTO chunks ...", chunks);
265
- },
266
- search: async (vector, topK) => {
267
- const results = await db.query(
268
- "SELECT * FROM chunks ORDER BY embedding <=> $1 LIMIT $2",
269
- [vector, topK],
270
- );
265
+ // WRONG there is no Embedder interface; the EmbedderFn signature takes ONE string
266
+ type Embedder = { embed(texts: string[]): Promise<number[][]>; dimensions: number };
271
267
 
272
- return results.rows;
273
- },
274
- clear: async () => {
275
- await db.query("DELETE FROM chunks");
276
- },
277
- };
268
+ // CORRECT — single string in, single vector out
269
+ type EmbedderFn = (text: string) => Promise<number[]>;
278
270
  ```
279
271
 
280
- ## Quick Reference
281
-
282
- | API | Import Path | Purpose |
283
- |---|---|---|
284
- | `createMCPAdapter` | `@directive-run/ai` | Connect to MCP tool servers |
285
- | `createRAGEnricher` | `@directive-run/ai` | RAG pipeline for prompt enrichment |
286
- | `createOpenAIEmbedder` | `@directive-run/ai/openai` | OpenAI text embeddings |
287
- | `createAnthropicEmbedder` | `@directive-run/ai/anthropic` | Anthropic text embeddings |
288
- | `createJSONFileStore` | `@directive-run/ai` | File-based vector storage |
272
+ ## Quick reference
273
+
274
+ | API | Path | Returns | Purpose |
275
+ |---|---|---|---|
276
+ | `createMCPAdapter(config)` | `@directive-run/ai/mcp` | `MCPAdapter` | Connect to MCP servers, expose tools |
277
+ | `mcp.connect()` / `connectServer(name)` | instance method | `Promise<void>` | Open one or all connections |
278
+ | `mcp.disconnect()` / `disconnectServer(name)` | instance method | `Promise<void>` | Close one or all connections |
279
+ | `mcp.getTools()` | instance method | `Map<string, MCPTool[]>` | All tools, keyed by server |
280
+ | `mcp.getResources()` | instance method | `Map<string, MCPResource[]>` | All resources, keyed by server |
281
+ | `mcp.callTool(server, tool, args, facts)` | instance method | `Promise<MCPToolResult>` | Constraint-checked tool call |
282
+ | `mcp.callToolDirect(server, tool, args)` | instance method | `Promise<MCPToolResult>` | Bypass constraints (requires `allowDirectCalls: true`) |
283
+ | `mcp.getServerStatus(name)` / `getAllServerStatuses()` | instance method | `MCPServerState` | Per-server connection state |
284
+ | `mcp.approve(id)` / `reject(id, reason?)` | instance method | `void` | Resolve a pending approval |
285
+ | `createRAGEnricher(config)` | `@directive-run/ai` | `RAGEnricher` | Embedding-driven context retrieval |
286
+ | `createBatchedEmbedder(config)` | `@directive-run/ai` | `{ embed, flush, dispose }` | Wrap an EmbedderFn with batching |
287
+ | `createTestEmbedder(dim?)` | `@directive-run/ai` | `EmbedderFn` | Deterministic embedder for tests |
288
+ | `createJSONFileStore(options)` | `@directive-run/ai` | `RAGStorage` | File-backed vector store for prototyping |
289
+
290
+ ## See also
291
+
292
+ - [`ai-orchestrator.md`](./ai-orchestrator.md) — where MCP-supplied tools and RAG-enriched runners flow into agent runs
293
+ - [`ai-security.md`](./ai-security.md) — `toolConstraints[…].requireApproval` and the per-tool approval workflow the MCP adapter exposes