@songyaeji/security-paper-mcp-server 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/README.md +112 -0
- package/dist/conferences.json +115 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +170 -0
- package/dist/paperSearch.d.ts +22 -0
- package/dist/paperSearch.js +187 -0
- package/dist/types.d.ts +65 -0
- package/dist/types.js +1 -0
- package/package.json +60 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Security Paper Search MCP Server
|
|
2
|
+
|
|
3
|
+
보안 학회 논문을 검색하는 MCP (Model Context Protocol) 서버입니다.
|
|
4
|
+
|
|
5
|
+
## 지원 컨퍼런스
|
|
6
|
+
|
|
7
|
+
### Top-Tier (4대 학회)
|
|
8
|
+
- **S&P** (IEEE Symposium on Security and Privacy)
|
|
9
|
+
- **USENIX Security** (USENIX Security Symposium)
|
|
10
|
+
- **CCS** (ACM Conference on Computer and Communications Security)
|
|
11
|
+
- **NDSS** (Network and Distributed System Security Symposium)
|
|
12
|
+
|
|
13
|
+
### Second-Tier
|
|
14
|
+
- **ACSAC** (Annual Computer Security Applications Conference)
|
|
15
|
+
- **RAID** (Research in Attacks, Intrusions and Defenses)
|
|
16
|
+
- **ESORICS** (European Symposium on Research in Computer Security)
|
|
17
|
+
|
|
18
|
+
## 설치
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
npm run build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Claude Code에서 사용하기
|
|
26
|
+
|
|
27
|
+
`~/.claude/claude_desktop_config.json` 또는 `settings.json`에 추가:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"security-papers": {
|
|
33
|
+
"command": "node",
|
|
34
|
+
"args": ["<절대경로>/security-paper-mcp-server/dist/index.js"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 제공 도구 (Tools)
|
|
41
|
+
|
|
42
|
+
### 1. search_papers
|
|
43
|
+
논문 검색 (키워드, 저자, 연도, 컨퍼런스 필터)
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
search_papers(
|
|
47
|
+
keyword: "fuzzing", // 검색 키워드
|
|
48
|
+
author: "John Smith", // 저자명
|
|
49
|
+
yearFrom: 2022, // 시작 연도
|
|
50
|
+
yearTo: 2024, // 종료 연도
|
|
51
|
+
conferences: ["sp", "ccs"], // 특정 컨퍼런스
|
|
52
|
+
tier: "top", // top, second, all
|
|
53
|
+
limit: 50 // 최대 결과 수
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. get_conference_papers
|
|
58
|
+
특정 컨퍼런스의 특정 연도 논문 조회
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
get_conference_papers(
|
|
62
|
+
conference: "sp", // sp, usenix, ccs, ndss, acsac, raid, esorics
|
|
63
|
+
year: 2024
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. list_conferences
|
|
68
|
+
지원되는 컨퍼런스 목록 확인
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
list_conferences()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 4. get_stats
|
|
75
|
+
검색 결과 통계 (연도별, 컨퍼런스별)
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
get_stats(
|
|
79
|
+
keyword: "malware",
|
|
80
|
+
tier: "top"
|
|
81
|
+
)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## 사용 예시
|
|
85
|
+
|
|
86
|
+
Claude에게 다음과 같이 요청하면 됩니다:
|
|
87
|
+
|
|
88
|
+
- "2024년 S&P 논문 중 fuzzing 관련 논문 찾아줘"
|
|
89
|
+
- "CCS와 NDSS에서 발표된 malware 분석 논문 검색해줘"
|
|
90
|
+
- "2022년부터 2024년까지 top-tier 학회의 LLM 보안 관련 논문 찾아줘"
|
|
91
|
+
- "USENIX Security 2023 논문 목록 보여줘"
|
|
92
|
+
|
|
93
|
+
## 데이터 소스
|
|
94
|
+
|
|
95
|
+
이 서버는 [DBLP](https://dblp.org) API를 사용하여 논문 정보를 검색합니다.
|
|
96
|
+
|
|
97
|
+
## 개발
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# 개발 모드 실행
|
|
101
|
+
npm run dev
|
|
102
|
+
|
|
103
|
+
# 빌드
|
|
104
|
+
npm run build
|
|
105
|
+
|
|
106
|
+
# 실행
|
|
107
|
+
npm start
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 라이선스
|
|
111
|
+
|
|
112
|
+
MIT
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
{
|
|
2
|
+
"conferences": {
|
|
3
|
+
"top_tier": {
|
|
4
|
+
"sp": {
|
|
5
|
+
"name": "IEEE Symposium on Security and Privacy (S&P/Oakland)",
|
|
6
|
+
"shortName": "S&P",
|
|
7
|
+
"tier": "top",
|
|
8
|
+
"urls": {
|
|
9
|
+
"2020": "https://www.ieee-security.org/TC/SP2020/program-papers.html",
|
|
10
|
+
"2021": "https://www.ieee-security.org/TC/SP2021/program-papers.html",
|
|
11
|
+
"2022": "https://www.ieee-security.org/TC/SP2022/program-papers.html",
|
|
12
|
+
"2023": "https://www.ieee-security.org/TC/SP2023/program-papers.html",
|
|
13
|
+
"2024": "https://www.ieee-security.org/TC/SP2024/program-papers.html",
|
|
14
|
+
"2025": "https://www.ieee-security.org/TC/SP2025/program-papers.html"
|
|
15
|
+
},
|
|
16
|
+
"dblpKey": "conf/sp"
|
|
17
|
+
},
|
|
18
|
+
"usenix": {
|
|
19
|
+
"name": "USENIX Security Symposium",
|
|
20
|
+
"shortName": "USENIX Security",
|
|
21
|
+
"tier": "top",
|
|
22
|
+
"urls": {
|
|
23
|
+
"2020": "https://www.usenix.org/conference/usenixsecurity20/technical-sessions",
|
|
24
|
+
"2021": "https://www.usenix.org/conference/usenixsecurity21/technical-sessions",
|
|
25
|
+
"2022": "https://www.usenix.org/conference/usenixsecurity22/technical-sessions",
|
|
26
|
+
"2023": "https://www.usenix.org/conference/usenixsecurity23/technical-sessions",
|
|
27
|
+
"2024": "https://www.usenix.org/conference/usenixsecurity24/technical-sessions",
|
|
28
|
+
"2025": "https://www.usenix.org/conference/usenixsecurity25/technical-sessions"
|
|
29
|
+
},
|
|
30
|
+
"dblpKey": "conf/uss"
|
|
31
|
+
},
|
|
32
|
+
"ccs": {
|
|
33
|
+
"name": "ACM Conference on Computer and Communications Security (CCS)",
|
|
34
|
+
"shortName": "CCS",
|
|
35
|
+
"tier": "top",
|
|
36
|
+
"urls": {
|
|
37
|
+
"2020": "https://www.sigsac.org/ccs/CCS2020/accepted-papers.html",
|
|
38
|
+
"2021": "https://www.sigsac.org/ccs/CCS2021/accepted-papers.html",
|
|
39
|
+
"2022": "https://www.sigsac.org/ccs/CCS2022/program/accepted-papers.html",
|
|
40
|
+
"2023": "https://www.sigsac.org/ccs/CCS2023/tocs/tocs-ccs23.html",
|
|
41
|
+
"2024": "https://www.sigsac.org/ccs/CCS2024/program/accepted-papers.html",
|
|
42
|
+
"2025": "https://www.sigsac.org/ccs/CCS2025/accepted-papers/"
|
|
43
|
+
},
|
|
44
|
+
"dblpKey": "conf/ccs"
|
|
45
|
+
},
|
|
46
|
+
"ndss": {
|
|
47
|
+
"name": "Network and Distributed System Security Symposium (NDSS)",
|
|
48
|
+
"shortName": "NDSS",
|
|
49
|
+
"tier": "top",
|
|
50
|
+
"urls": {
|
|
51
|
+
"2020": "https://www.ndss-symposium.org/ndss2020/accepted-papers/",
|
|
52
|
+
"2021": "https://www.ndss-symposium.org/ndss2021/accepted-papers/",
|
|
53
|
+
"2022": "https://www.ndss-symposium.org/ndss2022/accepted-papers/",
|
|
54
|
+
"2023": "https://www.ndss-symposium.org/ndss2023/accepted-papers/",
|
|
55
|
+
"2024": "https://www.ndss-symposium.org/ndss2024/accepted-papers/",
|
|
56
|
+
"2025": "https://www.ndss-symposium.org/ndss2025/accepted-papers/"
|
|
57
|
+
},
|
|
58
|
+
"dblpKey": "conf/ndss"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"second_tier": {
|
|
62
|
+
"acsac": {
|
|
63
|
+
"name": "Annual Computer Security Applications Conference (ACSAC)",
|
|
64
|
+
"shortName": "ACSAC",
|
|
65
|
+
"tier": "second",
|
|
66
|
+
"urls": {
|
|
67
|
+
"2020": "https://www.acsac.org/2020/program/papers/",
|
|
68
|
+
"2021": "https://www.acsac.org/2021/program/papers/",
|
|
69
|
+
"2022": "https://www.acsac.org/2022/program/papers/",
|
|
70
|
+
"2023": "https://www.acsac.org/2023/program/papers/",
|
|
71
|
+
"2024": "https://www.acsac.org/2024/program/"
|
|
72
|
+
},
|
|
73
|
+
"dblpKey": "conf/acsac"
|
|
74
|
+
},
|
|
75
|
+
"raid": {
|
|
76
|
+
"name": "International Symposium on Research in Attacks, Intrusions and Defenses (RAID)",
|
|
77
|
+
"shortName": "RAID",
|
|
78
|
+
"tier": "second",
|
|
79
|
+
"urls": {
|
|
80
|
+
"2020": "https://raid2020.org/accepted-papers/",
|
|
81
|
+
"2021": "https://dl.acm.org/doi/proceedings/10.1145/3471621",
|
|
82
|
+
"2022": "https://dblp.org/db/conf/raid/raid2022.html",
|
|
83
|
+
"2023": "https://dblp.org/db/conf/raid/raid2023.html",
|
|
84
|
+
"2024": "https://raid2024.github.io/accepted_open.html"
|
|
85
|
+
},
|
|
86
|
+
"dblpKey": "conf/raid"
|
|
87
|
+
},
|
|
88
|
+
"esorics": {
|
|
89
|
+
"name": "European Symposium on Research in Computer Security (ESORICS)",
|
|
90
|
+
"shortName": "ESORICS",
|
|
91
|
+
"tier": "second",
|
|
92
|
+
"urls": {
|
|
93
|
+
"2020": "https://dblp.org/db/conf/esorics/esorics2020-1.html",
|
|
94
|
+
"2021": "https://esorics2021.athene-center.de/program.php",
|
|
95
|
+
"2022": "https://esorics2022.compute.dtu.dk/program.html",
|
|
96
|
+
"2023": "https://esorics2023.org/program/",
|
|
97
|
+
"2024": "https://dblp.org/db/conf/esorics/esorics2024-1.html"
|
|
98
|
+
},
|
|
99
|
+
"dblpKey": "conf/esorics"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"dblpBaseUrl": "https://dblp.org/search/publ/api",
|
|
104
|
+
"alternativeSources": {
|
|
105
|
+
"dblp": {
|
|
106
|
+
"description": "DBLP Computer Science Bibliography - comprehensive paper database",
|
|
107
|
+
"searchUrl": "https://dblp.org/search/publ/api?q={query}+venue:{venue}&format=json",
|
|
108
|
+
"venueUrl": "https://dblp.org/db/conf/{venue}/index.html"
|
|
109
|
+
},
|
|
110
|
+
"semanticScholar": {
|
|
111
|
+
"description": "Semantic Scholar API for paper metadata",
|
|
112
|
+
"searchUrl": "https://api.semanticscholar.org/graph/v1/paper/search"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import * as z from 'zod';
|
|
5
|
+
import { searchPapers, searchByConference, listAvailableConferences, getAvailableYears, getConferenceInfo } from './paperSearch.js';
|
|
6
|
+
const server = new McpServer({
|
|
7
|
+
name: 'security-paper-search',
|
|
8
|
+
version: '1.0.0'
|
|
9
|
+
});
|
|
10
|
+
// Tool 1: Search papers with various filters
|
|
11
|
+
server.registerTool('search_papers', {
|
|
12
|
+
title: 'Search Security Papers',
|
|
13
|
+
description: `Search for security research papers from top-tier (S&P, USENIX Security, CCS, NDSS) and second-tier (ACSAC, RAID, ESORICS) conferences.
|
|
14
|
+
|
|
15
|
+
Supports filtering by:
|
|
16
|
+
- keyword: Search in paper titles
|
|
17
|
+
- author: Author name
|
|
18
|
+
- yearFrom/yearTo: Year range (2020-present)
|
|
19
|
+
- conferences: Specific conference keys (sp, usenix, ccs, ndss, acsac, raid, esorics)
|
|
20
|
+
- tier: "top", "second", or "all"`,
|
|
21
|
+
inputSchema: {
|
|
22
|
+
keyword: z.string().optional().describe('Keyword to search in paper titles'),
|
|
23
|
+
author: z.string().optional().describe('Author name to search'),
|
|
24
|
+
yearFrom: z.number().min(2020).max(2026).optional().describe('Start year (default: 2020)'),
|
|
25
|
+
yearTo: z.number().min(2020).max(2026).optional().describe('End year (default: current year)'),
|
|
26
|
+
conferences: z.array(z.string()).optional().describe('Conference keys: sp, usenix, ccs, ndss, acsac, raid, esorics'),
|
|
27
|
+
tier: z.enum(['top', 'second', 'all']).optional().describe('Conference tier filter'),
|
|
28
|
+
limit: z.number().min(1).max(200).optional().describe('Max results (default: 50)')
|
|
29
|
+
}
|
|
30
|
+
}, async (args) => {
|
|
31
|
+
try {
|
|
32
|
+
const papers = await searchPapers({
|
|
33
|
+
keyword: args.keyword,
|
|
34
|
+
author: args.author,
|
|
35
|
+
yearFrom: args.yearFrom || 2020,
|
|
36
|
+
yearTo: args.yearTo || new Date().getFullYear(),
|
|
37
|
+
conferences: args.conferences,
|
|
38
|
+
tier: args.tier || 'all',
|
|
39
|
+
limit: args.limit || 50
|
|
40
|
+
});
|
|
41
|
+
const resultText = papers.length > 0
|
|
42
|
+
? `Found ${papers.length} papers:\n\n${papers.map((p, i) => `${i + 1}. "${p.title}"\n Authors: ${p.authors.join(', ')}\n Conference: ${p.conference} ${p.year} (${p.tier}-tier)\n Paper URL: ${p.url || 'N/A'}${p.conferenceUrl ? `\n Conference Page: ${p.conferenceUrl}` : ''}`).join('\n\n')}`
|
|
43
|
+
: 'No papers found matching your criteria.';
|
|
44
|
+
return {
|
|
45
|
+
content: [{ type: 'text', text: resultText }]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
content: [{ type: 'text', text: `Error searching papers: ${error}` }],
|
|
51
|
+
isError: true
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
// Tool 2: Get papers from a specific conference and year
|
|
56
|
+
server.registerTool('get_conference_papers', {
|
|
57
|
+
title: 'Get Conference Papers',
|
|
58
|
+
description: 'Get all papers from a specific security conference in a specific year.',
|
|
59
|
+
inputSchema: {
|
|
60
|
+
conference: z.enum(['sp', 'usenix', 'ccs', 'ndss', 'acsac', 'raid', 'esorics'])
|
|
61
|
+
.describe('Conference key'),
|
|
62
|
+
year: z.number().min(2020).max(2026)
|
|
63
|
+
.describe('Conference year')
|
|
64
|
+
}
|
|
65
|
+
}, async ({ conference, year }) => {
|
|
66
|
+
try {
|
|
67
|
+
const confInfo = getConferenceInfo(conference);
|
|
68
|
+
const papers = await searchByConference(conference, year);
|
|
69
|
+
const resultText = papers.length > 0
|
|
70
|
+
? `${confInfo?.name || conference} ${year} - Found ${papers.length} papers:\n\n${papers.map((p, i) => `${i + 1}. "${p.title}"\n Authors: ${p.authors.join(', ')}\n Paper URL: ${p.url || 'N/A'}${p.conferenceUrl ? `\n Conference Page: ${p.conferenceUrl}` : ''}`).join('\n\n')}`
|
|
71
|
+
: `No papers found for ${conference} ${year}.`;
|
|
72
|
+
return {
|
|
73
|
+
content: [{ type: 'text', text: resultText }]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
79
|
+
isError: true
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
// Tool 3: List available conferences
|
|
84
|
+
server.registerTool('list_conferences', {
|
|
85
|
+
title: 'List Available Conferences',
|
|
86
|
+
description: 'List all supported security conferences with their keys and full names.',
|
|
87
|
+
inputSchema: {}
|
|
88
|
+
}, async () => {
|
|
89
|
+
const { topTier, secondTier } = listAvailableConferences();
|
|
90
|
+
const years = getAvailableYears();
|
|
91
|
+
const text = `## Supported Security Conferences
|
|
92
|
+
|
|
93
|
+
### Top-Tier (Big 4)
|
|
94
|
+
${topTier.map(c => `- **${c.shortName}** (key: \`${c.key}\`)\n ${c.name}\n Available years: ${c.years.join(', ')}`).join('\n')}
|
|
95
|
+
|
|
96
|
+
### Second-Tier
|
|
97
|
+
${secondTier.map(c => `- **${c.shortName}** (key: \`${c.key}\`)\n ${c.name}\n Available years: ${c.years.join(', ')}`).join('\n')}
|
|
98
|
+
|
|
99
|
+
### Data Source
|
|
100
|
+
- Primary: DBLP (reliable, structured API)
|
|
101
|
+
- Conference URLs: Reference links to official conference pages
|
|
102
|
+
|
|
103
|
+
### Available Years
|
|
104
|
+
${years.join(', ')}
|
|
105
|
+
|
|
106
|
+
### Usage Examples
|
|
107
|
+
- Search by keyword: \`search_papers(keyword: "fuzzing")\`
|
|
108
|
+
- Search by author: \`search_papers(author: "John Smith")\`
|
|
109
|
+
- Filter by year: \`search_papers(yearFrom: 2023, yearTo: 2024)\`
|
|
110
|
+
- Specific conference: \`get_conference_papers(conference: "sp", year: 2024)\`
|
|
111
|
+
- Top-tier only: \`search_papers(keyword: "malware", tier: "top")\``;
|
|
112
|
+
return {
|
|
113
|
+
content: [{ type: 'text', text: text }]
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
// Tool 4: Get conference statistics
|
|
117
|
+
server.registerTool('get_stats', {
|
|
118
|
+
title: 'Get Paper Statistics',
|
|
119
|
+
description: 'Get statistics about papers matching search criteria (count by year, conference, etc.)',
|
|
120
|
+
inputSchema: {
|
|
121
|
+
keyword: z.string().optional().describe('Keyword to search'),
|
|
122
|
+
tier: z.enum(['top', 'second', 'all']).optional().describe('Conference tier')
|
|
123
|
+
}
|
|
124
|
+
}, async ({ keyword, tier }) => {
|
|
125
|
+
try {
|
|
126
|
+
const papers = await searchPapers({
|
|
127
|
+
keyword,
|
|
128
|
+
tier: tier || 'all',
|
|
129
|
+
limit: 200
|
|
130
|
+
});
|
|
131
|
+
// Group by year
|
|
132
|
+
const byYear = {};
|
|
133
|
+
const byConference = {};
|
|
134
|
+
for (const paper of papers) {
|
|
135
|
+
byYear[paper.year] = (byYear[paper.year] || 0) + 1;
|
|
136
|
+
byConference[paper.conference] = (byConference[paper.conference] || 0) + 1;
|
|
137
|
+
}
|
|
138
|
+
const text = `## Paper Statistics${keyword ? ` for "${keyword}"` : ''}
|
|
139
|
+
|
|
140
|
+
**Total papers found:** ${papers.length}
|
|
141
|
+
|
|
142
|
+
### By Year
|
|
143
|
+
${Object.entries(byYear)
|
|
144
|
+
.sort(([a], [b]) => Number(b) - Number(a))
|
|
145
|
+
.map(([year, count]) => `- ${year}: ${count} papers`)
|
|
146
|
+
.join('\n')}
|
|
147
|
+
|
|
148
|
+
### By Conference
|
|
149
|
+
${Object.entries(byConference)
|
|
150
|
+
.sort(([, a], [, b]) => b - a)
|
|
151
|
+
.map(([conf, count]) => `- ${conf}: ${count} papers`)
|
|
152
|
+
.join('\n')}`;
|
|
153
|
+
return {
|
|
154
|
+
content: [{ type: 'text', text: text }]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
160
|
+
isError: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
// Start the server
|
|
165
|
+
async function main() {
|
|
166
|
+
const transport = new StdioServerTransport();
|
|
167
|
+
await server.connect(transport);
|
|
168
|
+
console.error('Security Paper Search MCP Server running on stdio');
|
|
169
|
+
}
|
|
170
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Paper, SearchOptions, ConferenceInfo } from './types.js';
|
|
2
|
+
export declare function getAllConferences(): Record<string, ConferenceInfo>;
|
|
3
|
+
export declare function getConferencesByTier(tier: 'top' | 'second' | 'all'): Record<string, ConferenceInfo>;
|
|
4
|
+
export declare function getConferenceInfo(key: string): ConferenceInfo | undefined;
|
|
5
|
+
export declare function getConferenceWebsiteUrl(key: string, year: number): string | undefined;
|
|
6
|
+
export declare function searchPapers(options: SearchOptions): Promise<Paper[]>;
|
|
7
|
+
export declare function searchByConference(conferenceKey: string, year: number): Promise<Paper[]>;
|
|
8
|
+
export declare function listAvailableConferences(): {
|
|
9
|
+
topTier: {
|
|
10
|
+
key: string;
|
|
11
|
+
name: string;
|
|
12
|
+
shortName: string;
|
|
13
|
+
years: string[];
|
|
14
|
+
}[];
|
|
15
|
+
secondTier: {
|
|
16
|
+
key: string;
|
|
17
|
+
name: string;
|
|
18
|
+
shortName: string;
|
|
19
|
+
years: string[];
|
|
20
|
+
}[];
|
|
21
|
+
};
|
|
22
|
+
export declare function getAvailableYears(): number[];
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import conferencesData from './conferences.json' with { type: 'json' };
|
|
2
|
+
const conferences = conferencesData;
|
|
3
|
+
// DBLP venue key mappings for accurate matching
|
|
4
|
+
const DBLP_VENUE_MAPPINGS = {
|
|
5
|
+
'sp': ['sp', 'ieee symposium on security and privacy', 's&p', 'oakland'],
|
|
6
|
+
'usenix': ['uss', 'usenix security', 'usenix security symposium'],
|
|
7
|
+
'ccs': ['ccs', 'acm ccs', 'computer and communications security'],
|
|
8
|
+
'ndss': ['ndss', 'network and distributed system security'],
|
|
9
|
+
'acsac': ['acsac', 'annual computer security applications'],
|
|
10
|
+
'raid': ['raid', 'research in attacks', 'intrusions and defenses'],
|
|
11
|
+
'esorics': ['esorics', 'european symposium on research in computer security']
|
|
12
|
+
};
|
|
13
|
+
export function getAllConferences() {
|
|
14
|
+
return {
|
|
15
|
+
...conferences.conferences.top_tier,
|
|
16
|
+
...conferences.conferences.second_tier
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function getConferencesByTier(tier) {
|
|
20
|
+
if (tier === 'top') {
|
|
21
|
+
return conferences.conferences.top_tier;
|
|
22
|
+
}
|
|
23
|
+
else if (tier === 'second') {
|
|
24
|
+
return conferences.conferences.second_tier;
|
|
25
|
+
}
|
|
26
|
+
return getAllConferences();
|
|
27
|
+
}
|
|
28
|
+
export function getConferenceInfo(key) {
|
|
29
|
+
const all = getAllConferences();
|
|
30
|
+
return all[key.toLowerCase()];
|
|
31
|
+
}
|
|
32
|
+
// Get conference website URL for a specific year
|
|
33
|
+
export function getConferenceWebsiteUrl(key, year) {
|
|
34
|
+
const conf = getConferenceInfo(key);
|
|
35
|
+
if (!conf)
|
|
36
|
+
return undefined;
|
|
37
|
+
return conf.urls[year.toString()];
|
|
38
|
+
}
|
|
39
|
+
function buildDblpQuery(options) {
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (options.keyword) {
|
|
42
|
+
parts.push(options.keyword);
|
|
43
|
+
}
|
|
44
|
+
if (options.author) {
|
|
45
|
+
parts.push(`author:${options.author}`);
|
|
46
|
+
}
|
|
47
|
+
// Build venue filter
|
|
48
|
+
const targetConferences = options.conferences?.length
|
|
49
|
+
? options.conferences
|
|
50
|
+
: Object.keys(getConferencesByTier(options.tier || 'all'));
|
|
51
|
+
const venueQueries = targetConferences
|
|
52
|
+
.map(c => {
|
|
53
|
+
const conf = getConferenceInfo(c);
|
|
54
|
+
return conf?.dblpKey;
|
|
55
|
+
})
|
|
56
|
+
.filter(Boolean);
|
|
57
|
+
if (venueQueries.length > 0) {
|
|
58
|
+
// DBLP uses venue: prefix for conference filtering
|
|
59
|
+
const venueFilter = venueQueries.map(v => `venue:${v}:`).join('|');
|
|
60
|
+
parts.push(venueFilter);
|
|
61
|
+
}
|
|
62
|
+
return parts.join(' ');
|
|
63
|
+
}
|
|
64
|
+
// Improved venue matching using multiple identifiers
|
|
65
|
+
function matchConference(venue) {
|
|
66
|
+
const venueLower = venue.toLowerCase();
|
|
67
|
+
const allConfs = getAllConferences();
|
|
68
|
+
for (const [key, conf] of Object.entries(allConfs)) {
|
|
69
|
+
// Check against DBLP key
|
|
70
|
+
if (venueLower.includes(conf.dblpKey)) {
|
|
71
|
+
return { key, conf };
|
|
72
|
+
}
|
|
73
|
+
// Check against known venue mappings
|
|
74
|
+
const mappings = DBLP_VENUE_MAPPINGS[key] || [];
|
|
75
|
+
for (const mapping of mappings) {
|
|
76
|
+
if (venueLower.includes(mapping)) {
|
|
77
|
+
return { key, conf };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// Check against short name
|
|
81
|
+
if (venueLower.includes(key) || venueLower.includes(conf.shortName.toLowerCase())) {
|
|
82
|
+
return { key, conf };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
export async function searchPapers(options) {
|
|
88
|
+
const query = buildDblpQuery(options);
|
|
89
|
+
const limit = options.limit || 50;
|
|
90
|
+
const url = new URL('https://dblp.org/search/publ/api');
|
|
91
|
+
url.searchParams.set('q', query);
|
|
92
|
+
url.searchParams.set('format', 'json');
|
|
93
|
+
url.searchParams.set('h', limit.toString()); // max hits
|
|
94
|
+
try {
|
|
95
|
+
const response = await fetch(url.toString(), {
|
|
96
|
+
headers: {
|
|
97
|
+
'User-Agent': 'SecurityPaperMCP/1.0'
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok) {
|
|
101
|
+
throw new Error(`DBLP API error: ${response.status}`);
|
|
102
|
+
}
|
|
103
|
+
const data = await response.json();
|
|
104
|
+
const hits = data.result?.hits?.hit || [];
|
|
105
|
+
const papers = [];
|
|
106
|
+
for (const hit of hits) {
|
|
107
|
+
const info = hit.info;
|
|
108
|
+
const year = parseInt(info.year, 10);
|
|
109
|
+
// Apply year filter
|
|
110
|
+
if (options.yearFrom && year < options.yearFrom)
|
|
111
|
+
continue;
|
|
112
|
+
if (options.yearTo && year > options.yearTo)
|
|
113
|
+
continue;
|
|
114
|
+
// Extract authors
|
|
115
|
+
let authors = [];
|
|
116
|
+
if (info.authors?.author) {
|
|
117
|
+
const authorData = info.authors.author;
|
|
118
|
+
if (Array.isArray(authorData)) {
|
|
119
|
+
authors = authorData.map(a => a.text);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
authors = [authorData.text];
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Match conference using improved matching
|
|
126
|
+
const venue = info.venue || '';
|
|
127
|
+
const matched = matchConference(venue);
|
|
128
|
+
// Filter by requested conferences
|
|
129
|
+
if (options.conferences?.length && matched && !options.conferences.includes(matched.key)) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
// Get conference website URL for reference
|
|
133
|
+
const confWebsiteUrl = matched ? getConferenceWebsiteUrl(matched.key, year) : undefined;
|
|
134
|
+
papers.push({
|
|
135
|
+
title: info.title,
|
|
136
|
+
authors,
|
|
137
|
+
year,
|
|
138
|
+
conference: matched?.conf.shortName || info.venue,
|
|
139
|
+
conferenceFull: matched?.conf.name || info.venue,
|
|
140
|
+
tier: matched?.conf.tier || 'second',
|
|
141
|
+
url: info.ee || info.url,
|
|
142
|
+
doi: info.doi,
|
|
143
|
+
conferenceUrl: confWebsiteUrl // Reference link to conference website
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return papers;
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('Search error:', error);
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export async function searchByConference(conferenceKey, year) {
|
|
154
|
+
const conf = getConferenceInfo(conferenceKey);
|
|
155
|
+
if (!conf) {
|
|
156
|
+
throw new Error(`Unknown conference: ${conferenceKey}`);
|
|
157
|
+
}
|
|
158
|
+
return searchPapers({
|
|
159
|
+
conferences: [conferenceKey],
|
|
160
|
+
yearFrom: year,
|
|
161
|
+
yearTo: year,
|
|
162
|
+
limit: 200
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
export function listAvailableConferences() {
|
|
166
|
+
const topTier = Object.entries(conferences.conferences.top_tier).map(([key, conf]) => ({
|
|
167
|
+
key,
|
|
168
|
+
name: conf.name,
|
|
169
|
+
shortName: conf.shortName,
|
|
170
|
+
years: Object.keys(conf.urls).sort()
|
|
171
|
+
}));
|
|
172
|
+
const secondTier = Object.entries(conferences.conferences.second_tier).map(([key, conf]) => ({
|
|
173
|
+
key,
|
|
174
|
+
name: conf.name,
|
|
175
|
+
shortName: conf.shortName,
|
|
176
|
+
years: Object.keys(conf.urls).sort()
|
|
177
|
+
}));
|
|
178
|
+
return { topTier, secondTier };
|
|
179
|
+
}
|
|
180
|
+
export function getAvailableYears() {
|
|
181
|
+
const currentYear = new Date().getFullYear();
|
|
182
|
+
const years = [];
|
|
183
|
+
for (let y = 2020; y <= currentYear + 1; y++) { // Include next year for upcoming conferences
|
|
184
|
+
years.push(y);
|
|
185
|
+
}
|
|
186
|
+
return years;
|
|
187
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface Paper {
|
|
2
|
+
title: string;
|
|
3
|
+
authors: string[];
|
|
4
|
+
year: number;
|
|
5
|
+
conference: string;
|
|
6
|
+
conferenceFull: string;
|
|
7
|
+
tier: 'top' | 'second';
|
|
8
|
+
url?: string;
|
|
9
|
+
doi?: string;
|
|
10
|
+
abstract?: string;
|
|
11
|
+
conferenceUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ConferenceInfo {
|
|
14
|
+
name: string;
|
|
15
|
+
shortName: string;
|
|
16
|
+
tier: 'top' | 'second';
|
|
17
|
+
urls: Record<string, string>;
|
|
18
|
+
dblpKey: string;
|
|
19
|
+
}
|
|
20
|
+
export interface ConferencesConfig {
|
|
21
|
+
conferences: {
|
|
22
|
+
top_tier: Record<string, ConferenceInfo>;
|
|
23
|
+
second_tier: Record<string, ConferenceInfo>;
|
|
24
|
+
};
|
|
25
|
+
dblpBaseUrl: string;
|
|
26
|
+
alternativeSources: Record<string, {
|
|
27
|
+
description: string;
|
|
28
|
+
searchUrl: string;
|
|
29
|
+
venueUrl?: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export interface SearchOptions {
|
|
33
|
+
keyword?: string;
|
|
34
|
+
author?: string;
|
|
35
|
+
yearFrom?: number;
|
|
36
|
+
yearTo?: number;
|
|
37
|
+
conferences?: string[];
|
|
38
|
+
tier?: 'top' | 'second' | 'all';
|
|
39
|
+
limit?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface DblpHit {
|
|
42
|
+
info: {
|
|
43
|
+
title: string;
|
|
44
|
+
authors?: {
|
|
45
|
+
author: {
|
|
46
|
+
text: string;
|
|
47
|
+
} | {
|
|
48
|
+
text: string;
|
|
49
|
+
}[];
|
|
50
|
+
};
|
|
51
|
+
year: string;
|
|
52
|
+
venue: string;
|
|
53
|
+
url?: string;
|
|
54
|
+
doi?: string;
|
|
55
|
+
ee?: string;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export interface DblpResponse {
|
|
59
|
+
result: {
|
|
60
|
+
hits: {
|
|
61
|
+
hit?: DblpHit[];
|
|
62
|
+
'@total': string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@songyaeji/security-paper-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for searching security conference papers from top-tier (S&P, USENIX Security, CCS, NDSS) and second-tier (ACSAC, RAID, ESORICS) conferences",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"security-paper-mcp-server": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"dev": "tsx src/index.ts",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"security",
|
|
20
|
+
"papers",
|
|
21
|
+
"conference",
|
|
22
|
+
"research",
|
|
23
|
+
"dblp",
|
|
24
|
+
"ieee-sp",
|
|
25
|
+
"usenix-security",
|
|
26
|
+
"acm-ccs",
|
|
27
|
+
"ndss",
|
|
28
|
+
"acsac",
|
|
29
|
+
"raid",
|
|
30
|
+
"esorics",
|
|
31
|
+
"claude",
|
|
32
|
+
"ai-assistant"
|
|
33
|
+
],
|
|
34
|
+
"author": "",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": ""
|
|
39
|
+
},
|
|
40
|
+
"homepage": "",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": ""
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=18.0.0"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"dist",
|
|
49
|
+
"README.md"
|
|
50
|
+
],
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
53
|
+
"zod": "^3.23.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.10.0",
|
|
57
|
+
"tsx": "^4.7.0",
|
|
58
|
+
"typescript": "^5.3.0"
|
|
59
|
+
}
|
|
60
|
+
}
|