@ductape/mcp 0.2.4 → 0.2.7

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.
@@ -0,0 +1,133 @@
1
+ export declare const EVENTS_IMPLEMENTATION_WARNING = "Ductape manages consumer delivery retries and dead-letter queues. Do not create a parallel application transport DLQ. Use an application transactional outbox only to bridge canonical database commits to Ductape publication. Consumers must remain idempotent and replay-safe.";
2
+ export declare const EVENTS_RECOVERY_SEARCH_TERMS: string[];
3
+ export declare const EVENTS_DELIVERY_SEMANTICS: {
4
+ publisherAcceptance: string;
5
+ consumerRetriesManagedBy: string;
6
+ deadLetterQueueManagedBy: string;
7
+ applicationDlqRequired: boolean;
8
+ applicationOutboxRecommended: boolean;
9
+ applicationOutboxPurpose: string;
10
+ orderingGuarantees: string;
11
+ deliveryGuarantee: string;
12
+ deduplicationGuidance: string;
13
+ idempotentConsumerRequired: boolean;
14
+ responsibilities: {
15
+ applicationTransactionalOutbox: string;
16
+ brokerDeliveryRetries: string;
17
+ exhaustedDeliveryDlq: string;
18
+ replayAndReprocessing: string;
19
+ domainRejectedOrInvalidCommands: string;
20
+ consumerMutationIdempotency: string;
21
+ };
22
+ };
23
+ export declare const EVENTS_PROVIDER_CAPABILITIES: Record<string, Record<string, unknown>>;
24
+ type Operation = {
25
+ name: string;
26
+ description: string;
27
+ inputSchema: Record<string, string>;
28
+ outputSchema: Record<string, string>;
29
+ requiredIdentifiers: string[];
30
+ environmentRequirements: string;
31
+ connectionRequirements: string;
32
+ permissions: string;
33
+ idempotency: string;
34
+ pagination: string;
35
+ applicableStates: string[];
36
+ commonErrors: Array<{
37
+ error: string;
38
+ remediation: string;
39
+ }>;
40
+ mutatesState: boolean;
41
+ mayIncurProviderCost: boolean;
42
+ };
43
+ export declare const EVENTS_OPERATION_REGISTRY: Operation[];
44
+ export declare function eventsCapabilityOverview(): {
45
+ capability: string;
46
+ aliases: string[];
47
+ implementationWarning: string;
48
+ deliverySemantics: {
49
+ publisherAcceptance: string;
50
+ consumerRetriesManagedBy: string;
51
+ deadLetterQueueManagedBy: string;
52
+ applicationDlqRequired: boolean;
53
+ applicationOutboxRecommended: boolean;
54
+ applicationOutboxPurpose: string;
55
+ orderingGuarantees: string;
56
+ deliveryGuarantee: string;
57
+ deduplicationGuidance: string;
58
+ idempotentConsumerRequired: boolean;
59
+ responsibilities: {
60
+ applicationTransactionalOutbox: string;
61
+ brokerDeliveryRetries: string;
62
+ exhaustedDeliveryDlq: string;
63
+ replayAndReprocessing: string;
64
+ domainRejectedOrInvalidCommands: string;
65
+ consumerMutationIdempotency: string;
66
+ };
67
+ };
68
+ recoverySearchTerms: string[];
69
+ documentationTopics: string[];
70
+ providerCapabilities: Record<string, Record<string, unknown>>;
71
+ operations: Operation[];
72
+ };
73
+ export declare function searchEventsCapabilities(query: string): {
74
+ query: string;
75
+ canonicalTopic: string;
76
+ recoveryMatch: boolean;
77
+ deliverySemantics: {
78
+ publisherAcceptance: string;
79
+ consumerRetriesManagedBy: string;
80
+ deadLetterQueueManagedBy: string;
81
+ applicationDlqRequired: boolean;
82
+ applicationOutboxRecommended: boolean;
83
+ applicationOutboxPurpose: string;
84
+ orderingGuarantees: string;
85
+ deliveryGuarantee: string;
86
+ deduplicationGuidance: string;
87
+ idempotentConsumerRequired: boolean;
88
+ responsibilities: {
89
+ applicationTransactionalOutbox: string;
90
+ brokerDeliveryRetries: string;
91
+ exhaustedDeliveryDlq: string;
92
+ replayAndReprocessing: string;
93
+ domainRejectedOrInvalidCommands: string;
94
+ consumerMutationIdempotency: string;
95
+ };
96
+ };
97
+ operations: Operation[];
98
+ };
99
+ export declare function inspectEventsComponent(component: any): {
100
+ component: any;
101
+ provider: any;
102
+ environments: any;
103
+ topics: any;
104
+ subscriptions: any;
105
+ consumerBindings: any;
106
+ messageStatistics: string;
107
+ availableOperationalActions: Operation[];
108
+ deliverySemantics: {
109
+ publisherAcceptance: string;
110
+ consumerRetriesManagedBy: string;
111
+ deadLetterQueueManagedBy: string;
112
+ applicationDlqRequired: boolean;
113
+ applicationOutboxRecommended: boolean;
114
+ applicationOutboxPurpose: string;
115
+ orderingGuarantees: string;
116
+ deliveryGuarantee: string;
117
+ deduplicationGuidance: string;
118
+ idempotentConsumerRequired: boolean;
119
+ responsibilities: {
120
+ applicationTransactionalOutbox: string;
121
+ brokerDeliveryRetries: string;
122
+ exhaustedDeliveryDlq: string;
123
+ replayAndReprocessing: string;
124
+ domainRejectedOrInvalidCommands: string;
125
+ consumerMutationIdempotency: string;
126
+ };
127
+ };
128
+ implementationWarning: string;
129
+ configurationWarnings: any;
130
+ documentationTopics: string[];
131
+ };
132
+ export {};
133
+ //# sourceMappingURL=events-capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-capabilities.d.ts","sourceRoot":"","sources":["../src/events-capabilities.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,6BAA6B,sRAC2O,CAAC;AAEtR,eAAO,MAAM,4BAA4B,UAWxC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;CAmBrC,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkDhF,CAAC;AAEF,KAAK,SAAS,GAAG;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,uBAAuB,EAAE,MAAM,CAAC;IAChC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,YAAY,EAAE,OAAO,CAAC;IACtB,oBAAoB,EAAE,OAAO,CAAC;CAC/B,CAAC;AAyCF,eAAO,MAAM,yBAAyB,EAAE,SAAS,EA8IhD,CAAC;AAEF,wBAAgB,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWvC;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;EASrD;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCpD"}
@@ -0,0 +1,311 @@
1
+ export const EVENTS_IMPLEMENTATION_WARNING = 'Ductape manages consumer delivery retries and dead-letter queues. Do not create a parallel application transport DLQ. Use an application transactional outbox only to bridge canonical database commits to Ductape publication. Consumers must remain idempotent and replay-safe.';
2
+ export const EVENTS_RECOVERY_SEARCH_TERMS = [
3
+ 'dlq',
4
+ 'dead letter',
5
+ 'dead letters',
6
+ 'failed messages',
7
+ 'retry',
8
+ 'retries',
9
+ 'poison message',
10
+ 'replay',
11
+ 'reprocess',
12
+ 'consumer failure',
13
+ ];
14
+ export const EVENTS_DELIVERY_SEMANTICS = {
15
+ publisherAcceptance: 'Ductape acknowledges events accepted for publication; acceptance is not proof of consumer-side mutation.',
16
+ consumerRetriesManagedBy: 'ductape',
17
+ deadLetterQueueManagedBy: 'ductape',
18
+ applicationDlqRequired: false,
19
+ applicationOutboxRecommended: true,
20
+ applicationOutboxPurpose: 'Protect the canonical database transaction-to-Ductape publication boundary.',
21
+ orderingGuarantees: 'Provider and configuration dependent; inspect each environment. Never infer global ordering.',
22
+ deliveryGuarantee: 'At-least-once processing should be assumed unless the inspected provider/configuration explicitly proves otherwise.',
23
+ deduplicationGuidance: 'Use publishIdempotent with a stable logical-operation key where appropriate and deduplicate consumer mutations.',
24
+ idempotentConsumerRequired: true,
25
+ responsibilities: {
26
+ applicationTransactionalOutbox: 'application',
27
+ brokerDeliveryRetries: 'ductape',
28
+ exhaustedDeliveryDlq: 'ductape',
29
+ replayAndReprocessing: 'ductape',
30
+ domainRejectedOrInvalidCommands: 'application (not a transport DLQ)',
31
+ consumerMutationIdempotency: 'application',
32
+ },
33
+ };
34
+ export const EVENTS_PROVIDER_CAPABILITIES = {
35
+ google_pubsub: {
36
+ ordering: 'supported only when provider ordering keys/configuration are enabled; not inferred by Ductape',
37
+ acknowledgement: 'provider subscription acknowledgement deadline',
38
+ retriesAndBackoff: 'provider subscription policy; inspect the deployed subscription',
39
+ dlq: 'Ductape recovery operations are available, but provider dead-letter policy must be verified before claiming a provider DLQ exists',
40
+ retention: 'provider topic/subscription retention; inspect live provider configuration',
41
+ },
42
+ aws_sqs: {
43
+ ordering: 'FIFO only for FIFO queues; standard queues do not guarantee strict ordering',
44
+ acknowledgement: 'visibility timeout',
45
+ retriesAndBackoff: 'receive count and visibility timeout/provider redrive policy',
46
+ dlq: 'provider redrive/DLQ must be configured and verified; Ductape exposes tracked dead letters and reprocessing',
47
+ retention: 'provider queue retention',
48
+ },
49
+ azure_servicebus: {
50
+ ordering: 'sessions can provide ordered handling when configured; otherwise provider-controlled',
51
+ acknowledgement: 'message lock duration',
52
+ retriesAndBackoff: 'max delivery count and lock/redelivery behavior',
53
+ dlq: 'provider entity DLQ exists, but its configured state and access must be verified',
54
+ retention: 'provider entity configuration',
55
+ },
56
+ kafka: {
57
+ ordering: 'within a partition only',
58
+ acknowledgement: 'consumer offset commit',
59
+ retriesAndBackoff: 'Ductape consumer failure tracking plus application/provider configuration',
60
+ dlq: 'do not assume a native broker DLQ topic exists unless it is configured and verified',
61
+ retention: 'topic retention configuration',
62
+ },
63
+ rabbitmq: {
64
+ ordering: 'queue ordering can be affected by redelivery and multiple consumers',
65
+ acknowledgement: 'broker acknowledgement/nack',
66
+ retriesAndBackoff: 'redelivery and configured exchange/queue policies',
67
+ dlq: 'dead-letter exchange/queue must be configured and verified before claiming provider DLQ availability',
68
+ retention: 'queue/message TTL configuration',
69
+ },
70
+ redis: {
71
+ ordering: 'provider/mode dependent',
72
+ acknowledgement: 'not uniformly supported across Redis pub/sub and stream modes',
73
+ retriesAndBackoff: 'Ductape tracking; provider-native retry semantics may be unsupported',
74
+ dlq: 'no provider DLQ should be implied without a verified stream/consumer-group recovery configuration',
75
+ retention: 'provider/mode dependent',
76
+ },
77
+ nats: {
78
+ ordering: 'subject/stream configuration dependent',
79
+ acknowledgement: 'JetStream configuration dependent; core NATS has different semantics',
80
+ retriesAndBackoff: 'provider mode/configuration dependent',
81
+ dlq: 'do not imply a provider DLQ without verified JetStream/recovery configuration',
82
+ retention: 'stream configuration dependent',
83
+ },
84
+ };
85
+ const runtimeRead = {
86
+ environmentRequirements: 'product environment must exist and the Events component must be configured for it',
87
+ connectionRequirements: 'configured provider connection for the selected Events environment',
88
+ permissions: 'publishable-key runtime access plus product/environment access',
89
+ applicableStates: ['active', 'provider-ready'],
90
+ commonErrors: [
91
+ { error: 'broker/topic not found', remediation: 'inspect the Events component and use its exact broker/topic tags' },
92
+ { error: 'environment not configured', remediation: 'configure that environment on the Events component' },
93
+ { error: 'authentication/permission denied', remediation: 'verify publishable key, workspace, product, and environment access' },
94
+ ],
95
+ };
96
+ function adminOperation(name, description, inputSchema, requiredIdentifiers, mutatesState) {
97
+ return {
98
+ name, description, inputSchema,
99
+ outputSchema: mutatesState ? { resource: 'persisted Events component/topic' } : { resource: 'Events component/topic or array' },
100
+ requiredIdentifiers,
101
+ environmentRequirements: 'administrative product access; create/update must cover every product environment where required',
102
+ connectionRequirements: 'provider-specific credentials or a verified cloud connection for every configured environment',
103
+ permissions: 'authenticated access-key CLI session with workspace/product administration permission',
104
+ idempotency: mutatesState ? 'not guaranteed; fetch first and use update rather than duplicate create' : 'read-only',
105
+ pagination: name.endsWith('.list') ? 'provider/component list; pagination is not currently exposed by this admin method' : 'none',
106
+ applicableStates: ['draft', 'active', 'provider-ready'],
107
+ commonErrors: [
108
+ { error: 'missing environment/provider fields', remediation: 'read the targeted live events.create/events.update schema and cover every product environment' },
109
+ { error: 'unsupported provider configuration', remediation: 'use the selected env type’s live config branch; do not copy fields between providers' },
110
+ { error: 'permission denied', remediation: 'authenticate the CLI and select the correct workspace; do not use ductape_execute for admin writes' },
111
+ ],
112
+ mutatesState,
113
+ mayIncurProviderCost: mutatesState,
114
+ };
115
+ }
116
+ export const EVENTS_OPERATION_REGISTRY = [
117
+ adminOperation('events.create', 'Register an Events component and its per-environment provider connections.', { product: 'string', tag: 'string', name: 'string', description: 'string?', envs: 'provider-specific env[]' }, ['product', 'tag', 'name', 'envs'], true),
118
+ adminOperation('events.update', 'Update an Events component/provider configuration.', { product: 'string', brokerTag: 'string', data: 'targeted live events.update schema' }, ['product', 'brokerTag'], true),
119
+ adminOperation('events.fetch', 'Fetch one Events component.', { product: 'string', brokerTag: 'string' }, ['product', 'brokerTag'], false),
120
+ adminOperation('events.list', 'List Events components for a product.', { product: 'string' }, ['product'], false),
121
+ adminOperation('events.delete', 'Delete or retire an Events component.', { product: 'string', brokerTag: 'string' }, ['product', 'brokerTag'], true),
122
+ adminOperation('events.topics.create', 'Create a topic definition on a registered Events component.', { product: 'string', tag: 'broker:topic', name: 'string', sample: 'object?', idempotent: 'boolean?', queueUrls: 'array?' }, ['product', 'tag', 'name'], true),
123
+ adminOperation('events.topics.update', 'Update an Events topic definition.', { product: 'string', topicTag: 'broker:topic', data: 'topic patch' }, ['product', 'topicTag'], true),
124
+ adminOperation('events.topics.fetch', 'Fetch one Events topic definition.', { product: 'string', topicTag: 'broker:topic' }, ['product', 'topicTag'], false),
125
+ adminOperation('events.topics.list', 'List topic definitions for an Events component.', { product: 'string', brokerTag: 'string' }, ['product', 'brokerTag'], false),
126
+ {
127
+ name: 'events.produce', description: 'Publish an event immediately.',
128
+ inputSchema: { product: 'string', env: 'string', event: 'broker:topic', message: 'object', session: 'string?' },
129
+ outputSchema: { accepted: 'boolean', messageId: 'string?' }, requiredIdentifiers: ['product', 'env', 'event'],
130
+ ...runtimeRead, idempotency: 'not inherently idempotent; use events.publishIdempotent when duplicate publication matters',
131
+ pagination: 'none', mutatesState: true, mayIncurProviderCost: true,
132
+ },
133
+ {
134
+ name: 'events.publishIdempotent', description: 'Publish with a stable deduplication key.',
135
+ inputSchema: { product: 'string', env: 'string', event: 'broker:topic', message: 'object', idempotencyKey: 'string', idempotencyTtl: 'number?' },
136
+ outputSchema: { accepted: 'boolean', duplicate: 'boolean?', messageId: 'string?' }, requiredIdentifiers: ['product', 'env', 'event', 'idempotencyKey'],
137
+ ...runtimeRead, idempotency: 'deduplicates the logical publication for the configured TTL',
138
+ pagination: 'none', mutatesState: true, mayIncurProviderCost: true,
139
+ },
140
+ {
141
+ name: 'events.consume', description: 'Register a server-side consumer; successful return acknowledges and thrown errors trigger managed failure handling.',
142
+ inputSchema: { product: 'string', env: 'string', event: 'broker:topic', callback: 'function' },
143
+ outputSchema: { subscribed: 'boolean', consumerTag: 'string?' }, requiredIdentifiers: ['product', 'env', 'event'],
144
+ ...runtimeRead, permissions: 'server-side SDK access; browser clients cannot consume',
145
+ idempotency: 'consumer mutations must be idempotent and replay-safe', pagination: 'none',
146
+ mutatesState: true, mayIncurProviderCost: true,
147
+ },
148
+ {
149
+ name: 'events.dispatch', description: 'Queue or schedule an Events publication.',
150
+ inputSchema: { product: 'string', env: 'string', event: 'broker:topic', input: '{message: object}', retries: 'number?', schedule: 'object?' },
151
+ outputSchema: { job_id: 'string', status: 'scheduled|queued', scheduled_at: 'string?', recurring: 'boolean' },
152
+ requiredIdentifiers: ['product', 'env', 'event'], ...runtimeRead,
153
+ connectionRequirements: 'Events provider connection plus redis_url for dispatch scheduling',
154
+ idempotency: 'job/publication idempotency depends on stable job and message keys; consumers remain idempotent',
155
+ pagination: 'none', mutatesState: true, mayIncurProviderCost: true,
156
+ },
157
+ {
158
+ name: 'events.messages.query', description: 'Query tracked broker messages and delivery state.',
159
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string', topicTag: 'string?', producerTag: 'string?', consumerTag: 'string?', status: 'string?', startDate: 'ISO date?', endDate: 'ISO date?', page: 'number?', limit: 'number?' },
160
+ outputSchema: { messages: 'array', total: 'number', page: 'number', limit: 'number', hasMore: 'boolean' },
161
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
162
+ idempotency: 'read-only', pagination: 'page/limit with total and hasMore', mutatesState: false, mayIncurProviderCost: false,
163
+ },
164
+ {
165
+ name: 'events.getEvents', description: 'Query tracked Events records by broker, topic, category, status, idempotency, and date range.',
166
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string', topic: 'string?', category: 'string?', status: 'string?', idempotent: 'boolean?', startDate: 'Date?', endDate: 'Date?', page: 'number?', limit: 'number?' },
167
+ outputSchema: { events: 'array', total: 'number', page: 'number', limit: 'number', hasMore: 'boolean' },
168
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
169
+ idempotency: 'read-only', pagination: 'page/limit with total and hasMore',
170
+ mutatesState: false, mayIncurProviderCost: false,
171
+ },
172
+ {
173
+ name: 'events.getEvent', description: 'Fetch one tracked Events record by ID.',
174
+ inputSchema: { product: 'string', eventId: 'string' },
175
+ outputSchema: { event: 'tracked event or null' },
176
+ requiredIdentifiers: ['product', 'eventId'], ...runtimeRead,
177
+ environmentRequirements: 'the event ID must belong to the selected product',
178
+ idempotency: 'read-only', pagination: 'none',
179
+ mutatesState: false, mayIncurProviderCost: false,
180
+ },
181
+ {
182
+ name: 'events.getEventStats', description: 'Fetch aggregate tracked event statistics.',
183
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string' },
184
+ outputSchema: { total_events: 'number', failed_count: 'number', success_count: 'number', status_breakdown: 'object?' },
185
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
186
+ idempotency: 'read-only', pagination: 'none',
187
+ mutatesState: false, mayIncurProviderCost: false,
188
+ },
189
+ {
190
+ name: 'events.messages.getDeadLetters', description: 'List messages whose managed consumer delivery attempts were exhausted.',
191
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string', topicTag: 'string?', consumerTag: 'string?', startDate: 'ISO date?', endDate: 'ISO date?', page: 'number?', limit: 'number?' },
192
+ outputSchema: { deadLetters: 'array', total: 'number', page: 'number', limit: 'number', hasMore: 'boolean' },
193
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
194
+ idempotency: 'read-only', pagination: 'page/limit with total and hasMore', mutatesState: false, mayIncurProviderCost: false,
195
+ },
196
+ {
197
+ name: 'events.reprocessDLQ', description: 'Requeue selected or bounded dead-lettered messages for Ductape-managed delivery.',
198
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string', topicTag: 'string?', messageIds: 'string[]?', limit: 'number?' },
199
+ outputSchema: { reprocessed: 'number', failed: 'number', messageIds: 'string[]?' },
200
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
201
+ permissions: 'publishable-key runtime mutation access plus product/environment access',
202
+ idempotency: 'may redeliver; consumer mutation must be idempotent', pagination: 'bounded by messageIds or limit, not page-based',
203
+ applicableStates: ['dead-lettered', 'provider-ready'], mutatesState: true, mayIncurProviderCost: true,
204
+ },
205
+ {
206
+ name: 'events.replayEvent', description: 'Replay one tracked event by event identifier.',
207
+ inputSchema: { product: 'string', env: 'string', eventId: 'string', force: 'boolean?' },
208
+ outputSchema: { replayed: 'boolean', eventId: 'string', messageId: 'string?' },
209
+ requiredIdentifiers: ['product', 'env', 'eventId'], ...runtimeRead,
210
+ permissions: 'publishable-key runtime mutation access plus product/environment access',
211
+ idempotency: 'replay intentionally redelivers; force may bypass replay guards; consumer mutation must be idempotent',
212
+ pagination: 'none', applicableStates: ['tracked', 'failed', 'dead-lettered'],
213
+ mutatesState: true, mayIncurProviderCost: true,
214
+ },
215
+ {
216
+ name: 'events.messages.getStats', description: 'Return aggregate delivery and failure statistics.',
217
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string' },
218
+ outputSchema: { totals: 'object', delivery: 'object', failures: 'object' },
219
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
220
+ idempotency: 'read-only', pagination: 'none', mutatesState: false, mayIncurProviderCost: false,
221
+ },
222
+ {
223
+ name: 'events.messages.getDashboard', description: 'Return operational overview, recent messages, and recovery indicators.',
224
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string' },
225
+ outputSchema: { overview: 'object', recent_messages: 'array', health: 'object?' },
226
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
227
+ idempotency: 'read-only', pagination: 'fixed dashboard window', mutatesState: false, mayIncurProviderCost: false,
228
+ },
229
+ {
230
+ name: 'events.messages.getProducers', description: 'List observed producers.',
231
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string', topicTag: 'string?', page: 'number?', limit: 'number?' },
232
+ outputSchema: { producers: 'array', total: 'number', page: 'number', limit: 'number', hasMore: 'boolean' },
233
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
234
+ idempotency: 'read-only', pagination: 'page/limit', mutatesState: false, mayIncurProviderCost: false,
235
+ },
236
+ {
237
+ name: 'events.messages.getConsumers', description: 'List observed consumers and bindings.',
238
+ inputSchema: { product: 'string', env: 'string', brokerTag: 'string', topicTag: 'string?', page: 'number?', limit: 'number?' },
239
+ outputSchema: { consumers: 'array', total: 'number', page: 'number', limit: 'number', hasMore: 'boolean' },
240
+ requiredIdentifiers: ['product', 'env', 'brokerTag'], ...runtimeRead,
241
+ idempotency: 'read-only', pagination: 'page/limit', mutatesState: false, mayIncurProviderCost: false,
242
+ },
243
+ {
244
+ name: 'events.checkIdempotency', description: 'Check whether a publication idempotency key has already been accepted.',
245
+ inputSchema: { product: 'string', env: 'string', idempotencyKey: 'string' },
246
+ outputSchema: { exists: 'boolean', event_id: 'string?' }, requiredIdentifiers: ['product', 'env', 'idempotencyKey'],
247
+ ...runtimeRead, idempotency: 'read-only idempotency lookup', pagination: 'none',
248
+ mutatesState: false, mayIncurProviderCost: false,
249
+ },
250
+ {
251
+ name: 'events.testConnection', description: 'Test the configured provider connection for one Events environment.',
252
+ inputSchema: { product: 'string', env: 'string', broker: 'string' },
253
+ outputSchema: { connected: 'boolean', latency: 'number?', error: 'string?' },
254
+ requiredIdentifiers: ['product', 'env', 'broker'], ...runtimeRead,
255
+ idempotency: 'read-only connection probe', pagination: 'none',
256
+ mutatesState: false, mayIncurProviderCost: false,
257
+ },
258
+ ];
259
+ export function eventsCapabilityOverview() {
260
+ return {
261
+ capability: 'events',
262
+ aliases: ['messageBrokers', 'brokers', 'messaging'],
263
+ implementationWarning: EVENTS_IMPLEMENTATION_WARNING,
264
+ deliverySemantics: EVENTS_DELIVERY_SEMANTICS,
265
+ recoverySearchTerms: EVENTS_RECOVERY_SEARCH_TERMS,
266
+ documentationTopics: ['events', 'resilience'],
267
+ providerCapabilities: EVENTS_PROVIDER_CAPABILITIES,
268
+ operations: EVENTS_OPERATION_REGISTRY,
269
+ };
270
+ }
271
+ export function searchEventsCapabilities(query) {
272
+ const normalized = query.toLowerCase();
273
+ const recoveryMatch = EVENTS_RECOVERY_SEARCH_TERMS.some(term => normalized.includes(term));
274
+ const operations = EVENTS_OPERATION_REGISTRY.filter(operation => recoveryMatch
275
+ ? /deadletter|reprocess|replay|query|getstats|getdashboard/i.test(operation.name)
276
+ : `${operation.name} ${operation.description}`.toLowerCase().includes(normalized));
277
+ return { query, canonicalTopic: 'events', recoveryMatch, deliverySemantics: EVENTS_DELIVERY_SEMANTICS, operations };
278
+ }
279
+ export function inspectEventsComponent(component) {
280
+ const environments = (component?.envs || []).map((env) => ({
281
+ slug: env.slug ?? env.env_slug ?? null,
282
+ provider: env.type ?? component.type ?? null,
283
+ connection: env.cloud ?? env.instance ?? (env.config ? 'configured' : null),
284
+ retryPolicy: env.retryPolicy ?? component.retryPolicy ?? 'provider-default',
285
+ maximumDeliveryAttempts: env.maxDeliveryAttempts ?? component.maxDeliveryAttempts ?? 'provider-controlled-or-unverified',
286
+ acknowledgementDeadline: env.acknowledgementDeadline ?? component.acknowledgementDeadline ?? 'provider-controlled-or-unverified',
287
+ backoff: env.backoff ?? component.backoff ?? 'provider-controlled-or-unverified',
288
+ dlq: env.dlq ?? component.dlq ?? { configured: 'unverified', managedBy: 'ductape' },
289
+ retention: env.retention ?? component.retention ?? 'provider-controlled-or-unverified',
290
+ ordering: env.ordering ?? component.ordering ?? 'provider-controlled-or-unverified',
291
+ filters: env.filters ?? component.filters ?? 'none-configured-or-unverified',
292
+ providerCapabilities: EVENTS_PROVIDER_CAPABILITIES[env.type ?? component.type] ??
293
+ { status: 'unsupported-or-unknown-provider; inspect live provider configuration' },
294
+ }));
295
+ return {
296
+ component,
297
+ provider: component?.type ?? 'environment-specific',
298
+ environments,
299
+ topics: component?.topics ?? [],
300
+ subscriptions: component?.subscriptions ?? 'provider-controlled-or-unverified',
301
+ consumerBindings: component?.consumers ?? [],
302
+ messageStatistics: 'Call events.messages.getStats or events.messages.getDashboard with product, env, and brokerTag.',
303
+ availableOperationalActions: EVENTS_OPERATION_REGISTRY,
304
+ deliverySemantics: EVENTS_DELIVERY_SEMANTICS,
305
+ implementationWarning: EVENTS_IMPLEMENTATION_WARNING,
306
+ configurationWarnings: environments
307
+ .filter((env) => env.retryPolicy === 'provider-default' || env.dlq?.configured === 'unverified')
308
+ .map((env) => `${env.slug ?? 'unknown env'}: retry/DLQ configuration is not verified; provider defaults may apply.`),
309
+ documentationTopics: ['events', 'resilience'],
310
+ };
311
+ }
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { homedir } from 'os';
15
15
  import { delimiter, join } from 'path';
