Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 277 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
# SEMOSS MCP Tool Development

Comprehensive guide for building SEMOSS MCP tools with custom UIs.

---

## Quick Reference

### Architecture
- **Key modules**: `client` (React/Vite UI), `portals` (published UI), `mcp` (generated manifests), `py` (Python MCP tools)
- **Flow**: UI in `client` builds to `portals` and publishes via SEMOSS UI. UI calls MCP tools via SDK and returns data to the client
- **Frontend**: shadcn/ui + Radix + Tailwind, contexts in `client/src/contexts`, routes in `client/src/pages`, keep components keyboard accessible
- **Integrations**: SEMOSS SDK (`@semoss/sdk`) and SEMOSS runtime

### Development Workflow
1. **Setup**: Install Node + pnpm and Python
2. **Common commands**:
- `pnpm i` (root and `client`)
- `pnpm dev` (development server)
- `pnpm build` (production build)
3. **Environment**: Replace the app id in `client/.env` with:
```
APP="your-app-id"
```
4. **Publish**: After changes, build and publish via SEMOSS UI; if `portals/` is missing, run `pnpm i` and `pnpm build` at client folder root

### MCP Development Basics
- **Manifests**: `mcp/py_mcp.json` (Python) is generated by reactors. Do not edit manually
- **Tools location**: Add tools to `py/mcp_driver.py`. Only expose end tools; move helpers to separate modules
- **Docstrings**: Add concise docstrings that describe behavior, inputs, and outputs
- **Metadata**: Every tool must use `@mcp_metadata` from `smssutil.py`
- **ROOT**: Injected into `mcp_driver.py`; propagate it to dependencies
- **smssutil**: The `smssutil` module (containing `@mcp_metadata`) is automatically injected by SEMOSS, similar to `ROOT`. You don't need to create it
- **Generation**: Generate `mcp/py_mcp.json` via the SEMOSS reactor (see `MakePythonMCPReactor`)

---

## Python MCP Tools

### Tool Structure
```python
@mcp_metadata({
'loadingMessage': 'Processing...',
'resourceURI': None,
'execution': 'ask', # 'auto', 'ask', or 'disabled'
'displayLocation': 'inline' # 'inline', 'sidebar', or 'hidden'
})
def your_tool_name(param: str, model_id: str) -> str:
"""
Brief description of what the tool does.

Args:
param: Description of parameter
model_id: The ID of the AI model to use

Returns:
JSON string with results
"""
try:
# Your logic here
result = {'success': True, 'data': your_data}
return json.dumps(result)
except Exception as e:
import traceback
return json.dumps({
'success': False,
'error': str(e),
'traceback': traceback.format_exc()
})
```

### Using Model Engines
Always require `model_id` as a parameter:

```python
from ai_server import ModelEngine

def your_tool(content: str, model_id: str) -> str:
model = ModelEngine(engine_id=model_id)
response = model.ask(command=prompt, param_dict={
"temperature": 0.7,
"max_completion_tokens": 2000
})
# Parse and return response
```

### File Operations
Saving to insights folder:

```python
from datetime import datetime
from pathlib import Path
import os

# ROOT is injected by SEMOSS
file_path = os.path.join(Path(ROOT), filename)

with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
```

---

## React UI Development

### SDK Hooks
- **`useInsight()`**: Provides `actions`, `isReady`, `tool`, etc.
- **`useAppContext()`**: Custom context with `runPixel`, `sendMCPResponseToPlayground`

```typescript
import { useInsight } from "@semoss/sdk/react";
import { useAppContext } from "./contexts/AppContext";

const { actions, isReady } = useInsight(); // For MCP tools
const { runPixel } = useAppContext(); // For Pixel commands
```

### Model Selection Pattern
Fetching available models:

