usb-workday-agent 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,9 @@
1
+ """USB Workday Agent - MCP integration for Workday automation."""
2
+
3
+ __version__ = "1.0.0"
4
+
5
+ from usb_workday_agent.server import main
6
+
7
+ __all__ = ["main", "__version__"]
8
+
9
+ # Made with Bob
@@ -0,0 +1,607 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ USB Workday Agent MCP Server
4
+
5
+ A FastMCP server for watsonx Orchestrate integration with Workday automation.
6
+ Provides tools for interacting with Workday APIs and automating common tasks.
7
+ """
8
+
9
+ import os
10
+ import json
11
+ import subprocess
12
+ import requests
13
+ from pathlib import Path
14
+ from typing import Optional, Dict, Any, List
15
+ from datetime import datetime
16
+ from fastmcp import FastMCP
17
+
18
+ # Initialize FastMCP server
19
+ mcp = FastMCP("usb-workday-agent")
20
+
21
+
22
+ def execute_command(command: str, cwd: Optional[str] = None) -> str:
23
+ """Execute a shell command and return output."""
24
+ try:
25
+ env = os.environ.copy()
26
+ result = subprocess.run(
27
+ command,
28
+ shell=True,
29
+ cwd=cwd or os.getcwd(),
30
+ capture_output=True,
31
+ text=True,
32
+ check=True,
33
+ env=env
34
+ )
35
+ return result.stdout
36
+ except subprocess.CalledProcessError as e:
37
+ raise Exception(f"Command failed: {e.stderr or e.stdout}")
38
+
39
+
40
+ # Workday API Configuration
41
+ WORKDAY_BASE_URL = os.environ.get("WORKDAY_BASE_URL", "https://<tenant>.workday.com")
42
+ WORKDAY_TENANT = os.environ.get("WORKDAY_TENANT", "")
43
+ WORKDAY_USERNAME = os.environ.get("WORKDAY_USERNAME", "")
44
+ WORKDAY_PASSWORD = os.environ.get("WORKDAY_PASSWORD", "")
45
+
46
+
47
+ def make_workday_request(
48
+ endpoint: str,
49
+ params: Optional[Dict[str, Any]] = None,
50
+ method: str = "GET"
51
+ ) -> Dict[str, Any]:
52
+ """
53
+ Make a request to Workday API.
54
+
55
+ Args:
56
+ endpoint: API endpoint path
57
+ params: Query parameters
58
+ method: HTTP method
59
+
60
+ Returns:
61
+ JSON response from Workday API
62
+ """
63
+ url = f"{WORKDAY_BASE_URL}{endpoint}"
64
+ auth = (WORKDAY_USERNAME, WORKDAY_PASSWORD) if WORKDAY_USERNAME else None
65
+
66
+ try:
67
+ response = requests.request(
68
+ method=method,
69
+ url=url,
70
+ params=params,
71
+ auth=auth,
72
+ headers={"Accept": "application/json"}
73
+ )
74
+ response.raise_for_status()
75
+ return response.json()
76
+ except requests.exceptions.RequestException as e:
77
+ return {
78
+ "error": str(e),
79
+ "message": "Failed to connect to Workday API. Check credentials and configuration."
80
+ }
81
+
82
+ except subprocess.CalledProcessError as e:
83
+ raise Exception(f"Command failed: {e.stderr or e.stdout}")
84
+
85
+
86
+ @mcp.tool()
87
+ def get_workday_status() -> str:
88
+ """
89
+ Get the current status of Workday integration.
90
+
91
+ Returns:
92
+ Status information about the Workday connection
93
+ """
94
+ return json.dumps({
95
+ "status": "ready",
96
+ "message": "USB Workday Agent is operational",
97
+ "version": "1.0.0"
98
+ })
99
+
100
+
101
+ @mcp.tool()
102
+ def query_workday_data(
103
+ endpoint: str,
104
+ query_params: Optional[Dict[str, Any]] = None
105
+ ) -> str:
106
+ """
107
+ Query data from Workday API endpoint.
108
+
109
+ Args:
110
+ endpoint: Workday API endpoint to query
111
+ query_params: Optional query parameters as key-value pairs
112
+
113
+ Returns:
114
+ JSON response from Workday API
115
+ """
116
+ # Placeholder implementation - will be expanded with actual Workday API integration
117
+ return json.dumps({
118
+ "endpoint": endpoint,
119
+ "query_params": query_params or {},
120
+ "message": "Workday query functionality - to be implemented",
121
+ "status": "placeholder"
122
+ })
123
+
124
+
125
+ @mcp.tool()
126
+ def create_workday_record(
127
+ record_type: str,
128
+ data: Dict[str, Any]
129
+ ) -> str:
130
+ """
131
+ Create a new record in Workday.
132
+
133
+ Args:
134
+ record_type: Type of record to create (e.g., employee, position, organization)
135
+ data: Record data as key-value pairs
136
+
137
+ Returns:
138
+ JSON response with created record details
139
+ """
140
+ return json.dumps({
141
+ "record_type": record_type,
142
+ "data": data,
143
+ "message": "Workday record creation - to be implemented",
144
+ "status": "placeholder"
145
+ })
146
+
147
+
148
+ @mcp.tool()
149
+ def update_workday_record(
150
+ record_type: str,
151
+ record_id: str,
152
+ data: Dict[str, Any]
153
+ ) -> str:
154
+ """
155
+ Update an existing record in Workday.
156
+
157
+ Args:
158
+ record_type: Type of record to update
159
+ record_id: Unique identifier of the record
160
+ data: Updated data as key-value pairs
161
+
162
+ Returns:
163
+ JSON response with update status
164
+ """
165
+ return json.dumps({
166
+ "record_type": record_type,
167
+ "record_id": record_id,
168
+ "data": data,
169
+ "message": "Workday record update - to be implemented",
170
+ "status": "placeholder"
171
+ })
172
+
173
+
174
+ @mcp.tool()
175
+ def search_workday_records(
176
+ record_type: str,
177
+ search_criteria: Dict[str, Any],
178
+ limit: int = 100
179
+ ) -> str:
180
+ """
181
+ Search for records in Workday.
182
+
183
+ Args:
184
+ record_type: Type of records to search
185
+ search_criteria: Search criteria as key-value pairs
186
+ limit: Maximum number of results to return (default: 100)
187
+
188
+ Returns:
189
+ JSON array of matching records
190
+ """
191
+ return json.dumps({
192
+ "record_type": record_type,
193
+ "search_criteria": search_criteria,
194
+ "limit": limit,
195
+ "message": "Workday search functionality - to be implemented",
196
+ "status": "placeholder",
197
+ "results": []
198
+ })
199
+
200
+
201
+ @mcp.tool()
202
+ def execute_workday_report(
203
+ report_name: str,
204
+ parameters: Optional[Dict[str, Any]] = None
205
+ ) -> str:
206
+ """
207
+ Execute a Workday report and retrieve results.
208
+
209
+ Args:
210
+ report_name: Name of the Workday report to execute
211
+ parameters: Optional report parameters
212
+
213
+ Returns:
214
+ JSON response with report data
215
+ """
216
+ return json.dumps({
217
+ "report_name": report_name,
218
+ "parameters": parameters or {},
219
+ "message": "Workday report execution - to be implemented",
220
+ "status": "placeholder",
221
+ "data": []
222
+ })
223
+
224
+ # @mcp.tool()
225
+ # def get_workday_attendance(
226
+ # from_date: str,
227
+ # to_date: str,
228
+ # worker_ids: Optional[List[str]] = None,
229
+ # limit: int = 20,
230
+ # offset: int = 0
231
+ # ) -> str:
232
+ # """
233
+ # Retrieves time clock events (check-in/check-out) for workers within a specified date range.
234
+
235
+ # Args:
236
+ # from_date: Start date in yyyy-mm-dd format
237
+ # to_date: End date in yyyy-mm-dd format
238
+ # worker_ids: Optional list of worker IDs
239
+ # limit: Maximum records to return (default 20, max 100)
240
+ # offset: Pagination offset
241
+
242
+ # Returns:
243
+ # JSON string with attendance data
244
+ # """
245
+ # params = {
246
+ # "fromDate": from_date,
247
+ # "toDate": to_date,
248
+ # "limit": min(limit, 100),
249
+ # "offset": offset
250
+ # }
251
+
252
+ # if worker_ids:
253
+ # params["worker"] = worker_ids
254
+
255
+ # result = make_workday_request("/timeTracking/v4/timeClockEvents", params)
256
+ # return json.dumps(result, indent=2)
257
+
258
+
259
+ # @mcp.tool()
260
+ # def get_workday_holidays(
261
+ # from_date: str,
262
+ # to_date: str,
263
+ # worker_ids: Optional[List[str]] = None,
264
+ # limit: int = 20,
265
+ # offset: int = 0
266
+ # ) -> str:
267
+ # """
268
+ # Retrieves holiday events for specified workers and time period.
269
+
270
+ # Args:
271
+ # from_date: Start date in yyyy-mm-dd format
272
+ # to_date: End date in yyyy-mm-dd format
273
+ # worker_ids: Optional list of worker IDs
274
+ # limit: Maximum records to return (default 20, max 100)
275
+ # offset: Pagination offset
276
+
277
+ # Returns:
278
+ # JSON string with holiday data
279
+ # """
280
+ # params = {
281
+ # "fromDate": from_date,
282
+ # "toDate": to_date,
283
+ # "limit": min(limit, 100),
284
+ # "offset": offset
285
+ # }
286
+
287
+ # if worker_ids:
288
+ # params["worker"] = worker_ids
289
+
290
+ # result = make_workday_request("/holiday/v1/holidayEvents", params)
291
+ # return json.dumps(result, indent=2)
292
+
293
+
294
+ # @mcp.tool()
295
+ # def get_workday_time_off(
296
+ # worker_id: str,
297
+ # from_date: Optional[str] = None,
298
+ # to_date: Optional[str] = None,
299
+ # status: Optional[List[str]] = None,
300
+ # time_off_types: Optional[List[str]] = None,
301
+ # limit: int = 20,
302
+ # offset: int = 0
303
+ # ) -> str:
304
+ # """
305
+ # Retrieves time off details for a specified worker.
306
+
307
+ # Args:
308
+ # worker_id: Worker ID or 'me' for current user
309
+ # from_date: Optional start date in yyyy-mm-dd format
310
+ # to_date: Optional end date in yyyy-mm-dd format
311
+ # status: Optional list of statuses (Approved, Submitted, etc.)
312
+ # time_off_types: Optional list of time off type IDs
313
+ # limit: Maximum records to return (default 20, max 100)
314
+ # offset: Pagination offset
315
+
316
+ # Returns:
317
+ # JSON string with time off data
318
+ # """
319
+ # params = {
320
+ # "limit": min(limit, 100),
321
+ # "offset": offset
322
+ # }
323
+
324
+ # if from_date:
325
+ # params["fromDate"] = from_date
326
+ # if to_date:
327
+ # params["toDate"] = to_date
328
+ # if status:
329
+ # params["status"] = status
330
+ # if time_off_types:
331
+ # params["timeOffType"] = time_off_types
332
+
333
+ # endpoint = f"/absenceManagement/v5/workers/{worker_id}/timeOffDetails"
334
+ # result = make_workday_request(endpoint, params)
335
+ # return json.dumps(result, indent=2)
336
+
337
+
338
+ # @mcp.tool()
339
+ # def get_workday_time_and_attendance(
340
+ # worker_ids: List[str],
341
+ # from_date: str,
342
+ # to_date: str,
343
+ # include_attendance: bool = True,
344
+ # include_time_off: bool = True,
345
+ # include_holidays: bool = True,
346
+ # limit: int = 20
347
+ # ) -> str:
348
+ # """
349
+ # Retrieves comprehensive time and attendance data combining clock events and time off.
350
+
351
+ # Args:
352
+ # worker_ids: List of worker IDs
353
+ # from_date: Start date in yyyy-mm-dd format
354
+ # to_date: End date in yyyy-mm-dd format
355
+ # include_attendance: Include time clock events
356
+ # include_time_off: Include time off details
357
+ # include_holidays: Include holiday events
358
+ # limit: Maximum records per data type
359
+
360
+ # Returns:
361
+ # JSON string with combined time and attendance data
362
+ # """
363
+ # combined_data = {
364
+ # "workers": worker_ids,
365
+ # "date_range": {"from": from_date, "to": to_date},
366
+ # "data": {}
367
+ # }
368
+
369
+ # if include_attendance:
370
+ # attendance = make_workday_request(
371
+ # "/timeTracking/v4/timeClockEvents",
372
+ # {"fromDate": from_date, "toDate": to_date, "worker": worker_ids, "limit": limit}
373
+ # )
374
+ # combined_data["data"]["attendance"] = attendance
375
+
376
+ # if include_time_off:
377
+ # time_off_data = []
378
+ # for worker_id in worker_ids:
379
+ # endpoint = f"/absenceManagement/v5/workers/{worker_id}/timeOffDetails"
380
+ # result = make_workday_request(
381
+ # endpoint,
382
+ # {"fromDate": from_date, "toDate": to_date, "limit": limit}
383
+ # )
384
+ # time_off_data.append({"worker_id": worker_id, "time_off": result})
385
+ # combined_data["data"]["time_off"] = time_off_data
386
+
387
+ # if include_holidays:
388
+ # holidays = make_workday_request(
389
+ # "/holiday/v1/holidayEvents",
390
+ # {"fromDate": from_date, "toDate": to_date, "worker": worker_ids, "limit": limit}
391
+ # )
392
+ # combined_data["data"]["holidays"] = holidays
393
+
394
+ # return json.dumps(combined_data, indent=2)
395
+
396
+
397
+ # @mcp.tool()
398
+ # def get_workday_time_and_attendance_by_date(
399
+ # from_date: Optional[str] = None,
400
+ # to_date: Optional[str] = None,
401
+ # date: Optional[str] = None,
402
+ # worker_ids: Optional[List[str]] = None,
403
+ # include_attendance: bool = True,
404
+ # include_time_off: bool = True,
405
+ # include_holidays: bool = True,
406
+ # limit: int = 100
407
+ # ) -> str:
408
+ # """
409
+ # Retrieves time and attendance data for a specific date or date range.
410
+
411
+ # Args:
412
+ # from_date: Start date in yyyy-mm-dd format
413
+ # to_date: End date in yyyy-mm-dd format
414
+ # date: Specific date (overrides from_date/to_date if provided)
415
+ # worker_ids: Optional list of worker IDs
416
+ # include_attendance: Include time clock events
417
+ # include_time_off: Include time off details
418
+ # include_holidays: Include holiday events
419
+ # limit: Maximum records per data type
420
+
421
+ # Returns:
422
+ # JSON string with date-based time and attendance data
423
+ # """
424
+ # if date:
425
+ # from_date = to_date = date
426
+
427
+ # combined_data = {
428
+ # "date_range": {"from": from_date, "to": to_date},
429
+ # "data": {}
430
+ # }
431
+
432
+ # params = {"fromDate": from_date, "toDate": to_date, "limit": limit}
433
+ # if worker_ids:
434
+ # params["worker"] = worker_ids
435
+
436
+ # if include_attendance:
437
+ # combined_data["data"]["attendance"] = make_workday_request(
438
+ # "/timeTracking/v4/timeClockEvents", params
439
+ # )
440
+
441
+ # if include_holidays:
442
+ # combined_data["data"]["holidays"] = make_workday_request(
443
+ # "/holiday/v1/holidayEvents", params
444
+ # )
445
+
446
+ # if include_time_off and worker_ids:
447
+ # time_off_data = []
448
+ # for worker_id in worker_ids:
449
+ # endpoint = f"/absenceManagement/v5/workers/{worker_id}/timeOffDetails"
450
+ # result = make_workday_request(
451
+ # endpoint,
452
+ # {"fromDate": from_date, "toDate": to_date, "limit": limit}
453
+ # )
454
+ # time_off_data.append({"worker_id": worker_id, "time_off": result})
455
+ # combined_data["data"]["time_off"] = time_off_data
456
+
457
+ # return json.dumps(combined_data, indent=2)
458
+
459
+
460
+ # @mcp.tool()
461
+ # def get_workday_time_and_attendance_by_employee(
462
+ # worker_id: str,
463
+ # from_date: str,
464
+ # to_date: str,
465
+ # include_attendance: bool = True,
466
+ # include_time_off: bool = True,
467
+ # include_holidays: bool = True,
468
+ # time_off_status: Optional[List[str]] = None,
469
+ # limit: int = 100
470
+ # ) -> str:
471
+ # """
472
+ # Retrieves comprehensive time and attendance data for a specific employee.
473
+
474
+ # Args:
475
+ # worker_id: Worker ID or 'me' for current user
476
+ # from_date: Start date in yyyy-mm-dd format
477
+ # to_date: End date in yyyy-mm-dd format
478
+ # include_attendance: Include time clock events
479
+ # include_time_off: Include time off details
480
+ # include_holidays: Include holiday events
481
+ # time_off_status: Optional filter for time off status
482
+ # limit: Maximum records per data type
483
+
484
+ # Returns:
485
+ # JSON string with employee time and attendance data
486
+ # """
487
+ # combined_data = {
488
+ # "worker_id": worker_id,
489
+ # "date_range": {"from": from_date, "to": to_date},
490
+ # "data": {}
491
+ # }
492
+
493
+ # if include_attendance:
494
+ # combined_data["data"]["attendance"] = make_workday_request(
495
+ # "/timeTracking/v4/timeClockEvents",
496
+ # {"fromDate": from_date, "toDate": to_date, "worker": [worker_id], "limit": limit}
497
+ # )
498
+
499
+ # if include_time_off:
500
+ # params = {"fromDate": from_date, "toDate": to_date, "limit": limit}
501
+ # if time_off_status:
502
+ # params["status"] = time_off_status
503
+
504
+ # endpoint = f"/absenceManagement/v5/workers/{worker_id}/timeOffDetails"
505
+ # combined_data["data"]["time_off"] = make_workday_request(endpoint, params)
506
+
507
+ # if include_holidays:
508
+ # combined_data["data"]["holidays"] = make_workday_request(
509
+ # "/holiday/v1/holidayEvents",
510
+ # {"fromDate": from_date, "toDate": to_date, "worker": [worker_id], "limit": limit}
511
+ # )
512
+
513
+ # return json.dumps(combined_data, indent=2)
514
+
515
+
516
+ # @mcp.tool()
517
+ # def get_workday_time_and_attendance_by_manager(
518
+ # manager_id: str,
519
+ # from_date: str,
520
+ # to_date: str,
521
+ # include_attendance: bool = True,
522
+ # include_time_off: bool = True,
523
+ # include_holidays: bool = True,
524
+ # time_off_status: Optional[List[str]] = None,
525
+ # include_indirect_reports: bool = False,
526
+ # limit: int = 100
527
+ # ) -> str:
528
+ # """
529
+ # Retrieves time and attendance data for all workers reporting to a manager.
530
+
531
+ # Args:
532
+ # manager_id: Manager ID or 'me' for current user
533
+ # from_date: Start date in yyyy-mm-dd format
534
+ # to_date: End date in yyyy-mm-dd format
535
+ # include_attendance: Include time clock events
536
+ # include_time_off: Include time off requests
537
+ # include_holidays: Include holiday events
538
+ # time_off_status: Optional filter for time off status (e.g., Submitted for approval)
539
+ # include_indirect_reports: Include indirect reports
540
+ # limit: Maximum records per worker
541
+
542
+ # Returns:
543
+ # JSON string with manager team time and attendance data
544
+ # """
545
+ # combined_data = {
546
+ # "manager_id": manager_id,
547
+ # "date_range": {"from": from_date, "to": to_date},
548
+ # "include_indirect_reports": include_indirect_reports,
549
+ # "data": {},
550
+ # "note": "This is a placeholder. Actual implementation requires Workday organizational hierarchy API."
551
+ # }
552
+
553
+ # # Note: This would require additional Workday API calls to:
554
+ # # 1. Get list of direct reports for the manager
555
+ # # 2. Optionally get indirect reports
556
+ # # 3. Then fetch time and attendance data for each report
557
+
558
+ # # Placeholder implementation
559
+ # combined_data["data"]["message"] = (
560
+ # "Manager view requires organizational hierarchy API integration. "
561
+ # "Use get_workday_time_and_attendance with specific worker_ids for now."
562
+ # )
563
+
564
+ # return json.dumps(combined_data, indent=2)
565
+
566
+ @mcp.resource("workday://config")
567
+ def get_workday_config() -> str:
568
+ """
569
+ Get Workday configuration information.
570
+
571
+ Returns:
572
+ Configuration details for Workday integration
573
+ """
574
+ return json.dumps({
575
+ "tenant": "placeholder",
576
+ "environment": "sandbox",
577
+ "api_version": "v1",
578
+ "endpoints": {
579
+ "base_url": "https://api.workday.com",
580
+ "auth_url": "https://auth.workday.com"
581
+ }
582
+ })
583
+
584
+
585
+ @mcp.resource("workday://status")
586
+ def get_connection_status() -> str:
587
+ """
588
+ Get current connection status to Workday.
589
+
590
+ Returns:
591
+ Connection status information
592
+ """
593
+ return json.dumps({
594
+ "connected": False,
595
+ "last_check": "2026-05-05T21:57:00Z",
596
+ "message": "Connection not yet configured"
597
+ })
598
+
599
+
600
+ def main():
601
+ """Run the MCP server."""
602
+ mcp.run()
603
+
604
+
605
+ if __name__ == "__main__":
606
+ main()
607
+
@@ -0,0 +1,178 @@
1
+ Metadata-Version: 2.4
2
+ Name: usb-workday-agent
3
+ Version: 1.0.0
4
+ Summary: USB Workday Agent - MCP integration for Workday automation
5
+ Project-URL: Homepage, https://github.com/amandawinkles/usb-workday-agent
6
+ Project-URL: Repository, https://github.com/amandawinkles/usb-workday-agent
7
+ Project-URL: Issues, https://github.com/amandawinkles/usb-workday-agent/issues
8
+ Author-email: Amanda Winkles <amanda.winkles@ibm.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: automation,fastmcp,mcp,usb,workday
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: fastmcp>=3.0.0
22
+ Requires-Dist: mcp>=0.9.0
23
+ Requires-Dist: requests>=2.31.0
24
+ Provides-Extra: dev
25
+ Requires-Dist: black>=23.0.0; extra == 'dev'
26
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
27
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # USB Workday Agent
31
+
32
+ MCP integration for Workday automation using the wxO-nit virtual environment.
33
+
34
+ ## Overview
35
+
36
+ This project provides an MCP (Model Context Protocol) server for automating Workday operations. It leverages the FastMCP framework to expose tools and resources for Workday integration.
37
+
38
+ ## Prerequisites
39
+
40
+ - Python 3.10 or higher
41
+ - wxO-nit virtual environment (located at `../wxO-nit`)
42
+ - MCP-compatible client (e.g., Claude Desktop, Bob)
43
+
44
+ ## Installation
45
+
46
+ ### Using the wxO-nit Virtual Environment
47
+
48
+ This project uses the existing wxO-nit virtual environment:
49
+
50
+ ```bash
51
+ # Activate the wxO-nit virtual environment
52
+ source ../wxO-nit/bin/activate
53
+
54
+ # Install this package in development mode
55
+ pip install -e .
56
+ ```
57
+
58
+ ### Standalone Installation
59
+
60
+ Alternatively, create a new virtual environment:
61
+
62
+ ```bash
63
+ # Create virtual environment
64
+ python -m venv venv
65
+
66
+ # Activate virtual environment
67
+ source venv/bin/activate # On Windows: venv\Scripts\activate
68
+
69
+ # Install dependencies
70
+ pip install -e .
71
+ ```
72
+
73
+ ## Development
74
+
75
+ ### Install Development Dependencies
76
+
77
+ ```bash
78
+ pip install -e ".[dev]"
79
+ ```
80
+
81
+ ### Code Formatting
82
+
83
+ ```bash
84
+ # Format code with black
85
+ black .
86
+
87
+ # Lint with ruff
88
+ ruff check .
89
+ ```
90
+
91
+ ### Testing
92
+
93
+ ```bash
94
+ pytest
95
+ ```
96
+
97
+ ## Project Structure
98
+
99
+ ```
100
+ usb-workday-agent/
101
+ ├── README.md
102
+ ├── pyproject.toml
103
+ ├── setup.py
104
+ └── (source files to be added)
105
+ ```
106
+
107
+ ## Dependencies
108
+
109
+ - **mcp** (>=0.9.0): Model Context Protocol core library
110
+ - **fastmcp** (>=3.0.0): FastMCP framework for building MCP servers
111
+
112
+ ## Running the MCP Server
113
+
114
+ ### Start the Server
115
+
116
+ ```bash
117
+ # Activate the wxO-nit virtual environment
118
+ source ../wxO-nit/bin/activate
119
+
120
+ # Run the server directly
121
+ python3 server.py
122
+
123
+ # Or use the installed command (after pip install -e .)
124
+ usb-workday-agent
125
+ ```
126
+
127
+ ### MCP Client Configuration
128
+
129
+ Add to your MCP client configuration (e.g., Claude Desktop):
130
+
131
+ ```json
132
+ {
133
+ "mcpServers": {
134
+ "usb-workday-agent": {
135
+ "command": "python",
136
+ "args": ["/Users/amandawinkles/bob/usb-workday-agent/server.py"],
137
+ "env": {
138
+ "PYTHONPATH": "/Users/amandawinkles/bob/wxO-nit/lib/python3.10/site-packages"
139
+ }
140
+ }
141
+ }
142
+ }
143
+ ```
144
+
145
+ ## Available Tools
146
+
147
+ The server provides the following tools for watsonx Orchestrate:
148
+
149
+ - **get_workday_status**: Check Workday integration status
150
+ - **query_workday_data**: Query data from Workday API endpoints
151
+ - **create_workday_record**: Create new records in Workday
152
+ - **update_workday_record**: Update existing Workday records
153
+ - **search_workday_records**: Search for records with criteria
154
+ - **execute_workday_report**: Run Workday reports and retrieve results
155
+
156
+ ## Available Resources
157
+
158
+ - **workday://config**: Get Workday configuration details
159
+ - **workday://status**: Check connection status to Workday
160
+
161
+ ## Configuration
162
+
163
+ Workday API credentials and configuration should be set via environment variables:
164
+
165
+ ```bash
166
+ export WORKDAY_TENANT="your-tenant"
167
+ export WORKDAY_USERNAME="your-username"
168
+ export WORKDAY_PASSWORD="your-password"
169
+ export WORKDAY_API_VERSION="v1"
170
+ ```
171
+
172
+ ## License
173
+
174
+ MIT License
175
+
176
+ ## Author
177
+
178
+ Amanda Winkles
@@ -0,0 +1,7 @@
1
+ usb_workday_agent/__init__.py,sha256=HEM0dlCAMuIEx8S7m9K7AvBOpJJ57Uyi0q6dkiqlKOs,184
2
+ usb_workday_agent/server.py,sha256=eZy_zD5kxHvCCePfAz8Ewg3Ps9AzhgcQO-KbpOusdN8,18287
3
+ usb_workday_agent-1.0.0.dist-info/METADATA,sha256=lN2YLRldShkaqW7Ap_-djTvaUpELWjEhWPYeAACLZlY,4269
4
+ usb_workday_agent-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
5
+ usb_workday_agent-1.0.0.dist-info/entry_points.txt,sha256=XG_3SzRVZ4hJYje6Ajxv0kR5Zil4jefdhWd-PcIoazw,68
6
+ usb_workday_agent-1.0.0.dist-info/licenses/LICENSE,sha256=CKn_by5Ze62wMjBRyBdhlsD---XINMaH3N0jDK8VELM,1070
7
+ usb_workday_agent-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ usb-workday-agent = usb_workday_agent.server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amanda Winkles
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.