Skip to content

Commit 754404a

Browse files
committed
feat: remove SAP Jira connection logic and update related comments for clarity
1 parent e848879 commit 754404a

8 files changed

Lines changed: 12 additions & 114 deletions

File tree

src/app/api/mcp/servers/for-agent/route.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { NextResponse } from 'next/server';
22
import { auth } from '@clerk/nextjs/server';
33
import { prisma } from '@/server/db';
4-
import { ensureSapJiraSeeded } from '@/lib/mcp-user-store';
54

65
export const dynamic = 'force-dynamic';
76

@@ -13,7 +12,6 @@ export async function GET() {
1312
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
1413

1514
try {
16-
await ensureSapJiraSeeded(userId);
1715
const rows = await prisma.mcpServerConfig.findMany({
1816
where: { userId },
1917
orderBy: { createdAt: 'asc' },

src/app/api/mcp/servers/route.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
validateInput,
77
probeMcpServer,
88
invalidateUserMcpToolsCache,
9-
ensureSapJiraSeeded,
109
type AuthType,
1110
} from '@/lib/mcp-user-store';
1211

@@ -16,7 +15,6 @@ export async function GET() {
1615
const { userId } = await auth();
1716
if (!userId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
1817
try {
19-
await ensureSapJiraSeeded(userId);
2018
const servers = await listUserServers(userId);
2119
return NextResponse.json({ servers });
2220
} catch (err) {

src/components/settings/McpServerSettings.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ export function McpServerSettings() {
4848
const [addOpen, setAddOpen] = useState(false);
4949
const [deleteTarget, setDeleteTarget] = useState<UserMcpServer | null>(null);
5050
// ?connectLoopback=<id> on the URL means "open the loopback paste dialog
51-
// for this server automatically." Set by the agent's sap_jira_connect tool
52-
// (which emits a /api/mcp/servers/<id>/auth URL → that route 302s here).
51+
// for this server automatically." The /api/mcp/servers/<id>/auth route
52+
// 302s here when a server uses the loopback OAuth flow.
5353
const [autoConnectId, setAutoConnectId] = useState<string | null>(null);
5454

5555
const refresh = async () => {

src/hooks/use-agent-stream.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,12 @@ export function useAgentStream() {
266266
}
267267

268268
case "requiresMcpAuth": {
269-
const label =
270-
event.server === "sap-jira" ? "Connect Jira" : `Connect ${event.server}`;
271-
toast.message(`${event.server === "sap-jira" ? "SAP Jira" : event.server} authorization required`, {
269+
toast.message(`${event.server} authorization required`, {
272270
description: "Sign in once to let the agent access your data.",
273271
duration: Infinity,
274272
id: `mcp-auth-${event.server}`,
275273
action: {
276-
label,
274+
label: `Connect ${event.server}`,
277275
onClick: () => {
278276
window.open(event.authUrl, "_blank", "noopener,noreferrer");
279277
},

src/lib/agent.ts

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -137,58 +137,6 @@ const createSandboxTool = tool(
137137
}
138138
);
139139

140-
// ─── SAP Jira Connect Tool (OAuth bootstrap) ───────────────────────────────
141-
142-
const SAP_JIRA_URL = 'https://mcp.jira.tools.sap/mcp';
143-
144-
async function isSapJiraConnected(serverId: string, userId: string): Promise<boolean> {
145-
const appUrl =
146-
process.env.APP_URL ?? process.env.NEXT_PUBLIC_APP_URL ?? 'http://host.docker.internal:3000';
147-
const internalSecret = process.env.INTERNAL_AGENT_SECRET;
148-
if (!internalSecret) return false;
149-
try {
150-
const res = await fetch(
151-
`${appUrl}/api/mcp/internal/servers/${serverId}/credentials?userId=${encodeURIComponent(userId)}`,
152-
{
153-
headers: { Authorization: `Bearer ${internalSecret}` },
154-
cache: 'no-store',
155-
},
156-
);
157-
if (!res.ok) return false;
158-
const data = await res.json();
159-
return Boolean(data?.oauth?.tokens?.access_token);
160-
} catch {
161-
return false;
162-
}
163-
}
164-
165-
const sapJiraConnectTool = tool(
166-
async (_input: Record<string, never>, config: LangGraphRunnableConfig) => {
167-
const userId = config.configurable?.userId as string | undefined;
168-
if (!userId) {
169-
return 'Cannot start SAP Jira connection: user is not signed in.';
170-
}
171-
const userServers = (config.configurable?.userMcpServers ?? []) as UserMcpServerConfig[];
172-
const sapJira = userServers.find((s) => s.url === SAP_JIRA_URL);
173-
if (!sapJira) {
174-
return 'SAP Jira is not provisioned for this user yet. Ask the user to open Settings → Apps once so it can be seeded.';
175-
}
176-
// Tokens may have arrived since this agent run started (e.g. user just
177-
// completed loopback connect). Check Next.js, not our own cache.
178-
if (await isSapJiraConnected(sapJira.id, userId)) {
179-
return 'SAP Jira IS connected. Tools are loading on the next message — tell the user to retry their request.';
180-
}
181-
const authUrl = `/api/mcp/servers/${sapJira.id}/auth`;
182-
config.writer?.({ type: 'requiresMcpAuth', server: 'sap-jira', authUrl });
183-
return 'SAP Jira is not connected yet. Tell the user to click the "Connect Jira" button shown in the chat to authorize, then ask them to retry their request.';
184-
},
185-
{
186-
name: 'sap_jira_connect',
187-
description: 'Initiate the SAP Jira OAuth flow when the user asks for Jira data but no sap_jira_* tools are available yet. Emits a UI prompt asking the user to authorize. Call once, then stop and wait for the user to retry.',
188-
schema: z.object({}).describe('No arguments.'),
189-
},
190-
);
191-
192140
// ─── Dynamic System Prompt ──────────────────────────────────────────────────
193141

194142
// Inject the calling user's MCP tools at model-call time. Server configs
@@ -243,21 +191,13 @@ const userMcpToolsMiddleware = createMiddleware({
243191
},
244192
});
245193

246-
const SAP_JIRA_TOOL_HINT = `
247-
248-
## SAP Jira Tools
249-
250-
If the user asks about Jira issues / SAP tickets / sprints / boards and you do NOT see any tool names containing \`jira\` (e.g. \`SAP_Jira__jira_*\`) in your toolset, the user has not connected SAP Jira yet. Call \`sap_jira_connect\` once, then stop and wait — a "Connect Jira" button will appear in the UI for them to authorize. Do not retry until they confirm they've connected.`;
251-
252194
function buildPrompt(templateType: TemplateType, sbxId?: string, sandboxUrl?: string): string {
253195
if (templateType === 'chat') {
254-
// chat-agent-prompt already includes the SAP Jira hint, so no append.
255196
return createChatPrompt();
256197
}
257-
const base = templateType === 'n8n'
198+
return templateType === 'n8n'
258199
? createN8nPrompt(sbxId, sandboxUrl)
259200
: createNextjsPrompt(sbxId, sandboxUrl);
260-
return base + SAP_JIRA_TOOL_HINT;
261201
}
262202

263203
const sandboxAwarePrompt = dynamicSystemPromptMiddleware(
@@ -320,7 +260,7 @@ const n8nMcpToolsMiddleware = createMiddleware({
320260

321261
export const agent = createAgent({
322262
model,
323-
tools: [setTemplateTool, createSandboxTool, sapJiraConnectTool, ...e2bTools],
263+
tools: [setTemplateTool, createSandboxTool, ...e2bTools],
324264
middleware: [
325265
sandboxAwarePrompt,
326266
n8nMcpToolsMiddleware,

src/lib/chat-agent-prompt.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,14 @@ export function createChatPrompt(): string {
33
44
## What you do
55
6-
Answer questions, look things up, summarize, plan, explain. Use the MCP tools available to you when relevant. Built-in tools include Next.js docs lookup and n8n workflow knowledge. Users may also have added their own MCP servers (SAP Jira, GitHub, Linear, internal tools, etc.) via Settings → Apps; their tools are prefixed with the server name they chose (e.g. \`SAP_Jira__search_issues\`).
6+
Answer questions, look things up, summarize, plan, explain. Use the MCP tools available to you when relevant. Built-in tools include Next.js docs lookup and n8n workflow knowledge. Users may also have added their own MCP servers (GitHub, Linear, Jira, internal tools, etc.) via Settings → Apps; their tools are prefixed with the server name they chose. If the user asks about data from a service whose tools you don't currently have, tell them to connect it under Settings → Apps and stop — don't fabricate answers.
77
88
## What you DO NOT do
99
1010
- Do NOT call \`create_sandbox\` or any \`e2b_*\` tools. There is no sandbox in this mode.
1111
- Do NOT write code files unless the user pastes some and asks you to modify it inline as text in your reply.
1212
- Do NOT call \`set_template\` again — the user has already picked chat mode for this thread.
1313
14-
## SAP Jira
15-
16-
If the user asks about Jira issues / SAP tickets / sprints / boards and you do NOT see any tool names containing \`jira\` (e.g. \`SAP_Jira__jira_*\`) in your toolset, the user has not connected SAP Jira yet. Call \`sap_jira_connect\` once, then stop and wait — a "Connect Jira" button will appear in the UI for them to authorize. Do not retry until they confirm they've connected.
17-
1814
## Style
1915
2016
Be direct and concise. Show source IDs (e.g., issue keys, URLs) so the user can verify. If a question is ambiguous, ask one clarifying question rather than guessing.`;

src/lib/mcp-client.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ const cleanEnv = (extra: Record<string, string> = {}): Record<string, string> =>
88
return { ...out, ...extra };
99
};
1010

11-
// System-managed MCP servers shared by all users. User-added servers (incl.
12-
// SAP Jira) live per-user in McpServerConfig and are loaded by
13-
// getUserMcpTools() — see mcp-user-store.ts.
11+
// System-managed MCP servers shared by all users. User-added servers live
12+
// per-user in McpServerConfig and are loaded by getUserMcpTools() — see
13+
// mcp-user-store.ts.
1414
const client = new MultiServerMCPClient({
1515
throwOnLoadError: false,
1616
prefixToolNameWithServerName: false,

src/lib/mcp-user-store.ts

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ function appUrl(): string {
3636
}
3737

3838
// Servers whose IdP only accepts loopback redirect URIs (RFC 8252) because
39-
// the BTP / prod host isn't on their allowlist. For these we skip the normal
40-
// callback route and have the user paste the loopback URL back into the UI
41-
// after IAS dumps them on a dead localhost page.
39+
// their host allowlist won't include our prod URL. The user adds these via
40+
// Settings → Apps; OAuth is then completed by pasting the loopback URL back.
4241
const LOOPBACK_PORT = 33418;
4342
export const LOOPBACK_REDIRECT_URI = `http://127.0.0.1:${LOOPBACK_PORT}/callback`;
4443
const LOOPBACK_HOST_URLS = new Set<string>([
@@ -147,37 +146,6 @@ export async function deleteUserServer(userId: string, id: string) {
147146
return res.count > 0;
148147
}
149148

150-
// Pre-populate the well-known SAP Jira MCP server for a user the first time
151-
// they hit settings. Keeps the per-user model clean while not making every
152-
// new user re-discover the URL by hand.
153-
const SAP_JIRA_NAME = 'SAP Jira';
154-
const SAP_JIRA_URL = 'https://mcp.jira.tools.sap/mcp';
155-
156-
export async function ensureSapJiraSeeded(userId: string) {
157-
const existing = await prisma.mcpServerConfig.findFirst({
158-
where: { userId, url: SAP_JIRA_URL },
159-
select: { id: true },
160-
});
161-
if (existing) return existing.id;
162-
const row = await prisma.mcpServerConfig.create({
163-
data: {
164-
userId,
165-
name: SAP_JIRA_NAME,
166-
url: SAP_JIRA_URL,
167-
authType: 'oauth',
168-
},
169-
});
170-
return row.id;
171-
}
172-
173-
export async function getSapJiraServerId(userId: string): Promise<string | null> {
174-
const row = await prisma.mcpServerConfig.findFirst({
175-
where: { userId, url: SAP_JIRA_URL },
176-
select: { id: true },
177-
});
178-
return row?.id ?? null;
179-
}
180-
181149
export function decryptBearer(row: { authType: string; encryptedBearerToken: string | null }): string | undefined {
182150
if (row.authType !== 'bearer' || !row.encryptedBearerToken) return undefined;
183151
return decrypt(row.encryptedBearerToken);

0 commit comments

Comments
 (0)