```typescript
interface Engine {
app_id: string;
app_name: string;
}

// Text generation models
const models = await runPixel<Engine[]>(
`MyEngines( metaKeys = [], metaFilters = [{ "tag" : "text-generation" }], engineTypes = [ 'MODEL' ])`
);

### Calling MCP Tools
```typescript
const { output } = await actions.runMCPTool("tool_name", {
param1: value1,
model_id: selectedModelId,
});
```

### Response Parsing
MCP tool responses can be encoded multiple ways. Always handle all cases:

```typescript
let data: YourResultType;

if (typeof output === "string") {
try {
const parsed = JSON.parse(output);
// Check if double-encoded
if (typeof parsed === "string") {
data = JSON.parse(parsed) as YourResultType;
} else {
data = parsed as YourResultType;
}
} catch {
throw new Error("Failed to parse response");
}
} else {
// Already an object
data = output as YourResultType;
}
```

---

## Common Gotchas

### 1. Response Parsing
❌ **Wrong**: Assuming output is always a string
✅ **Right**: Handle object, string, and double-encoded cases

### 2. MCP Metadata
❌ **Wrong**: Forgetting `@mcp_metadata` decorator
✅ **Right**: Every MCP tool must have `@mcp_metadata()`

### 3. Parameter Passing
❌ **Wrong**: Using kwargs
✅ **Right**: Pass the parameter name and type - they will be interpretted as required parameters

### 4. MCP Execution
- UI must target a specific tool name (from `mcp_driver.py`)
- Execute tools with `actions.runMCPTool(name, params)`
- Forward results with `actions.sendMCPResponseToPlayground` when needed

---

## Best Practices

### Python
- Use type hints for all parameters and return values
- Wrap MCP tool logic in try-catch blocks
- Return structured JSON from all MCP tools
- Keep MCP tool execution under 30 seconds
- Use streaming for long operations
- Cache expensive operations when appropriate

### TypeScript
- Define interfaces for all data structures
- Handle all response encoding formats
- Validate user inputs before calling MCP tools
- Provide loading states during MCP execution
- Show meaningful error messages to users

### Security
- Never expose API keys in frontend code
- Use environment variables for sensitive data
- Validate all user inputs in Python
- Sanitize HTML content before rendering

### Testing Checklist
- [ ] Test with different models
- [ ] Test with empty/invalid inputs
- [ ] Test error handling
- [ ] Build without errors (`pnpm build`)
- [ ] Test in playground mode after publishing

---

## File Pointers

### Entry Points
- `portals/index.html` - Published app entry
- `client/src/index.tsx` - React entry point
- `client/src/App.tsx` - Main app component

### MCP Tools
- `py/mcp_driver.py` - Python MCP tool definitions
- `mcp/py_mcp.json` - Generated Python manifest

### Configuration
- `client/vite.config.ts` - Vite build config
- `client/tailwind.config.js` - Tailwind CSS config
- `biome.json` - Linting/formatting config

### Generated
- Avoid direct edits to: `portals/`, `classes/`, `target/`

---

## Do and Do Not

### Do
- Keep UI in `client`
- Put custom logic in `py/mcp_driver.py`
- Build then publish via SEMOSS UI
- Use type hints in Python
- Handle all response encoding formats
- Validate inputs before processing

### Do Not
- Edit built assets in `portals/`
- Manually edit `mcp/*.json`
- Commit secrets in `.env.local`
- Assume response format
- Skip error handling
- Forget `@mcp_metadata` decorator

---

## Resources

### Documentation
- [SEMOSS GitHub](https://github.com/SEMOSS/Semoss) (docs: https://github.com/SEMOSS/Semoss/tree/main/docs)
- [Monolith](https://github.com/SEMOSS/Monolith)
- [SEMOSS UI](https://github.com/SEMOSS/semoss-ui)

### SDK
- `@semoss/sdk` and `@semoss/sdk/react`
- Python SDK: `ModelEngine`, `DatabaseEngine`, `Insight` from SEMOSS modules

### Getting Help
When debugging:
1. Check browser console for frontend errors
2. Check SEMOSS logs for backend errors
3. Verify MCP manifest generated correctly
4. Test Python functions independently
5. Use network tab to inspect MCP tool responses