16
16
  import { z } from 'zod';
17
17
  import { executeViaProxy, generateExecutablePayload, getAssetSchemas, } from './proxy-client.js';
18
+ import { EVENTS_DELIVERY_SEMANTICS, EVENTS_IMPLEMENTATION_WARNING, eventsCapabilityOverview, inspectEventsComponent, searchEventsCapabilities, } from './events-capabilities.js';
18
19
  const MODULES = [
19
20
  'product', 'app', 'databases', 'graph', 'webhooks', 'notifications',
20
21
  'events', 'messageBrokers', 'storage', 'vector', 'caches', 'sessions', 'quotas',
@@ -556,6 +557,14 @@ ALL params are passed as a JSON array in positional order matching the SDK signa
556
557
  events.messages.getDeadLetters [{ product, env, brokerTag, topicTag?, consumerTag?, startDate?, endDate?, page?, limit? }]
557
558
  events.messages.getStats [{ product, env, brokerTag }]
558
559
  events.messages.getDashboard [{ product, env, brokerTag }]
560
+ events.getEvents [{ product, env, brokerTag, topic?, category?, status?, idempotent?, startDate?, endDate?, page?, limit? }]
561
+ events.getEvent [{ product, eventId }]
562
+ events.getEventStats [{ product, env, brokerTag }]
563
+ events.replayEvent [{ product, env, eventId, force? }]
564
+ events.reprocessDLQ [{ product, env, brokerTag, topicTag?, messageIds?, limit? }]
565
+ events.publishIdempotent [{ product, env, event, message, idempotencyKey, idempotencyTtl? }]
566
+ events.checkIdempotency [{ product, env, idempotencyKey }]
567
+ events.testConnection [{ product, env, broker }]
559
568
 
560
569
  ━━━ MODULE: storage ━━━
561
570
  storage.create [{ product: string, tag: string, name: string, description?: string, envs: [{ slug: string, type: "aws"|"azure"|"gcp", config: { bucket?: string, region?: string, accessKeyId?: string, secretAccessKey?: string, containerName?: string, connectionString?: string, projectId?: string, keyFilename?: string } }] }]
@@ -981,7 +990,8 @@ const snippetGenerateInputSchema = payloadGenerateInputSchema.extend({
981
990
  language: z.enum(['typescript', 'python']).default('typescript'),
982
991
  });
983
992
  const schemaInputSchema = z.object({
984
- module: z.enum(['app', 'product']).optional().describe('Optional. Scope the result to a single module ("app" or "product"). Omit to get the full manifest including enums.'),
993
+ module: z.enum(['app', 'product']).optional().describe('Optional. Scope the result to one module. With no method, returns only the compact list of available methods.'),
994
+ method: z.string().optional().describe('Optional method key such as "databases.create" or "notifications.update". Use with module to return only that method schema.'),
985
995
  });
986
996
  function toPrettyJson(value) {
987
997
  return JSON.stringify(value ?? {}, null, 2);
@@ -1341,6 +1351,11 @@ const docsInputSchema = z.object({
1341
1351
  'graphs, storage, cloud, vector, warehouse, secrets, apps, products, sessions, caches, ' +
1342
1352
  'notifications, resilience, features, events, logs, migration, frontend, frontend-analytics, client, react, vue'),
1343
1353
  });
1354
+ const eventsDiscoveryInputSchema = z.object({
1355
+ query: z.string().optional().describe('Capability or recovery search, including DLQ, dead letter, failed messages, retry, poison message, replay, or consumer failure.'),
1356
+ product: z.string().optional().describe('Product tag used for live component inspection.'),
1357
+ component: z.string().optional().describe('Events component/broker tag used for live inspection.'),
1358
+ });
1344
1359
  const migrationInputSchema = z.object({
1345
1360
  source: z.string().describe('Absolute path to the existing codebase.'),
1346
1361
  e2e_baseline: z.string().describe('Absolute path to a passing migration-e2e baseline manifest created before migration inspection.'),
@@ -2806,6 +2821,13 @@ DUCTAPE RESILIENCE
2806
2821
  Resilience covers three mechanisms: quotas (rate-limited provider pools), fallbacks (automatic
2807
2822
  provider switching), and healthchecks (continuous probe monitoring with failure actions).
2808
2823
 
2824
+ EVENTS DELIVERY BOUNDARY
2825
+ Events transport retries, exhausted-delivery DLQs, replay, and reprocessing are Ductape
2826
+ responsibilities. Applications own transactional outboxes at the database-to-publication
2827
+ boundary, domain rejection records, and idempotent/replay-safe consumer mutations. Never advise
2828
+ a parallel application transport DLQ. See ductape_docs({ topic: "events" }) and
2829
+ ductape_events_discover({ query: "consumer failure" }).
2830
+
2809
2831
  CONFIGURATION BOUNDARY
2810
2832
  Quotas, fallbacks, and health checks are administrative product configuration. Manage them with
2811
2833
  the authenticated CLI:
@@ -3470,6 +3492,19 @@ When you call features.define({ handler }), the handler runs TWICE:
3470
3492
  events: `
3471
3493
  DUCTAPE EVENTS (MESSAGE BROKERS)
3472
3494
 
3495
+ RELIABILITY OWNERSHIP — DO NOT DUPLICATE THE TRANSPORT DLQ
3496
+ Ductape owns broker delivery retries, exhausted-delivery dead-letter handling, replay,
3497
+ reprocessing, and message observability. Do not build a parallel MongoDB/application transport
3498
+ DLQ. The application may use a transactional outbox only to bridge its canonical database commit
3499
+ to Ductape publication. Domain-invalid or rejected commands remain application records and are
3500
+ not transport dead letters. Every consumer mutation must be idempotent and replay-safe.
3501
+ Recovery operations: events.messages.getDeadLetters, events.reprocessDLQ, events.replayEvent,
3502
+ events.messages.query, events.messages.getStats, and events.messages.getDashboard.
3503
+ Structured contracts and live component inspection:
3504
+ ductape_events_discover({ query: "DLQ" })
3505
+ ductape_events_discover({ product: "<product-tag>", component: "<events-tag>" })
3506
+ Resilience boundary reference: ductape_docs({ topic: "resilience" }).
3507
+
3473
3508
  ARCHITECTURE — always two separate steps:
3474
3509
  Step 1: Register the BROKER COMPONENT (establishes the connection to the broker service).
3475
3510
  The broker's envs[] holds connection credentials and host/project info, NOT topics.
@@ -3774,6 +3809,9 @@ Import (register an EXISTING cloud resource):
3774
3809
  events.messages.getDeadLetters [{ product, env, brokerTag, topicTag?, consumerTag?, limit? }]
3775
3810
  events.messages.getProducers [{ product, env, brokerTag, topicTag?, page?, limit? }]
3776
3811
  events.messages.getConsumers [{ product, env, brokerTag, topicTag?, page?, limit? }]
3812
+ events.getEvents [{ product, env, brokerTag, topic?, category?, status?, idempotent?, startDate?, endDate?, page?, limit? }]
3813
+ events.getEvent [{ product, eventId }]
3814
+ events.getEventStats [{ product, env, brokerTag }]
3777
3815
  events.replayEvent [{ product, env, eventId, force? }]
3778
3816
  events.reprocessDLQ [{ product, env, brokerTag, topicTag?, messageIds?, limit? }]
3779
3817
  events.checkIdempotency [{ product, env, brokerTag, idempotency_key }]
@@ -4370,7 +4408,9 @@ DEPRECATED ALIASES
4370
4408
  `.trim(),
4371
4409
  };
4372
4410
  const docsHandler = async (args) => {
4373
- const key = args.topic.toLowerCase().trim();
4411
+ const requested = args.topic.toLowerCase().trim();
4412
+ const recoveryAliases = /^(dlq|dead[- ]?letters?|failed messages?|retry|retries|poison messages?|replay|reprocess|consumer failures?)$/;
4413
+ const key = recoveryAliases.test(requested) ? 'events' : requested;
4374
4414
  const doc = DOCS[key];
4375
4415
  if (!doc) {
4376
4416
  const available = Object.keys(DOCS).join(', ');
@@ -4378,7 +4418,16 @@ const docsHandler = async (args) => {
4378
4418
  content: [{ type: 'text', text: `Unknown topic "${args.topic}". Available topics: ${available}` }],
4379
4419
  };
4380
4420
  }
4381
- return { content: [{ type: 'text', text: doc }] };
4421
+ const structured = key === 'events'
4422
+ ? `\n\nSTRUCTURED EVENTS CAPABILITY INDEX\n${JSON.stringify(eventsCapabilityOverview(), null, 2)}`
4423
+ : key === 'resilience'
4424
+ ? `\n\nEVENTS DELIVERY RESPONSIBILITY BOUNDARY\n${JSON.stringify({
4425
+ implementationWarning: EVENTS_IMPLEMENTATION_WARNING,
4426
+ deliverySemantics: EVENTS_DELIVERY_SEMANTICS,
4427
+ canonicalDocumentationTopic: 'events',
4428
+ }, null, 2)}`
4429
+ : '';
4430
+ return { content: [{ type: 'text', text: doc + structured }] };
4382
4431
  };
4383
4432
  const cliInputSchema = z.object({
4384
4433
  command: z.string().describe('The ductape CLI command to run, without the leading "ductape" word. ' +
@@ -4527,6 +4576,74 @@ async function main() {
4527
4576
  ...(result.success ? {} : { isError: true }),
4528
4577
  };
4529
4578
  };
4579
+ const eventsDiscoveryHandler = async (args) => {
4580
+ try {
4581
+ if (args.product || args.component) {
4582
+ if (!args.product || !args.component) {
4583
+ throw new Error('Both product and component are required for live Events inspection.');
4584
+ }
4585
+ const result = await cliHandler({
4586
+ command: `products components get --product-tag ${shellArgument(args.product)} --type events --json`,
4587
+ });
4588
+ if (result.isError)
4589
+ return result;
4590
+ const raw = result.content?.[0]?.text ?? '';
4591
+ let parsed;
4592
+ try {
4593
+ parsed = JSON.parse(raw);
4594
+ }
4595
+ catch {
4596
+ throw new Error(`Events inspection did not return JSON. ${raw}`);
4597
+ }
4598
+ const candidates = Array.isArray(parsed)
4599
+ ? parsed
4600
+ : Array.isArray(parsed?.events)
4601
+ ? parsed.events
4602
+ : Array.isArray(parsed?.messageBrokers)
4603
+ ? parsed.messageBrokers
4604
+ : [parsed];
4605
+ const component = candidates.find((item) => item?.tag === args.component || item?._id === args.component);
4606
+ if (!component) {
4607
+ throw new Error(`Events component "${args.component}" was not found in product "${args.product}". ` +
4608
+ 'Use the exact component tag or ID returned by products components get --type events.');
4609
+ }
4610
+ const inspection = inspectEventsComponent(component);
4611
+ const key = process.env.DUCTAPE_PUBLISHABLE_KEY;
4612
+ if (key) {
4613
+ inspection.messageStatistics = {};
4614
+ for (const environment of inspection.environments) {
4615
+ if (!environment.slug)
4616
+ continue;
4617
+ try {
4618
+ inspection.messageStatistics[environment.slug] = await executeViaProxy(key, 'events', 'messages.getStats', [{ product: args.product, env: environment.slug, brokerTag: component.tag }]);
4619
+ }
4620
+ catch (error) {
4621
+ inspection.messageStatistics[environment.slug] = {
4622
+ status: 'unavailable',
4623
+ error: error instanceof Error ? error.message : String(error),
4624
+ remediation: 'Verify publishable-key permission, environment configuration, provider readiness, and the exact broker tag.',
4625
+ };
4626
+ }
4627
+ }
4628
+ }
4629
+ return {
4630
+ content: [{ type: 'text', text: JSON.stringify(inspection, null, 2) }],
4631
+ };
4632
+ }
4633
+ const data = args.query ? searchEventsCapabilities(args.query) : eventsCapabilityOverview();
4634
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
4635
+ }
4636
+ catch (err) {
4637
+ const message = err instanceof Error ? err.message : String(err);
4638
+ return {
4639
+ content: [{
4640
+ type: 'text',
4641
+ text: `Error: ${message}\nRemediation: verify product/component identifiers, CLI authentication, workspace selection, and environment/provider configuration.`,
4642
+ }],
4643
+ isError: true,
4644
+ };
4645
+ }
4646
+ };
4530
4647
  const migrationHandler = async (args) => {
4531
4648
  if (args.mode === 'new-codebase' && !args.destination) {
4532
4649
  return {
@@ -4683,8 +4800,58 @@ async function main() {
4683
4800
  };
4684
4801
  const schemaHandler = async (args) => {
4685
4802
  try {
4686
- const data = await getAssetSchemas(args.module);
4687
- return { content: [{ type: 'text', text: JSON.stringify(data ?? null, null, 2) }] };
4803
+ if (args.method && !args.module) {
4804
+ throw new Error('module is required when method is provided');
4805
+ }
4806
+ const requestedMethod = args.method;
4807
+ const schemaMethod = requestedMethod === 'events.create'
4808
+ ? 'messageBrokers.create'
4809
+ : requestedMethod === 'events.update'
4810
+ ? 'messageBrokers.update'
4811
+ : requestedMethod;
4812
+ const data = await getAssetSchemas(args.module, schemaMethod);
4813
+ const compactData = args.module &&
4814
+ !args.method &&
4815
+ data &&
4816
+ typeof data === 'object' &&
4817
+ !Array.isArray(data.methods) &&
4818
+ data.methods
4819
+ ? {
4820
+ module: args.module,
4821
+ methods: Object.keys(data.methods),
4822
+ hint: `Call ductape_schema with module="${args.module}" and one method for its complete field schema`,
4823
+ }
4824
+ : data;
4825
+ const isEventsSchema = args.module === 'product' &&
4826
+ Boolean(requestedMethod && /^(events|messageBrokers)\.(create|update)$/.test(requestedMethod));
4827
+ const enrichedData = isEventsSchema
4828
+ ? {
4829
+ ...compactData,
4830
+ method: requestedMethod,
4831
+ eventsCapability: {
4832
+ implementationWarning: EVENTS_IMPLEMENTATION_WARNING,
4833
+ deliverySemantics: EVENTS_DELIVERY_SEMANTICS,
4834
+ operations: eventsCapabilityOverview().operations,
4835
+ configurationValidation: {
4836
+ retryOrDlqUnspecified: 'Warn that provider defaults apply and inspect the deployed provider asset before claiming DLQ availability.',
4837
+ unsupportedFields: 'The live Joi schema rejects provider settings not supported by the selected env type; use the reported field path and provider matrix for remediation.',
4838
+ costSensitive: 'Azure Service Bus tier must be confirmed; provider operations may incur usage charges.',
4839
+ },
4840
+ documentationTopics: ['events', 'resilience'],
4841
+ },
4842
+ }
4843
+ : args.module === 'product' && !requestedMethod
4844
+ ? {
4845
+ ...compactData,
4846
+ eventsOwnership: {
4847
+ implementationWarning: EVENTS_IMPLEMENTATION_WARNING,
4848
+ deliverySemantics: EVENTS_DELIVERY_SEMANTICS,
4849
+ schemaMethods: ['events.create', 'events.update', 'messageBrokers.create', 'messageBrokers.update'],
4850
+ discoveryTool: 'ductape_events_discover',
4851
+ },
4852
+ }
4853
+ : compactData;
4854
+ return { content: [{ type: 'text', text: JSON.stringify(enrichedData ?? null, null, 2) }] };
4688
4855
  }
4689
4856
  catch (err) {
4690
4857
  const message = err instanceof Error ? err.message : String(err);
@@ -4722,10 +4889,10 @@ async function main() {
4722
4889
  }, snippetGenerateHandler);
4723
4890
  server.registerTool('ductape_schema', {
4724
4891
  title: 'Ductape Asset Schema',
4725
- description: 'Returns the full field manifest for Ductape asset creation/update methods, ' +
4726
- 'derived live from the SDK Joi validators. Includes field types, required flags, ' +
4727
- 'enum values, nested structures, and all enum constants. ' +
4728
- 'Pass module="app" or module="product" to scope the result.\n\n' +
4892
+ description: 'Returns a compact method index or one targeted asset schema derived from the SDK Joi validators. ' +
4893
+ 'Call with module="app" or module="product" first to list method keys, then call again with ' +
4894
+ 'module and method (for example method="databases.create") for the complete field schema. ' +
4895
+ 'Avoid calling without module unless you explicitly need the entire manifest.\n\n' +
4729
4896
  'ALWAYS call this before constructing a file for "resources <type> create" or any cloud ' +
4730
4897
  'import/provision operation — field shapes are not guessable from context.\n\n' +
4731
4898
  'Conditional fields: some fields are returned as oneOf (an array of variant shapes). ' +
@@ -4746,6 +4913,14 @@ async function main() {
4746
4913
  'notifications, resilience, features, events, logs, migration, frontend, frontend-analytics, client, react, vue',
4747
4914
  inputSchema: docsInputSchema,
4748
4915
  }, docsHandler);
4916
+ server.registerTool('ductape_events_discover', {
4917
+ title: 'Ductape Events Capability Discovery',
4918
+ description: 'Discover Ductape Events delivery ownership, retries, dead-letter queues, replay/reprocessing, observability operations, ' +
4919
+ 'live input/output contracts, and provider-aware component configuration. Search recovery synonyms with query, or pass ' +
4920
+ 'product and component together for live inspection. Ductape owns transport retries and exhausted-delivery DLQs; ' +
4921
+ 'applications own transactional outboxes, domain rejection handling, and idempotent consumer mutations.',
4922
+ inputSchema: eventsDiscoveryInputSchema,
4923
+ }, eventsDiscoveryHandler);
4749
4924
  server.registerTool('ductape_migration_plan', {
4750
4925
  title: 'Ductape AI Migration Guidance',
4751
4926
  description: 'Inspect a TypeScript, Go, Java, or .NET repository without exposing secret values. ' +
@@ -4870,6 +5045,7 @@ async function main() {
4870
5045
  server.tool('ductape_generate_snippet', snippetGenerateInputSchema.shape, snippetGenerateHandler);
4871
5046
  server.tool('ductape_schema', schemaInputSchema.shape, schemaHandler);
4872
5047
  server.tool('ductape_docs', docsInputSchema.shape, docsHandler);
5048
+ server.tool('ductape_events_discover', eventsDiscoveryInputSchema.shape, eventsDiscoveryHandler);
4873
5049
  server.tool('ductape_migration_plan', migrationInputSchema.shape, migrationHandler);
4874
5050
  server.tool('ductape_cli', cliInputSchema.shape, cliHandler);
4875
5051
  }
@@ -24,6 +24,6 @@ export interface IGenerateExecutablePayloadResponse {
24
24
  payload: Record<string, unknown>;
25
25
  meta: Record<string, unknown>;
26
26
  }
27
- export declare function getAssetSchemas(module?: string): Promise<unknown>;
27
+ export declare function getAssetSchemas(module?: string, method?: string): Promise<unknown>;
28
28
  export declare function generateExecutablePayload<T = IGenerateExecutablePayloadResponse>(request: IGenerateExecutablePayloadRequest): Promise<T>;
29
29
  //# sourceMappingURL=proxy-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"proxy-client.d.ts","sourceRoot":"","sources":["../src/proxy-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,YAAY,4BAA4B,CAAC;AAEtD,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,KAAK,GACL,WAAW,GACX,OAAO,GACP,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,UAAU,GACV,MAAM,GACN,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,SAAS,CAAC;AAUd;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,CAAC,CAAC,CAuBZ;AAED,MAAM,WAAW,iCAAiC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACpD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AASD,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAYvE;AA4BD,wBAAsB,yBAAyB,CAAC,CAAC,GAAG,kCAAkC,EACpF,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,CAAC,CAAC,CAiCZ"}
1
+ {"version":3,"file":"proxy-client.d.ts","sourceRoot":"","sources":["../src/proxy-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,YAAY,4BAA4B,CAAC;AAEtD,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,KAAK,GACL,WAAW,GACX,OAAO,GACP,UAAU,GACV,eAAe,GACf,gBAAgB,GAChB,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,SAAS,GACT,UAAU,GACV,MAAM,GACN,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,UAAU,GACV,SAAS,CAAC;AAUd;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,GAAG,OAAO,EAC/C,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,CAAC,CAAC,CAuBZ;AAED,MAAM,WAAW,iCAAiC;IAChD,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACpD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,QAAQ,GAAG,aAAa,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AASD,wBAAsB,eAAe,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAuBxF;AA4BD,wBAAsB,yBAAyB,CAAC,CAAC,GAAG,kCAAkC,EACpF,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,CAAC,CAAC,CAiCZ"}
@@ -28,11 +28,22 @@ export async function executeViaProxy(publishable_key, module, method, params =
28
28
  }
29
29
  return body.data?.data;
30
30
  }
31
- export async function getAssetSchemas(module) {
32
- const path = module ? `/proxy/v1/schema/${encodeURIComponent(module)}` : '/proxy/v1/schema';
31
+ export async function getAssetSchemas(module, method) {
32
+ const path = module
33
+ ? `/proxy/v1/schema/${encodeURIComponent(module)}${method ? `/${encodeURIComponent(method)}` : ''}`
34
+ : '/proxy/v1/schema';
33
35
  const url = `${API_BASE_URL.replace(/\/$/, '')}${path}`;
34
36
  const res = await fetch(url);
35
37
  const body = (await res.json());
38
+ if (res.status === 404 && module && method) {
39
+ const fallbackUrl = `${API_BASE_URL.replace(/\/$/, '')}/proxy/v1/schema/${encodeURIComponent(module)}`;
40
+ const fallbackRes = await fetch(fallbackUrl);
41
+ const fallbackBody = (await fallbackRes.json());
42
+ const methodSchema = fallbackBody.data?.methods?.[method];
43
+ if (fallbackRes.ok && methodSchema) {
44
+ return { module, method, schema: methodSchema };
45
+ }
46
+ }
36
47
  if (!res.ok) {
37
48
  throw new Error(body.message ?? `Schema request failed: ${res.status}`);
38
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ductape/mcp",
3
- "version": "0.2.4",
3
+ "version": "0.2.7",
4
4
  "description": "MCP server that exposes Ductape SDK operations via the backend proxy",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  ],
16
16
  "scripts": {
17
17
  "build": "tsc",
18
- "test": "node scripts/check-frontend-analytics-guidance.mjs",
18
+ "test": "npm run build && node scripts/check-frontend-analytics-guidance.mjs && node scripts/check-events-discovery.mjs",
19
19
  "start": "node dist/index.js",
20
20
  "dev": "tsx src/index.ts"
21
21
  },