Skip to content

Commit a57154e

Browse files
committed
refactor: move tools to mcp/tools folder
1 parent ac34540 commit a57154e

File tree

5 files changed

+199
-123
lines changed

5 files changed

+199
-123
lines changed

docs/architecture.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- **UI**: React + Ink (terminal UI)
1111
- **State Management**: Easy-peasy
1212
- **Agent SDK**: Claude Agent SDK
13+
- **MCP SDK**: [Model Context Protocol TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
1314
- **Language**: TypeScript
1415

1516
### Core Components
@@ -224,18 +225,21 @@ The CLI can also run as an MCP server itself, exposing the agent as a tool to ot
224225

225226
**Shared MCP Infrastructure:**
226227

227-
- [src/mcp/utils/getMcpServer.ts](../src/mcp/utils/getMcpServer.ts) - MCP server factory
228+
- [src/mcp/getMcpServer.ts](../src/mcp/getMcpServer.ts) - MCP server factory
228229
- Creates `McpServer` instance
229-
- Registers `ask_agent` tool with Zod validation
230-
- Registers `get_agent_status` tool for initialization
230+
- Imports and registers tools from individual tool files
231231
- Shared by both stdio and HTTP modes
232-
- [src/mcp/utils/runStandaloneAgentLoop.ts](../src/mcp/utils/runStandaloneAgentLoop.ts) - Query execution
232+
- [src/mcp/tools/](../src/mcp/tools/) - Individual MCP tool definitions
233+
- [askAgent.ts](../src/mcp/tools/askAgent.ts) - `ask_agent` tool with Zod validation (general purpose)
234+
- [askAgentSlackbot.ts](../src/mcp/tools/askAgentSlackbot.ts) - `ask_agent_slackbot` tool with thread session support
235+
- [getAgentStatus.ts](../src/mcp/tools/getAgentStatus.ts) - `get_agent_status` tool for initialization
236+
- [src/mcp/runStandaloneAgentLoop.ts](../src/mcp/runStandaloneAgentLoop.ts) - Query execution
233237
- Uses shared `runAgentLoop()` from agent integration
234238
- Manages message queue and session state
235239
- Processes streaming responses with logging notifications
236240
- Implements `onServerConnection` callback for dynamic server selection
237241
- Returns final text response
238-
- [src/mcp/utils/getAgentStatus.ts](../src/mcp/utils/getAgentStatus.ts) - Status utility
242+
- [src/mcp/getAgentStatus.ts](../src/mcp/getAgentStatus.ts) - Status utility
239243
- Initializes agent to get available MCP servers
240244
- Called by `get_agent_status` tool on client initialization
241245
- Returns session ID and MCP servers list

src/mcp/getMcpServer.ts

Lines changed: 20 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
2-
import { getAgentStatus } from "mcp/getAgentStatus"
3-
import { runStandaloneAgentLoop } from "mcp/runStandaloneAgentLoop"
4-
import { z } from "zod"
2+
import { registerAskAgentTool } from "mcp/tools/askAgent"
3+
import { registerAskAgentSlackbotTool } from "mcp/tools/askAgentSlackbot"
4+
import { registerGetAgentStatusTool } from "mcp/tools/getAgentStatus"
55

66
export const getMcpServer = () => {
77
// Store Claude Agent SDK sessionId per-instance (not shared across threads)
@@ -25,127 +25,29 @@ export const getMcpServer = () => {
2525
}
2626
)
2727

28-
mcpServer.registerTool(
29-
"ask_agent",
30-
{
31-
description:
32-
"Passes a query to the internal agent and returns its full output. The agent has access to configured MCP tools and will provide a complete response. DO NOT reprocess, analyze, or summarize the output - return it directly to the user as-is.",
33-
inputSchema: {
34-
query: z.string().min(1).describe("The query to send to the agent"),
28+
// Register tools
29+
registerAskAgentTool({
30+
mcpServer,
31+
context: {
32+
get sessionId() {
33+
return sessionId
3534
},
36-
},
37-
async ({ query }) => {
38-
const existingConnectedServers = sessionId
39-
? sessionConnectedServers.get(sessionId)
40-
: undefined
41-
42-
const { response, connectedServers } = await runStandaloneAgentLoop({
43-
prompt: query,
44-
mcpServer,
45-
sessionId,
46-
existingConnectedServers,
47-
onSessionIdReceived: (newSessionId) => {
48-
sessionId = newSessionId
49-
},
50-
})
51-
52-
// Update the session's connected servers
53-
if (sessionId) {
54-
sessionConnectedServers.set(sessionId, connectedServers)
55-
}
56-
57-
return {
58-
content: [
59-
{
60-
type: "text",
61-
text: response,
62-
},
63-
],
64-
}
65-
}
66-
)
67-
68-
mcpServer.registerTool(
69-
"ask_agent_slackbot",
70-
{
71-
description:
72-
"Slack bot integration tool. Passes a query to the internal agent and returns a response optimized for Slack. Supports per-thread session isolation.",
73-
inputSchema: {
74-
query: z
75-
.string()
76-
.min(1)
77-
.describe("The slack query to send to the agent"),
78-
systemPrompt: z
79-
.string()
80-
.optional()
81-
.describe("Optional additional system prompt to prepend"),
82-
threadId: z
83-
.string()
84-
.optional()
85-
.describe("Slack thread identifier for session isolation"),
35+
sessionConnectedServers,
36+
onSessionIdUpdate: (newSessionId) => {
37+
sessionId = newSessionId
8638
},
8739
},
88-
async ({ query, systemPrompt, threadId }) => {
89-
const existingSessionId = threadId
90-
? threadSessions.get(threadId)
91-
: undefined
40+
})
9241

93-
const existingConnectedServers = existingSessionId
94-
? sessionConnectedServers.get(existingSessionId)
95-
: undefined
96-
97-
const { response, connectedServers } = await runStandaloneAgentLoop({
98-
prompt: query,
99-
mcpServer,
100-
sessionId: existingSessionId,
101-
additionalSystemPrompt: systemPrompt,
102-
existingConnectedServers,
103-
onSessionIdReceived: (newSessionId) => {
104-
if (threadId) {
105-
threadSessions.set(threadId, newSessionId)
106-
}
107-
},
108-
})
109-
110-
// Update the session's connected servers
111-
if (existingSessionId || threadId) {
112-
const sessionId = existingSessionId || threadSessions.get(threadId!)
113-
if (sessionId) {
114-
sessionConnectedServers.set(sessionId, connectedServers)
115-
}
116-
}
117-
118-
return {
119-
content: [
120-
{
121-
type: "text",
122-
text: response,
123-
},
124-
],
125-
}
126-
}
127-
)
128-
129-
mcpServer.registerTool(
130-
"get_agent_status",
131-
{
132-
description:
133-
"Get the status of the agent including which MCP servers it has access to. Call this on initialization to see available servers.",
134-
inputSchema: {},
42+
registerAskAgentSlackbotTool({
43+
mcpServer,
44+
context: {
45+
threadSessions,
46+
sessionConnectedServers,
13547
},
136-
async () => {
137-
const status = await getAgentStatus(mcpServer)
48+
})
13849

139-
return {
140-
content: [
141-
{
142-
type: "text",
143-
text: JSON.stringify(status, null, 2),
144-
},
145-
],
146-
}
147-
}
148-
)
50+
registerGetAgentStatusTool({ mcpServer })
14951

15052
return mcpServer
15153
}

src/mcp/tools/askAgent.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
2+
import { runStandaloneAgentLoop } from "mcp/runStandaloneAgentLoop"
3+
import { z } from "zod"
4+
5+
export interface AskAgentContext {
6+
sessionId?: string
7+
sessionConnectedServers: Map<string, Set<string>>
8+
onSessionIdUpdate: (sessionId: string) => void
9+
}
10+
11+
export interface RegisterAskAgentToolProps {
12+
mcpServer: McpServer
13+
context: AskAgentContext
14+
}
15+
16+
export const registerAskAgentTool = ({
17+
mcpServer,
18+
context,
19+
}: RegisterAskAgentToolProps) => {
20+
mcpServer.registerTool(
21+
"ask_agent",
22+
{
23+
description:
24+
"Passes a query to the internal agent and returns its full output. The agent has access to configured MCP tools and will provide a complete response. DO NOT reprocess, analyze, or summarize the output - return it directly to the user as-is.",
25+
inputSchema: {
26+
query: z.string().min(1).describe("The query to send to the agent"),
27+
},
28+
},
29+
async ({ query }) => {
30+
const existingConnectedServers = context.sessionId
31+
? context.sessionConnectedServers.get(context.sessionId)
32+
: undefined
33+
34+
const { response, connectedServers } = await runStandaloneAgentLoop({
35+
prompt: query,
36+
mcpServer,
37+
sessionId: context.sessionId,
38+
existingConnectedServers,
39+
onSessionIdReceived: (newSessionId) => {
40+
context.onSessionIdUpdate(newSessionId)
41+
},
42+
})
43+
44+
// Update the session's connected servers
45+
if (context.sessionId) {
46+
context.sessionConnectedServers.set(context.sessionId, connectedServers)
47+
}
48+
49+
return {
50+
content: [
51+
{
52+
type: "text",
53+
text: response,
54+
},
55+
],
56+
}
57+
}
58+
)
59+
}

src/mcp/tools/askAgentSlackbot.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
2+
import { runStandaloneAgentLoop } from "mcp/runStandaloneAgentLoop"
3+
import { z } from "zod"
4+
5+
export interface AskAgentSlackbotContext {
6+
threadSessions: Map<string, string>
7+
sessionConnectedServers: Map<string, Set<string>>
8+
}
9+
10+
export interface RegisterAskAgentSlackbotToolProps {
11+
mcpServer: McpServer
12+
context: AskAgentSlackbotContext
13+
}
14+
15+
export const registerAskAgentSlackbotTool = ({
16+
mcpServer,
17+
context,
18+
}: RegisterAskAgentSlackbotToolProps) => {
19+
mcpServer.registerTool(
20+
"ask_agent_slackbot",
21+
{
22+
description:
23+
"Slack bot integration tool. Passes a query to the internal agent and returns a response optimized for Slack. Supports per-thread session isolation.",
24+
inputSchema: {
25+
query: z
26+
.string()
27+
.min(1)
28+
.describe("The slack query to send to the agent"),
29+
systemPrompt: z
30+
.string()
31+
.optional()
32+
.describe("Optional additional system prompt to prepend"),
33+
threadId: z
34+
.string()
35+
.optional()
36+
.describe("Slack thread identifier for session isolation"),
37+
},
38+
},
39+
async ({ query, systemPrompt, threadId }) => {
40+
const existingSessionId = threadId
41+
? context.threadSessions.get(threadId)
42+
: undefined
43+
44+
const existingConnectedServers = existingSessionId
45+
? context.sessionConnectedServers.get(existingSessionId)
46+
: undefined
47+
48+
const { response, connectedServers } = await runStandaloneAgentLoop({
49+
prompt: query,
50+
mcpServer,
51+
sessionId: existingSessionId,
52+
additionalSystemPrompt: systemPrompt,
53+
existingConnectedServers,
54+
onSessionIdReceived: (newSessionId) => {
55+
if (threadId) {
56+
context.threadSessions.set(threadId, newSessionId)
57+
}
58+
},
59+
})
60+
61+
// Update the session's connected servers
62+
if (existingSessionId || threadId) {
63+
const sessionId =
64+
existingSessionId || context.threadSessions.get(threadId!)
65+
if (sessionId) {
66+
context.sessionConnectedServers.set(sessionId, connectedServers)
67+
}
68+
}
69+
70+
return {
71+
content: [
72+
{
73+
type: "text",
74+
text: response,
75+
},
76+
],
77+
}
78+
}
79+
)
80+
}

src/mcp/tools/getAgentStatus.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"
2+
import { getAgentStatus } from "mcp/getAgentStatus"
3+
4+
export interface RegisterGetAgentStatusToolProps {
5+
mcpServer: McpServer
6+
}
7+
8+
export const registerGetAgentStatusTool = ({
9+
mcpServer,
10+
}: RegisterGetAgentStatusToolProps) => {
11+
mcpServer.registerTool(
12+
"get_agent_status",
13+
{
14+
description:
15+
"Get the status of the agent including which MCP servers it has access to. Call this on initialization to see available servers.",
16+
inputSchema: {},
17+
},
18+
async () => {
19+
const status = await getAgentStatus(mcpServer)
20+
21+
return {
22+
content: [
23+
{
24+
type: "text",
25+
text: JSON.stringify(status, null, 2),
26+
},
27+
],
28+
}
29+
}
30+
)
31+
}

0 commit comments

Comments
 (0)