tarvacode-agent-selector 1.0.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.
- package/INSTRUCTIONS.md +95 -0
- package/README.md +163 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +122 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/embeddings.d.ts +9 -0
- package/dist/lib/embeddings.d.ts.map +1 -0
- package/dist/lib/embeddings.js +72 -0
- package/dist/lib/embeddings.js.map +1 -0
- package/dist/lib/supabase.d.ts +26 -0
- package/dist/lib/supabase.d.ts.map +1 -0
- package/dist/lib/supabase.js +54 -0
- package/dist/lib/supabase.js.map +1 -0
- package/dist/tools/getAgentSkills.d.ts +14 -0
- package/dist/tools/getAgentSkills.d.ts.map +1 -0
- package/dist/tools/getAgentSkills.js +17 -0
- package/dist/tools/getAgentSkills.js.map +1 -0
- package/dist/tools/listAgents.d.ts +10 -0
- package/dist/tools/listAgents.d.ts.map +1 -0
- package/dist/tools/listAgents.js +12 -0
- package/dist/tools/listAgents.js.map +1 -0
- package/dist/tools/selectAgent.d.ts +17 -0
- package/dist/tools/selectAgent.d.ts.map +1 -0
- package/dist/tools/selectAgent.js +24 -0
- package/dist/tools/selectAgent.js.map +1 -0
- package/package.json +36 -0
- package/src/index.ts +149 -0
- package/src/lib/embeddings.ts +99 -0
- package/src/lib/supabase.ts +89 -0
- package/src/tools/getAgentSkills.ts +32 -0
- package/src/tools/listAgents.ts +18 -0
- package/src/tools/selectAgent.ts +39 -0
- package/tsconfig.json +19 -0
package/INSTRUCTIONS.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# MCP Server Instructions
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This MCP server enables Claude Code to select the optimal specialized agent for any given task using semantic similarity matching against a database of agent and skill embeddings.
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
Use this server when:
|
|
10
|
+
- Processing complex, multi-domain tasks that require specialist agents
|
|
11
|
+
- The task description is ambiguous and you need to determine the best expert
|
|
12
|
+
- Building automated workflows that route work to appropriate agents
|
|
13
|
+
- The `find-best-agent` micro-agent needs to decompose and route PRD tasks
|
|
14
|
+
|
|
15
|
+
## Tool Usage Guide
|
|
16
|
+
|
|
17
|
+
### select_best_agent
|
|
18
|
+
|
|
19
|
+
**Primary tool for agent selection.**
|
|
20
|
+
|
|
21
|
+
Call this with a clear task description. The tool will:
|
|
22
|
+
1. Generate a 1536-dimensional embedding using OpenAI
|
|
23
|
+
2. Query Supabase for the best matching agent
|
|
24
|
+
3. Return only the agent name
|
|
25
|
+
|
|
26
|
+
**Best practices:**
|
|
27
|
+
- Provide descriptive task text (50-500 characters works well)
|
|
28
|
+
- Include domain-specific keywords for better matching
|
|
29
|
+
- Use `match_threshold` of 0.5 (default) for balanced precision/recall
|
|
30
|
+
- Lower threshold (0.3-0.4) for exploratory matching
|
|
31
|
+
- Higher threshold (0.6-0.7) for high-confidence matching only
|
|
32
|
+
|
|
33
|
+
**Example calls:**
|
|
34
|
+
```
|
|
35
|
+
// Database work
|
|
36
|
+
select_best_agent({ task: "Design a normalized PostgreSQL schema for user authentication with OAuth2 support" })
|
|
37
|
+
// → database-architect
|
|
38
|
+
|
|
39
|
+
// Frontend work
|
|
40
|
+
select_best_agent({ task: "Build a responsive React dashboard with data tables and charts" })
|
|
41
|
+
// → react-developer
|
|
42
|
+
|
|
43
|
+
// Architecture work
|
|
44
|
+
select_best_agent({ task: "Plan the microservices architecture for a real-time collaboration platform" })
|
|
45
|
+
// → chief-technology-architect
|
|
46
|
+
|
|
47
|
+
// Patent work
|
|
48
|
+
select_best_agent({ task: "Draft claims for a novel machine learning inference optimization method" })
|
|
49
|
+
// → patent-attorney
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### list_agents
|
|
53
|
+
|
|
54
|
+
**Discovery tool for available agents.**
|
|
55
|
+
|
|
56
|
+
Use when you need to:
|
|
57
|
+
- See all available agents before making selection decisions
|
|
58
|
+
- Validate that an agent exists before routing work
|
|
59
|
+
- Present options to the user
|
|
60
|
+
|
|
61
|
+
### get_agent_skills
|
|
62
|
+
|
|
63
|
+
**Drill-down tool for agent capabilities.**
|
|
64
|
+
|
|
65
|
+
Use when you need to:
|
|
66
|
+
- Understand what specific skills an agent has
|
|
67
|
+
- Validate that an agent can handle a specific deliverable type
|
|
68
|
+
- Explore domain coverage for an agent
|
|
69
|
+
|
|
70
|
+
## Integration with find-best-agent
|
|
71
|
+
|
|
72
|
+
The `find-best-agent` micro-agent uses this server to:
|
|
73
|
+
|
|
74
|
+
1. **Decompose** a complex PRD into logical tasks
|
|
75
|
+
2. **Route** each task through `select_best_agent`
|
|
76
|
+
3. **Map** tasks to their optimal agents
|
|
77
|
+
4. **Execute** in parallel (independent tasks) or sequential (dependent tasks)
|
|
78
|
+
|
|
79
|
+
## Caching
|
|
80
|
+
|
|
81
|
+
The server implements a 15-minute embedding cache to reduce OpenAI API calls. Similar task descriptions will reuse cached embeddings.
|
|
82
|
+
|
|
83
|
+
## Error Handling
|
|
84
|
+
|
|
85
|
+
If no agent matches above the threshold, the server returns "chief-technology-architect" as a fallback. This ensures you always get a valid agent name to route work to.
|
|
86
|
+
|
|
87
|
+
## Fallback Behavior
|
|
88
|
+
|
|
89
|
+
| Scenario | Result |
|
|
90
|
+
|----------|--------|
|
|
91
|
+
| No agents match threshold | Returns "chief-technology-architect" |
|
|
92
|
+
| Empty task description | Returns error |
|
|
93
|
+
| Invalid agent name (get_skills) | Returns empty skills array |
|
|
94
|
+
| OpenAI API error | Returns error with details |
|
|
95
|
+
| Supabase connection error | Returns error with details |
|
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# tarvacode-agent-selector MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server for semantic agent selection using pgvector embeddings in Supabase.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This server provides tools to find the best specialized Claude Code agent for a given task using semantic similarity matching:
|
|
8
|
+
|
|
9
|
+
1. **select_best_agent** - Find the optimal agent for a task description
|
|
10
|
+
2. **list_agents** - List all available agents
|
|
11
|
+
3. **get_agent_skills** - Get skills for a specific agent
|
|
12
|
+
|
|
13
|
+
## Architecture
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Task Description
|
|
17
|
+
↓
|
|
18
|
+
OpenAI Embedding (text-embedding-3-small)
|
|
19
|
+
↓
|
|
20
|
+
Supabase Stored Procedure (two-tier selection)
|
|
21
|
+
- Top-K agent shortlist by embedding similarity
|
|
22
|
+
- Rerank by max skill similarity
|
|
23
|
+
- Fallback to chief-technology-architect
|
|
24
|
+
↓
|
|
25
|
+
Agent Name
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Setup
|
|
29
|
+
|
|
30
|
+
### Prerequisites
|
|
31
|
+
|
|
32
|
+
- Node.js >= 18.0.0
|
|
33
|
+
- npm or yarn
|
|
34
|
+
- Access to:
|
|
35
|
+
- Supabase project with pgvector extension
|
|
36
|
+
- OpenAI API key
|
|
37
|
+
|
|
38
|
+
### Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
cd find-best-agent/mcp-server
|
|
42
|
+
npm install
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Environment Variables
|
|
47
|
+
|
|
48
|
+
| Variable | Description | Required |
|
|
49
|
+
|----------|-------------|----------|
|
|
50
|
+
| `OPENAI_API_KEY` | OpenAI API key for embeddings | Yes |
|
|
51
|
+
| `SUPABASE_URL` | Supabase project URL | Yes |
|
|
52
|
+
| `SUPABASE_ANON_KEY` | Supabase anon/public key | Yes |
|
|
53
|
+
|
|
54
|
+
### Claude Code Configuration
|
|
55
|
+
|
|
56
|
+
Add to your project's `.claude/mcp.json`:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"tarvacode-agent-selector": {
|
|
62
|
+
"command": "node",
|
|
63
|
+
"args": ["./find-best-agent/mcp-server/dist/index.js"],
|
|
64
|
+
"env": {
|
|
65
|
+
"OPENAI_API_KEY": "${OPENAI_API_KEY}",
|
|
66
|
+
"SUPABASE_URL": "${SUPABASE_URL}",
|
|
67
|
+
"SUPABASE_ANON_KEY": "${SUPABASE_ANON_KEY}"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Tools
|
|
75
|
+
|
|
76
|
+
### select_best_agent
|
|
77
|
+
|
|
78
|
+
Finds the best agent for a task using two-tier semantic matching.
|
|
79
|
+
|
|
80
|
+
**Input:**
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"task": "Design a PostgreSQL schema for a multi-tenant SaaS application",
|
|
84
|
+
"match_threshold": 0.5
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Output:**
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"agent_name": "database-architect"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### list_agents
|
|
96
|
+
|
|
97
|
+
Lists all available agents.
|
|
98
|
+
|
|
99
|
+
**Output:**
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"agents": [
|
|
103
|
+
{
|
|
104
|
+
"name": "database-architect",
|
|
105
|
+
"description": "World-class Database Architect...",
|
|
106
|
+
"tools": ["Read", "Write", "Edit", "Grep", "Glob", "Bash", "Task"]
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
"count": 20
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### get_agent_skills
|
|
114
|
+
|
|
115
|
+
Gets skills for a specific agent.
|
|
116
|
+
|
|
117
|
+
**Input:**
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"agent_name": "database-architect"
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Output:**
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"agent_name": "database-architect",
|
|
128
|
+
"skills": [
|
|
129
|
+
{
|
|
130
|
+
"skill_id": "A1",
|
|
131
|
+
"skill_name": "Workload Classification",
|
|
132
|
+
"domain": "A: Architecture strategy",
|
|
133
|
+
"deliverable": "classification_report"
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
"count": 52
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Development
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Run in development mode
|
|
144
|
+
npm run dev
|
|
145
|
+
|
|
146
|
+
# Type check
|
|
147
|
+
npm run typecheck
|
|
148
|
+
|
|
149
|
+
# Build
|
|
150
|
+
npm run build
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Selection Algorithm
|
|
154
|
+
|
|
155
|
+
The stored procedure implements a two-tier selection:
|
|
156
|
+
|
|
157
|
+
1. **Agent-Level Shortlist**: Find top-K agents by cosine similarity between task embedding and agent embeddings
|
|
158
|
+
2. **Skill-Level Rerank**: For each candidate, compute max similarity against skill embeddings
|
|
159
|
+
3. **Final Score**: Use the higher of agent similarity or max skill similarity
|
|
160
|
+
4. **Threshold**: Only return agents above the match threshold
|
|
161
|
+
5. **Fallback**: Return "chief-technology-architect" if no match above threshold
|
|
162
|
+
|
|
163
|
+
This approach balances speed (coarse agent matching) with accuracy (granular skill validation).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { selectAgentTool } from "./tools/selectAgent.js";
|
|
6
|
+
import { listAgentsTool } from "./tools/listAgents.js";
|
|
7
|
+
import { getAgentSkillsTool } from "./tools/getAgentSkills.js";
|
|
8
|
+
const SERVER_NAME = "tarvacode-agent-selector";
|
|
9
|
+
const SERVER_VERSION = "1.0.0";
|
|
10
|
+
// Define available tools
|
|
11
|
+
const TOOLS = [
|
|
12
|
+
{
|
|
13
|
+
name: "select_best_agent",
|
|
14
|
+
description: "Finds the best specialized agent for a given task description using semantic similarity matching. Returns ONLY the agent name.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
task: {
|
|
19
|
+
type: "string",
|
|
20
|
+
description: "The task description to find the best agent for",
|
|
21
|
+
},
|
|
22
|
+
match_threshold: {
|
|
23
|
+
type: "number",
|
|
24
|
+
description: "Minimum similarity score (0-1) to consider a match. Default: 0.5",
|
|
25
|
+
default: 0.5,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
required: ["task"],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "list_agents",
|
|
33
|
+
description: "Lists all available specialized agents with their descriptions and available tools.",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: {},
|
|
37
|
+
required: [],
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "get_agent_skills",
|
|
42
|
+
description: "Gets the skills available for a specific agent, organized by domain.",
|
|
43
|
+
inputSchema: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
agent_name: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "The name of the agent to get skills for",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ["agent_name"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
async function main() {
|
|
56
|
+
const server = new Server({
|
|
57
|
+
name: SERVER_NAME,
|
|
58
|
+
version: SERVER_VERSION,
|
|
59
|
+
}, {
|
|
60
|
+
capabilities: {
|
|
61
|
+
tools: {},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
// Handle list tools request
|
|
65
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
66
|
+
return { tools: TOOLS };
|
|
67
|
+
});
|
|
68
|
+
// Handle tool calls
|
|
69
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
70
|
+
const { name, arguments: args } = request.params;
|
|
71
|
+
try {
|
|
72
|
+
let result;
|
|
73
|
+
switch (name) {
|
|
74
|
+
case "select_best_agent":
|
|
75
|
+
result = await selectAgentTool({
|
|
76
|
+
task: args?.task,
|
|
77
|
+
match_threshold: args?.match_threshold,
|
|
78
|
+
});
|
|
79
|
+
break;
|
|
80
|
+
case "list_agents":
|
|
81
|
+
result = await listAgentsTool();
|
|
82
|
+
break;
|
|
83
|
+
case "get_agent_skills":
|
|
84
|
+
result = await getAgentSkillsTool({
|
|
85
|
+
agent_name: args?.agent_name,
|
|
86
|
+
});
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
content: [
|
|
93
|
+
{
|
|
94
|
+
type: "text",
|
|
95
|
+
text: JSON.stringify(result, null, 2),
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
102
|
+
return {
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: "text",
|
|
106
|
+
text: JSON.stringify({ error: errorMessage }),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
isError: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
// Start the server
|
|
114
|
+
const transport = new StdioServerTransport();
|
|
115
|
+
await server.connect(transport);
|
|
116
|
+
console.error(`${SERVER_NAME} v${SERVER_VERSION} started`);
|
|
117
|
+
}
|
|
118
|
+
main().catch((error) => {
|
|
119
|
+
console.error("Fatal error:", error);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
});
|
|
122
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GAEvB,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,WAAW,GAAG,0BAA0B,CAAC;AAC/C,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,yBAAyB;AACzB,MAAM,KAAK,GAAW;IACpB;QACE,IAAI,EAAE,mBAAmB;QACzB,WAAW,EACT,gIAAgI;QAClI,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,iDAAiD;iBAC/D;gBACD,eAAe,EAAE;oBACf,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,kEAAkE;oBACpE,OAAO,EAAE,GAAG;iBACb;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;KACF;IACD;QACE,IAAI,EAAE,aAAa;QACnB,WAAW,EACT,qFAAqF;QACvF,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE,EAAE;YACd,QAAQ,EAAE,EAAE;SACb;KACF;IACD;QACE,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,sEAAsE;QACxE,WAAW,EAAE;YACX,IAAI,EAAE,QAAiB;YACvB,UAAU,EAAE;gBACV,UAAU,EAAE;oBACV,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,yCAAyC;iBACvD;aACF;YACD,QAAQ,EAAE,CAAC,YAAY,CAAC;SACzB;KACF;CACF,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;KACxB,EACD;QACE,YAAY,EAAE;YACZ,KAAK,EAAE,EAAE;SACV;KACF,CACF,CAAC;IAEF,4BAA4B;IAC5B,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAC1D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,oBAAoB;IACpB,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;QAEjD,IAAI,CAAC;YACH,IAAI,MAAe,CAAC;YAEpB,QAAQ,IAAI,EAAE,CAAC;gBACb,KAAK,mBAAmB;oBACtB,MAAM,GAAG,MAAM,eAAe,CAAC;wBAC7B,IAAI,EAAE,IAAI,EAAE,IAAc;wBAC1B,eAAe,EAAE,IAAI,EAAE,eAAqC;qBAC7D,CAAC,CAAC;oBACH,MAAM;gBAER,KAAK,aAAa;oBAChB,MAAM,GAAG,MAAM,cAAc,EAAE,CAAC;oBAChC,MAAM;gBAER,KAAK,kBAAkB;oBACrB,MAAM,GAAG,MAAM,kBAAkB,CAAC;wBAChC,UAAU,EAAE,IAAI,EAAE,UAAoB;qBACvC,CAAC,CAAC;oBACH,MAAM;gBAER;oBACE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;YAC7C,CAAC;YAED,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;qBACtC;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,wBAAwB,CAAC;YAEpE,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;qBAC9C;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,mBAAmB;IACnB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,OAAO,CAAC,KAAK,CAAC,GAAG,WAAW,KAAK,cAAc,UAAU,CAAC,CAAC;AAC7D,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate embedding for a single text with caching.
|
|
3
|
+
*/
|
|
4
|
+
export declare function generateEmbedding(text: string): Promise<number[]>;
|
|
5
|
+
/**
|
|
6
|
+
* Generate embeddings for multiple texts with batching.
|
|
7
|
+
*/
|
|
8
|
+
export declare function generateEmbeddings(texts: string[]): Promise<number[][]>;
|
|
9
|
+
//# sourceMappingURL=embeddings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../src/lib/embeddings.ts"],"names":[],"mappings":"AAmDA;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA8BvE;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAS7E"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
const EMBEDDING_MODEL = "text-embedding-3-small";
|
|
3
|
+
let openaiClient = null;
|
|
4
|
+
const embeddingCache = new Map();
|
|
5
|
+
const CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
|
6
|
+
function getOpenAIClient() {
|
|
7
|
+
if (openaiClient) {
|
|
8
|
+
return openaiClient;
|
|
9
|
+
}
|
|
10
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
throw new Error("Missing OpenAI configuration. Set OPENAI_API_KEY environment variable.");
|
|
13
|
+
}
|
|
14
|
+
openaiClient = new OpenAI({ apiKey });
|
|
15
|
+
return openaiClient;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Normalize text for cache key.
|
|
19
|
+
*/
|
|
20
|
+
function normalizeText(text) {
|
|
21
|
+
return text.trim().toLowerCase();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Clean expired cache entries.
|
|
25
|
+
*/
|
|
26
|
+
function cleanCache() {
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
for (const [key, entry] of embeddingCache.entries()) {
|
|
29
|
+
if (now - entry.timestamp > CACHE_TTL_MS) {
|
|
30
|
+
embeddingCache.delete(key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Generate embedding for a single text with caching.
|
|
36
|
+
*/
|
|
37
|
+
export async function generateEmbedding(text) {
|
|
38
|
+
const cacheKey = normalizeText(text);
|
|
39
|
+
// Check cache
|
|
40
|
+
const cached = embeddingCache.get(cacheKey);
|
|
41
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
|
|
42
|
+
return cached.embedding;
|
|
43
|
+
}
|
|
44
|
+
// Clean old entries periodically
|
|
45
|
+
if (embeddingCache.size > 100) {
|
|
46
|
+
cleanCache();
|
|
47
|
+
}
|
|
48
|
+
const client = getOpenAIClient();
|
|
49
|
+
const response = await client.embeddings.create({
|
|
50
|
+
model: EMBEDDING_MODEL,
|
|
51
|
+
input: text,
|
|
52
|
+
});
|
|
53
|
+
const embedding = response.data[0].embedding;
|
|
54
|
+
// Cache the result
|
|
55
|
+
embeddingCache.set(cacheKey, {
|
|
56
|
+
embedding,
|
|
57
|
+
timestamp: Date.now(),
|
|
58
|
+
});
|
|
59
|
+
return embedding;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Generate embeddings for multiple texts with batching.
|
|
63
|
+
*/
|
|
64
|
+
export async function generateEmbeddings(texts) {
|
|
65
|
+
const client = getOpenAIClient();
|
|
66
|
+
const response = await client.embeddings.create({
|
|
67
|
+
model: EMBEDDING_MODEL,
|
|
68
|
+
input: texts,
|
|
69
|
+
});
|
|
70
|
+
return response.data.map((item) => item.embedding);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=embeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embeddings.js","sourceRoot":"","sources":["../../src/lib/embeddings.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD,IAAI,YAAY,GAAkB,IAAI,CAAC;AAQvC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAsB,CAAC;AACrD,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;AAElD,SAAS,eAAe;IACtB,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAE1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,wEAAwE,CACzE,CAAC;IACJ,CAAC;IAED,YAAY,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,SAAS,UAAU;IACjB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;QACpD,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;YACzC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAY;IAClD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IAErC,cAAc;IACd,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,GAAG,YAAY,EAAE,CAAC;QAC3D,OAAO,MAAM,CAAC,SAAS,CAAC;IAC1B,CAAC;IAED,iCAAiC;IACjC,IAAI,cAAc,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC;QAC9B,UAAU,EAAE,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IAEjC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9C,KAAK,EAAE,eAAe;QACtB,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAE7C,mBAAmB;IACnB,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE;QAC3B,SAAS;QACT,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC,CAAC;IAEH,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAe;IACtD,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;IAEjC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9C,KAAK,EAAE,eAAe;QACtB,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
export declare function getSupabaseClient(): SupabaseClient;
|
|
3
|
+
export interface Agent {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
tools: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface Skill {
|
|
9
|
+
skill_id: string;
|
|
10
|
+
skill_name: string;
|
|
11
|
+
domain: string;
|
|
12
|
+
deliverable: string | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Select the best agent for a task using the two-tier stored procedure.
|
|
16
|
+
*/
|
|
17
|
+
export declare function selectBestAgent(taskEmbedding: number[], matchThreshold?: number, topK?: number): Promise<string>;
|
|
18
|
+
/**
|
|
19
|
+
* List all available agents.
|
|
20
|
+
*/
|
|
21
|
+
export declare function listAgents(): Promise<Agent[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Get skills for a specific agent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function getAgentSkills(agentName: string): Promise<Skill[]>;
|
|
26
|
+
//# sourceMappingURL=supabase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/lib/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAIrE,wBAAgB,iBAAiB,IAAI,cAAc,CAgBlD;AAED,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EAAE,EACvB,cAAc,GAAE,MAAY,EAC5B,IAAI,GAAE,MAAU,GACf,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC,CAUnD;AAED;;GAEG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAYxE"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { createClient } from "@supabase/supabase-js";
|
|
2
|
+
let supabaseClient = null;
|
|
3
|
+
export function getSupabaseClient() {
|
|
4
|
+
if (supabaseClient) {
|
|
5
|
+
return supabaseClient;
|
|
6
|
+
}
|
|
7
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
8
|
+
const supabaseKey = process.env.SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
9
|
+
if (!supabaseUrl || !supabaseKey) {
|
|
10
|
+
throw new Error("Missing Supabase configuration. Set SUPABASE_URL and SUPABASE_ANON_KEY environment variables.");
|
|
11
|
+
}
|
|
12
|
+
supabaseClient = createClient(supabaseUrl, supabaseKey);
|
|
13
|
+
return supabaseClient;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Select the best agent for a task using the two-tier stored procedure.
|
|
17
|
+
*/
|
|
18
|
+
export async function selectBestAgent(taskEmbedding, matchThreshold = 0.5, topK = 5) {
|
|
19
|
+
const supabase = getSupabaseClient();
|
|
20
|
+
const { data, error } = await supabase.rpc("select_best_agent", {
|
|
21
|
+
task_embedding: taskEmbedding,
|
|
22
|
+
match_threshold: matchThreshold,
|
|
23
|
+
top_k: topK,
|
|
24
|
+
});
|
|
25
|
+
if (error) {
|
|
26
|
+
throw new Error(`Failed to select agent: ${error.message}`);
|
|
27
|
+
}
|
|
28
|
+
return data;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* List all available agents.
|
|
32
|
+
*/
|
|
33
|
+
export async function listAgents() {
|
|
34
|
+
const supabase = getSupabaseClient();
|
|
35
|
+
const { data, error } = await supabase.rpc("list_agents");
|
|
36
|
+
if (error) {
|
|
37
|
+
throw new Error(`Failed to list agents: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get skills for a specific agent.
|
|
43
|
+
*/
|
|
44
|
+
export async function getAgentSkills(agentName) {
|
|
45
|
+
const supabase = getSupabaseClient();
|
|
46
|
+
const { data, error } = await supabase.rpc("get_agent_skills", {
|
|
47
|
+
agent_name: agentName,
|
|
48
|
+
});
|
|
49
|
+
if (error) {
|
|
50
|
+
throw new Error(`Failed to get agent skills: ${error.message}`);
|
|
51
|
+
}
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=supabase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase.js","sourceRoot":"","sources":["../../src/lib/supabase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAkB,MAAM,uBAAuB,CAAC;AAErE,IAAI,cAAc,GAA0B,IAAI,CAAC;AAEjD,MAAM,UAAU,iBAAiB;IAC/B,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC7C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IAE3F,IAAI,CAAC,WAAW,IAAI,CAAC,WAAW,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,+FAA+F,CAChG,CAAC;IACJ,CAAC;IAED,cAAc,GAAG,YAAY,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACxD,OAAO,cAAc,CAAC;AACxB,CAAC;AAeD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,aAAuB,EACvB,iBAAyB,GAAG,EAC5B,OAAe,CAAC;IAEhB,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IAErC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,mBAAmB,EAAE;QAC9D,cAAc,EAAE,aAAa;QAC7B,eAAe,EAAE,cAAc;QAC/B,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,2BAA2B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,IAAc,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IAErC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAE1D,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,OAAO,IAAe,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAiB;IACpD,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IAErC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,kBAAkB,EAAE;QAC7D,UAAU,EAAE,SAAS;KACtB,CAAC,CAAC;IAEH,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,+BAA+B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,IAAe,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Skill } from "../lib/supabase.js";
|
|
2
|
+
export interface GetAgentSkillsInput {
|
|
3
|
+
agent_name: string;
|
|
4
|
+
}
|
|
5
|
+
export interface GetAgentSkillsOutput {
|
|
6
|
+
agent_name: string;
|
|
7
|
+
skills: Skill[];
|
|
8
|
+
count: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the skills available for a specific agent.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getAgentSkillsTool(input: GetAgentSkillsInput): Promise<GetAgentSkillsOutput>;
|
|
14
|
+
//# sourceMappingURL=getAgentSkills.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAgentSkills.d.ts","sourceRoot":"","sources":["../../src/tools/getAgentSkills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3D,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CAc/B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getAgentSkills } from "../lib/supabase.js";
|
|
2
|
+
/**
|
|
3
|
+
* Get the skills available for a specific agent.
|
|
4
|
+
*/
|
|
5
|
+
export async function getAgentSkillsTool(input) {
|
|
6
|
+
const { agent_name } = input;
|
|
7
|
+
if (!agent_name || agent_name.trim().length === 0) {
|
|
8
|
+
throw new Error("Agent name is required");
|
|
9
|
+
}
|
|
10
|
+
const skills = await getAgentSkills(agent_name);
|
|
11
|
+
return {
|
|
12
|
+
agent_name,
|
|
13
|
+
skills,
|
|
14
|
+
count: skills.length,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=getAgentSkills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getAgentSkills.js","sourceRoot":"","sources":["../../src/tools/getAgentSkills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAS,MAAM,oBAAoB,CAAC;AAY3D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAA0B;IAE1B,MAAM,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAE7B,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC5C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,UAAU,CAAC,CAAC;IAEhD,OAAO;QACL,UAAU;QACV,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,MAAM;KACrB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Agent } from "../lib/supabase.js";
|
|
2
|
+
export interface ListAgentsOutput {
|
|
3
|
+
agents: Agent[];
|
|
4
|
+
count: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* List all available agents with their descriptions and tools.
|
|
8
|
+
*/
|
|
9
|
+
export declare function listAgentsTool(): Promise<ListAgentsOutput>;
|
|
10
|
+
//# sourceMappingURL=listAgents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listAgents.d.ts","sourceRoot":"","sources":["../../src/tools/listAgents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAOhE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { listAgents } from "../lib/supabase.js";
|
|
2
|
+
/**
|
|
3
|
+
* List all available agents with their descriptions and tools.
|
|
4
|
+
*/
|
|
5
|
+
export async function listAgentsTool() {
|
|
6
|
+
const agents = await listAgents();
|
|
7
|
+
return {
|
|
8
|
+
agents,
|
|
9
|
+
count: agents.length,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=listAgents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listAgents.js","sourceRoot":"","sources":["../../src/tools/listAgents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAS,MAAM,oBAAoB,CAAC;AAOvD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAElC,OAAO;QACL,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,MAAM;KACrB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface SelectAgentInput {
|
|
2
|
+
task: string;
|
|
3
|
+
match_threshold?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface SelectAgentOutput {
|
|
6
|
+
agent_name: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Select the best agent for a given task description.
|
|
10
|
+
*
|
|
11
|
+
* This tool:
|
|
12
|
+
* 1. Generates an embedding for the task description
|
|
13
|
+
* 2. Calls the Supabase stored procedure to find the best matching agent
|
|
14
|
+
* 3. Returns ONLY the agent name
|
|
15
|
+
*/
|
|
16
|
+
export declare function selectAgentTool(input: SelectAgentInput): Promise<SelectAgentOutput>;
|
|
17
|
+
//# sourceMappingURL=selectAgent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectAgent.d.ts","sourceRoot":"","sources":["../../src/tools/selectAgent.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAgB5B"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { generateEmbedding } from "../lib/embeddings.js";
|
|
2
|
+
import { selectBestAgent } from "../lib/supabase.js";
|
|
3
|
+
/**
|
|
4
|
+
* Select the best agent for a given task description.
|
|
5
|
+
*
|
|
6
|
+
* This tool:
|
|
7
|
+
* 1. Generates an embedding for the task description
|
|
8
|
+
* 2. Calls the Supabase stored procedure to find the best matching agent
|
|
9
|
+
* 3. Returns ONLY the agent name
|
|
10
|
+
*/
|
|
11
|
+
export async function selectAgentTool(input) {
|
|
12
|
+
const { task, match_threshold = 0.5 } = input;
|
|
13
|
+
if (!task || task.trim().length === 0) {
|
|
14
|
+
throw new Error("Task description is required");
|
|
15
|
+
}
|
|
16
|
+
// Generate embedding for the task
|
|
17
|
+
const taskEmbedding = await generateEmbedding(task);
|
|
18
|
+
// Call stored procedure to find best agent
|
|
19
|
+
const agentName = await selectBestAgent(taskEmbedding, match_threshold);
|
|
20
|
+
return {
|
|
21
|
+
agent_name: agentName,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=selectAgent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selectAgent.js","sourceRoot":"","sources":["../../src/tools/selectAgent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAWrD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,KAAuB;IAEvB,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,GAAG,EAAE,GAAG,KAAK,CAAC;IAE9C,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAClD,CAAC;IAED,kCAAkC;IAClC,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAEpD,2CAA2C;IAC3C,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IAExE,OAAO;QACL,UAAU,EAAE,SAAS;KACtB,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tarvacode-agent-selector",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for semantic agent selection using pgvector embeddings",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"dev": "tsx src/index.ts",
|
|
11
|
+
"clean": "rm -rf dist",
|
|
12
|
+
"typecheck": "tsc --noEmit"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"claude",
|
|
17
|
+
"agent-selection",
|
|
18
|
+
"embeddings",
|
|
19
|
+
"pgvector"
|
|
20
|
+
],
|
|
21
|
+
"author": "",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
25
|
+
"@supabase/supabase-js": "^2.39.0",
|
|
26
|
+
"openai": "^4.28.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.11.0",
|
|
30
|
+
"tsx": "^4.7.0",
|
|
31
|
+
"typescript": "^5.3.3"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
Tool,
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
|
|
11
|
+
import { selectAgentTool } from "./tools/selectAgent.js";
|
|
12
|
+
import { listAgentsTool } from "./tools/listAgents.js";
|
|
13
|
+
import { getAgentSkillsTool } from "./tools/getAgentSkills.js";
|
|
14
|
+
|
|
15
|
+
const SERVER_NAME = "tarvacode-agent-selector";
|
|
16
|
+
const SERVER_VERSION = "1.0.0";
|
|
17
|
+
|
|
18
|
+
// Define available tools
|
|
19
|
+
const TOOLS: Tool[] = [
|
|
20
|
+
{
|
|
21
|
+
name: "select_best_agent",
|
|
22
|
+
description:
|
|
23
|
+
"Finds the best specialized agent for a given task description using semantic similarity matching. Returns ONLY the agent name.",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object" as const,
|
|
26
|
+
properties: {
|
|
27
|
+
task: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "The task description to find the best agent for",
|
|
30
|
+
},
|
|
31
|
+
match_threshold: {
|
|
32
|
+
type: "number",
|
|
33
|
+
description:
|
|
34
|
+
"Minimum similarity score (0-1) to consider a match. Default: 0.5",
|
|
35
|
+
default: 0.5,
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
required: ["task"],
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "list_agents",
|
|
43
|
+
description:
|
|
44
|
+
"Lists all available specialized agents with their descriptions and available tools.",
|
|
45
|
+
inputSchema: {
|
|
46
|
+
type: "object" as const,
|
|
47
|
+
properties: {},
|
|
48
|
+
required: [],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "get_agent_skills",
|
|
53
|
+
description:
|
|
54
|
+
"Gets the skills available for a specific agent, organized by domain.",
|
|
55
|
+
inputSchema: {
|
|
56
|
+
type: "object" as const,
|
|
57
|
+
properties: {
|
|
58
|
+
agent_name: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: "The name of the agent to get skills for",
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
required: ["agent_name"],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
async function main() {
|
|
69
|
+
const server = new Server(
|
|
70
|
+
{
|
|
71
|
+
name: SERVER_NAME,
|
|
72
|
+
version: SERVER_VERSION,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
capabilities: {
|
|
76
|
+
tools: {},
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Handle list tools request
|
|
82
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
83
|
+
return { tools: TOOLS };
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Handle tool calls
|
|
87
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
88
|
+
const { name, arguments: args } = request.params;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
let result: unknown;
|
|
92
|
+
|
|
93
|
+
switch (name) {
|
|
94
|
+
case "select_best_agent":
|
|
95
|
+
result = await selectAgentTool({
|
|
96
|
+
task: args?.task as string,
|
|
97
|
+
match_threshold: args?.match_threshold as number | undefined,
|
|
98
|
+
});
|
|
99
|
+
break;
|
|
100
|
+
|
|
101
|
+
case "list_agents":
|
|
102
|
+
result = await listAgentsTool();
|
|
103
|
+
break;
|
|
104
|
+
|
|
105
|
+
case "get_agent_skills":
|
|
106
|
+
result = await getAgentSkillsTool({
|
|
107
|
+
agent_name: args?.agent_name as string,
|
|
108
|
+
});
|
|
109
|
+
break;
|
|
110
|
+
|
|
111
|
+
default:
|
|
112
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: "text",
|
|
119
|
+
text: JSON.stringify(result, null, 2),
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
};
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const errorMessage =
|
|
125
|
+
error instanceof Error ? error.message : "Unknown error occurred";
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: "text",
|
|
131
|
+
text: JSON.stringify({ error: errorMessage }),
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
isError: true,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
// Start the server
|
|
140
|
+
const transport = new StdioServerTransport();
|
|
141
|
+
await server.connect(transport);
|
|
142
|
+
|
|
143
|
+
console.error(`${SERVER_NAME} v${SERVER_VERSION} started`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
main().catch((error) => {
|
|
147
|
+
console.error("Fatal error:", error);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
|
|
3
|
+
const EMBEDDING_MODEL = "text-embedding-3-small";
|
|
4
|
+
|
|
5
|
+
let openaiClient: OpenAI | null = null;
|
|
6
|
+
|
|
7
|
+
// Simple in-memory cache with 15-minute TTL
|
|
8
|
+
interface CacheEntry {
|
|
9
|
+
embedding: number[];
|
|
10
|
+
timestamp: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const embeddingCache = new Map<string, CacheEntry>();
|
|
14
|
+
const CACHE_TTL_MS = 15 * 60 * 1000; // 15 minutes
|
|
15
|
+
|
|
16
|
+
function getOpenAIClient(): OpenAI {
|
|
17
|
+
if (openaiClient) {
|
|
18
|
+
return openaiClient;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
22
|
+
|
|
23
|
+
if (!apiKey) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
"Missing OpenAI configuration. Set OPENAI_API_KEY environment variable."
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
openaiClient = new OpenAI({ apiKey });
|
|
30
|
+
return openaiClient;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Normalize text for cache key.
|
|
35
|
+
*/
|
|
36
|
+
function normalizeText(text: string): string {
|
|
37
|
+
return text.trim().toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Clean expired cache entries.
|
|
42
|
+
*/
|
|
43
|
+
function cleanCache(): void {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
for (const [key, entry] of embeddingCache.entries()) {
|
|
46
|
+
if (now - entry.timestamp > CACHE_TTL_MS) {
|
|
47
|
+
embeddingCache.delete(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate embedding for a single text with caching.
|
|
54
|
+
*/
|
|
55
|
+
export async function generateEmbedding(text: string): Promise<number[]> {
|
|
56
|
+
const cacheKey = normalizeText(text);
|
|
57
|
+
|
|
58
|
+
// Check cache
|
|
59
|
+
const cached = embeddingCache.get(cacheKey);
|
|
60
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
|
|
61
|
+
return cached.embedding;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Clean old entries periodically
|
|
65
|
+
if (embeddingCache.size > 100) {
|
|
66
|
+
cleanCache();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const client = getOpenAIClient();
|
|
70
|
+
|
|
71
|
+
const response = await client.embeddings.create({
|
|
72
|
+
model: EMBEDDING_MODEL,
|
|
73
|
+
input: text,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const embedding = response.data[0].embedding;
|
|
77
|
+
|
|
78
|
+
// Cache the result
|
|
79
|
+
embeddingCache.set(cacheKey, {
|
|
80
|
+
embedding,
|
|
81
|
+
timestamp: Date.now(),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return embedding;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generate embeddings for multiple texts with batching.
|
|
89
|
+
*/
|
|
90
|
+
export async function generateEmbeddings(texts: string[]): Promise<number[][]> {
|
|
91
|
+
const client = getOpenAIClient();
|
|
92
|
+
|
|
93
|
+
const response = await client.embeddings.create({
|
|
94
|
+
model: EMBEDDING_MODEL,
|
|
95
|
+
input: texts,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return response.data.map((item) => item.embedding);
|
|
99
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { createClient, SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
|
|
3
|
+
let supabaseClient: SupabaseClient | null = null;
|
|
4
|
+
|
|
5
|
+
export function getSupabaseClient(): SupabaseClient {
|
|
6
|
+
if (supabaseClient) {
|
|
7
|
+
return supabaseClient;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const supabaseUrl = process.env.SUPABASE_URL;
|
|
11
|
+
const supabaseKey = process.env.SUPABASE_ANON_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
|
|
12
|
+
|
|
13
|
+
if (!supabaseUrl || !supabaseKey) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Missing Supabase configuration. Set SUPABASE_URL and SUPABASE_ANON_KEY environment variables."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
supabaseClient = createClient(supabaseUrl, supabaseKey);
|
|
20
|
+
return supabaseClient;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Agent {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
tools: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Skill {
|
|
30
|
+
skill_id: string;
|
|
31
|
+
skill_name: string;
|
|
32
|
+
domain: string;
|
|
33
|
+
deliverable: string | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Select the best agent for a task using the two-tier stored procedure.
|
|
38
|
+
*/
|
|
39
|
+
export async function selectBestAgent(
|
|
40
|
+
taskEmbedding: number[],
|
|
41
|
+
matchThreshold: number = 0.5,
|
|
42
|
+
topK: number = 5
|
|
43
|
+
): Promise<string> {
|
|
44
|
+
const supabase = getSupabaseClient();
|
|
45
|
+
|
|
46
|
+
const { data, error } = await supabase.rpc("select_best_agent", {
|
|
47
|
+
task_embedding: taskEmbedding,
|
|
48
|
+
match_threshold: matchThreshold,
|
|
49
|
+
top_k: topK,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (error) {
|
|
53
|
+
throw new Error(`Failed to select agent: ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return data as string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* List all available agents.
|
|
61
|
+
*/
|
|
62
|
+
export async function listAgents(): Promise<Agent[]> {
|
|
63
|
+
const supabase = getSupabaseClient();
|
|
64
|
+
|
|
65
|
+
const { data, error } = await supabase.rpc("list_agents");
|
|
66
|
+
|
|
67
|
+
if (error) {
|
|
68
|
+
throw new Error(`Failed to list agents: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return data as Agent[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get skills for a specific agent.
|
|
76
|
+
*/
|
|
77
|
+
export async function getAgentSkills(agentName: string): Promise<Skill[]> {
|
|
78
|
+
const supabase = getSupabaseClient();
|
|
79
|
+
|
|
80
|
+
const { data, error } = await supabase.rpc("get_agent_skills", {
|
|
81
|
+
agent_name: agentName,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (error) {
|
|
85
|
+
throw new Error(`Failed to get agent skills: ${error.message}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return data as Skill[];
|
|
89
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { getAgentSkills, Skill } from "../lib/supabase.js";
|
|
2
|
+
|
|
3
|
+
export interface GetAgentSkillsInput {
|
|
4
|
+
agent_name: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface GetAgentSkillsOutput {
|
|
8
|
+
agent_name: string;
|
|
9
|
+
skills: Skill[];
|
|
10
|
+
count: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Get the skills available for a specific agent.
|
|
15
|
+
*/
|
|
16
|
+
export async function getAgentSkillsTool(
|
|
17
|
+
input: GetAgentSkillsInput
|
|
18
|
+
): Promise<GetAgentSkillsOutput> {
|
|
19
|
+
const { agent_name } = input;
|
|
20
|
+
|
|
21
|
+
if (!agent_name || agent_name.trim().length === 0) {
|
|
22
|
+
throw new Error("Agent name is required");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const skills = await getAgentSkills(agent_name);
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
agent_name,
|
|
29
|
+
skills,
|
|
30
|
+
count: skills.length,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { listAgents, Agent } from "../lib/supabase.js";
|
|
2
|
+
|
|
3
|
+
export interface ListAgentsOutput {
|
|
4
|
+
agents: Agent[];
|
|
5
|
+
count: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* List all available agents with their descriptions and tools.
|
|
10
|
+
*/
|
|
11
|
+
export async function listAgentsTool(): Promise<ListAgentsOutput> {
|
|
12
|
+
const agents = await listAgents();
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
agents,
|
|
16
|
+
count: agents.length,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { generateEmbedding } from "../lib/embeddings.js";
|
|
2
|
+
import { selectBestAgent } from "../lib/supabase.js";
|
|
3
|
+
|
|
4
|
+
export interface SelectAgentInput {
|
|
5
|
+
task: string;
|
|
6
|
+
match_threshold?: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface SelectAgentOutput {
|
|
10
|
+
agent_name: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Select the best agent for a given task description.
|
|
15
|
+
*
|
|
16
|
+
* This tool:
|
|
17
|
+
* 1. Generates an embedding for the task description
|
|
18
|
+
* 2. Calls the Supabase stored procedure to find the best matching agent
|
|
19
|
+
* 3. Returns ONLY the agent name
|
|
20
|
+
*/
|
|
21
|
+
export async function selectAgentTool(
|
|
22
|
+
input: SelectAgentInput
|
|
23
|
+
): Promise<SelectAgentOutput> {
|
|
24
|
+
const { task, match_threshold = 0.5 } = input;
|
|
25
|
+
|
|
26
|
+
if (!task || task.trim().length === 0) {
|
|
27
|
+
throw new Error("Task description is required");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Generate embedding for the task
|
|
31
|
+
const taskEmbedding = await generateEmbedding(task);
|
|
32
|
+
|
|
33
|
+
// Call stored procedure to find best agent
|
|
34
|
+
const agentName = await selectBestAgent(taskEmbedding, match_threshold);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
agent_name: agentName,
|
|
38
|
+
};
|
|
39
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*"],
|
|
18
|
+
"exclude": ["node_modules", "dist"]
|
|
19
|
+
}
|