foundry-mcp 0.8.22__py3-none-any.whl

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.

Potentially problematic release.


This version of foundry-mcp might be problematic. Click here for more details.

Files changed (153) hide show
  1. foundry_mcp/__init__.py +13 -0
  2. foundry_mcp/cli/__init__.py +67 -0
  3. foundry_mcp/cli/__main__.py +9 -0
  4. foundry_mcp/cli/agent.py +96 -0
  5. foundry_mcp/cli/commands/__init__.py +37 -0
  6. foundry_mcp/cli/commands/cache.py +137 -0
  7. foundry_mcp/cli/commands/dashboard.py +148 -0
  8. foundry_mcp/cli/commands/dev.py +446 -0
  9. foundry_mcp/cli/commands/journal.py +377 -0
  10. foundry_mcp/cli/commands/lifecycle.py +274 -0
  11. foundry_mcp/cli/commands/modify.py +824 -0
  12. foundry_mcp/cli/commands/plan.py +640 -0
  13. foundry_mcp/cli/commands/pr.py +393 -0
  14. foundry_mcp/cli/commands/review.py +667 -0
  15. foundry_mcp/cli/commands/session.py +472 -0
  16. foundry_mcp/cli/commands/specs.py +686 -0
  17. foundry_mcp/cli/commands/tasks.py +807 -0
  18. foundry_mcp/cli/commands/testing.py +676 -0
  19. foundry_mcp/cli/commands/validate.py +982 -0
  20. foundry_mcp/cli/config.py +98 -0
  21. foundry_mcp/cli/context.py +298 -0
  22. foundry_mcp/cli/logging.py +212 -0
  23. foundry_mcp/cli/main.py +44 -0
  24. foundry_mcp/cli/output.py +122 -0
  25. foundry_mcp/cli/registry.py +110 -0
  26. foundry_mcp/cli/resilience.py +178 -0
  27. foundry_mcp/cli/transcript.py +217 -0
  28. foundry_mcp/config.py +1454 -0
  29. foundry_mcp/core/__init__.py +144 -0
  30. foundry_mcp/core/ai_consultation.py +1773 -0
  31. foundry_mcp/core/batch_operations.py +1202 -0
  32. foundry_mcp/core/cache.py +195 -0
  33. foundry_mcp/core/capabilities.py +446 -0
  34. foundry_mcp/core/concurrency.py +898 -0
  35. foundry_mcp/core/context.py +540 -0
  36. foundry_mcp/core/discovery.py +1603 -0
  37. foundry_mcp/core/error_collection.py +728 -0
  38. foundry_mcp/core/error_store.py +592 -0
  39. foundry_mcp/core/health.py +749 -0
  40. foundry_mcp/core/intake.py +933 -0
  41. foundry_mcp/core/journal.py +700 -0
  42. foundry_mcp/core/lifecycle.py +412 -0
  43. foundry_mcp/core/llm_config.py +1376 -0
  44. foundry_mcp/core/llm_patterns.py +510 -0
  45. foundry_mcp/core/llm_provider.py +1569 -0
  46. foundry_mcp/core/logging_config.py +374 -0
  47. foundry_mcp/core/metrics_persistence.py +584 -0
  48. foundry_mcp/core/metrics_registry.py +327 -0
  49. foundry_mcp/core/metrics_store.py +641 -0
  50. foundry_mcp/core/modifications.py +224 -0
  51. foundry_mcp/core/naming.py +146 -0
  52. foundry_mcp/core/observability.py +1216 -0
  53. foundry_mcp/core/otel.py +452 -0
  54. foundry_mcp/core/otel_stubs.py +264 -0
  55. foundry_mcp/core/pagination.py +255 -0
  56. foundry_mcp/core/progress.py +387 -0
  57. foundry_mcp/core/prometheus.py +564 -0
  58. foundry_mcp/core/prompts/__init__.py +464 -0
  59. foundry_mcp/core/prompts/fidelity_review.py +691 -0
  60. foundry_mcp/core/prompts/markdown_plan_review.py +515 -0
  61. foundry_mcp/core/prompts/plan_review.py +627 -0
  62. foundry_mcp/core/providers/__init__.py +237 -0
  63. foundry_mcp/core/providers/base.py +515 -0
  64. foundry_mcp/core/providers/claude.py +472 -0
  65. foundry_mcp/core/providers/codex.py +637 -0
  66. foundry_mcp/core/providers/cursor_agent.py +630 -0
  67. foundry_mcp/core/providers/detectors.py +515 -0
  68. foundry_mcp/core/providers/gemini.py +426 -0
  69. foundry_mcp/core/providers/opencode.py +718 -0
  70. foundry_mcp/core/providers/opencode_wrapper.js +308 -0
  71. foundry_mcp/core/providers/package-lock.json +24 -0
  72. foundry_mcp/core/providers/package.json +25 -0
  73. foundry_mcp/core/providers/registry.py +607 -0
  74. foundry_mcp/core/providers/test_provider.py +171 -0
  75. foundry_mcp/core/providers/validation.py +857 -0
  76. foundry_mcp/core/rate_limit.py +427 -0
  77. foundry_mcp/core/research/__init__.py +68 -0
  78. foundry_mcp/core/research/memory.py +528 -0
  79. foundry_mcp/core/research/models.py +1234 -0
  80. foundry_mcp/core/research/providers/__init__.py +40 -0
  81. foundry_mcp/core/research/providers/base.py +242 -0
  82. foundry_mcp/core/research/providers/google.py +507 -0
  83. foundry_mcp/core/research/providers/perplexity.py +442 -0
  84. foundry_mcp/core/research/providers/semantic_scholar.py +544 -0
  85. foundry_mcp/core/research/providers/tavily.py +383 -0
  86. foundry_mcp/core/research/workflows/__init__.py +25 -0
  87. foundry_mcp/core/research/workflows/base.py +298 -0
  88. foundry_mcp/core/research/workflows/chat.py +271 -0
  89. foundry_mcp/core/research/workflows/consensus.py +539 -0
  90. foundry_mcp/core/research/workflows/deep_research.py +4142 -0
  91. foundry_mcp/core/research/workflows/ideate.py +682 -0
  92. foundry_mcp/core/research/workflows/thinkdeep.py +405 -0
  93. foundry_mcp/core/resilience.py +600 -0
  94. foundry_mcp/core/responses.py +1624 -0
  95. foundry_mcp/core/review.py +366 -0
  96. foundry_mcp/core/security.py +438 -0
  97. foundry_mcp/core/spec.py +4119 -0
  98. foundry_mcp/core/task.py +2463 -0
  99. foundry_mcp/core/testing.py +839 -0
  100. foundry_mcp/core/validation.py +2357 -0
  101. foundry_mcp/dashboard/__init__.py +32 -0
  102. foundry_mcp/dashboard/app.py +119 -0
  103. foundry_mcp/dashboard/components/__init__.py +17 -0
  104. foundry_mcp/dashboard/components/cards.py +88 -0
  105. foundry_mcp/dashboard/components/charts.py +177 -0
  106. foundry_mcp/dashboard/components/filters.py +136 -0
  107. foundry_mcp/dashboard/components/tables.py +195 -0
  108. foundry_mcp/dashboard/data/__init__.py +11 -0
  109. foundry_mcp/dashboard/data/stores.py +433 -0
  110. foundry_mcp/dashboard/launcher.py +300 -0
  111. foundry_mcp/dashboard/views/__init__.py +12 -0
  112. foundry_mcp/dashboard/views/errors.py +217 -0
  113. foundry_mcp/dashboard/views/metrics.py +164 -0
  114. foundry_mcp/dashboard/views/overview.py +96 -0
  115. foundry_mcp/dashboard/views/providers.py +83 -0
  116. foundry_mcp/dashboard/views/sdd_workflow.py +255 -0
  117. foundry_mcp/dashboard/views/tool_usage.py +139 -0
  118. foundry_mcp/prompts/__init__.py +9 -0
  119. foundry_mcp/prompts/workflows.py +525 -0
  120. foundry_mcp/resources/__init__.py +9 -0
  121. foundry_mcp/resources/specs.py +591 -0
  122. foundry_mcp/schemas/__init__.py +38 -0
  123. foundry_mcp/schemas/intake-schema.json +89 -0
  124. foundry_mcp/schemas/sdd-spec-schema.json +414 -0
  125. foundry_mcp/server.py +150 -0
  126. foundry_mcp/tools/__init__.py +10 -0
  127. foundry_mcp/tools/unified/__init__.py +92 -0
  128. foundry_mcp/tools/unified/authoring.py +3620 -0
  129. foundry_mcp/tools/unified/context_helpers.py +98 -0
  130. foundry_mcp/tools/unified/documentation_helpers.py +268 -0
  131. foundry_mcp/tools/unified/environment.py +1341 -0
  132. foundry_mcp/tools/unified/error.py +479 -0
  133. foundry_mcp/tools/unified/health.py +225 -0
  134. foundry_mcp/tools/unified/journal.py +841 -0
  135. foundry_mcp/tools/unified/lifecycle.py +640 -0
  136. foundry_mcp/tools/unified/metrics.py +777 -0
  137. foundry_mcp/tools/unified/plan.py +876 -0
  138. foundry_mcp/tools/unified/pr.py +294 -0
  139. foundry_mcp/tools/unified/provider.py +589 -0
  140. foundry_mcp/tools/unified/research.py +1283 -0
  141. foundry_mcp/tools/unified/review.py +1042 -0
  142. foundry_mcp/tools/unified/review_helpers.py +314 -0
  143. foundry_mcp/tools/unified/router.py +102 -0
  144. foundry_mcp/tools/unified/server.py +565 -0
  145. foundry_mcp/tools/unified/spec.py +1283 -0
  146. foundry_mcp/tools/unified/task.py +3846 -0
  147. foundry_mcp/tools/unified/test.py +431 -0
  148. foundry_mcp/tools/unified/verification.py +520 -0
  149. foundry_mcp-0.8.22.dist-info/METADATA +344 -0
  150. foundry_mcp-0.8.22.dist-info/RECORD +153 -0
  151. foundry_mcp-0.8.22.dist-info/WHEEL +4 -0
  152. foundry_mcp-0.8.22.dist-info/entry_points.txt +3 -0
  153. foundry_mcp-0.8.22.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,308 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * OpenCode AI SDK Wrapper for Claude SDD Toolkit
