mcp-server-opencitations 0.1.0 → 0.1.1
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/dist/index.js +47 -81
- package/dist/lib.js +67 -0
- package/dist/vitest.config.js +13 -0
- package/package.json +9 -4
package/dist/index.js
CHANGED
|
@@ -1,112 +1,78 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// =============================================================================
|
|
3
|
-
// OpenCitations MCP Server
|
|
3
|
+
// OpenCitations MCP Server
|
|
4
4
|
// =============================================================================
|
|
5
|
-
// This is a minimal MCP server to help you learn TypeScript and MCP concepts.
|
|
6
|
-
// Comments explain TypeScript features as we go!
|
|
7
|
-
// -----------------------------------------------------------------------------
|
|
8
|
-
// IMPORTS
|
|
9
|
-
// -----------------------------------------------------------------------------
|
|
10
|
-
// In TypeScript, we import modules using ES6 syntax.
|
|
11
|
-
// The "from" path can be a package name or a relative file path.
|
|
12
|
-
// McpServer is the main class that handles MCP protocol communication
|
|
13
5
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
14
|
-
// StdioServerTransport handles communication over stdin/stdout
|
|
15
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
16
|
-
// 'z' is Zod - a TypeScript-first schema validation library
|
|
17
|
-
// It lets us define the shape of data and validate it at runtime
|
|
18
7
|
import { z } from "zod";
|
|
19
8
|
// -----------------------------------------------------------------------------
|
|
20
|
-
//
|
|
9
|
+
// CONFIGURATION: Environment Variables + Command Line Args
|
|
21
10
|
// -----------------------------------------------------------------------------
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
11
|
+
// Priority: Command line args > Environment variables
|
|
12
|
+
// process.argv is an array of command-line arguments:
|
|
13
|
+
// [0] = path to node
|
|
14
|
+
// [1] = path to script
|
|
15
|
+
// [2+] = user arguments
|
|
16
|
+
//
|
|
17
|
+
// Example: node dist/index.js --token=abc123
|
|
18
|
+
// process.argv[2] would be "--token=abc123"
|
|
19
|
+
function getAccessToken() {
|
|
20
|
+
// Check command line args first
|
|
21
|
+
const tokenArg = process.argv.find(arg => arg.startsWith('--token='));
|
|
22
|
+
if (tokenArg) {
|
|
23
|
+
return tokenArg.split('=')[1];
|
|
24
|
+
}
|
|
25
|
+
// Fall back to environment variable
|
|
26
|
+
return process.env.OPENCITATIONS_ACCESS_TOKEN;
|
|
27
|
+
}
|
|
28
|
+
const ACCESS_TOKEN = getAccessToken();
|
|
29
|
+
if (!ACCESS_TOKEN) {
|
|
30
|
+
console.error("Warning: No access token set.");
|
|
31
|
+
console.error(" Use: --token=YOUR_TOKEN");
|
|
32
|
+
console.error(" Or set OPENCITATIONS_ACCESS_TOKEN environment variable");
|
|
33
|
+
}
|
|
34
|
+
// Server setup
|
|
25
35
|
const server = new McpServer({
|
|
26
|
-
name: "opencitations-server",
|
|
27
|
-
version: "0.1.0",
|
|
36
|
+
name: "opencitations-server",
|
|
37
|
+
version: "0.1.0",
|
|
28
38
|
});
|
|
29
39
|
// -----------------------------------------------------------------------------
|
|
30
|
-
//
|
|
40
|
+
// TOOLS
|
|
31
41
|
// -----------------------------------------------------------------------------
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// Human-readable description - helps LLMs understand when to use this tool
|
|
41
|
-
description: "A simple hello world tool that greets the user",
|
|
42
|
-
// inputSchema defines what parameters this tool accepts
|
|
43
|
-
// We use Zod schemas here - they provide both TypeScript types AND runtime validation
|
|
44
|
-
inputSchema: {
|
|
45
|
-
// z.string() means this parameter must be a string
|
|
46
|
-
// .optional() means it's not required
|
|
47
|
-
// .describe() adds documentation for the LLM
|
|
48
|
-
name: z.string().optional().describe("Name to greet (optional)"),
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
// The handler function - this runs when the tool is called
|
|
52
|
-
// 'async' means this function can use 'await' for asynchronous operations
|
|
53
|
-
// The parameter 'args' is automatically typed based on our inputSchema!
|
|
54
|
-
async (args) => {
|
|
55
|
-
// TypeScript knows 'args.name' is string | undefined because of our schema
|
|
56
|
-
const greeting = args.name
|
|
57
|
-
? `Hello, ${args.name}! Welcome to OpenCitations MCP server.`
|
|
58
|
-
: "Hello! Welcome to OpenCitations MCP server.";
|
|
59
|
-
// Tools return a result object with a 'content' array
|
|
60
|
-
// Each content item has a 'type' (usually "text") and the actual content
|
|
42
|
+
server.registerTool("check_token", {
|
|
43
|
+
description: "Check if the OpenCitations API token is configured",
|
|
44
|
+
inputSchema: {},
|
|
45
|
+
}, async () => {
|
|
46
|
+
// Never expose the actual token! Just confirm if it's set
|
|
47
|
+
const status = ACCESS_TOKEN
|
|
48
|
+
? `Token is set (${ACCESS_TOKEN.length} characters)`
|
|
49
|
+
: "Token is NOT set. Set OPENCITATIONS_ACCESS_TOKEN environment variable.";
|
|
61
50
|
return {
|
|
62
|
-
content: [
|
|
63
|
-
{
|
|
64
|
-
type: "text", // 'as const' is a TypeScript assertion for literal types
|
|
65
|
-
text: greeting,
|
|
66
|
-
},
|
|
67
|
-
],
|
|
51
|
+
content: [{ type: "text", text: status }],
|
|
68
52
|
};
|
|
69
53
|
});
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// -----------------------------------------------------------------------------
|
|
73
|
-
server.registerTool("echo", {
|
|
74
|
-
description: "Echoes back the message you send (useful for testing)",
|
|
54
|
+
server.registerTool("hello", {
|
|
55
|
+
description: "A simple hello world tool",
|
|
75
56
|
inputSchema: {
|
|
76
|
-
|
|
77
|
-
message: z.string().describe("The message to echo back"),
|
|
57
|
+
name: z.string().optional().describe("Name to greet"),
|
|
78
58
|
},
|
|
79
59
|
}, async (args) => {
|
|
80
|
-
|
|
60
|
+
const greeting = args.name
|
|
61
|
+
? `Hello, ${args.name}!`
|
|
62
|
+
: "Hello!";
|
|
81
63
|
return {
|
|
82
|
-
content: [
|
|
83
|
-
{
|
|
84
|
-
type: "text",
|
|
85
|
-
text: `You said: "${args.message}"`,
|
|
86
|
-
},
|
|
87
|
-
],
|
|
64
|
+
content: [{ type: "text", text: greeting }],
|
|
88
65
|
};
|
|
89
66
|
});
|
|
90
67
|
// -----------------------------------------------------------------------------
|
|
91
|
-
// START
|
|
68
|
+
// START SERVER
|
|
92
69
|
// -----------------------------------------------------------------------------
|
|
93
|
-
// This is an async function that starts our MCP server.
|
|
94
|
-
// In TypeScript, we use 'async function' for functions that return Promises.
|
|
95
70
|
async function main() {
|
|
96
|
-
// Promise<void> is the return type - void means we don't return a value
|
|
97
|
-
// The function is async, so it automatically returns a Promise
|
|
98
|
-
// Create a transport layer for stdin/stdout communication
|
|
99
71
|
const transport = new StdioServerTransport();
|
|
100
|
-
// Connect the server to the transport
|
|
101
|
-
// 'await' pauses execution until the Promise resolves
|
|
102
72
|
await server.connect(transport);
|
|
103
|
-
// Log to stderr (not stdout, which is used for MCP protocol messages)
|
|
104
73
|
console.error("OpenCitations MCP Server running on stdio");
|
|
105
74
|
}
|
|
106
|
-
// Run the main function and handle any errors
|
|
107
|
-
// .catch() handles any rejected promises (errors)
|
|
108
75
|
main().catch((error) => {
|
|
109
|
-
// 'unknown' is a safe type for caught errors in TypeScript
|
|
110
76
|
console.error("Fatal error:", error);
|
|
111
|
-
process.exit(1);
|
|
77
|
+
process.exit(1);
|
|
112
78
|
});
|
package/dist/lib.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// lib.ts - Helper functions for OpenCitations MCP Server
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// This file contains reusable functions that your tools can call.
|
|
5
|
+
// Separating logic into lib.ts makes code easier to test and maintain.
|
|
6
|
+
// -----------------------------------------------------------------------------
|
|
7
|
+
// ASYNC FUNCTION EXAMPLE
|
|
8
|
+
// -----------------------------------------------------------------------------
|
|
9
|
+
/**
|
|
10
|
+
* Simulates fetching data (like from an API)
|
|
11
|
+
*
|
|
12
|
+
* In TypeScript:
|
|
13
|
+
* - 'async' keyword makes this function return a Promise automatically
|
|
14
|
+
* - Promise<string> is the return type annotation
|
|
15
|
+
* - Whatever you 'return' becomes the resolved value of the Promise
|
|
16
|
+
*/
|
|
17
|
+
export async function fetchData() {
|
|
18
|
+
// Simulate network delay (like a real API call would have)
|
|
19
|
+
delay(100);
|
|
20
|
+
return 'data from fetchData()';
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A utility function to create a delay (useful for simulating async operations)
|
|
24
|
+
*
|
|
25
|
+
* @param ms - milliseconds to wait
|
|
26
|
+
* @returns Promise that resolves after the delay
|
|
27
|
+
*/
|
|
28
|
+
export function delay(ms) {
|
|
29
|
+
// This is how you create a Promise manually:
|
|
30
|
+
// - 'resolve' is called when the operation succeeds
|
|
31
|
+
// - setTimeout calls resolve after 'ms' milliseconds
|
|
32
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Simulates looking up a citation by DOI
|
|
36
|
+
* Later, this will call the real OpenCitations API
|
|
37
|
+
*
|
|
38
|
+
* @param doi - The DOI to look up (e.g., "10.1234/example")
|
|
39
|
+
* @returns Promise<Citation> - resolves to a Citation object
|
|
40
|
+
* @throws Error if DOI is not found
|
|
41
|
+
*/
|
|
42
|
+
export async function lookupCitation(doi) {
|
|
43
|
+
// Simulate API delay
|
|
44
|
+
await delay(50);
|
|
45
|
+
// For now, return dummy data
|
|
46
|
+
// Later, this will make a real HTTP request to OpenCitations API
|
|
47
|
+
if (!doi || doi.trim() === '') {
|
|
48
|
+
throw new Error('DOI cannot be empty');
|
|
49
|
+
}
|
|
50
|
+
// Simulated response
|
|
51
|
+
return {
|
|
52
|
+
doi: doi,
|
|
53
|
+
title: `Sample Paper for ${doi}`,
|
|
54
|
+
authors: ['Alice Smith', 'Bob Jones'],
|
|
55
|
+
year: 2024,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Formats a Citation object into a readable string
|
|
60
|
+
*
|
|
61
|
+
* @param citation - The citation to format
|
|
62
|
+
* @returns Formatted citation string
|
|
63
|
+
*/
|
|
64
|
+
export function formatCitation(citation) {
|
|
65
|
+
const authorList = citation.authors.join(', ');
|
|
66
|
+
return `${authorList} (${citation.year}). "${citation.title}". DOI: ${citation.doi}`;
|
|
67
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from 'vitest/config';
|
|
2
|
+
export default defineConfig({
|
|
3
|
+
test: {
|
|
4
|
+
globals: true,
|
|
5
|
+
environment: 'node',
|
|
6
|
+
include: ['**/__tests__/**/*.test.ts'],
|
|
7
|
+
coverage: {
|
|
8
|
+
provider: 'v8',
|
|
9
|
+
include: ['**/*.ts'],
|
|
10
|
+
exclude: ['**/__tests__/**', '**/dist/**', 'vitest.config.ts'],
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-server-opencitations",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "MCP server for OpenCitations API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ahmeshaf",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
],
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "https://github.com/ahmeshaf/mcp-server-opencitations"
|
|
16
|
+
"url": "git+https://github.com/ahmeshaf/mcp-server-opencitations.git"
|
|
17
17
|
},
|
|
18
18
|
"type": "module",
|
|
19
19
|
"bin": {
|
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsc && shx chmod +x dist/*.js",
|
|
27
27
|
"prepare": "npm run build",
|
|
28
|
-
"watch": "tsc --watch"
|
|
28
|
+
"watch": "tsc --watch",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"test:coverage": "vitest run --coverage"
|
|
29
32
|
},
|
|
30
33
|
"dependencies": {
|
|
31
34
|
"@modelcontextprotocol/sdk": "^1.24.0",
|
|
@@ -33,7 +36,9 @@
|
|
|
33
36
|
},
|
|
34
37
|
"devDependencies": {
|
|
35
38
|
"@types/node": "^22",
|
|
39
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
36
40
|
"shx": "^0.3.4",
|
|
37
|
-
"typescript": "^5.8.2"
|
|
41
|
+
"typescript": "^5.8.2",
|
|
42
|
+
"vitest": "^2.1.8"
|
|
38
43
|
}
|
|
39
44
|
}
|