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 CHANGED
@@ -1,112 +1,78 @@
1
1
  #!/usr/bin/env node
2
2
  // =============================================================================
3
- // OpenCitations MCP Server - Hello World Starter
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
- // SERVER SETUP
9
+ // CONFIGURATION: Environment Variables + Command Line Args
21
10
  // -----------------------------------------------------------------------------
22
- // Create a new MCP server instance.
23
- // The object passed to McpServer is called an "object literal" in TypeScript.
24
- // It has typed properties: 'name' and 'version' are both strings.
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", // Server identifier
27
- version: "0.1.0", // Semantic versioning
36
+ name: "opencitations-server",
37
+ version: "0.1.0",
28
38
  });
29
39
  // -----------------------------------------------------------------------------
30
- // TOOL REGISTRATION
40
+ // TOOLS
31
41
  // -----------------------------------------------------------------------------
32
- // MCP servers expose "tools" that LLMs can call.
33
- // Let's register a simple "hello" tool.
34
- // registerTool takes 3 arguments:
35
- // 1. Tool name (string)
36
- // 2. Tool configuration (object with description, inputSchema, etc.)
37
- // 3. Handler function (async function that runs when tool is called)
38
- server.registerTool("hello", // Tool name - this is what the LLM will use to call it
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
- // ANOTHER EXAMPLE: Tool with required parameters
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
- // No .optional() here - this parameter is required
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
- // TypeScript knows args.message is definitely a string (not undefined)
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 THE SERVER
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); // Exit with error code
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.0",
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
  }