5
+ *
6
+ * This wrapper bridges the Python provider with the Node.js OpenCode AI SDK.
7
+ * It receives prompts and configuration via stdin (to avoid CLI argument length limits)
8
+ * and outputs streaming responses as line-delimited JSON to stdout.
9
+ */
10
+
11
+ import { createOpencodeClient } from '@opencode-ai/sdk/client';
12
+ import { createInterface } from 'readline';
13
+
14
+ // Global client instance for graceful shutdown
15
+ let opcodeClient = null;
16
+
17
+ /**
18
+ * Parse command line arguments for simple flags
19
+ */
20
+ function parseArgs(args) {
21
+ const flags = {
22
+ help: args.includes('--help') || args.includes('-h'),
23
+ version: args.includes('--version') || args.includes('-v'),
24
+ test: args.includes('--test'),
25
+ };
26
+ return flags;
27
+ }
28
+
29
+ /**
30
+ * Display help message
31
+ */
32
+ function showHelp() {
33
+ console.log(`
34
+ OpenCode AI Wrapper
35
+
36
+ Usage: node opencode_wrapper.js < input.json
37
+
38
+ Input Format (JSON via stdin):
39
+ {
40
+ "prompt": "User prompt text",
41
+ "system_prompt": "System prompt (optional)",
42
+ "config": {
43
+ "model": "model-name",
44
+ "temperature": 0.7,
45
+ "max_tokens": 1000
46
+ },
47
+ "allowedTools": ["tool1", "tool2"] (optional)
48
+ }
49
+
50
+ Output Format (line-delimited JSON to stdout):
51
+ {"type": "chunk", "content": "token"}
52
+ {"type": "done", "response": {...}}
53
+ {"type": "error", "code": "category", "message": "details"}
54
+
55
+ Options:
56
+ -h, --help Show this help message
57
+ -v, --version Show version information
58
+ --test Run in test mode (returns mock responses without API key)
59
+ `);
60
+ }
61
+
62
+ /**
63
+ * Display version information
64
+ */
65
+ function showVersion() {
66
+ // Read version from package.json
67
+ import('./package.json', { with: { type: 'json' } })
68
+ .then(pkg => {
69
+ console.log(`OpenCode Wrapper v${pkg.default.version}`);
70
+ console.log(`@opencode-ai/sdk v${pkg.default.dependencies['@opencode-ai/sdk']}`);
71
+ })
72
+ .catch(() => {
73
+ console.log('OpenCode Wrapper v1.0.0');
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Cleanup function for graceful shutdown
79
+ */
80
+ async function cleanup() {
81
+ // OpenCode client doesn't need explicit cleanup
82
+ // Just null out the reference
83
+ opcodeClient = null;
84
+ }
85
+
86
+ /**
87
+ * Setup signal handlers for graceful shutdown
88
+ */
89
+ function setupSignalHandlers() {
90
+ process.on('SIGINT', async () => {
91
+ await cleanup();
92
+ process.exit(0);
93
+ });
94
+
95
+ process.on('SIGTERM', async () => {
96
+ await cleanup();
97
+ process.exit(0);
98
+ });
99
+ }
100
+
101
+ /**
102
+ * Read JSON payload from stdin
103
+ */
104
+ async function readStdin() {
105
+ return new Promise((resolve, reject) => {
106
+ let data = '';
107
+ const rl = createInterface({
108
+ input: process.stdin,
109
+ terminal: false
110
+ });
111
+
112
+ rl.on('line', (line) => {
113
+ data += line;
114
+ });
115
+
116
+ rl.on('close', () => {
117
+ try {
118
+ const payload = JSON.parse(data);
119
+ resolve(payload);
120
+ } catch (error) {
121
+ reject(new Error(`Invalid JSON input: ${error.message}`));
122
+ }
123
+ });
124
+
125
+ rl.on('error', (error) => {
126
+ reject(error);
127
+ });
128
+ });
129
+ }
130
+
131
+ /**
132
+ * Main execution function
133
+ */
134
+ async function main() {
135
+ // Setup signal handlers for graceful shutdown
136
+ setupSignalHandlers();
137
+
138
+ // Parse CLI arguments
139
+ const flags = parseArgs(process.argv.slice(2));
140
+
141
+ // Handle simple flags
142
+ if (flags.help) {
143
+ showHelp();
144
+ process.exit(0);
145
+ }
146
+
147
+ if (flags.version) {
148
+ showVersion();
149
+ process.exit(0);
150
+ }
151
+
152
+ // Read input from stdin
153
+ const payload = await readStdin();
154
+
155
+ // Validate required fields
156
+ if (!payload.prompt) {
157
+ throw new Error('Missing required field: prompt');
158
+ }
159
+
160
+ // Test mode - return mock response without requiring API key
161
+ if (flags.test) {
162
+ // Simulate streaming chunks
163
+ const mockChunks = ['Mock ', 'response ', 'from ', 'OpenCode ', 'test ', 'mode'];
164
+ for (const chunk of mockChunks) {
165
+ console.log(JSON.stringify({
166
+ type: 'chunk',
167
+ content: chunk
168
+ }));
169
+ }
170
+
171
+ // Return mock successful response
172
+ const mockResponse = {
173
+ type: 'done',
174
+ response: {
175
+ text: 'Mock response from OpenCode test mode',
176
+ usage: {
177
+ prompt_tokens: 10,
178
+ completion_tokens: 6,
179
+ total_tokens: 16
180
+ },
181
+ model: 'mock-model',
182
+ sessionId: 'test-session-123'
183
+ }
184
+ };
185
+ console.log(JSON.stringify(mockResponse));
186
+ process.exit(0);
187
+ }
188
+
189
+ // Get server configuration from environment variables
190
+ const serverUrl = process.env.OPENCODE_SERVER_URL || 'http://localhost:4096';
191
+ const apiKey = process.env.OPENCODE_API_KEY;
192
+
193
+ // Note: OPENCODE_API_KEY is only required for Zen models
194
+ // For non-Zen models (openai/*, anthropic/*, etc.), the SDK uses ~/.local/share/opencode/auth.json
195
+
196
+ // Validate configuration object
197
+ if (payload.config && typeof payload.config !== 'object') {
198
+ throw new Error('config must be an object');
199
+ }
200
+
201
+ // Create OpenCode client that connects to server
202
+ // Python provider ensures server is running via _ensure_server_running()
203
+ opcodeClient = createOpencodeClient({
204
+ baseUrl: serverUrl
205
+ });
206
+
207
+ // Parse model specification
208
+ const modelConfig = payload.config?.model || 'default-model';
209
+ let providerID, modelID;
210
+
211
+ // Parse model format: "provider/model" or just "model"
212
+ if (typeof modelConfig === 'string' && modelConfig.includes('/')) {
213
+ [providerID, modelID] = modelConfig.split('/', 2);
214
+ } else if (typeof modelConfig === 'object') {
215
+ providerID = modelConfig.providerID;
216
+ modelID = modelConfig.modelID;
217
+ } else {
218
+ providerID = 'opencode';
219
+ modelID = modelConfig;
220
+ }
221
+
222
+ // Create session
223
+ const session = await opcodeClient.session.create();
224
+
225
+ try {
226
+ // Build request body, filtering out null/undefined values
227
+ const requestBody = {
228
+ model: {
229
+ providerID: providerID,
230
+ modelID: modelID
231
+ },
232
+ parts: [
233
+ {
234
+ type: 'text',
235
+ text: payload.prompt
236
+ }
237
+ ]
238
+ };
239
+
240
+ // Only add system prompt if it's not null/undefined
241
+ if (payload.system_prompt != null) {
242
+ requestBody.system = payload.system_prompt;
243
+ }
244
+
245
+ // Execute prompt using the session API with correct structure
246
+ const response = await opcodeClient.session.prompt({
247
+ path: {
248
+ id: session.data.id
249
+ },
250
+ body: requestBody
251
+ });
252
+
253
+ // Extract response text from parts
254
+ const responseParts = response.data?.parts || [];
255
+ const textParts = responseParts
256
+ .filter(part => part.type === 'text')
257
+ .map(part => part.text)
258
+ .join('');
259
+
260
+ // Validate we got actual content - fail if empty
261
+ if (!textParts) {
262
+ const errorResponse = {
263
+ type: 'error',
264
+ code: 'EMPTY_RESPONSE',
265
+ message: `OpenCode server returned empty response. Is the server running on port ${serverUrl.split(':').pop()}? Raw response: ${JSON.stringify(response.data)}`
266
+ };
267
+ console.log(JSON.stringify(errorResponse));
268
+ process.exit(1);
269
+ }
270
+
271
+ // Emit response as line-delimited JSON
272
+ const finalResponse = {
273
+ type: 'done',
274
+ response: {
275
+ text: textParts,
276
+ usage: response.data?.usage || {},
277
+ model: `${providerID}/${modelID}`,
278
+ sessionId: session.data.id
279
+ }
280
+ };
281
+ console.log(JSON.stringify(finalResponse));
282
+
283
+ } catch (error) {
284
+ throw new Error(`Prompt execution failed: ${error.message}`);
285
+ }
286
+
287
+ // Cleanup before exit
288
+ await cleanup();
289
+
290
+ // Success - prompt executed and streamed
291
+ process.exit(0);
292
+ }
293
+
294
+ // Run main function if this is the entry point
295
+ if (import.meta.url === `file://${process.argv[1]}`) {
296
+ main().catch((error) => {
297
+ // Structured error output (required by spec)
298
+ const errorResponse = {
299
+ type: 'error',
300
+ code: 'WRAPPER_ERROR',
301
+ message: error.message
302
+ };
303
+ console.log(JSON.stringify(errorResponse));
304
+ process.exit(1);
305
+ });
306
+ }
307
+
308
+ export { readStdin, parseArgs };
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@foundry-mcp/opencode-provider",
3
+ "version": "0.1.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@foundry-mcp/opencode-provider",
9
+ "version": "0.1.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "@opencode-ai/sdk": "^1.0.218"
13
+ },
14
+ "engines": {
15
+ "node": ">=18.0.0"
16
+ }
17
+ },
18
+ "node_modules/@opencode-ai/sdk": {
19
+ "version": "1.0.218",
20
+ "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.0.218.tgz",
21
+ "integrity": "sha512-c6ss6UPAMskSVQUecuhNvPLFngyVh2Os9o0kpVjoqJJ16HXhzjVSk5axgh3ueQrfP5aZfg5o6l6srmjuCTPNnQ=="
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "@foundry-mcp/opencode-provider",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode AI provider integration for Foundry MCP",
5
+ "type": "module",
6
+ "main": "opencode_wrapper.js",
7
+ "scripts": {
8
+ "test": "node opencode_wrapper.js --test < test_input.json"
9
+ },
10
+ "keywords": [
11
+ "opencode",
12
+ "ai",
13
+ "provider",
14
+ "foundry",
15
+ "mcp"
16
+ ],
17
+ "author": "Foundry MCP",
18
+ "license": "MIT",
19
+ "dependencies": {
20
+ "@opencode-ai/sdk": "^1.0.218"
21
+ },
22
+ "engines": {
23
+ "node": ">=18.0.0"
24
+ }
25
+ }